MudEngine Part 4: Single-Player Dioxus GUI
The World model — game logic
We reuse the same Room and World structs from Part 2, but expand the map to 9 rooms arranged in a 3×3 grid.
The player starts in room 4 — Town Square, the center of the grid. Each room connects to its grid neighbours via cardinal exits (north, south, east, west).
The two methods we need are:
look()— formats the current room's name, description, and exit list into aStringgo(dir)— looks for a matching exit; if found, updatesplayer_roomand returnstrue; otherwise returnsfalse
This is pure game logic — no Dioxus types, no Element, no rsx!. It lives in src/game/world.rs.
Note that every type and method is now pub: the frontend components in src/components/ need to call World::new(), world.go(..), and read world.rooms / world.player_room, and Rust items are private to their module by default.
| Row | Col 0 | Col 1 | Col 2 |
|---|---|---|---|
| 0 | Forest Path (0) | Hilltop (1) | Abandoned Tower (2) |
| 1 | Dark Forest (3) | Town Square (4) | Temple Courtyard (5) |
| 2 | Riverbank (6) | Old Bridge (7) | Graveyard (8) |
#[derive(Debug, Clone, Copy, PartialEq)] pub enum Direction { North, South, East, West, } impl std::fmt::Display for Direction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::North => write!(f, "north"), Self::South => write!(f, "south"), Self::East => write!(f, "east"), Self::West => write!(f, "west"), } } } #[derive(Clone, PartialEq)] pub struct Room { pub name: String, pub description: String, pub exits: Vec<(Direction, usize)>, } #[derive(Clone, PartialEq)] pub struct World { pub rooms: Vec<Room>, pub player_room: usize, } impl World { pub fn new() -> Self { let rooms = vec![ Room { name: "Forest Path".into(), description: "A winding path leads through ancient oaks. Sunlight \ filters through the canopy.".into(), exits: vec![(Direction::South, 3), (Direction::East, 1)], }, Room { name: "Hilltop".into(), description: "From this vantage point you can see the entire valley. \ A cool breeze carries the scent of pine.".into(), exits: vec![(Direction::South, 4), (Direction::West, 0), (Direction::East, 2)], }, Room { name: "Abandoned Tower".into(), description: "A crumbling stone tower stands alone. Vines crawl up \ its walls and crows nest in the windows.".into(), exits: vec![(Direction::South, 5), (Direction::West, 1)], }, Room { name: "Dark Forest".into(), description: "Twisted trees block out most of the light. Strange \ sounds echo through the undergrowth.".into(), exits: vec![(Direction::North, 0), (Direction::South, 6), (Direction::East, 4)], }, Room { name: "Town Square".into(), description: "A bustling town square with a fountain at its center. \ Cobblestones gleam from the morning rain.".into(), exits: vec![ (Direction::North, 1), (Direction::South, 7), (Direction::West, 3), (Direction::East, 5), ], }, Room { name: "Temple Courtyard".into(), description: "Ancient stone pillars surround a quiet courtyard. Moss \ clings to weathered statues.".into(), exits: vec![(Direction::North, 2), (Direction::South, 8), (Direction::West, 4)], }, Room { name: "Riverbank".into(), description: "A slow-moving river borders a muddy bank. Frogs croak \ from the reeds.".into(), exits: vec![(Direction::North, 3), (Direction::East, 7)], }, Room { name: "Old Bridge".into(), description: "A weathered stone bridge crosses the river. Moss covers \ the ancient masonry.".into(), exits: vec![(Direction::North, 4), (Direction::West, 6), (Direction::East, 8)], }, Room { name: "Graveyard".into(), description: "Rows of moss-covered headstones stretch into the fog. \ An iron gate creaks in the wind.".into(), exits: vec![(Direction::North, 5), (Direction::West, 7)], }, ]; World { rooms, player_room: 4, } } pub fn look(&self) -> String { let room = &self.rooms[self.player_room]; let mut out = String::new(); out.push_str(&room.name); out.push_str("\n"); out.push_str(&room.description); out.push_str("\n\nExits:"); for (dir, _) in &room.exits { out.push(' '); out.push_str(&dir.to_string()); } out } pub fn go(&mut self, direction: Direction) -> bool { for (dir, idx) in &self.rooms[self.player_room].exits { if *dir == direction { self.player_room = *idx; return true; } } false } }
In Dioxus 0.7, props must implement Clone and PartialEq. Dioxus uses PartialEq to detect whether a prop changed — if it did not change, the component skips re-rendering. Both derives are added automatically via #[derive(Clone, PartialEq)].
The PartialEq comparison on World walks every room, name, description, and exit. That is fine for 9 rooms but you would want a smarter scheme (like a version counter) for a larger world.
Room index = row * 3 + col. The player starts in the center (4).
Exits mirror the grid: each room connects to its immediate neighbours. The go method checks whether an exit exists for the given direction.