{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/game-kit",
  "slug": "packages/game-kit",
  "kind": "capabilities",
  "bucket": "package",
  "title": "@leumas/game-kit",
  "name": "@leumas/game-kit",
  "eyebrow": null,
  "chip": null,
  "summary": "The Leumas game simulation kernel: ECS, fixed-step loop, physics seam, prefabs, defineGame, netcode. Isomorphic — runs identically in the browser and in a Node sim worker. Contains no three.js, no...",
  "keywords": [
    "game-kit",
    "fixed-step",
    "prefabs",
    "definegame",
    "netcode",
    "game kit api",
    "leumas game kit",
    "simulation"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# @leumas/game-kit\n\nThe Leumas game simulation kernel: ECS, fixed-step loop, physics seam, prefabs, `defineGame`, netcode.\n\n**It is isomorphic, and that is the point.** Everything here runs unchanged in a browser tab and in a\nNode sim worker, so the authoritative server and the predicting client execute the *same* rules over\nthe *same* components. There is no `three`, no DOM, no React and no clock anywhere beneath any entry\npoint. `pnpm check:game` fails the build if that stops being true.\n\nPresentation lives in `@leumas/game-3d`. Games live in `@leumas/games`. Neither belongs here.\n\n## The five things worth knowing\n\n**1. The public surface is five factories, deliberately.** Every JSDoc block in this package is read\nby `check:props-coverage`, the function index and the MCP schema generator. A kernel of two hundred\nloose functions would be two hundred tool entries no agent could choose between, so the kernel exports\n`createWorld`, `createLoop`, `defineComponent`, `defineGame` and `createPrefabPack`, and everything\nelse is a documented key on a returned handle.\n\n**2. Entity ids are packed, and the generation bits are load-bearing.** An id is\n`(generation << 20) | index`. When an entity is destroyed its slot is recycled with `generation + 1`,\nso a stale id fails `world.alive(e)` instead of silently addressing whoever moved in. Eleven bits of\ngeneration keeps an id inside a positive `Int32`, which is what lets it live in an `eid` component\nfield and leaves `-1` free for `NO_ENTITY`.\n\n**3. `headless` is the entire client/server delta.** It skips the `interpolate` and `present` phases\nand nothing else. A server is not a different world; it is the same world with two phases off. The\nmatching rule on the content side is that `resolvePrefab` strips `Render` when headless — which is\nwhat lets one prefab file feed both sides without a build step or a naming convention to drift.\n\n**4. The loop truncates; it does not catch up.** Past `maxCatchUp` steps the accumulated time is\nthrown away and `onStarve` fires. A four-second GC pause must cost four seconds of *simulated* time.\nDraining the debt means 240 late steps, each making the next frame later still — that is the spiral of\ndeath, and admitting the time is gone is the only reliable escape. On a server, `onStarve` belongs on\na dashboard: a room that starves is a room whose players are being cheated, and it is otherwise\ninvisible.\n\n**5. `world.rng` is the only randomness.** A `Math.random()` inside a system desyncs client from\nserver — both run the same code over the same inputs, get different numbers, and diverge silently over\nminutes rather than failing at once. Use `rng.fork(label)` for a new subsystem so adding one does not\nshift every other system's stream and break a replay that used to pass.\n\n## Entry points\n\n| Subpath | What | Status |\n|---|---|---|\n| `.` | ECS, loop, prefabs, `defineGame`, rng, math | shipped |\n| `./character` | the pure-JS controller prediction replays, plus static box collision | shipped |\n| `./physics` | the Rapier seam — dynamic-imports the WASM, returns opaque handles | shipped |\n| `./net` | protocol, snapshot/delta, interest, interpolation, prediction | shipped |\n| `./ai` | steering, flow fields, sensing | P7 |\n| `./world` | chunk maths, streaming policy, the shrinking zone | P9b |\n\n## The split that matters most: `/character` versus `/physics`\n\nRapier is deterministic for a given binary but does not promise **bit-identical** results across\narchitectures. Reconciliation works by rewinding to the server's state and re-running every input the\nclient has not had acknowledged — so if that replay does not reproduce the server's arithmetic\nexactly, the correction never converges and the player rubber-bands forever.\n\nSo the line is drawn once, here:\n\n| | Moved by | Authority | Predicted? |\n|---|---|---|---|\n| The local player | `/character`, pure JS | server, replayed by the client | **yes** |\n| AI, remote players | `/physics` or `/character` | server | no — interpolated |\n| Ragdolls, vehicles, debris, thrown props | `/physics` (Rapier) | server | no — interpolated |\n\nIf you reach for `/physics` inside a function on the `/character` path, prediction quietly stops\nconverging. Take a query result as an argument instead.\n\n`/character` collides against **axis-aligned boxes**, not meshes. Arenas, cover, crates, walls,\nfloors and platforms are boxes; terrain is not. When a title needs to walk on a mesh, the answer is a\nheightfield or a navmesh, not a swept triangle solver bolted on here.\n\n## Five traps\n\n**Never hold `Component.array.x` across a `create()`.** Storage grows by doubling and reallocates the\nbacking TypedArrays. Read `.array` each tick.\n\n**Never store the result of `query()`.** It is a cached view, stable only until the next structural\nchange. Iterate it and let it go.\n\n**`physics.drain()` is destructive and per-step.** Exactly one consumer may call it, once, after each\n`step()`. A second caller gets an empty array and its triggers silently never fire.\n\n**Snapshot writing is O(players x visible entities).** Interest management makes that cheap while\nplayers are spread out and does nothing once they are not: 24 dispersed players cost 1.4 ms/tick,\nbut 60 players standing in one 60-metre circle cost 43 ms against a 33 ms budget. The fix is sharing\none computed frame per interest cell, not a faster encoder. See `bench/net.bench.mjs`.\n\n**`physics.takeSnapshot()` is ~1.5KB per body** (measured: 312KB for 201). It is for late-join\nkeyframes and editor undo — never a per-tick operation.\n\n## Example\n\n```js\nimport { createWorld, createLoop, defineComponent } from '@leumas/game-kit';\n\nconst Transform = defineComponent('Transform', { x: 'f32', y: 'f32', z: 'f32' });\nconst Velocity = defineComponent('Velocity', { x: 'f32', y: 'f32', z: 'f32' });\n\nconst world = createWorld({ components: [Transform, Velocity], headless: true, seed: 1337 });\n\nworld.addSystem({\n  id: 'integrate',\n  phase: 'physics',\n  reads: [Velocity],\n  writes: [Transform],\n  run: ({ world, dt }) => {\n    const q = world.query([Transform, Velocity]);\n    const t = Transform.array;\n    const v = Velocity.array;\n    for (let n = 0; n < q.length; n++) {\n      const i = world.indexOf(q[n]);          // id -> index, once\n      t.x[i] += v.x[i] * dt;\n      t.y[i] += v.y[i] * dt;\n      t.z[i] += v.z[i] * dt;\n    }\n  },\n});\n\nworld.create({ Transform: { x: 0, y: 2, z: 0 }, Velocity: { x: 1, y: 0, z: 0 } });\n\nconst loop = createLoop({ hz: 30, step: (dt) => world.step(dt), onStarve: (ms) => console.warn('starved', ms) });\nloop.start();\n```\n\n## Verify\n\n```sh\npnpm --filter @leumas/game-kit test    # 191 tests across 4 entry points\npnpm check:game                        # the renderer-free guarantee, plus its own self-test\nnode shared/packages/game-kit/bench/physics.bench.mjs   # tick p50/p95/p99 against the frame budget\nnode shared/packages/game-kit/bench/net.bench.mjs       # bytes/sec and write cost per client\n```\n\nThe tests worth knowing about: **the convergence test** in `net.test.js` runs a two-client, 3,000-tick\nmatch over 150ms RTT with 5% loss and 5% reordering and asserts both clients converge — and asserts the\nimpairments really happened, because a convergence test over a clean link proves nothing.\n**`REPLAY CONVERGES`** in `character.test.js` asserts that re-running\ninputs from a saved state reproduces the future exactly — that is the property prediction depends on,\nand it fails silently rather than loudly when broken. **`isomorphic.test.js`** asserts in plain Node,\nwith no DOM shim, that no module beneath any entry point reaches for three, react or a browser global.\n",
  "source": {
    "path": "shared/packages/game-kit/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 7949,
    "hash": "62ca6d1a1fa1addc78f0f5a6813fb23aa998b4f7"
  },
  "urls": {
    "html": "/p/packages/game-kit",
    "json": "/docs/packages/game-kit.json",
    "md": "/docs/packages/game-kit.md"
  },
  "links": {
    "composes": [],
    "usedBy": [
      "pkg:@leumas/features",
      "pkg:@leumas/game-3d",
      "pkg:@leumas/game-server",
      "pkg:@leumas/sound",
      "pkg:@leumas/studio",
      "pkg:@leumas/web"
    ],
    "product": [
      "pkg:@leumas/admin",
      "pkg:@leumas/studio",
      "pkg:@leumas/web"
    ],
    "howTo": [],
    "skills": []
  },
  "exports": {
    "total": 301,
    "component": 121,
    "hook": 0,
    "helper": 180,
    "names": [
      {
        "n": "Ammo",
        "k": "component"
      },
      {
        "n": "ARMOUR_COMPONENTS",
        "k": "component"
      },
      {
        "n": "ASSET_KINDS",
        "k": "component"
      },
      {
        "n": "ATTACK",
        "k": "component"
      },
      {
        "n": "BASE_WEIGHT",
        "k": "component"
      },
      {
        "n": "BOON_COMPONENTS",
        "k": "component"
      },
      {
        "n": "Boons",
        "k": "component"
      },
      {
        "n": "Brain",
        "k": "component"
      },
      {
        "n": "C2S",
        "k": "component"
      },
      {
        "n": "CANCELS",
        "k": "component"
      },
      {
        "n": "CHASSIS",
        "k": "component"
      },
      {
        "n": "CONDITION_OPS",
        "k": "component"
      },
      {
        "n": "CORE_COMPONENTS",
        "k": "component"
      },
      {
        "n": "COYOTE_MS",
        "k": "component"
      },
      {
        "n": "DEFINITION_KEYS",
        "k": "component"
      },
      {
        "n": "DENIED",
        "k": "component"
      },
      {
        "n": "DOUBLE_JUMP_SPEED",
        "k": "component"
      },
      {
        "n": "DOWN",
        "k": "component"
      },
      {
        "n": "DOWN_COMPONENTS",
        "k": "component"
      },
      {
        "n": "DOWN_REFUSED",
        "k": "component"
      },
      {
        "n": "Downed",
        "k": "component"
      },
      {
        "n": "ENVIRONMENT_IDS",
        "k": "component"
      },
      {
        "n": "FACES",
        "k": "component"
      },
      {
        "n": "FALL_BOOST",
        "k": "component"
      },
      {
        "n": "FALLOFF",
        "k": "component"
      },
      {
        "n": "FIELD_KINDS",
        "k": "component"
      },
      {
        "n": "FIRING",
        "k": "component"
      },
      {
        "n": "GAME_KEYS",
        "k": "component"
      },
      {
        "n": "GAME_SCHEMA_VERSION",
        "k": "component"
      },
      {
        "n": "GRAVITY",
        "k": "component"
      },
      {
        "n": "GRAVITY",
        "k": "component"
      },
      {
        "n": "Health",
        "k": "component"
      },
      {
        "n": "Hitbox",
        "k": "component"
      },
      {
        "n": "HORDE_COMPONENTS",
        "k": "component"
      },
      {
        "n": "HUNT",
        "k": "component"
      },
      {
        "n": "INTERACT_COMPONENTS",
        "k": "component"
      },
      {
        "n": "Interactable",
        "k": "component"
      },
      {
        "n": "Item",
        "k": "component"
      },
      {
        "n": "JUMP_BUFFER_MS",
        "k": "component"
      },
      {
        "n": "JUMP_SPEED",
        "k": "component"
      },
      {
        "n": "KICK",
        "k": "component"
      },
      {
        "n": "LEDGE_HANG",
        "k": "component"
      },
      {
        "n": "LEDGE_HANG_MS",
        "k": "component"
      },
      {
        "n": "LEDGE_MANTLE",
        "k": "component"
      },
      {
        "n": "LEDGE_NONE",
        "k": "component"
      },
      {
        "n": "LOOT_COMPONENTS",
        "k": "component"
      },
      {
        "n": "MANIFEST_VERSION",
        "k": "component"
      },
      {
        "n": "MANTLE_MAX_RISE",
        "k": "component"
      },
      {
        "n": "MANTLE_MIN_RISE",
        "k": "component"
      },
      {
        "n": "MANTLE_MS",
        "k": "component"
      },
      {
        "n": "MANTLE_REACH",
        "k": "component"
      },
      {
        "n": "MANTLE_RISE_FRACTION",
        "k": "component"
      },
      {
        "n": "MASK_WORDS",
        "k": "component"
      },
      {
        "n": "MAX_COMPONENTS",
        "k": "component"
      },
      {
        "n": "MAX_IMPACT",
        "k": "component"
      },
      {
        "n": "MAX_PERKS",
        "k": "component"
      },
      {
        "n": "MAX_REWIND_MS",
        "k": "component"
      },
      {
        "n": "MAX_ROLLBACK",
        "k": "component"
      },
      {
        "n": "MAX_TIMED_BOONS",
        "k": "component"
      },
      {
        "n": "Module",
        "k": "component"
      },
      {
        "n": "MODULES",
        "k": "component"
      },
      {
        "n": "MOVER_COMPONENTS",
        "k": "component"
      },
      {
        "n": "MOVER_EASES",
        "k": "component"
      },
      {
        "n": "MOVER_KINDS",
        "k": "component"
      },
      {
        "n": "Networked",
        "k": "component"
      },
      {
        "n": "NEUTRAL",
        "k": "component"
      },
      {
        "n": "NO_ENTITY",
        "k": "component"
      },
      {
        "n": "ORDERS",
        "k": "component"
      },
      {
        "n": "ORIENTED_SCHEMA",
        "k": "component"
      },
      {
        "n": "OUT",
        "k": "component"
      },
      {
        "n": "OUTCOME",
        "k": "component"
      },
      {
        "n": "Owned",
        "k": "component"
      },
      {
        "n": "PART",
        "k": "component"
      },
      {
        "n": "PERK_COMPONENTS",
        "k": "component"
      },
      {
        "n": "PERK_REFUSED",
        "k": "component"
      },
      {
        "n": "Perks",
        "k": "component"
      },
      {
        "n": "PERMISSIVE",
        "k": "component"
      },
      {
        "n": "PERSIST_MODES",
        "k": "component"
      },
      {
        "n": "PERSIST_VERSION",
        "k": "component"
      },
      {
        "n": "PHASE",
        "k": "component"
      },
      {
        "n": "PHASES",
        "k": "component"
      },
      {
        "n": "Plate",
        "k": "component"
      },
      {
        "n": "PRESENTATION_COMPONENTS",
        "k": "component"
      },
      {
        "n": "READY",
        "k": "component"
      },
      {
        "n": "REFUSED",
        "k": "component"
      },
      {
        "n": "REJECT",
        "k": "component"
      },
      {
        "n": "RELOADING",
        "k": "component"
      },
      {
        "n": "Render",
        "k": "component"
      },
      {
        "n": "REWIND",
        "k": "component"
      },
      {
        "n": "Rider",
        "k": "component"
      },
      {
        "n": "RigidBody",
        "k": "component"
      },
      {
        "n": "ROTATIONS",
        "k": "component"
      },
      {
        "n": "RUN_MULTIPLIER",
        "k": "component"
      },
      {
        "n": "S2C",
        "k": "component"
      },
      {
        "n": "SCOPES",
        "k": "component"
      },
      {
        "n": "Sequence",
        "k": "component"
      },
      {
        "n": "SEQUENCE_COMPONENTS",
        "k": "component"
      },
      {
        "n": "SOURCE_KINDS",
        "k": "component"
      },
      {
        "n": "SPEED",
        "k": "component"
      },
      {
        "n": "STAGGER",
        "k": "component"
      },
      {
        "n": "STATES",
        "k": "component"
      },
      {
        "n": "STEP",
        "k": "component"
      },
      {
        "n": "Structure",
        "k": "component"
      },
      {
        "n": "STRUCTURE_COMPONENTS",
        "k": "component"
      },
      {
        "n": "Team",
        "k": "component"
      },
      {
        "n": "Transform",
        "k": "component"
      },
      {
        "n": "Turret",
        "k": "component"
      },
      {
        "n": "UNREACHABLE",
        "k": "component"
      },
      {
        "n": "UP",
        "k": "component"
      },
      {
        "n": "Vehicle",
        "k": "component"
      },
      {
        "n": "VEHICLE_COMPONENTS",
        "k": "component"
      },
      {
        "n": "Velocity",
        "k": "component"
      },
      {
        "n": "VERBS",
        "k": "component"
      },
      {
        "n": "VISIBILITY",
        "k": "component"
      },
      {
        "n": "Wallet",
        "k": "component"
      },
      {
        "n": "Wave",
        "k": "component"
      },
      {
        "n": "WAVE_COMPONENTS",
        "k": "component"
      },
      {
        "n": "WIRE_VERSION",
        "k": "component"
      },
      {
        "n": "ZONE_DONE",
        "k": "component"
      },
      {
        "n": "ZONE_HOLD",
        "k": "component"
      },
      {
        "n": "ZONE_SHRINK",
        "k": "component"
      },
      {
        "n": "__resetComponentBits",
        "k": "helper"
      },
      {
        "n": "acceptPayload",
        "k": "helper"
      },
      {
        "n": "aimTurret",
        "k": "helper"
      },
      {
        "n": "applyArea",
        "k": "helper"
      },
      {
        "n": "applyHit",
        "k": "helper"
      },
      {
        "n": "applyInterpolated",
        "k": "helper"
      },
      {
        "n": "applyPenetration",
        "k": "helper"
      },
      {
        "n": "areaCandidates",
        "k": "helper"
      },
      {
        "n": "bakePrefab",
        "k": "helper"
      },
      {
        "n": "beginReload",
        "k": "helper"
      },
      {
        "n": "bindSeat",
        "k": "helper"
      },
      {
        "n": "blueprintsToPrefabs",
        "k": "helper"
      },
      {
        "n": "botInput",
        "k": "helper"
      },
      {
        "n": "canFire",
        "k": "helper"
      },
      {
        "n": "captureHitboxes",
        "k": "helper"
      },
      {
        "n": "claim",
        "k": "helper"
      },
      {
        "n": "claimsOf",
        "k": "helper"
      },
      {
        "n": "clamp",
        "k": "helper"
      },
      {
        "n": "clamp01",
        "k": "helper"
      },
      {
        "n": "compileFrames",
        "k": "helper"
      },
      {
        "n": "compileGame",
        "k": "helper"
      },
      {
        "n": "compileTables",
        "k": "helper"
      },
      {
        "n": "componentBudget",
        "k": "helper"
      },
      {
        "n": "confirmLedge",
        "k": "helper"
      },
      {
        "n": "countBits",
        "k": "helper"
      },
      {
        "n": "createCharacter",
        "k": "helper"
      },
      {
        "n": "createChunkGrid",
        "k": "helper"
      },
      {
        "n": "createCollisionWorld",
        "k": "helper"
      },
      {
        "n": "createDowns",
        "k": "helper"
      },
      {
        "n": "createFighter",
        "k": "helper"
      },
      {
        "n": "createFlowField",
        "k": "helper"
      },
      {
        "n": "createHitLog",
        "k": "helper"
      },
      {
        "n": "createHorde",
        "k": "helper"
      },
      {
        "n": "createInteractions",
        "k": "helper"
      },
      {
        "n": "createInterestGrid",
        "k": "helper"
      },
      {
        "n": "createInterpolator",
        "k": "helper"
      },
      {
        "n": "createLedgeState",
        "k": "helper"
      },
      {
        "n": "createLevelPool",
        "k": "helper"
      },
      {
        "n": "createLink",
        "k": "helper"
      },
      {
        "n": "createLinkPair",
        "k": "helper"
      },
      {
        "n": "createLoop",
        "k": "helper"
      },
      {
        "n": "createLoot",
        "k": "helper"
      },
      {
        "n": "createMatchFlow",
        "k": "helper"
      },
      {
        "n": "createMover",
        "k": "helper"
      },
      {
        "n": "createMoverSystem",
        "k": "helper"
      },
      {
        "n": "createOwnerRegistry",
        "k": "helper"
      },
      {
        "n": "createPersistence",
        "k": "helper"
      },
      {
        "n": "createPhases",
        "k": "helper"
      },
      {
        "n": "createPhysicsWorld",
        "k": "helper"
      },
      {
        "n": "createPredictor",
        "k": "helper"
      },
      {
        "n": "createPrefabPack",
        "k": "helper"
      },
      {
        "n": "createRewindBuffer",
        "k": "helper"
      },
      {
        "n": "createRng",
        "k": "helper"
      },
      {
        "n": "createRng",
        "k": "helper"
      },
      {
        "n": "createRollback",
        "k": "helper"
      },
      {
        "n": "createSnapshotCache",
        "k": "helper"
      },
      {
        "n": "createStreamingPolicy",
        "k": "helper"
      },
      {
        "n": "createStructures",
        "k": "helper"
      },
      {
        "n": "createTurnOrder",
        "k": "helper"
      },
      {
        "n": "createWaves",
        "k": "helper"
      },
      {
        "n": "createWeaponState",
        "k": "helper"
      },
      {
        "n": "createWorld",
        "k": "helper"
      },
      {
        "n": "createZone",
        "k": "helper"
      },
      {
        "n": "createZones",
        "k": "helper"
      },
      {
        "n": "cross",
        "k": "helper"
      },
      {
        "n": "damageAt",
        "k": "helper"
      },
      {
        "n": "damp",
        "k": "helper"
      },
      {
        "n": "decodeFrame",
        "k": "helper"
      },
      {
        "n": "decodeSnapshot",
        "k": "helper"
      },
      {
        "n": "defineAsset",
        "k": "helper"
      },
      {
        "n": "defineBoons",
        "k": "helper"
      },
      {
        "n": "defineComponent",
        "k": "helper"
      },
      {
        "n": "defineGame",
        "k": "helper"
      },
      {
        "n": "defineLevel",
        "k": "helper"
      },
      {
        "n": "defineLootTable",
        "k": "helper"
      },
      {
        "n": "defineMover",
        "k": "helper"
      },
      {
        "n": "defineNetSchema",
        "k": "helper"
      },
      {
        "n": "definePerks",
        "k": "helper"
      },
      {
        "n": "defineSequence",
        "k": "helper"
      }
    ]
  }
}
