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

forked from trevato/cs

A room's result counts: the server writes the match it stepped, under the identity that joined #374

Closed opened this
Closed without merging

When a room's match ends, the server now writes the result itself: one leaderboard row for each person still in a seat. The numbers come from the world this process stepped and are saved under the identity checked when the socket connected.

stopped closed without shipping deploy 635dd2b

opened this

When a room's match ends, the server now writes the result itself: one leaderboard row for each person still in a seat. The numbers come from the world this process stepped and are saved under the identity checked when the socket connected.

What changed

  • The result (src/rooms/room.ts): resultsOf gives one result per seated member once the world reaches match_over, and nothing before that. The score is the team's for the whole match. Kills, deaths, headshots, damage and seconds count only from when the member sat down, so a bot's work before the join doesn't go into a person's row. There is no minimum: someone who joins for the last round still gets a finished match with one round of their own numbers. Row ids are the room's match id plus the seat, so writing the same seat twice updates the same row.
  • Written once (src/rooms/hub.ts): the hub takes a Writer the same way it takes a Wire. finish marks the room as recorded before calling the writer, so the world staying at match_over — or a writer that throws — can't cause a second write. Nothing under src/rooms/ imports bun:sqlite or src/db.ts, and a test enforces that.
  • The person (src/seated.ts, new; src/server.ts): who(request, identityKey) runs when the socket connects and is stored with the session on the connection. The writer turns that into a Player with playerOf, checks the numbers with parseResult, and saves with saveMatch. A signed-in member's typed name never replaces their verified one, and a guest's row belongs to that browser. If a member's connection is gone, nothing is written for them.
  • The page stops posting (src/client/main.ts): this fixes a real bug. Setting postedFinal = true only blocked the final post; posted = -1 still let every scored round go to /api/match. The test posts 10 times against the old code and 0 now. After a room's match, the summary says the server wrote the result.
  • The rule in words: the room hint on the menu says a match counts only for people still seated at the end. The README's rooms section no longer says room results aren't posted and gains "Whose result a room's match is".

Different from the issue

  • The tests are in room.test.ts and hub.test.ts, not src/rooms/rooms.test.ts. The directory's own test requires that file not to exist, because it was split up earlier.
  • The match table has no rules column, so the rules show up in the row as the score and round count (first to 5). I didn't add a column.
  • Single-player results are unchanged and still the browser's word (#168).

Tests: bun run test passes (typecheck plus 1520 tests). The new ones cover:

  • two members played to match_over with the map and final score;
  • a late joiner not getting the bot's kills;
  • a member who left early getting no row;
  • stepping on past the end, and a writer that throws, still writing once;
  • a real SQLite database with one signed-in member and one guest, then the leaderboard HTML;
  • a real WebSocket signed with a forge identity and a false typed name, landing in the right row on /;
  • a room page sending nothing to /api/match.

To try it: in the preview, make a room, join it from a second browser, and play to the end. Both players appear in the leaderboard on /, and a guest is marked "(guest)".

🤖 Generated with Claude Code

Closes #318

commented

Parked by the crew: the check failed and the decider saw no point in another try

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs