@jgengine/convex 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +199 -57
- package/package.json +2 -2
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +8 -8
- package/skills/jgengine/api.md +30 -10
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,203 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
26
26
|
`packages/core/src/meta/changelog.ts` (the typed `CHANGELOG` export).
|
|
27
27
|
-->
|
|
28
28
|
|
|
29
|
+
## 0.49.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.49.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.46.0`; `@jgengine/github` is `0.36.0`.
|
|
34
|
+
- rewrite each proofs/flows/<name>.json as <name>.flow.ts with defineFlow, add "proofs" to the game's tsconfig include, and name the capture state in the flow (state: "...") instead of passing --state.
|
|
35
|
+
- `HeadlessRunner.step(dt)` advances all of `dt` now, in frames of at most `maxStepSeconds` (default 0.05 s); it used to clamp the step to one frame and drop the rest. Code that stepped many small frames keeps working; code that relied on the clamp gets the time it asked for.
|
|
36
|
+
- `HeadlessRunner.snapshot()`/`restore()` capture `ctx.state()` (every system's save) instead of the replication view.
|
|
37
|
+
- `createHeadlessRunner` turns on the walk controller when the game's input binds a walk action (`moveForward`, `moveBack`, `moveLeft`, `moveRight`, `jump`), as the shell does; pass `playerMovement: false` to keep it off. With no `loop`, it runs the definition's own loop (and so its `systems`).
|
|
38
|
+
- `@jgengine/shell/TerritoryOverlay` → `@jgengine/core/placement/TerritoryOverlay.view`
|
|
39
|
+
- `@jgengine/shell/render/CharacterCrowd` → `@jgengine/core/anim/CharacterCrowd.draw`
|
|
40
|
+
- `@jgengine/shell/render/CorpseRagdolls` → `@jgengine/core/anim/CorpseRagdolls.draw`
|
|
41
|
+
- `@jgengine/shell/render/InteriorDressing` → `@jgengine/core/buildings/InteriorDressing.draw`
|
|
42
|
+
- `@jgengine/shell/render/LivingWorldLayer` → `@jgengine/core/weather/LivingWorldLayer.draw`
|
|
43
|
+
- `@jgengine/shell/render/PartMotion` → `@jgengine/core/anim/PartMotion.draw`
|
|
44
|
+
- `@jgengine/shell/render/SkinnedInstances` → `@jgengine/core/anim/SkinnedInstances.draw`
|
|
45
|
+
- `@jgengine/shell/render/characterGlance` → `@jgengine/core/anim/characterGlance.draw`
|
|
46
|
+
- `@jgengine/shell/render/crowdBatch` → `@jgengine/core/anim/crowdBatch.draw`
|
|
47
|
+
- `@jgengine/shell/render/crowdRegistry` → `@jgengine/core/anim/crowdRegistry.draw`
|
|
48
|
+
- `@jgengine/shell/render/entityMuzzle` → `@jgengine/core/anim/entityMuzzle.draw`
|
|
49
|
+
- `@jgengine/shell/render/livingWorld` → `@jgengine/core/weather/livingWorld.draw`
|
|
50
|
+
- `@jgengine/shell/render/testFixtures/proceduralSpriteAtlas` → `@jgengine/core/render/testFixtures/proceduralSpriteAtlas.view`
|
|
51
|
+
- `@jgengine/shell/render/testFixtures/proceduralTileLayer` → `@jgengine/core/render/testFixtures/proceduralTileLayer.view`
|
|
52
|
+
- `@jgengine/shell/render/useEntityRenderCues` → `@jgengine/core/anim/useEntityRenderCues.draw`
|
|
53
|
+
- `@jgengine/shell/render/useFootIk` → `@jgengine/core/anim/useFootIk.draw`
|
|
54
|
+
- `@jgengine/shell/render/useModelAnimation` → `@jgengine/core/anim/useModelAnimation.draw`
|
|
55
|
+
- `@jgengine/shell/render/useRagdoll` → `@jgengine/core/anim/useRagdoll.draw`
|
|
56
|
+
- `@jgengine/shell/world/AuthoredFlocks` → `@jgengine/core/ai/AuthoredFlocks.draw`
|
|
57
|
+
- `@jgengine/shell/world/DataObjects` → `@jgengine/core/data/DataObjects.draw`
|
|
58
|
+
- `@jgengine/shell/world/InstancedBodies` → `@jgengine/core/physics/InstancedBodies.draw`
|
|
59
|
+
- `@jgengine/shell/world/InstancedJoints` → `@jgengine/core/physics/InstancedJoints.draw`
|
|
60
|
+
- `@jgengine/shell/world/SpriteBatch` → `@jgengine/core/render/SpriteBatch.draw`
|
|
61
|
+
- `@jgengine/shell/world/StaticObjectModels` → `@jgengine/core/instancing/StaticObjectModels.draw`
|
|
62
|
+
- `@jgengine/shell/world/TileLayerRenderer` → `@jgengine/core/render/TileLayerRenderer.draw`
|
|
63
|
+
- `@jgengine/shell/world/WorldItems` → `@jgengine/core/item/WorldItems.draw`
|
|
64
|
+
- `@jgengine/shell/world/WorldTireMarks` → `@jgengine/core/vfx/tireMarks.draw`
|
|
65
|
+
- `@jgengine/shell/world/entityPose` → `@jgengine/core/anim/entityPose`
|
|
66
|
+
- `@jgengine/shell/world/staticObjectBatches` → `@jgengine/core/instancing/staticObjectBatches.draw`
|
|
67
|
+
- `@jgengine/shell/world/tileLayerInstances` → `@jgengine/core/render/tileLayerInstances`
|
|
68
|
+
- `@jgengine/shell/world/worldBarSamples` → `@jgengine/core/hud/barSamples.draw`
|
|
69
|
+
|
|
70
|
+
`verify:fast` boots only games the load check can open: a change touching llmville no longer fails the load guard with "unknown game". `siteGames` moved to `scripts/siteGames.ts`.
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- `assets find <name>... [--list <file.json>] [--game <dir>] [--provision] [--limit N]` (also `jgengine assets find`): one JSON document per run. `--list` takes a JSON array of names or `{ "query", "pick" }`. `--provision` serves each confident top match, or the named `pick`, to the game: packs and library models through the machine's shared asset store (pulling only what no checkout has), another game's file copied to the same path. Pull output goes to stderr so stdout stays JSON.
|
|
75
|
+
- `findAssets(query, { has, gameFiles, game?, limit? })` (`@jgengine/assets/findAssets`) is the search behind it; `scoreAssetText` (`@jgengine/assets/rankAssets`) is the scorer every asset search ranks by.
|
|
76
|
+
- `@jgengine/assets/assetStore`: the shared asset store (`assetStoreRoot`, `linkFromStore`, `seedStore`, `provisionJobs`) moved here from the games folder, so the CLI and `games:provision` serve packs one way.
|
|
77
|
+
- `createBreedingField` in `@jgengine/core/breeding/breeding`: mating pairs in range, eggs incubating inside a temperature band or gestation, hatching or birth, baby stages, care requests and imprinting, multiples, cooldowns, speed multipliers and save/restore; the game supplies species times and how genes combine.
|
|
78
|
+
- `inheritStats` and `scatterLevels` in `@jgengine/core/breeding/inheritance`: per-stat inheritance from two parents with mutation rolls, and spreading levels over stats at random.
|
|
79
|
+
- `SceneEntity.size`: one entity's model drawn larger or smaller than its kind's scale, on its own rig and in a crowd batch, so a hatchling grows up without a new kind.
|
|
80
|
+
- `createBaseBuilding` (`@jgengine/core/basebuilding/baseBuilding`): snap base building from data. Shapes are boxes with sockets; pieces pair a shape with a tier and health. Plans a piece at a cursor (snapping to the nearest free socket, or standing on the ground within a step limit), places, rotates through snap variants, toggles doors, damages by damage class per tier, removes and collapses whatever loses its path to the ground, raycasts, and hands back world boxes and collision solids; snapshot/restore.
|
|
81
|
+
- `BaseStructures` (`@jgengine/core/basebuilding/BaseStructures.draw`): draws placed pieces by colour, darkened as they lose health, and the build ghost tinted valid or blocked.
|
|
82
|
+
- `EntityMovement.runMultiplier`: a body's own sprint ratio, so a mount sprints at its species' ratio instead of the rider's.
|
|
83
|
+
- `caveLayout`, `caveMesh` and `<Cave>` in `@jgengine/core/cave`: describe a cave by its path, width, height, chamber and cover and get its collision, inside/depth queries, rock and floor.
|
|
84
|
+
- `ctx.camera.turnTo(yaw)`: the orbit, first-person, chase and shoulder rigs turn to face that heading on their next frame, e.g. after a teleport or a respawn.
|
|
85
|
+
- `repeatSeconds` on authored weather: loop the schedule forever, each lap starting where the last ended.
|
|
86
|
+
- `precipitationAs: "rain" | "snow"` on a weather zone: whatever falls there falls as this, blending at the zone's edge.
|
|
87
|
+
- `solidOverhead(solids, at, { clearance?, reach? })` in `@jgengine/core/world/overhead`: whether a roof, ceiling or rock is over a point.
|
|
88
|
+
- `sky({ dayHold })`: how long the noon sky holds either side of noon under `timeOfDay`.
|
|
89
|
+
- `overcastDaylight(state, cover)` in `@jgengine/core/sky/daylightCycle`, and the `Sky` component's `weather` prop: falling rain or snow clouds the sky over. The shell passes the world's weather to the sky whenever the authored scene has weather.
|
|
90
|
+
- `createKnockoutTaming` (`@jgengine/core/taming/knockoutTaming`): ARK-style torpor knock-out and passive feeding taming. Per species: torpor by level, torpor drain, a food table (count and seconds at level 1 and 30, ineffectiveness). Tracks preferred food first, the meal clock, taming effectiveness, bonus levels, waking and starving; snapshot/restore.
|
|
91
|
+
- `createCreatureDriver` (`@jgengine/core/ai/creatureDriver`): moves and fights a creature from a mind (wild temperament, tamed companion intent, or still) and a body (walk, sprint, attack, wander). Wild creatures sense with their own perception and hold a grudge against whoever hurt them; they flee, stalk, bite or graze. The charge stops where the body's bite reaches, so a temperament tuned with a shorter range never stalls just out of reach, and steps veer around trunks and rocks instead of stopping behind them.
|
|
92
|
+
- `ridingOf` (`@jgengine/core/scene/riding`): ride a mount by seat offset. It hands controls to the mount through possession, freezes the rider and carries them on the seat, and sets them down beside the mount on dismount; snapshot/restore.
|
|
93
|
+
- `createRemainsField({ lastsSeconds })` in `@jgengine/core/remains/remains`: leave what someone carried where they fell, find the nearest, take back what fits, lose it when it expires, snapshot and restore.
|
|
94
|
+
- Orbit camera `snapDistance`: a follow target that jumps farther than this is cut to rather than glided to (default 30).
|
|
95
|
+
- `createAirdropField` in `@jgengine/core/airdrop/airdrop`: timed supply crates at authored sites, a tier picked per site by weight, falling from a height, rolled loot on landing, opened only at the tier's level, expiring when ignored, one per site, with `call` for an admin or scripted drop.
|
|
96
|
+
- `LightBeam` in `@jgengine/core/map/LightBeam.draw`: a tall glowing column marking a spot to find from far away.
|
|
97
|
+
- `createStatPoints` (`@jgengine/core/progression/statPoints`): level-up points spent into stats. Each stat has a base and what a point adds, either an amount (a survivor's +10 health) or a fraction of the base (a tame's +5.4% health), with a per-owner base override and an optional per-stat cap. Sheets are plain `{ stat: points }` records that save as they are.
|
|
98
|
+
- Fernwild tames level as in ARK: kills and a passive trickle give XP on the creature curve (10 x n per level, 88 levels past the tame), each level is a point for the tame's stat sheet, and mounts spend their own stamina on a sprint (raptor 6 a second, phiomia 20).
|
|
99
|
+
- `surveyFromDocument`, `surveyTerrain`, `surveyHeightField`, `surveyBiomeAt`, `surveyIsLand` (`@jgengine/core/terrain/surveyTerrain`): build a real-world-style landmass from scene shapes — `coast` outlines, `peak` markers (height, radius, crater), `ridge` paths (crest heights, width), `river` paths (width, bed) and `biome` outlines (ground material and colours) — into terrain config.
|
|
100
|
+
- `TerrainCoast.outlines`: a coast shaped by one or more closed shore outlines (islands, bays, capes) instead of a single wobbled ellipse.
|
|
101
|
+
- `TerrainPolygonRegion`: paint terrain ground inside any polygon, blended over its falloff.
|
|
102
|
+
- `PopulationTickContext.isActive` and `notifyRemoved(..., { delay })`: a region can sleep with its slots kept until it wakes, and a game can refill a culled slot at once.
|
|
103
|
+
- `sceneScatterBudget` (`defineGame`) and `AuthoredScene` `scatterBudget`: how many scatter instances a game's scene draws; default stays 20000.
|
|
104
|
+
- `capScatterInstances` (`@jgengine/core/scatter/InstancedScatter.draw`): thin a scatter list evenly to a budget.
|
|
105
|
+
- `readGlbModel(bytes)` in `@jgengine/core/creatureBody/glbRead`: a binary glTF's meshes baked into model space (positions, normals, UVs, colours, indices), its materials and embedded images.
|
|
106
|
+
- `autoRig(model, { shape, facing?, head?, tail?, ... })` in `@jgengine/core/creatureBody/autoRig`: give a static quadruped, biped, flyer, bug or serpent a skeleton, skin weights and gait clips, ready for `encodeGlb`.
|
|
107
|
+
- `gaitClips(rig)` in `@jgengine/core/creatureBody/gait`: the Idle, Walk, Run, Attack and Death clips for any limb-and-tail rig, shared by generated bodies and rigged models.
|
|
108
|
+
- `encodeGlb` writes UVs, vertex colours, skins (joints, weights, inverse bind matrices) and embedded base-colour images.
|
|
109
|
+
- `bodyModel`, `bodyGlbModel`, `BodyPlan` (`@jgengine/core/creatureBody/bodyPlan`): describe a creature's shape in a few numbers and get an animated `ModelConfig` (Idle, Walk, Run, Attack, Death clips), built lazily as a `data:` GLB the first time its url is read.
|
|
110
|
+
- `encodeGlb`, `glbDataUrl` (`@jgengine/core/creatureBody/glbWrite`): write a node-animated binary glTF from meshes, nodes and clips.
|
|
111
|
+
- `jgengine plan <game> [--brief "<text>"]`: writes or refreshes `<game>/PLAN.md` — features, the engine owner of each, the gaps, milestones.
|
|
112
|
+
- `jgengine has <feature> ...`: one JSON answer for many features from the capability index and code map `find` reads.
|
|
113
|
+
- `PLAN_GAPS_HEADING` (`jgengine/plan/plan`): the generated `## Gaps` heading of a game's `PLAN.md`.
|
|
114
|
+
- `HITCH_MS` (`@jgengine/core/devtools/frameBreakdown`): Where each frame goes: the record of one frame (CPU per system, GPU per render pass, draws, shader compiles, uploads, heap) and the summary of a window of them (percentiles, the costliest systems, passes, objects and materials, and every hitch with what caused it). Also `DrawCost`, `FrameBreakdownRecord`, `FrameBreakdownSummary`, `Hitch`, `NamedCost`, `Spread`.
|
|
115
|
+
- `measureComposerPasses` (`@jgengine/core/devtools/frameBreakdown.draw`): Time each post pass of a composer by name, CPU and GPU. Also `BreakdownRenderer`, `BreakdownStore`, `FrameBreakdown`.
|
|
116
|
+
- `LastFrameSample` (`@jgengine/core/devtools/types`): The last frame `devtools.frame.record` saw: its sim milliseconds and named phases.
|
|
117
|
+
- `pagePrefetch` (`@jgengine/core/boot/assetPrefetch`): The page's prefetch: one game per page, started when the game opens. Also `AssetPrefetch`, `PrefetchState`.
|
|
118
|
+
- `ENTER_QUIET_MS` (`@jgengine/core/boot/BootDirector.draw`): How long the model loader must stay idle after Play before the entering screen lifts. Also `BootSignals`.
|
|
119
|
+
- `bootLoadingAreas` (`@jgengine/core/boot/bootScreen.view`): How many world pieces are still loading; Play waits on the entering screen until none are. Also `useBootStep`.
|
|
120
|
+
- `BOOT_SEQUENCE_ATTRIBUTE` (`@jgengine/core/boot/bootSequence`): The page attribute a boot is stamped on, for a check that reads a running game. Also `BOOT_STEPS`, `BootSequence`, `BootSequenceState`, `BootStep`, `pageBoot`.
|
|
121
|
+
- `orderPrefetch` (`@jgengine/core/boot/prefetchTier`): Order a list near tier first, keeping each tier's own order and dropping repeats. Also `PrefetchItem`, `PrefetchTier`.
|
|
122
|
+
- `linkDrawsOffFrame` and `sceneRevealed` (`@jgengine/core/render/shaderWarmup.draw`): link the programs a set of draws would run without drawing them, and wait for a scene's loading cover to lift.
|
|
123
|
+
- `bun run shoot --load` reports the worst frame in the two seconds after the cover lifts, and each run's frame count, worst, median and frames over 33 ms in that window.
|
|
124
|
+
- the agent bridge answers camera_heading { yaw } and teleport { x, z, y?, yaw? }.
|
|
125
|
+
- `FLOW_PRESS_MS` (`@jgengine/core/play/flow`): How long {@link Flow.press} holds its keys. Also `BrowserFlow`, `BrowserFlowSpec`, `BrowserProofFlow`, `FLOW_WAIT_MS`, `Flow`, `FlowCommandResult`, `FlowHold`, `FlowKey` and 7 more.
|
|
126
|
+
- `FlowFailure` (`@jgengine/core/play/flowSteps`): The step a flow failed at and why, with the probe readings at that moment when the game has a probe. Also `FlowRunReport`, `FlowStepRecord`, `FlowStepper`.
|
|
127
|
+
- `runFlowHeadless` (`@jgengine/core/play/headlessFlow.draw`): Play a proof flow headless: the real game through {@link createPlaySession}, keys mapped to the game's own bindings, every wait in game time, so a flow that takes a minute in the browser takes seconds here. Also `HeadlessFlowOptions`.
|
|
128
|
+
- `createPlaySession` (`@jgengine/core/play/playSession.draw`): Boot a `PlayableGame` through the headless runner and tick it the way the shell does: movement from held actions, then the per-frame action phase (bound commands, prompts, hotbar). Also `PlaySession`, `PlaySessionOptions`.
|
|
129
|
+
- `createHeadlessRunner({ game })` boots a whole playable game (what `defineGame` returns): content, phase-synced loop, movement and collision config come from it, and passing them alongside `game` is a type error. `HeadlessRunner.look` reads the heading the walk steers by.
|
|
130
|
+
- `teleportPlayer(ctx, userId, { x, z, y?, yaw? })` (`@jgengine/core/movement/teleport`) moves the body a player controls (their mount while riding), drops carried velocity, snaps the render pose and turns the camera.
|
|
131
|
+
- `PlayableGame<TProbe>` and `GameCaptureConfig<TProbe>`: `defineGame` infers what `capture.probe` returns, so tools reading `typeof game` see each probe by name. `ProbeReadings` names the shape.
|
|
132
|
+
|
|
133
|
+
### Changed
|
|
134
|
+
|
|
135
|
+
- The dev build's missing-model banner names the command that fetches a model no checkout has: `jgengine assets find <name> --game <dir> --provision`.
|
|
136
|
+
- New: bun run data-table <url> --out <file.ts> fetches a web page's data table (a wiki's species stats) once into a typed file a game imports; the page is cached for every worktree and a second run fetches nothing
|
|
137
|
+
- Static batches take composed kit models (models built from `parts` with no roles, variants or motion): each part becomes its own instanced draw at the offset the single-model renderer gives it. Removing a batched prop no longer rebuilds its batch; regrouping one elsewhere still does.
|
|
138
|
+
- The `scenery` catalog flag now covers props a run clears away, such as a yard's clutter; Drift Foundry marks its zone clutter, yard lamps and towers, barricades and exit arch as scenery.
|
|
139
|
+
- Building chunks default to 256 m cells instead of 64 m (`partitionInstances` `cellSize`). Drift Foundry's beauty and shadow draws for buildings drop by about two thirds for about 2% more triangles; Harbor Heat's draws and triangles are unchanged.
|
|
140
|
+
- Box-building window and storefront glass no longer casts shadows when box walls are drawn: each pane sits inside its full wall panel, 2.5 cm proud, so the wall already casts that shadow.
|
|
141
|
+
- Building chunk distance tests reuse the chunk's bounds until its world matrix changes, and building chunks leave three's per-frame matrix pass (`freezeStaticTransforms`, which now also settles the node's parents first).
|
|
142
|
+
- The scene's matrix pass skips object and entity markers that culling hid, and rebuilds them in the pass that shows them again (`@jgengine/core/visibility/culledTransforms.draw`).
|
|
143
|
+
- Culling no longer re-places unchanged objects in its spatial index every frame, and the culling driver stops allocating a sphere and a lookup key per object per frame.
|
|
144
|
+
- After the reveal, each draw of the scene leaves out any drawable whose shader is not linked yet, starts its link off the frame, and draws it on the first frame it is ready (`deferUnlinkedDraws` in `@jgengine/core/render/shaderWarmup.draw`, installed by the scene warmup gate). A link that never reports completion stops hiding its drawable after 30 s.
|
|
145
|
+
- The reflection probe links, in the background, the programs a capture's environment map will need for every material reflecting for the first time, then hands the map over; the first capture included. `compileObjects` takes the material each drawable is about to draw with.
|
|
146
|
+
- The shared screen-space bump (`SURFACE_BUMP_NORMAL_GLSL`, `@jgengine/core/terrain/terrainSurfaceShader.draw`) keeps the incoming normal when the pixel frame is degenerate. A wet puddle's flat normal seen edge-on made the bump vector zero, normalizing it gave one NaN pixel, and bloom's blur spread that pixel over half the frame whenever TAA's jitter landed on it. Model surface detail now uses the same chunk instead of its own copy.
|
|
147
|
+
- Drift Foundry marks its five newer tracks' standing props (edge posts, lamps, clutter, set-pieces) as `scenery`, so they draw through shared instanced batches instead of about 2,000 separate props whose scene graph three walked every frame.
|
|
148
|
+
- `bun run shoot --load`: when a game's own full-screen menu covers the canvas at the reveal (Scrap Signal's broadcast menu stops the world drawing under it), the two-second frame window and the blank-scene read wait until the menu lifts after the Play press, and the run line says so.
|
|
149
|
+
- `buildPolylineIndex(points, reach?)` takes the bound most queries will pass and sizes its grid for it; `nearest` searches outward from the query instead of walking the whole bound, with the same answers.
|
|
150
|
+
- A scatter field over its instance budget thins evenly across the map instead of dropping every instance past the budget in document order (whole regions used to vanish).
|
|
151
|
+
- Scatter resolution about twice as fast: no surface-normal sample unless slope or alignment needs it, numeric spacing buckets.
|
|
152
|
+
- Asset provisioning links a pack a game names only through an asset alias (Fernwild's survivor model was a missing-model cube).
|
|
153
|
+
- Coast signed distance uses a polyline index and a coarse land grid, so a detailed outline costs about the same per sample as the old ellipse.
|
|
154
|
+
- `PopulationTickContext.isActive` is asked at most once per region per tick, and a tick with nothing due no longer copies the respawn queue.
|
|
155
|
+
- has: a feature worded differently from its capability now matches through the code's folder names and doc comments (supply drops, hunger thirst)
|
|
156
|
+
- Doc comments only finish a match the names started, and one comment must carry every word, so a stray word never covers a feature
|
|
157
|
+
- `prepareCollisionMeshSource` and `prepareCollisionMesh` split BVH nodes with a median select instead of a full sort (same tree contents, 4x faster on a 100k-triangle prop).
|
|
158
|
+
- The scene warmup reveals one empty round after the last link instead of three; `linkAhead` also waits for programs the compile reused while another caller was still linking them.
|
|
159
|
+
|
|
160
|
+
| game | scene ready before (ms) | after (ms) |
|
|
161
|
+
|---|---:|---:|
|
|
162
|
+
| harbor-heat | 8787 | 7378 |
|
|
163
|
+
| drift-foundry | 10029 | 8422 |
|
|
164
|
+
| scrap-signal | 8895 | 7233 |
|
|
165
|
+
|
|
166
|
+
Still above 5 s: what is left is the GPU linking 60-100 distinct shader variants one after another on a cold Direct3D cache (about 25-35 ms each, ~3 s per game).
|
|
167
|
+
- In-repo game `jgengine.json` `capabilities` holds only `debt` (`{ reason, issue }` per capability id) and `probes`; adoption is read from the game's imports.
|
|
168
|
+
- `jgengine sync` no longer writes capability answers.
|
|
169
|
+
- lane:close -m writes the change note, commit message and PR body
|
|
170
|
+
- The note lists the exported API the diff added or removed and, for a game change, its engine seam; Closes/Prevention/Root cause lines stay out of the changelog
|
|
171
|
+
- Skill routing for core feature folders is generated (`bun run gen skill-routing`); adding a folder needs no hand edit.
|
|
172
|
+
- Frame breakdown and perf command: where every frame goes
|
|
173
|
+
- Dev builds keep a per-frame breakdown (window.__jgFrameBreakdown, perf panel): CPU self time per engine and game system and per useFrame callback by file:line, GPU ms per pass (shadow, main, each post effect) via timer queries, draws, triangles, programs, uploads, top objects and materials, and hitches over 33 ms with their cause (compile, upload, gc, system, gpu).
|
|
174
|
+
- bun run perf <game> now defaults to 1080p, the uncapped clock and a walk path, writes .scratch/perf/<game>.json, prints a short text summary, and --compare before.json prints a before/after diff. The jgengine-verify skill has a 5-minute guide.
|
|
175
|
+
- Games on jgengine.com load every model, and every game opens through one loading sequence
|
|
176
|
+
- Cause: the production /play build never copied each game's public/ folder (only the dev server served it), so models 404'd on the live site and two scatter loaders crashed hold-the-line on a 404. Each game's files now ship under /play/games/<id>/, with a per-game file list the runner loads from; a .glb or .gltf's own textures resolve there too.
|
|
177
|
+
- Release gate: scripts/check-site-assets.ts runs before every site deploy and fails on any model, texture, sound, pack or model texture a game names that the build lacks.
|
|
178
|
+
- The dev missing-models overlay and placeholder labels never show in production.
|
|
179
|
+
- One boot sequence (open > loading > intro > menu > entering > playing) drives every loading surface: one jgengine loading screen, no second loading screen or flash. A game's files start downloading in the background the moment it opens, near files first and distance/LOD files after; Play enters at once when they are in, else shows the loading screen with real file progress. The page stamps the sequence as data-jg-boot, and bun run shoot --load fails a game whose sequence repeats or goes backwards or whose files 404.
|
|
180
|
+
- Migration: resolveAssetBaseUrl moved from @jgengine/core/assets/assetBase.draw to @jgengine/core/assets/assetBase; installAssetBase stays in assetBase.draw.
|
|
181
|
+
- `PostProcessing` (`@jgengine/core/postfx/PostProcessing.draw`) builds its effect passes switched off and turns them on after the scene's loading cover lifts and their programs have linked off the frame, fading them in over 0.3 s. A settings change that rebuilds the chain does the same. `immediate` draws every effect from the first frame (the shell passes it for posters).
|
|
182
|
+
- The beauty pass under temporal AA jitters only while the TAA pass is on.
|
|
183
|
+
- land: a re-close with only new -m wording rewrites the note and retitles the open PR
|
|
184
|
+
- Proof flows are TypeScript: <game>/proofs/flows/<name>.flow.ts exports defineFlow(game, { state?, browser?, run(f) }) with typed keys and chords, probe checks typed from the game's own capture.probe, f.heading, f.teleport, f.command, f.timeScale and f.waitFor. bun run drive <game> --flow <name> plays it headless by default (under a second where the browser took half a minute) or in the browser with browser: true or --browser. A pass prints one line; a failure names the step, the probe readings and the steps before it. Every game's flows are converted.
|
|
185
|
+
- Each core feature folder names its skill with one // @skill line in its own code; skill routing is generated from those lines, so all 106 folders route exactly as chosen
|
|
186
|
+
- A new folder with no tag gets a guess written into it (the skill whose folders its names and doc comments read most like), visible in the diff; the guess matches the chosen skill 72 times in 105, up from 64
|
|
187
|
+
- `bun run lane:close` is the one verification pass: it runs `gen` and `verify:fast` itself, so nothing runs by hand before it, and its PR body carries whole commit messages, not subjects alone.
|
|
188
|
+
- The Bends section lists only deleted assertions, skipped tests and disabled checks; a clean diff gets none, and `changes/*.md` folding keeps it out of the changelog.
|
|
189
|
+
- `bun run check games-capabilities` checks ids and issue numbers only: debt on an unknown, imported or probe-less capability, a debt entry with no issue number, a probe for a module the game does not adopt, a stale ledger. It no longer judges debt reasons, reads GitHub issues or keeps a debt budget.
|
|
190
|
+
- `bun run check games-framework` exempts a game file through a first-line `// framework-ok: <reason>`; the `--allow` flag CI never saw is gone, so local and CI run the same command.
|
|
191
|
+
- Root command guards are 600 s each, matching the 10-minute rule.
|
|
192
|
+
|
|
193
|
+
### Fixed
|
|
194
|
+
|
|
195
|
+
- A skinned character drawn on its own rig followed its entity again: the scene's matrix pass refreshes an attached skin's bind inverse the way three's own pass does.
|
|
196
|
+
- the boss arena's generated skill routing, regenerated on the current beta (it files under gameplay).
|
|
197
|
+
- Fernwild's number keys never equipped anything: the hotbar inventory was not marked as the HUD hotbar and items had no use handler, so Digit keys fell through to the bag.
|
|
198
|
+
- Distant skinned creatures drew at the wrong size: the character crowd measured a skinned mesh by its node transform instead of its bones, so a boar normalised to 1.9 m stood about 9 m tall past the crowd distance. The bake now measures exactly as the full model path does.
|
|
199
|
+
- A skinned model with no clips (a Rocketbox duck) baked into the crowd lying on its side at its raw quantized size; static crowd copies now bake each vertex at its skin-applied bind pose.
|
|
200
|
+
- The stray-browser sweep killed a dead run's Chrome and removed its profile at once; on Windows a folder whose files Chrome still held stays until Chrome has exited, so the sweep's test failed now and then under a loaded verify. The real kill now waits for the browser to exit before removing its profile. (Seen failing twice in `verify:fast`; not reproduced on demand, so the fix rests on the cause, not on a red-then-green run.)
|
|
201
|
+
- Shift never held its action: pressed keys fold ShiftLeft/ShiftRight to `Shift`, but bindings kept `ShiftLeft` as written, so `sprint: ["ShiftLeft"]` (the documented form, in ten games) never matched. Binding and pressed key now fold the same way.
|
|
202
|
+
- Drive proofs could not hold a modifier: the chord sender gave Shift the key name `ShiftLeft` and no modifier bit. One shared sender now sends a real Shift, Control or Alt, used by both drive paths.
|
|
203
|
+
- Bare fists did no torpor. A punch now deals ARK's 1.75x its damage as torpor (a club 3.75x).
|
|
204
|
+
- The HUD's health read the survivor's vitals copy, which catches up with the health pool only on the next tick, so it could show a stale value. It now reads the pool itself.
|
|
205
|
+
- Phiomia stats were guesses; they are now ARK's (stamina 300, food 3000, weight 200, melee 10, walk 1.5 m/s).
|
|
206
|
+
- a lane no longer hand-writes an Engine seam line, an API list or a changes/ file
|
|
207
|
+
- closing again with -m rewrites the open PR's body, not only the note
|
|
208
|
+
- headless runs start on the game's camera.initialYaw, so a walk faces the same way as in the page.
|
|
209
|
+
- bun run check games-design checked no games when run from the repository root, as CI runs it; it now checks all 18.
|
|
210
|
+
- The headless runner ignored the game's own `ctx.camera.turnTo`, so a teleport cheat that faced the camera left headless walks going the old way.
|
|
211
|
+
- A fixed-rate game stepped headlessly lost every tick past its catch-up cap on a long step.
|
|
212
|
+
- The dev server no longer watches `.jgengine/`, the folder `jgengine desktop` stages its build in, so desktop builds stop reloading or crashing a running `bun dev`. Root cause: Vite's default watch ignores cover only `.git`, `node_modules` and `test-results`, so every write into the staging folder reached the watcher. Generated `vite.config.ts` files set `server.watch.ignored` (`jgengine sync` carries it to existing games), and the standalone `.gitignore` lists `.jgengine/`.
|
|
213
|
+
|
|
214
|
+
### Removed
|
|
215
|
+
|
|
216
|
+
- The not-applicable decline and the per-game `adopts` list; `CapabilityDecline` is replaced by `CapabilityDebt`.
|
|
217
|
+
- `@jgengine/core/play/flow` no longer exports `BrowserFlow`, `BrowserFlowSpec`, `BrowserProofFlow`, `FLOW_PRESS_MS`, `FLOW_WAIT_MS`, `Flow`, `FlowCommandResult`, `FlowHold` and 8 more.
|
|
218
|
+
- `@jgengine/core/play/flowSteps` no longer exports `FlowFailure`, `FlowRunReport`, `FlowStepRecord`, `FlowStepper`.
|
|
219
|
+
- `@jgengine/core/play/headlessFlow.draw` no longer exports `HeadlessFlowOptions`, `runFlowHeadless`.
|
|
220
|
+
- `@jgengine/core/play/playSession.draw` no longer exports `PlaySession`, `PlaySessionOptions`, `createPlaySession`.
|
|
221
|
+
- the JSON flow format, and drive's --steps and --check flags with the look, aim-at and move-mouse steps; f.heading replaces them.
|
|
222
|
+
- `check fix-has-test`: it could not tell a bugfix from any other change, and `verify:fast` already exits 2 when a changed runtime file reaches no test.
|
|
223
|
+
- `scripts/debt-budget.json` and the `--write` / `--no-issues` flags of `check games-capabilities`; `land --watch`.
|
|
224
|
+
- `capabilities.json` `domain`: it only served the deleted reason check.
|
|
225
|
+
|
|
29
226
|
## 0.48.0
|
|
30
227
|
|
|
31
228
|
### Migrate
|
|
@@ -192,10 +389,6 @@ Prevention: none - the change is two redrawn PNG images with no code path a test
|
|
|
192
389
|
- `arrangeHudPanels` shrinks the HUD before lifting a panel off its edge into the play view: a bottom bar squeezed between the joystick and the action cluster (Fernwild's build bar) stays on the bottom edge at a smaller zoom, and lifts only when no zoom down to `minScale` keeps it there.
|
|
193
390
|
- Drift Foundry registers its steering and pedal pads as control regions; Apex Nights' garage panel grows to fit its price tags instead of spilling onto the action buttons.
|
|
194
391
|
|
|
195
|
-
### Bends
|
|
196
|
-
|
|
197
|
-
- Deleted the three `touchDockInsets` unit tests along with the function they tested; the new "touch controls are obstacles" browser test and the solver tests cover the behaviour that replaced it.
|
|
198
|
-
|
|
199
392
|
## 0.46.0
|
|
200
393
|
|
|
201
394
|
### Migrate
|
|
@@ -664,10 +857,6 @@ Verification:
|
|
|
664
857
|
- `bun scripts/check-types-all.ts` and the six games' `check-types`: pass
|
|
665
858
|
- fernwild in the browser: the build strip shows each structure with its cost and key, greyed when unaffordable, and clicking one starts building
|
|
666
859
|
|
|
667
|
-
## Bends
|
|
668
|
-
|
|
669
|
-
- `kitHud.test.tsx` lost its `KitActionBar` test with the component; the same behaviour (detail text, unaffordable as ghost, meters, prompt above) is asserted on `ActionBar look="kit"` in `actionHud.test.ts`.
|
|
670
|
-
- The `hudBlockOverlaps` tests in `hudLanes.test.ts` now run the same cases through `detectLayoutCollisions`; no case was dropped.
|
|
671
860
|
- City blocks are traced from the exact street graph instead of welding centerlines, so curved and filleted streets still close their blocks. Every city closes more blocks and places more buildings (the default district goes from 184 to 235 lots; Manhattan from 134 to 225). Apex Nights, Briarcrown, Ember Command, Hold the Line and Saltwick place city volumes and get the fuller fabric.
|
|
672
861
|
- Playground: "Lot frontage", "Lot depth" and "Plot variety" become one "Lot scale" dial (`?lotScale=`); "Landmarks" goes up to 0.5.
|
|
673
862
|
|
|
@@ -677,10 +866,6 @@ Verification:
|
|
|
677
866
|
- Playground in the browser: city and street race grow with buildings, an arena landmark, and the lap with kerbs and barriers
|
|
678
867
|
- `shoot apex-nights` and `shoot hold-the-line`: load and render normally
|
|
679
868
|
|
|
680
|
-
## Bends
|
|
681
|
-
|
|
682
|
-
- `cityKind.test.ts` pins the default district's lot counts; the pins move from 184/189 to 235/252 because the fabric now closes more blocks, which is the intended change.
|
|
683
|
-
- `cityLayout.ts` stays: it is Harbor Heat's authored road classes over the shared lot deriver, not a generator.
|
|
684
869
|
- Drop the i-have-adhd plugin from the repo settings; nothing on this PC uses it
|
|
685
870
|
- `check concept-homes` scans `games/*/src`, `games/*/packages/*/src` and `games/shared`. Each game is its own namespace (two games may each have a `Hud`); a clash with an engine export fails. The names a game's entry files hand the engine (`game`, `world`, `editorLayers`, `editorCatalogs`, `uiScenario` in top-level `src/` files) are the contract and pass.
|
|
686
871
|
- Games use the engine where theirs was a copy: `UiKitName` (`@jgengine/react/uiKits`) in the nine idea games, `FogBounds` in ember-command, `Vec2` in beacon-bastion, `clamp` in odd-orbit (`clampNeed` wraps it), and `openTitle` (`@jgengine/core/game/gameFlow`) inside beacon-bastion's `openKeepTitle` and deepward's `openMarrowTitle`.
|
|
@@ -692,9 +877,6 @@ Verification:
|
|
|
692
877
|
- every game's `check-types`: pass
|
|
693
878
|
- `bun test` on the touched games, `games/shared` and `scripts/check-concept-homes.test.ts`: pass
|
|
694
879
|
|
|
695
|
-
## Bends
|
|
696
|
-
|
|
697
|
-
- None. The entry-export allowance covers only the five names the template writes into top-level `src/` files; the same name anywhere else in a game still fails (tested).
|
|
698
880
|
- A ground over 66k vertices splits into 128-cell tiles, each a `THREE.LOD` at strides 1/2/4/8/16. Scene load builds only the coarsest level of every tile plus what the camera needs nearby; finer levels build in the background as the camera reaches them. Each level drops a skirt below its edges, so neighbouring tiles at different detail show no crack. Normals come from the sampled heights, so a tile edge lights the same from both sides.
|
|
699
881
|
- A pulled terrain scan that may be missing no longer meshes the ground twice (once for the procedural stand-in, again when its maps load): the same mesh swaps material.
|
|
700
882
|
- A compatible prop alone in its 128 m cell joins the nearest batch of its model, and a model with no cell batch gathers its lone props into one. Drawn as a plain mesh it linked a second, non-instanced program for every material the batched copies already had (harbor-heat start: 112 programs, now 107).
|
|
@@ -716,9 +898,6 @@ Verification:
|
|
|
716
898
|
- `bun scripts/check-types-all.ts`: 23 dirs, 0 failing; every game's `check-types`: pass
|
|
717
899
|
- `bun test` on threshold, stats, vec2/vec3, waypoints, ui, movement, combat, nav, ws, node, convex, shell audio: 1693 pass
|
|
718
900
|
|
|
719
|
-
## Bends
|
|
720
|
-
|
|
721
|
-
- None. The concept-homes template-literal skip removes false positives only (text a generator prints is not a definition); it has its own test.
|
|
722
901
|
- `HudCanvas` places every panel with `arrangeHudPanels` instead of nine independent anchor regions, so panels in different anchors no longer run into each other on phone and tablet screens.
|
|
723
902
|
- Beacon Bastion, Deepward, Harbor Heat, Odd Orbit and Scrap Signal declare one anchor and a priority per panel; their per-size placements are gone.
|
|
724
903
|
- Ember Command draws the ledger, orders note, pause seal, survey map, Bram's card, command board, order hints and level-up writ as `HudCanvas` panels with one anchor and a priority each, instead of hand-placed CSS. The map folds into a toggle when there is no room for it rather than being hidden below 1180px, and the hand-registered layout regions are gone because every panel registers its own.
|
|
@@ -855,12 +1034,6 @@ Verification:
|
|
|
855
1034
|
- `bun test scripts/load-check.test.ts scripts/verify-fast.test.ts packages/shell/src/terrain/terrainDetailMaterial.test.ts`: pass
|
|
856
1035
|
- `bun run shoot --load deepward --guard` with a planted stall: exit 1 (above)
|
|
857
1036
|
|
|
858
|
-
## Bends
|
|
859
|
-
|
|
860
|
-
- None.
|
|
861
|
-
|
|
862
|
-
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
863
|
-
|
|
864
1037
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
865
1038
|
- `Ocean` (`@jgengine/shell/water`) animates its waves on the game clock (`ctx.time.advancedSeconds()`), so the sea stops when the game pauses, follows slow motion and lines up with the water surface buoyancy samples. Before any game time has passed (a title screen), or with no game (an editor preview), it runs on real time.
|
|
866
1039
|
- Ocean glints follow the sun the mounted `Sky` placed, through the day-night cycle, and fade out once the sun sets; without a sky they keep the fixed day heading. `syncOceanMaterial` takes the sun direction as an optional fourth argument.
|
|
@@ -889,9 +1062,6 @@ Verification:
|
|
|
889
1062
|
- `bun test packages/jgengine scripts/scaffold-editor.test.ts`: 387 pass
|
|
890
1063
|
- fernwild and odd-orbit run from their own folder (`bun run dev`): styled title menu, fernwild's HUD and build strip in play
|
|
891
1064
|
|
|
892
|
-
## Bends
|
|
893
|
-
|
|
894
|
-
- `templates.test.ts`: the "in-repo tsconfig paths match the exact map doctor requires" test went with the map; it is replaced by a test that the in-repo wiring extends the base tsconfig, runs on `games/shared` and names the game by id. The AGENTS.md test now runs on the standalone template only, since the in-repo template no longer writes one.
|
|
895
1065
|
- Moved to `@jgengine/core/terrain/`: `terrain` (was `world/terrain`), `terraform` (was `world/terraform`), `pathTerrain` (was `world/pathTerrain`), `terrainGuides` (was `world/terrainGuides`), `terrainGrade` (was `movement/terrainGrade`), and `terrainPaint` (was `editor/terrainMaterials`).
|
|
896
1066
|
- `terrainGroundColorSampler` moved to `@jgengine/shell/terrain/terrainGroundColor` (was `environment/terrainGroundColor`).
|
|
897
1067
|
- The editor's paint palette is `TerrainPaintPreset` / `TERRAIN_PAINT_PRESETS` / `TERRAIN_PAINT_COLORS` (was `TerrainMaterial` / `TERRAIN_MATERIALS` / `TERRAIN_MATERIAL_COLORS`), imported from `@jgengine/core/terrain/terrainPaint`. `@jgengine/core/editor` and `@jgengine/editor` no longer re-export it.
|
|
@@ -914,10 +1084,6 @@ Root cause: gameplay keys are read on the shell's focusable wrapper. Closing the
|
|
|
914
1084
|
Verification:
|
|
915
1085
|
- `bun test packages/shell/src/playSurfaceFocus.test.ts`: pass (new test fails without the fix)
|
|
916
1086
|
- fernwild in the browser: New Game, Escape opens pause, Escape closes it, focus is back on the play surface, Escape opens pause again (before: focus stayed on `<body>` and pause did not reopen).
|
|
917
|
-
|
|
918
|
-
## Bends
|
|
919
|
-
|
|
920
|
-
- None.
|
|
921
1087
|
- `jgengine find <feature>` answers by folder and file names once a folder is named for the query: an export that only mentions the word (`MAX_OCEAN_WAVES` for "waves") no longer shows `shell/water` as a home for waves.
|
|
922
1088
|
- `shoot` and `drive` `--size half` keep the page's full CSS viewport and halve only the pixel ratio, so a half-size shot lays out exactly like a full one. Before, the 800x450 viewport switched games into their compact HUD and the layout-collision check failed the run (Scrap Signal, Beacon Bastion).
|
|
923
1089
|
- `EntityPreview` no longer throws "Cannot read properties of null (reading 'addEventListener')" when it unmounts while react-three-fiber is still creating its canvas (a dialogue or menu that re-mounts a portrait in one beat, e.g. Harbor Heat's Marco dialogue).
|
|
@@ -930,9 +1096,6 @@ Verification:
|
|
|
930
1096
|
- the new "hovering the menu while a field has focus" test fails without the fix (focus lands on the body) and passes with it
|
|
931
1097
|
- waits are `requestAnimationFrame` frames, not `waitForTimeout`
|
|
932
1098
|
|
|
933
|
-
## Bends
|
|
934
|
-
|
|
935
|
-
- None. No assertion was relaxed; the timer waits became frame waits and one regression test was added.
|
|
936
1099
|
- A shadow-casting light whose shadow map no frame had drawn yet left every lit draw failing (WebGL: "Mismatch between texture format and sampler type"), so the scene showed only its clear colour. The shader warmup holds the frame's draws behind the loading cover, and cascaded shadows took the first held frame as their one refresh, so far cascades never got a map. A cascade now refreshes until it has a map, or when something asks for a redraw.
|
|
937
1100
|
- The warmup's own draws ran before any shadow map existed, so every lit warmup draw failed and finished nothing. The first warmup draw now makes the missing maps, and the next real frame redraws them with every caster. WebGL errors on a cold load drop from 230-250 per game to under 50.
|
|
938
1101
|
- Ocean and authored water drew a different wave set from the CPU `waterSurface`, so floating bodies bobbed on waves that were not on screen. Both now come from `synthesizeWaves`.
|
|
@@ -942,8 +1105,6 @@ Verification:
|
|
|
942
1105
|
|
|
943
1106
|
- `buildingSpatialBatch` (replaced by `partitionInstances`), `chunkScatterInstances` / `ScatterChunk` (replaced by `groupInstancesByCell`).
|
|
944
1107
|
|
|
945
|
-
Bends:
|
|
946
|
-
- Left on their own writers: `InstancedBodies` and `SpriteBatch` (per-frame packed-array hot loops over thousands of bodies), `staticObjectBatches` (skips the upload when nothing moved), `ImpostorCardPool` (packs many slices into one shared mesh), `crowdBatch` / `SkinnedInstances` (the crowd concept) and `ImpactFeedback` (combat feedback, another builder's area).
|
|
947
1108
|
- `GridWorldScene` now computes a culling sphere where it had none.
|
|
948
1109
|
|
|
949
1110
|
Verification:
|
|
@@ -953,17 +1114,11 @@ Verification:
|
|
|
953
1114
|
- Unverified: LLMVille's endless datacenter on screen (it needs its Convex backend to run); it type-checks.
|
|
954
1115
|
- `jgengine upgrade` (with `--plan`, `--apply`, `--to`, `--json`), with no alias.
|
|
955
1116
|
|
|
956
|
-
## Bends
|
|
957
|
-
|
|
958
|
-
- `upgrade.test.ts` and `upgradeCatalog.test.ts` are deleted. Their changelog tests moved to `releaseNotes.test.ts`; the catalog cases (bump, `--check`, `catalog:` kept, downgrade refused) are now sync tests.
|
|
959
1117
|
- Sync always targets the CLI's own version (`npx jgengine@<version> sync` picks a version). The registry-publication check and the `--to` and `--json` flags from upgrade are dropped.
|
|
960
|
-
- The harness no longer runs a project's own `scripts/shoot.mjs`/`drive.mjs` copy
|
|
961
|
-
- The harness sources moved from `templates/gameFiles.ts` to `templates/harnessScripts.ts`; `check-scaffold-launch.ts` reads them there.
|
|
1118
|
+
- The harness no longer runs a project's own `scripts/shoot.mjs`/`drive.mjs` copy; the bundled harness runs instead.
|
|
962
1119
|
- The duplicate `TerrainMaterial` export in `editor/terrainMaterials`.
|
|
963
1120
|
|
|
964
|
-
|
|
965
|
-
- `map/terrainMap` (bakes a top-down map image) stays with the map concept; `shell/src/terrain.ts` stays as the package's `@jgengine/shell/terrain` entry.
|
|
966
|
-
- Moved files count as new exports, so 19 terrain and terraform symbols that had no doc line got one.
|
|
1121
|
+
- 19 terrain and terraform symbols that had no doc line got one.
|
|
967
1122
|
|
|
968
1123
|
Verification:
|
|
969
1124
|
- `bun run check types`, `bun run gen` (all stages), `bun run check concept-homes --all` (0 duplicates), `bun test packages/core/src packages/shell/src packages/editor/src apps/web/src scripts` (11223 pass, 0 fail).
|
|
@@ -971,12 +1126,6 @@ Verification:
|
|
|
971
1126
|
- Standalone editor: created a terrain, the Paint tab lists all eight presets, a grass stroke paints the ground.
|
|
972
1127
|
- Play shots of Beacon Bastion, Harbor Heat, Odd Orbit, Scrap Signal and Fernwild render as before; load guard passes for all five (Harbor Heat failed once while the full test suite ran beside it, then passed alone).
|
|
973
1128
|
|
|
974
|
-
### Bends
|
|
975
|
-
|
|
976
|
-
- Remaining instanced/plain pairs in harbor-heat are vehicles and characters: far ones draw through the baked crowd (its own shader), near ones on their full rig. That is two techniques, not one material compiled twice, so they stay.
|
|
977
|
-
- Making every lone prop a batch of one was tried and dropped: drift-foundry's scene ready went from about 10 s to 19–28 s (per-batch machinery on hundreds of one-prop batches).
|
|
978
|
-
- None.
|
|
979
|
-
|
|
980
1129
|
## 0.45.0
|
|
981
1130
|
|
|
982
1131
|
### Migrate
|
|
@@ -1121,8 +1270,6 @@ Root cause: #2505 removed the `games:<id>` root scripts without checking the des
|
|
|
1121
1270
|
Verification:
|
|
1122
1271
|
- `bun test packages/jgengine/src/create.test.ts apps/desktop/project/commands.test.ts`: 23 pass, 0 fail.
|
|
1123
1272
|
- `bun --cwd=games/harbor-heat run dev`, the argv `buildShellCommand` builds for Standalone: serves `http://localhost:5173/` with 200, title "Harbor Heat", and `#root`.
|
|
1124
|
-
|
|
1125
|
-
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
1126
1273
|
- `assets pull` sends the GitHub token (`GITHUB_TOKEN`, `GH_TOKEN`, else the signed-in `gh` CLI's) to `api.github.com`, `raw.githubusercontent.com` and `codeload.github.com`, so parallel pulls on one machine no longer hit the anonymous 60-an-hour limit and fail with 403. Every pull request also has a hard budget (`JG_FETCH_TIMEOUT_MS`, 10 minutes) and fails naming the URL that stalled.
|
|
1127
1274
|
- A model's `materialAssignments` no longer throw "matched no slots" on a variant level whose simplified asset dropped the selected slot. The source level still validates strictly, and a level skips only the selectors it lacks; `applyMaterialAssignments` takes `{ unmatched: "skip" }` for the same policy. Harbor Heat's HHPD cruiser crashed on spawn at its far level (issue #259).
|
|
1128
1275
|
- Camera shake now reaches the screen on every rig: `cameraShake()` used to feed a channel the rigs never read, and the orbit, first-person and inspection rigs applied no shake at all.
|
|
@@ -1143,11 +1290,6 @@ Prevention: `games-sync` gate; `packages/jgengine/src/sync.test.ts`.
|
|
|
1143
1290
|
|
|
1144
1291
|
Verification: `bun test packages/jgengine/src` pass; `bun run jgengine sync --check` up to date for all 18 games; a `--template 3d` game created outside the repo with npm boots, and after renaming it and turning the editor off in `jgengine.json`, `jgengine sync` rewrote the page, wiring and package.json and it still boots.
|
|
1145
1292
|
|
|
1146
|
-
## Bends
|
|
1147
|
-
|
|
1148
|
-
- Deleted three template tests (`world`, `track`, `places` `.test.ts.tmpl`) together with the `games:new` generator they tested.
|
|
1149
|
-
- `games/scripts/vite-engine-source.test.ts` no longer checks the deleted `games:new` template's `vite.config.ts`; every game's config is still checked.
|
|
1150
|
-
- `games/scripts/check-capabilities.test.ts` and `scripts/ci-scope.test.ts` expectations changed to the new file and message names, not loosened.
|
|
1151
1293
|
- `packages/jgengine/src/sync.test.ts` builds its games under the OS temp folder (`fixture-location: allow`): a standalone game has to sit outside every workspace.
|
|
1152
1294
|
- Capability answers are synced only for games in the engine repo's `games/`; an outside game's `jgengine.json` has none (the ledger reads engine source).
|
|
1153
1295
|
- `physics/kinematicVehicle`, `physics/vehicleBody`, `damagedTuning`, and Harbor Heat's own tyre-smoke glow (the engine's `tires` option replaces it). The retired `run-loop` and `kinematic-vehicle` capabilities are dropped from game declarations.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/convex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "Convex adapters for JGengine: game transport, presence transport, and backend wiring over @jgengine/core.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"check-types": "tsgo --noEmit -p tsconfig.json"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@jgengine/core": "^0.
|
|
33
|
+
"@jgengine/core": "^0.49.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"convex": "^1.31.2",
|
|
@@ -20,13 +20,13 @@ Adapted for JGengine from [EveryInc/compound-engineering-plugin `ce-handoff`](ht
|
|
|
20
20
|
|
|
21
21
|
### Outcome
|
|
22
22
|
|
|
23
|
-
Create one immutable handoff at the destination the user requested, or use the managed temporary store by default. Briefly summarize what the handoff captured, then report its final path or URL, retention limits, and continuity warnings. The handoff supplements authoritative artifacts (issues, PRs,
|
|
23
|
+
Create one immutable handoff at the destination the user requested, or use the managed temporary store by default. Briefly summarize what the handoff captured, then report its final path or URL, retention limits, and continuity warnings. The handoff supplements authoritative artifacts (issues, PRs, skills); it does not replace them.
|
|
24
24
|
|
|
25
25
|
### Build the handoff
|
|
26
26
|
|
|
27
27
|
1. Distill the current objective and the user's latest intent. If a focus was supplied, make it the `resume_focus`.
|
|
28
28
|
2. Inspect only the workspace state needed to explain what exists now. Use JGengine project instructions already in context (`AGENTS.md`, `workflow`, domain skills).
|
|
29
|
-
3. Point to plans, issues, commits, diffs, documentation, and relevant files instead of reproducing their contents. Prefer repository-relative paths (`
|
|
29
|
+
3. Point to plans, issues, commits, diffs, documentation, and relevant files instead of reproducing their contents. Prefer repository-relative paths (`packages/…`, `games/…`, `changes/…`).
|
|
30
30
|
4. Redact secrets, credentials, tokens, and unrelated personal information. Preserve operational paths only when the next agent needs them.
|
|
31
31
|
5. Write or publish with existing tools. If the user named another path, folder, format, or publication destination, honor it. Do not also create a managed-store copy unless asked.
|
|
32
32
|
|
package/skills/jgengine/SKILL.md
CHANGED
|
@@ -19,15 +19,15 @@ capability, adopt it, or improve its shared owner before adding reusable game-lo
|
|
|
19
19
|
Games keep unique rules, content, assets and presentation. Custom markup is not a reason
|
|
20
20
|
to copy focus handling, input tracking, storage or model lifecycle.
|
|
21
21
|
|
|
22
|
-
Check
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
Check engine `beta` before recreating an upstream fix: games run the engine source from
|
|
23
|
+
the same commit, so a fix that exists is adopted by importing it. After adoption, remove
|
|
24
|
+
superseded local machinery and verify the actual game. Outside this repository, report
|
|
25
|
+
missing SDK contracts upstream rather than treating a local engine implementation as the
|
|
26
|
+
finished solution.
|
|
27
27
|
|
|
28
28
|
## 1. Establish the target
|
|
29
29
|
|
|
30
|
-
For greenfield builds, write `src/art-direction.md` before any code.
|
|
30
|
+
Before building a game or a big feature, plan it: `npx jgengine plan <game> --brief "<brief>"` writes `PLAN.md`; list every feature the brief implies under `## Features` and run `npx jgengine plan <game>` again to fill each feature's engine owner and the gaps; then order `## Milestones`, one PR each, and tick each as it lands. For greenfield builds, also write `src/art-direction.md` before any code.
|
|
31
31
|
|
|
32
32
|
Treat the pitch as a unique composition of needs, not a genre to fill in — never reach for a genre kit, preset, archetype, or class template ("default sports car", "default RPG", "default FPS") in the SDK or skills (see [AGENTS.md](../../../AGENTS.md)). Use `game-design` to turn the pitch into a testable player promise, verbs, loops, progression, failure, and completion scenario. Use `level-design` when play depends on authored spaces, routes, encounters, exploration, or spatial pacing. Capture only decisions that change architecture:
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ Treat the pitch as a unique composition of needs, not a genre to fill in — nev
|
|
|
38
38
|
- HUD/menu states and **custom** UI art direction (compose shipped building blocks + HudTheme; no unskinned stock face)
|
|
39
39
|
- one observable completion scenario
|
|
40
40
|
|
|
41
|
-
Pick the dimension first: `create` defaults to a 3D world (terrain, sky, player model, editor scene); a grid, card, tile, or other board game with no 3D scene takes `create --template 2d` (a `board` world drawn by GameUI over `@jgengine/core/puzzle/cellGrid`). `create --template 3d|2d|builder` is the one way to start a game, in this monorepo too (`bun run jgengine create "<Title>"` lands in `games/`); never copy `games/*`. The game's `jgengine.json` says what it is and uses (template, editor, `@jgengine` packages, and in-repo its
|
|
41
|
+
Pick the dimension first: `create` defaults to a 3D world (terrain, sky, player model, editor scene); a grid, card, tile, or other board game with no 3D scene takes `create --template 2d` (a `board` world drawn by GameUI over `@jgengine/core/puzzle/cellGrid`). `create --template 3d|2d|builder` is the one way to start a game, in this monorepo too (`bun run jgengine create "<Title>"` lands in `games/`); never copy `games/*`. The game's `jgengine.json` says what it is and uses (template, editor, `@jgengine` packages, and in-repo its adoption debt and boot probes). Change what a game uses by editing it, then run `jgengine sync`, which also brings a game up to the running engine: its `@jgengine` versions and its template's shared files (wiring, page, tooling scripts, AGENTS.md). A shared file the game rewrote goes in its `owns` list so sync leaves it alone.
|
|
42
42
|
|
|
43
43
|
For an existing game asked to pick up a new engine release ("jgengine published a new version, grab it"), run `npx jgengine@latest sync` in the project (or at its workspace root): it moves every `@jgengine` pin and catalog entry to that release, refreshes the template's files, and prints every Migrate step and Adopt-worthy addition in between. Install, migrate oldest-first, then work the Adopt lists instead of reconstructing the changelog by hand.
|
|
44
44
|
|
|
@@ -70,7 +70,7 @@ World content routes editor-first: any request that adds, moves, restyles, or re
|
|
|
70
70
|
Within each selected domain:
|
|
71
71
|
|
|
72
72
|
0. Fastest start for a common composition: `npx jgengine recipe <name>` prints a vetted, SDK-typechecked snippet (imports + wired code) (`combat-loop`, `loot`, `world-drops`, `click-target`, `ability-bar`, `hitscan-weapon`, `rolled-gear`, `enter-vehicle` and more); `npx jgengine recipe` lists them. Copy that instead of reconning per-file.
|
|
73
|
-
1. Search capabilities by intent; do not read `capabilities.md` top to bottom. `npx jgengine find <intent>` names the concept's home folder in each layer (read from the engine source or the installed packages), searches every domain's index and every recipe at once, and prints the primitive and its import (or the recipe to open) and matching assets; agents can use `--json --limit 5` for ranked imports and recipes with total, partial-match, and truncation fields. In the monorepo, `rg -i "<intent>" .claude/skills/*/capabilities.md` does the same search. Search the moment you are tempted to hand-roll a HUD window, inventory grid, paperdoll, hotkey listener, minimap, stat bar, or character motion. A `z-index`, a `keydown` for a panel, a `<div>` inventory, or a limb bob almost certainly has a shipped drop-in (`usePanels`/`PanelHost`, `InventoryGrid`, `CharacterSheet`/`Paperdoll`, `PartMotionRig`, `EntityPreview`); games own the *skin*, not the re-derivation.
|
|
73
|
+
1. Search capabilities by intent; do not read `capabilities.md` top to bottom. Several features at once: `npx jgengine has torpor riding "snap building"` answers each (covered, partial or gap, with folders and imports) as one JSON document. `npx jgengine find <intent>` names the concept's home folder in each layer (read from the engine source or the installed packages), searches every domain's index and every recipe at once, and prints the primitive and its import (or the recipe to open) and matching assets; agents can use `--json --limit 5` for ranked imports and recipes with total, partial-match, and truncation fields. In the monorepo, `rg -i "<intent>" .claude/skills/*/capabilities.md` does the same search. Search the moment you are tempted to hand-roll a HUD window, inventory grid, paperdoll, hotkey listener, minimap, stat bar, or character motion. A `z-index`, a `keydown` for a panel, a `<div>` inventory, or a limb bob almost certainly has a shipped drop-in (`usePanels`/`PanelHost`, `InventoryGrid`, `CharacterSheet`/`Paperdoll`, `PartMotionRig`, `EntityPreview`); games own the *skin*, not the re-derivation.
|
|
74
74
|
2. Open `api.md` (monorepo) or the package `.d.ts` only when exact signatures are needed; grep it, never read it whole.
|
|
75
75
|
3. Open the linked reference only for a deeper recipe or trap.
|
|
76
76
|
4. For how several primitives wire into a running loop, read the domain's `recipes/` — connected, genre-free walkthroughs organized by composition seam; this skill's own [recipes/minimal-game.md](recipes/minimal-game.md) is the default whole-game walkthrough. Prefer a recipe over reading a game's source; the games under `games/*` are not templates or references, and a hybrid game is just a different composition of the same primitives.
|
package/skills/jgengine/api.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.49.0" — Installed `@jgengine/core` semver - compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
@@ -274,6 +274,23 @@
|
|
|
274
274
|
- `FrameModule` (interface): interface FrameModule — Frame subsystem exposing the frame-timing, profiling, and render-sample facades.
|
|
275
275
|
- `createFrameModule` (const): const createFrameModule: (deps: { readProbes: () => Record<string, unknown>; }) => FrameModule — Create the frame subsystem that records frame/sim/phase timings, computes stats, and captures long-frame events.
|
|
276
276
|
|
|
277
|
+
## @jgengine/core/devtools/frameBreakdown
|
|
278
|
+
|
|
279
|
+
- `DrawCost` (interface): interface DrawCost — One object or material's share of the draws: totals while gathering, per-frame averages in a summary.
|
|
280
|
+
- `FrameBreakdownRecord` (interface): interface FrameBreakdownRecord — One frame, as the hooks saw it. GPU times arrive a few frames late; `gpu` is null until they do.
|
|
281
|
+
- `FrameBreakdownSummary` (interface): interface FrameBreakdownSummary — A window of frames, summarized for a person or a diff.
|
|
282
|
+
- `HITCH_MS` (const): const HITCH_MS: 33 — Where each frame goes: the record of one frame (CPU per system, GPU per render pass, draws, shader compiles, uploads, heap) and the summary of a window of them (percentiles, the costliest systems, passes, objects and materials, and every hitch with what caused it). This file is the arithmetic; `frameBreakdown.draw.ts` fills the records from a live renderer.
|
|
283
|
+
- `Hitch` (interface): interface Hitch — A frame over {@link HITCH_MS}, with the cause read from what happened inside it.
|
|
284
|
+
- `NamedCost` (interface): interface NamedCost — A named cost over the window: its average per frame and its 95th percentile.
|
|
285
|
+
- `Spread` (interface): interface Spread — Percentiles of one series.
|
|
286
|
+
|
|
287
|
+
## @jgengine/core/devtools/frameBreakdown.draw
|
|
288
|
+
|
|
289
|
+
- `BreakdownRenderer` (interface): interface BreakdownRenderer — What the breakdown needs from a three.js WebGLRenderer.
|
|
290
|
+
- `BreakdownStore` (interface): interface BreakdownStore — What it needs from the r3f store: the subscriber list and the subscribe call.
|
|
291
|
+
- `FrameBreakdown` (interface): interface FrameBreakdown — A running breakdown.
|
|
292
|
+
- `measureComposerPasses` (function): function measureComposerPasses(composer: { passes: readonly object[] }): void — Time each post pass of a composer by name, CPU and GPU. Cheap when no breakdown is installed; call it each frame before `composer.render` so passes added later are timed too.
|
|
293
|
+
|
|
277
294
|
## @jgengine/core/devtools/frameSampler.view
|
|
278
295
|
|
|
279
296
|
- `FrameReportInputs` (interface): interface FrameReportInputs — Everything the report needs, gathered by whichever producer is asking.
|
|
@@ -395,6 +412,7 @@
|
|
|
395
412
|
- `DiscoveredEntry` (interface): interface DiscoveredEntry — A control auto-discovered by scanning an object/table, with its binding metadata and current reader.
|
|
396
413
|
- `FrameRecordSample` (interface): interface FrameRecordSample — Per-frame timing input recorded by callers: total frame time, sim time, and optional named phase durations.
|
|
397
414
|
- `FrameStats` (interface): interface FrameStats — Aggregated frame-timing statistics over the recent sampling window, including per-phase breakdown.
|
|
415
|
+
- `LastFrameSample` (interface): interface LastFrameSample — The last frame `devtools.frame.record` saw: its sim milliseconds and named phases.
|
|
398
416
|
- `LatencyStats` (interface): interface LatencyStats — Aggregated latency statistics (last/avg/min/max) over recorded latency samples.
|
|
399
417
|
- `LongFrameEvent` (interface): interface LongFrameEvent — Recorded spike event for a frame that exceeded the long-frame threshold, with culprit attribution.
|
|
400
418
|
- `OverrideApplyResult` (interface): interface OverrideApplyResult — Outcome of applying a devtools overrides payload: counts of applied and skipped entries plus diagnostics.
|
|
@@ -420,7 +438,7 @@
|
|
|
420
438
|
|
|
421
439
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
422
440
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
423
|
-
- `VERSION` (const): const VERSION: "0.
|
|
441
|
+
- `VERSION` (const): const VERSION: "0.49.0" — Installed `@jgengine/core` semver - compare against {@link CHANGELOG} keys when migrating.
|
|
424
442
|
|
|
425
443
|
## @jgengine/core/runtime/adapter
|
|
426
444
|
|
|
@@ -548,9 +566,11 @@
|
|
|
548
566
|
## @jgengine/core/runtime/headlessRunner
|
|
549
567
|
|
|
550
568
|
- `HeadlessCommandInterface` (interface): interface HeadlessCommandInterface — Canvas-free command/intent surface for {@link HeadlessRunner} — invoke a game's registered UI commands and read the resulting reactive state off `ctx`.
|
|
569
|
+
- `HeadlessGameSource` (type): type HeadlessGameSource<TAssetRef extends ModelAssetRef, TMultiplayer> = | { game: HeadlessPlayable; definition?: never; content?: never; loop?: never; movement?: never; collision?: never; } | { definition: GameDefinition<TAssetRef, TMultiplayer>; game?: never; /** Catalog resolvers (`entityById`/`o… — Where a headless run gets its game. Pass the whole playable (`game`, what `defineGame` returns) and the runner reads its content, its phase-synced loop, its movement and collision config and whether the shell walks the player, the way play does. Pass a bare `definition` only for a test that builds a partial game; then content, loop, movement and collision are yours to pass, and passing them alongside `game` is a type error.
|
|
551
570
|
- `HeadlessInput` (interface): interface HeadlessInput — One step's worth of player intent handed to {@link HeadlessRunner.step} — the held-action set and pointer state the shell would otherwise publish from the browser each frame.
|
|
552
|
-
- `
|
|
553
|
-
- `
|
|
571
|
+
- `HeadlessPlayable` (type): type HeadlessPlayable<TProbe extends ProbeReadings = ProbeReadings> = PlayableGameContract<unknown, unknown, unknown, unknown, unknown, unknown, TProbe> — Any playable game, whatever its UI and render types (what `defineGame` returns); `TProbe` is what its `capture.probe` returns.
|
|
572
|
+
- `HeadlessRunner` (interface): interface HeadlessRunner — A renderer-free driver for a game loop: builds a {@link GameContext} from a game, runs the init hooks, then advances the simulation one step at a time from injected input. No React, R3F, or three.js — the whole play path (time, input, `onTick`, behaviour nav, player movement) runs from `core` primitives alone, so a non-React host (a server tick, a test, a CLI replay) can play a real game and read its world snapshot. The shell's FrameDriver is one such driver bolted to `useFrame`; this is the same step distilled out of the render tree.
|
|
573
|
+
- `HeadlessRunnerOptions` (type): type HeadlessRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> = HeadlessGameSource<TAssetRef, TMultiplayer> & { /** The single local player; defaults to `{ userId: "player", isNew: true }`. */ player?: { userId: string; isNew: boolean }; /** Wall-clock source for the sim clock — inject a… — ⚠ undocumented
|
|
554
574
|
- `createHeadlessRunner` (function): function createHeadlessRunner<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HeadlessRunnerOptions<TAssetRef, TMultiplayer>): HeadlessRunner — ⚠ undocumented
|
|
555
575
|
|
|
556
576
|
## @jgengine/core/runtime/hostPersistence
|
|
@@ -700,7 +720,7 @@
|
|
|
700
720
|
- `SimSnapshot` (type): type SimSnapshot = Record<string, unknown> — Captured contributor data keyed by contributor id.
|
|
701
721
|
- `SimSnapshotContributor` (interface): interface SimSnapshotContributor<T = unknown> — One piece of simulation state that can be captured and restored by id.
|
|
702
722
|
- `SimSnapshotRegistry` (interface): interface SimSnapshotRegistry — Registry of {@link SimSnapshotContributor}s that captures and restores them together.
|
|
703
|
-
- `createContextSimSnapshot` (function): function createContextSimSnapshot(ctx: GameContext): SimSnapshotRegistry — The engine's own contributors for a context:
|
|
723
|
+
- `createContextSimSnapshot` (function): function createContextSimSnapshot(ctx: GameContext): SimSnapshotRegistry — The engine's own contributors for a context: authoritative world state with every system save, the sim clock, the loop and pose history, and the rng cursor when `ctx.rng` is a seeded stream. Games register theirs on the same registry.
|
|
704
724
|
- `createSimSnapshotRegistry` (function): function createSimSnapshotRegistry(): SimSnapshotRegistry — A registry of everything that must round-trip for a simulation to resume bit-for-bit: world state, the clock, the rng cursor, the loop accumulator, and whatever a physics backend or game adds. Pair with {@link createInputRecorder} for replay.
|
|
705
725
|
|
|
706
726
|
## @jgengine/core/runtime/snapshot
|
|
@@ -791,8 +811,8 @@
|
|
|
791
811
|
|
|
792
812
|
## @jgengine/shell/defineGame
|
|
793
813
|
|
|
794
|
-
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
795
|
-
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef>(config: GameConfig<TAssetRef>): PlayableGame — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
814
|
+
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef, TProbe extends ProbeReadings = ProbeReadings> = EngineFields<TAssetRef> & PresentationFields<TProbe> — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
815
|
+
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef, TProbe extends ProbeReadings = ProbeReadings>(config: GameConfig<TAssetRef, TProbe>): PlayableGame<TProbe> — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
796
816
|
|
|
797
817
|
## @jgengine/shell/gameKit
|
|
798
818
|
|
|
@@ -804,7 +824,7 @@
|
|
|
804
824
|
- `EditorDocument` (interface): interface EditorDocument — The full authored scene: every marker, volume, path, note, and sculpted terrain for a game.
|
|
805
825
|
- `EditorSummonModule` (interface): interface EditorSummonModule — Structural shape of the module `GameHost`'s `editor` loader resolves — `import("@jgengine/editor")` satisfies it.
|
|
806
826
|
- `GameCameraConfig` (interface): interface GameCameraConfig — Camera tuning for the shell's rig stack: pick the rig via `rig`, then tune it through its matching config block. All fields optional — the default is the third-person orbit rig.
|
|
807
|
-
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
827
|
+
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef, TProbe extends ProbeReadings = ProbeReadings> = EngineFields<TAssetRef> & PresentationFields<TProbe> — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
808
828
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
809
829
|
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor, creator, creatorOpen = false, onCreatorOpenChange }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
810
830
|
- `GameHostProps` (interface): interface GameHostProps — Props for {@link GameHost}: the playable, optional multiplayer overrides, and the optional editor loader that enables the engine-owned F2+E summon.
|
|
@@ -814,12 +834,12 @@
|
|
|
814
834
|
- `HudFade` (function): function HudFade({ watch, idleMs = 3500, dimTo = 0.18, hold = false, children, className, style }: HudFadeProps): ReactNode — A HUD group that steps back when nothing is happening: it fades after a quiet spell and comes back the moment a watched value changes, so the world fills the screen and the HUD speaks only when it has news. Hovering it also brings it back.
|
|
815
835
|
- `HudPanel` (function): function HudPanel({ id, anchor = "top-left", order, chip, interactive, inset, locked, showDuring, priority, mobileBehavior, allowOverlapWith, collisionGroup, region = true, width, height, type, className, style, children, }: { id: string; anchor?: HudAnchor; /** Stack position within the anchor, asc… — A HUD block anchored to a corner, edge or the centre. `HudCanvas` places every panel together: panels sharing an anchor stack outward from the screen edge in ascending `order`, and a panel that would cover another slides to the nearest free spot. When the screen is too small, lower-`priority` panels fold into a toggle first. On fine pointers panels stay draggable through the edit chord; a dragged panel leaves the flow and keeps its custom placement.
|
|
816
836
|
- `HudSlots` (function): function HudSlots({ storageKey, slots, overlay, showDuring = DEFAULT_SLOT_PHASES }: HudSlotsProps): ReactNode — The play HUD in one line: a few small panels pinned to the screen's edges, scaled from a 1600x900 design to any window and movable by the player (the layout is remembered per `storageKey`). Keep it to a handful of slots; the world is the picture.
|
|
817
|
-
- `PlayableGame` (type): type PlayableGame = PlayableGameContract< ComponentType, ComponentType, RenderEntity, RenderObject, ComponentType<ViewmodelProps>, ComponentType<WorldOverlayProps
|
|
837
|
+
- `PlayableGame` (type): type PlayableGame<TProbe extends ProbeReadings = ProbeReadings> = PlayableGameContract< ComponentType, ComponentType, RenderEntity, RenderObject, ComponentType<ViewmodelProps>, ComponentType<WorldOverlayProps>, TProbe > & { /** HUD theme the shell applies at the `GameUI` root. Unset uses `defaultHud… — The concrete React-bound playable a shell `defineGame` returns and `GameHost` mounts — the one PlayableGame games handle. `TProbe` is what the game's `capture.probe` returns, so a proof flow over `typeof game` names only real probes.
|
|
818
838
|
- `StoreHandle` (interface): interface StoreHandle<T> — A typed, cast-free handle onto one slot of the reactive game store (`ctx.game.store`). The single type parameter is fixed at definition; `read`/`write`/`update` never widen to `unknown`, so no call site repeats `store.get(key) as T`. Writes flow through the engine store, so they bump `ctx.version()`, serialize into a {@link WorldSnapshot}, and replay under host authority — the same guarantees a hand-rolled `store.get`/`store.set` pair gives up the moment it forks run state into a module-level singleton.
|
|
819
839
|
- `SystemDefinition` (interface): interface SystemDefinition — A reusable game capability — lifecycle, timing, events, and optional save / replication / reset / disposal. Pass instances via `defineGame({ systems })`. Prefer one system per meaningful capability (`combat`, `quests`), not per micro-tick.
|
|
820
840
|
- `authoredSpawnPosition` (function): function authoredSpawnPosition(document: AuthoredSpawnDocumentLike, options: AuthoredSpawnOptions = {}): [number, number, number] | null — Position of the authored spawn marker as a spawn-ready `[x, y, z]` tuple, or null when the document has none. Reads the first `player_spawn` marker by default, so dragging the marker in the editor moves where players spawn — no coordinates copied into game code.
|
|
821
841
|
- `collectAuthoredTriggers` (function): function collectAuthoredTriggers(document: SceneDocumentLike): AuthoredTrigger[] — Collect every authored trigger on a document's markers and volumes. Pure — no runtime state. Action params use the live {@link registerTriggerAction} registry when present.
|
|
822
|
-
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef>(config: GameConfig<TAssetRef>): PlayableGame — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
842
|
+
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef, TProbe extends ProbeReadings = ProbeReadings>(config: GameConfig<TAssetRef, TProbe>): PlayableGame<TProbe> — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
823
843
|
- `defineKeyedStore` (function): function defineKeyedStore<T>(keyFor: (id: string) => string, initial: T | (() => T)): KeyedStoreHandle<T> — Define a typed per-owner keyed family on the game store: game code expresses `read(ctx, userId)`/`write(ctx, userId, v)`/`update(...)` with the value type it means, and both the `` `prefix:${id}` `` key composition and the `unknown → T` cast live once, here, behind the boundary. Reach for this over N separate `defineStore` calls whenever the owning id varies at runtime (per-user class, per-instance auras) — `defineStore` stays the right call for a single fixed slot.
|
|
824
844
|
- `defineStore` (function): function defineStore<T>(key: string, initial: T | (() => T)): StoreHandle<T> — Define a typed slot on the game store: game code expresses `read(ctx)`/`write(ctx, v)`/`update(...)` with the value type it means, and the `unknown → T` cast lives once, here, behind the boundary. Pass a factory for `initial` when the fallback is a fresh mutable object; the factory runs at most once and its result is reused, so an unwritten slot keeps a stable identity across reads (no per-read churn for a React selector, no allocation on a hot path). A value that carries functions (a closure-based run store, an input handle) is runtime-only: snapshots, saves and replication skip it and hydration keeps it in place. Hold such objects with `perContext` when they need no store notifications.
|
|
825
845
|
- `defineSystem` (function): function defineSystem(definition: SystemDefinition): SystemDefinition — Declare a composable game system. Pure data + hooks — the engine compiles the schedule and installs lifecycle when the game boots.
|