feat: implement safe leave, profile reset, and full game reset semantics
This commit is contained in:
+13
-1
@@ -38,7 +38,7 @@ Failure (maximum 160 encoded bytes):
|
||||
```
|
||||
|
||||
`code` is one of `MALFORMED_JSON`, `PAYLOAD_TOO_LARGE`, `INVALID_NAME`,
|
||||
`INVALID_ROLE`, `INVALID_MODE`, `INVALID_COORDINATE`, `UNAUTHORIZED`,
|
||||
`INVALID_ROLE`, `INVALID_MODE`, `INVALID_COORDINATE`, `UNAUTHORIZED`, `SESSION_INVALIDATED`,
|
||||
`NO_PLAYER_SLOT`, `NO_SPECTATOR_SLOT`, `FORBIDDEN_ROLE`, `WRONG_PHASE`,
|
||||
`NOT_YOUR_TURN`, `CELL_ALREADY_SHOT`, `STALE_GAME`, or `SERVER_BUSY`.
|
||||
`message` is Russian and at most 80 UTF-8 bytes.
|
||||
@@ -51,11 +51,14 @@ Failure (maximum 160 encoded bytes):
|
||||
| `GET /api/health` | 128 B target | diagnostics without secrets, 320 B; includes reset reason |
|
||||
| `POST /api/session/join` | 192 B body | `{name,requestedRole}`; token and role, 192 B |
|
||||
| `POST /api/session/resume` | 96 B body | `{token}`; role and state metadata, 192 B |
|
||||
| `POST /api/session/leave` | 80 B body | `{token,gameId}`; releases only that session |
|
||||
| `POST /api/session/profile-reset` | 80 B body | `{token,gameId}`; server release for local profile reset |
|
||||
| `POST /api/game/config` | 96 B body | `{token,gameId,mode}`; common envelope |
|
||||
| `POST /api/game/start` | 80 B body | `{token,gameId}`; common envelope |
|
||||
| `POST /api/game/shot` | 96 B body | `{token,gameId,x,y}`; common envelope |
|
||||
| `POST /api/game/rematch` | 80 B body | `{token,gameId}`; common envelope |
|
||||
| `POST /api/game/abort` | 80 B body | `{token,gameId}`; common envelope |
|
||||
| `POST /api/game/reset` | 80 B body | `{token,gameId}`; player-only full RAM reset |
|
||||
| `GET /api/state?version=N` | 128 B target | one role-safe state, 768 B |
|
||||
| `GET /api/statistics` | 128 B target | role and bounded match/cumulative counters |
|
||||
|
||||
@@ -63,6 +66,15 @@ The session token is in each POST body. For `GET /api/state`, it is supplied
|
||||
in `X-Session-Token`; absence creates a spectator-safe view. It is never a URL
|
||||
parameter.
|
||||
|
||||
`leave` and `profile-reset` release only the requesting session; profile data is
|
||||
cleared by the browser in Milestone 022. A player leaving an active match aborts
|
||||
that match and returns remaining valid players to the lobby. `game/reset` is
|
||||
available only to an authenticated player and atomically invalidates every
|
||||
session, clears match and cumulative statistics, and starts a fresh game
|
||||
generation. Successful recovery responses contain `resetReason` (`session_left`,
|
||||
`profile_reset`, or `game_reset`) and `generation`. Invalidated-token state
|
||||
polling returns `SESSION_INVALIDATED` with the same bounded recovery metadata.
|
||||
|
||||
## Role-safe state event
|
||||
|
||||
The HTTP state response and WebSocket `state` event use this single 768-byte
|
||||
|
||||
@@ -274,10 +274,13 @@ struct Board {
|
||||
| `GET` | `/api/info` | Состояние устройства и доступность мест без скрытых данных |
|
||||
| `POST` | `/api/session/join` | Вход по имени и желаемой роли |
|
||||
| `POST` | `/api/session/resume` | Восстановление роли по токену |
|
||||
| `POST` | `/api/session/leave` | Освобождение только текущей сессии |
|
||||
| `POST` | `/api/session/profile-reset` | Освобождение сессии перед локальной очисткой профиля |
|
||||
| `POST` | `/api/game/config` | Выбор режима игроком 1 |
|
||||
| `POST` | `/api/game/start` | Запуск готовой партии |
|
||||
| `POST` | `/api/game/shot` | Выстрел по координатам |
|
||||
| `POST` | `/api/game/rematch` | Подтверждение повторной игры |
|
||||
| `POST` | `/api/game/reset` | Аварийный полный сброс игровой памяти игроком |
|
||||
| `GET` | `/api/state?version=N` | Снимок разрешённого состояния и резервный опрос |
|
||||
| `GET` | `/api/health` | Проверка доступности сервера |
|
||||
|
||||
|
||||
@@ -73,6 +73,24 @@ paths:
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'413': { $ref: '#/components/responses/PayloadTooLarge' }
|
||||
/api/session/leave:
|
||||
post:
|
||||
summary: Release only the authenticated session
|
||||
requestBody:
|
||||
required: true
|
||||
content: { application/json: { schema: { $ref: '#/components/schemas/GameRequest' } } }
|
||||
responses: &recoveryResponses
|
||||
'200': { $ref: '#/components/responses/RecoveryAccepted' }
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'409': { $ref: '#/components/responses/Conflict' }
|
||||
'413': { $ref: '#/components/responses/PayloadTooLarge' }
|
||||
/api/session/profile-reset:
|
||||
post:
|
||||
summary: Release the authenticated session for a browser-local profile reset
|
||||
requestBody:
|
||||
required: true
|
||||
content: { application/json: { schema: { $ref: '#/components/schemas/GameRequest' } } }
|
||||
responses: *recoveryResponses
|
||||
/api/game/config:
|
||||
post:
|
||||
summary: Set the game mode (Player 1, lobby only)
|
||||
@@ -131,6 +149,18 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GameRequest'
|
||||
responses: *commandResponses
|
||||
/api/game/reset:
|
||||
post:
|
||||
summary: Player-only full in-memory game reset
|
||||
requestBody:
|
||||
required: true
|
||||
content: { application/json: { schema: { $ref: '#/components/schemas/GameRequest' } } }
|
||||
responses:
|
||||
'200': { $ref: '#/components/responses/RecoveryAccepted' }
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
'409': { $ref: '#/components/responses/Conflict' }
|
||||
'413': { $ref: '#/components/responses/PayloadTooLarge' }
|
||||
/api/state:
|
||||
get:
|
||||
summary: Get a complete role-safe state snapshot
|
||||
@@ -175,6 +205,11 @@ components:
|
||||
schema: { $ref: '#/components/schemas/Token' }
|
||||
description: Omit only when a spectator-safe public view is intended.
|
||||
responses:
|
||||
RecoveryAccepted:
|
||||
description: Recovery completed or an idempotent retry observed the same completed recovery.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/RecoveryAccepted' }
|
||||
CommandAccepted:
|
||||
description: Command accepted by the authoritative application layer.
|
||||
content:
|
||||
@@ -269,6 +304,13 @@ components:
|
||||
- type: object
|
||||
required: [role]
|
||||
properties: { role: { $ref: '#/components/schemas/Role' } }
|
||||
RecoveryAccepted:
|
||||
type: object
|
||||
required: [ok, resetReason, generation]
|
||||
properties:
|
||||
ok: { type: boolean, enum: [true] }
|
||||
resetReason: { type: string, enum: [session_left, profile_reset, game_reset] }
|
||||
generation: { $ref: '#/components/schemas/GameId' }
|
||||
Info:
|
||||
type: object
|
||||
required: [ok, phase, gameId, version, player1Available, player2Available, player1Name, player2Name, spectatorsAvailable]
|
||||
|
||||
Reference in New Issue
Block a user