Ownership & the ladder
Every entity has exactly one owner. The owner is the only party whose writes are accepted; everyone else receives it read-only. Owners come in two kinds:
- A client — cheap, low-latency, good for cosmetic or self-reported state (your position, your cursor, your chat message).
- The server — authoritative, good for anything that decides the game (score, loot, whose turn it is, hit detection).
Assigning an owner
Set it when you create the entity, usually in onJoin:
onJoin(state, ctx) {
state.players[ctx.clientId] = { x: 0, y: 0, owner: ctx.clientId };
} Hand it to the server by marking the entity serverOwned in the schema, or by
setting owner: SERVER at creation:
import { SERVER } from '@irtio/room';
state.match = { phase: 'lobby', round: 0, owner: SERVER }; The ladder, one rung at a time
You don’t pick an architecture up front. You climb only when a feature demands it, and each rung is a local edit:
- Relay — no room deployed; clients just fan messages out.
- Owner-write sync — assign client owners; state syncs automatically.
- Validators — keep client ownership but add a
validateto clamp writes. - Server-owned + RPCs — move the decision server-side, expose it as an RPC.
- Server tick — the server simulates continuously in
tick(state, dt).
Rule of thumb: if a lie about this value would ruin the game, the server owns it. Everything else can stay on the client.
Transferring ownership
Ownership can move at runtime — handing a dragged object between players, or pulling a contested entity to the server to resolve a conflict:
state.pieces[id].owner = ctx.clientId; // pick up
state.pieces[id].owner = SERVER; // let the server settle it Next steps
Placeholder content.