feat(trails): render authoritative player route trails
This commit is contained in:
@@ -3436,7 +3436,7 @@ resources.
|
||||
|
||||
## Milestone 030 — Explicit session exit and slot release
|
||||
|
||||
**Status:** `IN_PROGRESS`
|
||||
**Status:** `COMPLETE`
|
||||
|
||||
### Objective
|
||||
|
||||
@@ -3644,3 +3644,207 @@ observer departure or a lobby departure must not reset gameplay.
|
||||
flow in the browser and confirmed that the issue was fixed: a remaining
|
||||
participant is returned from the result modal to a usable waiting lobby when
|
||||
the other player explicitly leaves.
|
||||
|
||||
## Milestone 031 — Authoritative persistent player trails
|
||||
|
||||
**Status:** `IN_PROGRESS`
|
||||
|
||||
### Objective
|
||||
|
||||
Show a persistent dotted record of every successful route used by each player
|
||||
during the current match, using the established yellow and blue ownership
|
||||
colors and a mixed two-color treatment for shared passages. Each player may see
|
||||
trail data only inside the current server-approved visibility projection, while
|
||||
observers may see both complete trails. Trails must survive refresh and
|
||||
reconnection without adding client requests or materially increasing ESP,
|
||||
network, or rendering load.
|
||||
|
||||
### Scope
|
||||
|
||||
- Do not start implementation until every preceding milestone is `COMPLETE`.
|
||||
Milestone 030 is `IN_PROGRESS` when this plan is created, so this milestone
|
||||
must remain `NOT_STARTED` until Milestone 030 is complete.
|
||||
- Keep trail ownership authoritative on the ESP. A browser-only history cannot
|
||||
provide consistent trails to another player and observers after refresh or
|
||||
reconnection, and must not be used as the source of truth.
|
||||
- Record only accepted authoritative player transitions between cells. Mark
|
||||
ordinary successful moves and the successful fifth-impact movement through a
|
||||
newly opened hedge. Do not mark blocked, rejected, rate-limited, stale,
|
||||
queued-but-superseded, or otherwise unsuccessful movement attempts.
|
||||
- Represent an undirected traversed cell edge with a compact two-bit ownership
|
||||
mask: Player 1, Player 2, both, or neither. Use a fixed-size per-match layout
|
||||
derived from the bounded maze dimensions; do not allocate per step, store an
|
||||
ordered movement history, or create a collection that grows with match
|
||||
duration.
|
||||
- Preserve the trail for the complete current match, including countdown-to-
|
||||
active lifecycle, pause, normal reconnect, and page refresh. Clear it exactly
|
||||
when authoritative match state is reset, cancelled, replaced by a new match,
|
||||
or cleared by device reboot.
|
||||
- Piggyback compact trail ownership on the existing role-appropriate view
|
||||
projection and sequencing path. Do not add polling, a new browser request,
|
||||
an extra message per movement, or a second trail-specific broadcast.
|
||||
- Send player clients only the trail edges whose endpoints and passage are
|
||||
inside their current authorized projection. Never serialize trail
|
||||
coordinates outside the current visibility radius, and do not let the
|
||||
browser retain or reconstruct hidden maze geometry from past projections.
|
||||
- Send observers the complete authoritative trail together with their existing
|
||||
full-map projection. Preserve observer read-only behavior and existing
|
||||
snapshot chunk, message-size, queue, and backpressure bounds.
|
||||
- Reconstruct the visible trail entirely in the frontend from each complete
|
||||
server-approved projection. Prefer a fixed small number of batched SVG paths
|
||||
or an equivalently bounded drawing layer rather than one DOM element per dot,
|
||||
step, or historical event.
|
||||
- Render a dotted yellow route for Player 1 and a dotted blue route for Player
|
||||
2. When both players used the same passage, render an unmistakable mixed
|
||||
yellow-and-blue dotted pattern on the same centerline rather than overwriting
|
||||
one owner or blending them into an unrelated third color.
|
||||
- Draw trails along traversable passage centers below players, keys, artifacts,
|
||||
exits, doors, feedback, and controls but above the quiet floor. Trails must
|
||||
not resemble walls, obscure objects, imply that a closed hedge is passable,
|
||||
or expose a breakthrough until its opened edge is present in the same
|
||||
authoritative projection.
|
||||
- Keep current-player and other-player ownership understandable without color
|
||||
alone by using a documented dot rhythm, outline, alternation, or equivalent
|
||||
secondary cue consistent with the established player icon system. Shared
|
||||
edges must remain recognizable in grayscale and at supported cell sizes.
|
||||
- Preserve server-authoritative movement, fog of war, match results, explicit
|
||||
session exit, reconnect behavior, compact controls, connection-quality
|
||||
sampling, reduced-motion behavior, offline operation, Russian player-facing
|
||||
copy, and firmware and LittleFS budgets.
|
||||
|
||||
### Deliverables
|
||||
|
||||
- Updated trail-state, visibility, protocol, lifecycle, ownership, layering,
|
||||
and responsive-rendering contracts in `MVP.md`, `ARCHITECTURE.md`,
|
||||
`docs/PROTOCOL.md`, `docs/UI_STYLE.md`, and `docs/PLAYER_UI.md`.
|
||||
- A fixed-size authoritative two-player traversed-edge representation with
|
||||
deterministic initialization, successful-move updates, reconnect retention,
|
||||
and match-reset cleanup.
|
||||
- A compact backward-safe view encoding that carries only role-authorized
|
||||
trail ownership inside existing view chunks and broadcasts.
|
||||
- A bounded player and observer trail renderer with yellow, blue, and mixed
|
||||
two-color dotted treatments and no per-move accumulation of DOM nodes,
|
||||
timers, animations, or client history.
|
||||
- Deterministic game, protocol, visibility, serialization, browser, rendering,
|
||||
lifecycle, reconnect, backpressure, size, and regression tests.
|
||||
- Before-and-after maximum-session traffic, queue, heap, render-time,
|
||||
compressed-web-asset, firmware, LittleFS, and representative physical-device
|
||||
visual-verification evidence.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
1. Every accepted authoritative in-maze move marks exactly one undirected edge
|
||||
for the moving player's ownership. Ordinary passage moves and a successful
|
||||
hedge breakthrough are marked; blocked, rejected, stale, duplicate,
|
||||
rate-limited, and superseded attempts leave the trail unchanged.
|
||||
2. Trail storage has a documented compile-time maximum derived from the bounded
|
||||
maze cell count and consumes a fixed number of bits per possible canonical
|
||||
edge and player. Repeated traversal, loops, backtracking, and arbitrarily long
|
||||
matches cannot grow heap use or trail state.
|
||||
3. Player 1 traversal produces the established yellow dotted treatment, Player
|
||||
2 traversal produces the established blue dotted treatment, and traversal
|
||||
of the same edge by both players produces one clearly mixed yellow-and-blue
|
||||
pattern that preserves both ownership meanings without a third blended
|
||||
color.
|
||||
4. A player projection contains and renders trail ownership only for passages
|
||||
fully inside that player's current server-approved visibility. It contains
|
||||
no coordinates, ownership bits, inferred continuation, or retained drawing
|
||||
outside the current projection, including edges previously visible to that
|
||||
browser.
|
||||
5. Each player sees both players' trails when those used passages are inside the
|
||||
current permitted projection. The opponent trail cannot reveal the
|
||||
opponent's current or historical route outside that projection.
|
||||
6. An observer receives and renders the complete authoritative trails of both
|
||||
players across the full maze, including shared edges, without gaining any
|
||||
command authority or changing player projections.
|
||||
7. Refreshing or reconnecting a player or observer during the same match
|
||||
reconstructs the correct role-authorized trail from the fresh authoritative
|
||||
view without browser-persisted trail history. Pause and resume do not add,
|
||||
remove, duplicate, or reorder trail ownership.
|
||||
8. A normal match reset, explicit player-leave cancellation, completed-match
|
||||
restart, new maze, or device reboot clears all trail ownership before the
|
||||
next match view. Session restoration alone never clears it.
|
||||
9. Trail data is carried only in the existing view sequencing and chunking
|
||||
flow. The implementation introduces no trail polling, new periodic request,
|
||||
per-move trail message, additional broadcast pass, or independent retry
|
||||
queue, and incomplete or stale view chunks never partially update a trail.
|
||||
10. Every encoded view remains within the existing 512-byte WebSocket message
|
||||
limit and published chunk-count and queue bounds. Deterministic worst-case
|
||||
player and observer projections with every edge owned by both players
|
||||
serialize successfully without hidden-state disclosure.
|
||||
11. The frontend uses a documented fixed upper bound of drawing layers and DOM
|
||||
nodes independent of move count. Projection replacement removes obsolete
|
||||
geometry, and refresh, reconnect, reset, leave, result, and screen changes
|
||||
leave no stale paths, timers, or retained hidden trail data.
|
||||
12. Trails follow the center of actual open or server-opened passages and sit
|
||||
below all players, objects, exits, hedge art, breakthrough effects, and
|
||||
controls. They do not create overflow, cover interaction targets, resemble
|
||||
walls, or make an intact hedge appear open.
|
||||
13. Yellow, blue, and shared trail states remain distinguishable at 34, 40, and
|
||||
46 CSS-pixel cells, in normal color and grayscale, and on supported narrow
|
||||
portrait, compact-landscape, tablet, and observer layouts without relying
|
||||
on sound, vibration, animation, or color alone.
|
||||
14. Under the maximum supported two-player/four-observer load and a worst-case
|
||||
fully marked maze, accepted movement latency, outgoing queue high-water,
|
||||
dropped player messages, heap minima, observer responsiveness, and browser
|
||||
rendering remain within the established Milestone 010 and Milestone 017
|
||||
budgets with no material regression attributable to trail processing.
|
||||
15. Automated game, protocol, browser, and host tests cover both owners, shared
|
||||
edges, repeated traversal, loops, breakthroughs, rejected movement,
|
||||
visibility clipping, observer completeness, chunk boundaries, worst-case
|
||||
size, refresh, reconnect, pause, reset, explicit leave, stale views,
|
||||
layering, cleanup, accessibility, and responsive layouts.
|
||||
16. All host and browser tests pass; compressed-web-asset validation passes;
|
||||
the payload remains within the 1.5 MiB web budget and LittleFS partition;
|
||||
and firmware remains within the factory partition.
|
||||
17. Physical-device testing confirms correct trail continuity during a complete
|
||||
two-player match, refresh and reconnect reconstruction, mixed shared
|
||||
passages, observer completeness, reset cleanup, and stable gameplay with no
|
||||
perceptible input delay, rendering stutter, disconnect, or accumulated
|
||||
resource use.
|
||||
18. The project owner confirms on representative player and observer displays
|
||||
that the dotted trails make used routes easy to follow, shared passages are
|
||||
clearly attributed to both players, fog of war remains intact, and the map
|
||||
stays readable and responsive, or explicitly approves a documented evidence
|
||||
deviation.
|
||||
|
||||
### Evidence
|
||||
|
||||
- On 2026-09-13, the project owner required Player 1 and Player 2 routes to
|
||||
remain visible until the end of the current match, survive refresh and
|
||||
reconnect, and include only successful authoritative movement.
|
||||
- The project owner required each player to see trail data only within the
|
||||
current permitted visibility projection, while observers see both complete
|
||||
trails, and selected a mixed two-color dotted treatment when both players use
|
||||
the same passage.
|
||||
- Planning analysis found that frontend-only state cannot consistently satisfy
|
||||
cross-client, observer, refresh, and reconnect requirements without violating
|
||||
fog of war. The milestone therefore limits ESP state to fixed-size per-edge
|
||||
ownership bits, piggybacks authorized data on existing projections, and
|
||||
delegates all trail drawing to a bounded frontend layer without additional
|
||||
requests or broadcasts.
|
||||
- Implementation and verification have not started, as requested by the
|
||||
project owner.
|
||||
- On 2026-09-13, implementation added a fixed 512-byte packed two-bit owner
|
||||
mask for the at-most 2,048 canonical maze edges. Accepted in-maze movement,
|
||||
including a completed hedge breakthrough, marks its canonical edge; rejected
|
||||
and outward moves do not. Each existing view cell carries only authorized
|
||||
east and south ownership masks, so no trail request, broadcast, or chunk type
|
||||
was added. The browser replaces one non-interactive SVG layer per projection
|
||||
with yellow, blue, and dual-rhythm shared dotted paths.
|
||||
- On 2026-09-13, `bash scripts/run_host_tests.sh` passed all suites, including
|
||||
authoritative shared-edge ownership and visibility coverage. The worst-case
|
||||
observer serialization remained bounded at 88 chunks and 501 / 512 bytes.
|
||||
`node --test test/web/test_player_app.js` passed 32 tests, including trail
|
||||
mask validation and bounded rendering treatment. `python3
|
||||
scripts/verify_web_assets.py` passed with deterministic payload
|
||||
`245f402b654fdec986974d62d7f7cd3190fd79293a36c7c1e289200127d98f73` at
|
||||
33,635 / 1,572,864 bytes. `/home/sasa/.platformio/penv/bin/pio run` passed
|
||||
with firmware at 1,030,784 / 2,097,152 bytes, and `/home/sasa/.platformio/
|
||||
penv/bin/pio run --target buildfs` passed at 2,031,616 / 2,031,616 bytes.
|
||||
- On 2026-09-13, trail presentation was softened without changing protocol or
|
||||
authority: stroke width is 0.065 cells, opacity is 58%, and both ownership
|
||||
rhythms use shorter, more widely spaced dots. `node --test
|
||||
test/web/test_player_app.js`, LittleFS build, and web-asset validation passed
|
||||
with payload `5546c680b4d527815381735ec1e9016e02f0002efa865cb09e45b84e8f697cec`
|
||||
at 33,638 / 1,572,864 bytes.
|
||||
|
||||
Reference in New Issue
Block a user