Multiplayer limitations
Ziva exposes two different multiplayer models. They are not interchangeable — picking the wrong one is the single most common source of “it worked in the lobby but lost everyone’s progress” bugs. This page is the decision aid.
The two models
Model 1 — Host-client (relay)
The relay is a message switch between peers with no dedicated server. One real peer is elected host (the lowest peer id) and owns the MultiplayerSpawner and shared world state; the rest are clients. State lives in the host’s running game and is replicated with MultiplayerSynchronizer. This is the model the Multiplayer guide builds end to end.
- Authority: a player’s machine (the host).
- State lifetime: only as long as the host stays connected. Host leaves → the live world cannot be migrated, so the session ends for shared state. (Players keep playing under a new host, but anything not covered by a synchronizer is lost.)
- Cost: bandwidth only — no per-room storage.
Model 2 — Durable-object state
A server-side Durable Object holds the room’s authoritative state. Peers read and write it through the relay, so the state is owned by infrastructure, not by any player. Because no player is the authority, the room survives every peer leaving and rejoining — the next peer to connect picks up the persisted state. Build it with the ZivaState autoload: set_state/get_state for a live shared document and save_world/world_loaded for a durable save blob.
- Authority: a server-side Durable Object.
- State lifetime: persists across disconnects, host changes, and empty rooms.
- Cost: bandwidth plus persisted storage, and you must keep the state under the blob ceiling below.
When to use which
| Your room needs… | Use |
|---|---|
| A transient session (lobby, deathmatch round, co-op demo) | Model 1 |
| Lowest latency, no server round-trip for state | Model 1 |
| State that must outlive the host leaving | Model 2 |
| Progress that survives everyone disconnecting and rejoining | Model 2 |
| Authoritative state a player’s machine must not be trusted with | Model 2 |
| Mostly position/animation sync with a clear “host” | Model 1 |
Rule of thumb: if losing the host would lose data you care about, you need Model 2. Otherwise Model 1 is simpler and faster.
Hard limits
These apply regardless of model.
32 peers per room
A single room accepts at most 32 concurrent peers. The 33rd connection to the same room id is rejected. For larger player counts, shard across multiple room ids (e.g. one room per match) rather than trying to grow a single room.
~1MB persistence blob (Model 2)
A Durable Object room’s persisted state is capped at ~1MB per room. This is a storage ceiling, not a per-message limit: keep the authoritative blob small (ids and counters, not full asset payloads or chat logs). Exceeding it fails the write loudly — there is no silent truncation — so size your state model up front.
Latency
Every message in both models takes a full application round-trip through the relay: peer → Cloudflare edge → the room’s Durable Object → the other peer, and back. There is no peer-to-peer path and no LAN shortcut. The number that matters for game feel is therefore the relay round-trip, not a one-way ping.
Measured against the deployed relay with two WebSocket clients in the same region as the serving colo (client in US-West, Durable Object served from Cloudflare’s SJC colo, 40–50 samples per run):
| Metric | Relay round-trip |
|---|---|
| p50 | ~40 ms |
| p90 | ~135 ms |
| best observed | ~25 ms |
This is the near-colo floor — players sitting close to the DO’s colo. A room’s Durable Object is pinned to a single colo (the one nearest whoever opens the room first) and there is no regional sharding or migration: every peer, wherever they are, routes to that one colo. So a player far from it pays their cross-region internet RTT to that colo on top of the floor above. As a rough global bound, add the inter-region round-trip (commonly ~150 ms US↔EU, ~250 ms US↔Asia) to the ~40 ms floor: a transatlantic peer should expect a relay round-trip on the order of 150–250+ ms, and an antipodal peer worse.
That single-colo design is fine for the model the relay is built for and bad for the model it is not:
| Use case | Verdict |
|---|---|
| Turn-based / card / board games | ✓ Comfortable — ~40–250 ms is invisible between turns. |
| Casual real-time (co-op, lobby, slow movement) | ~ OK when players are regionally close to the colo; degrades as they spread across regions. |
| Competitive or global twitch / FPS | ✗ A single-colo relay cannot give every player the sub-50 ms, geographically-fair round-trip these need. Use a purpose-built authoritative netcode stack instead. |
These figures are the deployed-relay measurement, not an estimate; re-run worker/test/e2e-staging.ts against staging to reproduce them. Treat them as round-trip latency only — they say nothing about throughput, which is bounded separately by the 32-peer and ~1MB limits above.