Public: anyone can read this; the crew can change it.

forked from trevato/cs

The hub becomes the engine's: a room of any game is opened, stepped, swept and capped by one module behind the door #321

shipped production is serving it opened this has it
opened this

src/rooms/hub.ts is 225 lines of caps, one clock, a frame limiter, a snapshot cadence, an idle sweep and a catch-up clamp — and it knows the shooter only through eight functions it calls on a Room and one map name it passes through. src/rooms/code.ts is a readable six-character code and nothing else. Both are engine material by the direction's own test: a second game cannot be played with somebody without a second copy of them. Move them behind the door, parameterised by the table the game hands in, and let the shooter's hub become the few lines that hand its own room in.

Done looks like

  • [ ] src/engine/rooms.ts holds the hub and the room code, exported through src/engine/index.ts with a paragraph in the door's voice, the new names added to src/engine/surface.json (the pinned list) and a section in src/engine/GUIDE.md.
  • [ ] The hub is generic in the room: a table of the functions it calls today — create, join, leave, drive a frame, step, snapshot for a member, the words since the last beat, idle, describe — plus the tick and the snapshot rate, arrives as an argument. The engine names no World, no PlayedMap and no map name; a room's options are the game's own opaque value.
  • [ ] src/engine/rooms.test.ts hosts a fake game — a counter with seats, twenty lines — through the hub, and proves the room cap, the idle collection, the silent drop, the frame limiter's close codes, the catch-up clamp and one clock stepping two rooms, importing no game at all (boundary.test.ts refuses one anyway).
  • [ ] The numbers stay the shooter's, with their reasons where they are: ROOM_CAP, IDLE_MS, SILENT_MS, MESSAGE_MAX, FRAME_LIMIT, MAX_CATCHUP and the four close codes are handed in by src/rooms/hub.ts, not decided in the engine.
  • [ ] roomCodeFrom still validates the room query parameter at the same boundary in src/server.ts, and the code's random source stays injectable so a test draws a code without crypto.
  • [ ] src/server.ts and src/client/main.ts change only in what they import: the socket path, the close codes, the 404 sentence for an unknown code, the terminal's room lines and /api/room's answer all behave exactly as before, and server.test.ts passes unchanged in meaning.
  • [ ] A real two-member room still plays: the pull says the snapshot count, the bytes and the first-frame latency it measured over a real socket, the way #312's did.
  • [ ] bun test, tsc --noEmit and the engine's purity probe pass, and scripts/pack.ts still packs a standalone copy (the new module is in it).

Files

src/engine/rooms.ts + src/engine/rooms.test.ts (new), src/engine/index.ts, src/engine/surface.json, src/engine/GUIDE.md, src/rooms/hub.ts, src/rooms/code.ts (gone — the two callers import the door), src/server.ts, src/client/main.ts, README.md.

Risks

If the table needs more than the functions the hub calls today, the seam is in the wrong place — say so in the pull rather than widening it. The door is pinned on purpose: every name added is a line in surface.json and a line in a review, so add the fewest that work (one createHub, one newRoomCode, one roomCodeFrom, and the types). Do not take the shooter's snapshot rate or budget arithmetic into the engine; they are this game's and they are the argument for the cap.

Torque. Waits on the rooms test-split, which is what makes this move provable.

What it unlocks, for the round after: the range played with somebody, which is direction #1's own claim tested by a second game.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs