6.0 KiB
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-onlySessionNodeinterface.SessionTree— hierarchical graph (Map<id, node>+Set<rootId>) with O(1) lookups, Zod-validated boundaries, and typed event emission.SessionStore—ISessionManagerimplementation composingSessionTreewith an injectableSessionPersistenceadapter for durability.PlayerManager—IPlayerManagerimplementation with heartbeat-driven liveness, reconnection grace window, and a timer-driven monitor that routes throughTimersLike.
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'sSessionNodeinterface; preserves timestamps on persistence round-trips. - ADDED:
SessionTree(engine/session-tree) — graph manager withcreateSession/destroySession/getStats/validateIntegrityplus theattachRestoredseam used bySessionStore.restore. - ADDED:
SessionStore(engine/session-store) —ISessionManagerimplementation with pluggableSessionPersistence(default in-memory). - ADDED:
SessionLifecycle(engine/session-lifecycle) — per-session FSM with stable error codes for every rejection path. - ADDED:
PlayerManager(engine/player-manager) —IPlayerManagerimplementation with injectableClock/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_totalcounter,game_server.session.dwell_mshistogram,game_server.player.lifecycle_totalcounter. - ADDED: subpath exports
./engine/session-lifecycle,./engine/session-node,./engine/session-store,./engine/session-tree,./engine/player-manager.
Out of scope
GameEnginere-wiring to consumeSessionStore/PlayerManagerat 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—ISessionManagerconformance harness, persist/restore round-trip, orphan handling.src/engine/player-manager/player-manager.test.ts—IPlayerManagerconformance 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.