Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
# 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.