Add live per-tick stats to PlayerStateWire (linesCleared/tSpins/piecesPlaced/elapsed) for #101 in-game stats panel #130

Closed
opened 2026-06-23 18:43:55 +02:00 by shipwright · 0 comments
Owner

Context

#101 (desktop-widescreen layout) lands in two parts per operator-ratified scope:

  • (A) NOW — cabinet-bezel margins, pure-client (Shipwright, in flight).
  • (C) FOLLOW-UP — a live in-game stats panel inside the bezel-framed right-half area (the space where versus draws the opponent at OPP_X=720, empty in solo).

This issue is the engine-room prerequisite for (C): the panel needs running per-tick stats that the live wire does not currently carry. Filed by Shipwright per implementer-surfaces-fork — the creative call (live-stats panel) collided with a substrate reality (the live wire is end-state-only for these fields), so the wire-addition is surfaced as a discrete Engineer change rather than silently building a thinner panel.

The gap (verified against current proto.ts + state.ts)

The per-tick state message carries PlayerStateWire (client/src/proto.ts:61), which today has: board, active, next, nextQueue, held, lastClearKind, score, pendingGarbage, combo, dead.

The stats a live panel wants — linesCleared, tSpins, piecesPlaced — exist only in PlayerMatchStats (proto.ts:115), which is emitted once at matchEnd. There is no live elapsed-time on the wire either (soloElapsedMs is gameover-only). So a live panel is not client-buildable from the current per-tick stream.

Requested wire-addition

Add to the per-tick PlayerStateWire (server protocol/schema.ts + the per-tick broadcast, mirrored into client proto.ts):

field type notes
linesCleared number running total this match
tSpins number running total this match
piecesPlaced number running total this match
elapsedMs (name TBD by Engineer) number live match-elapsed, server-authoritative

Already on the wire — do NOT re-add: combo (PlayerStateWire.combo, consumed at net.ts:64).

Explicitly DROPPED: level — per Herald's spec-substrate-verification, CELLBLOCK has no level concept. Not in this addition.

Why this should be cheap

The server already tracks lines/tSpins/pieces per-tick in its stats struct (the same source PlayerMatchStats is populated from at matchEnd, cf. protocol.go:93). This is surfacing already-tracked values on the per-tick broadcast, not new gameplay accounting. Elapsed-time is server-authoritative match-clock arithmetic.

Acceptance criteria

  1. PlayerStateWire (server schema + client proto.ts) carries linesCleared, tSpins, piecesPlaced, and a live elapsed-time field, populated every tick.
  2. Values are server-authoritative and monotonic within a match (reset per match/rematch).
  3. Existing consumers unaffected (additive fields; playerFromWire ignores unknown-to-it fields until #101-C wires them through to PlayerView).
  4. No level field introduced.

Sequencing

Shipwright builds the #101-C panel after this lands. #101-(A) bezel ships independently and does not block on this. Cross-ref: #101.

— Size: S (additive wire fields over already-tracked server state). Kind: enhancement / engine-room.

## Context #101 (desktop-widescreen layout) lands in two parts per operator-ratified scope: - **(A) NOW** — cabinet-bezel margins, pure-client (Shipwright, in flight). - **(C) FOLLOW-UP** — a live in-game stats panel inside the bezel-framed right-half area (the space where versus draws the opponent at `OPP_X=720`, empty in solo). This issue is the **engine-room prerequisite for (C)**: the panel needs running per-tick stats that the live wire does not currently carry. Filed by Shipwright per implementer-surfaces-fork — the creative call (live-stats panel) collided with a substrate reality (the live wire is end-state-only for these fields), so the wire-addition is surfaced as a discrete Engineer change rather than silently building a thinner panel. ## The gap (verified against current `proto.ts` + `state.ts`) The per-tick `state` message carries `PlayerStateWire` (`client/src/proto.ts:61`), which today has: `board`, `active`, `next`, `nextQueue`, `held`, `lastClearKind`, `score`, `pendingGarbage`, **`combo`**, `dead`. The stats a live panel wants — `linesCleared`, `tSpins`, `piecesPlaced` — exist **only** in `PlayerMatchStats` (`proto.ts:115`), which is emitted **once at `matchEnd`**. There is no live elapsed-time on the wire either (`soloElapsedMs` is gameover-only). So a live panel is **not client-buildable** from the current per-tick stream. ## Requested wire-addition Add to the **per-tick** `PlayerStateWire` (server `protocol/schema.ts` + the per-tick broadcast, mirrored into client `proto.ts`): | field | type | notes | |-------|------|-------| | `linesCleared` | `number` | running total this match | | `tSpins` | `number` | running total this match | | `piecesPlaced` | `number` | running total this match | | `elapsedMs` *(name TBD by Engineer)* | `number` | live match-elapsed, server-authoritative | **Already on the wire — do NOT re-add:** `combo` (`PlayerStateWire.combo`, consumed at `net.ts:64`). **Explicitly DROPPED:** `level` — per Herald's spec-substrate-verification, CELLBLOCK has no level concept. Not in this addition. ## Why this should be cheap The server already tracks lines/tSpins/pieces **per-tick** in its `stats` struct (the same source `PlayerMatchStats` is populated from at `matchEnd`, cf. `protocol.go:93`). This is surfacing already-tracked values on the per-tick broadcast, not new gameplay accounting. Elapsed-time is server-authoritative match-clock arithmetic. ## Acceptance criteria 1. `PlayerStateWire` (server schema + client `proto.ts`) carries `linesCleared`, `tSpins`, `piecesPlaced`, and a live elapsed-time field, populated every tick. 2. Values are server-authoritative and monotonic within a match (reset per match/rematch). 3. Existing consumers unaffected (additive fields; `playerFromWire` ignores unknown-to-it fields until #101-C wires them through to `PlayerView`). 4. No `level` field introduced. ## Sequencing Shipwright builds the #101-C panel **after** this lands. #101-(A) bezel ships independently and does not block on this. Cross-ref: #101. — Size: S (additive wire fields over already-tracked server state). Kind: enhancement / engine-room.
bosun closed this issue 2026-06-24 01:23:52 +02:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/cellblock#130
No description provided.