← All Workshops
MudEngine Part 6: Multiplayer
Step 18 / 18
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), move (D-pad, clickable rooms, keyboard), leave (disconnect detection)
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 |
| D-pad controls | Clicking direction buttons sends ClientMessage::Move through the WebSocket |
| Clickable room cards | Reachable rooms are highlighted and fire onmove when clicked — the server still validates |
| Keyboard shortcuts | Arrow keys / WASD / vim keys send moves via the same onmove callback |
LazyLock | Thread-safe lazy initialization of global server state |
Step 18 / 18