/** * How often the arena speaks. * * The simulation is 30Hz either way; what is decided here is how much of it * the browser is told about, which is the difference between a body drawn * from fifteen samples a second and one drawn from thirty. */ import { expect, test } from "bun:test"; import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; process.env.FORGE_APP_DATA ??= mkdtempSync(join(tmpdir(), "app-")); const { advance, attach, broadcastEvery, match, release, seatCount, snapshot } = await import("./arena.ts"); test("a small room hears every tick, a full one every other", () => { expect(broadcastEvery(0)).toBe(1); expect(broadcastEvery(1)).toBe(1); expect(broadcastEvery(2)).toBe(1); expect(broadcastEvery(3)).toBe(2); expect(broadcastEvery(10)).toBe(2); }); /** A seat that only counts what it was sent. */ function listen(session: string) { const got: string[] = []; attach(session, match().players[0]!.id, `test-${session}`, session, (data) => got.push(data)); return got; } // The arena is one match shared by the whole test run, and another file may // be holding a seat of its own while this one runs; what is checked is that // the loop broadcasts at exactly the cadence the room it is in asks for. test("the loop sends at the cadence the seat count asks for", () => { const one = listen("rate-a"); const two = listen("rate-b"); const every = broadcastEvery(seatCount()); advance(1); // Thirty ticks to the second, one snapshot every `every` of them. expect(one.length).toBe(30 / every); expect(two.length).toBe(one.length); release("rate-a", "rate-a"); release("rate-b", "rate-b"); }); test("a full room halves the cadence rather than multiplying the traffic", () => { const seats = ["rate-c", "rate-d", "rate-e"].map(listen); expect(seatCount()).toBeGreaterThan(2); advance(1); for (const got of seats) expect(got.length).toBe(15); for (const session of ["rate-c", "rate-d", "rate-e"]) release(session, session); }); test("a snapshot stays small enough to send thirty times a second", () => { // The trade is only worth making if the payload is small. Measured over a // live round it is 3.2-4.5 KiB a seat, mean 3.4 — a hundred KiB a second // at 30Hz, and at most two seats are sent that much. This is the number // the pull quotes, so it is checked here rather than assumed; the ceiling // is loose enough not to fail on a busy tick and tight enough that a // snapshot which doubled in size would say so. for (let i = 0; i < 40 && match().phase !== "live"; i++) advance(1); const bytes = Buffer.byteLength(snapshot(match().players[0]!.id)); expect(bytes).toBeGreaterThan(200); expect(bytes).toBeLessThan(8000); });