Initial Commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user