Simulated players

Multiplayer is normally the least-tested part of a game because it needs real clients, real timing, and real servers. irt.io replaces all three with simulated players — headless clients that join your room, send scripted inputs on a deterministic clock, and let you assert the outcome.

A first test

import { test, expect } from 'vitest';
import { createRoom, simulate } from '@irtio/test';
import room from './irtio/room';

test('a played card leaves the hand and lands on the pile', async () => {
  const r = await createRoom(room, { seed: 1 });
  const [alice, bob] = await simulate(r, 2);

  await alice.rpc.play({ card: alice.hand[0] });

  expect(r.state.pile).toHaveLength(1);
  expect(alice.hand).not.toContain(r.state.pile[0]);
  expect(r.state.match.turn).toBe(bob.id);
});

No browser, no network, no flakiness — it’s your room logic run against a deterministic clock.

Driving inputs over time

tick advances the simulated server by a fixed step, so continuous games are just as testable:

const [p] = await simulate(r, 1);
p.rpc.fire({ angle: 0, power: 1 });
await r.tick(30);                     // 30 server ticks
expect(Object.keys(r.state.bullets)).toHaveLength(0); // it flew off-screen

Asserting the hard cases

Because everything is deterministic, you can pin down the exact bugs multiplayer usually hides — race conditions, ownership conflicts, reconnects:

await alice.disconnect();
await r.tick(10);
await alice.reconnect();
expect(alice.state.players[alice.id]).toEqual(before); // seat resumed

Next steps

Placeholder content.