feat(session): add explicit leave and immediate slot release
This commit is contained in:
@@ -3265,7 +3265,7 @@ indicator.
|
||||
|
||||
## Milestone 029 — Low-overhead live connection quality
|
||||
|
||||
**Status:** `NOT_STARTED`
|
||||
**Status:** `COMPLETE`
|
||||
|
||||
### Objective
|
||||
|
||||
@@ -3412,5 +3412,205 @@ resources.
|
||||
measured ESP handling interval and an honestly labelled communication
|
||||
remainder, while keeping the displayed headline value equal to the complete
|
||||
browser-to-ESP application round trip.
|
||||
- Implementation and verification have not started, as requested by the
|
||||
project owner.
|
||||
- On 2026-09-13, implementation began with a single in-flight browser
|
||||
heartbeat sampled no more often than every five seconds, a five-sample
|
||||
median, 15-second stale timeout, and explicit unknown, stale, and offline
|
||||
indicator states. Heartbeat acknowledgements now include bounded ESP-only
|
||||
`handlingMs`, and the browser derives the non-negative `Связь` remainder.
|
||||
- On 2026-09-13, `node --test test/web/test_player_app.js
|
||||
test/web/test_hedge_svg.js` passed all 33 tests, including bounded
|
||||
heartbeat correlation, malformed-field rejection, smoothing, stale state,
|
||||
indicator copy, and compact header placement.
|
||||
- On 2026-09-13, `bash scripts/run_host_tests.sh` passed. `python3
|
||||
scripts/verify_web_assets.py` passed with a deterministic 32,503 /
|
||||
1,572,864 byte compressed payload. `/home/sasa/.platformio/penv/bin/pio
|
||||
run` passed with firmware at 1,029,808 / 2,097,152 bytes and
|
||||
`/home/sasa/.platformio/penv/bin/pio run --target buildfs` passed with a
|
||||
2,031,616 / 2,031,616 byte LittleFS image.
|
||||
- Physical-device latency measurements and mobile and desktop visual
|
||||
acceptance were pending until the project-owner check below.
|
||||
- On 2026-09-13, the project owner manually checked the quality indicator and
|
||||
accepted `ESP: 0 мс` as the correct rounded display for the measured
|
||||
sub-millisecond heartbeat handling interval. The project owner found no UI
|
||||
issue and accepted the indicator on the available application screens.
|
||||
|
||||
## Milestone 030 — Explicit session exit and slot release
|
||||
|
||||
**Status:** `IN_PROGRESS`
|
||||
|
||||
### Objective
|
||||
|
||||
Let an authenticated player or observer deliberately leave through the
|
||||
top-right settings menu, revoke only that browser's session, immediately free
|
||||
its slot, and return it to the name-entry screen with its previous display name
|
||||
prefilled. A player leaving an unfinished match must authoritatively cancel
|
||||
that match and return every remaining participant to the lobby, while an
|
||||
observer departure or a lobby departure must not reset gameplay.
|
||||
|
||||
### Scope
|
||||
|
||||
- Do not start implementation until every preceding milestone is `COMPLETE`.
|
||||
Milestone 029 is `COMPLETE` when this plan is created; this milestone remains
|
||||
`NOT_STARTED` because implementation was not requested.
|
||||
- Add one clearly labelled Russian `Выйти из игры` action to the existing
|
||||
gear-triggered settings menu. Show it only while the browser owns an
|
||||
authenticated player or observer session; do not show it on the unauthenticated
|
||||
welcome and name-entry screen.
|
||||
- Require a modal confirmation before sending an exit command. For a player in
|
||||
countdown, active, finish-pending, or paused state, clearly warn that the
|
||||
current match will end for everyone and the player's slot will be freed. For
|
||||
a player in the lobby or an observer in any phase, explain that only this
|
||||
browser's place will be released.
|
||||
- Treat opening or cancelling the confirmation as browser-local UI only. While
|
||||
it is open, suppress input from that browser without pausing or otherwise
|
||||
mutating authoritative state; cancelling must restore the previous screen,
|
||||
focus, controls, and session unchanged.
|
||||
- Introduce a versioned authenticated explicit-leave protocol command and a
|
||||
correlated success response. It must be distinct from an unexpected socket
|
||||
disconnect, page reload, or transient connection loss, which retain the
|
||||
existing 60-second player reconnection contract.
|
||||
- On an accepted player leave during countdown, active, finish-pending, or
|
||||
paused state, stop all match timers, clear the current authoritative match,
|
||||
return the lobby to its waiting state, retain every other connected session
|
||||
and role, revoke the leaving session, free its player slot immediately, and
|
||||
broadcast the cancellation and fresh lobby state to the remaining players
|
||||
and observers.
|
||||
- On an accepted player leave while already in the waiting lobby or after a
|
||||
completed result, revoke only that player's session and free its slot. Do not
|
||||
reset an unfinished match because none is active in those phases.
|
||||
- On an accepted observer leave in any phase, revoke only that observer's
|
||||
session and free its observer slot. Never pause, reset, finish, restart, or
|
||||
otherwise mutate the match because an observer leaves.
|
||||
- Preserve the roles and sessions of all participants who did not leave. If
|
||||
Player 1 leaves, Player 2 remains in its existing slot and a later player may
|
||||
claim the newly free Player 1 slot and become the lobby owner under the
|
||||
existing ownership rule.
|
||||
- Invalidate the departed session token on the ESP before confirming success.
|
||||
The revoked token must never restore its former role, slot, position,
|
||||
inventory, or reconnect reservation, and a duplicate or replayed leave must
|
||||
be harmless.
|
||||
- After confirmed success, remove the session token from browser-local storage,
|
||||
clear all session, match, input, timer, heartbeat, animation, and pending
|
||||
command state, and show the welcome/name-entry screen without requiring a
|
||||
page reload.
|
||||
- Store the last successfully used display name separately from the temporary
|
||||
session token in a bounded browser-local value. Prefill it on return to the
|
||||
name-entry screen, but still apply normal name validation and duplicate-name
|
||||
rejection if the user tries to join again.
|
||||
- Do not claim that the slot was released before the ESP acknowledges the
|
||||
explicit leave. If the command cannot be delivered or confirmed, show a
|
||||
concise Russian error and retain honest reconnect behavior rather than
|
||||
silently turning a network failure into a successful exit.
|
||||
- Bound command size, correlation state, acknowledgement waiting, and cleanup.
|
||||
The leave path must not disclose hidden maze state, permit one participant to
|
||||
revoke another session, bypass observer read-only gameplay rules, or create
|
||||
a new unbounded queue or retry loop.
|
||||
- Preserve the compact header, live connection-quality indicator, Wi-Fi and
|
||||
preference actions, responsive layouts, offline operation, Russian
|
||||
player-facing copy, and firmware and LittleFS budgets.
|
||||
|
||||
### Deliverables
|
||||
|
||||
- Updated explicit-leave, match-cancellation, slot-release, session-revocation,
|
||||
and reconnect contracts in `MVP.md`, `ARCHITECTURE.md`, `docs/PROTOCOL.md`,
|
||||
`docs/UI_STYLE.md`, and `docs/PLAYER_UI.md`.
|
||||
- A bounded authenticated leave command and response implemented across the
|
||||
protocol parser, lobby, authoritative game lifecycle, gateway, and browser.
|
||||
- A settings-menu exit action with role- and phase-appropriate confirmation,
|
||||
failure handling, focus restoration, and successful return to the
|
||||
name-entry screen.
|
||||
- Separate bounded persistence for the last player display name and complete
|
||||
cleanup of the revoked temporary session.
|
||||
- Deterministic protocol, lobby, gateway, game-lifecycle, browser,
|
||||
accessibility, race, replay, reconnect, responsive-layout, and regression
|
||||
tests.
|
||||
- Host, browser, web-asset, firmware, LittleFS, maximum-session, and
|
||||
physical-device verification evidence.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
1. An authenticated player or observer sees exactly one `Выйти из игры` item
|
||||
in the gear menu on every applicable screen. An unauthenticated browser sees
|
||||
no exit item, and primary game controls remain outside the menu.
|
||||
2. Selecting the item opens a keyboard- and touch-accessible confirmation
|
||||
modal and sends no protocol command until explicit confirmation. Cancelling,
|
||||
pressing Escape, or activating the modal's cancel action leaves the session
|
||||
and authoritative state unchanged and safely restores focus.
|
||||
3. A player confirmation during countdown, active, finish-pending, or paused
|
||||
state explicitly says that the match will end for everyone and the slot will
|
||||
be freed. Confirmation produces exactly one authoritative cancellation,
|
||||
stops every relevant timer, clears the current match, and returns all
|
||||
remaining connected participants to a fresh waiting lobby.
|
||||
4. The leaving player's session is removed and its slot becomes available in
|
||||
the same accepted server transition. Every other player's and observer's
|
||||
session, role, and connection remain valid, and the resulting lobby snapshot
|
||||
consistently shows the newly free slot.
|
||||
5. A player leaving the waiting lobby or a completed result releases only that
|
||||
player's session and slot. It does not disconnect other participants or
|
||||
perform an additional match reset.
|
||||
6. An observer leaving during any phase releases only that observer session and
|
||||
observer slot. Match phase, players, timers, maze, objects, inventory, and
|
||||
results remain byte-for-byte unchanged except for the expected lobby
|
||||
connection revision or participant count.
|
||||
7. The explicit leave command authenticates the sending session and cannot
|
||||
name or revoke another token or slot. Duplicate, stale, reordered, malformed,
|
||||
unauthorized, and replayed requests cause no second cancellation, no extra
|
||||
slot release, and no unrelated state mutation.
|
||||
8. An accepted explicit leave never starts the 60-second reconnect reservation.
|
||||
A normal page reload, accidental socket loss, or unconfirmed exit continues
|
||||
to follow the existing reconnect and pause rules without behavioral change.
|
||||
9. Only after the correlated success response, the departing browser deletes
|
||||
its temporary session token, clears all role and gameplay state, stops all
|
||||
input, animation, heartbeat, reconnect, and pending-command work, and shows
|
||||
the welcome/name-entry screen without a full reload or stale map flash.
|
||||
10. The last successfully used display name survives separately from the
|
||||
revoked token and is prefilled in the name field after exit and on a later
|
||||
visit. It is bounded to the existing name limit, contains no token data,
|
||||
and receives the normal validation and uniqueness checks when reused.
|
||||
11. If the leave request cannot be sent, times out, loses its socket, or receives
|
||||
a rejection, the browser does not report a successful release. It presents
|
||||
concise Russian recovery information, retains bounded state, and neither
|
||||
retries indefinitely nor corrupts a later reconnect or successful leave.
|
||||
12. If Player 1 leaves, Player 2 remains in Player 2's slot and a new player can
|
||||
immediately claim the free Player 1 slot, becoming the lobby owner and
|
||||
gaining start authority only through the existing server rule. Equivalent
|
||||
behavior applies when Player 2 leaves.
|
||||
13. The exit item and confirmation fit supported narrow portrait,
|
||||
compact-landscape, tablet, and desktop layouts, respect safe areas, avoid
|
||||
page and map overflow, and remain distinguishable without relying only on
|
||||
color, sound, vibration, or animation.
|
||||
14. Automated tests cover every role and phase, confirmation and cancellation,
|
||||
authoritative match cancellation, retained participants, immediate slot
|
||||
reuse, token revocation, remembered-name prefill, disconnect races,
|
||||
idempotence, authorization, cleanup, accessibility, and responsive layout.
|
||||
15. 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.
|
||||
16. Physical-device testing confirms that a player can cancel an unfinished
|
||||
match and immediately release its slot, an observer can leave without
|
||||
affecting play, the remaining browsers receive the correct lobby state,
|
||||
and the departing browser returns to a prefilled name-entry screen without
|
||||
stale controls, delayed input, or reconnect side effects.
|
||||
17. The project owner confirms on representative mobile and desktop browsers
|
||||
that the exit action is easy to find but difficult to trigger accidentally,
|
||||
its consequences are clear, and all role-specific outcomes match the
|
||||
intended behavior, or explicitly approves a documented evidence deviation.
|
||||
|
||||
### Evidence
|
||||
|
||||
- On 2026-09-13, planning inspection confirmed that unexpected player socket
|
||||
loss currently pauses an active match and reserves the player session for a
|
||||
60-second reconnect, while observer disconnect does not affect gameplay.
|
||||
Therefore immediate voluntary slot release requires an explicit authenticated
|
||||
lifecycle command rather than client-only token deletion or socket closure.
|
||||
- On 2026-09-13, the project owner required a settings-menu exit action with a
|
||||
confirmation modal, complete session-token revocation, immediate slot
|
||||
release, and return to the name-entry screen with the previous player name
|
||||
prefilled.
|
||||
- The project owner also required an unfinished match to be cancelled for all
|
||||
participants when a player leaves, while a lobby player or observer departure
|
||||
must affect only the departing session.
|
||||
- On 2026-09-13, implementation began with an authenticated `leave` command,
|
||||
immediate server-side session revocation, player-match cancellation, and a
|
||||
browser confirmation flow that clears the temporary token only after the
|
||||
correlated acknowledgement.
|
||||
|
||||
Reference in New Issue
Block a user