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.