Files
2026-05-27 23:29:53 +01:00

121 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# game-server-engine-sessions — sessions + players (Wave B-2)
**Package**: `@suaveplan/game-server`
**Status**: PROPOSED
**Type**: feature (Wave B-2 migration slice)
- **Design**: see this file
- **Spec delta**: ./specs/game-server/spec.md
## Why
Wave A landed the foundations and Wave A interfaces (`ISessionManager`,
`IPlayerManager` et al). Wave B-1 shipped the engine top-level (GameEngine
+ GameLoop + PluginManager). Waves B-3 and B-4 shipped state + network.
What is missing: the session and player runtime that backs those Wave A
interfaces. Without it, the engine has nowhere to register a session or
track a connected player, and the `ISessionManager` /
`IPlayerManager` slots in `GameEngine` can only be filled by ad-hoc
stubs.
This slice (Wave B-2) ships five engine modules that round out the
runtime:
- `SessionLifecycle` — per-session FSM (`lobby → active → ended`) with
transition validation, audit history, and OTel dwell metrics.
- `SessionGraphNode` — internal mutable graph node implementing Wave A's
read-only `SessionNode` interface.
- `SessionTree` — hierarchical graph (`Map<id, node>` + `Set<rootId>`)
with O(1) lookups, Zod-validated boundaries, and typed event emission.
- `SessionStore` — `ISessionManager` implementation composing
`SessionTree` with an injectable `SessionPersistence` adapter for
durability.
- `PlayerManager` — `IPlayerManager` implementation with heartbeat-driven
liveness, reconnection grace window, and a timer-driven monitor that
routes through `TimersLike`.
This is a re-dispatch of an earlier B-2 attempt that branched from a
stale base and re-invented Wave A's interfaces. The new work builds on
top of the existing canonical interfaces and types.
## What changes
- ADDED: `SessionGraphNode` (`engine/session-node`) — internal mutable
graph node, structurally compatible with Wave A's `SessionNode`
interface; preserves timestamps on persistence round-trips.
- ADDED: `SessionTree` (`engine/session-tree`) — graph manager with
`createSession`/`destroySession`/`getStats`/`validateIntegrity` plus
the `attachRestored` seam used by `SessionStore.restore`.
- ADDED: `SessionStore` (`engine/session-store`) — `ISessionManager`
implementation with pluggable `SessionPersistence` (default in-memory).
- ADDED: `SessionLifecycle` (`engine/session-lifecycle`) — per-session
FSM with stable error codes for every rejection path.
- ADDED: `PlayerManager` (`engine/player-manager`) — `IPlayerManager`
implementation with injectable `Clock`/`TimersLike`/`EventBus`.
- ADDED: `GAME_SERVER.*` codes — `SESSION_NOT_FOUND`,
`SESSION_ALREADY_ENDED`, `INVALID_SESSION_TRANSITION`,
`DUPLICATE_PLAYER_IN_SESSION`, `INVALID_SESSION_DATA`,
`DUPLICATE_SESSION_ID`, `PARENT_SESSION_NOT_FOUND`,
`INVALID_SESSION_STORE_OPTIONS`, `INVALID_PLAYER_MANAGER_OPTIONS`,
`INVALID_HEARTBEAT_INTERVAL`, `PLAYER_DISCONNECTED`.
- ADDED: OTel instruments — `game_server.session.lifecycle_total`
counter, `game_server.session.dwell_ms` histogram,
`game_server.player.lifecycle_total` counter.
- ADDED: subpath exports `./engine/session-lifecycle`,
`./engine/session-node`, `./engine/session-store`,
`./engine/session-tree`, `./engine/player-manager`.
## Out of scope
- `GameEngine` re-wiring to consume `SessionStore` / `PlayerManager` at
construction (later orchestration slice; engine accepts them via DI).
- Hono / network plugin layer for session/player REST (later slice).
- The matchmaking / queue surface in upstream's `enhanced/` — out of
scope for B-2.
## Source mapping
| Requirement | Source lines | Notes |
|---|---|---|
| SessionNode graph helpers | `packages/universal/game-server/src/engine/SessionNode.ts` L8–L175 | Recast — `Date.now()` replaced by injectable `Clock`; idempotent `addChild`. |
| SessionTree O(1) graph | `packages/universal/game-server/src/engine/SessionTree.ts` L16–L441 | Recast — plain `Error` → `GameServerError`; Zod-validated boundary; persistence concern lifted into `SessionStore`. |
| SessionStore persistence | `packages/universal/game-server/src/engine/SessionStore.ts` L22–L132 | Recast — `AbstractStorage` byte API replaced by `SessionPersistence` adapter accepting structured records. |
| PlayerManager lifecycle | `packages/universal/game-server/src/engine/PlayerManager.ts` L13–L357 | Recast — `setInterval` replaced by `TimersLike`-driven self-rescheduling tick; reconnection grace window. |
### Requirements with no source line
| Requirement | Justification |
|---|---|
| `SessionLifecycle` FSM | Genesis bar — upstream `SessionLifecycle.ts` was a session+player coordinator, NOT an FSM. The B-2 dispatch explicitly requires a per-session `lobby → active → ended` FSM with transition validation + audit history. |
| OTel session lifecycle counter + dwell histogram | Genesis bar — upstream had no OTel. |
| OTel player lifecycle counter | Genesis bar — upstream had no OTel. |
| Zod-validated `SessionData` and option boundaries | Genesis bar. |
## Verification approach
- `src/engine/session-lifecycle/session-lifecycle.test.ts` — FSM
transition table, audit history, dwell sample emission, error paths.
- `src/engine/session-node/session-node.test.ts` — tree-walking helpers,
idempotent attach, persistence round-trip with preserved timestamps.
- `src/engine/session-tree/session-tree.test.ts` — Zod boundary, O(1)
lookups, bottom-up cascade, integrity audit, restore seam.
- `src/engine/session-store/session-store.test.ts` — `ISessionManager`
conformance harness, persist/restore round-trip, orphan handling.
- `src/engine/player-manager/player-manager.test.ts` —
`IPlayerManager` conformance harness, heartbeat monitor scheduling,
grace-window eviction, reconnection metadata merge.
## Impact
### Affected dependents
The package has no shipped dependents on genesis at the time of this
change. Additive surface only.
## Migration impact
None — additive only. Existing `./errors`, `./types`, `./core`, `./engine/*`
surfaces are unchanged. `GAME_SERVER_CODES` registry is **extended**;
all existing codes are preserved.