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

A spectator's snapshot has no `me`, and the client throws on it #13

Merged opened this in answer to

shipped production is serving it deploy b42aa0d

opened this

Pushed. The branch is green: 219 tests, including the new ones, and the server boots and hands a live no-cookie socket the spectator snapshot.

A connection that gets no seat — no session cookie, or a second client from an address that already holds one — now draws the watching view instead of throwing. static/val.js reached into snap.me on the way in (feed(timeline, data.me.id, …)), so a spectator's first frame threw and the page stopped drawing; the new static/watch.js recognises a seatless snapshot, turns it into the lines it can honestly show, and draws them on a flat panel with no map and no bodies. val.js takes that path on the way in, stops sending input it has nothing to drive, refuses pointer lock, and blanks the panels a seat owns (kit, roster, the two team tables) rather than leaving the server-rendered first paint to go stale.

Two small server changes came with it, both argued for by the issue's own description of the view: the spectator snapshot now carries the kill feed (names and a gun, never a place — the same shape a seat is sent, and the existing "no coordinate anywhere" assertion still holds), and a why of "address" or "cookie", because the two reasons have different answers for the person reading them. The team scoreboards are deliberately not sent to a watcher — the snapshot has no board, and adding per-player rosters to a sideless view is a call for whoever drew the spectator line, not for this fix — so those tables are emptied rather than left stale.

To see it in thirty seconds: open the preview, then open it again in a second tab. The second tab shows red 0 — 0 blue, the clock, the round line, and the sentence "Somebody at this address is already playing — that tab holds the seat, and this one watches", with the kill feed filling in the top right as bots trade. A private window with cookies blocked gets the other sentence. The first tab keeps playing as before.

Tests: src/watch.test.ts covers the view's text and drawing (one background rect, no world), src/client.test.ts now feeds the bundled client the server's real spectatorSnapshot() and renders a frame — it fails with TypeError: undefined is not an object (evaluating 'data.me.id') against the old val.js, which is the bug in the issue — and src/server.test.ts asserts the reason and the feed over a real socket. I could not click through a browser from here; the client was exercised through the repository's own stub-DOM harness against the live server, and the server was run and queried directly.

🤖 Generated with Claude Code

Closes #12

Sign in to join this conversation.

↑↓ navigate ↵ open esc close

Keyboard shortcuts