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

5.0 KiB

Game-server enhanced infra: service-discovery + state-sync + timing

Package: @suaveplan/game-server Status: PROPOSED Type: feature

Why

Wave C-4 lands the three "enhanced" subsystems that ride on top of the wave-A/B core: a heart-beating game-instance registry for multi-host discovery + load-balancing, a binary delta state-sync layer driven by @suaveplan/bitpacker (complementary to the JSON-delta state/state-sync shipped in wave B-3), and a vector-clock causality tracker for distributed event ordering. Without these, the higher-level orchestrator cannot fan a session across processes, ship compact state updates over WebRTC, or reason about who-saw-what when two clients race a move.

What changes

  • ADDED: GameInstanceRegistry — TTL-evicting in-memory registry of live instances with four load-balancing strategies (least-players, random, region-based, round-robin), region-latency map, capacity / health updates, filter+select discovery API, and 6 lifecycle event keys wired through the existing GameEventEmitter.
  • ADDED: BitPackerStateSync<TState, TSchema> — bitpacker-backed binary state-sync with client-side prediction, server reconciliation, and frame interpolation. Uses @suaveplan/bitpacker's BitPacker.pack/unpack for the wire format. Coexists with the JSON-delta state/state-sync from wave B-3.
  • ADDED: VectorClockSync<TEventData> — distributed event-ordering layer built over @suaveplan/timing's createVectorClock. Records game actions per session, detects concurrent actions, runs last-write-wins | merge | custom conflict resolution, and supports request/response sync rounds against remote nodes.
  • ADDED: 5 error codes — REGISTRY_INSTANCE_NOT_FOUND, REGISTRY_HEARTBEAT_EXPIRED, BITPACK_ENCODE_FAILED, BITPACK_DECODE_FAILED, VECTOR_CLOCK_NODE_UNKNOWN — appended to GAME_SERVER_CODES.
  • ADDED: 3 OTel instruments — game_server.registry.instance_lifecycle_total (counter), game_server.state.bitpack_bytes (histogram), game_server.vector_clock.actions_total (counter).
  • ADDED: 3 package subpath exports: ./enhanced/service-discovery/game-instance-registry, ./enhanced/state-sync/bitpacker-state-sync, ./enhanced/timing/vector-clock-sync.
  • ADDED: @suaveplan/bitpacker and @suaveplan/timing as workspace:^ runtime deps.

Out of scope

  • Cross-process IPC for the registry (HTTP/gRPC façade) — the registry is the in-memory primitive; a transport façade is a separate package.
  • Lamport-clock variant (only vector clocks ship; Lamport is the degenerate single-counter case).
  • Bitpacker schema authoring helpers — callers supply their own BitPackerSchema.

Source mapping

This change derives from ../suaveplan/packages/universal/game-server/src/enhanced/.

Requirement Source lines Notes
GameInstanceRegistry lifecycle (start/stop/register/deregister) service-discovery/game-instance-registry/game-instance-registry.ts L33-L94 Reworded; genesis injects Clock (no Date.now) and routes events through GameEventEmitter.
Heartbeat-TTL eviction same file L340-L351 Verbatim semantics; injectable Clock substituted for Date.now.
Load-balancing strategies same file L258-L338 All four strategies preserved; Rng injected for random.
Filter / select discovery same file L138-L208 Preserved.
BitPackerStateSync pack/unpack/prediction/reconciliation/interpolation state-sync/bitpacker-state-sync/bitpacker-state-sync.ts L36-L307 Reworded; logger dep dropped (genesis OTel-only); enableDelta flag plumbed via BitPackerOptions.
VectorClockSync.recordAction / receiveAction timing/vector-clock-sync/vector-clock-sync.ts L86-L147 Rewritten on top of genesis createVectorClock (no upstream VectorClockManager); causality compare uses vectorClockCompare.
Conflict resolution strategies same file L320-L350 Preserved (last-write-wins, merge, custom).
Causality chain + concurrent-group detection same file L170-L205, L383-L414 Preserved.

Requirements with no source line

Requirement Justification
Injectable Clock + IdGenerator everywhere a timestamp / id is minted Genesis enterprise rule — no implicit Date.now or Math.random.
OTel counters / histogram on every public path Genesis observability baseline.
Zod validation on GameInstanceRegistration and BitPackerStateSyncConfig input boundaries Genesis rule: typed boundary at every public entry.
Five new error codes routed through GameServerError Upstream throws plain Error; genesis policy is typed errors with the stable code registry.

Risks

  • The genesis timing package exposes only createVectorClock (not the upstream VectorClockManager). The rewrite preserves the upstream public surface of VectorClockSync but is built on the simpler primitive — this is the documented divergence.
  • Coverage target is 100/100/100/100; BitPackerStateSync.interpolate has multi-branch lerp logic that requires careful test design.