155 lines
8.1 KiB
Markdown
155 lines
8.1 KiB
Markdown
|
|
# 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](../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, 2024–2026) 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.
|