# 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` + `Set`) 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.