Presence & lifecycle

A room is a live membership. irt.io gives you the join/leave lifecycle on the server and a connection state on the client, so “who’s here?” and “am I connected?” are never guesswork.

Server lifecycle

export default defineRoom(schema, {
  onJoin(state, ctx) {
    state.players[ctx.clientId] = { name: ctx.meta.name, owner: ctx.clientId };
  },
  onLeave(state, ctx) {
    delete state.players[ctx.clientId];
  },
});

ctx carries the stable clientId, any meta you passed to joinRoom, and helpers for ownership and broadcasts.

Client connection state

const room = await joinRoom(schema);

room.onStatus((status) => {
  // 'connecting' | 'live' | 'reconnecting' | 'closed'
  banner.set(status);
});

Brief drops reconnect automatically and replay missed diffs. A client that stays gone past the grace window fires onLeave server-side.

Presence metadata

For lightweight “who’s typing / who’s ready” signals that don’t belong in game state, use presence — ephemeral per-client data that isn’t persisted:

room.presence.set({ typing: true });
room.presence.subscribe((all) => renderTypingIndicators(all));

Grace, resume, and idle

SettingDefaultMeaning
graceMs15000how long a dropped client can resume its seat
idleMs300000when an empty room suspends (and stops billing)

Next steps

Placeholder content.