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

Above two players, every enemy is drawn 42ms further in the past than it needs to be #31

Merged opened this in answer to

shipped production is serving it deploy b42aa0d

opened this

Pushed. The last step is the pull description itself, which needs nothing further from the tree.

Snapshots now go out every tick to every seat the room can hold. The old rule halved the rate at the third seat, and since the client's render delay follows the rate it measures, that seat drew every other body for everyone 42ms further in the past. The honest answer to the issue's question is that for this game the cadence is every tick always: the threshold now sits at ten seats, which is the whole room, and the comment on broadcastEvery in src/arena.ts carries the numbers so nobody has to re-derive them.

What to feel in the first ten seconds. Open the preview in three tabs from three addresses (or have two friends join), press F3 in one, and watch the readout: it stays at 30.0 Hz in · 45 ms behind when the third seat fills instead of falling to 15.0 Hz in · 87 ms behind. Peek a corner at a running bot: it is where the server has it 45ms ago, not 87.

Feel numbers, from the new harness in src/feel.test.ts (a real arena with N idle seats, the viewer's real snapshots over the same jittery wire as watchBots, warmed through the buy phase, the first three seconds of the rush measured):

roomcadencesnapshots in 3sdelay in forceper-frame step spreadrestartsframes guessed past the newest sample while the delay settled
4before (every other tick)4586.4ms9.5%018
4after (every tick)9145.0ms1.9%00
8before4586.4ms17.2%018
8after9145.0ms16.3%00

So other players come 41ms closer to the present with no new jitter: steps in the room of four go from 9.5% to 1.9% spread, and in the room of eight they are no worse. The 16% at eight is the same in both columns and is the server's own motion: with eight idle humans only two bots move, and the one measured threads past standing bodies at spawn. One bonus the table shows: a timeline meeting a 15Hz stream spends two seconds drawing bodies past the newest sample while its delay creeps from 45ms to 87ms, which is what a room felt at the moment its third seat filled. That transient is gone because the rate no longer changes.

Cost, measured on my machine, not the box (6-vCPU EPYC 9555P, bun 1.3; medians of seven 20-second runs of a seeded match, a never-sending run subtracted to isolate the snapshots). I could not reach the production box from the sandbox.

seatswhole loop, every tickof which snapshotsbytes out, live roundbytes out, whole match
41.3ms per second0.8ms210 kB/s340 kB/s
82.0ms per second1.7ms385 kB/s645 kB/s
102.1ms per second2.0ms465 kB/s790 kB/s

One snapshot is 6µs to build with the vision filter, one world tick 27µs. A full room is 0.2% of one core here; a box five times slower is at 1%. Bandwidth is the larger number: 6 Mbit/s out at ten seats, 80 kB/s to each seat over a whole match, since a buy-phase snapshot is larger than the 1.6 KiB live one.

What changed and why. src/engine/arena.ts gained one optional cadence on the game contract, defaulting to what it had; no val number went into the engine, and the engine's own test covers the hook. The val game is now a factory (valGame, makeValArena) so the harness can seat a room of its own without touching the shared match; the singleton exports are unchanged. src/arena.test.ts had two tests that pinned the old threshold and one comment quoting a 3.4 KiB snapshot from before the sticky board; those were the tests being wrong about the rule, and they now assert the new one. README updated to match. No client change was needed: interp.js follows the rate it measures, as the issue expected.

One unrelated note: a client test about the drawn body's speed failed once in a baseline run before I changed anything and passed on three reruns. The shared match is seeded from the clock, so it is order- or seed-dependent, and it is not touched here.

🤖 Generated with Claude Code

Closes #29

Sign in to join this conversation.

↑↓ navigate ↵ open esc close

Keyboard shortcuts