Concepts
The authority model
Section titled “The authority model”A room is an authoritative simulation with attached clients. Your game
implements RoomAuthority — validation, simulation and snapshots — and the
room engine (RoomHub) owns everything else: transports, the session
handshake, acks, rate limits, snapshot encoding and the social lane.
The boundary is strict in both directions:
- Networking never imports physics, UI or game types.
- The game never sees a socket; it sees peer ids, commands and ticks.
Clients send requests (inputs, publishes, chat); the authority owns all
state; everyone else receives snapshots. That is the entire security
model — a misbehaving client can only ever change what the authority lets it
change. The full call-site contract is specified in
/spec/room-authority.md.
Hosting shapes
Section titled “Hosting shapes”One room engine runs everywhere; hosting is a composition choice, not an architecture:
| Shape | Entry point | Transport in |
|---|---|---|
| Dedicated server | RoomServer (server_sdk), binds HTTP and upgrades /play/<roomKey> WebSocket requests |
Real WebSocket listener (native only) |
| In-process host | RoomHub + MemoryTransportPair (protocol) |
Memory pipe, no sockets |
| Player-hosted | RoomHub + any GameConnection adapter (e.g. WebRTC) |
Anything implementing the connection interfaces |
| Rooms node | packages/node — many rooms in one process behind a supervisor |
One port, /play/<roomKey> + /v1 HTTP API |
Join / resume lifecycle
Section titled “Join / resume lifecycle”- Join. The client sends a
joinframe with aprotocolversion (exact match required), a player name and optional game fields. The hub validates, callsvalidateJoin, assigns a stable peer id (p1,p2, …) and answerswelcome— with a fresh one-time resume token — followed by a baseline, an initial snapshot and the roster (with chat history). - Play. Commands flow client → authority; snapshots flow hub → clients
at
snapshotHz. - Disconnect. A lost transport starts a grace window (default 15 s). The player’s slot and state are reserved; the roster shows them as disconnected but present.
- Resume. Reconnecting with the resume token inside the window restores the same peer id, replays roster/chat history and closes any old transport. Resumes never need a new join token from the control plane.
- Reap. Past the grace window the player is removed for good
(
removePlayer), their row disappears from snapshots, and the next join is a fresh id.
A resume token bound to a verified transport identity cannot be used by another identity.
Baseline / delta replication
Section titled “Baseline / delta replication”Snapshots flow through SnapshotEncoder as baseline-relative deltas:
- A reliable full baseline is refreshed once per second and on every epoch change. Every delta refers to the current baseline — never to another delta — so a lost or reordered delta cannot corrupt the next one; the next baseline always repairs the receiver.
- Disposable deltas ride a separate (optionally unreliable) state lane between baselines. Receivers that miss too much get display-only recovery snapshots instead of a new baseline.
- Floats are quantized to 4 decimal places with a float32 deadband, so
sub-quantum changes drop out entirely; presence masks preserve resets to
zero,
falseandnull. - Acknowledgment runs the other way: every applied snapshot tick is acked, and a receiver that stops ackging for > 5 s while blocked is evicted.
- When the transport supports binary and a
WireStateCodec(e.g. FlatBuffers) is configured, states are encoded as binary frames; JSON text remains the fallback, and the two can share a transport during migration.
The exact frame shapes are in the wire protocol reference
and /spec/rooms-protocol.json.
Room social
Section titled “Room social”Chat, pings, roster and moderation ride the same reliable lane as control frames — never the disposable state lane:
- Chat text is single-line, capped at 280 characters, rejects hidden control characters and bidi overrides; history caps at 100 entries; rate limit 4 messages / 10 s per player.
- Pings (
here,help,ball) carry authority-supplied locations and expire after 8 s; speech bubbles after 10 s. - The room leader can lock the room, and kick or ban players. Bans require an identity verified by the transport adapter — never a join payload — and the room-lifetime ban list caps at 256 identities.
Templates overview
Section titled “Templates overview”When you do not want to write Dart at all, the rooms node runs platform-provided authorities configured by JSON room configs:
- Relay — client-authoritative state rows (presence, chat, small published state), quota-enforced. Your game publishes key/value rows; the server owns quotas and replication.
- Movement — server-simulated physics over configured static colliders with spawn points, movement parameters and an input whitelist. Positions are server-simulated and server-validated (anti-cheat by construction).
See the room templates reference for the config schemas.
Distilled from docs/protocol.md and the source of packages/protocol; the
wire protocol reference embeds the repository document
verbatim.