Files
suaveplan/openspec/archive/2026-05-24-game-server-engine-core/proposal.md
2026-05-27 23:29:53 +01:00

4.8 KiB
Raw Blame History

game-server-engine-core — GameEngine + GameLoop + PluginManager

Package: @suaveplan/game-server Status: PROPOSED Type: feature (Wave B-1 migration slice)

  • Design: see this file (no separate design.md required for a thin orchestrator slice)
  • Spec delta: ./specs/game-server/spec.md

Why

Wave A landed the foundations (errors, types, engine/di-container). The broader package still has no engine top-level — there is no GameEngine to orchestrate services, no GameLoop to drive deterministic ticks, and no PluginManager to wire third-party extensions. Without these the package is unusable for actual game work.

This slice (Wave B-1) ships the three engine primitives in dependency order so Wave B-2 (sessions/player-manager) and later state/network/matchmaking slices have a stable top-level to bind to. The migration is verbatim in intent (preserve upstream lifecycle + statistics surface) but recast at genesis bar — injectable Clock/TimersLike/IdGenerator, typed GameServerError codes, Zod-validated boundaries, OTel counters and histograms with real MeterProvider assertions, kebab-case <feature>/ layout, 100/100/100/100 coverage.

What changes

  • ADDED: GameEngine (in ./engine/game-engine) — top-level orchestrator wiring DI-injected services with start/stop/tick/dispose lifecycle.
  • ADDED: GameLoop (in ./engine/game-loop) — deterministic fixed-step loop with Clock/TimersLike injection, tick-based/event-driven/hybrid modes, pause/resume, tick() manual driver.
  • ADDED: PluginManager (in ./engine/plugin-manager) — typed EnginePlugin registry with install/uninstall/enable/disable/get + lifecycle-hook execution wrapped in PLUGIN_LIFECYCLE_FAILED.
  • ADDED: GAME_SERVER.* codes — ENGINE_NOT_STARTED, ENGINE_ALREADY_STARTED, ENGINE_DISPOSED, INVALID_ENGINE_OPTIONS, INVALID_GAME_LOOP_OPTIONS, GAME_LOOP_NOT_RUNNING, GAME_LOOP_ALREADY_RUNNING, PLUGIN_NOT_FOUND, DUPLICATE_PLUGIN, PLUGIN_LIFECYCLE_FAILED.
  • ADDED: subpath exports ./engine/game-engine, ./engine/game-loop, ./engine/plugin-manager.

Out of scope

  • SessionManager, PlayerManager, SessionTree (Wave B-2 — orchestrator consumes them as opaque structural service interfaces only).
  • StateManager, NetworkAdapter, MatchmakingEngine, QueueManager (later waves).
  • Game-logic strategy concretes (AbstractGameLogic) — the engine accepts an opaque IGameLogicLike slot per upstream contract.

Source mapping

This change derives from upstream files on /mnt/s/web/suaveplan:

Requirement Source lines Notes
GameEngine orchestrator packages/universal/game-server/src/engine/GameEngine.ts L60–L497 Recast — drops session/state/network constructor wiring (Wave B-2+ scope); preserves DI-service registration + start/stop/dispose lifecycle.
GameLoop deterministic ticks packages/universal/game-server/src/engine/GameLoop.ts L36–L358 Recast — Date.now() replaced by injectable Clock; setInterval/clearInterval replaced by TimersLike.
PluginManager lifecycle packages/universal/game-server/src/engine/PluginManager.ts L27–L355 Recast — plain Error → GameServerError; dependency-aware topo sort preserved.

Requirements with no source line

Requirement Justification
OTel counters + histograms Genesis bar — upstream had no OTel; this slice adds game_server.engine.lifecycle_total, game_server.loop.tick_duration_ms, game_server.plugin.lifecycle_total to match the package quality bar (mirrors @suaveplan/ecs).
Zod-validated option boundaries Genesis bar — every public option boundary parses through Zod before reaching state.
Typed GameServerError everywhere Genesis bar — upstream threw plain Error for lifecycle violations.

Verification approach

  • src/engine/game-engine/game-engine.test.ts — covers GameEngine lifecycle, DI wiring, plugin handoff, OTel emission.
  • src/engine/game-loop/game-loop.test.ts — covers loop modes, deterministic ticking via injected Clock/TimersLike, pause/resume, error propagation.
  • src/engine/plugin-manager/plugin-manager.test.ts — covers install/uninstall/enable/disable, duplicate detection, dependency topo sort, hook execution.
  • src/errors/game-server-error.test.ts — extended to assert every new code.

Impact

Affected dependents

Run bunx turbo ls --filter=...@suaveplan/game-server to enumerate. The package has no shipped dependents on genesis at the time of this change (verified: bunx turbo ls --filter=...@suaveplan/game-server returns only the package itself).

  • No dependents — additive surface only.

Migration impact

None — additive only. Existing ./errors, ./types, ./engine/di-container surfaces are unchanged. The GAME_SERVER_CODES registry is extended; the existing 4 codes are preserved.