@jgengine/editor 0.19.0 → 0.22.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 +364 -0
- package/dist/ClipPreviewLayer.d.ts +2 -3
- package/dist/ClipPreviewLayer.js +10 -28
- package/dist/CreatorApp.d.ts +12 -0
- package/dist/CreatorApp.js +107 -0
- package/dist/DebugDraw.js +3 -1
- package/dist/EditorApp.d.ts +14 -2
- package/dist/EditorApp.js +29 -14
- package/dist/EditorChrome.d.ts +9 -1
- package/dist/EditorChrome.js +54 -35
- package/dist/InspectorPanel.d.ts +3 -1
- package/dist/InspectorPanel.js +12 -8
- package/dist/MaterialAssetEditor.d.ts +13 -0
- package/dist/MaterialAssetEditor.js +109 -0
- package/dist/MaterialPreview.d.ts +16 -0
- package/dist/MaterialPreview.js +41 -0
- package/dist/MaterialsWorkspacePanel.d.ts +14 -2
- package/dist/MaterialsWorkspacePanel.js +78 -4
- package/dist/RuntimePlayBridge.js +1 -6
- package/dist/SelectionGizmo.js +23 -14
- package/dist/SimulationInspector.d.ts +9 -0
- package/dist/SimulationInspector.js +47 -0
- package/dist/TerrainSculpt.js +12 -59
- package/dist/agent/toolBridge.js +2 -0
- package/dist/authoringPlacement.d.ts +21 -0
- package/dist/authoringPlacement.js +34 -0
- package/dist/handlers/context.d.ts +1 -0
- package/dist/handlers/document.d.ts +1 -1
- package/dist/handlers/document.js +29 -1
- package/dist/handlers/hierarchy.d.ts +1 -1
- package/dist/handlers/hierarchy.js +4 -0
- package/dist/handlers/runtime.js +20 -17
- package/dist/handlers/terrain.js +52 -12
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/materialControls.d.ts +32 -0
- package/dist/materialControls.js +79 -0
- package/dist/mcp/fieldSpec.d.ts +1 -1
- package/dist/mcp/fieldSpec.js +11 -2
- package/dist/mcp/importOptionalGameModule.d.ts +2 -0
- package/dist/mcp/importOptionalGameModule.js +13 -0
- package/dist/mcp/loadGameCatalogs.js +5 -12
- package/dist/mcp/rpcRequest.d.ts +3 -3
- package/dist/mcp/rpcRequest.js +10 -4
- package/dist/mcp/tools.js +10 -1
- package/dist/modelAnimationAuthoring.d.ts +27 -7
- package/dist/modelAnimationAuthoring.js +69 -72
- package/dist/session.d.ts +62 -0
- package/dist/session.js +56 -7
- package/dist/shell/AnimationGraphPanel.js +18 -10
- package/dist/shell/PlayModeBar.d.ts +2 -1
- package/dist/shell/PlayModeBar.js +2 -2
- package/dist/shell/SceneToolbar.d.ts +2 -1
- package/dist/shell/SceneToolbar.js +5 -4
- package/dist/shell/TopAppBar.d.ts +2 -1
- package/dist/shell/TopAppBar.js +2 -2
- package/dist/shell/animGraphPreview.d.ts +2 -0
- package/dist/shell/animGraphPreview.js +5 -2
- package/dist/shell/clipPreview.d.ts +1 -0
- package/dist/uiStore.d.ts +1 -10
- package/dist/uiStore.js +1 -13
- package/dist/useDocumentSave.d.ts +20 -0
- package/dist/useDocumentSave.js +33 -0
- package/package.json +6 -5
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/jgengine/SKILL.md +23 -2
- package/skills/jgengine/api.md +28 -17
- package/skills/jgengine/recipes/minimal-game.md +7 -2
- package/skills/jgengine-editor/SKILL.md +9 -1
- package/skills/jgengine-editor/api.md +111 -16
- package/skills/jgengine-editor/capabilities.md +40 -0
- package/skills/jgengine-editor/reference.md +149 -1
- package/skills/jgengine-editor/references/live-sync.md +15 -0
|
@@ -8,6 +8,38 @@ in-memory session and, when every RPC succeeds, writes the session document to
|
|
|
8
8
|
the GUI's Ctrl+S through `devSavePlugin`). Without `--save`, `--rpc` mutations are discarded on
|
|
9
9
|
exit — use it for read-only inspection only, or capture `export_document` yourself.
|
|
10
10
|
|
|
11
|
+
The source-only CLI writer validates the full document before replacing any saved bytes, matching the dev save endpoint. Validation failures report document paths and leave the existing file intact.
|
|
12
|
+
|
|
13
|
+
Scene and catalog modules are optional. A present module with a broken dependency, initialization error, or invalid export returns load diagnostics; the CLI and stdio host stop before opening a session or saving. Only absent optional files resolve to empty content.
|
|
14
|
+
|
|
15
|
+
`dispatch` with `setEnvironment` validates only the candidate environment before changing the session, undo history, or live document revision. Supported presets are `day`, `dusk`, and `night`; custom palette, sun, intensity, and fog fields remain explicit authored values. Invalid inputs report `$.environment` paths. Validated nested fields are copied, so mutating the command afterward cannot change the document. Passing `environment: undefined` removes the bag and remains undoable. Repair unsupported authored values through an explicit editor command; preserve the author's palette and fog rather than substituting a default.
|
|
16
|
+
|
|
17
|
+
## Atomic authoring batches
|
|
18
|
+
|
|
19
|
+
Use `document_revision` to inspect the current revision, then send one `push_document_patch` for a complete placement operation. Commands use the `EditorCommand` shapes from `@jgengine/core/editor/commands`, rather than RPC verb shapes. Earlier commands' ids are available to later commands in the same batch:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"method": "push_document_patch",
|
|
24
|
+
"patch": {
|
|
25
|
+
"type": "commands",
|
|
26
|
+
"baseRevision": 0,
|
|
27
|
+
"commands": [
|
|
28
|
+
{ "type": "addMarker", "marker": { "id": "placed_a", "kind": "prop", "catalogId": "my-model", "position": { "x": 1, "y": 0, "z": 2 } } },
|
|
29
|
+
{ "type": "addPath", "path": { "id": "route_a", "kind": "route", "points": [{ "x": 0, "y": 0, "z": 0 }, { "x": 1, "y": 0, "z": 2 }] } },
|
|
30
|
+
{ "type": "createCollection", "id": "region_a", "name": "Region A", "memberIds": ["placed_a", "route_a"] }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`my-model` is a model in the game's own catalog. Send this JSON through `--rpc-file <file> --save`, the live bridge, or the embedded agent's existing tool. Human code can use `session.transaction(commands)` with the same commands and undo contract.
|
|
37
|
+
|
|
38
|
+
- All commands stage before commit. A malformed command, missing/locked target, parent cycle, or cross-kind object-id collision rejects the whole batch, preserving the document, selection, undo/redo history, revision, and live subscribers. Errors include `commands[N]`; the RPC also returns `result.commandIndex` and the unchanged `result.revision`.
|
|
39
|
+
- A successful edit emits one session notification, publishes one document revision, and forms one undo/redo step. Selection-only changes do not publish or consume document history. `undo`/`redo` commands cannot run inside a transaction.
|
|
40
|
+
- Existing same-kind object ids upsert without automatic renaming. Repeating an already-current transform or stable-id placement succeeds without publishing another revision or consuming history. After a revision conflict, inspect the latest document before retrying against its revision. `force` skips only the revision check; validation and rollback still apply.
|
|
41
|
+
- Explicit copy operations such as `duplicate`, `addFragment`, and `insertPrefab` retain their existing generated-id behavior. Use direct `addMarker`/`addVolume`/`addPath`/`addNote` commands when gameplay needs caller-owned stable ids.
|
|
42
|
+
|
|
11
43
|
# Editor agent panel (embedded)
|
|
12
44
|
|
|
13
45
|
Toolbar **Agent** opens a dockable chat panel in `EditorChrome`. Tool calls use the same editor RPC verbs as the MCP/CLI bridge and the GUI — one session undo stack, interleaved with human edits.
|
|
@@ -45,6 +77,7 @@ Each `toolCalls[].name` is an editor RPC method (`set_transform`, `select`, …)
|
|
|
45
77
|
The bridge is trustworthy so agents author instead of hardcoding — every path is honest about failure:
|
|
46
78
|
|
|
47
79
|
- **Rejected mutations return `ok:false` with a reason.** A locked/cyclic `set_transform`, a `set_parent` that would form a cycle, a collection/prefab verb targeting a missing id, and a batch verb that matches nothing all fail loudly — never a phantom `{ok:true}`.
|
|
80
|
+
- **Command patches are atomic.** `push_document_patch` commands share `EditorSession.transaction` with core live sync: a bad command rolls back the entire batch, and an accepted batch has one undo step and document publication. See [atomic authoring batches](#atomic-authoring-batches).
|
|
48
81
|
- **One decode/migrate boundary.** `decodeEditorDocument` (`@jgengine/core/editor`) validates every field with a path-specific diagnostic (`$.markers[2].position`) and migrates forward; `import_document` and a `push_document_patch` **snapshot** both clear it, so a malformed or old document fails or migrates loudly rather than corrupting a live session.
|
|
49
82
|
- **Document-global id uniqueness.** Placeable ids (markers/volumes/paths/notes) are one namespace: adds re-id on collision, and a single imported document that reuses an id is rejected with its path — a duplicate-id import is impossible. Combine paths (`mergeEditorDocuments`, duplicate, overlay) re-id instead.
|
|
50
83
|
- **Schema-validated input.** `decodeEditorBridgeRequest` type-checks each field a method understands against the per-method schema before it reaches `handle` — a fuzzed value (string where a number belongs, scalar where an object belongs) is rejected at the `--rpc`/HTTP/stdio/agent-tool boundary, never cast in blind. Missing/unknown fields are left to `handle`'s guards so the boundary stays forward-compatible.
|
|
@@ -97,13 +130,40 @@ export const STASH_KIND = definePlaceableMarkerKind({
|
|
|
97
130
|
|
|
98
131
|
A placed rigged asset's animation override lives at `marker.meta.animation` and reaches the game through `markerAnimation` → `ModelConfig.animation`. It is `"auto"`, `"none"` or a config with `states`, `oneShots` and an optional stored `graph` (an `AnimGraph`, validated with `parseAnimGraph` on read).
|
|
99
132
|
|
|
100
|
-
- **Inspector → Animation** picks the mode and binds idle/walk/run and one-shot clips.
|
|
133
|
+
- **Inspector → Animation** picks the mode and binds idle/walk/run and one-shot clips. Custom playback exposes rate and Hold pose; choosing Single clip explicitly replaces locomotion states and a stored graph, then exposes seek time and looping. Playback edits preserve the remaining configuration. One-shot lists retain all variants; Ctrl/Cmd-click selects several clips.
|
|
134
|
+
- The first locomotion edit after single-clip playback seeds the rig's detected role clips, preserving deeper playback settings. Missing idle keeps an explicitly chosen single clip active; without one, playback stays at bind pose and reports the incomplete mapping. Missing walk keeps the idle pose while moving and displays a repair hint. A stored graph remains authoritative.
|
|
101
135
|
- **Animation workspace → Graph** (rail button, or the bottom dock's Animation tab) shows the graph the selected placement plays: layers, states and transitions, with the source (stored, built from states, or derived from clip roles).
|
|
102
136
|
- The scrub slider replays the graph runtime from its entry states. Parameter sliders come from the graph's blend points and conditions.
|
|
103
137
|
- Trigger buttons record a press at the playhead, so scrubbing back and forth replays it. The rig at the camera focus is posed from the runtime's output through the shell's `createGraphPose`.
|
|
104
138
|
- Editing a crossfade, or **Store graph**, writes the graph to `meta.animation.graph` as one undoable edit. **Use derived graph** drops it.
|
|
105
139
|
- Scripted: `set_meta` with `{ "animation": { "graph": { ... } } }` on the marker; see the `jgengine-world` recipe `character-animation.md` for the graph shape.
|
|
106
140
|
|
|
141
|
+
For a held single-clip pose, use `set_meta` with `{ "animation": { "clip": "Idle", "paused": true, "time": 0.75, "timeScale": 1, "loop": true } }`. Read it with `get_marker`, inspect it in play, adjust through the inspector, and save through the host's normal Save action. Reopen the document and read the same marker id to verify the saved values. `time` seeks single clips; graph playback has its own timeline. A placement without a moving entity remains at speed zero unless the game supplies animation parameters.
|
|
142
|
+
|
|
143
|
+
A game-owned player spawn can use the same marker's `catalogId` to associate the rig's catalog clips with the Animation inspector. In `defineGame`, set `scenePlacement: { excludeKinds: [...ENTITY_MARKER_KINDS, "player_spawn"] }` (`ENTITY_MARKER_KINDS` comes from `@jgengine/core/world/authoredObjects`). The canonical spawn marker remains available to the game's existing entity-spawn and `markerAnimation` composition, while automatic object placement skips that kind and keeps ordinary props. This exclusion list replaces the default mob/boss list. It controls placement eligibility. Live editor synchronization removes previously authored static objects that become excluded; outside synchronization, existing store objects remain. Game-owned entities are unaffected.
|
|
144
|
+
|
|
145
|
+
## Character movement tuning
|
|
146
|
+
|
|
147
|
+
`createMovementSchema` (`@jgengine/core/editor/movementCatalog`) creates ordinary Data-tab controls from game-supplied defaults. Export a named `EditorCatalogDefinition` with that schema and named rows, then use `set_catalog_entry` or the GUI to adjust them. Only supplied controls appear: walk speed, signed world gravity, jump velocity, step height, acceleration, braking, air control, sprint multiplier, crouch multiplier and optional maximum climb grade. No movement style or capsule/backend is selected for the game.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const readMovement = createAuthoredMovementReader(
|
|
151
|
+
() => getDocumentLiveSync()?.getDocument() ?? editorLayers,
|
|
152
|
+
"player_motion",
|
|
153
|
+
"player",
|
|
154
|
+
);
|
|
155
|
+
const authored = bindAuthoredMovement(readMovement, {
|
|
156
|
+
movement: existingMovement,
|
|
157
|
+
physics: existingPhysics,
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Pass `authored.movement` to the playable configuration and `authored.physics` to the game definition's physics. These stable objects expose live numeric getters; policy callbacks, collision settings and backend remain the game's choices. The reader scans only when the immutable document changes. `authored.diagnostics` reports invalid persisted values with document paths and repair guidance; invalid values retain the explicit game fallback. Supply `readMovement().config.walkSpeed` when resolving the game's selected player catalog entry, preserving its other stats and poses. Walk speed applies when that entity spawns or reseats; this API does not overwrite existing entities every frame.
|
|
162
|
+
|
|
163
|
+
Use the normal edit → play → inspect → adjust → save → reload loop. `get_catalog_entry` inspects saved controls; a fresh `import_document` or reopened scene must produce the same reader result. Runtime tuning requires this explicit binding; merely adding a Data row does not change gameplay. Capsule dimensions and terrain/controller backend configuration remain outside this catalog contract.
|
|
164
|
+
|
|
165
|
+
`maxClimbGrade` limits heightfield rise per horizontal distance, including airborne travel. Expose it only when the game requires that policy; the optional `climbGradeHeight` sampler remains a game-owned callback retained by the binding. This is independent of a capsule's slope angle and does not select collision geometry.
|
|
166
|
+
|
|
107
167
|
## Pure API (`@jgengine/editor`)
|
|
108
168
|
|
|
109
169
|
```ts
|
|
@@ -182,8 +242,30 @@ save); never hardcode entity stats where the document should own them.
|
|
|
182
242
|
|
|
183
243
|
## Asset import
|
|
184
244
|
|
|
245
|
+
A prop marker can store `meta.hiddenNodes` as an array of exact imported node names through an undoable `setMarker` metadata edit. Runtime props and the Materials model preview share `modelWithAuthoredNodeVisibility` (`@jgengine/core/editor/modelNodeVisibility`): omitted metadata preserves game model defaults, while `[]` clears that selection. JSON import and editor transactions reject malformed or blank names. Node hiding belongs to the cloned individual model; it does not make originally invisible source nodes visible. Nonempty selections bypass automatic prop batching, and generic scatter rejects them. Entity/player models use explicit `ModelConfig.hiddenNodes`; this marker adapter does not hydrate entity templates.
|
|
246
|
+
|
|
185
247
|
Dropping a `.glb`/`.gltf` into the editor persists it durably, but where depends on the project shape (`editorHostPlugin`, `@jgengine/node`). A **standalone folder workspace** copies the bytes into the scanned asset folder and re-lists them under a scan-stable id (`importEditorAsset`); the id comes from the file's workspace-relative path, so a placement referencing it resolves after reload. A **promoted game** (one with a typed `src/game/assets.ts` catalog — `isPromotedProject`) instead copies the bytes into `public/<basePath>/imported/` and rewrites `assets.ts` to add a durable `extras` entry to its `buildCatalog({ extras })` call (`importPromotedAsset` → `upsertCatalogExtra`), so the **shipped** game serves and resolves the asset through its own typed catalog rather than a dev-only route. The id matches what a folder rescan would produce, the rewrite is idempotent (re-import of the same id collapses to a single entry), and an unparseable or multi-`buildCatalog` source throws so the host falls back to the folder-scan import rather than corrupt the file.
|
|
186
248
|
|
|
249
|
+
## Material assets and named slots
|
|
250
|
+
|
|
251
|
+
The Materials workspace authors `document.materialAssets` through `upsert_material_asset {asset,coalesce?}`. Each versioned `MaterialAsset` has a stable ID, sparse surface fields, explicit capability declarations, texture metadata and provenance (`@jgengine/core/material/materialAsset`). `materialCapabilitiesForAsset(asset)` derives the shader requirements for its fields, maps and fabric/hair adapters; artist edits union those with existing declarations, while RPC validation still rejects undeclared requirements. Empty fields inherit the imported value; resetting a field removes its override. Renaming does not change the ID. Editable wool, cotton, silk, metal, glass, plastic, stone and hair-card starting points describe response; games own textures, models and art direction.
|
|
252
|
+
|
|
253
|
+
Use `assign_material_asset {ids,materialId,selector}` with a mesh name, material slot name or slot index. Every supplied selector field must match. Separate character skin, eyes, clothing and metal parts with separate assignments. Empty selectors fail rather than flatten a whole imported model. `list_material_assets` reads the library; `remove_material_asset {id}` removes unused assets; `clear_material_assets {ids}` removes selected objects' assignments and restores imported materials. Changes join the scene undo/redo and save path. The workspace exposes roughly six family-specific groups, full physical fields and per-map UV/sampler settings. Neutral and document-light previews use the runtime material renderer; the latter uses the authored environment and fixed sun, while game lighting can add other lights or motion.
|
|
254
|
+
|
|
255
|
+
Before assigning, select the model and open Materials, then call `list_material_slots {id}` with its authored marker ID. It reports canonical loaded mesh/slot names, local slot indices, material types, UV sets and tangents, including for a document with no material assets yet. `ready` returns observed slots; `loading` and `unavailable` return `ok:false` with an explicit status/reason instead of pretending empty data is loaded. Headless CLI cannot load the renderer. The typed host `getMaterialSlots`/`reportMaterialSlots` seam shares the workspace observation, retains up to 64 inspected model sources and rejects stale reports after a marker or catalog URL changes. Choosing another library asset edits its controls; assign it to a slot to change the selected model.
|
|
256
|
+
|
|
257
|
+
For generated building-kit models, reuse this same document library through `defineBuildingKit({ materialAssets: document.materialAssets, parts })`. Copy a selected marker's `authoredMaterialAssignments(marker.meta)` into the corresponding model variant's `materialAssignments`; do not create a second kit material catalog. The shell applies named assignments before instanced harvesting, preserving omitted native slots. This path rejects newly assigned height/displacement maps before allocation; choose an individual model renderer for them. Palette materials remain the separate broad surface path; the kit does not author scene placement or repair navigation.
|
|
258
|
+
|
|
259
|
+
Texture roles define the actual shader channels: roughness G, metalness B, AO R, sheen roughness A, clearcoat roughness G, specular intensity A, transmission R and thickness G. Anisotropy RG stores direction and B strength. Color/emissive/sheen-color/specular-color maps use sRGB; normal and numeric maps use linear data. Alpha maps use G; base-color alpha remains in its color map. Supply real runtime URLs and declare UV set, transform, normal convention, sampler, compression and physical size. Compression metadata does not transcode an image; physical size documents metres while UV transforms control rendering. Repack unsupported channels instead of silently relabelling them.
|
|
260
|
+
|
|
261
|
+
The installed renderer is Three r182. Its [physical material](https://threejs.org/docs/pages/MeshPhysicalMaterial.html) supports sheen, anisotropy, clearcoat, specular/IOR, transmission/volume and iridescence. Check the installed shader before adding controls: online docs can cover later releases. Transmission needs content behind the surface, adds rendering work and should use opacity 1. Alpha coverage cuts or blends the surface; it does not implement refraction. Directional response needs suitable UVs/normals or authored tangents, and must be reviewed after rotating the mesh or light.
|
|
262
|
+
|
|
263
|
+
[HDRP's fabric distinction](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.0/manual/understand-fabrics.html) separates diffuse cotton/wool fuzz from directional silk. The fuzzy adapter uses native grazing sheen; the woven adapter adds UV-oriented thread normals and roughness variation. Thread scale is cycles per UV, weave direction is radians, normal strength changes slope and variation changes roughness. Neither creates fibre silhouettes, cloth deformation or collision. Explicit surface values override appearance-adapter defaults.
|
|
264
|
+
|
|
265
|
+
[Unreal shading models](https://dev.epicgames.com/documentation/en-us/unreal-engine/shading-models-in-unreal-engine) have dedicated hair and cloth responses, including hair scatter/tangent/backlit inputs. [Unreal's groom workflow](https://dev.epicgames.com/documentation/en-us/unreal-engine/hair-rendering-and-simulation-in-unreal-engine) also owns strand geometry and simulation. JGengine's hair cards/strand meshes use an explicit approximation: directional physical highlights, authored coverage and card backlighting. It provides no hair BSDF, multiple scattering, groom, collision or simulation, and its skin family does not supply subsurface scattering. Similar control names do not imply parity. Review front-lit, grazing and backlit views with the actual authored geometry.
|
|
266
|
+
|
|
267
|
+
For resource review, `materialResourceMetrics` (`@jgengine/shell/render/materialAsset`) estimates loaded texture-view bytes and identifies masked, blended and transmission materials; shared uploads can make that byte estimate an upper bound. Opt in to `measureProjectedOverdraw(renderer,scene,camera,{width,height})` (`@jgengine/shell/render/projectedOverdraw`) to sample actual projected mesh fragments with native alpha discard. It requires float rendering/blending/readback, stalls the GPU, restores scene/renderer ownership and disposes its temporary resources. Depth rejection is disabled, so overlap is an upper bound rather than production early-Z cost; shadow, refraction and postprocessing passes and GPU timing are excluded. Queue and coverage flags overlap. Compare normal renderer draw counters separately, and use hardware measurements for performance claims.
|
|
268
|
+
|
|
187
269
|
## Grid / tile layers
|
|
188
270
|
|
|
189
271
|
Grid-addressed content (rooms, tactics maps, farms, nav/rule layers) lives on the scene document as
|
|
@@ -229,3 +311,69 @@ undoable `setMinimapBake` command. Runtime feeds the stored `background`/`bounds
|
|
|
229
311
|
error inline.
|
|
230
312
|
- RPC/CLI verb: `bake_minimap` (optional `padding`, `resolution`, `waterLevel`). The bake path uses
|
|
231
313
|
no `Date`/`Math.random`, so the same scene + sampler always bakes byte-identical output.
|
|
314
|
+
|
|
315
|
+
## Terrain and viewport iteration
|
|
316
|
+
|
|
317
|
+
`create_terrain` rejects invalid dimensions and more than 1,000,000 vertices before allocation; increase `cellSize` for larger maps. Brush coordinates, radius and strength must be finite, radius positive, strength nonnegative. A ramp requires both `toX` and `toZ`; blend strength is 0–1.
|
|
318
|
+
|
|
319
|
+
`blend_terrain` adds a missing material layer and paints it as one authored action: one notification and one undo. A missed or invalid brush changes neither the layer stack nor undo/redo. `set_terrain_layers` and `EditableTerrain.setLayers` preserve blends by stable layer id across additions, reorder and material parameter edits. Removing a painted layer normalizes surviving weights; replacing every painted layer clears the obsolete buffer.
|
|
320
|
+
|
|
321
|
+
Viewport placement honors grid snap for marker, zone, note and path points on X/Z, then resamples the authored sculpt composed over the shared base ground field. Paths select along visible segments as well as vertices. Multi-selection gizmos translate the whole group; rotate/scale remain available for individual supported objects.
|
|
322
|
+
|
|
323
|
+
Terrain-only scenes work with `camera_frame` / Frame all: the authored footprint and sculpt relief contribute to document bounds without a temporary marker. Native runner captures use `drive editor --param editor=standalone` and the same editor RPCs.
|
|
324
|
+
|
|
325
|
+
Grid placement resamples the authored sculpt composed over the live base ground field at snapped XZ for markers, volumes, notes and draft path points. It uses the same terrain/path policy as the ground overlays (including the ground field’s off-map behavior), and flat Y=0 when no ground sampler is mounted. Nonfinite surface heights reject placement without changing the document or clearing the tool.
|
|
326
|
+
|
|
327
|
+
## Static prefab export
|
|
328
|
+
|
|
329
|
+
Keep source parts in `EditorPrefab.fragment` and author export settings with `set_prefab_static_bake`
|
|
330
|
+
(or the `setPrefabStaticBake` command): `prefabId` and `bake: { assetId, collisionBoxes?, clearances? }`.
|
|
331
|
+
`bake: null` clears settings. Save/reopen and undo/redo retain settings alongside the editable parts;
|
|
332
|
+
an invalid setting rejects the whole command without changing history. Each box uses local
|
|
333
|
+
`min`/`max` XYZ triples; clearances add a unique `id`. Clearances require explicit solid boxes and
|
|
334
|
+
may touch their edges but must not overlap them. These are authored constraints, not automatic
|
|
335
|
+
proof that an entrance is navigable for a particular character.
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{"method":"set_prefab_static_bake","prefabId":"courtyard-arch","bake":{"assetId":"own:arch","collisionBoxes":[{"min":[-2,0,-1],"max":[-1,3,1]},{"min":[1,0,-1],"max":[2,3,1]}],"clearances":[{"id":"entry","min":[-1,0,-1],"max":[1,2,1]}]}}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Export pinned source bytes offline with `bakeStaticPrefab` from
|
|
342
|
+
`@jgengine/assets/staticPrefabBake` (Node/Bun only; see `jgengine-assets`). Register the returned
|
|
343
|
+
`dims`, `collisionMesh` and `anchor: "origin"` with the output URL in the game's asset catalog.
|
|
344
|
+
The shared model resolver preserves that authored origin for rendering and collider consumption.
|
|
345
|
+
World placements remain catalog markers in `editor.scene.json`; baking never replaces or moves
|
|
346
|
+
them. Validate the game's real walker/route clearance before advertising walkable interiors.
|
|
347
|
+
|
|
348
|
+
## Production creator
|
|
349
|
+
|
|
350
|
+
The game owns its menu and scene rules. The shared creator owns named durable documents, editor mounting, Save and isolated playtests:
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
import { createCreatorDocumentStorage, type CreatorConfig, type CreatorPolicy } from "@jgengine/core/editor/creatorStorage";
|
|
354
|
+
import { GameHost } from "@jgengine/shell/GameHost";
|
|
355
|
+
import type { PlayableGame } from "@jgengine/shell/registry";
|
|
356
|
+
|
|
357
|
+
const policy: CreatorPolicy = {
|
|
358
|
+
maxDocuments: 8, maxBytes: 128_000, maxObjects: 48,
|
|
359
|
+
maxPathPoints: 128, maxGridCells: 0, maxTerrainVertices: 0,
|
|
360
|
+
allowedKinds: ["player_spawn", "prop", "checkpoint", "finish"],
|
|
361
|
+
allowedAssets: ["own:platform"], allowedCatalogIds: ["platform"],
|
|
362
|
+
validate(document) { validateGameRules(document); },
|
|
363
|
+
};
|
|
364
|
+
const creator: CreatorConfig<PlayableGame> = {
|
|
365
|
+
policy,
|
|
366
|
+
storage: createCreatorDocumentStorage({ storage: localStorage, key: "my-game:creator:v1", policy }),
|
|
367
|
+
initialDocument: () => authoredStartingDocument,
|
|
368
|
+
createPlayable: document => buildGameFromDocument(document),
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
<GameHost playable={game} editor={() => import("@jgengine/editor")}
|
|
372
|
+
creator={creator} creatorOpen={creatorOpen} onCreatorOpenChange={setCreatorOpen} />;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Keep `creator` and its callbacks stable across renders. `createPlayable` recomposes rendering, world and gameplay from the supplied document; do not close over the bundled starting scene. `initialDocument` supplies game-authored content, not an SDK genre preset. Creator saves are version 1 named records and reject optimistic revision conflicts. Saving at the maximum safe integer revision rejects with `Invalid scene revision` before writing; the last valid catalog stays readable. The injected storage promise must resolve only after durable success. The browser adapter writes one bounded catalog value and surfaces access/quota failures; hosted storage can implement the same list/load/save interface with transactional revisions.
|
|
376
|
+
|
|
377
|
+
The approved Add kinds and asset palette are filtered in the editor. The same validator applies before commands and transactions commit, on import, and on save. External asset URLs, prefab/directive expansion and undeclared simulation budgets are rejected. `maxSimulationParticles`, `maxFireCells`, `maxForceFields` and `maxPopulation` opt into bounded authored simulation (weather reserves 5,300 precipitation particles plus 256 rain-impact slots; collision effects reserve capacity for 32 concurrent child bursts alongside standing emitters); `validate` can restrict species, triggers or required markers. Save/export contain the authored scene; gameplay mutations stay in the captured playtest runtime and are discarded on Return to editor. Returning to saved scenes reopens the last durable save. Unsaved edits stay through Play/Return, and are discarded when leaving the editor catalog entry.
|
|
378
|
+
|
|
379
|
+
The repeatable snapshot → Play → Return workflow follows the iteration principle in [UEFN playtesting](https://dev.epicgames.com/documentation/en-us/fortnite/playtesting-your-island-in-unreal-editor-for-fortnite). Each Play captures a new validated snapshot; authoring stays locked during isolated play, and Save always writes the editable document.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Live-sync reconnect
|
|
2
|
+
|
|
3
|
+
`DocumentLiveSync.pullPatches(revision)` retains up to 64 patches. A cursor older
|
|
4
|
+
than that history receives one current snapshot with the requested base revision
|
|
5
|
+
and the authority's current revision. Apply it with `applyDocumentPatch`; preserve
|
|
6
|
+
the emitted revision so the next command patch joins the same stream. Snapshot
|
|
7
|
+
recovery replaces the whole document and accounts for deleted authored content.
|
|
8
|
+
|
|
9
|
+
`pull_runtime_deltas` retains up to 128 ephemeral deltas. When a requested cursor
|
|
10
|
+
has a gap, the RPC returns an empty `deltas` array and a current `snapshot` even
|
|
11
|
+
without `includeSnapshot`. Replace the cached runtime snapshot; do not merge it
|
|
12
|
+
into old entities, since evicted removals must disappear. Continue polling from
|
|
13
|
+
its `seq`. A current cursor receives ordinary deltas; `includeSnapshot` can also
|
|
14
|
+
request the snapshot explicitly. Neither channel changes authored content unless
|
|
15
|
+
an explicit write-back command does so.
|