@jgengine/convex 0.44.0 → 0.45.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 +216 -0
- package/dist/convexSaveBackend.d.ts +1 -1
- package/dist/convexSaveBackend.js +1 -1
- package/dist/server.d.ts +1 -1
- package/package.json +5 -5
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/jgengine/SKILL.md +3 -3
- package/skills/jgengine/api.md +15 -34
- package/skills/jgengine/capabilities.md +15 -7
- package/skills/jgengine/recipes/minimal-game.md +4 -4
- package/skills/jgengine-multiplayer/SKILL.md +1 -1
- package/skills/jgengine-multiplayer/api.md +2 -1
- package/skills/jgengine-multiplayer/capabilities.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,222 @@ 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.45.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.45.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.42.0`; `@jgengine/github` is `0.32.0`.
|
|
34
|
+
- The damage pipeline lives in `combat/damage/`: `@jgengine/core/combat/{damageTable,damageMatchup,receivedDamage,damageResolution,damageRuntime,damageInterceptors,resistance}` → `@jgengine/core/combat/damage/<same name>`.
|
|
35
|
+
- New `landDamage(ctx, runtime, hit)` and `landAreaDamage(ctx, runtime, blast, prepare?)` (`@jgengine/core/combat/damage/landDamage`) resolve a hit through a damage runtime and land it as the world's `damage` effect (optional `item` credit and `round`). Harbor Heat and Scrap Signal drop their hand-written copies of that glue.
|
|
36
|
+
- Drop tables live in `loot/` with item generation: `@jgengine/core/game/{lootTable,lootPipeline,lootFilter,lootModifiers}` → `@jgengine/core/loot/<same name>`; `@jgengine/core/game/worldItem` → `@jgengine/core/loot/groundItem`.
|
|
37
|
+
- `WeaponStats` from `combat/weaponSystem` (the lifetime fire/reload counters) is now `WeaponCounters`; `WeaponStats` is only the item stat reader in `item/weapon`.
|
|
38
|
+
- Saves live in one folder per layer. Update deep imports:
|
|
39
|
+
- `@jgengine/core/game/saveStore`, `game/saveSlots`, `game/saveSlotIndex` → `@jgengine/core/save/<same name>`
|
|
40
|
+
- `@jgengine/core/game/gameSave` → `@jgengine/core/save/saveDocument`; `@jgengine/core/runtime/runtimeSave` → `@jgengine/core/save/snapshotSave`
|
|
41
|
+
- `@jgengine/core/runtime/save` → `@jgengine/core/save/saveConfig`; `@jgengine/core/runtime/hostedWorldStore` → `@jgengine/core/save/hostedWorldStore`
|
|
42
|
+
- `@jgengine/react/save` → `@jgengine/react/save/saveStore`; `@jgengine/react/saveSlots` → `@jgengine/react/save/saveSlots`; `@jgengine/react/saveThumbnail` → `@jgengine/react/save/saveThumbnail`
|
|
43
|
+
- `asyncMemoryWorldStore` is removed; wrap `memoryWorldStore()` when an async `HostedWorldStore` is needed (`memoryWorldPersistence` does).
|
|
44
|
+
- `SaveSlotMenu` is removed; use `SaveSlotsPane` (`@jgengine/react/gameMenuSaves`), with `heading` for the old `title`.
|
|
45
|
+
- `devSavePlugin` has one home, `@jgengine/node/devSavePlugin`.
|
|
46
|
+
- `pickWeighted` has one definition, `@jgengine/core/random/pick` (`pickWeighted(rng, items, weightOf)`). The `world/scatterItems` copy (`pickWeighted(entries, roll)`) is removed.
|
|
47
|
+
- `createSeededRandom` (`@jgengine/shell/terrain`) is removed; use `seededRng` from `@jgengine/core/random/rng`, which gives the same stream for a string seed.
|
|
48
|
+
- Leveling curves live in `progression/`: `@jgengine/core/game/progression` → `@jgengine/core/progression/leveling`. The particle keyframe type is `ParticleCurve` (was a second `Curve`).
|
|
49
|
+
- Flight lives in `flight/`: `@jgengine/core/movement/freeFlight` → `@jgengine/core/flight/freeFlight`; `@jgengine/core/physics/aircraftDynamics` → `@jgengine/core/flight/aircraftDynamics`.
|
|
50
|
+
- Spatial indexes live in `spatial/`: `@jgengine/core/visibility/spatialIndex` → `@jgengine/core/spatial/spatialIndex`; `world/spatialBuckets` → `spatial/spatialBuckets`. `SpatialGrid` (`physics/spatialGrid`) is removed; use `createSpatialIndex`.
|
|
51
|
+
- The falling-block grid lives in `puzzle/`: `@jgengine/core/tactics/fallingGrid` → `@jgengine/core/puzzle/fallingGrid`. `puzzle/fallingPiece` is removed; its shape tables, drop distance, level and line score are `ShapeTable`, `pieceFootprint`, `dropDistance`, `levelForLines` and `lineClearScore` in `puzzle/fallingGrid`.
|
|
52
|
+
- `Grapple` and `Glide` (`physics/traversal`) are removed; use `createGrappleSwing` and `createGlideModel` (`movement/`). `createGrappleSwing` gains `maxLength` (`fire` returns `false` past it) and `moveAnchor`.
|
|
53
|
+
- `createDecayMeterSet` and `DecayMeterSet` are removed; `createNeedSet` (`@jgengine/core/sim/needs`) is the one meter set. Map `serialize()` → `snapshot()`, `snapshot()` → `states()`, and `DecayMeterSetState` → `NeedSnapshot`. It gains `state(id)`, `states()` and `moodles()`. The `@jgengine/core/procedural` barrel exports `createNeedSet`.
|
|
54
|
+
- The navmesh free functions `findPath(mesh, …)` and `closestPoint(mesh, …)` are removed; call them on `createNavMeshQuery(mesh)`.
|
|
55
|
+
- Waves and spawning live in `waves/`: `@jgengine/core/ai/waveRunner`, `ai/spawnDirector`, `ai/spawnPoint`, `ai/director/encounter`, `ai/director/waveCountdown`, `ai/director/wavePreview`, `ai/director/pressure` and `combat/encounterSequence` → `@jgengine/core/waves/<same name>`. The `waves` folder reads with the combat skill, so `createWaveRunner` and the spawn director now come from the `@jgengine/core/combat` barrel instead of `@jgengine/core/world`.
|
|
56
|
+
- The phase sequencer's types are named for it: `EncounterConfig` → `EncounterSequenceConfig` and `EncounterState` → `EncounterSequenceState` (`waves/encounterSequence`). `EncounterConfig` and `EncounterState` are only the wave encounter's (`waves/encounter`).
|
|
57
|
+
- Quests live beside missions: `@jgengine/core/game/quest` → `@jgengine/core/mission/quest`, `game/questCatalog` → `mission/questCatalog`.
|
|
58
|
+
- `world/connectors` is removed (no adopters); build-piece snapping is `world/buildSockets`. Its cursor snap is `createBuildSocketCatalog(...).snapNearest(placed, type, cursor, { snapDistance })`, which replaces `snapToNearest`.
|
|
59
|
+
- New `placePiece(ctx, world, piece, at)` and `defineBuildKeys(ctx, pieces, use)` (`@jgengine/core/worldModel/buildTool`): place a build piece with on-screen refusals, and bind `build1`..`buildN`. The builder, colony, defense and survival templates (and Briarcrown, Red Frontier, Orbit Watch, Hold the Line, Fernwild) build through them instead of four hand-written copies.
|
|
60
|
+
- `jgengine create --2d` is now `--template 2d`, and `--shape shared-world-builder` is `--template builder`. `--3d`, `--world` and `--no-world` are gone: `--template 3d` is the default and always has a world.
|
|
61
|
+
- A game made before this has no `jgengine.json`. Add one with `name`, `id`, `template`, `editor` and `engine` (its `@jgengine` packages without the scope), then run `npx jgengine sync`.
|
|
62
|
+
- `runCreate` is now async.
|
|
63
|
+
- `GrassField` is replaced by `GrassPatch` (`@jgengine/shell/terrain`), which takes `look` (from `readGrassLook`) and `groundColor`. Its `budget`, `distanceFade`, `segments`, `tuftBlades` props are gone: cost follows the camera, and the look carries blade count and shape.
|
|
64
|
+
- `GrassRules` / `readGrassRules` are now `GrassLook` / `readGrassLook`, and `grass()` descriptors carry `look` instead of `rules`. Renamed knobs: `colorBase` → `baseColor`, `colorTip` → `tipColor`, `colorVariation` → `valueNoise`, `tuftRadius` → `tuftSpread`, `accentShare`/`accentHeight`/`accentColor` → `mixShare`/`mixHeight`/`mixColor`. `bladeWidthMin`/`bladeWidthMax` become `bladeWidth` + `widthVariation`; `bladeBendMin`/`bladeBendMax` become `curvature`.
|
|
65
|
+
- `density` is now 0..1 fullness, not blades/m²: blades per m² follow from blade size (`grassBladeDensity`), so no value comes out sparse. `GRASS_MIN_DENSITY` and `GRASS_BLADE_BUDGET` are removed; `ScatterCoverageSpec.budget` is `null` for grass.
|
|
66
|
+
- `createGrassBladeGeometry`, `resolveGrassBladeGeometryOptions`, `resolveGrassRange`, `DEFAULT_GRASS_WIND` and `resolveGrassWind` are removed with the old renderer.
|
|
67
|
+
- Camera shake, haptics and notifications each live in one folder per layer. Update deep imports:
|
|
68
|
+
- `@jgengine/core/vfx/cameraShake` → `@jgengine/core/camera/shake/cameraShake`
|
|
69
|
+
- `@jgengine/core/input/haptics` → `@jgengine/core/haptics/haptics`
|
|
70
|
+
- `@jgengine/core/game/notices` → `@jgengine/core/notifications/notices`
|
|
71
|
+
- `@jgengine/core/game/notifications` → `@jgengine/core/notifications/notificationCenter`
|
|
72
|
+
- `@jgengine/shell/camera/shakeChannel` → `@jgengine/shell/camera/shake/shakeChannel` (`@jgengine/shell/camera` still exports `cameraShake`)
|
|
73
|
+
- `@jgengine/shell/audio/hapticsRoute` → `@jgengine/shell/haptics/hapticsRoute`; `@jgengine/shell/input/padHaptics` → `@jgengine/shell/haptics/padHaptics`
|
|
74
|
+
- `@jgengine/shell/ShellNotices` → `@jgengine/shell/notifications/ShellNotices`
|
|
75
|
+
- `@jgengine/react/cameraShake` → `@jgengine/react/camera/shake/cameraShake`
|
|
76
|
+
- `@jgengine/react/notices` → `@jgengine/react/notifications/notices`; `@jgengine/react/notifications` → `@jgengine/react/notifications/notificationCenter`
|
|
77
|
+
- `ToastStack` moved from `@jgengine/react/components` to `@jgengine/react/notifications/toastStack` (still exported from `@jgengine/react`).
|
|
78
|
+
- The camera config's shake options type is `CameraRigShakeConfig` (was a second `CameraShakeConfig`, which still names the `createCameraShake` options).
|
|
79
|
+
- `appendToast`, `pruneToasts` and `createToastQueue` (`@jgengine/core/game/toasts`) are removed; push ad-hoc player messages to `ctx.game.notices` (`createNoticeFeed`).
|
|
80
|
+
- `findAssets` is removed; use `rankAssets(query).map((entry) => entry.match)`. Its options type is `RankAssetsOptions` (was `FindOptions`). The `assets search` command is removed; use `jgengine find <words>` or `assets add <query>`.
|
|
81
|
+
- `createRunLoop` (`@jgengine/core/run/runLoop`) is gone. Use `createSessionLoop` (`@jgengine/core/run/sessionLoop`): `run` → `session`, `completed` → `closed`, `haul` → `carried`, `changeId` → `optionId`, and `settle(input)` takes the game's own input and returns its `result`.
|
|
82
|
+
- `createAircraftDynamics` and `AircraftTuning` (`physics/flightDynamics`) are gone. Use `createRigidAircraft` (`@jgengine/core/physics/aircraftDynamics`, also on `@jgengine/core/world`); the flight-feel recipe maps the fields.
|
|
83
|
+
- Camera shake is one controller. `createCameraShakeChannel`, `defaultCameraShakeChannel`, `ControllerCameraShake`, `traumaShake`/`addTrauma`/`stepTrauma`/`shakeOffset` and `shakeChannelMath` are gone. `cameraShake(amplitude, decay?)` (`@jgengine/shell/camera`) feeds `defaultCameraShake`, the core `createCameraShake` controller every rig applies; `camera.shake` tunes it.
|
|
84
|
+
- `useCameraShake(controller, animate)` (`@jgengine/react`) is `useCameraShakeReadout(controller)`; it only reads. `CameraShakeMeter` drops `animate`.
|
|
85
|
+
- `DEFAULT_WALK_CODES` moved to `@jgengine/core/input/actionBindings` (still on `@jgengine/shell/gameKit`).
|
|
86
|
+
- `GameFrame` is gone: pass its `material`/`cut`/`depth`/`tint`/`edge`/`glow` props to `KitPanel` (a panel) or `KitButton` (a button) from `@jgengine/react/uiKitRoot`. `@jgengine/react/gameFrame` is now `@jgengine/react/kitFrame`: `GameMaterial` → `KitMaterial`, `GameCut` → `KitCut`, `GameMaterialLook` → `KitMaterialLook`, `GAME_MATERIALS` → `KIT_MATERIALS`, `gameCutPath` → `kitCutPath`, `gameFrameVars` → `kitFrameVars`; `GameFramesPreview` is `KitFramesPreview`.
|
|
87
|
+
- `HudFade` and `useHudAwake` moved from `@jgengine/react/hudFade` to `@jgengine/react/hudLayout`.
|
|
88
|
+
- `StatBar`, `VitalBar`, `EntitySummary`, `StatMeter`, the `@jgengine/react/bars` bars (`HealthBar`, `ShieldBar`, `ManaBar`, `BossBar`, `AmmoCounter`, `barTokens`, …) and `@jgengine/react/hudPanel` (the old name of `HudSurface`; `HudPanel` now means only the `HudCanvas` slot) are removed. Draw every meter with `KitMeter`; a HUD's vitals card is `KitVitals`.
|
|
89
|
+
- `HudTheme.bar`, `HudSkin.bar` and the `--jg-bar-*` variables are removed; meters take the UI kit's tokens.
|
|
90
|
+
- `diagnose` from `jgengine` (the doctor's check list) is now `diagnoseProject`; the shell's frame `diagnose` keeps its name. Rename the import; arguments are unchanged.
|
|
91
|
+
- There is one car sim: `createKinematicVehicle` (`physics/kinematicVehicle`) and `createVehicleBody` (`physics/vehicleBody`) are gone. Describe a car with `VehicleSpec` and drive it with `createVehicleDynamics(vehicleTuning(spec), options)` (`@jgengine/core/physics/vehicleSpec`, `@jgengine/core/physics/vehicleDynamics`, both on `@jgengine/core/world`). Map `engineAccel` → `accel`, `brakeAccel` → `brake`, `gripStrength` → `grip` (in g), `turnSpeedRef` → `turnSpeed`, `coastDeceleration` → `coast`, `hopGravity` → `hop.gravity`, and `chassis` mass, tyre grip and geometry to `weight`, `grip`, `comHeight`, `trackWidth` and `wheelbase`.
|
|
92
|
+
- Per-tick modifiers use the dynamics names: `topSpeedScale` → `speedLimitScale`, `accelScale` → `driveScale`, `turnRateScale` → `steerScale`; `gripScale` and `brakeScale` stay. `hop(speed)` is `jump(speed)`.
|
|
93
|
+
- `damagedTuning(tuning, effect)` is `damagedSpec(spec, effect)` (`physics/vehicleDamage`); re-derive with `vehicleTuning` and `retune`. `applyVehicleParts` composes onto a `VehicleSpec` and returns `{ spec, feel, parts }`; `GripCurve`, `sampleGripCurve` and `DEFAULT_GRIP_CURVE` live in `physics/gripCurve`.
|
|
94
|
+
- `tickDrivableVehicle` takes a `VehicleDynamics`. Its step has no `loadGrip`; read `slip` (`0..1`) for squeal, smoke and drift scoring.
|
|
95
|
+
- `grass({ density, colors })` is now `grass({ density, colorBase, colorTip, ... })`; density below 40 blades/m² is raised to the floor. `GrassField` takes `rules` (from `readGrassRules`).
|
|
96
|
+
- `Daylight`, `SkyDaylight`, `StaticSkyDaylight`, `TimeOfDayDaylight`, `BiomeDaylight`, `DrivenDaylight`, `AuthoredDaylight` and `DayNightSky` are removed: render `<Sky sky={...} clock={...} />` (`@jgengine/shell/environment`).
|
|
97
|
+
- `createBuildingGrid` and `generateBuildingDistrict` are removed: use `building({ count, position })` with `resolveStructureBuildings`, which places through the frontage placer.
|
|
98
|
+
- A `building({ along })` road's `setback` is now measured from the back of its sidewalk; pass `sidewalk` on the road (a `road()` descriptor fits as-is).
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
|
|
102
|
+
- `bun run dev` for any game pulls the asset packs and library models its source names and its `public/` lacks before the dev server starts (`games/shared/provisionAssets.ts`); `bun run shoot` and `bun run drive` do the same for the game they capture. `JG_DEV_PROVISION=0` skips it offline; CI skips it unless `JG_DEV_PROVISION=1`.
|
|
103
|
+
- A model that still fails to load is named: in a dev build its placeholder box carries a "MISSING <name>" label and a bottom-left banner lists every missing model; `modelLoadFallbacks()` returns `MissingModelRecord`s with that name, and `subscribeModelLoadFallbacks` notifies on new ones.
|
|
104
|
+
- `VfxKind` gains `"arc"`: a melee swing drawn as a sweep across the attacker's front, high to low.
|
|
105
|
+
- `jgengine/gameShape` exports `moduleGlobalName(line)` and `isGameSourceFile(name)`, the module-global rule (#1579) the engine and game repos' gates share.
|
|
106
|
+
- `jgengine.json` at every game's root: name, id, template, editor, the `@jgengine` packages it runs on, template files it took over (`owns`), and in the engine repo its capability answers. It is the one file to read to know what a game uses.
|
|
107
|
+
- `jgengine sync [dir...] [--check]` sets the `@jgengine` dependencies to the config and this engine version, rewrites the template's shared files (wiring, page, tooling scripts, AGENTS.md) unless the game owns them, and in the engine repo seeds capability answers and adds each new game-facing capability as a TODO decline. From the repo root it syncs every game.
|
|
108
|
+
- A new grass system: `GrassPatch` streams each patch around the camera (`GrassStream`, a quadtree over 8 m cells), drawing every tuft up close, a screen-density share further out (`GRASS_SCREEN_DENSITY`), fewer blade segments far away, and nothing past where a blade is under a pixel. Cost is bounded by the view, not the patch area.
|
|
109
|
+
- Grass looks: blade `profile` (needle → strap → broad leaf), `curvature`, `stiffness`, `tuftBlades`, tip `gradient`, `hueNoise`, `season`, `seasonNoise`, `dryness`, `noiseScale`, a second species (`mixWidth` too), flowers (`flowerShare`, `flowerSize`, `flowerColor`, `flowerColor2`), `patchiness`, `slopeThin`, `windDirection` and `translucency`. `GRASS_PRESETS` (meadow, lawn, prairie, marsh, alpine, autumn, scrub, jungle) are schema presets the inspector applies in one click.
|
|
110
|
+
- `grass-looks` dev demo: ten looks from the same knobs side by side.
|
|
111
|
+
- `jgengine find <words>` answers where a concept lives: its home folder in each layer (core model, shell draw, react view, editor author) with the files and exports in it and the pieces outside it, read from the code itself, next to the shipped capabilities and matching assets. In a game it reads the installed `@jgengine/*` packages. `jgengine find --text <regex>` (or `--in`, `--kind`, `--glob`, `--files`) is a plain ripgrep search.
|
|
112
|
+
- `describeAssetMatch` (`@jgengine/assets`) prints one line for a ranked asset match.
|
|
113
|
+
- `createNavCrowd` and `createSquadCombat` take `clock`, the millisecond clock their re-plan time cap reads (default `performance.now`); a fixed clock makes a seeded run deterministic.
|
|
114
|
+
- `DEFAULT_DRIVE_CODES` (WASD/arrows throttle, brake, steer, Space handbrake) and public `hotbarSlotBindings(count, { action })` in `@jgengine/core/input/actionBindings`.
|
|
115
|
+
- `useActionBindings()` and `ActionBindingsProvider` (`@jgengine/react/uiKitRoot`): the game's live key bindings, rebinds included. The shell provides them to every game, so `useActionGlyph` and kit prompts work without a UI kit and follow a rebind.
|
|
116
|
+
- `HudSlots` and `HUD_CORNER_WIDTH` (`@jgengine/react/hudLayout`): a play HUD's eight edge slots on a `HudCanvas`, with the layout saved per game.
|
|
117
|
+
- `@jgengine/react/kitHud`: `KitVitals` (meters plus the labeled objective), `KitWaveClock`, `KitHint`, `KitActionBar` (keyed build/action buttons with costs, progress and the interact prompt), `KitCount` and `KitCounts`.
|
|
118
|
+
- `bun run shoot --load <game|all>` measures cold-cache load times per game on a production build of the site, in a fresh Chrome profile on the real GPU: first paint, menu shown, worst menu stall, scene ready, playable, long tasks and KB. `--compare <json>` prints a before/after table, and `--trace` saves a Chrome trace. It is local only and never runs in CI.
|
|
119
|
+
- `VehicleSpec` feel knobs — `topSpeed`, `accel`, `brake`, `turnRate`, `grip`, `gripCurve`, `drift`, `assist`, `weight`, `power`, `coast`, `bodyRoll`, `suspension`, `hop`, `gearbox`, `downforce` — and `VEHICLE_STYLES.arcade`/`.sim`/`.drift` starting points. Every style launches, grips, slides on the handbrake, catches the slide and never spins on a held keyboard corner (`vehicleHandling.test.ts`).
|
|
120
|
+
- Skid marks and tyre smoke for every car: pass `tires: { id, time, marks: ctx.tireMarks, particles: ctx.particles }` to `tickDrivableVehicle`. Sliding, spinning or locked wheels lay fading rubber (`createTireMarks`, `ctx.tireMarks`) that the 3D shell draws on the ground, and hard slides play the new `smoke/tire` effect.
|
|
121
|
+
- `createVehicleDynamics` options `obstacles` + `bodyRadius` (the car slides along world solids and its step reports the hardest `impact`), `dragAt` (off-road drag), modifier `speedLimitScale`, a flat `jump` without suspension (`jump.gravity`), and a step `slip`. A gearbox car honours `coast` on top of engine braking.
|
|
122
|
+
- `Roads` (`@jgengine/shell/environment/RoadRibbons`) draws every road, track and trail; `pathRoad` (`@jgengine/core/world/pathRoads`) turns a scene path into one. `road()` gains `closed`, `cornerRadius`, `wear` and `edgeSoftness`; `DEFAULT_ROAD_SCAN` is the default paved surface.
|
|
123
|
+
- `GRASS_MIN_DENSITY`, grass `clumping`, `clumpSize`, `accentShare`, `accentHeight`, `accentColor`, `heightVariation`.
|
|
124
|
+
|
|
125
|
+
### Changed
|
|
126
|
+
|
|
127
|
+
- `shoot` and `drive` fail (exit 1) when any model was drawn as a placeholder, naming each one, instead of passing with identical pink boxes.
|
|
128
|
+
- The `slash`, `sword` and `melee` vfx presets are amber arcs in front of the attacker instead of a white spark burst from the body center. A slash with no target sweeps where the attacker faces.
|
|
129
|
+
- Drive step files refuse `ms` outside a `key` step and `timeoutMs` outside `waitFor`; the runner ignored both, so a flow carrying one looked like it waited and did not. Move a cap into `before` / `after { "timeoutMs": n }`. An error inside an included flow now starts with that flow's file.
|
|
130
|
+
- `jgengine create` writes `jgengine.json` and runs sync.
|
|
131
|
+
- In-repo games: each `capabilities.json` moved into its `jgengine.json` (`capabilities`), adopts and declines sorted. The capability gate and probe read it there. A new `games-sync` gate (`jgengine sync --check`) runs in the games job and verify:fast.
|
|
132
|
+
- `bun run lane:close` regenerates derived files first; `--skill-api` is no longer passed to `verify:fast` there.
|
|
133
|
+
- `bun run check fixture-location|orphan-ratchet|skills|script-help` run only when named.
|
|
134
|
+
- The land test "two landings watch their checks at once" no longer flakes when three copies run at once: each landing's watch ends when the other is watching too (an event), instead of a shared start time, a 3s sleep and wall-clock ratios that a loaded machine stretched. A 60s cap ends a lone watch and fails the test.
|
|
135
|
+
- `bun run lane:close` exits 0 closed, 1 verify or land failed, 3 cannot answer. `--waive` and `--session` are unknown arguments now.
|
|
136
|
+
- `.claude/hooks/session-start.mjs` prints at most two notes (cold checkout, unpushed commits) and never fetches, fast-forwards or resets a branch.
|
|
137
|
+
- A missing model or texture now names the file and the fix: `bun run games:provision` in the jgeng...
|
|
138
|
+
- Every game's HUD runs on the one kit: the nine starter games drop their own `HudFrame` copies for `HudSlots` and the `kitHud` blocks, and the seven frame games draw their frames through `KitPanel`/`KitButton`.
|
|
139
|
+
- Move the probe games into this repo's `games/` folder
|
|
140
|
+
- Games run engine source from the same commit, so `jgengine-games`, its engine pin and the cross-repo bump workflows are gone. CI's `games` job checks only the games a change reaches.
|
|
141
|
+
- `jgengine create`'s agent rules point at the repo's lowercase `games/*` folder.
|
|
142
|
+
- One command each for capturing, driving and landing. `bun run shoot --prove <game> ...` replaces `bun run games:prove`. `bun run lane:close --batch <branch ...>` replaces `bun run land:batch`, and `bun run lane:close --queue` replaces `bun run land:queue`. The bare `bun run land` script is gone, since `lane:close` runs it.
|
|
143
|
+
- Removed the one-off `scripts/drive-settings.ts`, `scripts/drive-presentation-recovery.ts` and `scripts/drive-hud-lean-entry.ts` proof drivers. `bun run drive` is the only drive command.
|
|
144
|
+
- Shader warmup (`packages/shell/src/render/shaderWarmup.ts`, `SceneWarmupGate.tsx`):
|
|
145
|
+
- It holds only the frame loop's own draws.
|
|
146
|
+
- It links what those held draws need (post-processing quads and override passes) and the shadow pass's depth programs. It also links the render-target variants for a reflection probe.
|
|
147
|
+
- It links in batches with a GPU fence between them, using `KHR_parallel_shader_compile` readiness. It reads each program's uniforms once the program is ready, then draws each program once per vertex layout while still covered.
|
|
148
|
+
- It never touches a program that is still building. One stall guard now covers a slow program (up to 30 s).
|
|
149
|
+
- The cover bar shows real link progress.
|
|
150
|
+
- The environment and world-probe PMREM bakes link their programs ahead of time, off the frame, at the probe's size. A probe taking over at a different size no longer relinks every lit program.
|
|
151
|
+
- Cascaded shadows patch swapped materials before the warmup links them.
|
|
152
|
+
- Carved terrain meshes build in slices after mount (`useSlicedValue`, `@jgengine/core/run/slicedWork`), and count as scene loading. Scanned terrain maps count as loading until they answer, so their textured program is linked under the cover.
|
|
153
|
+
- The /play runner loads each game's code on demand.
|
|
154
|
+
- Monorepo tooling has one root command per tool: 26 instead of 102, with checks and generators behind `check` and `gen`.
|
|
155
|
+
- `decodePng` (`@jgengine/assets`) throws `decodePng: not a PNG file (bad signature)` on input that is not a PNG, instead of misreading it. It is now the repo's only PNG decoder; the scripts' own copy is gone.
|
|
156
|
+
- `bun run check [name...]` runs every CI check or the named ones (`--list`, `--sdk`), `bun run gen [target...]` regenerates every committed artifact, and `worktree`, `background`, `perf` and `games:capabilities` take a verb (`bun run worktree reap`, `bun run perf gate`). Folded in: `preflight --ship`, `release --await`, `shoot --at <commit>`, `ci:status --sweep`, `verify:fast --full`, `test`, `dev:site`, `dev:games`, `host:game`. Each check, generator and verb is one row in `scripts/run-stages.ts` or its dispatcher, and CI, `verify:fast`, `preflight` and the type shard all read that table.
|
|
157
|
+
- The games module-globals check is merged into `bun run check module-globals`, which now scans every game.
|
|
158
|
+
- `land` writes the commit subject from the branch's own commit, never the base's merge commit, and refuses a branch with neither a commit nor a `changes/<branch>.md`.
|
|
159
|
+
|
|
160
|
+
### Fixed
|
|
161
|
+
|
|
162
|
+
- The dev runner starts again. Its vite config imported the shared `devSavePlugin` from `@jgengine/node` source, which imports `@jgengine/core` by package name, and a config file cannot resolve that; the running server now loads the plugin, so its own aliases resolve core to source.
|
|
163
|
+
- Desktop Standalone start runs `bun --cwd=games/<id> run dev` instead of the `games:<id>` root script that #2505 removed, so every start failed.
|
|
164
|
+
- `jgengine create` in the monorepo no longer adds a `games:<id>` root script; its next steps print `bun --cwd=games/<folder> run dev`.
|
|
165
|
+
|
|
166
|
+
Context: the launcher and `create` still called a root script that no longer exists, and their tests pinned that name, so they kept passing.
|
|
167
|
+
|
|
168
|
+
Root cause: #2505 removed the `games:<id>` root scripts without checking the desktop launcher's argv, and the launcher's test asserted that same argv.
|
|
169
|
+
|
|
170
|
+
Verification:
|
|
171
|
+
- `bun test packages/jgengine/src/create.test.ts apps/desktop/project/commands.test.ts`: 23 pass, 0 fail.
|
|
172
|
+
- `bun --cwd=games/harbor-heat run dev`, the argv `buildShellCommand` builds for Standalone: serves `http://localhost:5173/` with 200, title "Harbor Heat", and `#root`.
|
|
173
|
+
|
|
174
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
175
|
+
- `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.
|
|
176
|
+
- 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).
|
|
177
|
+
- 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.
|
|
178
|
+
- Right-click never opens the browser menu over a game (text fields keep it). Right-drag orbits the orbit rig, and a right-drag no longer also fires the game's right-click action.
|
|
179
|
+
- A parked car comes to an exact stop instead of creeping on an endless decay, and a wall clamp never hands a car speed it did not have.
|
|
180
|
+
- Models no longer disappear while part of them is still on screen: culling uses measured model bounds.
|
|
181
|
+
- World reflections no longer flicker while models stream in.
|
|
182
|
+
- Apex Nights no longer draws a second road ribbon under its track.
|
|
183
|
+
|
|
184
|
+
### Removed
|
|
185
|
+
|
|
186
|
+
- `bun run games:new`, its idea kinds, templates and world layout (`games/scripts/new-game*`, `games/docs/new-game.md`). In-repo games start with `bun run jgengine create "<Title>"`.
|
|
187
|
+
- `bun run games:capabilities declare`: sync seeds a game's capability answers.
|
|
188
|
+
|
|
189
|
+
Engine seam: packages/jgengine/src/sync.ts, packages/jgengine/src/projectConfig.ts
|
|
190
|
+
|
|
191
|
+
Prevention: `games-sync` gate; `packages/jgengine/src/sync.test.ts`.
|
|
192
|
+
|
|
193
|
+
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.
|
|
194
|
+
|
|
195
|
+
## Bends
|
|
196
|
+
|
|
197
|
+
- Deleted three template tests (`world`, `track`, `places` `.test.ts.tmpl`) together with the `games:new` generator they tested.
|
|
198
|
+
- `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.
|
|
199
|
+
- `games/scripts/check-capabilities.test.ts` and `scripts/ci-scope.test.ts` expectations changed to the new file and message names, not loosened.
|
|
200
|
+
- `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.
|
|
201
|
+
- 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).
|
|
202
|
+
- `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.
|
|
203
|
+
|
|
204
|
+
### Load
|
|
205
|
+
|
|
206
|
+
| game | worst menu stall, before → after (ms) | scene ready, before → after (ms) |
|
|
207
|
+
|---|---:|---:|
|
|
208
|
+
| apex-nights | 15772 → 766 | 33926 → 13290 |
|
|
209
|
+
| beacon-bastion | 26865 → 709 | 33395 → 23257 |
|
|
210
|
+
| briarcrown | 25072 → 734 | 37717 → 23545 |
|
|
211
|
+
| brightway-park | 29063 → 564 | - → 22168 |
|
|
212
|
+
| deepward | 3012 → 683 | 11614 → 6381 |
|
|
213
|
+
| drift-foundry | 15105 → 4058 | 48325 → - |
|
|
214
|
+
| ember-command | 23891 → 843 | 30415 → 25080 |
|
|
215
|
+
| fernwild | 30046 → 1614 | 42530 → 28572 |
|
|
216
|
+
| harbor-heat | 15463 → 2286 | - → 37479 |
|
|
217
|
+
| hold-the-line | 7183 → 931 | 21502 → 11833 |
|
|
218
|
+
| odd-orbit | 22766 → 893 | 58756 → 24926 |
|
|
219
|
+
| orbit-watch | 7481 → 720 | 18861 → 11500 |
|
|
220
|
+
| outpost-zero | 8842 → 776 | 20065 → 10615 |
|
|
221
|
+
| red-frontier | 8335 → 884 | 18819 → 11713 |
|
|
222
|
+
| saltwick | 23683 → 639 | 36378 → 20877 |
|
|
223
|
+
| scrap-signal | menu not found (13197 longest task) → 815 | 34231 → 33846 |
|
|
224
|
+
| the-sunken-crown | 23627 → 630 | 36931 → 24092 |
|
|
225
|
+
|
|
226
|
+
Menus are responsive throughout; how long the cover stays up depends on the driver's shader compile time.
|
|
227
|
+
|
|
228
|
+
### Verification
|
|
229
|
+
|
|
230
|
+
- `bun run verify:fast`: pass (36/36)
|
|
231
|
+
- `bun test packages/shell`: 1465 pass (before the rebase onto the streamed grass system)
|
|
232
|
+
- `bun run gen`: ok
|
|
233
|
+
- `bun run shoot --load all --compare .scratch/load/before.json`: table above
|
|
234
|
+
- Seen in the browser on the local production build: Hold The Line's menu moves to Controls at about 2 s while "Preparing scene" is still up.
|
|
235
|
+
|
|
236
|
+

|
|
237
|
+

|
|
238
|
+
|
|
239
|
+
### Known
|
|
240
|
+
|
|
241
|
+
- drift-foundry (4 s), harbor-heat (2.3 s) and fernwild (1.6 s) still have one long task. In drift-foundry it is a React commit of its scene models and colliders.
|
|
242
|
+
- The cover itself stays up 10–37 s on a cold driver cache, because large programs take that long to compile on ANGLE/D3D11. So the progress bar is not under 5 s.
|
|
243
|
+
- `presentationIdentity.test.tsx` now removes the stand-in `Image` it installs. It used to hang `sceneCaptureRequest`'s decode test whenever the two ran in one process.
|
|
244
|
+
|
|
29
245
|
## 0.44.0
|
|
30
246
|
|
|
31
247
|
### Migrate
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConvexReactClient } from "convex/react";
|
|
2
2
|
import type { FunctionReference } from "convex/server";
|
|
3
|
-
import { type SaveBackend } from "@jgengine/core/
|
|
3
|
+
import { type SaveBackend } from "@jgengine/core/save/saveStore";
|
|
4
4
|
/** The three Convex functions a save backend calls — a `query` that returns the stored string (or `null`) and two `mutation`s. Point them at your app's own module, or lean on the default `saves.*` convention. */
|
|
5
5
|
export interface ConvexSaveFunctions {
|
|
6
6
|
read: FunctionReference<"query", "public", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { anyApi } from "convex/server";
|
|
2
|
-
import { remoteSaveBackend } from "@jgengine/core/
|
|
2
|
+
import { remoteSaveBackend } from "@jgengine/core/save/saveStore";
|
|
3
3
|
/** Default `saves.read` / `saves.write` / `saves.remove` refs for apps that follow the convention — pass your own `functions` to override. */
|
|
4
4
|
export function defaultConvexSaveFunctions() {
|
|
5
5
|
return anyApi.saves;
|
package/dist/server.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { CommandScope } from "@jgengine/core/runtime/commandRunner";
|
|
|
7
7
|
import type { GameRuntime } from "@jgengine/core/runtime/gameRuntime";
|
|
8
8
|
import type { LeaderboardIncrement } from "@jgengine/core/runtime/hostPersistence";
|
|
9
9
|
import { type MatchmakingMode } from "@jgengine/core/runtime/hostPolicy";
|
|
10
|
-
import type { SaveConfig } from "@jgengine/core/
|
|
10
|
+
import type { SaveConfig } from "@jgengine/core/save/saveConfig";
|
|
11
11
|
import type { GameRuntimeSnapshot, RuntimePlayerRow } from "@jgengine/core/runtime/snapshot";
|
|
12
12
|
/** @internal Re-export shared host-policy pure helpers so existing convex imports keep working. */
|
|
13
13
|
export { JG_MAX_MEMBERS_PER_SERVER, canJoinPrivateServer, isListablePublicly, validateSlotsPerServer, type MatchmakingMode, } from "@jgengine/core/runtime/hostPolicy";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/convex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.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,16 +30,16 @@
|
|
|
30
30
|
"check-types": "tsgo --noEmit -p tsconfig.json"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@jgengine/core": "^0.
|
|
33
|
+
"@jgengine/core": "^0.45.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"convex": "^1.31.2",
|
|
37
37
|
"react": "^19.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"convex": "
|
|
41
|
-
"react": "
|
|
42
|
-
"@types/react": "
|
|
40
|
+
"convex": "catalog:",
|
|
41
|
+
"react": "catalog:",
|
|
42
|
+
"@types/react": "catalog:"
|
|
43
43
|
},
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
|
@@ -26,7 +26,7 @@ Create one immutable handoff at the destination the user requested, or use the m
|
|
|
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 (`docs/plans/…`, `packages/…`, `
|
|
29
|
+
3. Point to plans, issues, commits, diffs, documentation, and relevant files instead of reproducing their contents. Prefer repository-relative paths (`docs/plans/…`, `packages/…`, `games/…`).
|
|
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
|
@@ -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 --2d` (a `board` world drawn by GameUI over `@jgengine/core/puzzle/cellGrid`).
|
|
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 capability answers). 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 upgrade` in the project first: it diffs installed `@jgengine/*` versions against the latest release and prints every Migrate step and Adopt-worthy addition in between (`--json` for structured output). Bump, migrate oldest-first, then work the Adopt lists instead of reconstructing the changelog by hand.
|
|
44
44
|
|
|
@@ -70,10 +70,10 @@ 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>` searches every domain's index and every recipe at once,
|
|
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.
|
|
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
|
-
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 `
|
|
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.
|
|
77
77
|
|
|
78
78
|
If no capability fits, identify the upstream package seam before writing game-local code. In this repository, implement a reusable primitive with its first adopter and a reviewed consumer cleanup; coordinate a differently composed second consumer when applicable. Recipes wire SDK APIs, not copied implementations. If editor-owned content cannot be expressed, file the editor gap first; track any fallback and its removal path.
|
|
79
79
|
|
package/skills/jgengine/api.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- GENERATED by scripts/gen-skill-api.ts — do not edit. Regenerate: bun run gen
|
|
1
|
+
<!-- GENERATED by scripts/gen-skill-api.ts — do not edit. Regenerate: bun run gen skill-api -->
|
|
2
2
|
|
|
3
3
|
# jgengine — exported API surface
|
|
4
4
|
|
|
@@ -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.45.0" — Installed `@jgengine/core` semver - compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
- `contextVerb` (function): function contextVerb(label: string, command: string, args?: Record<string, unknown>): ContextVerb — Builds a {@link ContextVerb} for a right-click menu entry.
|
|
45
45
|
- `environment` (function): function environment(config: EnvironmentWorldConfig = {}): EnvironmentWorldFeature — Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
46
46
|
- `flat` (function): function flat(): WorldFeature — Declares an empty flat world — the minimal `WorldFeature` for games with no terrain of their own.
|
|
47
|
-
- `grass` (function): function grass(config: GrassEnvironmentConfig = {}): GrassEnvironmentDescriptor — Declares a grass vegetation patch for `environment()` —
|
|
47
|
+
- `grass` (function): function grass(config: GrassEnvironmentConfig = {}): GrassEnvironmentDescriptor — Declares a grass vegetation patch for `environment()` — the same blade field and knobs as an editor `grass_field` volume.
|
|
48
48
|
- `isSceneOwnershipManifest` (function): function isSceneOwnershipManifest(value: unknown): value is SceneOwnershipManifest — Narrow unknown parsed JSON to a {@link SceneOwnershipManifest}. Structural only — verifies the version, the declarations array, and each declaration's provenance shape; it does not judge whether declarations satisfy the boundary (use {@link auditManifest} for that).
|
|
49
49
|
- `lootTable` (function): function lootTable(def: LootTableDef): LootTableDef — Validates a loot table definition and returns it unchanged, for use with {@link createLootRegistry}.
|
|
50
50
|
- `ocean` (function): function ocean(config: OceanEnvironmentConfig = {}): OceanEnvironmentDescriptor — Declares an ocean water body for `environment()` — bounds, level, and wave tuning.
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
- `FrameStats` (interface): interface FrameStats — Aggregated frame-timing statistics over the recent sampling window, including per-phase breakdown.
|
|
94
94
|
- `FrameWindow` (interface): interface FrameWindow — Frame timings over the measured window, as the player sees them.
|
|
95
95
|
- `GlReport` (interface): interface GlReport — GPU-object creation counts. `total` is since the page loaded, the named fields since warmup.
|
|
96
|
-
- `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — The one perf record shape. `bun run perf` (CDP, out of process) and the in-page telemetry report (`window.__JG.report()`, the dev-server route, `bun run
|
|
96
|
+
- `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — The one perf record shape. `bun run perf` (CDP, out of process) and the in-page telemetry report (`window.__JG.report()`, the dev-server route, `bun run perf live`) both produce exactly this object, so a budget check and a diff read either one.
|
|
97
97
|
- `LatencyStats` (interface): interface LatencyStats — Aggregated latency statistics (last/avg/min/max) over recorded latency samples.
|
|
98
98
|
- `LogBusCounts` (interface): interface LogBusCounts — Lifetime totals per level, plus how many lines the ring dropped.
|
|
99
99
|
- `LogBusEntry` (interface): interface LogBusEntry — One captured line: when, how bad, which system said it, and what it said.
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic { readonly id: string; readonly reason: string } — ⚠ undocumented
|
|
105
105
|
- `OverrideApplyResult` (interface): interface OverrideApplyResult — Outcome of applying a devtools overrides payload: counts of applied and skipped entries plus diagnostics.
|
|
106
106
|
- `PerfQuality` (type): type PerfQuality = "full" | "draft" — Graphics tier the record was measured at.
|
|
107
|
-
- `PerfReport` (interface): interface PerfReport — One measured window of one game: the record `bun run perf`, `window.__JG.report()` and `bun run
|
|
107
|
+
- `PerfReport` (interface): interface PerfReport — One measured window of one game: the record `bun run perf`, `window.__JG.report()` and `bun run perf live` print.
|
|
108
108
|
- `PhaseStats` (interface): interface PhaseStats — Timing summary for a single named profiling phase across the sampling window.
|
|
109
109
|
- `ProfileEntry` (interface): interface ProfileEntry — One sampled stack frame reduced to a readable key and its self time.
|
|
110
110
|
- `ProfileReport` (interface): interface ProfileReport — CPU profile summary; only the CDP harness can produce one.
|
|
@@ -185,9 +185,9 @@
|
|
|
185
185
|
|
|
186
186
|
- `FrameWindow` (interface): interface FrameWindow — Frame timings over the measured window, as the player sees them.
|
|
187
187
|
- `GlReport` (interface): interface GlReport — GPU-object creation counts. `total` is since the page loaded, the named fields since warmup.
|
|
188
|
-
- `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — The one perf record shape. `bun run perf` (CDP, out of process) and the in-page telemetry report (`window.__JG.report()`, the dev-server route, `bun run
|
|
188
|
+
- `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — The one perf record shape. `bun run perf` (CDP, out of process) and the in-page telemetry report (`window.__JG.report()`, the dev-server route, `bun run perf live`) both produce exactly this object, so a budget check and a diff read either one.
|
|
189
189
|
- `PerfQuality` (type): type PerfQuality = "full" | "draft" — Graphics tier the record was measured at.
|
|
190
|
-
- `PerfReport` (interface): interface PerfReport — One measured window of one game: the record `bun run perf`, `window.__JG.report()` and `bun run
|
|
190
|
+
- `PerfReport` (interface): interface PerfReport — One measured window of one game: the record `bun run perf`, `window.__JG.report()` and `bun run perf live` print.
|
|
191
191
|
- `ProfileEntry` (interface): interface ProfileEntry — One sampled stack frame reduced to a readable key and its self time.
|
|
192
192
|
- `ProfileReport` (interface): interface ProfileReport — CPU profile summary; only the CDP harness can produce one.
|
|
193
193
|
- `RenderReport` (interface): interface RenderReport — Renderer-side counts for the measured window; `null` where the source could not read them.
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
|
|
199
199
|
## @jgengine/core/devtools/reportEndpoint
|
|
200
200
|
|
|
201
|
-
- `REPORT_ENDPOINT_PATH` (const): const REPORT_ENDPOINT_PATH: "/__jgengine/report" — The dev-only route a running game pushes its in-page report to, and that `bun run
|
|
201
|
+
- `REPORT_ENDPOINT_PATH` (const): const REPORT_ENDPOINT_PATH: "/__jgengine/report" — The dev-only route a running game pushes its in-page report to, and that `bun run perf live <game>` reads back. One process: the page gathers, the dev server holds the newest record per game, the CLI fetches it. Nothing here ships in a production build — the Vite plugin mounts it under `apply: "serve"`.
|
|
202
202
|
- `ReportPushBody` (interface): interface ReportPushBody — Body the page POSTs: the game it measured and the record itself.
|
|
203
203
|
- `ReportRouteError` (interface): interface ReportRouteError — Body sent when there is nothing to return yet.
|
|
204
204
|
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
|
|
209
209
|
## @jgengine/core/devtools/saveEndpoint
|
|
210
210
|
|
|
211
|
-
- `SaveEndpointInfo` (interface): interface SaveEndpointInfo — Where dev-time saves land: the endpoint URL plus the
|
|
211
|
+
- `SaveEndpointInfo` (interface): interface SaveEndpointInfo — Where dev-time saves land: the endpoint URL plus the games/<gameId> directory it targets.
|
|
212
212
|
- `SaveEndpointRequest` (type): type SaveEndpointRequest = | { kind: "editor-document"; gameId: string; json: string } | { kind: "tunables"; gameId: string; deltas: readonly { table: string; key: string; value: unknown }[]; } — One write request the dev save endpoint accepts: a scene document or tunable deltas.
|
|
213
213
|
- `SaveEndpointResponse` (interface): interface SaveEndpointResponse — Result envelope the dev save endpoint returns for every write request.
|
|
214
214
|
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
|
|
286
286
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
287
287
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
288
|
-
- `VERSION` (const): const VERSION: "0.
|
|
288
|
+
- `VERSION` (const): const VERSION: "0.45.0" — Installed `@jgengine/core` semver - compare against {@link CHANGELOG} keys when migrating.
|
|
289
289
|
|
|
290
290
|
## @jgengine/core/runtime/adapter
|
|
291
291
|
|
|
@@ -494,15 +494,8 @@
|
|
|
494
494
|
- `HostedWorldStore` (interface): interface HostedWorldStore — Narrow persistence seam for a hosted world — the {@link HostedWorldRecord} counterpart of `HostPersistence`. Backends implement it (memory/file/sql/convex); the session never names one. A stateful host loads once and saves on a cadence; a stateless host reconstructs from `load()` each invocation.
|
|
495
495
|
- `HostedWorldSync` (type): type HostedWorldSync = | { kind: "baseline"; revision: number; snapshot: WorldSnapshot } | { kind: "diff"; diff: WorldDiff } — A client replication pull: a full baseline (first sync / fell behind) or a diff since the client's cursor.
|
|
496
496
|
- `SyncHostedWorldStore` (interface): interface SyncHostedWorldStore — Already-loaded store adapter: synchronous reads, with synchronous or asynchronous writes.
|
|
497
|
-
- `asyncMemoryWorldStore` (function): function asyncMemoryWorldStore(seed?: HostedWorldRecord): HostedWorldStore — Async in-process store for callers exercising the production persistence contract.
|
|
498
497
|
- `createHostedWorldSessionAsync` (function): function createHostedWorldSessionAsync<TAssetRef extends ModelAssetRef, TMultiplayer>(options: Omit<HostedWorldSessionOptions<TAssetRef, TMultiplayer>, "store"> & { store: HostedWorldStore }): Promise<HostedWorldSession> — Build a hosted session from an asynchronous persistence backend.
|
|
499
498
|
|
|
500
|
-
## @jgengine/core/runtime/hostedWorldStore
|
|
501
|
-
|
|
502
|
-
- `HostedWorldRecord` (interface): interface HostedWorldRecord — One hosted world's persisted authoritative state — the unit a {@link HostedWorldStore} loads and saves.
|
|
503
|
-
- `HostedWorldStore` (interface): interface HostedWorldStore — Narrow persistence seam for a hosted world — the {@link HostedWorldRecord} counterpart of `HostPersistence`. Backends implement it (memory/file/sql/convex); the session never names one. A stateful host loads once and saves on a cadence; a stateless host reconstructs from `load()` each invocation.
|
|
504
|
-
- `SyncHostedWorldStore` (interface): interface SyncHostedWorldStore — Already-loaded store adapter: synchronous reads, with synchronous or asynchronous writes.
|
|
505
|
-
|
|
506
499
|
## @jgengine/core/runtime/inputRecorder
|
|
507
500
|
|
|
508
501
|
- `InputRecorder` (interface): interface InputRecorder — Tick-indexed input log: record per tick, look up the frame in force at any tick.
|
|
@@ -572,21 +565,6 @@
|
|
|
572
565
|
- `PredictionStep` (type): type PredictionStep<S> = (state: S, input: InputFrame, dt: number) => S — Deterministic state transition used to advance one predicted input frame.
|
|
573
566
|
- `createPredictionBuffer` (function): function createPredictionBuffer<S>(config: { initial: S; step: PredictionStep<S>; maxTicks: number; startTick?: number; dt?: number; /** Error above which a caller should snap its rendered pose to the reconciled result. */ snapThreshold?: number; }): PredictionBuffer<S> — Keeps local input predictions ahead of the last server confirmation. Reconciliation replaces the confirmed state and deterministically replays unconfirmed inputs through the supplied step function. `maxTicks` bounds retained work and memory; dropped inputs are folded into the confirmed baseline.
|
|
574
567
|
|
|
575
|
-
## @jgengine/core/runtime/runtimeSave
|
|
576
|
-
|
|
577
|
-
- `RuntimeSave` (interface): interface RuntimeSave — Whole-world save/load bound to a live world and a pluggable backend. `save()` captures `target.state()` and writes it; `load()` reads it back and calls `target.restore()`. Older targets use `snapshot()`/`hydrate()`. In `autosave` mode it also writes on a trailing timer while the world keeps changing. Named slots, versioned migration, and offline↔cloud (backend swap) all come for free from the underlying save store.
|
|
578
|
-
- `RuntimeSaveConfig` (interface): interface RuntimeSaveConfig — How {@link createRuntimeSave} is wired — the live world `target`, the `backend` it persists through, and optional mode/slot/versioning/cadence knobs.
|
|
579
|
-
- `RuntimeSaveMode` (type): type RuntimeSaveMode = "autosave" | "manual" — `"autosave"` writes a fresh capture on a trailing timer while the world changes (at most once per `autosaveMs`, so a never-idle world still persists); `"manual"` writes only on an explicit `save()`/`checkpoint()` — the save-point / quest-trigger model.
|
|
580
|
-
- `RuntimeSaveOptions` (type): type RuntimeSaveOptions = Omit<RuntimeSaveConfig, "target"> — {@link RuntimeSaveConfig} without `target` — what a host (`createGameContext`) accepts to build `ctx.game.save` and bind it to the context itself.
|
|
581
|
-
- `RuntimeSaveSlot` (interface): interface RuntimeSaveSlot — One stored slot as a save-select menu reads it: the world document a {@link RuntimeSave.load | load} of that slot would restore, and the wall-clock time (ms) it was written. The document is a plain {@link WorldSnapshot}, so a game reads its own systems out of it (commander, mission, playtime, thumbnail) with the same keys `ctx.state()` writes.
|
|
582
|
-
- `RuntimeSaveTarget` (interface): interface RuntimeSaveTarget — The narrow slice of a live `GameContext` a runtime save reads and writes — a `GameContext` satisfies it directly (`ctx.state`/`ctx.restore`/`ctx.subscribe`). Depending on this instead of the full context keeps the save controller a deep, decoupled module: it captures and restores the *whole* opted-in world without knowing any subsystem.
|
|
583
|
-
- `createRuntimeSave` (function): function createRuntimeSave(config: RuntimeSaveConfig): RuntimeSave — Bridge a live world to a pluggable save backend: whole-state capture/restore with autosave, save points, named slots, and versioned migration — the same call for offline (localStorage) and cloud (Convex), only the `backend` differs. The game keeps control of *when* it saves (the `mode` plus explicit `checkpoint()` calls from quests/areas) and *when* it restores (`load()` on boot), so any save mechanic — continuous autosave, manual save points, checkpoint triggers — is expressible without new engine code.
|
|
584
|
-
|
|
585
|
-
## @jgengine/core/runtime/save
|
|
586
|
-
|
|
587
|
-
- `SaveConfig` (type): type SaveConfig = | "none" | { auto: string; scope: SaveScope; } — ⚠ undocumented · used by `persistServerSnapshot` (@jgengine/convex): Write a snapshot back: server row, dirty player profiles, dirty chunks, and drained leaderboard increments, all under `save`.
|
|
588
|
-
- `SaveScope` (type): type SaveScope = "player" | "chunks" | "player+chunks" — ⚠ undocumented
|
|
589
|
-
|
|
590
568
|
## @jgengine/core/runtime/simContext
|
|
591
569
|
|
|
592
570
|
- `SimContext` (interface): interface SimContext — `ctx.sim`: the simulation stepper shared by every driver plus the render-side interpolation it enables. Drivers call {@link advance} with real elapsed seconds and do their per-step work inside the callback; renderers call {@link renderPose} each frame.
|
|
@@ -718,7 +696,8 @@
|
|
|
718
696
|
|
|
719
697
|
- `Clock` (function): function Clock({ format = "24h", showDay = true, controls = false, style, className, }: { format?: "24h" | "12h"; showDay?: boolean; controls?: boolean; style?: CSSProperties; className?: string; }): React.JSX.Element — A time-of-day clock reading the sim calendar — `Day N · HH:MM`, 24h or 12h. `controls` adds pause + the game's speed multipliers as clickable pills (the "fast-forward" bar), off by default so a game opts into letting the player scrub time.
|
|
720
698
|
- `Coins` (function): function Coins({ currencyId, icon = "🪙", style, className, }: { currencyId: string; icon?: ReactNode; style?: CSSProperties; className?: string; }): React.JSX.Element — A currency counter — an icon (emoji/char, default a coin) plus the live amount for `currencyId`. *
|
|
721
|
-
- `
|
|
699
|
+
- `DEFAULT_DRIVE_CODES` (const): const DEFAULT_DRIVE_CODES: { readonly throttle: readonly ["KeyW", "ArrowUp"]; readonly brake: readonly ["KeyS", "ArrowDown"]; readonly steerLeft: readonly ["KeyA", "ArrowLeft"]; readonly steerRight: readonly ["KeyD", "ArrowRight"]; readonly handbrake: readonly ["Space"]; } — Stock keyboard codes for driving a vehicle: WASD or the arrows for throttle, brake and steering, Space for the handbrake. Spread it as the base of a racing map and override what the game uses differently (`input: { ...DEFAULT_DRIVE_CODES, handbrake: ["KeyX"], hop: ["Space"] }`).
|
|
700
|
+
- `DEFAULT_WALK_CODES` (const): const DEFAULT_WALK_CODES: { readonly moveForward: readonly ["KeyW"]; readonly moveBack: readonly ["KeyS"]; readonly moveLeft: readonly ["KeyA"]; readonly moveRight: readonly ["KeyD"]; readonly jump: readonly ["Space"]; } — Stock keyboard codes for the shell walk controller: WASD to move, Space to jump. Spread it as the base of a game's map (`input: { ...DEFAULT_WALK_CODES, sprint: ["ShiftLeft"], interact: ["KeyE"] }`) instead of re-typing the same five bindings in every game.
|
|
722
701
|
- `EditorCatalogDefinition` (interface): interface EditorCatalogDefinition — Game-exported catalog definition: a `ParamSchema` plus default entries. Schemas stay in code; entry values merge into `document.catalogs` and are what the editor/RPC edits and saves.
|
|
723
702
|
- `EditorDocument` (interface): interface EditorDocument — The full authored scene: every marker, volume, path, note, and sculpted terrain for a game.
|
|
724
703
|
- `EditorSummonModule` (interface): interface EditorSummonModule — Structural shape of the module `GameHost`'s `editor` loader resolves — `import("@jgengine/editor")` satisfies it.
|
|
@@ -730,9 +709,10 @@
|
|
|
730
709
|
- `GameLoop` (interface): interface GameLoop<TContext = unknown> — Lifecycle hooks a game implements to drive init, per-tick simulation, and player join/leave.
|
|
731
710
|
- `Hotbar` (function): function Hotbar({ inventoryId, activeSlot, keys, slotSize = 46, itemIcon, style, className, }: { inventoryId: string; activeSlot?: number; keys?: readonly string[]; slotSize?: number; /** Caller-supplied item id → icon registry; return null/undefined to fall back to the default glyph. */ itemIcon?: … — A numbered hotbar bound to an inventory — painted iconed slots (a `GameIcon` glyph over a school-keyed gradient with a count badge, #1035), an active-slot highlight, and a keycap per slot. Place it and pass the `inventoryId`; `activeSlot` highlights the equipped one. Supply `itemIcon` to map your item ids to your own glyphs — the default resolves a `GameIcon` from the item id.
|
|
732
711
|
- `HudCanvas` (function): function HudCanvas({ layout, editChord, compactScale, showDuring, className, style, children, }: { layout: HudLayoutStore; editChord?: HudEditChord | false; /** Zoom applied to the whole HUD on compact displays. Default 0.85. */ compactScale?: number; /** Opt-in play-phase gate: render the HUD only … — Full-viewport HUD surface. Panels declared with `HudPanel` flow into nine anchor regions and stack automatically with a gap — no per-panel pixel offsets, no manual clearance for sibling panels, the touch-control dock (`--jg-hud-dock-clearance`), or device safe areas. On compact displays the whole surface scales down and each panel applies its `compact` behavior.
|
|
712
|
+
- `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.
|
|
733
713
|
- `HudPanel` (function): function HudPanel({ id, anchor = "top-left", lane, order, compact: compactMode = "keep", chip, interactive, inset, locked, showDuring, priority, mobileBehavior, allowOverlapWith, collisionGroup, region = true, width, height, type, className, style, children, }: { id: string; anchor?: HudAnchor; /** … — A HUD block that lives in one of the nine anchor regions. Panels sharing a region stack outward from the screen edge in ascending `order`. On fine pointers panels stay draggable through the edit chord; a dragged panel leaves the flow and keeps its custom placement. On compact displays custom placements are ignored and the `compact` behavior applies.
|
|
714
|
+
- `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.
|
|
734
715
|
- `PlayableGame` (type): type PlayableGame = EnginePlayableGame< ComponentType, ComponentType, RenderEntity, RenderObject, ComponentType<ViewmodelProps>, ComponentType<WorldOverlayProps> > & { /** HUD theme the shell applies at the `GameUI` root. Unset uses `defaultHudTheme`; `false` opts out of the default theme and the en… — The concrete React-bound playable a shell `defineGame` returns and `GameHost` mounts — the one PlayableGame games handle.
|
|
735
|
-
- `StatBar` (function): function StatBar({ statId = "health", entityId, tone = "health", label, showValue = true, width = 200, icon, style, className, }: { statId?: string; entityId?: string; tone?: StatTone; label?: string; showValue?: boolean; width?: number; icon?: ReactNode; style?: CSSProperties; className?: string; }… — A polished stat bar (health/mana/stamina/shield/xp) — a rounded, glassy meter with a tone-colored fill and an optional value readout. Reads `statId` off `entityId` (defaults to the local player). Renders nothing until the stat exists.
|
|
736
716
|
- `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.
|
|
737
717
|
- `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.
|
|
738
718
|
- `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.
|
|
@@ -744,6 +724,7 @@
|
|
|
744
724
|
- `environment` (function): function environment(config: EnvironmentWorldConfig = {}): EnvironmentWorldFeature — Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
745
725
|
- `environmentContentFromDocument` (function): function environmentContentFromDocument(doc: EditorDocument, options: EnvironmentContentOptions = {}): EnvironmentContent — Derives the coordinate/placement content of an `environment()` world from its scene document: terrain footprint (via {@link terrainBoundsFromDocument}), ground clearings under authored spawns/POIs (via `clearanceZonesFrom`), and the document's sculpt snapshot. This is the seam that lets a game author its world footprint and flatten regions in the editor instead of hardcoding them — the `world.ts` that consumes it carries only engine tuning.
|
|
746
726
|
- `grounded` (function): function grounded(ctx: Pick<GameContext, "world">, x: number, z: number): readonly [number, number, number] — Ground an XZ point onto the live world surface as `[x, groundY, z]`.
|
|
727
|
+
- `hotbarSlotBindings` (function): function hotbarSlotBindings(count: number, options?: { action?: (slot: number) => string }): ActionCodesMap — Number-row bindings for `count` numbered actions: slot 1 on Digit1 through slot 10 on Digit0. Name the actions with `action` (`hotbarSlotBindings(4, { action: (slot) => `buildTower${slot}` })`); the default names are `hotbarSlot1`…, which the shell routes to the hotbar on its own.
|
|
747
728
|
- `offline` (function): function offline(): MultiplayerAdapterConfig — Explicit single-player adapter. Solo games never need this — omitting `multiplayer` in the shell `defineGame` already defaults to offline; pass it only where an adapter value is structurally required.
|
|
748
729
|
- `seededRng` (function): function seededRng(seed: string | number): SeededRng — Deterministic pseudo-random generator seeded from a string or number — same seed, same sequence.
|
|
749
730
|
- `useHudLayout` (function): function useHudLayout(options?: { storageKey?: string; snap?: number; locked?: boolean; /** * Scene-document `ui` section — source of truth for panel placement/size. * When provided, hydrates the layout store (and wins over legacy localStorage). */ documentUi?: EditorUiDocument; /** * When true (def… — Layout state for `HudCanvas` — panel placements, edit-mode drag/resize, and per-game persistence.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- GENERATED by scripts/gen-capability-index.ts — do not edit. Regenerate: bun run gen
|
|
1
|
+
<!-- GENERATED by scripts/gen-capability-index.ts — do not edit. Regenerate: bun run gen capabilities -->
|
|
2
2
|
|
|
3
3
|
# jgengine — capability index
|
|
4
4
|
|
|
@@ -40,6 +40,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
40
40
|
|
|
41
41
|
- `readQuantity` (function) · `import { readQuantity } from "@jgengine/core/runtime/commandInput"`
|
|
42
42
|
|
|
43
|
+
## default-drive-codes — stock WASD/arrow throttle, brake, steer and handbrake key codes
|
|
44
|
+
|
|
45
|
+
- `DEFAULT_DRIVE_CODES` (const) · `import { DEFAULT_DRIVE_CODES } from "@jgengine/shell/gameKit"`
|
|
46
|
+
|
|
43
47
|
## default-walk-codes — stock WASD + jump key codes for the shell walk controller
|
|
44
48
|
|
|
45
49
|
- `DEFAULT_WALK_CODES` (const) · `import { DEFAULT_WALK_CODES } from "@jgengine/shell/gameKit"`
|
|
@@ -88,6 +92,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
88
92
|
|
|
89
93
|
- `isListablePublicly` (function) · `import { isListablePublicly } from "@jgengine/core/runtime/hostPolicy"`
|
|
90
94
|
|
|
95
|
+
## hotbar-slot-bindings — number-row key codes for a run of numbered actions (hotbar, build menu, tools)
|
|
96
|
+
|
|
97
|
+
- `hotbarSlotBindings` (function) · `import { hotbarSlotBindings } from "@jgengine/shell/gameKit"`
|
|
98
|
+
|
|
91
99
|
## hud-canvas — the HUD root surface — design-resolution scaling plus live panel-placement editing (F2+C)
|
|
92
100
|
|
|
93
101
|
- `HudCanvas` (function) · `import { HudCanvas } from "@jgengine/shell/gameKit"`
|
|
@@ -100,6 +108,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
100
108
|
|
|
101
109
|
- `Coins` (function) · `import { Coins } from "@jgengine/shell/gameKit"`
|
|
102
110
|
|
|
111
|
+
## hud-fade — HUD group that fades when idle and returns when its values change
|
|
112
|
+
|
|
113
|
+
- `HudFade` (function) · `import { HudFade } from "@jgengine/shell/gameKit"`
|
|
114
|
+
|
|
103
115
|
## hud-hotbar — optional inventory hotbar building block — games own slot chrome
|
|
104
116
|
|
|
105
117
|
- `Hotbar` (function) · `import { Hotbar } from "@jgengine/shell/gameKit"`
|
|
@@ -112,9 +124,9 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
112
124
|
|
|
113
125
|
- `HudPanel` (function) · `import { HudPanel } from "@jgengine/shell/gameKit"`
|
|
114
126
|
|
|
115
|
-
## hud-
|
|
127
|
+
## hud-slots — a play HUD of edge-pinned, player-movable panels in one component
|
|
116
128
|
|
|
117
|
-
- `
|
|
129
|
+
- `HudSlots` (function) · `import { HudSlots } from "@jgengine/shell/gameKit"`
|
|
118
130
|
|
|
119
131
|
## input-recorder — tick-indexed input log for replay, rollback, and regression tests
|
|
120
132
|
|
|
@@ -174,10 +186,6 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
174
186
|
|
|
175
187
|
- `createSnapshotBuffer` (function) · `import { createSnapshotBuffer } from "@jgengine/core/runtime/snapshotBuffer"`
|
|
176
188
|
|
|
177
|
-
## runtime-save — save/load the whole game world through a pluggable backend, autosave or save points
|
|
178
|
-
|
|
179
|
-
- `createRuntimeSave` (function) · `import { createRuntimeSave } from "@jgengine/core/runtime/runtimeSave"`
|
|
180
|
-
|
|
181
189
|
## scene-ownership — audit a whole ownership manifest for boundary violations
|
|
182
190
|
|
|
183
191
|
- `auditManifest` (function) · `import { auditManifest } from "@jgengine/core/authoring"`
|
|
@@ -13,7 +13,7 @@ it should not recreate input, model lifecycle, storage or UI accessibility machi
|
|
|
13
13
|
|
|
14
14
|
## 1. Scaffold
|
|
15
15
|
|
|
16
|
-
`npx jgengine create <name>` (inside the engine monorepo: `bun run
|
|
16
|
+
`npx jgengine create <name>` (inside the engine monorepo: `bun run jgengine create "<Title>"`). The game's `jgengine.json` says what it is and uses; edit it and run `jgengine sync` to update the game.
|
|
17
17
|
The file set is thin and already runs:
|
|
18
18
|
|
|
19
19
|
- `src/main.tsx` — mounts `GameHost` with `editor={() => import("@jgengine/editor")}`; the
|
|
@@ -81,9 +81,9 @@ export function onNewPlayer(ctx: GameContext): void {
|
|
|
81
81
|
|
|
82
82
|
`GameUI` starts as an empty `HudCanvas` (`useHudLayout` + `HudCanvas` from the kit). That is
|
|
83
83
|
intentional: **every game owns its UI composition** — layout, terminology, placement, and art
|
|
84
|
-
direction. Compose the shipped building blocks the moment you need them (`
|
|
85
|
-
`Coins`, `InventoryGrid`, `CharacterSheet`, window shells from `jgengine-ui`), then reskin via
|
|
86
|
-
`
|
|
84
|
+
direction. Compose the shipped building blocks the moment you need them (`HudSlots`, `KitVitals`, `KitActionBar`,
|
|
85
|
+
`Hotbar`, `Coins`, `InventoryGrid`, `CharacterSheet`, window shells from `jgengine-ui`), then reskin via
|
|
86
|
+
the UI kit's tokens (`customizeKit`). Reaching for those is using the engine correctly, not incomplete work. What
|
|
87
87
|
the engine does not ship is a whole finished game *face* or genre theme preset — do not leave an
|
|
88
88
|
unarranged, unskinned generic dashboard as the product. Custom markup can use headless
|
|
89
89
|
SDK behavior; a missing reusable behavior is an upstream gap (see `jgengine-ui`).
|
|
@@ -69,4 +69,4 @@ Pass runtimes to `jgengineCronSpecs` so no tick cron registers when no runtime d
|
|
|
69
69
|
|
|
70
70
|
Join failures return typed `full`, `closed`, or `unauthorized` outcomes. Surface them through a retry gate; returning a rejection without visible UI is unfinished. Browser adapters leave sessions on `pagehide`. Membership is per client session: pass a fresh `sessionId` (`createTransportSessionId`) to each `joinServer` and its matching `leaveServer`, so a closed tab or a disposed mount never evicts a live one. A host mutation acting for a user who may have no live session calls `helpers.ensureJoined` before `helpers.runCommand`.
|
|
71
71
|
|
|
72
|
-
For the explicit shared-builder composition, `jgengine create "World Name" --
|
|
72
|
+
For the explicit shared-builder composition, `jgengine create "World Name" --template builder` generates the connected Convex shell, runtime scope declarations, claim/accrual examples, chat, indexed presence, and an online batch pipeline. The generated README owns setup; replace the demonstration economy and author buildable world content in the editor.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- GENERATED by scripts/gen-skill-api.ts — do not edit. Regenerate: bun run gen
|
|
1
|
+
<!-- GENERATED by scripts/gen-skill-api.ts — do not edit. Regenerate: bun run gen skill-api -->
|
|
2
2
|
|
|
3
3
|
# jgengine-multiplayer — exported API surface
|
|
4
4
|
|
|
@@ -368,6 +368,7 @@
|
|
|
368
368
|
|
|
369
369
|
- `EDITOR_SCENE_FILENAME` (const): const EDITOR_SCENE_FILENAME: "editor.scene.json" — Filename editor scene documents are saved under inside a game's `src/`.
|
|
370
370
|
- `SAVE_ENDPOINT_PATH` (const): const SAVE_ENDPOINT_PATH: "/__jgengine/save" — Route the dev save middleware answers on; matches `installSaveEndpoint` defaults.
|
|
371
|
+
- `SrcDirResolver` (type): type SrcDirResolver = (gameId: string) => string | null — Maps a save request's game id to the game `src/` directory it writes into, or `null` for an unknown game.
|
|
371
372
|
- `devSavePlugin` (function): function devSavePlugin(resolveSrcDir: SrcDirResolver): DevSavePluginShape — Dev-only Vite middleware that gives the F2 chord family its write path: editor mode's Save (Ctrl+S / `save_scene`) writes `editor.scene.json` and debug mode's Tune "Save to source" rewrites tunable literals, both into the directory `resolveSrcDir(gameId)` returns.
|
|
372
373
|
- `handleSaveRequest` (function): function handleSaveRequest(resolveSrcDir: SrcDirResolver, body: string): SaveEndpointResponse — Applies one parsed save request against a resolved game `src/` directory.
|
|
373
374
|
- `standaloneSavePlugin` (function): function standaloneSavePlugin(rootDir: string = process.cwd()): DevSavePluginShape — `devSavePlugin` preset for a standalone game project (one game, `src/` at the project root): every gameId maps to `<rootDir>/src`. Drop into the scaffolded `vite.config.ts` plugins array.
|