@jgengine/assets 0.18.1 → 0.21.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 +504 -7
- package/README.md +22 -3
- package/dist/assetBudget.d.ts +36 -0
- package/dist/assetBudget.js +110 -0
- package/dist/catalogs/build.d.ts +1 -0
- package/dist/catalogs/build.js +1 -0
- package/dist/cli/pull.js +56 -1
- package/dist/glbMetrics.d.ts +24 -0
- package/dist/glbMetrics.js +291 -0
- package/dist/gltfMaterials.d.ts +79 -0
- package/dist/gltfMaterials.js +183 -0
- package/dist/importSpec.d.ts +15 -6
- package/dist/importSpec.js +217 -45
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/indexGen.d.ts +3 -1
- package/dist/indexGen.js +14 -4
- package/dist/manifest.d.ts +7 -0
- package/dist/materials.d.ts +3 -3
- package/dist/materials.js +3 -3
- package/dist/snippet.js +1 -1
- package/dist/sources/ambientcg.js +37 -31
- package/dist/staticPrefabBake.d.ts +58 -0
- package/dist/staticPrefabBake.js +315 -0
- package/dist/verify.js +2 -2
- package/package.json +5 -2
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +28 -6
- package/skills/jgengine/api.md +139 -99
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +10 -2
- package/skills/jgengine-assets/SKILL.md +24 -3
- package/skills/jgengine-assets/api.md +119 -44
- package/skills/jgengine-assets/capabilities.md +41 -0
- package/skills/jgengine-assets/references/custom-imports.md +55 -0
package/CHANGELOG.md
CHANGED
|
@@ -17,13 +17,6 @@ between (`--json` for structured output).
|
|
|
17
17
|
|
|
18
18
|
## [Unreleased]
|
|
19
19
|
|
|
20
|
-
### Fixed
|
|
21
|
-
|
|
22
|
-
- Separate SDK publication validation from external Games quality checks while retaining the full local and main-branch gates.
|
|
23
|
-
|
|
24
|
-
- Correct foot-IK endpoint recomputation after hip rotation; verify reachable targets and clamping without changing bone lengths.
|
|
25
|
-
- Restore release CI prerequisites and make catalog-loader tests independent of the external Games checkout.
|
|
26
|
-
|
|
27
20
|
<!--
|
|
28
21
|
Every PR that changes `packages/*/src` records its consumer-facing change here, so
|
|
29
22
|
the next release's notes are complete by construction. Add a bullet under the right
|
|
@@ -33,6 +26,510 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
33
26
|
`packages/core/src/meta/changelog.ts` (the typed `CHANGELOG` export).
|
|
34
27
|
-->
|
|
35
28
|
|
|
29
|
+
## 0.21.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.21.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.18.0`; `@jgengine/github` is `0.8.0`.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Imported model animation can explicitly follow game speed controls while preserving the rig's automatic clip roles. Character clock and automatic-role controls survive authored scene edits, undo, save and reload; existing real-time playback remains the default.
|
|
38
|
+
- `readAuthoredAnimation` and `createAuthoredAnimationReader` consume saved marker animation for game-owned character mappings, with live document caching and located repairs for invalid configuration. Authored animation replaces game defaults; omitted or invalid overrides retain those defaults.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Selected game-clock model animation samples locally accepted clock progress, keeping gait and clip time aligned with clamped and fixed simulation steps. Saved clock offsets cannot seek playback; external clocks without the capability receive a repair warning. Real-time playback and unbound previews retain their behavior.
|
|
43
|
+
- Entity-bound imported animation holds its graph and mixer while the game clock is paused or has zero timescale, retaining queued triggers and playback for resume. Unbound previews keep playing.
|
|
44
|
+
- Clear inferred character gait across placement discontinuities and driven actor motor state on despawn, preserving graph timing, ordinary hydration and pre-spawn restores.
|
|
45
|
+
- Character Inspector and Graph panels show located errors in malformed saved animation overrides and wrap long diagnostic paths and block silent partial repair during unrelated edits. Explicit replacement remains undoable, and valid authored extensions and variant order survive editing.
|
|
46
|
+
- Static authored character placements use the shared strict animation reader, matching inspector and dynamic-character validation. Malformed saved overrides preserve catalog animation defaults and report located repairs without changing the document or placement.
|
|
47
|
+
|
|
48
|
+
## 0.20.0
|
|
49
|
+
|
|
50
|
+
### Migrate
|
|
51
|
+
|
|
52
|
+
- **Bump lockstep SDK packages to `^0.20.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.17.0`; `@jgengine/github` is `0.7.0`.
|
|
53
|
+
- World health bars and nameplates now hide behind blocking world geometry by default. Use `occlude: false` for intentional reveal overlays; distance is measured from the render camera. Nameplates suppress machine identifiers unless an explicit `resolveName` supplies a display label.
|
|
54
|
+
- Character controller standing and explicit crouch heights must be at least `2 * radius`, and crouch height must not exceed standing height. Invalid dimensions and control ranges now throw with repair guidance instead of creating a capsule below the declared feet.
|
|
55
|
+
- Await `useGame().commands.run(...)` when showing command results: authoritative shells now wait for host acknowledgement; offline calls remain synchronous. Trusted host execution stays on `ctx.game.commands.run` / `runAs`.
|
|
56
|
+
- Remove manual `ctx.time.advance` inside `ctx.sim.advance` callbacks. Simulation advances it once and supplies scaled gameplay time as the callback's third argument; prediction opts out with `{ advanceTime: false }`.
|
|
57
|
+
- Await hosted `save()` and Node `flush()` at persistence boundaries. Games can retire mirrored restore logic after migrating legacy saves; retain projections the UI reads. State never present in an old save cannot be reconstructed automatically.
|
|
58
|
+
- Direct hosted-runner callers must join or resume a member before submitting its input; frames from nonmembers are ignored.
|
|
59
|
+
- `MaterialMaps.ao`, `MaterialMaps.roughness`, and matching terrain roles are optional. Pass the resolved maps directly to material seams or check for a URL before loading it. Native ambientCG pulls no longer advertise unprovided KTX2 maps.
|
|
60
|
+
- Default offline `ctx.game.save` now reports error when local storage is unavailable, denied, or full. Check `status()` before displaying saved; `await save()` alone does not imply success. Explicitly injected backends keep their policy. Choose `persist: { storage: "memory" }` or `memorySaveBackend()` for intentional ephemeral/headless saves.
|
|
61
|
+
- Slot index publication remains a separate operation: a payload may succeed before metadata fails, without cross-key rollback. Use the gameplay device-save-status recipe for checkpoint adoption and truthful UI.
|
|
62
|
+
- Custom `createQuestJournal` reward dependencies and `applyQuestRewards` appliers with multiple item rewards must supply an all-or-reject `grantItems` callback. Legacy single-item callbacks remain supported; multiple items without a batch callback reject before writes.
|
|
63
|
+
- When constructing a complete `StreamingSettings` object, add all required fields: `maxConcurrentLoads: 4`, `maxResidentBytes: Infinity`, and `residentEvictionOrder: "oldest"`. Callers using `Partial<StreamingSettings>` or spreading `DEFAULT_STREAMING_SETTINGS` inherit these defaults. Existing streaming fields remain required in complete objects.
|
|
64
|
+
- `SpatialGrid.forEachPair` now throws `RangeError` for negative or nonfinite distances. Pass a finite, nonnegative distance.
|
|
65
|
+
- Explicit `StreamingSettings` objects need `maxConcurrentLoads` (default 4). Cancelled loads keep a concurrency slot until settlement; loaders must settle after cancellation and an `unload` hook releases stale successful loads.
|
|
66
|
+
- `setEnvironment` now rejects invalid environment data before changing editor state, history, or live revisions. Use supported `day`, `dusk`, or `night` presets and valid authored fields; rejection reports the existing document path diagnostic.
|
|
67
|
+
- Pass the admitted `userId` to direct `WorldGameHost.getServerView()` reads after awaiting `joinServer()`. Reads without successful admission return `null`.
|
|
68
|
+
- `WorldGameHost.stop()` now permanently closes admission and ticks, drains accepted operations and saves. Await its completion and construct a new host to restart; failed saves reject the same repeated stop promise.
|
|
69
|
+
- Consumers asserting an exact GameContext snapshot key set should include the always-on `pursuitBehaviors` key; empty worlds contain `{ version: 1, instances: [] }`. Replace game-local pursuit/home/cooldown maps with the descriptor when its direct movement and target policies match the game.
|
|
70
|
+
- Shaped-grid dimensions must be positive safe integers; footprint cells and placement origins must be finite safe integers. Empty or fractional footprints and invalid quarter-turn rotations reject before overlap checks.
|
|
71
|
+
- Bare APIs keep their existing default discovery behavior. Opt into incremental mode only if every membership/position write reports its ID or invalidates. Subsets with a broader position resolver must report explicit candidate presence; omitted presence asserts membership when the position resolves. Unresolved candidates retain a fallback proportional to their count. Direct runtime entity mutation requires `invalidateSpatial()`.
|
|
72
|
+
- Scene rays require finite origins, direction magnitudes and nonnegative finite `maxDistance`; invalid rays throw `RangeError`. Bound each caller's range instead of passing `Infinity`. World-solid cell sizes must be finite and positive.
|
|
73
|
+
- Use `RegenShield.refill(amount)` for pickups and scripted point gains. Numeric `restore(amount)` remains a deprecated compatibility alias; `restore(state)` loads saved shield state.
|
|
74
|
+
- Custom asset import files now require safe relative POSIX paths, printable roles and plain JSON metadata; `classifyAssetFile` returns `null` for unknown bytes even when the filename has a supported extension.
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- `EditorSession.transaction(commands)` stages heterogeneous authoring commands atomically with indexed validation errors and one undo/redo step.
|
|
79
|
+
- Editor prefabs retain validated static bake settings with local collision solids and named clearance volumes; `set_prefab_static_bake` supports atomic authoring, persistence and undo/redo.
|
|
80
|
+
- `@jgengine/assets/staticPrefabBake` exports pinned static prefab source models with preserved transforms, materials, reachable textures and provenance, plus deterministic budgets and collision metadata.
|
|
81
|
+
- Catalog models and extras support `anchor: "origin"`, preserving composed asset coordinates through shared rendering and collider resolution. Unmapped objects use the same catalog lookup in both paths, allowing origin-only game rendering maps to be removed.
|
|
82
|
+
- Named, versioned player scene saves through injected `CreatorDocumentStorage`, with optimistic revisions, schema validation, catalog permissions and explicit work budgets.
|
|
83
|
+
- Production `GameHost` creator configuration controlled by game-owned menus, with shared Create/Edit/Save/Reopen and fresh isolated document-snapshot playtests.
|
|
84
|
+
- Named particle emitters reuse the existing director, pooled simulation, and shell renderer. Start/stop preserves live particles; burst, seed, bindings, and output remain retunable and serializable. Capacity checks reject standing-emitter overflow explicitly.
|
|
85
|
+
|
|
86
|
+
- Disc, sphere, and cone spawn volumes compose with the shared force-field sampler. Explicit environment influence shares live wind and physical field envelopes; cosmetic pools follow simulation pause and time scaling. Particle collisions use bounded scene queries and consume-once cosmetic events; bounded child impacts cannot recurse. Graphics settings cap visual pools without changing physical authority.
|
|
87
|
+
|
|
88
|
+
- The existing renderer supports instanced streaks, flakes, flames, smoke, ribbons, and ground ripples alongside point output. Caller-selected projectile and flock models follow authoritative simulation positions; their existing model owner handles animation and cleanup. Habitat gameplay actors remain game-owned.
|
|
89
|
+
- Scene markers can store `meta.hiddenNodes` as exact node names, shared by runtime props and editor material previews. Omission preserves model defaults; an empty list clears the selection. Nonempty selections keep automatic props on individual renderers, and generic scatter rejects unsupported node hiding before cloning.
|
|
90
|
+
- Optional live travel in `createProjectileSystem` captures launch pose, applies injected environmental acceleration with per-shot response, target masks and force caps, sweeps each authoritative segment, and settles hits, range misses and lifetime expiry once. Active poses and detached snapshot/restore support caller rendering and replay; active and retained-shot budgets bound work and memory. Settlement reports and `projectile.settled` events carry the matching `shotId` and detached actual effect results so game policy can run after contact. Legacy immediate settlement remains available.
|
|
91
|
+
- Shared `physics/forceVolume` sampling covers signed sphere/box attraction, directional forces, independently tuned vortex spin and axial lift, attenuation, target masks and caps without renderer state. Authored physical fields and cosmetic particles reuse the sampler.
|
|
92
|
+
- Authored weather profiles, absolute schedules, transitions and local wind zones share one renderer-free sample with gameplay and precipitation.
|
|
93
|
+
- Persistent bounded surface wetness, snow, heat and exposure support snapshots, restoration, retuning and injected storage.
|
|
94
|
+
- Weather rendering consumes simulation time, clips particles against the nearest 32 authored shelters and supports bounded ground ripple pools.
|
|
95
|
+
- Bind editor-authored reusable material assets to named slots on instanced BuildingKit models, preserving omitted native materials, independent caller styles, and owned resource cleanup.
|
|
96
|
+
- Animation inspector playback rate, hold, single-clip seek and loop controls, and variant selection through existing scene metadata/RPC.
|
|
97
|
+
- Game-chosen movement catalog schemas, partial tuning with repair diagnostics, memoized document readers and stable live bindings that preserve game callbacks and physics policy.
|
|
98
|
+
- Optional live maximum climb grade in caller-declared movement catalog schemas, preserving the game's height sampler and existing default policy.
|
|
99
|
+
- Optional positive heightfield character collision height, authored through ordinary movement catalog controls and live bindings while preserving game policy, model proportions, camera placement and other controller dimensions.
|
|
100
|
+
- `resolveRigNode` (`@jgengine/shell/render/rigNode`) resolves an instance's bone or Object3D slot by unique runtime/original imported name with structured diagnostics. Resolve once per model/slot change, preserving animated hierarchy, local offsets and clone isolation.
|
|
101
|
+
- Supplied-only movement catalog controls for existing jump buffering, coyote grace, jump release, apex/fall gravity and landing recovery. The collapsed Jump response group saves and applies live tuning while preserving game policy, with located diagnostics for invalid values.
|
|
102
|
+
- Shared player movement exposes cached, read-only grounded, vertical velocity and crouch telemetry through indexed possession ownership. Unknown actors and restored movement report no telemetry until a physical step completes.
|
|
103
|
+
- Model graph playback feeds resolved `grounded`, `verticalSpeed` and `crouched` parameters without writing the entity blackboard. `readModelAnimationParams` supplies the same behavior to custom render hosts with a reusable output dictionary; custom movers retain their authored parameters when motor telemetry is unavailable.
|
|
104
|
+
- `playerMovementTelemetry(ctx, entityId)` (`@jgengine/core/movement/playerMovement`) exposes the last shared motor’s grounded, vertical velocity and crouch state through indexed ownership. It reuses a read-only view and returns `null` for unmanaged, unstepped or despawned entities, after restore/forget, and when another authority changes the motor proposal; it does not write animation stores each frame.
|
|
105
|
+
- `suspendPlayControls` composes independent player-control suspension leases across overlapping menus without pausing shared simulation.
|
|
106
|
+
- Anonymous authority fixtures accept validated, separate frontend and realm ports with an explicit frontend websocket endpoint.
|
|
107
|
+
- Relay Courtyard declares a persisted compact or detailed readout through the shared HUD settings framework.
|
|
108
|
+
- Core content reference and declared-source progression validators report located repair guidance across game-owned catalogs. Closed-source analysis detects unavailable prerequisites; intentional seeded cycles remain valid.
|
|
109
|
+
- Native recipe catalog validation checks item/station/unlock references, quantities and duplicate inputs without imposing economic or output-benefit policy.
|
|
110
|
+
- `useDialogBehavior` (`@jgengine/react/dialogBehavior`) shares dialog focus entry, Tab cycling, Escape close requests, and restoration with caller-owned screens; `ModalHost` uses the same behavior.
|
|
111
|
+
- `resolvePeerShellMultiplayer` can host a playable authoritative GameContext world with injected persistence, signaling, peers and tick scheduling; `close()` awaits its save boundary.
|
|
112
|
+
- `createWorldGameHost` supports a serialized `slotsPerServer` player cap while allowing existing members and spectators.
|
|
113
|
+
- `bindCommandTransport` / `dispatchCommand` provide the shared UI command boundary; `possession.ownerOf` resolves unique recorded ownership.
|
|
114
|
+
- A source-owned anonymous authority fixture reports separate compiled frontend and realm revisions and preserves throwaway progress through reload.
|
|
115
|
+
- `GameContext.state()` / `restore()` capture detached authoritative state without an offline save backend; hosted runners expose `state()` and `commit()`.
|
|
116
|
+
- Hosted sessions persist player identity and bounded command retry receipts alongside world state, expose `hasPlayer()` and `persistenceError()`, and accept stable operation ids.
|
|
117
|
+
- `disposeModelScene` (`@jgengine/shell/render/modelRender`) releases a `cloneModelScene` instance's materials and bone textures while preserving loader assets and separately owned attachments.
|
|
118
|
+
- Asset streaming supports a retunable `maxResidentBytes` target and deterministic oldest/largest eviction within the frame unload budget. Pinned and retained assets remain protected; byte pressure overrides idle grace and small-asset caching.
|
|
119
|
+
- Streaming residency diagnostics expose protected and over-budget bytes, unknown-size loads, eviction totals, and record counts. Explicit `forget(id)` removes only settled, inactive, unowned history so demand churn can bound metadata without silently invalidating record consumers.
|
|
120
|
+
- `assets budget` scans custom/imported or pulled GLBs offline and reports per-model bytes, stored triangles, and image dimensions with configurable CI limits and actionable failures.
|
|
121
|
+
- `readGlbMetrics` and Node `createAssetBudgetReport` / `checkAssetBudget` expose the same inspection; reindex records successful inventory in optional `IndexEntry.metrics`.
|
|
122
|
+
- The 3D shell batches compatible authored catalog props above eight placements per spatial cell into instanced model draws, preserving each chosen model, material group, shadow mode, collider measurement and object id for picking. Interactive, custom, animated, composed, transparent and mirrored models retain individual renderers; culled placements leave the submitted instance set.
|
|
123
|
+
- Bounded SFX reservations (default 64 simultaneous playbacks) with configurable total/per-sound caps, priorities, reject/steal policy, injected allocator storage, snapshot/restore, and live retuning.
|
|
124
|
+
- React exports `observeBrowserSuspension` and `useBrowserSuspension` for configurable blur, hidden-page and pointer-lock loss notifications. Games retain their pause and modal policy; listeners detach on cleanup and the hook invokes the latest callback.
|
|
125
|
+
- Compose deterministic timed away missions with pure expedition dispatch, bounded offline settlement, finite supply consumption, death, item capacity, and safe return transitions (`@jgengine/core/work/expedition`). Caller-owned policy and content use a persisted RNG cursor and plain saveable state.
|
|
126
|
+
- Apply caller-authored elapsed-time loot curves with `timeScaledRarity` (`@jgengine/core/game/lootModifiers`), retaining original and effective odds in loot pipeline provenance.
|
|
127
|
+
- Generated item registries accept caller-owned synchronous storage and a saved allocation sequence; `statePages` and `restorePages` stream detached save batches through atomic replacement while preserving the existing whole-state format.
|
|
128
|
+
- `validateQuestCatalog` (`@jgengine/core/game/questCatalog`) checks authored quest ids, references, quantities, and blocked prerequisite dependencies with explicit external unlock and quest-start declarations, optional caller catalog lookups, and located error/warning diagnostics. The `quest-authoring` CLI recipe validates bounded generated batches against caller-owned canonical facts.
|
|
129
|
+
|
|
130
|
+
- Snapshot and freeze reviewed authoring inputs across async generation so caller or generator mutation cannot replace canon, catalog references, or batch limits.
|
|
131
|
+
- Resolve a bounded staffing snapshot into a production rate with `stationOutputRate` (`@jgengine/core/work/staffedStation`), using caller-owned stats, base output, per-stat scaling, and efficiency. The pure function composes with existing production and serializable game state.
|
|
132
|
+
- Key-value cells and local save backends accept optional storage failure observers and `errorMode: "throw"`, exposing original read/write/remove errors while retaining their legacy fallback defaults.
|
|
133
|
+
- Asset streaming `retune` merges streaming policy; setting concurrency to zero pauses new loads.
|
|
134
|
+
- Pure compatible footprint merge/split with retained source identities and unequal capacities, JSON roundtrip, connected partitions and optional linear work budgets. Grid reservation commits, upgrade pricing and arbitrary cell-cut allocation remain caller-owned.
|
|
135
|
+
- Add read-only native material assignment preflight for selectors, UVs, construction prerequisites and scoped authored texture-role restrictions.
|
|
136
|
+
- `ModelConfig.hiddenNodes` and `useModelInstance` support per-instance named visual subtree selection before placement measurement, retaining imported rigs, clips, textures and cached source visibility. Missing or ambiguous names warn without hiding arbitrary nodes; malformed lists reject before model or texture allocation. Changing the selection replaces only that instance, while equivalent list contents retain its clone.
|
|
137
|
+
- Simple model and authored surface map configs now expose all 20 supported `MaterialTextureRole` URL fields. Composed model dependency discovery preloads advanced physical maps while preserving the seven legacy roles' loader order and ignoring unsupported roles. Authored surface keys include advanced URLs, so retuning reloads them and equivalent map ordering retains owned resources.
|
|
138
|
+
- Declarative `PersistConfig.migrate` forwards the existing runtime-save migration hook, allowing games to convert or reject incompatible old world snapshots before restoration. Matching versions bypass it; omitting migration preserves compatible mismatch loading.
|
|
139
|
+
- `HostRouter.drain()` waits for accepted message handlers and subscription reads; call `close()` first to prevent further work before owned host persistence teardown.
|
|
140
|
+
- `pursue` behavior data schedules bounded target acquisition, solid-aware chase, cooldown effects and return to spawn through the existing behavior lifecycle; optional threat uses the shared threat table.
|
|
141
|
+
- `StaticShapeInstances` batches document-derived boxes/cylinders by PBR, sampler and shadow policy, with owned resources and conservative transform bounds. `AuthoredSurfaceMaterial` and `useAuthoredSurfaceMaterial` compose shared material overrides, cloned texture-role color spaces, configurable wrapping/anisotropy, normal scale and optional metre UVs.
|
|
142
|
+
- `jgengine upgrade --plan/--apply [--to x.y.z]` reviews and updates existing root workspace SDK catalogs from verified published versions, preserving game declarations, editor overrides, and unrelated text; installation and lockfile updates remain a separate step.
|
|
143
|
+
- `jgengine doctor --workspace [root] [--json]` checks installed SDK identities across the root and declared workspace owners with bounded manifest enumeration, explicit incomplete coverage, and separate resolver checks for distinct directories sharing a manifest.
|
|
144
|
+
- Discoverable shaped-grid placement operations and bounded first-fit search with caller-ordered rotations, row-major positions, and distinct no-space/budget results.
|
|
145
|
+
- Bare spatial APIs can opt into notified incremental indexing with `incremental: true` and `updateEntity(id, candidatePresent?)`. An explicit presence flag supports candidate subsets independently of the position resolver. Entity stores offer a committed-ID/membership hook before existing subscribers.
|
|
146
|
+
- `ScrollRail` supplies native horizontal scrolling, visible overflow navigation, and keyboard focus reveal for caller-owned controls.
|
|
147
|
+
- `WorldGameHost.unload(serverId)` explicitly saves and releases idle world references. It refuses players, spectators and resident members, retains a world after save failure, and reloads through the session factory on a later join.
|
|
148
|
+
- `createWorldSolids` supplies optional `inRay` candidates through crossed hash-grid cells, preserving live layers and oversized blockers. Sparse or unrepresentable traversals fall back to cached AABB ray tests without dropping geometry. Optional caller-owned work counters expose cell, entry and bounds costs. Scene queries use it automatically; `inBox`-only adapters remain compatible.
|
|
149
|
+
- Heightfield walking supports an optional `movement.maxClimbGrade` uphill rise/run limit, with axis sliding and accepted-position foot grounding. `movement.climbGradeHeight` preserves game-specific terrain sampling independently of effective ground height. Omitted configuration leaves existing movement unchanged.
|
|
150
|
+
- `jgengine find --json --limit N` returns bounded ranked capability and recipe matches with total, partial-match, and truncation metadata. Invalid options now produce actionable errors.
|
|
151
|
+
- `validateDialogueGraph` diagnoses broken references, duplicate ids, unreachable nodes, and dialogue without a route to an ending using iterative linear traversal. Authoring guidance separates structural validation from story review.
|
|
152
|
+
- `freezeNarrativeCanon` and `validateNarrativeBatch` (`@jgengine/core/game/narrativeAuthoring`) keep reviewed revisions separate from drafts and locate explicit character/fact/chronology and dialogue graph issues within caller batch budgets.
|
|
153
|
+
- `jgengine recipe narrative-authoring` composes detached authoring snapshots, bounded repair attempts and caller-injected generation plus explicit semantic review; it never selects a model or changes canon.
|
|
154
|
+
- Serializable physical material assets, texture metadata and exact named mesh/slot assignments preserve imported materials and maps unless explicitly overridden.
|
|
155
|
+
- Shared physical surface rendering, fuzzy sheen, directional woven appearance, hair-card ribbons and coverage/backscatter approximations; geometry and simulation remain separate.
|
|
156
|
+
- Materials workspace edits six family-specific groups and advanced maps, previews neutral or document lighting, and shares editor undo, save and typed RPC with runtime.
|
|
157
|
+
- Native glTF material inventory and export diagnostics preserve packed channels, extensions and attribution; resource accounting and appearance adapters consume wetness/exposure without changing global lighting.
|
|
158
|
+
- Typed slot-inventory RPC reports actual loaded model prerequisites; opt-in projected-overdraw measurement preserves native coverage and restores rendering resources.
|
|
159
|
+
- Portable `jgengine drive --click-at x,y` sends ordered native left clicks to canvas targets in CSS viewport pixels, with finite nonnegative coordinates checked against device or custom viewport bounds before browser launch.
|
|
160
|
+
- Typed game definitions can inject context-aware projectile sweeps, acceleration and bounded target sources. Each world supplies its own context; custom acceleration overrides the authored sampler, while omitted providers retain existing defaults and engine-owned time.
|
|
161
|
+
- Pursuit can use a live-world `defineGame({ pursuit: { eligible } })` callback to authorize nearest, retained, explicit and forced targets without a game-local brain loop. Cold saves retain progress and receive the loading world's injected policy; the existing player-role default and effect acceptance checks remain unchanged.
|
|
162
|
+
- Core ability kits, item-instance registries, event meters, accumulator meters, and regen shields expose detached `state()` and `restore(state)` contracts for saves and deterministic continuation. Ability HUD views remain compatible; saves preserve retuning, independent cooldowns, allocation sequence, meter latches, and shield grace timing.
|
|
163
|
+
- `EventMeter.tick(dt, decayEnabled)` lets caller-owned visibility policy pause decay and its delay; existing one-argument ticks behave as before.
|
|
164
|
+
- `registerFirstPersonMuzzle(camera, reader)` supplies a custom rig's live presentation muzzle with camera isolation, bounded reader work, and owner-scoped cleanup.
|
|
165
|
+
- `Magazine.retune` changes capacity and reload duration while preserving ammo, shared reserve storage, and elapsed reload time; capacity overflow defaults to atomic rejection with explicit discard/return policies.
|
|
166
|
+
- `ProjectileSystemDeps.rng` supplies deterministic per-pellet spread sampling; `maxPellets` bounds ray work (64 by default, configurable up to 256).
|
|
167
|
+
- `planSolidRoute` (`@jgengine/core/nav/solidRoute`) plans bounded local routes over indexed movement collision boxes, with exact endpoints, caller-owned terrain policy and explicit no-path/budget results.
|
|
168
|
+
- `findPathResult` adds explicit grid search-budget results; grid edges and smoothing accept a caller traversal policy. Existing `findPath` callers retain their return contract.
|
|
169
|
+
- `useModelInstance` (`@jgengine/shell/render/useModelInstance`) owns shared loading, isolated clone cleanup, bind-pose placement and animation for custom R3F renderers; `EntityModel` consumes the same hook.
|
|
170
|
+
- Switch existing camera rigs at runtime with a serializable data overlay, and apply vehicle board/leave patches through shared helpers with explicit rider identity and caller camera policy. Seat occupancy exposes snapshot/restore/reset and an unseated-safe driven-vehicle query.
|
|
171
|
+
|
|
172
|
+
### Changed
|
|
173
|
+
|
|
174
|
+
- Root-motion graph states now extract travel and render horizontally in place. The renderer no longer writes entity poses outside the movement/collision authority. Hosts that relied on that behavior must advance the headless graph, transform its `rootDelta` with `takeRootMotion`, and resolve requested movement through their controller. Graph previews can pass `output.rootMotion` to `createGraphPose.apply` for the same policy. This does not provide automatic collision-aware root-motion locomotion or retargeting.
|
|
175
|
+
- Animation graph root-motion flags include influencing outgoing states. Root deltas still sample current-state travel; blended collision-authoritative root travel remains unsupported by the automatic renderer.
|
|
176
|
+
- `GrassField` partitions its existing seeded tuft stream into at most 64 shared-template draws. Each render camera skips offscreen or fully distance-faded chunks while preserving the density/budget prefix, transforms, material and shadow policy. Chunk geometries are disposed on replacement/unmount.
|
|
177
|
+
- GameContext lazily persists pursuit home, targets, cooldown, scheduling, threat and lifecycle state in `pursuitBehaviors`, including cold save/reload and AOI replication. Legacy snapshots without this module remain accepted.
|
|
178
|
+
- Upgrade reports include Rapier and Navbake, reject corrupt installed metadata, and distinguish missing migration notes or an unverified notes target from an up-to-date published SDK.
|
|
179
|
+
- Runtime entity radius/arc queries update individual grid entries after pose and membership writes instead of rebuilding or enumerating the whole population on every query. Cold invalidation and large membership bursts rebuild lazily; save/replication hydration still derives the index from authoritative entities. Immediate subscriber queries observe committed membership and poses.
|
|
180
|
+
- Asset authoring guidance keeps palette selection and visual identity game-owned while reusing catalog, import, validation, and rendering tools.
|
|
181
|
+
- Quest journal kill and collection credit uses active objective indexes instead of scanning the quest catalog. Party credit still reaches nearby members when the killer has no quest; replacement definitions and restored saves refresh indexes. Re-register after editing objective targets, items, or party sharing. Quests activated during credit receive subsequent events.
|
|
182
|
+
- Editor RPC (`decodeEditorBridgeRequest`, `@jgengine/editor`) now rejects any top-level request field the method does not declare with `unknown field "<name>" for method "<verb>"` instead of ignoring it and returning `ok: true`.
|
|
183
|
+
|
|
184
|
+
### Fixed
|
|
185
|
+
|
|
186
|
+
- World overlays use camera-to-anchor visibility through the shared object/wall/terrain query, hide depleted health and invisible entities, and bound nearby sampling and occlusion rays per refresh.
|
|
187
|
+
- Editor command patches no longer leave partial edits or multiple live publications when a later command fails. Stable-id upserts and already-current edits succeed without duplicate placements or new revisions.
|
|
188
|
+
- Rejected creator edits leave document history and live-sync publication unchanged; durable save failures remain visible and approved catalog placements avoid persisting raw asset URLs.
|
|
189
|
+
- Edit mode preserves the game's authored models and placement overlay alongside editor guides; save acknowledgements, dirty state and pending failures survive Play/Return.
|
|
190
|
+
- Live authored prop moves/removals update previews while unrelated edits preserve game-initialized prop state.
|
|
191
|
+
- Creator budgets include bounded collision child pools; play clock control preserves native canvas rendering without postprocessing.
|
|
192
|
+
- Player shell replacement, exit and failed initialization dispose the owned runtime context exactly once, including environment resources when game disposal throws.
|
|
193
|
+
- Ballistic sweeps now test complete segment chords, so thin cover between samples blocks projectiles. Translating sphere/box sweeps handle targets crossing a projectile path between endpoint poses; authored cover uses centerline projectiles, with radius-aware collision available through an injected sweep.
|
|
194
|
+
- Headless, hosted and shell authority integrate player movement with scaled game time and retain queued motion while paused or at zero timescale. Fixed owners opt into full authoritative durations for normal, voxel and free-flight movement; standalone movement retains its historical frame clamp. Explicit hosted client prediction retains its input-step duration.
|
|
195
|
+
|
|
196
|
+
- Fixed-step movement now uses scaled game seconds without the standalone browser-frame clamp; pause keeps queued motion and leaves physics unchanged.
|
|
197
|
+
- Settlement events include matching shot IDs and detached actual effect results, so game-owned policies can settle once on the struck target.
|
|
198
|
+
- Authored primitive surfaces and static shape batches now decode sheen-colour and specular-colour maps as sRGB, using the shared texture-role semantics. Numeric maps remain linear and loader-cached textures remain untouched.
|
|
199
|
+
- Shared precipitation uniforms preserve per-layer wind when the provider has no wind configured.
|
|
200
|
+
- Fire grids restore fuel/heat state, retune live wind/rates and reuse a bounded heat workspace; fire animation supports simulation time.
|
|
201
|
+
- Lightning animation supports authoritative start/time values and seeded flicker for pause and replay.
|
|
202
|
+
|
|
203
|
+
- Authored runtime state shares the fixed game clock, preserves fire/flock poses on retunes, restores atomically, and publishes explicit appearance signals without renderer ownership.
|
|
204
|
+
- Games select force targets through `installEnvironmentMotion`; caps and masks are game policy, and paused simulation does not queue forces.
|
|
205
|
+
- Fire tools and opted-in rain cooling extinguish burning cells while preserving fuel; authored fire bindings stop spawning when the area stops burning.
|
|
206
|
+
- Typed `get_simulation` / `set_simulation` authoring persists weather, schedules, wind zones, emitters, fields, fire areas and habitats through ordinary undo, save and import/export.
|
|
207
|
+
- Reject numerically unsafe force magnitudes and wind coupling before authority or caller buffers mutate.
|
|
208
|
+
- Forward game-selected visible path kinds so flock/patrol guide routes need not draw as ground roads.
|
|
209
|
+
- Changelog checks validate every existing release fragment before missing-base, skip-marker, or source-free exemptions. Malformed notes now fail CI even when they predate the checked diff; pure refactors still need no new note.
|
|
210
|
+
- Incomplete saved locomotion mappings no longer create an animation graph with undefined clip names. A named single clip remains active while idle is unconfigured; otherwise the rig retains its bind pose. Missing walk mappings hold the explicitly named idle until repaired. Diagnostics report incomplete roles and validate the effective playback mode, including explicit graph precedence.
|
|
211
|
+
- Explicit empty locomotion mappings survive playback edits and clearing the last role, so incomplete characters do not silently switch to the model's first clip. Choosing Single clip still replaces the mapping.
|
|
212
|
+
- Rendered animation variants use independent deterministic streams per model instead of advancing gameplay RNG. Mounting, hiding or culling animated models cannot change gameplay random outcomes. Each remount restarts its visual stream; callers whose clip selection affects gameplay should own a headless animation runtime, injected RNG and saved state.
|
|
213
|
+
- Preserve held-pose playback settings and all one-shot variants when editing an authored character's animation. Graph previews include variant clip durations and retain the shell's in-place root-motion policy.
|
|
214
|
+
- Equivalent freshly created character animation configurations retain mixer playback, pending triggers and per-instance visual variant state across React rerenders. This fixes store-driven custom characters such as Deepward’s imported Fitter restarting their gait on every update. Actual clip, held-frame, mapping or graph edits still reconfigure and clean up the owned mixer; authored one-shot order is preserved.
|
|
215
|
+
- Generated locomotion graphs give death priority and retain its held pose when later hit or attack events arrive. One-shot clip arrays survive graph conversion and JSON parsing; seeded runtime choices are recorded in snapshots.
|
|
216
|
+
- Animation layers keep independent mixer actions when they use the same imported clip. Crossfades keep the outgoing animation advancing at its own rate and loop policy. Full-cycle clip events are retained, multiple missed cycles coalesce, and muted contributions do not emit events.
|
|
217
|
+
- Model animation diagnostics identify missing clips, empty layer masks and unsupported root-track assumptions. Invalid explicit single clips retain the bind pose instead of looping the rig's first clip.
|
|
218
|
+
- The first locomotion edit after single-clip playback seeds the imported rig's semantic roles while preserving authored playback and variants. Editor previews and runtime agree on incomplete idle/walk mappings, with repair hints.
|
|
219
|
+
- PhysicsWorldBackend capsule casts allow separating or tangent motion from machine-scale starting contact, so a valid character proportion can jump and move on its first authored-floor frame. Entering contact, real overlap, walls and ceilings still block movement; the numerical tolerance scales with the struck face's world coordinate.
|
|
220
|
+
- Headroom overlap checks use the same numerical contact tolerance, allowing a restored crouched character at exact floor height to stand and jump immediately while retaining real penetration and low-ceiling blocking.
|
|
221
|
+
- Foot IK rejects missing, ambiguous, overlapping or disconnected leg chains with named diagnostics instead of deforming unrelated rig branches. Automatic pelvis correction now uses the common ancestor of resolved legs; explicit pelvis selection is preserved.
|
|
222
|
+
- Disabling, replacing or unmounting foot IK restores its previous joint corrections without overwriting newer animation poses, and replacement resets pelvis smoothing.
|
|
223
|
+
- Shared heightfield player movement stops jumps at blocking ceiling undersides and lands on crossed object tops, including thin bounds and full authoritative simulation steps. Blocked curb and terrain rises retain valid headroom and recheck alternate-axis wall collision instead of penetrating a roof and ejecting the player sideways.
|
|
224
|
+
- Heightfield descent and landing recovery now follow the accepted terrain/object support rather than prematurely landing at the previous support height. Exact-clearance contacts remain walkable at translated coordinates; explicit height and `beforeCommit` policies retain their authority.
|
|
225
|
+
- Resolve imported attachment slots by a unique original authored name when GLTFLoader sanitizes the runtime name, including `handslot.r` on the supported Knight and Rogue Hooded rigs. Exact runtime names retain priority; missing or ambiguous references report repair guidance instead of attaching to an arbitrary node.
|
|
226
|
+
- Explicit foot-IK joint, pelvis and look-at names use the same original-name fallback. Knight and Rogue authored leg chains retain the same walking corrections as their runtime-name configurations; ambiguous, disconnected and overlapping chains still warn and are skipped.
|
|
227
|
+
- Capsule movement uses the same resolved timestep as its shared jump integrator: standalone stalled frames retain the movement clamp, while authoritative game-time steps integrate in full. Game position callbacks still receive their original frame timestep.
|
|
228
|
+
|
|
229
|
+
- Supported crouch input now overrides sprint and suppresses jumping. Capsule standing waits for headroom; `CharacterController.setCrouch` preserves standing and jumping on the same frame.
|
|
230
|
+
- Physics-backed capsules share heightfield jump buffering, coyote grace, release shaping, apex/fall gravity and landing recovery. Legacy held-jump saves retain their latch.
|
|
231
|
+
- Resolved movement feel and gravity/jump tuning follow live declaration getters without resetting movement state.
|
|
232
|
+
- Automatic stair stepping requires grounded motion. Near-contact ground casts maintain the configured capsule skin gap.
|
|
233
|
+
- Rapier ramps preserve collider surface normals without stair forgiveness. Character casts exclude the character before choosing a blocker, so its own collider cannot hide a wall.
|
|
234
|
+
- Authored jump graphs can follow physical takeoff, apex, landing and buffered re-jumps rather than playing ground locomotion throughout a jump. Clip choices and transition policies remain game configuration; no rig-independent jump mapping is inferred.
|
|
235
|
+
- Short characters’ default crouch height fits their capsule diameter. Retuning proportions recomputes omitted defaults while preserving explicit crouch height, and live declared capsule field changes retune the shared motor before its next move.
|
|
236
|
+
- Editor clip and graph previews use the shared model-instance placement and cleanup lifecycle, preserving imported character root transforms and releasing instance bone textures without touching cached rigs.
|
|
237
|
+
- Preserve horizontal in-place playback while an outgoing root-motion clip still influences a crossfade, including interrupted fades and restored graph snapshots. Previously a dodge fading into idle could shift the visible rig away from its collision-authoritative entity.
|
|
238
|
+
- Forward authored-object kind exclusions through `defineGame` scene placement, allowing a game-owned player spawn to expose its catalog animation clips without also placing a static actor. Ordinary props and the default mob/boss exclusions retain their existing behavior.
|
|
239
|
+
- Input-owned chase cameras preserve their first acquired target's authored heading instead of starting at zero. The existing `camera.initialYaw` can override that one-time seed; later body turns remain independent of walking input.
|
|
240
|
+
- Shell settings clear held keyboard, pointer and analog input through close, context replacement and unmount, and preserve other owners' suspension.
|
|
241
|
+
- Shop stock preserves live entries when restoring a save with invalid entries or duplicate ids.
|
|
242
|
+
- Colour-grade LUTs survive graphics-quality and post-processing graph rebuilds. Disposing a grade pass releases its loaded LUT and any texture that finishes loading after disposal.
|
|
243
|
+
- Shared game presentations report render failures with an explicit display retry while retaining the live game context and realm connection.
|
|
244
|
+
- Terrain material retry clears only pending exact texture groups owned by the failed presentation, including failures before Suspense commits, while preserving successful caches.
|
|
245
|
+
- Player-facing recovery explains the failure plainly, keeps technical details collapsed, and restores game focus after a committed retry. A recovered draw retires only its handled presentation diagnostic.
|
|
246
|
+
- Keyboard, touch release, control suspension and shell unmount publish input outside the render loop. Discrete intent supersedes older pending input acknowledgements without replaying held controls.
|
|
247
|
+
- Invalid inventory slot indices and non-finite split amounts return a rejection instead of throwing or corrupting saved item counts.
|
|
248
|
+
- HUD-only shells mount the shared settings store, menu, audio volume bridge, and input rebinding runtime.
|
|
249
|
+
- Shell keyboard handlers preserve native HUD focus and button activation while retaining active gameplay bindings.
|
|
250
|
+
- Settings option buttons expose their selected state and group label to assistive technology.
|
|
251
|
+
- Preserve configured settings through the public gameKit definition into HUD and 3D shells, including custom actions, rows, menu layout, quick controls and explicit disabling. Saved player values continue to override configured defaults.
|
|
252
|
+
- Terrain blend authoring adds new layers and paints in one undoable edit; failed brushes preserve the document and redo history. Layer reorder/additions preserve existing blends by id.
|
|
253
|
+
- Terrain RPC rejects invalid brush/dimension/ramp input and oversized allocations with actionable diagnostics.
|
|
254
|
+
- Viewport placement honors grid snap, paths select along their segments, and multi-selection gizmos translate the whole group consistently.
|
|
255
|
+
- Oversized terrain brushes bound work to the actual grid; native standalone editor captures now arm the existing readiness handshake and report load failures.
|
|
256
|
+
- Terrain-only authored scenes now frame their footprint and relief without a dummy marker.
|
|
257
|
+
- Terrain mesh changes invalidate the on-demand viewport so brush/undo/RPC edits appear without another input.
|
|
258
|
+
- Grid placement now resamples the shared ground field after snapping XZ, preventing floating or buried markers, zones and path points on slopes; invalid heights leave authoring unchanged.
|
|
259
|
+
- Editor live-sync clients recover from evicted document history with a revision-preserving snapshot; reverse-channel RPCs replace stale runtime entities after evicted removals.
|
|
260
|
+
- Creator saves reject exhausted revisions before writing, preserving the readable last valid scene and original durable catalog.
|
|
261
|
+
- Catalog-backed editor markers use wireframe spheres and selection halos so their glyphs no longer fill over placed characters. Picking geometry, emphasis and logical marker glyphs stay intact, including legacy catalog bindings.
|
|
262
|
+
- Report existing editor scene/catalog module import errors before opening an editing session or saving. Only an absent optional module resolves to an empty document or catalog.
|
|
263
|
+
- Hosted HUD controls, registered target commands and per-actor input queues route to the authoritative host without mutating replicas before join.
|
|
264
|
+
- Hosted clock snapshots reach client clock hooks; prediction never advances authoritative gameplay time.
|
|
265
|
+
- Remote player and possessed-pawn deaths credit the resolved killer for XP commands, quests and loot.
|
|
266
|
+
- Hosted saves retain economy, clock, pose, progression, simulation and system save modules; client replication stays private. Legacy partial saves retain initialized missing modules.
|
|
267
|
+
- Hosted ticks advance game time. WS commands serialize per world, roll back registered state on rejection/failure, and acknowledge after persistence. Client operation ids avoid collisions between sessions.
|
|
268
|
+
- Async hosted saves and Node `flush()` / `close()` await storage. Convex queries project client state instead of exposing authoritative saves.
|
|
269
|
+
- Model instances retain skeleton and material sharing within each rig, reducing duplicate bone textures and skeleton updates without sharing poses between characters. Entity models, attachments and scatter release their owned resources.
|
|
270
|
+
- Model animation releases mixer bindings on replacement/unmount and skips paused or completed clip updates. Configuration changes refresh demand-rendered poses.
|
|
271
|
+
- Sprite atlas frames change UVs without repeatedly uploading the image.
|
|
272
|
+
- Cascaded shadows release lights, targets, GPU shadow maps and disposed model shader bindings. Streamed meshes retain authored shader hooks without a full-scene material scan each frame.
|
|
273
|
+
- Composed model assets start loading together through the existing cache. Material maps apply before their first GPU upload rather than after the parent model draws.
|
|
274
|
+
- Pointer hits on instanced meshes transform their surface normals through the instance matrix.
|
|
275
|
+
- Cancel pending audio before graph creation and release sample/synth graphs on completion, stealing, stop, failed load, graph construction/start failure, and teardown. Explicit retained-loop start requests can retry stolen/stopped voices. Preserve authored spatial gain without double attenuation and keep nonpositional cues flat.
|
|
276
|
+
- Generated item allocation checks attached storage for id collisions and rejects unsafe allocator sequences or exhausted numeric ids.
|
|
277
|
+
- Standalone `jgengine shoot` / `drive` wait for a declared capture-readiness handshake instead of accepting a canvas while the host is still loading. Legacy pages fail readiness when their canvas stays undersized or has an empty backing store.
|
|
278
|
+
- `jgengine doctor` resolves default and named Bun workspace catalogs before checking SDK version alignment, reports missing or malformed catalog entries, and recognizes hoisted SDK installs.
|
|
279
|
+
- `jgengine upgrade` reads hoisted installed packages, uses resolved catalog pins when packages are not installed, and identifies the workspace catalog that owns an upgrade. The command remains a read-only migration report.
|
|
280
|
+
- Model placement retains imported root transforms and normalizes bind bounds in the same frame used for ground contact and collision triangles.
|
|
281
|
+
- Shell culling retains independent records for always-visible rows, refreshes bounds after visual-scale changes, and prunes removed rows from shell and core tracking.
|
|
282
|
+
- Canvas mouse bindings now deliver brief presses, native mouse chords and held actions through the shared input tracker, release owned buttons on focus loss or suspension, and prevent a claimed mouse gesture from also firing the legacy hotbar action.
|
|
283
|
+
- Press edges survive release before a simulation sample and urgent neutral input arriving before a delayed press; each authoritative host or deterministic replay consumes an edge once.
|
|
284
|
+
- Owner resets always supersede delayed input, including an identical neutral release still carrying an unacknowledged press.
|
|
285
|
+
- Stateless hosted reconstruction retains input admission and pending presses in host-only save state, so replaying stored held intent never fires a consumed press again.
|
|
286
|
+
- Hosted input now compacts elapsed records in memory and retains departed admission marks within a configurable user/TTL window; `inputRetention` sets the policy and `inputStats()` reports retained storage. Explicit future replay edges and standalone input logs remain intact.
|
|
287
|
+
- Shared card and inventory drags resolve touch targets through pointer capture, ignore secondary pointers, finish releases outside, and retire capture/listeners on cancellation or unmount while preserving keyboard move, split, and card rotation.
|
|
288
|
+
- Keep inventory grid, toolbar, and custom HUD keyboard interactions from also triggering gameplay actions in the shell.
|
|
289
|
+
- `FullscreenMap` owns a single pointer, discards canceled route drafts, and retires gestures on close, tool changes and unmount. Native mouse chords and outside release preserve ownership; map clicks place waypoints while custom overlay controls keep their input.
|
|
290
|
+
- `useVoice` reads open-mic transmission from its controller on mount and gates newly captured audio tracks before publication using the current mode and mute state. Explicit voice policy remains consistent across transport readiness and updates with stable route arrays.
|
|
291
|
+
- `PushToTalkButton` owns one primary pointer or Enter/Space activation and releases its hold on cancellation, capture loss, interruption and unmount. Secondary touches, auxiliary mouse buttons and key repeats preserve the owner's session; caller-owned toggle and open-mic policy remain intact.
|
|
292
|
+
- Retire window title-bar drag listeners on blur, capture loss, and unmount, and keep secondary pointers from moving or ending another pointer's window gesture.
|
|
293
|
+
- Pinned generated choices now obey their step's compatibility constraints and backtrack earlier unpinned choices when needed.
|
|
294
|
+
- Material catalog URLs follow pinned source map availability, including absent AO in `ambientcg-metalplates001` and absent roughness in `ambientcg-gravel001`/`ambientcg-concrete001`. Pull rejects archives missing declared maps; terrain skips absent AO/roughness sampling and keeps authored scalar roughness.
|
|
295
|
+
- Strict key-value failures preserve the prior cell and rejected payload writes preserve the stored checkpoint. Save slot metadata failures now report the existing error status and callback, including asynchronous updates after load, instead of reporting saved. Whole-world load/hasSave/slot switches no longer treat a denied read as a successful cached load.
|
|
296
|
+
- Portable `jgengine shoot` and `drive` enable touch and coarse-pointer media queries for mobile device profiles and clear touch emulation for desktop.
|
|
297
|
+
- Portable `drive --click` scrolls text targets into view and requires an unobstructed, enabled point within the viewport and clipping ancestors. Text matching remains case-insensitive.
|
|
298
|
+
- Portable CDP requests time out instead of hanging; `--timeout` bounds screenshot capture as well as frame readiness, and closing a session rejects pending requests.
|
|
299
|
+
|
|
300
|
+
- Native click targets also resolve accessible labels and allow three stable samples on slower rendered pages within a bounded 15-second settling budget.
|
|
301
|
+
- Built-in quest rewards stage the complete item batch against inventory capacity and kind rules before committing, preventing duplicated earlier rewards on retry. Reentrant turn-in calls reject while rewards are applying.
|
|
302
|
+
- Spatial pair queries include qualifying pairs across every cell covered by the requested distance, including distances larger than the grid cell size.
|
|
303
|
+
- Standalone GameHost now exposes declared capture.probe to the portable playtest driver, with boot-scoped cleanup that cannot read a retired context or remove a replacement host hook.
|
|
304
|
+
- Streaming bounds outstanding loads across frames, cancellation, clear, and same-id retries. Clear releases resident resources, and stale completions cannot overwrite new demand.
|
|
305
|
+
- Host subscription refreshes now serialize reads, coalesce burst notifications, discard obsolete lifecycle completions, and advance world cursors only after sending a frame. Slow initial reads no longer block unsubscribe or commands.
|
|
306
|
+
- Flight gates again read the accepted same-frame stance once. Rejected height edits and thrown gates restore the driven pawn’s exact pose and retain queued motion and existing motor state (#1962).
|
|
307
|
+
- Active interaction prompt edits and replacements now notify subscribers when their content changes, keeping stationary HUD labels and command data current. Equivalent edits, inactive prompt mutations and unchanged frame selections remain silent; selection still recalculates on `resolve`.
|
|
308
|
+
- Scene entity `moveToward` and `moveTowardCommit` now stop at indexed world solids, matching player and walker collision without game-local blockers. Bare `SolidObstacleSource` adapters can inject the same bounded world-solid query.
|
|
309
|
+
- `defineGame` preserves legacy environment sky settings when editor sky fields override part of the sky, including clouds, haze and partial sun/fog values. Explicit game `backdrop.sky` remains authoritative.
|
|
310
|
+
- Editor environment commands store decoded copies of nested fields, preventing later input mutations from changing authored state.
|
|
311
|
+
- The monorepo editor CLI validates the full scene before saving and preserves existing file bytes when validation fails.
|
|
312
|
+
- Node file persistence now rejects chunk-directory read errors other than `ENOENT`, preventing host admission with an empty world when an existing checkpoint cannot be read. A missing directory still means no saved chunks; the same host can retry after backend repair.
|
|
313
|
+
- Agent governance, intake, improvement workflow and new-game briefings require shared SDK adoption or upstream fixes, tracked consumer cleanup and centralized workspace versions while preserving each game's unique content and presentation.
|
|
314
|
+
- Region split now refuses an output identity retained by the opposite partition, keeping successful split results eligible for the existing merge identity gate. Reusing an identity from the same partition remains valid.
|
|
315
|
+
- Distance thinning now collapses blade width and wind displacement along with height. Fully faded blades vanish instead of leaving flat wind-displaced triangles; partially fading blades narrow, while `keep = 1` grass retains its existing appearance. `distanceFade: false` still disables distance rejection.
|
|
316
|
+
- Reject invalid EntityModel assignments before texture allocation and release failed material replacements exactly once without changing imported resources.
|
|
317
|
+
- Release unreturned material clones and promotions when imported shader callbacks fail during asset or rim construction, preserving the original error and borrowed resource ownership.
|
|
318
|
+
- Attempt every owned replacement's cleanup during assignment rollback even when an earlier disposal callback throws.
|
|
319
|
+
- Custom first-person muzzle readers can release registrations during a read without invalidating iteration or revisiting disposed owners; each priority uses a snapshot of its newest 64 candidates.
|
|
320
|
+
- Node world-server shutdown now fences socket intake and ticks, drains accepted router work, and awaits the stopped host's final save. Repeated close calls share completion and persistence failure; interval-only `stop()` remains restartable.
|
|
321
|
+
- Failed WebRTC host offer acceptance now closes and releases its allocated peer connection immediately, while preserving existing peers and the original negotiation error. Hosts can accept a fresh offer after failure without retaining the failed connection until teardown.
|
|
322
|
+
- Failed hosted-world join saves and spectator promotions no longer consume player capacity. Initially resident host members retain their reserved seats; failed callers retain dirty player state for retry and must satisfy capacity again.
|
|
323
|
+
- Closing BroadcastChannel peer signaling now rejects pending offers instead of leaving guest bootstrap unresolved, allowing the shell to release its guest transport. Closed signaling refuses new work and ignores late answer completions; listener replacement and successful exchanges remain supported.
|
|
324
|
+
- Playable peer sessions retry transient world loads, reject unadmitted snapshot reads, and drain accepted router work before the final close save. Hosted-world admission becomes visible after its join save succeeds.
|
|
325
|
+
- Same-ID behavior updates and warm hydration reconcile changed/removed descriptors before subscribers without scanning the world; equal-data updates retain progress and lifecycle. EntityStore exposes `subscribeBehaviors`, and replaced running decision graphs release existing action ownership through an explicit exact-once `abort` lifecycle method.
|
|
326
|
+
- Warm pursuit-module hydration reuses the keyed behavior cache; cold initialization keeps its initial entity discovery.
|
|
327
|
+
- Custom model placement retuning uses the configured bind frame after root animation. Generated asset markers use shared static-shape batching while preserving material defaults and authored transforms; Field Station's bookcase requires only the published package upgrade.
|
|
328
|
+
- `jgengine doctor` now detects private SDK copies reached through linked consumers and SDK dependencies/peers, including distinct copies with identical versions; it reports resolver paths and a root catalog/reinstall action without changing files.
|
|
329
|
+
- Shaped grids reject nonfinite/fractional dimensions, origins, and footprint cells instead of admitting non-discrete placements.
|
|
330
|
+
- Shared settings categories remain reachable on narrow screens and short sidebars; dialogs share focus trapping, Escape policy, and return focus.
|
|
331
|
+
- The floating joystick capture zone yields to interactive HUD panels while touch action buttons retain their layer.
|
|
332
|
+
- Replacing a focused action preserves a keyboard entry point into the action bar.
|
|
333
|
+
- `useDialogBehavior` keeps focus trapping and the original opener when an open dialog replaces its root; portalled children retain focus and return to the replacement parent.
|
|
334
|
+
- WebSocket router joins that finish after disconnect now release their memberships through the reconnect grace period, reclaiming capacity without removing a live reconnect waiting on admission.
|
|
335
|
+
- Scene raycasts now include indexed world solids as physical, non-damage wall hits. Generated and authored cover blocks the same LOS queries as placed objects; existing wall filters, hit acceptance, and projectile obstacle policy apply.
|
|
336
|
+
- Keep compiled hair-card backlight uniforms owned by each cloned material so local environment exposure and baseline resets affect only that instance while preserving the original material and shader composition.
|
|
337
|
+
- Compose hair-card backlighting with Three.js physical lighting across the supported peer versions, including r186's updated diffuse energy compensation, while retaining native lighting and independently owned clone uniforms.
|
|
338
|
+
- Shared grade constraints resolve travel before foot height, avoiding the rejected-neighbor height retained by game-level position interceptors. The restriction also applies during jumps while preserving the vertical arc.
|
|
339
|
+
- Explicit `onDeath` drop rules with `when.reason: "any"` can spawn world items after non-player deaths. Ordinary tables remain suppressed without a player killer, and bag and currency grants still require one. Hosted players and uniquely owned pawns retain their own loot recipient rather than the host's inventory.
|
|
340
|
+
- `createAuctionBook` rejects duplicate explicit IDs with `duplicate-id` and skips occupied generated IDs, preserving active auctions and escrow.
|
|
341
|
+
- `AuctionBook.claimItem` rejects nonpositive and noninteger counts without changing collection items.
|
|
342
|
+
- Listing books preserve occupied listing IDs: generated IDs skip collisions, and explicit duplicates return `duplicate-id` without replacing retained goods.
|
|
343
|
+
- Invalid negative, fractional and non-finite item claims leave collection boxes unchanged; zero-count no-ops and valid integer claims keep their behavior.
|
|
344
|
+
- Resource ledgers preserve positive finite reserves and remainders below `1e-9` through settlement and JSON reload.
|
|
345
|
+
- `createTechTree` consistently uses the last definition for duplicate IDs, preventing prerequisite bypass and obsolete recipe or category exposure.
|
|
346
|
+
- `createUnlockPoints` preserves `__proto__` unlock IDs and paid costs across JSON saves and reloads. Restoration discards invalid saved costs instead of granting free unlocks, while retaining valid fractional and zero-cost unlocks.
|
|
347
|
+
- NaN item wear throws before tracker mutation; nonfinite repair targets return `null` instead of producing repair quotes.
|
|
348
|
+
- `createModularItem` rejects initial parts with unknown slots, incompatible categories, or duplicate occupancy using the same validation as `install`.
|
|
349
|
+
- Model material promotions and shared texture views have explicit ownership and cleanup; unused material-library maps are not loaded.
|
|
350
|
+
- Editor module-load coverage uses a present, nonempty authoring fixture instead of relying on SDK/example imports from an isolated Games checkout.
|
|
351
|
+
- Grid navigation recognizes a reached goal at the exact `maxNodes` expansion budget instead of reporting exhaustion.
|
|
352
|
+
- Created standalone projects pin the exact scaffolding CLI locally, independently of SDK dependency versions.
|
|
353
|
+
- Skill installs check the entire selected set before overwriting, preserve newer and unversioned differing copies unless `--force` is passed, and record portable CLI/SDK version metadata.
|
|
354
|
+
- Doctor reports when the running CLI targets an older SDK minor than the project's installed packages.
|
|
355
|
+
- Standalone 2D and `--no-editor` games omit the unused editor dependency, avoiding its unnecessary package and misleading styling finding.
|
|
356
|
+
- Projectile restore now rejects missing radius-aware collision providers, oversized pellet/sample counts and inconsistent flight counts before replacing active or retained owner state. Compatible cold restores preserve pending impacts, death rewards and every settlement within retention budgets.
|
|
357
|
+
- A supplied pursuit eligibility callback accepts targets only when it returns exactly `true`; missing returns, nulls, objects and Promises deny attacks. An absent callback retains the documented player-role default.
|
|
358
|
+
- Generated building boxes and kit meshes use spatially bounded instance batches, so distant parts no longer keep a whole settlement batch in view. The shell applies the existing resolved graphics draw distance and visibility overrides while retaining material groups, fitted geometry bounds and nearby offscreen shadow casters.
|
|
359
|
+
- AO and depth-of-field scene prepasses reuse the beauty pass's shadow maps instead of redrawing shadow casters; failed prepasses restore shadow update settings and overlay visibility.
|
|
360
|
+
- Depth-of-field resizing and cleanup use the pass lifecycle, fixing a crash on current Three releases and duplicate resource disposal.
|
|
361
|
+
- First-person tracers can start at a custom world-overlay weapon's real muzzle even when the stock viewmodel is disabled. Stock muzzle tracking is camera-scoped; authoritative projectile origins remain unchanged.
|
|
362
|
+
- Scene-backed projectile spread now samples independent cone rays, each respecting its own nearest receiver, hitboxes, line of sight, and cover. Zero-spread pellets hit the nearest receiver instead of cycling through center-ray targets; center-ray prediction and ballistic paths remain unchanged.
|
|
363
|
+
- Projectile firing copies angular aim, explicit ray vectors and selected origin-policy vectors before injected RNG runs; caller reuse cannot redirect delayed settlement.
|
|
364
|
+
- Restoring an earlier living entity into the same context clears its stale death marker, allowing future lethal effects to despawn it and resolve rewards once. Transient depleted victims captured during `entity.died` retain the duplicate-death guard.
|
|
365
|
+
- Composed systems retain each world's schedules and listeners when a definition hosts multiple contexts. Disposal drains all system and classic cleanup once despite errors or reentrancy, then rethrows the first error. Failed installation retires entered systems and acquired listeners while preserving the startup error; schedule/listener reinitialization starts a fresh lifecycle.
|
|
366
|
+
- Validate untrusted custom asset specs and preserve independent game-owned metadata; detect GLB/glTF, KTX2, MP3, WOFF2 and HDR formats from bytes with malformed/truncated header checks.
|
|
367
|
+
- Preserve hidden riders during entity hydration and provide a validated, copied velocity setter so dismounts clear stale movement without rewriting controller algorithms.
|
|
368
|
+
- Chase cameras can set `headingSource: "input"` to keep held strafe and backpedal movement from circling as the body turns. Vehicle body follow remains the default; runtime chase tuning can switch the source, and seat views retain body heading.
|
|
369
|
+
- Vehicle obstacle recovery no longer launches kinematic or force-model cars or reports recovery displacement as crash speed. Clamp results may include permitted `motion` displacement separately from their recovered endpoint; plain tuple callbacks keep their behavior.
|
|
370
|
+
- Atomic vitals bars retain palette fills under `hudThemeVars`, clear inherited skins in nested themes, and honor explicit `fill` colors over bar skins.
|
|
371
|
+
- Skinned model height, grounding, and fitted collision triangles now use the imported skin-applied bind pose. Creature rigs with nonidentity inverse bind transforms no longer render several times larger than their configured height.
|
|
372
|
+
- Retire microphone grants arriving after voice-hook unmount, channel/transport replacement, or a newer permission request. Stop replaced capture tracks, preserve accepted capture on transport changes or failed retries, reject ended audio grants and callbacks retained from retired channels, and handle publication failures without unhandled rejections or stale microphone updates.
|
|
373
|
+
- World bars and nameplates omit the local player's possessed body, including retained health anchors. Shared overlays mounted through a game's `WorldOverlay` now inherit the same renderer visibility context as the built-in overlays while keeping caller markup and explicit through-wall reveal policy.
|
|
374
|
+
|
|
375
|
+
## 0.19.0
|
|
376
|
+
|
|
377
|
+
### Migrate
|
|
378
|
+
|
|
379
|
+
- **Bump lockstep SDK packages to `^0.19.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.16.0`; `@jgengine/github` is `0.6.0`.
|
|
380
|
+
- `environment({ structures: building(...) })` buildings and `city` volume buildings now block the player, walking NPCs, `syncWorldColliders` physics backends and `populateNavGridFromSolids` through the new `ctx.world.solids`. Before, they were render-only. If a game re-authored generated buildings as placed scene objects to make them solid, drop those objects. To keep a set passable, pass `building({ solid: false })` or turn off `solid buildings` on the city volume. A mock `GameContext` needs `world.solids` (`createWorldSolids()` from `@jgengine/core/world/worldSolids`).
|
|
381
|
+
- `createAircraftDynamics` and `AircraftTuning` (`physics/flightDynamics`) are deprecated. They command rotation from rates with pitch clamped, so aircraft can't loop, and mass and stall are fixed. Move to `createRigidAircraft` (`@jgengine/core/physics/aircraftDynamics`):
|
|
382
|
+
- `mass` → `massKg`, plus an `inertia` tensor (roughly `m·(span/4)²` for roll and `m·(length/4)²` for pitch and yaw).
|
|
383
|
+
- `fixedWing`: `lift`/`stallSpeed` → left and right wing `surfaces` (area, `liftSlope`, `stallAngle`, trim `incidence`), a tailplane and a fin; `controls.pitch/roll/yaw` → `control` authority on those surfaces plus `controls.<channel>.rate`; `maxThrust` → `engine.maxThrust`; `drag`/`maxSpeed` → `dragArea` and surface `cd0` (top speed is where thrust meets drag); `sideDrag` → the fin.
|
|
384
|
+
- `rotorcraft`: `hoverThrust` → `rotor.maxThrust`, `rotorResponse` → `rotor.spoolRate`, `groundEffectHeight` → `rotor.radius`; add `rotor.torque` and a `rotor.tail`, and pass `input.collective`.
|
|
385
|
+
- `vtol` has no one-block equivalent: combine `rotor`, `surfaces` and `engine`, or keep the deprecated model for now.
|
|
386
|
+
- `controls.stability` → `assists.autoLevel` or `assists.sas`; `afterburner` → `modifiers.thrustScale`; `airbrake` → `modifiers.dragScale`; `groundClearance` → `gear.height`.
|
|
387
|
+
- Euler `rotation` → step `heading`/`pitch`/`bank` and `orientation`; spawn with `aircraftAttitudeQuaternion(heading, pitch, bank)`.
|
|
388
|
+
- Tune against `measureFlight` with the `jgengine-world` recipe `flight-feel.md`.
|
|
389
|
+
- `KinematicVehicleTuning.chassis` and `.steering` are deprecated. Their heading is still commanded from steer, so a held steer at speed spins the car. For handling that matters, move to `createVehicleDynamics` (`@jgengine/core/physics/vehicleDynamics`). Map `massKg`/`engineForce`/`brakeForce`/`tireGrip`/`comHeight`/`trackWidth` to `massKg`, `powertrain: { kind: "direct", maxForce, maxPower }` or a gearbox, `brakeForce`, `front.peakGrip`, `comHeight` and `trackWidth`. Map `steering.wheelbase`/`maxAngle`/`highSpeedAngle`/`highSpeedAt` to the same `steering` fields plus a rack `rate`. Then tune against `measureHandling` with the `jgengine-world` recipe `vehicle-feel.md`. Plain `kinematicVehicle`, without those blocks, stays for karts, top-down and bumper cars.
|
|
390
|
+
- The default third-person orbit camera sits lower and closer (`initialDistance` 9 → 7, `initialHeight` 5.5 → 2.4), so the horizon sits near the top third and the sky is in frame. To keep the old framing, set `camera: { initialDistance: 9, initialHeight: 5.5 }` in `defineGame`.
|
|
391
|
+
|
|
392
|
+
### Added
|
|
393
|
+
|
|
394
|
+
- Split-screen viewports (#1686). `defineGame({ viewports })` draws one scissored viewport per joined local seat (`splitViewports`, `resolveViewports`, `viewportPixels` in `@jgengine/core/game/viewports`); extra seats follow their entity with a chase or top-down camera and walk relative to it. `ViewportHuds` (`@jgengine/shell/camera/Viewports`) renders a HUD root per seat. `bun run drive` gains `--param key=value`; `?gamepad=N` synthetic pads now hold Start. Dev demo: `couch`.
|
|
395
|
+
- `createPerception` takes `maxStimuli` (default 256; the oldest are dropped) and adds `forget(observerId, targetId?)`. Stimuli insert in time order instead of re-sorting on every push.
|
|
396
|
+
- Decision graphs (`@jgengine/core/ai/decisionGraph`) gain `invert`, `cooldown`, `wait` and `random` nodes and `memory` selectors/sequences that resume at their running child. `createDecisionGraphRuntime(graph, actions, { onAbort, rng })` calls `onAbort` when a branch pre-empts a running action, and `random` draws from the injected `rng` only. `running()` names the running action; snapshots now carry timers, memory and cooldowns (older `{ runningPath }` saves still restore).
|
|
397
|
+
- Decision graph behaviors keep a persistent per-entity blackboard: seed it with `blackboard`, write facts from perception or game systems through `behaviorControl(ctx).blackboard(id)`, and it round-trips through `serialize`/`restore`. `thinkInterval` ticks the graph every N seconds, staggered across siblings, with the elapsed time as `dt`. `registerBehaviorActions(id, actions, { onAbort })`; `random` nodes draw from `ctx.rng`.
|
|
398
|
+
- Editor Animation workspace (#1683). The rail button opens the bottom dock's new **Graph** mode for the selected placement, showing layers, states and transitions.
|
|
399
|
+
- A scrub slider replays the graph runtime; parameter sliders and trigger buttons record presses on the timeline.
|
|
400
|
+
- The rig at the camera focus is posed from the runtime's output.
|
|
401
|
+
- Crossfade edits and **Store graph** save the graph to `marker.meta.animation.graph` as undoable edits.
|
|
402
|
+
- Supporting APIs: `parseAnimGraph` (`@jgengine/core/anim/animGraph`) validates stored graphs, `animGraphFromConfig` (`@jgengine/core/anim/locomotionGraph`) resolves the graph a config plays, and `createGraphPose` (`@jgengine/shell/render/useModelAnimation`) poses a rig from graph output.
|
|
403
|
+
- `npx jgengine recipe` adds `world-drops`, `click-target`, `ability-bar`, `hitscan-weapon`, `rolled-gear` and `enter-vehicle`, each type-checked against the SDK.
|
|
404
|
+
- `SkinnedInstances` (`@jgengine/shell/render/SkinnedInstances`) renders hundreds of animated copies of one rig, each with its own clip, time offset and speed, in one instanced draw per material. Clips are baked once to a bone-matrix texture and sampled on the GPU. Shadows follow the pose. The layout and frame lookup are pure: `planBoneTexture` and `sampleBoneTexture` (`@jgengine/core/anim/boneTexture`). Dev runner demo: `bun run drive skinned-crowd`, with `skinned-crowd-mixers` as the one-mixer-per-character baseline (#1683).
|
|
405
|
+
- Continuous haptics (#1769). `ctx.input.haptics(userId)` returns named rumble channels (`createHapticChannels`, `@jgengine/core/input/haptics`): `set` a level per tick, `pulse` a fading hit, mixed by priority so an impact ducks the engine hum. The shell drives each local seat's pad with refreshed short effects and resets it when silent; channels are local only. The `handling` demo feeds engine, road and impact channels.
|
|
406
|
+
- Local seats for couch co-op (#1686). `defineGame({ localPlayers: { maxSlots, claimPrimary? } })` and `localPlayers(ctx)` (`@jgengine/core/runtime/localPlayers`): pads hot-join their own seat on a button press, the shell spawns each through `loop.onNewPlayer(ctx, { userId, isNew: true })`, routes that pad to the seat's own `InputSnapshot` (`local(slotId).input`), and moves its entity with the walk controller. The seat table has `snapshot`/`restore`/`retune`. `?gamepad=N` injects N synthetic pads for headless checks.
|
|
407
|
+
- Action contexts rebind live input (#1769). `ActionContextStack` gains `subscribe`, `version`, `ids` and `activeAxes()`; a context may carry `axes` and serializable `shaping`. The shell re-binds keyboard, touch and gamepad when a context is pushed or popped, without remounting: keys held across the swap keep counting toward their new action, and context-only actions reach `ctx.input`. `ActionStateTracker` gains `rebind` and `actions`. The `handling` demo drives through a `driving` context (F parks it), with pad codes on every action. Recipe: `jgengine-world/recipes/controls.md`.
|
|
408
|
+
- `createNavMeshQuery` (`@jgengine/core/nav/navMesh`) caches a nav mesh's adjacency and a uniform polygon grid, then answers `findPath`, `findPolygon`, `closestPoint` and `raycast` without full-mesh scans. Paths use a binary-heap A* priced by `areaCosts` per `NavMeshData.areas` id (non-finite blocks an area), stop at `maxNodes` with a `partial` route, and are straightened by a portal funnel. `retune`/`snapshot`/`restore` cover the costs, so a door or hazard can close an area during play. `NavMeshLink` gains optional `start`/`end` landing points.
|
|
409
|
+
- `createWeaponFeedbackSignals` (`@jgengine/core/combat/weaponFeedback`) turns `WeaponHandling.fire()` shots, their recoil kick and impacts into one-tick pulses (`shot`, `kick`, `impact`) plus the frame's `recoil`, `cameraKick`, `spread` and `ads`, keyed for `createFeedbackMixer` routes to camera shake, audio and rumble. It has `snapshot`/`restore`/`reset`.
|
|
410
|
+
- `measureWeapon` now reports `spreadByShot` and `shotsToKill`, fires in a given `stance` (e.g. ADS), and computes time-to-kill at a `range`. Each projectile (`pellets` per shot) counts by the share of its spread cone the `targetRadius` covers, scaled by the game's `damageAt(range)` falloff. The recipe is `jgengine-combat` `weapon-feel.md`.
|
|
411
|
+
- `defineGame({ gamepad: { deadzone, curve, triggerDeadzone } })` sets pad feel for the shell's gamepad poll (#1769). Triggers now go through `triggerDeadzone` and the same curve as the sticks, and a trigger resting inside its deadzone no longer holds its action. `resolveGamepadFrame` takes a browser `Gamepad` directly and an optional reusable `out` frame, and the shell poll no longer allocates per frame. `gamepadFeelOptions` resolves the config with the old defaults (axial `0.12`/`0.95`, linear).
|
|
412
|
+
- `measureFlight` (`@jgengine/core/physics/handlingProbe`) flies a `createRigidAircraft` through deterministic autopilot scenarios. It reports roll rate (deg/s), sustained turn rate, stall speed, climb rate, time to 90% throttle response and hands-off hover drift, so a flight feel target can be a test.
|
|
413
|
+
- `RigidAircraftTuning.assists`: flight assists on the same actuators as the pilot, so full stick stays the pilot's.
|
|
414
|
+
- `sas` per axis (`0..1`) damps rates and holds attitude or heading hands-off with a learned trim. On a helicopter it holds heading against rotor torque.
|
|
415
|
+
- `autoLevel`, `maxAngleOfAttack` and `maxG` limiters, and `hoverHold` for helicopters.
|
|
416
|
+
- A `policy(context, command)` callback has the last word.
|
|
417
|
+
- The step adds `command` and `limited`. `RigidAircraftState` gains the hold, trim and drift integrals.
|
|
418
|
+
- T toggles assists in `bun run drive flight` and `flight-heli`.
|
|
419
|
+
- `RigidAircraftTuning.motor` makes `createRigidAircraft` a rocket.
|
|
420
|
+
- Thrust follows `thrustCurve` over burn time. `propellantKg` leaves at `massFlow`, so mass and inertia fall and acceleration rises through the burn. The nozzle `gimbal` steers on the pitch and yaw inputs.
|
|
421
|
+
- To stage, `retune` to the next stage: a new `motor` loads its propellant.
|
|
422
|
+
- The step reports `massKg` and `motor` telemetry. `RigidAircraftState` gains `burnTime` and `propellantKg`.
|
|
423
|
+
- Dev runner demo: `bun run drive flight-rocket`.
|
|
424
|
+
- Jump shape on `movement.feel`: `jumpCutFactor` (releasing jump early cuts the rise, so a tap hops), `apexGravityScale`/`apexSpeed` (hang at the peak), `fallGravityScale` (drop faster than you rose) and `landingRecoveryMs`/`landingSpeedScale` (a short slowdown and jump lockout after landing). All default to no change. `measureMovement` adds `tapJumpHeight`. Dev runner demos `walk-floaty` and `walk-weighty` differ only in these fields (#1772).
|
|
425
|
+
- `RigidAircraftTuning.rotor` turns `createRigidAircraft` into a helicopter: `input.collective` sets thrust, throttle spools the rotor, cyclic tilts the disc, and rotor drag torque yaws the body unless the `tail` rotor (pedal) cancels it. Thrust adds translational lift, drops while climbing through the disc, and grows in ground effect. The step reports `rotor` speed, thrust, torque, ground effect and translational lift. Dev runner demo: `bun run drive flight-heli`.
|
|
426
|
+
- `snapshot()`/`restore(next)` on `createGlideModel`, `createLodScheduler`, `createSimulationCuller` and `createSpatialIndex`, and `restore(state)` on `createGrappleSwing`, so gliding, rope swings, LOD throttling and the spatial hash save, load and replay bit-exactly (#1775).
|
|
427
|
+
- `snapshot()`/`restore(next)` on `createThreatTable`, `createAssistNetwork`, `createReputationLedger`, `createConcealmentSensor`, `createFreezeMonitor` and `createRecordingBuffer`, so AI aggro, faction standing and sensor dwell save, load and replay bit-exactly (#1775).
|
|
428
|
+
- Engine audio depth (#1770):
|
|
429
|
+
- `ctx.game.audio.setLoop` takes `lowpass` and `highpass` cutoffs in Hz, ramped like rate and gain.
|
|
430
|
+
- `SoundDef.doppler` pitches a retained loop by listener and emitter velocity; pass the emitter's with `setLoop({ velocity })`. The shell tracks the listener's from the camera. `dopplerRate` (`@jgengine/core/audio/doppler`) is the pure formula.
|
|
431
|
+
- `createEngineLayers` (`@jgengine/core/audio/engineLayers`): N rpm-keyed engine samples crossfaded at equal power, with on-load and off-load sets picked by smoothed `engineLoad`. `play(ctx.game.audio, ...)` drives one retained loop per layer. It has `snapshot`/`restore`/`retune`.
|
|
432
|
+
- `snapshot()`/`restore(next)` on `createFootprintGrid`, `createWallDrawTool` and `createTerraformBrush`, so build-grid claims, wall drafts and brush settings save, load and replay bit-exactly (#1775).
|
|
433
|
+
- `createInputBuffer` (`@jgengine/core/input/inputBuffer`): buffered presses, coyote time, hold duration and double tap, with `snapshot`/`restore`. The walk controller uses it for `movement.feel.jumpBufferMs` (a jump pressed just before landing fires on landing) and `movement.feel.coyoteMs` (a jump just after walking off a ledge still fires). Both default to 0, so walking is unchanged until a game sets them (#1686, #1772).
|
|
434
|
+
- `snapshot()`/`restore(next)` on `createLootRegistry`, `createToastQueue`, `createVfxInstanceStore`, `createRoundState` and `createLookChannel`, and `restore(state)` on `createDragCapture`, so loot tables, toasts, retained VFX, match rounds and look/drag input save, load and replay bit-exactly. `RoundSnapshot` gains `pendingWinner` (#1775).
|
|
435
|
+
- `measureMovement` (`@jgengine/core/movement/movementProbe`) reports a walking character's feel from the same `movement.feel` and `physics` fields a game sets: time to top speed, stop distance, turn-around time, jump height, apex and air time, and air-control reach. Deterministic, for tests (#1772).
|
|
436
|
+
- Chase camera depth (`camera.chase`, #1770):
|
|
437
|
+
- `fov.response` eases the speed FOV, and speed now comes from the followed entity's sim `velocity` when it publishes one, so FOV no longer jitters when render and sim rates differ.
|
|
438
|
+
- `distanceBySpeed`, `pitchFollow` (boom follows body pitch on slopes), `lookBackAction` (hold to look behind), and `collision` (the boom stops short of objects, terrain and walls via `ctx.scene.raycast`; on by default).
|
|
439
|
+
- `ctx.camera.kickFov(degrees)` queues a decaying FOV punch, tuned by `chase.fovKick`.
|
|
440
|
+
- `ChaseCameraTuning` accepts `view` and the new fields; `nextChaseView` (`@jgengine/core/runtime/cameraDirector`) cycles chase, hood and cockpit.
|
|
441
|
+
- `createRigidAircraft` (`@jgengine/core/physics/aircraftDynamics`): a quaternion rigid-body aircraft with an inertia tensor, lifting surfaces (area, lift slope, stall, post-stall lift, drag polar) placed on the body, rate-limited control surfaces, an engine with spool and optional gear. Loops, rolls and stalls come from the surfaces. Same contract as `vehicleDynamics`: fixed substeps, `snapshot`/`restore`/`retune`, per-tick modifiers, and telemetry for AoA, sideslip, g-load, airspeed and stall. Dev runner demo: `bun run drive flight`.
|
|
442
|
+
- Graphics settings a player can feel: an Ultra tier, a frame-rate limit (V-Sync or 30/60/120/144 cap), texture filtering (anisotropy, now applied to every loaded texture), and an FPS counter. `GraphicsProfile` gains `anisotropy`; a game that overrides tiers with `defineGame({ graphics })` can set it per tier.
|
|
443
|
+
- `postProcessing.stylize` (`outline`, `bands`, `pixelSize`) and two new looks, `look: "comic"` (ink outlines + cel bands, Borderlands-style) and `look: "retro"` (pixelated, posterized). Same models, different art style.
|
|
444
|
+
- `PhysicsBackend.applyForce(handle, force, point?)` and `applyTorque(handle, torque)` act over the next step, and `BodyDesc.linearDamping`/`angularDamping` set damping per body. Both are implemented on `PhysicsWorld` (translation only) and Rapier, and covered by the conformance suite. `createVehicleBackendLink` (`@jgengine/core/physics/vehicleBackendLink`) collides a vehicle sim with a backend world: a kinematic chassis shoves props, `clampMove` stops the car at walls and slides it along them, and `lastHit()` reports the impact.
|
|
445
|
+
- `VehicleAssistTuning.antiWheelie` and `.antiStoppie` (`0..1`) cap drive and braking below the pitch-over limit (`g·b/h` forward, `g·a/h` braking). With `suspension` they also back off as the front or rear goes light. They report through `tractionLimited` and `absActive`.
|
|
446
|
+
- Motorcycles on `createVehicleDynamics`: a `VehicleDynamicsTuning.lean` block (`maxLean`, `leanRate`, `countersteer`, `directSteerBelow`).
|
|
447
|
+
- Steer asks for a lean, capped at what grip can hold. The bar steers the balanced turn that lean needs, with a rider trim for tire slip and a countersteer tip-in.
|
|
448
|
+
- The bar steers directly at walking pace, and there is no lateral load transfer.
|
|
449
|
+
- The step adds `lean` (balance lean, `bodyRoll = -lean`) and `wheelie`/`stoppie` for all vehicles.
|
|
450
|
+
- `measureLean` (`physics/handlingProbe`) reports steady lean, time to lean and counter-lean.
|
|
451
|
+
- Dev runner demo: `bun run drive handling-bike`.
|
|
452
|
+
- `measureCourse` (`@jgengine/core/physics/handlingProbe`): understeer gradient (deg/g), skidpad g at a set radius, and the fastest clean slalom, from simple deterministic drivers. `measureHandling` adds `handbrakeRecoverySeconds`.
|
|
453
|
+
- `createWeaponHandling` (`@jgengine/core/combat/weaponHandling`): weapon feel as serializable state.
|
|
454
|
+
- Recoil: a per-shot `pattern` or flat `pitch`, a random cone from an injected `random`, a `cameraShare` split between view punch and aim, `recoverRate`/`recoverDelay`, and `adsScale`.
|
|
455
|
+
- Spread: `base`, `perShot` bloom, `max`, recovery, and stance/ADS multipliers.
|
|
456
|
+
- ADS time, plus `snapshot`/`restore`/`retune`/`reset`.
|
|
457
|
+
- `measureWeapon` reports first and tenth shot spread, burst climb, reset time, ADS time and time-to-kill.
|
|
458
|
+
- `createWeaponPresentation` (`@jgengine/core/combat/weaponPresentation`): per-weapon viewmodel hip and ADS offsets, viewmodel FOV, ADS zoom, sway from look speed, bob from the movement probe, and recoil kick, from a `createWeaponHandling` frame. `camera.weapon` hands that frame to the rigs: first person poses its viewmodel and adds the recoil to the look, and `shoulder` takes its ADS blend and recoil from the same frame. Dev demos: `weapon-handling`, `weapon-handling-shoulder`.
|
|
459
|
+
- `createFeedbackMixer` (`@jgengine/core/vfx/feedbackMixer`): declares how sim telemetry drives presentation. Routes map a signal through a piecewise `curve` with `attack`/`release` smoothing onto a named target, targets combine by `sum` or `max` over a `base`, and threshold `events` with cooldowns fire one-shots. No allocation per update, with `snapshot`/`restore`/`retune`/`reset`. `sampleFeedbackCurve` exposes the curve.
|
|
460
|
+
- `snapshotPlayerMovement(ctx, userId)` / `restorePlayerMovement(ctx, userId, snapshot)` (`@jgengine/core/movement/playerMovement`): per-player walk state (heading, velocities, jump latch, capsule controller) as a plain JSON copy, so walking characters predict and roll back like vehicles. A snapshot restored before the capsule exists is applied when it is created.
|
|
461
|
+
- `PlayerMovementConfig.feel` (`MovementFeelConfig`): `groundAcceleration`, `airAcceleration`, `groundFriction` (response rates, 1/s), `runMultiplier` and `crouchMultiplier` for the built-in walk controller and the voxel controller. They were hard-coded before, so every game walked the same. Omitted fields keep today's defaults.
|
|
462
|
+
- `createAxisShaper` (`@jgengine/core/input/axisShaper`): per-axis response shaping between `ctx.input.axis(...)` and a sim. Keys and sticks get separate profiles (deadzone, saturation, curve, rise/fall/return rates), plus an optional `scale(signal)` callback for speed-sensitive steering, with `snapshot`/`restore`/`retune`. `analogAxes(bindings, ctx.input.analog())` picks the profile per frame, and `shapeAxisValue` exposes the deadzone and curve math. The `handling` dev demo uses it for keyboard steering that eases in and self-centres.
|
|
463
|
+
- `VehicleDynamicsTuning.jump` (`speed`, `count`, `window`) with `car.jump()`, and `VehicleDynamicsTuning.air` (`pitchAccel`/`yawAccel`/`rollAccel`, `damping`, `maxRate`) with `modifiers.air` for air control, plus `applyAngularImpulse` for dodges and flips. The body angles use the true geometry of the tilt rather than a small-angle guess. `measureAir` (`physics/handlingProbe`) reports apex height and time, double-jump height and air rotation rates. Held brakes and the handbrake now grip statically at rest, so a parked car stays put on a slope.
|
|
464
|
+
- `VehicleDynamicsTuning.suspension` (`springRate`, `damperRate`, `reboundRate`, `travel`, `rideHeight`, `antiRoll`) and a `groundHeight` option on `createVehicleDynamics`. The car rides four sprung corners over terrain: heave, pitch and roll come from real springs, axle loads come from spring forces, slopes pull downhill, and the car can leave the ground and land. The step adds `airborne`, `landingSpeed`, `wheelLoads` and a terrain-relative `airOffset`, and `applyImpulse` takes a vertical `dvy`. `measureRide` (`physics/handlingProbe`) reports settle time, heave overshoot, brake dive, roll gradient and landing bounces. Without the block, behaviour is unchanged.
|
|
465
|
+
- `createKinematicVehicle` and `createAircraftDynamics` gain `snapshot()`/`restore()` (`KinematicVehicleState`, `AircraftDynamicsState`) for prediction, rollback and replay. `createAircraftDynamics` also gains `retune()`. The stateful-primitive ratchet no longer misses factories with a default parameter like `options = {}`, so 20 more handles without state-out are now tracked in its baseline.
|
|
466
|
+
- Boat sim `createBoatDynamics` (`@jgengine/core/physics/boatDynamics`) on the same tick/snapshot/restore/retune contract as the car. Drag climbs toward hull speed and a planing hull sheds it; the keel resists sliding sideways; a `rudder` needs water flowing past it (speed or prop wash) while an `outboard` swings its own thrust and turns at rest. `waterHeight(x, z, t)` for waves and `current(x, z)` for drift. `measureHandling` and `tickDrivableVehicle` take it as-is.
|
|
467
|
+
- Force-based car sim `createVehicleDynamics` (`@jgengine/core/physics/vehicleDynamics`): a two-axle model where yaw comes from tire forces, so understeer, oversteer, lift-off tuck, power slides, trail-braking and handbrake turns come out of physical knobs (mass, weight split, CoM height, per-axle Pacejka-style tires, roll-stiffness split, drive split, gearbox or direct drive, aero, ABS/TC/stability, caster self-align). Fixed substeps, `snapshot`/`restore`, `retune`, `thrust` modifier. `measureHandling` (`@jgengine/core/physics/handlingProbe`) drives any vehicle sim through fixed scenarios and returns 0–100, top speed, braking distance, lateral g, turn-in latency, sideslip, spin and lift-off numbers a test can assert. `tickDrivableVehicle` now accepts either sim. `drive --key` takes chords (`KeyW+KeyD:2000`). Dev runner demo: `bun run drive handling`.
|
|
468
|
+
- `jgengine create --2d` scaffolds a board game with no 3D scene, editor document, or models: a `board` world, `presentation: "hud"`, a store slot holding a `CellGrid`, arrow/WASD/Space bound to commands with held-key repeat, and a GameUI that draws the grid. `--3d` names the default. 3D-only flags (`--player`, `--ground`, `--scene`, `--from-scene`, `--shape`, `--no-world`, `--no-editor`) are refused with `--2d` instead of ignored.
|
|
469
|
+
- `@jgengine/core/puzzle/cellGrid` is documented public API (`cell-grid` capability): an immutable 2D board with `withCell`/`withCells`, `fullRows`/`clearRows`, `collapseColumns`, and `findRuns`. It was importable but tagged internal, so agents never found it.
|
|
470
|
+
- HUD-only games (`presentation: "hud"`) set `data-jg-capture="ready"` after their first painted frame unless a capture host owns the flag, so `shoot` and `drive` no longer time out waiting for a `<canvas>` that never exists.
|
|
471
|
+
- The built-in Graphics settings tab now exposes render scale plus ambient occlusion, bloom, depth-of-field, and SMAA toggles alongside the quality tier; picking a tier re-applies its defaults. `@jgengine/core/settings/graphicsSettings` (`readGraphicsSettings`, `applyGraphicsQuality`, `GRAPHICS_POST_STAGES`) resolves the stored choices onto the `GraphicsProfile` the shell renders with, and `SETTING_IDS` gains `graphics.renderScale` and `graphics.post.*`. Every game gets the rows in its existing settings menu with no wiring (#1688).
|
|
472
|
+
|
|
473
|
+
- Model triangle budget: the shared GLB loader counts each model's triangles as it lands and warns once per URL when one exceeds `DEFAULT_MODEL_TRIANGLE_BUDGET` (100k; retune with `setModelTriangleBudget`). `heavyModels()` lists the offenders and `debug_snapshot` reports them under `probes.heavyModels`, so a heavy prop shows up in evidence instead of only as a slow frame.
|
|
474
|
+
- `bun run drive <game> --state <name>` boots into a `capture.states` entry the same way `shoot --state` does, so a staged scene can be measured with `--rpc debug_snapshot` or probed instead of clicked together by hand.
|
|
475
|
+
- Building palettes take textured surfaces: a `BuildingPalette` part (and `BuildingKitPart.material`) may be `{ color?, maps?, repeat?, roughness?, metalness? }` with `maps` straight from `buildMaterialCatalog(...).resolve(id)!.maps`, not only a hex colour. Generated facade boxes and kit models tile the PBR maps per slot; unbound kinds keep their flat colour. `buildingSurfaceColor` / `resolveBuildingSurface` read either form.
|
|
476
|
+
- `CommandDef.parse` and `defineCommand` (`@jgengine/core/runtime/commandRunner`). `parse` turns the untrusted wire value into typed input once. The runner refuses a `null` result with `MALFORMED_COMMAND_INPUT_REASON` before `scope`, `validate` or `apply` run, so those stages only see parsed input.
|
|
477
|
+
- Bounded input readers in `@jgengine/core/runtime/commandInput`: `readInputNumber` (always finite, optional range and integer), `readInputString`, `readInputOneOf`, `readInputPoint2`, `readInputPoint3`, `readInputArray` and `isInputRecord`.
|
|
478
|
+
- `helpers.ensureJoined(ctx, { gameId, userId, serverId? })` (`@jgengine/convex/server`) joins a trusted actor through the `joinServer` path and returns its outcome, so host mutations can run commands for users with no live session. It is idempotent and pins no client session.
|
|
479
|
+
- `createTransportSessionId()` (`@jgengine/core/runtime/transport`) makes a fresh id for one client session's join/leave pair.
|
|
480
|
+
- `CommandDef.access` (`@jgengine/core/runtime/commandRunner`). `"server"` marks a command whose input carries host-decided facts. The public Convex `runCommand` mutation and WebSocket client messages refuse it with `SERVER_ONLY_COMMAND_REASON`, while `helpers.runCommand` and `host.runCommand({ trusted: true })` still run it. `GameRuntime.commandAccess(name)` reports it.
|
|
481
|
+
|
|
482
|
+
### Changed
|
|
483
|
+
|
|
484
|
+
- Graph playback no longer resets the rig's first bone to its bind position every frame. On rigs whose first bone is the hips, that flattened the walk bob in every clip. The root bone is pinned only while a `rootMotion` state is current (the new `AnimGraphOutput.rootMotion` flag), and only horizontally. Root travel now moves the entity along its facing and at the model's scale instead of in rig space, and its vertical part stays in the clip. The shell helper is `takeRootMotion` (#1683).
|
|
485
|
+
- Perception judges each sound or damage stimulus once per observer, from where the observer stood when it first observed after the stimulus. Before, every `observe` re-scored past sounds from the observer's current position, so a guard walking toward an old noise grew more certain of it. `PerceptionSnapshot` gains `heard` and `nextSeq`; older saves re-judge their stimuli once.
|
|
486
|
+
- Perception `loudness` now scales how far a sound carries (`hearingRange × loudness`) instead of only its confidence, so a gunshot is heard past normal hearing range and a footstep only close by. Loudness `1` behaves as before.
|
|
487
|
+
- `npx jgengine find` now surfaces seams that had no capability row: `vehicle-seats`, `drivable-vehicle`, `kinematic-vehicle`, `affix-roller`, `hitscan-shot`, `loot-beams`, `world-item-pickup`, `on-death-drops`, `pointer-commands`, `current-target`, `fire-input`, `global-cooldown`. `PointerConfig.moveCommand` JSDoc now says it receives the clicked entity (click-to-target).
|
|
488
|
+
- `npx jgengine find` matches whole stemmed words (`aim` no longer hits `claim`), drops stopwords, tries multi-word queries joined (`pick up` → `pickup`), returns the closest partial matches instead of "no match", and searches CLI recipes and skill recipe docs.
|
|
489
|
+
- `raycastNav` and `NavMeshQuery.raycast` step into the polygon a segment enters when it starts on or crosses a shared vertex, so a funnel path's corner-to-corner legs raycast clear.
|
|
490
|
+
- `bakeNavMesh` (`@jgengine/navbake`) now voxelizes geometry with recast: the walkable surface is eroded by `agentRadius`, cut under obstacles lower than `agentHeight`, and split at `maxSlope` and `maxClimb`, so a wall standing on a floor leaves a hole the path goes around. Call `await initNavBake()` once before baking; `navBakeReady()` reports it. Walkable triangles must wind counter-clockwise seen from above. Optional `cellSize`/`cellHeight` trade precision for speed. The editor warms the baker when a host starts.
|
|
491
|
+
- `findPath`, `closestPoint` and `raycastNav` on `NavMeshData` share the cached query: paths bend only at portal corners, stacked floors resolve by height, and raycasts walk polygon edges instead of sampling.
|
|
492
|
+
- Mouse look now asks for raw, unaccelerated mouse deltas (`requestPointerLock({ unadjustedMovement: true })`) in `createMouseLookTracker`, the first-person camera and the shoulder rig, and falls back to a plain lock where that is unsupported. The helper is `requestRawPointerLock` (`@jgengine/shell/input/pointerLock`).
|
|
493
|
+
- Foot IK (`ModelConfig.ik`) keeps each clip's swing lift and moves feet by their ground's height relative to the model origin, instead of pinning every ankle to the ground. It lowers the pelvis so the lower leg can reach, keeps soles above the ground where a clip dips below it, bends toward the animated knee, tilts planted feet to the ground normal and fades out while airborne. `ik: "auto"` finds the legs by bone name, and `pelvis`, `maxAdjust` and `alignToGround` are optional. Probes hit terrain and blocking objects. The math is `placeFeet` and `inferLegChains` (`@jgengine/core/anim/footPlacement`) (#1683, #1758).
|
|
494
|
+
- Sky and fog colors render as authored under any tone mapping. The sky dome and sky fog treat their colors as on-screen swatches and pre-invert the renderer's tone-mapping curve, so a `#cfe4f5` horizon no longer shows as grey `#a6b3c2` under the default `neutral` look (AgX). The dome now runs three's tone-mapping and color-space chunks, so it matches with or without a post chain. Looks with bloom (`cinematic`, `photoreal`) get a brighter sky that blooms more near the sun; raise `bloom.threshold` if that is too much.
|
|
495
|
+
- The sky's sun shadows lose their stair-step: one 2048 map over a 70 m box that leads the camera (was 180 m centered on it), texel-snapped, with a 6-texel PCF radius. Time-of-day, biome and `DayNightSky` suns use the same rig instead of three's default 10 m box.
|
|
496
|
+
- `jgengine create` seeds the sun lower and ahead-left of the spawn (bearing 128°, 20° up), so its glow is in the first frame.
|
|
497
|
+
- Scaffolded `scripts/shoot.mjs` and `scripts/drive.mjs` refuse a viewport that stays one flat color for 10 s instead of saving it; nothing is written and the command fails.
|
|
498
|
+
- `jgengine create` defaults to a 3D terrain world instead of an infinite flat slab. `editor.scene.json` seeds rolling hills as a terrain sculpt (deterministic per game id, flat around the spawn) and a bright `day` sky with a sun bearing and distance fog; `src/world.ts` renders them through `environment()` and `environmentContentFromDocument` and holds only the detail-shaded ground palette. Reshape the hills in the editor. `--ground flat` keeps the old `place()` slab (#1762).
|
|
499
|
+
- `GameRuntimeTransport.joinServer`/`leaveServer` take an optional `sessionId`. The Convex `joinServer`/`leaveServer` mutations and the WebSocket `join`/`leave` messages accept it (at most 128 characters). `useServerSession` and the shell's multiplayer sync send a fresh one per mount.
|
|
500
|
+
- Shared-topology `jgServerMembers` rows keep up to 16 live `sessionIds`. A leave with a `sessionId` ends only that session; the user leaves once none remain. A leave without one, or on a row without session ids, still leaves outright. Rooms topology is unchanged.
|
|
501
|
+
- The WebSocket router tracks sessions per connection and only leaves the host when no other connection of the same user holds the server.
|
|
502
|
+
- `helpers.resetPlayerProfile` (`@jgengine/convex/server`) now resets a player who has left the server but still has a profile, instead of throwing, so scheduled wipes and insolvency resets no longer abort a whole batch. A user with neither membership nor a profile still throws.
|
|
503
|
+
|
|
504
|
+
### Fixed
|
|
505
|
+
|
|
506
|
+
- The `combat-loop` recipe drained health with `stats.delta`, which never reaches the death pipeline, so copied enemies never died. It now damages through `ctx.scene.entity.effect` and a catalog `receive` map. The `loot` recipe now registers on `ctx.game.loot` and rolls from catalog `onDeath`. `KinematicVehicleTuning.camera` and `DRIVE_AXIS_BINDINGS` JSDoc now state what they actually do.
|
|
507
|
+
- Editor RPC `add_marker` and `set_marker` accept `catalogId`. It used to be dropped while the call still returned `ok`, so a headlessly authored `mob`/`boss` marker spawned nothing.
|
|
508
|
+
- Agent bridge `editor_summon` now resolves once the editor has mounted (up to 15 s). It used to return immediately, so editor verbs later in the same `drive` batch failed with "no live editor".
|
|
509
|
+
- Catalog `onDeath: { dropMode: "world" }` scattered drops with `Math.random`, so kill loot landed in different spots on replay and on each peer. The runtime now passes the world's seeded `ctx.rng` to the scatter.
|
|
510
|
+
- The chase camera no longer falls behind fast vehicles. Its spring eased the camera's world position toward a moving target, so the lag grew with speed (about 9 m extra at 220 km/h); it now eases the boom offset from the target (#1770).
|
|
511
|
+
- A `createVehicleDynamics` bike with both `lean` and `suspension` launched itself the moment it leaned: the springs read the lean as body roll across the narrow track. Springs now ignore lean, and lateral g keeps the same lag it has without springs.
|
|
512
|
+
- `@jgengine/rapier`: a body created with `mass` now weighs exactly that. It used to add `mass` on top of the collider's density-derived mass, so a 2 kg sphere of radius 0.5 weighed 2.52 kg, and every force, impulse and joint on a massed body was off by the collider's volume.
|
|
513
|
+
- `createVehicleDynamics` with `suspension` spawned and reset level even on a slope. The wheels started buried, so the springs fired the car into the air and it tumbled. It now fits pitch, roll and height to the terrain under its four wheels.
|
|
514
|
+
- A sky authored in the scene document (`editor.scene.json` `environment`, written by the editor lighting workspace) now renders in `place()` worlds. `defineGame` never passed `skyFromDocument(editorLayers)` to the renderer, so those edits were silently dropped. A game's own `backdrop.sky` still wins, and a document holding only point lights leaves the world sky alone (#1762).
|
|
515
|
+
- `jgengine create --scene starter` placed its props and goal behind the spawn, so the first frame showed empty ground and W walked away from the goal. The starter scene now sits ahead of the default +Z facing.
|
|
516
|
+
- A standalone game's `bun run desktop` failed with `jgengine: command not found` because the CLI is not a dependency; the script now runs `npx jgengine desktop`.
|
|
517
|
+
- `@jgengine/navbake` joins the published lockstep set. `@jgengine/editor@0.18.1` depended on it while nothing published it, so `bun install` on the released SDK failed. `check-release-set` (in `gate`, `check-types`, and the publish workflow) now fails when a published package depends on a workspace package outside the publish list or out of dependency order, or when the publish, version-bump, and changelog package lists disagree.
|
|
518
|
+
- Publish has been red since that change because a CLI packaging test still hardcoded the old publish list, so navbake never reached npm and `npx jgengine create` scaffolds failed to install. The test now defers the order to `check-release-set`.
|
|
519
|
+
- `jgengine-verify` was silently skipped by the skills installer for every new game: its `description` held an unquoted `: `, which is invalid YAML. `check-skills` now parses frontmatter as YAML and requires `name` to match the folder.
|
|
520
|
+
- `jgengine create --help` printed usage instead of scaffolding a game named after the first argument, and `create` rejects unknown flags rather than ignoring them. The usage lists `--player`, `--ground`, and `--scene`.
|
|
521
|
+
|
|
522
|
+
- `pickModel` / `resolveModelPlan` warn once in dev when a `ModelPick.fallbackModel` is not in the asset catalog (a typo or an undeclared pack), instead of silently shipping a fallback that can never take over.
|
|
523
|
+
|
|
524
|
+
- Volumetric clouds no longer cut through distant buildings: the proxy dome writes per-fragment depth at the raymarch's first cloud hit, so geometry between the camera and the cloud slab occludes the clouds instead of the dome radius deciding. `STUDIO_STAGE_POST` ambient occlusion is softer (radius 1.1, intensity 1.5) so tower silhouettes stop streaking.
|
|
525
|
+
|
|
526
|
+
- Separate SDK publication validation from external Games quality checks while retaining the full local and main-branch gates.
|
|
527
|
+
|
|
528
|
+
- Correct foot-IK endpoint recomputation after hip rotation; verify reachable targets and clamping without changing bone lengths.
|
|
529
|
+
- Restore release CI prerequisites and make catalog-loader tests independent of the external Games checkout.
|
|
530
|
+
|
|
531
|
+
- Closing a second tab, or a join that resolved after a remount, no longer evicts a user whose live tab still shows them joined, which made every later command fail with "Not a member of this server".
|
|
532
|
+
|
|
36
533
|
## 0.18.1
|
|
37
534
|
|
|
38
535
|
### Migrate
|