npm install -E @plot/handlerdefineRoom
defineRoom<S, M = unknown>({
initialState: S,
tickRate?: number, // 0–30 Hz; 0 disables onTick
onCreate?(ctx),
onJoin?(player, ctx),
onMessage?(player, msg: M, ctx),
onLeave?(player, ctx),
onTick?(ctx),
onTimer?(payload, ctx),
})player is { id: string; joinedAt: number }.
HandlerContext
| Member | Type / signature | Notes |
|---|---|---|
ctx.state | S | Authoritative state; mutate freely. |
ctx.roomCode | string | This room’s code. |
ctx.appId | string | The app this room belongs to. |
ctx.region | string | The region the room is running in. |
ctx.players | Player[] | Current players. |
ctx.firstPlayer | Player | undefined | Convenience. |
ctx.broadcast(channel, data) | void | Send to all. |
ctx.sendTo(playerId, channel, data) | void | Send to one. |
ctx.kick(playerId, reason?) | void | Remove a player. |
ctx.profile.get/update | — | Profiles. |
ctx.leaderboard(name).submit/top/around | — | Leaderboards. |
ctx.save.get/put/delete/list | — | Save slots. |
ctx.replay.enabled() / append(event) | — | Replays. |
ctx.assets.url(path) | string | Public immutable CDN URL, built offline. Assets. |
ctx.assets.signedUrl(path, { ttl }) | Promise<string> | Short-lived signed URL for a private asset. |
ctx.assets.list(prefix?) | Promise<AssetMeta[]> | The app’s asset manifest. |
ctx.ai.npc(id).say({ from, text }) | Promise<{ requestId }> | Request an NPC line; arrives via onAiResult. AI NPCs. |
ctx.ai.npc(id).forget(key?) | Promise<void> | Reset a conversation’s memory. |
onAiResult(result, ctx) | callback | Room def callback: an NPC completion is ready. |
ctx.scheduleTimer(payload, delayMs) | string | Durable timer; returns id. |
ctx.cancelTimer(id) | void | Cancel a timer. |
ctx.rewindTo(targetTs, cb) | Promise<T> | Lag compensation. |
ctx.log / warn / error | void | Structured logging. |
Execution model
Handlers run in an isolated tenant Worker (Workers for Platforms). The handler
cannot call back into the room — every effect is expressed through ctx and
applied by the platform after the call returns. This keeps tenant code sandboxed
and lets the same handler run client-side for prediction.
Last updated on