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

6.0 KiB
Raw Permalink Blame History

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.