← All Workshops
MudEngine Part 7: Save & Load
Step 8 / 10
Update the Game Component
The Game component previously used ROOM_NAMES[idx] and ROOM_DESCS[info.room] from static arrays. Now room data comes from the server's State message.
Changes:
- Add a
rooms: Signal<HashMap<usize, RoomInfo>>to store the server-provided room data - In the receive loop, populate the room map from the
Statemessage - Replace all
ROOM_NAMES[...]andROOM_DESCS[...]lookups with room-map lookups - Remove the
static ROOM_NAMESandstatic ROOM_DESCSdeclarations
Update the Game component:
mud-engine/src/main.rs
// ── Game UI ── #[component] fn Game(name: String) -> Element { let mut players = use_signal::<HashMap<String, PlayerInfo>>(|| HashMap::new()); let mut my_id = use_signal(|| String::new()); let mut rooms = use_signal::<HashMap<usize, RoomInfo>>(|| HashMap::new()); let mut socket = use_websocket(move || { mud_ws(name.clone(), WebSocketOptions::new()) }); // ── Message receive loop ── { let mut players = players.clone(); let mut my_id = my_id.clone(); let mut rooms = rooms.clone(); use_future(move || async move { loop { _ = socket.connect().await; while let Ok(msg) = socket.recv().await { match msg { ServerMessage::State { players: p, your_id, rooms: r, } => { let mut map = players.write(); map.clear(); for pl in p { map.insert(pl.id.clone(), pl); } my_id.set(your_id); // Store room data from the server let mut room_map = rooms.write(); room_map.clear(); for room in r { room_map.insert(room.id, room); } } ServerMessage::PlayerJoined(pl) | ServerMessage::PlayerMoved(pl) => { players.write().insert(pl.id.clone(), pl); } ServerMessage::PlayerLeft { id } => { players.write().remove(&id); } } } } }); } let my_info = { let p = players.read(); let id = my_id.read(); id.as_ref() .and_then(|id| p.get(id).cloned()) }; rsx! { div { class: "game-layout", // ── Left: 3×3 grid ── div { class: "grid-area", div { class: "world-grid", for y in 0..3 { for x in 0..3 { let idx = y * 3 + x; let room_name = rooms.read() .get(&idx) .map(|r| r.name.clone()) .unwrap_or_default(); // Collect the names of players in this room let occupants: Vec<String> = players.read().values() .filter(|p| p.room == idx) .map(|p| p.name.clone()) .collect(); // Is this the cell where "we" are standing? let is_my_cell = my_id.read().as_ref() .and_then(|id| players.read().get(id)) .map_or(false, |p| p.room == idx); div { key: "{idx}", class: if is_my_cell { "cell active" } else { "cell" }, div { class: "room-name", "{room_name}" } for occupant_name in &occupants { div { class: "player-indicator", "{occupant_name}" } } } } } } } // ── Right: player list sidebar ── div { class: "sidebar", h2 { "⚔️ Adventurers" } for p in players.read().values() { let is_me = my_id.read().as_ref() .map_or(false, |id| id == &p.id); let room_name = rooms.read() .get(&p.room) .map(|r| r.name.clone()) .unwrap_or_default(); div { key: "{p.id}", class: if is_me { "player-entry me" } else { "player-entry" }, span { class: "player-name", "{p.name}" if is_me { " (you)" } } span { class: "player-room", "{room_name}" } } } } } // ── Description panel ── if let Some(ref info) = my_info { let room_data = rooms.read().get(&info.room); let rname = room_data.map(|r| r.name.clone()).unwrap_or_default(); let rdesc = room_data.map(|r| r.description.clone()).unwrap_or_default(); div { class: "description", "{rname}" "\n" "{rdesc}" } } // ── D-pad movement controls ── div { class: "dpad", button { class: "dpad-btn up", onclick: move |_| { let _ = socket.send(ClientMessage::Move { direction: "north".into(), }); }, "▲" } div { class: "dpad-row", button { class: "dpad-btn left", onclick: move |_| { let _ = socket.send(ClientMessage::Move { direction: "west".into(), }); }, "◀" } button { class: "dpad-btn center", disabled: "true", "●" } button { class: "dpad-btn right", onclick: move |_| { let _ = socket.send(ClientMessage::Move { direction: "east".into(), }); }, "▶" } } button { class: "dpad-btn down", onclick: move |_| { let _ = socket.send(ClientMessage::Move { direction: "south".into(), }); }, "▼" } } } }
💡 Removed statics
The old ROOM_NAMES and ROOM_DESCS static arrays are completely removed. If you scroll up in your main.rs, you should delete them — they're not referenced anywhere anymore.
Also remove the old EXITS constant from inside the srv module — the new can_move function handles movement validation from the TOML-loaded room data.
Clean up after yourself! Unused statics generate compiler warnings.
Step 8 / 10