Sovereign Storm’s multiplayer is server-authoritative and built on Colyseus. The guiding rule is simple: the server is the only thing that simulates a round, and clients only ever animate the result. This page traces how a turn travels from a captain’s plan to a synchronized animation on every screen.
See also: Architecture · Game Design.
The plan → submit → resolve → replay flow
- Plan. A captain edits their move and cannon plan locally. Each edit is sent to the server as a message; the server validates it (legal moves, token budget, forced-stall rules) and accepts or rejects it. Nothing is simulated yet.
- Submit. Submitting flips the captain’s “ready” flag and broadcasts it. The server then checks whether the round can resolve.
- Gate. Resolution proceeds only when every active captain has submitted and a minimum planning window has elapsed. Both gates must pass.
- Resolve. The server runs the authoritative resolver: it advances the round, applies repairs and respawns and buffs, dispatches into the active mode strategy, and walks each move slot — building a sequence of ticks that captures every ship’s position and state at each sub-step, including ship-push, ramming, and cannon volleys.
- Replay. The server broadcasts that tick sequence (plus supporting state like hotspots and wreckage). Each client plays it back as an animation — tweening ships, firing particle effects, updating HP bars and scores.
The client does not re-simulate. Because there is exactly one simulation and it lives on the server, no two players can ever see a divergent outcome. The cost is that each round’s payload carries the full resolution detail; the project accepts that bandwidth in exchange for guaranteed consistency.
Lobby and joining
The server defines two room types: a lobby room and battle rooms. The lobby uses the framework’s built-in real-time room listing — battle rooms advertise themselves, and the lobby renders a live, self-updating match list. There is no separate matchmaking service. A captain joins or creates a battle room from the lobby, the client persists a reconnect token, and the scene transitions into the match.
Why custom messages are the real protocol
The game uses Colyseus schema state for only a minimal slice (notably the match phase) and broadcasts almost everything else — scores, round number, player lists, round results — through explicit custom messages. This was a deliberate response to schema-propagation behavior in the framework version in use. The practical takeaway for anyone reading the code: treat the custom messages as the canonical protocol, not the schema state. The wholesale-broadcast pattern is consistent and documented, with discipline around mutating-then-broadcasting state.
Player lists are redacted per-client where appropriate, so a captain’s view contains only what that captain is entitled to see.
Reconnection
Sessions carry a reconnect token. If a captain’s connection drops, the server holds their seat for a short grace window; reopening the page within that window reclaims the same captain seat and resumes the match. After the grace window expires, the seat is released and the player returns to the lobby.
Trust boundary
Every inbound client message crosses a trust boundary into authoritative state. Handlers validate and sanitize untrusted payloads — guarding on the current phase, the sender’s team, move legality, token budgets, and length/rate limits on free-text like names and chat — before any state mutation. The shared package centralizes the validators so both tiers agree on what is legal. Host-only debug handlers exist but are gated behind an environment flag and are never enabled in playtest or production builds.