4.8 KiB
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 withstart/stop/tick/disposelifecycle. - ADDED:
GameLoop(in./engine/game-loop) — deterministic fixed-step loop withClock/TimersLikeinjection,tick-based/event-driven/hybridmodes, pause/resume,tick()manual driver. - ADDED:
PluginManager(in./engine/plugin-manager) — typedEnginePluginregistry withinstall/uninstall/enable/disable/get+ lifecycle-hook execution wrapped inPLUGIN_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 opaqueIGameLogicLikeslot 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.