clay-borg/research/CB-RES-0001-game-kernel.md

8.1 KiB
Raw Blame History

CB-RES-0001: game-state kernel

capability: game.kernel.authoritative-state status: draft # becomes approved only after adversarial review (T04) tier: L (structural L, chaos roll pending at T04 declaration — see history trail) runnable-baseline: invoked — harness in research/CB-RES-0001-harness/boardgame-io/

Survey of the best existing implementations of a turn/phase game-state kernel: deterministic authoritative state, command → validation → events, simultaneous commit/reveal, hidden information, replay. Conducted 2026-07-31; research trail in history/260731-game-kernel-research.md.


Candidates

1. boardgame.io 0.50.2 (JS/TS) — measured

The most direct comparator: a declarative turn-based game engine.

  • Data model: single plain-object G (game state) + framework ctx (turn/phase bookkeeping). Game defined declaratively: setup, moves, phases, turn.stages.

  • Mutation: moves are reducer functions run through Redux + Immer; mutate a draft, framework produces immutable next state and appends to an action log (basis for time travel).

  • Determinism/replay: seeded RNG via random plugin; log + seed give replay and time travel. Measured: same seed → identical state hash across runs; different seed diverges.

  • Hidden information: playerView projection (e.g. STRIP_SECRET) — server strips secret state per player.

  • Simultaneous actions: activePlayers stages give simultaneous move windows; no built-in cryptographic commit/reveal — commitment is plain state the server can see (fine for server-authoritative, nothing for peer settings).

  • Maturity: 12.4k GitHub stars, but inactive — last npm release 0.50.2 ≈ 4 years ago (Snyk: "maintenance: Inactive").

  • Measured performance (our harness, synthetic 3-player GROUND-shaped commit/reveal workload, Node v24, this machine bnt-lap001):

    applied moves moves/s elapsed RSS
    5,000 1,930 2.6 s 224 MB
    10,000 1,605 6.2 s 229 MB
    20,000 870 23.0 s 271 MB
    100,000 did not finish in 300 s

    Per-move cost grows with history length (log accumulation + state pipeline): throughput halves as move count doubles — superlinear total cost. This is architectural (unbounded redux log per client), not a tuning artifact.

  • Weight: 120 transitive npm packages, 37 MB node_modules, core package 3.9 MB.

2. Tabletop Simulator scripting model (Lua) — cited

The dominant commercial virtual tabletop; reference for tabletop semantics, not a rules kernel.

  • Data model: none authoritative — game state is the physical scene; Lua state serialized as strings into the save JSON (onSave/onLoad).
  • Mutation: imperative Lua in a Global script + per-object scripts with event hooks; no move validation or rules engine — rules are social, physics is primary (sandbox mode in Clay-Borg terms).
  • Determinism/replay: none. Hidden info via hand zones (engine feature, not a projection model).
  • Valuable as the pattern source for object-attached behavior and hand zones; architecturally the anti-model for an authoritative kernel.

3. Rune SDK (JS) — cited

Modern (active, 20242026) deterministic multiplayer engine for casual web games.

  • Data model/mutation: pure logic.js — game state + action functions, statically checked for nondeterminism (mutation escape, Math.random patched deterministic).
  • Sync: predict-rollback: all clients + server simulate the same deterministic logic; server authoritative, clients predict. Strongest determinism discipline of the candidates — enforced by tooling, not convention.
  • Limits: platform-bound (Rune's hosted app ecosystem), not an embeddable open kernel; no phase/stage framework, hidden-information projection, or event-sourced replay surface comparable to boardgame.io.

4. Event-sourcing kernels (Rust cqrs-es pattern / EventStoreDB) — cited

The general-purpose form of our mutation pipeline (command → validate → events → fold).

  • Aggregates validate commands and emit events; state is a fold over the append-only log; snapshots bound replay cost. Replay/audit are native.
  • Performance (cited/estimated): in-process Rust event application is memory-bandwidth-bound — order 10⁵10⁶ small events/s per core is the commonly reported range for fold-style aggregates; dedicated stores (EventStoreDB) sustain tens of thousands of appends/s over the network. No game semantics: phases, visibility, simultaneity all DIY.

5. bevy_ecs 0.x (Rust) — cited

Archetypal ECS; the world/spatial layer in our architecture, surveyed as a kernel candidate for completeness.

  • Cache-friendly iteration: millions of entity-component accesses per frame (cited from Bevy's own benches; ns-scale per component access).
  • No authoritative command/event pipeline, no replay, no hidden-info projection; determinism requires care (system ordering, hash maps). Confirms the ADR-anticipated split: ECS for world representation, typed aggregates for the semantic kernel — not a competitor on this capability.

Baselines (benchmark-to-beat)

Dimension Baseline holder Metric Value Provenance
D1 ease of specification boardgame.io LOC to express the synthetic 3p commit/reveal game (declarative object) ~45 LOC measured (harness bench.js game def)
D1 — (no candidate) rule-to-scenario traceability (M-D1-COV) 0 % — none of the candidates link rules to tests measured/observed
D2 implementation weight boardgame.io transitive deps / install size 120 pkgs / 37 MB measured
D3 throughput boardgame.io applied moves/s, 3p workload @5k moves 1,930 moves/s measured, bnt-lap001
D3 scaling boardgame.io throughput @20k vs @5k moves 0.45× (superlinear cost) measured, bnt-lap001
D3 memory boardgame.io RSS @5k moves 224 MB measured, bnt-lap001
D3 ceiling (adjacent layer) in-proc event-sourcing (Rust) events applied/s per core ~10⁵10⁶ cited/estimated — directional, caps our verdict at parity unless we measure a Rust comparator
D4 optionality Rune determinism enforced by tooling static nondeterminism checks cited
D4 boardgame.io replaceability of subsystems plugin API, but JS-ecosystem-locked; no null/reference impl pattern observed

Headline benchmark-to-beat for the Clay-Borg kernel (proposed for the ADR): ≥ 100,000 applied events/s sustained with flat scaling (throughput @100k events within 10% of @5k), deterministic replay bit-identical, on the same machine and workload shape as the boardgame.io harness.

Verdict

  • Per dimension: D1 — boardgame.io's declarative game object is the bar to match; nobody has rule-to-scenario traceability (open surpass lane). D2 — boardgame.io's 120-dep footprint is beatable by an order of magnitude in Rust. D3 — boardgame.io is slow and degrades; the honest comparison class is in-proc event sourcing (10⁵10⁶/s), and our D3 advantage over boardgame.io is partly language choice — the meaningful target is flat scaling + the 100k/s floor, not the ×50 headline. D4 — Rune's tooling-enforced determinism is the discipline to assimilate; no candidate offers a null/reference/optimized port pattern.
  • What none of them do: combine deterministic replayable authoritative state, first-class simultaneous commit/reveal with hidden-information projection, flat per-event cost with snapshots, and an embeddable language-portable boundary. That combination is the surpass opportunity.
  • Risks in these baselines: the boardgame.io harness measures the headless client pipeline (includes subscription/log overhead — canonical usage, but a bare server-side master could differ); the event-sourcing numbers are cited, not locally measured; TTS and Rune numbers are qualitative. The D3 event-sourcing row is directional and caps related evidence verdicts at parity per MetricsAndScenarios §3.