← All Workshops
MudEngine Part 6: Multiplayer
Step 12 / 12
Congratulations!
You just built a multiplayer MUD engine with real-time WebSocket communication!
What we achieved:
- Fullstack Dioxus — one codebase, two targets (server + WASM), all in Rust
- WebSocket endpoint — a
#[get]server function that upgrades HTTP to a persistent bidirectional connection - Authoritative server — movement validation prevents wall-walking; the server is the single source of truth
- Real-time broadcasting —
tokio::sync::broadcastfans out every player action to all connected clients - Reactive client UI —
use_websocket+use_futurereact to incoming messages and update the grid/sidebar instantly - Player lifecycle — join (name entry via
Inputcomponent), move (D-pad viaButtoncomponent), leave (disconnect detection) - Part 5 components reused —
RoomCellshows occupant names per grid cell,Cardwraps the description,Buttondrives the D-pad,Inputhandles name registration
Architecture recap
┌──────────────┐ WebSocket ┌──────────────────┐
│ Browser A │ ◄──────────────► │ Axum Server │
│ - Alice │ │ - #[get] handler │
│ - use_ws │ │ - PlayerManager │
│ - grid+dpad │ │ - broadcast │
└──────────────┘ │ - room exits │
└──────────────────┘
┌──────────────┐ WebSocket ▲
│ Browser B │ ◄────────────────────────┘
│ - Bob │
│ - use_ws │
│ - grid+dpad │
└──────────────┘
Every player action follows the same path: client → server → validate → broadcast → all clients update. No client trusts its own input for game state — the server decides and tells everyone.
What's next?
The dungeon keeps growing! Future parts could explore:
- Persistent world — store player positions and room state in a database (SQLite via
sqlx) - Chat — add a chat panel where players can send messages to each other
- Combat — monsters in rooms, attack commands, health tracking
- Inventory — items the player can pick up and carry between rooms
- Authentication — sessions and login so players keep their name across page reloads
- Room state — locks, doors, torches that can be lit or extinguished
The MUD lives! 🏰⚔️
📚 What we learned
| Concept | How we used it |
|---|---|
#[get] server function | Axum endpoint that handles WebSocket upgrades with WebSocketOptions |
Websocket<In, Out> | Strongly-typed WebSocket with JSON serialization of custom enums |
use_websocket | Dioxus hook that reactively manages a WebSocket connection |
use_future | Long-lived async loop for receiving WebSocket messages |
tokio::select! | Multiplexing between client messages and broadcast messages |
tokio::sync::broadcast | Fan-out channel that delivers every message to all subscribers |
cfg(feature = "server") | Conditional compilation — server state only exists in the server binary |
RoomCell (custom) | Reusable grid cell with name, active, and players props — shows occupant names per room |
Button / ButtonVariant | Clickable direction buttons from dioxus-components sending ClientMessage::Move through the WebSocket |
Card / CardHeader / CardTitle / CardContent | Structured description panel with theme styling from dioxus-components |
Input | Themed name input from dioxus-components for player registration |
Separator | Horizontal divider in the description card |
mod components; | Declaring the dioxus-components module auto-generated in Part 5 |
LazyLock | Thread-safe lazy initialization of global server state |
Step 12 / 12