Add a room rule
Start with the quickstart. Its scaffold gives each client a player record and lets that client update it. Now keep those cursors inside the canvas.
Clamp cursor positions
Replace the scaffold’s validate block in irtio/room.ts with:
validate: {
players(prev, next) {
return {
...next,
x: Math.max(0, Math.min(800, next.x)),
y: Math.max(0, Math.min(500, next.y)),
};
},
}, The server applies the corrected position and sends it to the clients. A cursor can move anywhere inside the canvas; it doesn’t need a speed limit.
Keep the scaffold’s onJoin reconnect guard. It prevents a resumed session from replacing the player’s existing record.
Test the boundary
Add this test to irtio/room.test.ts, using the imports already in that file:
test('cursor positions stay inside the canvas', async () => {
const t = await testRoom(room);
const [a, b] = await t.join(2, { role: 'player' });
const player = a.state.players.get(a.me)!;
player.x = 900;
player.y = -20;
a.flush();
await t.tick(2);
expect(t.state.players.get(a.me)?.x).toBe(800);
expect(t.state.players.get(a.me)?.y).toBe(0);
expect(b.state.players.get(a.me)?.x).toBe(800);
expect(a.state.players.get(a.me)?.y).toBe(0);
}); npx vitest run irtio/room.test.ts For a value the game decides, such as a score, use server-owned state and RPCs. See Room configuration for all room options and callbacks.