MudEngine Part 9: The Twin Guardians
Run it!
Start the fullstack dev server. The server loads the quest config from game.toml and the vault room as part of the world.
dx serve --openOpen three browser tabs:
Characters
| Tab | Player | Role |
|---|---|---|
| 1 | Aria | Sun Guardian โ stands on the Sun Pedestal (room 5, Temple Courtyard) |
| 2 | Kael | Moon Guardian โ stands on the Moon Pedestal (room 3, Dark Forest) |
| 3 | Lira | Hero โ retrieves the Star Fragment from the vault |
Phase 1 โ Open the portal
- Aria walks east โ Temple Courtyard (room 5), Kael stays in Town Square
- Kael walks west โ Dark Forest (room 3), Aria stays
- Each tab sees half the guardians active โ โณ for the empty pedestal
- Aria and Kael coordinate to both be on their pedestals simultaneously
- Both pedestals active โ "โ๏ธ๐ The Twin Guardians are united! A shimmering portal opens in Town Square!"
- The quest panel shows both guardians as โ active
- King Aldric in Town Square says: "The portal is open! Someone must venture below and retrieve the Star Fragment!"
Phase 2 โ Retrieve the Star Fragment
- Lira (tab 3) is in Town Square โ sees the "โฌ Descend into the Ancient Vault" button
- Lira clicks it โ descends into the Ancient Vault
- Lira sees the vault description and items: "star fragment", "ancient crown"
- Lira types
take star fragmentโ it's now in Lira's inventory - Lira ascends back to Town Square (click "โฌ Ascend" or press up)
- Town Square shows the star fragment in Lira's inventory
- All tabs see the item change in the player list
Phase 3 โ Complete the quest
- Lira is now in Town Square with the star fragment in inventory
- King Aldric says: "You have it! The Star Fragment! Quick, hand it to me so I can restore the wards!"
- Lira types
drop star fragmentto hand it to the King - King Aldric celebration fires for all players: "๐ King Aldric beams with joy! 'The Star Fragment is returned! The realm is saved!' All hail the heroes who reclaimed the star fragment!"
- The quest panel shows "๐ Quest Complete: Twin Guardians"
- King Aldric now says: "You have my eternal gratitude, brave adventurers!"
- The guardians can leave their pedestals โ the quest is won permanently
What happens if ...
... a guardian leaves mid-retrieval?
- Lira is in the vault, Aria leaves Temple Courtyard
- Portal closes, Lira is teleported back to Town Square
- Lira keeps the star fragment in inventory
- Guardians must re-activate to re-open the portal
... Lira disconnects with the fragment?
- Disconnect handler drops the star fragment into the vault floor
- Portal closes (guardian check runs)
- Next time the portal opens, the fragment is waiting on the floor for someone to pick up
... someone carries the fragment into Town Square and the quest completes?
- Once
check_quest_completionfires,completed = true update_questreturnsNoneimmediately โ guardians are free- The vault remains accessible through the dynamic exit until the guardians leave
... a single player tries to do everything?
state.players.len() >= 2prevents activating the portal with one player- All three roles are required โ two guardians plus a retriever
Verifying in game.toml
Open game.toml after the quest completes. The [quest] section is unchanged (read-only config). Player and room data reflects whatever the last actions were.
"cannot find type QuestStatus" or "cannot find type TomlQuest"
โ Make sure the quest types are defined before the srv module that references them. The type definitions should go after the existing shared types and before #[cfg(feature = "server")].
"field quest not found on GameState"
โ You added the quest field to GameState but forgot the other locations that construct GameState โ load_game_state and anywhere else that creates one.
Vault button does nothing when clicked
โ The movement handler has a direct check for dir == "down" && current == 4. If the button sends a different direction string, the check won't match. Make sure the button sends ClientMessage::Move { direction: "down".into() }.
"no field quest on TomlGame" when saving
โ The save_game_state function constructs a TomlGame without the quest field. Make sure TomlGame has quest: Option<TomlQuest> and that save_game_state passes quest: None.
Players can't leave the vault
โ The quest activation code must add a return exit ("up") from room 9 back to room 4. Check the update_quest function โ the code block in Step 6 shows both exits being added. If the return exit isn't added, players are trapped.
Quest panel shows stale guardian status
โ The client derives guardian active state from player positions in the State handler. If a PlayerMoved changes guardian occupancy, the client doesn't automatically re-evaluate. Either re-evaluate in the PlayerJoined/PlayerMoved handler, or rely on QuestEvent broadcasts. The simplest fix: re-build the quest display whenever the players map changes.
"cannot find variant QuestCompleted" compiler error
โ You added QuestCompleted to the enum but forgot to handle it in all pattern matches. The ServerMessage enum is used in both the receive loop (match all variants) and the serde serialization โ no match needs _ => {} as a fallback because Dioxus enforces exhaustive matching. Add a QuestCompleted { .. } arm everywhere you match ServerMessage.
Star Fragment doesn't trigger quest completion when dropped in Town Square
โ check_quest_completion checks completion_room and retrieval_item from the quest config. Make sure game.toml has retrieval_item = "star fragment" and completion_room = 4. Also verify the Take/Drop handlers call check_quest_completion after the successful action.
King Aldric dialog never appears
โ The NPC dialog renders when info.room == 4. Make sure the npc-dialog block is inside a if let Some(ref info) = my_info check. Also confirm my_info is correctly derived from the players signal and my_id signal.
Quest completion doesn't persist โ shows incomplete after page refresh
โ The quest completed state is held in the server's QuestStatus struct which is in-memory only. A server restart resets it. To persist, you'd need to save it to game.toml (add a field to TomlQuest or a standalone serialize block). This is left as an exercise.