MudEngine Part 6: Multiplayer
Server Game State
The server needs to keep track of all connected players. We use global static state behind #[cfg(feature = "server")] — it is only compiled into the server binary, never into the WASM client.
The state has two parts:
PLAYERS— aMutex<HashMap<String, PlayerInfo>>mapping player IDs to their infoBROADCAST— atokio::sync::broadcast::Senderthat fans out every message to all connected clients
This state lives in src/game/server.rs, along with the WebSocket endpoint we add in the next step. The file starts with the imports it needs (the #[get] macro, the WebSocket types, and the shared message types and EXITS), then defines the server-only state module:
use dioxus::prelude::*; use dioxus_fullstack::{Websocket, WebSocketOptions}; use super::messages::{ClientMessage, PlayerInfo, ServerMessage}; use super::room_data::EXITS; // ── Server-only state ── #[cfg(feature = "server")] mod srv { use std::collections::HashMap; use std::sync::{LazyLock, Mutex}; use tokio::sync::broadcast; use super::*; /// All connected players, keyed by their unique ID. pub static PLAYERS: LazyLock<Mutex<HashMap<String, PlayerInfo>>> = LazyLock::new(|| Mutex::new(HashMap::new())); /// Broadcast channel — every connected WebSocket subscribes. /// When a player moves, anyone sends, everyone receives. pub static BROADCAST: LazyLock<broadcast::Sender<ServerMessage>> = LazyLock::new(|| { let (tx, _) = broadcast::channel(64); tx }); }
Mutex::new() is not a const function, so we cannot write static PLAYERS: Mutex<...> = Mutex::new(HashMap::new()). LazyLock initializes the value on first access — the closure runs once and the result lives for the program's lifetime.
broadcast::channel(64) creates a sender/receiver pair with a buffer of 64 messages. If a client is too slow to keep up, they miss messages (the receiver gets a Lagged error). For our 9-room MUD this is more than enough.
Col 0 Col 1 Col 2
┌──────────┬──────────┬──────────┐
│ Forest │ Hilltop │ Abandoned│ Row 0
│ Path (0) │ (1) │ Tower(2) │
├──────────┼──────────┼──────────┤
│ Dark │ Town │ Temple │ Row 1
│ Forest(3)│ Square(4)│ Crt. (5) │
├──────────┼──────────┼──────────┤
│ River- │ Old │ Grave- │ Row 2
│ bank (6) │ Bridge(7)│ yard (8) │
└──────────┴──────────┴──────────┘
Room index = row × 3 + col. All new players start at room 4 (Town Square, the center). The exit table on the server enforces that movement only works along actual connections — no walking through walls.