@vgai/engine 0.5.21 → 0.5.23
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/README.md +6 -3
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +2 -4
- package/dist/adapter/adapter-surface.d.ts +1 -1
- package/dist/adapter/adapter-surface.js +1 -1
- package/dist/adapter/authoring.d.ts +19 -21
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +2 -2
- package/dist/adapter/binding.d.ts +341 -0
- package/dist/adapter/binding.d.ts.map +1 -0
- package/dist/adapter/binding.js +148 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -1
- package/dist/adapter/colyseus-networking-adapter.js +23 -4
- package/dist/adapter/entrypoint-selection-readers.d.ts +24 -0
- package/dist/adapter/entrypoint-selection-readers.d.ts.map +1 -0
- package/dist/adapter/entrypoint-selection-readers.js +63 -0
- package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -1
- package/dist/adapter/entrypoint-selection-source.js +1 -44
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +1 -52
- package/dist/adapter/host-context.d.ts +46 -15
- package/dist/adapter/host-context.d.ts.map +1 -1
- package/dist/adapter/host-context.js +2 -9
- package/dist/adapter/index.d.ts +11 -12
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +9 -10
- package/dist/adapter/ingest/game-contract.d.ts +0 -3
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +0 -3
- package/dist/adapter/ingest/scene-capture.d.ts +19 -23
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +143 -17
- package/dist/adapter/ingest/visible-capture-window.d.ts +34 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
- package/dist/adapter/manifest-interpreter.d.ts +59 -0
- package/dist/adapter/manifest-interpreter.d.ts.map +1 -0
- package/dist/adapter/manifest-interpreter.js +50 -0
- package/dist/adapter/native-debug-module.d.ts +71 -3
- package/dist/adapter/native-debug-module.d.ts.map +1 -1
- package/dist/adapter/native-debug-module.js +146 -10
- package/dist/adapter/rapier-physics-adapter.d.ts +31 -1
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -1
- package/dist/adapter/rapier-physics-adapter.js +50 -36
- package/dist/adapter/renderer-config.d.ts +3 -3
- package/dist/adapter/renderer-config.js +3 -3
- package/dist/adapter/root-adapter.d.ts +7 -7
- package/dist/adapter/root-adapter.js +4 -4
- package/dist/adapter/system-adapter.d.ts +45 -7
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-seam-contract.d.ts +3 -3
- package/dist/adapter/system-seam-contract.js +3 -3
- package/dist/adapter/system-slot.d.ts +168 -0
- package/dist/adapter/system-slot.d.ts.map +1 -0
- package/dist/adapter/system-slot.js +229 -0
- package/dist/adapter/transform.d.ts +11 -1
- package/dist/adapter/transform.d.ts.map +1 -1
- package/dist/ai/navigation.d.ts +17 -1
- package/dist/ai/navigation.d.ts.map +1 -1
- package/dist/ai/navigation.js +26 -5
- package/dist/animation/anim-graph-types.d.ts +11 -3
- package/dist/animation/anim-graph-types.d.ts.map +1 -1
- package/dist/animation/theatre-clock-binding.d.ts +4 -4
- package/dist/animation/theatre-clock-binding.js +4 -4
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +2 -2
- package/dist/animation/xstate-animation-meta.d.ts +13 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -1
- package/dist/animation/xstate-animation-meta.js +14 -0
- package/dist/asset-formats/index.d.ts +4 -7
- package/dist/asset-formats/index.d.ts.map +1 -1
- package/dist/asset-formats/index.js +4 -7
- package/dist/asset-formats/render-env.d.ts +5 -8
- package/dist/asset-formats/render-env.d.ts.map +1 -1
- package/dist/asset-formats/render-env.js +3 -6
- package/dist/asset-parse-error.d.ts +2 -5
- package/dist/asset-parse-error.d.ts.map +1 -1
- package/dist/asset-parse-error.js +2 -5
- package/dist/audio/bus-mixer.d.ts +101 -0
- package/dist/audio/bus-mixer.d.ts.map +1 -0
- package/dist/audio/bus-mixer.js +115 -0
- package/dist/canvas-react/index.d.ts +5 -12
- package/dist/canvas-react/index.d.ts.map +1 -1
- package/dist/canvas-react/index.js +5 -12
- package/dist/canvas-react/pixi-react-root-factory.d.ts +34 -21
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.js +274 -36
- package/dist/core/countdown-timer.d.ts +118 -0
- package/dist/core/countdown-timer.d.ts.map +1 -0
- package/dist/core/countdown-timer.js +137 -0
- package/dist/core/deferred-commands.d.ts +127 -0
- package/dist/core/deferred-commands.d.ts.map +1 -0
- package/dist/core/deferred-commands.js +132 -0
- package/dist/core/frame-pacing.d.ts +1 -1
- package/dist/core/frame-pacing.js +2 -2
- package/dist/core/game-loop.d.ts +1 -3
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +1 -3
- package/dist/core/seeded-random.d.ts +1 -1
- package/dist/core/sim-clock.d.ts +19 -8
- package/dist/core/sim-clock.d.ts.map +1 -1
- package/dist/core/sim-clock.js +18 -7
- package/dist/core/types.d.ts +1 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/defaults.d.ts +6 -14
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +6 -14
- package/dist/dev/instruments.d.ts +38 -11
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +81 -11
- package/dist/dev/register-render-vitals.d.ts +1 -4
- package/dist/dev/register-render-vitals.d.ts.map +1 -1
- package/dist/dev/register-render-vitals.js +1 -4
- package/dist/dev/render-debug-adapter.d.ts +2 -3
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +2 -3
- package/dist/dev/render-vitals.d.ts +3 -3
- package/dist/dev/render-vitals.js +3 -3
- package/dist/dev/webgl-frame-capture.d.ts +1 -1
- package/dist/dev/webgl-frame-capture.js +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/input/host-pointer.d.ts +122 -0
- package/dist/input/host-pointer.d.ts.map +1 -0
- package/dist/input/host-pointer.js +160 -0
- package/dist/input/input-manager.d.ts +33 -5
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +48 -13
- package/dist/input/rebind-controller.d.ts.map +1 -1
- package/dist/input/rebind-controller.js +6 -8
- package/dist/input/schema.d.ts.map +1 -1
- package/dist/input/schema.js +56 -28
- package/dist/manifest/filename.d.ts.map +1 -1
- package/dist/manifest/filename.js +8 -12
- package/dist/manifest/load.d.ts +10 -10
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +9 -6
- package/dist/manifest/schema.d.ts +17 -21
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +45 -35
- package/dist/physics/collider-dimensions.d.ts +8 -12
- package/dist/physics/collider-dimensions.d.ts.map +1 -1
- package/dist/physics/collider-dimensions.js +8 -12
- package/dist/pixi/scene-capture.d.ts +11 -13
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/react/world-state.d.ts +1 -1
- package/dist/react/world-state.d.ts.map +1 -1
- package/dist/render/directional-shadow-fit.d.ts +88 -0
- package/dist/render/directional-shadow-fit.d.ts.map +1 -0
- package/dist/render/directional-shadow-fit.js +133 -0
- package/dist/render/environment-capture.d.ts +68 -0
- package/dist/render/environment-capture.d.ts.map +1 -0
- package/dist/render/environment-capture.js +90 -0
- package/dist/render/ibl-override-material.d.ts +97 -0
- package/dist/render/ibl-override-material.d.ts.map +1 -0
- package/dist/render/ibl-override-material.js +114 -0
- package/dist/render/light-camera-factory.d.ts +5 -7
- package/dist/render/light-camera-factory.d.ts.map +1 -1
- package/dist/render/light-camera-factory.js +8 -11
- package/dist/render/material-factory.js +1 -1
- package/dist/render/render-batch-system.d.ts.map +1 -1
- package/dist/render/render-features.d.ts +17 -6
- package/dist/render/render-features.d.ts.map +1 -1
- package/dist/render/render-features.js +0 -28
- package/dist/runtime/create-runtime.d.ts +34 -25
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +218 -93
- package/dist/runtime/debug-registry.d.ts +77 -8
- package/dist/runtime/debug-registry.d.ts.map +1 -1
- package/dist/runtime/debug-registry.js +3 -3
- package/dist/runtime/dev-build.d.ts +2 -2
- package/dist/runtime/dev-build.js +2 -2
- package/dist/runtime/game-input-seams.d.ts +51 -0
- package/dist/runtime/game-input-seams.d.ts.map +1 -0
- package/dist/runtime/game-input-seams.js +86 -0
- package/dist/runtime/game.d.ts +104 -40
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +119 -61
- package/dist/runtime/mount-game.d.ts +0 -16
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-game.js +26 -41
- package/dist/runtime/mount-manifest.d.ts +22 -14
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +22 -20
- package/dist/runtime/playtest.d.ts +22 -0
- package/dist/runtime/playtest.d.ts.map +1 -0
- package/dist/runtime/playtest.js +12 -0
- package/dist/runtime/state-bridge.d.ts +2 -3
- package/dist/runtime/state-bridge.d.ts.map +1 -1
- package/dist/runtime/state-bridge.js +1 -0
- package/dist/setup/setup-audio.d.ts +12 -0
- package/dist/setup/setup-audio.d.ts.map +1 -1
- package/dist/setup/setup-audio.js +9 -10
- package/dist/setup/setup-renderer.d.ts +2 -25
- package/dist/setup/setup-renderer.d.ts.map +1 -1
- package/dist/setup/setup-renderer.js +2 -31
- package/dist/world3d-react/index.d.ts +15 -25
- package/dist/world3d-react/index.d.ts.map +1 -1
- package/dist/world3d-react/index.js +15 -25
- package/dist/world3d-react/r3f-root-factory.d.ts +28 -34
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +403 -57
- package/dist/world3d-react/rapier-physics-bridge.d.ts +30 -17
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +143 -58
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +16 -14
- package/schemas/vgai-project.schema.json +35 -26
- package/src/adapter/adapter-module.ts +2 -4
- package/src/adapter/adapter-surface.ts +1 -1
- package/src/adapter/authoring.ts +19 -21
- package/src/adapter/binding.ts +468 -0
- package/src/adapter/colyseus-networking-adapter.ts +20 -5
- package/src/adapter/entrypoint-selection-readers.ts +66 -0
- package/src/adapter/entrypoint-selection-source.ts +1 -40
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +1 -48
- package/src/adapter/host-context.ts +45 -15
- package/src/adapter/index.ts +23 -11
- package/src/adapter/ingest/game-contract.ts +0 -3
- package/src/adapter/ingest/scene-capture.ts +190 -39
- package/src/adapter/ingest/visible-capture-window.ts +35 -0
- package/src/adapter/manifest-interpreter.ts +64 -0
- package/src/adapter/native-debug-module.ts +248 -15
- package/src/adapter/rapier-physics-adapter.ts +76 -20
- package/src/adapter/renderer-config.ts +3 -3
- package/src/adapter/root-adapter.ts +7 -7
- package/src/adapter/system-adapter.ts +43 -8
- package/src/adapter/system-seam-contract.ts +3 -3
- package/src/adapter/system-slot.ts +291 -0
- package/src/adapter/transform.ts +18 -1
- package/src/ai/navigation.ts +30 -3
- package/src/animation/anim-graph-types.ts +11 -3
- package/src/animation/theatre-clock-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +2 -2
- package/src/animation/xstate-animation-meta.ts +16 -0
- package/src/asset-formats/index.ts +4 -7
- package/src/asset-formats/render-env.ts +3 -6
- package/src/asset-parse-error.ts +2 -5
- package/src/audio/bus-mixer.ts +161 -0
- package/src/canvas-react/index.ts +5 -21
- package/src/canvas-react/pixi-react-root-factory.tsx +301 -39
- package/src/core/countdown-timer.ts +188 -0
- package/src/core/deferred-commands.ts +174 -0
- package/src/core/frame-pacing.ts +2 -2
- package/src/core/game-loop.ts +1 -3
- package/src/core/seeded-random.ts +1 -1
- package/src/core/sim-clock.ts +19 -8
- package/src/core/types.ts +1 -3
- package/src/defaults.ts +6 -14
- package/src/dev/instruments.ts +108 -11
- package/src/dev/register-render-vitals.ts +1 -4
- package/src/dev/render-debug-adapter.ts +2 -3
- package/src/dev/render-vitals.ts +3 -3
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/index.ts +2 -5
- package/src/input/host-pointer.ts +230 -0
- package/src/input/input-manager.ts +49 -13
- package/src/input/input-types.ts +1 -1
- package/src/input/rebind-controller.ts +6 -8
- package/src/input/schema.ts +216 -188
- package/src/manifest/filename.ts +8 -12
- package/src/manifest/load.ts +17 -13
- package/src/manifest/schema.ts +55 -47
- package/src/physics/collider-dimensions.ts +8 -12
- package/src/pixi/scene-capture.ts +12 -15
- package/src/react/world-state.tsx +1 -1
- package/src/render/directional-shadow-fit.ts +156 -0
- package/src/render/environment-capture.ts +102 -0
- package/src/render/ibl-override-material.ts +170 -0
- package/src/render/light-camera-factory.ts +8 -11
- package/src/render/material-factory.ts +1 -1
- package/src/render/render-batch-system.ts +14 -6
- package/src/render/render-features.ts +17 -35
- package/src/runtime/create-runtime.ts +270 -108
- package/src/runtime/debug-registry.ts +84 -8
- package/src/runtime/dev-build.ts +2 -2
- package/src/runtime/game-input-seams.ts +108 -0
- package/src/runtime/game.ts +231 -85
- package/src/runtime/mount-game.ts +31 -57
- package/src/runtime/mount-manifest.ts +47 -36
- package/src/runtime/playtest.ts +22 -0
- package/src/runtime/state-bridge.ts +3 -3
- package/src/setup/setup-audio.ts +21 -11
- package/src/setup/setup-renderer.ts +2 -61
- package/src/world3d-react/index.ts +15 -31
- package/src/world3d-react/r3f-root-factory.tsx +451 -60
- package/src/world3d-react/rapier-physics-bridge.tsx +154 -58
- package/dist/adapter/setup-three-root-adapter.d.ts +0 -86
- package/dist/adapter/setup-three-root-adapter.d.ts.map +0 -1
- package/dist/adapter/setup-three-root-adapter.js +0 -908
- package/dist/animation/clip-map.d.ts +0 -12
- package/dist/animation/clip-map.d.ts.map +0 -1
- package/dist/animation/clip-map.js +0 -31
- package/dist/asset-registry.d.ts +0 -38
- package/dist/asset-registry.d.ts.map +0 -1
- package/dist/asset-registry.js +0 -66
- package/dist/canvas-react/engine-bridge.d.ts +0 -45
- package/dist/canvas-react/engine-bridge.d.ts.map +0 -1
- package/dist/canvas-react/engine-bridge.js +0 -45
- package/dist/canvas-react/pixi-react-adapter.d.ts +0 -77
- package/dist/canvas-react/pixi-react-adapter.d.ts.map +0 -1
- package/dist/canvas-react/pixi-react-adapter.js +0 -294
- package/dist/canvas-react/world-context.d.ts +0 -98
- package/dist/canvas-react/world-context.d.ts.map +0 -1
- package/dist/canvas-react/world-context.js +0 -173
- package/dist/dev/debug-draw.d.ts +0 -24
- package/dist/dev/debug-draw.d.ts.map +0 -1
- package/dist/dev/debug-draw.js +0 -73
- package/dist/render/auto-batcher.d.ts +0 -34
- package/dist/render/auto-batcher.d.ts.map +0 -1
- package/dist/render/auto-batcher.js +0 -140
- package/dist/render/lod.d.ts +0 -13
- package/dist/render/lod.d.ts.map +0 -1
- package/dist/render/lod.js +0 -16
- package/dist/runtime/types.d.ts +0 -303
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/world3d-react/engine-bridge.d.ts +0 -47
- package/dist/world3d-react/engine-bridge.d.ts.map +0 -1
- package/dist/world3d-react/engine-bridge.js +0 -73
- package/dist/world3d-react/r3f-adapter.d.ts +0 -58
- package/dist/world3d-react/r3f-adapter.d.ts.map +0 -1
- package/dist/world3d-react/r3f-adapter.js +0 -634
- package/dist/world3d-react/world-context.d.ts +0 -182
- package/dist/world3d-react/world-context.d.ts.map +0 -1
- package/dist/world3d-react/world-context.js +0 -235
- package/src/adapter/setup-three-root-adapter.ts +0 -1032
- package/src/animation/clip-map.ts +0 -34
- package/src/asset-registry.ts +0 -89
- package/src/canvas-react/engine-bridge.ts +0 -59
- package/src/canvas-react/pixi-react-adapter.tsx +0 -356
- package/src/canvas-react/world-context.ts +0 -253
- package/src/dev/debug-draw.ts +0 -80
- package/src/render/auto-batcher.ts +0 -168
- package/src/render/lod.ts +0 -17
- package/src/runtime/types.ts +0 -328
- package/src/world3d-react/engine-bridge.ts +0 -86
- package/src/world3d-react/r3f-adapter.tsx +0 -717
- package/src/world3d-react/world-context.ts +0 -358
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* WHY IT EXISTS. `SystemAdapters.physics` is what the editor's transform
|
|
6
6
|
* coordination (`freeze → apply → unfreeze`) and the universal
|
|
7
|
-
* "Colliders drawn" instrument (`@engine/dev/instruments`) read.
|
|
8
|
-
* first-party
|
|
9
|
-
*
|
|
10
|
-
* Rapier runtime at all (see `R3FGameContext`'s doc comment) — it does physics
|
|
11
|
-
* with `@react-three/rapier` INSIDE the fiber tree, where nothing engine-side
|
|
7
|
+
* "Colliders drawn" instrument (`@engine/dev/instruments`) read. An R3F world
|
|
8
|
+
* builds no first-party Rapier runtime at all — it does physics with
|
|
9
|
+
* `@react-three/rapier` INSIDE the fiber tree, where nothing engine-side
|
|
12
10
|
* can see it. Result, before this: the colliders instrument read `null` (an
|
|
13
11
|
* em-dash) and its toggle threw "no physics adapter" in every shipped example
|
|
14
12
|
* and every scaffolded game.
|
|
@@ -40,18 +38,23 @@
|
|
|
40
38
|
* is how a `SystemAdapters` member says "not supported".
|
|
41
39
|
*
|
|
42
40
|
* ── RESOURCE OWNERSHIP, STATED ONCE ─────────────────────────────────────────
|
|
43
|
-
* OWNER: this component owns
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* a world that unmounts `<Physics>` leaves no stale adapter
|
|
54
|
-
*
|
|
41
|
+
* OWNER: this component owns one physics ADAPTER, published onto the ONE
|
|
42
|
+
* module-scoped slot `rapierPhysicsSystem()` forwards to (GAME-GLOBAL — the
|
|
43
|
+
* entry's static `systems.physics` declaration cannot be per-root). It allocates nothing
|
|
44
|
+
* else — no timers, no subscriptions, no per-frame work (see the phase note
|
|
45
|
+
* below), and no Rapier objects: the bodies it edits belong to `<Physics>`.
|
|
46
|
+
* SHARERS: every reader of `game.systemAdapters.physics` — the colliders
|
|
47
|
+
* instrument, the editor's transform coordination. They read; they never
|
|
48
|
+
* dispose.
|
|
49
|
+
* TEARDOWN: the effect's cleanup, and nothing else — it clears the SAME key
|
|
50
|
+
* and the slot it set (identity-guarded), so a play stop/start, a hot
|
|
51
|
+
* restart, or a world that unmounts `<Physics>` leaves no stale adapter
|
|
52
|
+
* behind. Because the slot is PAGE-global (see its doc below — an engine
|
|
53
|
+
* module is shared across the editor's Play instances), mount exactly ONE
|
|
54
|
+
* bridge per PAGE: with two — a scene that mounts two, or two Play instances
|
|
55
|
+
* of one rapier world — the later mount wins the slot and the later unmount
|
|
56
|
+
* clears it while the earlier bridge is still live. That state is reported
|
|
57
|
+
* loudly the moment it starts (`warnOnContendedMount`).
|
|
55
58
|
*
|
|
56
59
|
* PHASE ORDERING: none to respect — this component registers pull-only
|
|
57
60
|
* callbacks and runs no per-frame work. Every method it exposes is invoked
|
|
@@ -62,7 +65,7 @@ import { useRapier } from '@react-three/rapier';
|
|
|
62
65
|
import { useEffect } from 'react';
|
|
63
66
|
import * as THREE from 'three';
|
|
64
67
|
import { createRapierBodyEditing, } from '../adapter/rapier-physics-adapter';
|
|
65
|
-
import {
|
|
68
|
+
import { createSystemSlot } from '../adapter/system-slot';
|
|
66
69
|
function lateEntityObject(states, nodeId) {
|
|
67
70
|
let found;
|
|
68
71
|
for (const state of states) {
|
|
@@ -92,50 +95,51 @@ function owningBody(object, states) {
|
|
|
92
95
|
* `useRapier()`). Renders nothing.
|
|
93
96
|
*/
|
|
94
97
|
export function RapierPhysicsBridge({ onDebugChange }) {
|
|
95
|
-
|
|
98
|
+
// This bridge publishes the module slot below, which the entry's
|
|
99
|
+
// `systems.physics` declaration (`rapierPhysicsSystem()`) forwards to —
|
|
100
|
+
// that declaration is the ONE path the host reads this world's physics
|
|
101
|
+
// through.
|
|
96
102
|
const { colliderStates, rapier, rigidBodyStates, world } = useRapier();
|
|
97
|
-
const registerSystemAdapter = ctx.registerSystemAdapter;
|
|
98
103
|
useEffect(() => {
|
|
99
|
-
if (!registerSystemAdapter)
|
|
100
|
-
return;
|
|
101
104
|
/**
|
|
102
|
-
* node id → the Rapier body driving it,
|
|
103
|
-
*
|
|
105
|
+
* node id → the Rapier body driving it, entirely through the library's
|
|
106
|
+
* OWN body↔object map (`rigidBodyStates`, carried on the `RapierContext`
|
|
107
|
+
* `useRapier()` returns): resolve `userData.entityId` — this stack's
|
|
108
|
+
* identity convention, stamped by the editor's R3F authoring adapter —
|
|
109
|
+
* inside the body-owned subtrees, then walk ANCESTORS to the owning body
|
|
110
|
+
* (`<RigidBody>` owns a subtree, so the selected mesh is usually a
|
|
111
|
+
* descendant of the object the state names).
|
|
104
112
|
*
|
|
105
|
-
*
|
|
106
|
-
* is this stack's identity convention (the editor's R3F authoring
|
|
107
|
-
* adapter stamps it; `../adapter/setup-three-root-adapter.ts` resolves
|
|
108
|
-
* the same way for the imperative root).
|
|
109
|
-
* 2. `Object3D` → body, through `rigidBodyStates` — the library's OWN
|
|
110
|
-
* body↔object map, carried on the `RapierContext` `useRapier()`
|
|
111
|
-
* returns. Walking ANCESTORS matters: `<RigidBody>` owns a subtree, so
|
|
112
|
-
* the selected mesh is usually a descendant of the object the state
|
|
113
|
-
* names.
|
|
114
|
-
*
|
|
115
|
-
* Both maps are live, so this is computed per call rather than cached —
|
|
113
|
+
* The map is live, so this is computed per call rather than cached —
|
|
116
114
|
* calls arrive at gesture rate (a gizmo drag start/end, a dev command),
|
|
117
|
-
* never per frame
|
|
118
|
-
*
|
|
119
|
-
* after this component's first effect (`world-context.ts`'s
|
|
120
|
-
* `setSceneCamera`), so a destructured copy would be permanently absent.
|
|
115
|
+
* never per frame, and only objects under a `<RigidBody>` can have a body
|
|
116
|
+
* at all, so the body subtrees are the complete search space.
|
|
121
117
|
*/
|
|
122
118
|
const bodyFor = (nodeId) => {
|
|
123
|
-
const
|
|
124
|
-
const object = indexed ?? lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
125
|
-
if (!indexed && object) {
|
|
126
|
-
// The editor assigns its stable authoring entity ids AFTER Fiber's
|
|
127
|
-
// first commit, while the root's SceneIndex is constructed during
|
|
128
|
-
// that commit. Resolve the one late id inside the library-owned body
|
|
129
|
-
// subtrees and immediately reindex it; subsequent gestures take the
|
|
130
|
-
// normal indexed path. This is the SceneIndex's documented escape
|
|
131
|
-
// hatch for a userData id written after construction, not a parallel
|
|
132
|
-
// scene mirror.
|
|
133
|
-
ctx.sceneIndex?.reindex(object);
|
|
134
|
-
}
|
|
119
|
+
const object = lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
135
120
|
if (!object)
|
|
136
121
|
return undefined;
|
|
137
122
|
return owningBody(object, rigidBodyStates.values());
|
|
138
123
|
};
|
|
124
|
+
/**
|
|
125
|
+
* The same question, with the two negative answers KEPT APART for the
|
|
126
|
+
* editing protocol — see {@link RapierBodyLookup}. This world's search
|
|
127
|
+
* space is the `<RigidBody>` subtrees, so "no object here" really is "not a
|
|
128
|
+
* node of mine", not "a node of mine without physics".
|
|
129
|
+
*/
|
|
130
|
+
const bodyLookup = (nodeId) => {
|
|
131
|
+
const object = lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
132
|
+
if (!object)
|
|
133
|
+
return { kind: 'unresolved' };
|
|
134
|
+
const body = owningBody(object, rigidBodyStates.values());
|
|
135
|
+
return body ? { kind: 'body', body } : { kind: 'no-body' };
|
|
136
|
+
};
|
|
137
|
+
/** Rapier's own name for a shape type, via the enum's reverse mapping —
|
|
138
|
+
* so an unprojected collider reports WHICH shape it is (`TriMesh`,
|
|
139
|
+
* `HeightField`, …) instead of a bare number, and a shape added by a
|
|
140
|
+
* future rapier still names itself. */
|
|
141
|
+
const rapierShapeName = (shapeType) => rapier.ShapeType[shapeType] ??
|
|
142
|
+
`shapeType ${shapeType}`;
|
|
139
143
|
const colliderShape = (collider) => {
|
|
140
144
|
switch (collider.shapeType()) {
|
|
141
145
|
case rapier.ShapeType.Cuboid: {
|
|
@@ -150,8 +154,13 @@ export function RapierPhysicsBridge({ onDebugChange }) {
|
|
|
150
154
|
halfHeight: collider.halfHeight(),
|
|
151
155
|
radius: collider.radius(),
|
|
152
156
|
};
|
|
157
|
+
// Rapier has many more shapes than this seam projects (TriMesh,
|
|
158
|
+
// HeightField, ConvexPolyhedron, Cylinder, Cone, …). The collider is
|
|
159
|
+
// REAL; only our plain-data projection of it is missing, so it is
|
|
160
|
+
// reported as itself rather than dropped — a body whose only collider
|
|
161
|
+
// is a trimesh used to inspect as a body with no colliders at all.
|
|
153
162
|
default:
|
|
154
|
-
return
|
|
163
|
+
return { type: 'unsupported', kind: rapierShapeName(collider.shapeType()) };
|
|
155
164
|
}
|
|
156
165
|
};
|
|
157
166
|
const colliders = (nodeId) => {
|
|
@@ -163,8 +172,6 @@ export function RapierPhysicsBridge({ onDebugChange }) {
|
|
|
163
172
|
if (state.collider.parent()?.handle !== body.handle)
|
|
164
173
|
continue;
|
|
165
174
|
const shape = colliderShape(state.collider);
|
|
166
|
-
if (!shape)
|
|
167
|
-
continue;
|
|
168
175
|
state.object.updateWorldMatrix(true, false);
|
|
169
176
|
const position = state.collider.translation();
|
|
170
177
|
const rotation = state.collider.rotation();
|
|
@@ -199,6 +206,11 @@ export function RapierPhysicsBridge({ onDebugChange }) {
|
|
|
199
206
|
collider.setHalfHeight(shape.halfHeight);
|
|
200
207
|
collider.setRadius(shape.radius);
|
|
201
208
|
break;
|
|
209
|
+
// A shape this seam does not project has no dimensions to write back.
|
|
210
|
+
// `colliders()` reports it so the body's physics is not invisible; that
|
|
211
|
+
// is a READ, and it does not make the shape editable.
|
|
212
|
+
case 'unsupported':
|
|
213
|
+
break;
|
|
202
214
|
}
|
|
203
215
|
};
|
|
204
216
|
const jointType = (type) => {
|
|
@@ -296,15 +308,88 @@ export function RapierPhysicsBridge({ onDebugChange }) {
|
|
|
296
308
|
joint.setAnchor2(value);
|
|
297
309
|
};
|
|
298
310
|
const adapter = {
|
|
299
|
-
...createRapierBodyEditing(
|
|
311
|
+
...createRapierBodyEditing(bodyLookup),
|
|
300
312
|
setDebugDrawEnabled: (enabled) => onDebugChange(enabled),
|
|
301
313
|
colliders,
|
|
302
314
|
previewCollider,
|
|
303
315
|
joints,
|
|
304
316
|
previewJointAnchor,
|
|
305
317
|
};
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
318
|
+
warnOnContendedMount();
|
|
319
|
+
const detach = physicsSlot.attach(adapter);
|
|
320
|
+
return () => {
|
|
321
|
+
liveBridgeCount -= 1;
|
|
322
|
+
detach();
|
|
323
|
+
};
|
|
324
|
+
}, [colliderStates, onDebugChange, rapier, rigidBodyStates, world]);
|
|
309
325
|
return null;
|
|
310
326
|
}
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
// The entry-declaration half
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
/**
|
|
331
|
+
* The bridge's slot. Module-scoped on purpose: the entry's `systems`
|
|
332
|
+
* declaration is static, the rapier world is not, and this slot is the seam
|
|
333
|
+
* between the two. Module scope here means one per PAGE, not one per game:
|
|
334
|
+
* this file is an ENGINE module, reached by bare specifier, so the editor's
|
|
335
|
+
* multi-instance Play (which stamps mount ids onto PROJECT-owned specifiers
|
|
336
|
+
* only — `server/project-module-instance.ts`) shares this one slot across
|
|
337
|
+
* every mounted instance. Two live instances of a rapier world therefore
|
|
338
|
+
* contend for it: the LAST bridge to mount wins every instance's declared
|
|
339
|
+
* `systems.physics`, and the loser's detach is dropped by the identity guard.
|
|
340
|
+
* {@link warnOnContendedMount} makes that state loud at the moment it
|
|
341
|
+
* happens; making the slot instance-scoped needs a per-mount rendezvous the
|
|
342
|
+
* context-free declaration (`rapierPhysicsSystem()` at project-module scope)
|
|
343
|
+
* does not yet have.
|
|
344
|
+
*
|
|
345
|
+
* Every member is an ACTION: each one either edits the sim or reads a live
|
|
346
|
+
* Rapier handle, and all of them arrive at gesture rate (a gizmo drag, an
|
|
347
|
+
* instrument command), never from a per-frame loop — so refusing by name is
|
|
348
|
+
* always the right answer, and a fabricated empty never is.
|
|
349
|
+
* `debugDraw`/`contactPoints` are OMITTED, not stubbed, for the anti-shim
|
|
350
|
+
* reason stated at the top of this file; a member the config does not list
|
|
351
|
+
* does not exist on the slot.
|
|
352
|
+
*/
|
|
353
|
+
let liveBridgeCount = 0;
|
|
354
|
+
/** The page-scope contention above, made LOUD instead of silent (never a
|
|
355
|
+
* silent degrade): a second concurrent bridge mount — a scene that mounted
|
|
356
|
+
* two, or a second Play instance of a rapier world — is reported the moment
|
|
357
|
+
* it happens, naming what breaks and until when. */
|
|
358
|
+
function warnOnContendedMount() {
|
|
359
|
+
liveBridgeCount += 1;
|
|
360
|
+
if (liveBridgeCount <= 1)
|
|
361
|
+
return;
|
|
362
|
+
// biome-ignore lint/suspicious/noConsole: the standing-warning channel for a capability the adapter cannot reach — the editor console surfaces it during play
|
|
363
|
+
console.warn(`[rapierPhysicsSystem] ${liveBridgeCount} <RapierPhysicsBridge> mounts are live on this ` +
|
|
364
|
+
'page, but the declared physics slot is one per page (engine modules are shared across ' +
|
|
365
|
+
"Play instances). Every instance's `systems.physics` now forwards to the LAST bridge " +
|
|
366
|
+
'mounted, so physics gizmo edits and instruments address that world — not necessarily ' +
|
|
367
|
+
'the one selected — until the extra mounts unmount.');
|
|
368
|
+
}
|
|
369
|
+
const physicsSlot = createSystemSlot({
|
|
370
|
+
name: 'rapierPhysicsSystem',
|
|
371
|
+
unattachedMessage: 'no <RapierPhysicsBridge> is mounted inside a live <Physics> right now — the world is not ' +
|
|
372
|
+
'playing, or the scene never mounted the bridge.',
|
|
373
|
+
members: {
|
|
374
|
+
ownerOf: { kind: 'action' },
|
|
375
|
+
freeze: { kind: 'action' },
|
|
376
|
+
commit: { kind: 'action' },
|
|
377
|
+
unfreeze: { kind: 'action' },
|
|
378
|
+
setDebugDrawEnabled: { kind: 'action' },
|
|
379
|
+
colliders: { kind: 'action' },
|
|
380
|
+
previewCollider: { kind: 'action' },
|
|
381
|
+
joints: { kind: 'action' },
|
|
382
|
+
previewJointAnchor: { kind: 'action' },
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
/**
|
|
386
|
+
* The `systems.physics` slot for a default-exported world — declare it from
|
|
387
|
+
* the entry (`export const systems = { physics: rapierPhysicsSystem() }`) and
|
|
388
|
+
* keep `<RapierPhysicsBridge />` mounted inside `<Physics>`. Every call
|
|
389
|
+
* forwards to the LIVE bridge adapter, so the static declaration answers with
|
|
390
|
+
* the running world's physics; while none is mounted, every call refuses by
|
|
391
|
+
* name rather than fabricating.
|
|
392
|
+
*/
|
|
393
|
+
export function rapierPhysicsSystem() {
|
|
394
|
+
return physicsSlot.slot;
|
|
395
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@vgai/engine",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.23",
|
|
6
6
|
"description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"game-engine",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$comment": "Curated by human/agent review, NOT auto-generated (unlike engine-api.json beside it). Every claim was verified against code — see each entry's `evidence`. Staleness is machine-checked at two granularities: `schemaDirHash` (sha256 over packages/engine/src/asset-formats/) and per-entry `evidenceHashes` (sha256 of each cited evidence FILE — catches drift in files outside the schema dir, which is where most entries' evidence lives; T5.5/§7.2).",
|
|
3
3
|
"_readme": "Read this before authoring scene JSON or engine code, to avoid the negatives below. Refresh flow after a schema-dir OR evidence-file change: `npx tsx scripts/capabilities-hash.ts --check` (fails naming the stale entry) -> re-review affected entries against current code -> `--write` to record fresh hashes. The pre-commit hook runs --check automatically. Each entry's `evidenceHashes` maps the repo-relative file path parsed off the front of each `evidence[]` citation (before any `:line-range` suffix) to a sha256 of that file's current content.",
|
|
4
4
|
"schemaDirPath": "packages/engine/src/asset-formats/",
|
|
5
|
-
"schemaDirHash": "
|
|
5
|
+
"schemaDirHash": "cc141166e16e49380581a0a80f014057b3cf31030df741ce41480a1e8dc8d3f6",
|
|
6
6
|
"reviewedAt": "2026-07-01",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
"status": "unsupported",
|
|
12
12
|
"claim": "No Continuous Collision Detection (CCD). Fast/small dynamic bodies can tunnel through thin colliders in one step.",
|
|
13
13
|
"evidence": [
|
|
14
|
-
"packages/engine/src/
|
|
14
|
+
"packages/engine/src/world3d-react/rapier-physics-bridge.tsx (the engine's one physics-facing module: observation/debug bridge only — it constructs no world, no bodies, and sets no ccd flag)"
|
|
15
15
|
],
|
|
16
|
-
"notes": "Grepped packages/engine/src for ccd/CCD/setCcdEnabled: zero matches. The engine constructs no rigid bodies
|
|
16
|
+
"notes": "Grepped packages/engine/src for ccd/CCD/setCcdEnabled: zero matches. The engine constructs no Rapier world or rigid bodies at all — physics lives in the game's own tree via @react-three/rapier (<Physics>/<RigidBody>), so CCD is a game choice: @react-three/rapier's `ccd` prop (or setCcdEnabled on a body ref) enables it per body with no engine involvement.",
|
|
17
17
|
"evidenceHashes": {
|
|
18
|
-
"packages/engine/src/
|
|
18
|
+
"packages/engine/src/world3d-react/rapier-physics-bridge.tsx": "03e8b28f7c60884a6fc9f00f488e6e157bc34287670c6cf723ee5e28e7924ce7"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
{
|
|
@@ -35,15 +35,17 @@
|
|
|
35
35
|
"id": "input-value-model",
|
|
36
36
|
"area": "input",
|
|
37
37
|
"status": "supported-with-caveats",
|
|
38
|
-
"claim": "F1 (spec §12) added a typed action-value model (digital/scalar/vector2/pointerDelta/pointerPosition, default digital) with real deadzone/combine rules and a valueType-mismatch throw. F2 ('Complete Device Backends') un-rejects mouse_move/gamepad_axis_pair, giving both real readers (mouse_move -> pointerDelta/vector2 off the real mouse-delta accumulator; gamepad_axis_pair -> vector2 off a coupled xAxis/yAxis stick, deadzone-rescaled like gamepad_axis), and adds touch_button/touch_stick (digital/vector2) via setTouchButton/setTouchStick. Real window focus (blur/focus) and pointer-lock state are tracked (requestPointerLock / exitPointerLock) and flush held REAL-device input on loss (#144: virtual/scheduled machine input gates on enabled only and survives blur); gamepad device identity (id/mapping) is exposed via getGamepadInfo/ActionValueSource.gamepadId. F4 adds getLastActiveDevice() + an optional getPrompt device param. Device-poll getters (isDeviceKeyPressed / isDeviceMouseButtonPressed / getDeviceMouseDelta / getDeviceMousePosition / hasDeviceGamepad / isDeviceGamepadButtonPressed / getDeviceGamepadAxis) read the same keysDown/mouseButtons/gamepads stores without inventing implicit actions, and return inert at READ time when inputActive is false. Caveat: pointerPosition still has no real-device backend (test_pointer_position injection only); scalar isn't fed by mouse_move (no axis-select field).",
|
|
38
|
+
"claim": "F1 (spec §12) added a typed action-value model (digital/scalar/vector2/pointerDelta/pointerPosition, default digital) with real deadzone/combine rules and a valueType-mismatch throw. F2 ('Complete Device Backends') un-rejects mouse_move/gamepad_axis_pair, giving both real readers (mouse_move -> pointerDelta/vector2 off the real mouse-delta accumulator; gamepad_axis_pair -> vector2 off a coupled xAxis/yAxis stick, deadzone-rescaled like gamepad_axis), and adds touch_button/touch_stick (digital/vector2) via setTouchButton/setTouchStick. Real window focus (blur/focus) and pointer-lock state are tracked (requestPointerLock / exitPointerLock) and flush held REAL-device input on loss (#144: virtual/scheduled machine input gates on enabled only and survives blur); gamepad device identity (id/mapping) is exposed via getGamepadInfo/ActionValueSource.gamepadId. F4 adds getLastActiveDevice() + an optional getPrompt device param. Device-poll getters (isDeviceKeyPressed / isDeviceMouseButtonPressed / getDeviceMouseDelta / getDeviceMousePosition / hasDeviceGamepad / isDeviceGamepadButtonPressed / getDeviceGamepadAxis) read the same keysDown/mouseButtons/gamepads stores without inventing implicit actions, and return inert at READ time when inputActive is false. getDeviceGamepadAxis is deliberately RAW — no deadzone: the engine's own stick deadzone is on the action path (gamepad_axis / gamepad_axis_pair, where a game declares and can override it), and a device-door caller that wants a stick applies its own engine's processor (unity-compat reproduces Unity's StickDeadzone at 0.125/0.925). The DOM edge those touch setters were written for now ships beside the manager as `input/host-pointer.ts` (`createHostPointer`): the host pointer stream resolved against a render surface, mapped into the game's own design space, with DENSE finger indices and a bounded queue, gated on the manager's own `isInputActive()` (published for exactly that, so a caller never recomposes `isEnabled() && isFocused()` into a looser gate). Caveat on that half: it reports fingers and does not press anything — the control layer (which rect owns which finger, how a knob becomes a stick vector) is the UI's, and no first-party game ships one. Caveat: pointerPosition still has no real-device backend (test_pointer_position injection only); scalar isn't fed by mouse_move (no axis-select field).",
|
|
39
39
|
"evidence": [
|
|
40
40
|
"packages/engine/src/input/input-types.ts:2-40 (InputBinding union incl. mouse_move/gamepad_axis_pair/touch_button/touch_stick/test_*)",
|
|
41
|
-
"packages/engine/src/input/input-manager.ts (digital/gating/typed getters/device-poll/inject*/setTouch*/getGamepadInfo/F4 getLastActiveDevice+getPrompt/requestPointerLock/exitPointerLock)"
|
|
41
|
+
"packages/engine/src/input/input-manager.ts (digital/gating/typed getters/device-poll/inject*/setTouch*/getGamepadInfo/F4 getLastActiveDevice+getPrompt/requestPointerLock/exitPointerLock)",
|
|
42
|
+
"packages/engine/src/input/host-pointer.ts (createHostPointer: surface/design-space mapping, dense finger indices, isInputActive gate)"
|
|
42
43
|
],
|
|
43
44
|
"notes": "gamepad disconnect/reconnect needs no special-cased code: poll() re-reads navigator.getGamepads() and every read skips a null slot, so a disconnected gamepad's actions go neutral with a correct isJustReleased edge for free — see input-device-backends-f2.test.ts.",
|
|
44
45
|
"evidenceHashes": {
|
|
45
|
-
"packages/engine/src/input/input-types.ts": "
|
|
46
|
-
"packages/engine/src/input/input-manager.ts": "
|
|
46
|
+
"packages/engine/src/input/input-types.ts": "b21ab448fc16d4881c900c57076ca1269241e1b23f8746fb8daa09c628f00f2b",
|
|
47
|
+
"packages/engine/src/input/input-manager.ts": "3cc14b5d3c5097bddfefbf1d620fc1ba8b0fb1d106191d9c640d94723330e888",
|
|
48
|
+
"packages/engine/src/input/host-pointer.ts": "aa012e3ddbcc236240e394614fb9e470e9ea18952faa848ffc1fa6597a2bdda9"
|
|
47
49
|
}
|
|
48
50
|
},
|
|
49
51
|
{
|
|
@@ -57,8 +59,8 @@
|
|
|
57
59
|
],
|
|
58
60
|
"notes": "This is a compatibility integration, not the canonical animation model: project-owned native controllers may drive AnimationMixer directly. When XState is deliberately used, parallel regions use distinct layer names; the binding filters native clip tracks, uses AnimationUtils.makeClipAdditive, can register with ctx.systems, and publishes format-neutral native runtime inspection until disposal.",
|
|
59
61
|
"evidenceHashes": {
|
|
60
|
-
"packages/engine/src/animation/xstate-animation-meta.ts": "
|
|
61
|
-
"packages/engine/src/animation/xstate-animation-binding.ts": "
|
|
62
|
+
"packages/engine/src/animation/xstate-animation-meta.ts": "d976f58b0bbd8b8159209083075c8860e02a59dd2475e55d9551794b06334035",
|
|
63
|
+
"packages/engine/src/animation/xstate-animation-binding.ts": "984db5596e51a2ea4e20e419f94fbb3af47c1abbadfaf80431bcd2ba4577032c"
|
|
62
64
|
}
|
|
63
65
|
},
|
|
64
66
|
{
|
|
@@ -73,7 +75,7 @@
|
|
|
73
75
|
"notes": "Limits: ARCHITECTURE-CORE.md. Any editor capability a mount does not reach stands warned per capability until it works.",
|
|
74
76
|
"evidenceHashes": {
|
|
75
77
|
"vendor/games/verify-unaltered.mjs": "257a362030a10f4d72a3280815069fbd83f436e1619391635b6f8df2ddd6c339",
|
|
76
|
-
"packages/engine/src/manifest/schema.ts": "
|
|
78
|
+
"packages/engine/src/manifest/schema.ts": "1f191ca4d77de85bc9ec4dbe4e5cc83c571dab017ff070ac0d5ee344ebec6fa2"
|
|
77
79
|
}
|
|
78
80
|
},
|
|
79
81
|
{
|
|
@@ -83,12 +85,12 @@
|
|
|
83
85
|
"claim": "Unmodified native-React DOM games run via the manifest `ingest-react` identity: vendored byte-for-byte (vendor/games/, zero-diff CI), mounted through the react world stack under the host React 19 (deduped). NO authoring in v1 (the editor never writes JSX back into an unmodified game, D-N4).",
|
|
84
86
|
"evidence": [
|
|
85
87
|
"vendor/games/verify-unaltered.mjs (vendored-bytes gate: zero UNRECORDED diff — byte-identical files, plus any recorded per-file patch that must reverse-apply to the pinned upstream)",
|
|
86
|
-
"packages/editor/src/
|
|
88
|
+
"packages/editor/src/roots/react-root.ts"
|
|
87
89
|
],
|
|
88
90
|
"notes": "Scope is native-DOM React ONLY (D-N1): R3F/@pixi/react games are canvas games and ride the three.js/pixi seams instead. Generality limit: ONE proven game (react-rpg — menu-to-dungeon e2e under React 19; both game-2 candidates rejected on evidence, R-N7). Game deps must be host-installed root deps (D-N6). Open R-N6: vendored react games' raw window listeners bypass the play-mode input gate.",
|
|
89
91
|
"evidenceHashes": {
|
|
90
92
|
"vendor/games/verify-unaltered.mjs": "257a362030a10f4d72a3280815069fbd83f436e1619391635b6f8df2ddd6c339",
|
|
91
|
-
"packages/editor/src/
|
|
93
|
+
"packages/editor/src/roots/react-root.ts": "68c54cd5426b4480f40e48f11e1445e85697796c280d4f88addbdcd21981cfbd"
|
|
92
94
|
}
|
|
93
95
|
},
|
|
94
96
|
{
|
|
@@ -104,7 +106,7 @@
|
|
|
104
106
|
"notes": "This file exists for negatives engine-api.json can't express (e.g. 'parses but does nothing'). For the full positive surface, read engine-api.json/.md — regenerated by `npm run generate-engine-manifest`, can't drift.",
|
|
105
107
|
"evidenceHashes": {
|
|
106
108
|
"packages/engine/schemas/engine-api.json": "cdfd395fab88575a13c74451051e6c86f8b07980152120f00caa62098ecf7917",
|
|
107
|
-
"packages/engine/src/animation/xstate-animation-binding.ts": "
|
|
109
|
+
"packages/engine/src/animation/xstate-animation-binding.ts": "984db5596e51a2ea4e20e419f94fbb3af47c1abbadfaf80431bcd2ba4577032c",
|
|
108
110
|
"packages/engine/src/animation/blend-node.ts": "9e1d2018acf138bb984fd0d2c7ff1045ef00689e613abf531453af0a1ba06b2e"
|
|
109
111
|
}
|
|
110
112
|
}
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"required": [
|
|
36
36
|
"version"
|
|
37
37
|
],
|
|
38
|
+
"additionalProperties": false,
|
|
38
39
|
"description": "Engine version pin"
|
|
39
40
|
},
|
|
40
41
|
"roots": {
|
|
@@ -138,24 +139,6 @@
|
|
|
138
139
|
"captureTimeoutMs": {
|
|
139
140
|
"description": "How long the host waits for this game's first captured frame before the mount FAILS by name. A game that needs a long boot (streamed world assets, a menu the player must clear) raises it; the default is 10s.",
|
|
140
141
|
"type": "number"
|
|
141
|
-
},
|
|
142
|
-
"world": {
|
|
143
|
-
"description": "The component this root's WORLD is, in the game's own source — a three root whose scene is a React/R3F component tree names it here. Two readers, and they mount different things: the editor mounts THIS component alone as the root's design-time document (a still scene — its frameloop runs ONE bounded settle at mount, so physics-owned bodies rest at their authored spawn, and is then frozen forever; nothing moves in Edit), while `entry` above stays what PLAY mounts (the whole game, its own composition, its own HUD and loop). Declaring it is what lets a scene be a scene in Edit rather than the running game's mount surfaces. A game whose world is not a component omits it and keeps mounting through `entry` in both modes.",
|
|
144
|
-
"type": "object",
|
|
145
|
-
"properties": {
|
|
146
|
-
"entry": {
|
|
147
|
-
"type": "string",
|
|
148
|
-
"description": "Project-relative path to the game's OWN module exporting that component"
|
|
149
|
-
},
|
|
150
|
-
"export": {
|
|
151
|
-
"description": "Named export to read from that module; omit when the module default-exports it",
|
|
152
|
-
"type": "string"
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
"required": [
|
|
156
|
-
"entry"
|
|
157
|
-
],
|
|
158
|
-
"additionalProperties": false
|
|
159
142
|
}
|
|
160
143
|
},
|
|
161
144
|
"additionalProperties": false,
|
|
@@ -181,6 +164,24 @@
|
|
|
181
164
|
"type": "string"
|
|
182
165
|
}
|
|
183
166
|
},
|
|
167
|
+
"world": {
|
|
168
|
+
"description": "The component this root's WORLD is, in the game's own source. Two readers, and they mount different things: the EDITOR mounts THIS component alone as the root's design-time document, while `entry` above stays what PLAY mounts (the whole game — its own composition, its own HUD, its own loop). It sits on the ROOT rather than inside any one adapter because the split it names is not about ingest: a game whose runtime entry is a SHELL over the thing an author actually edits has this shape whether the source is vendored or first-party. Two shipped cases, one field: an ingested game whose `entry` mounts game+HUD+loop while the named export is the world alone; and a translated Unity/Godot port, whose `entry` is a scene HOST that mounts the scene the built player starts on (`EditorBuildSettings` index 0 — routinely a menu with no 3D content at all) while the named export opens the scene an author edits. That is Unity's own split: build settings say where the PLAYER starts; the Scene view opens a scene, and Unity keeps that fact somewhere else entirely. A game whose world is not a separate component omits this and keeps mounting through `entry` in both modes.",
|
|
169
|
+
"type": "object",
|
|
170
|
+
"properties": {
|
|
171
|
+
"entry": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"description": "Project-relative path to the game's OWN module exporting that component"
|
|
174
|
+
},
|
|
175
|
+
"export": {
|
|
176
|
+
"description": "Named export to read from that module; omit when the module default-exports it",
|
|
177
|
+
"type": "string"
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"required": [
|
|
181
|
+
"entry"
|
|
182
|
+
],
|
|
183
|
+
"additionalProperties": false
|
|
184
|
+
},
|
|
184
185
|
"zOrder": {
|
|
185
186
|
"default": 0,
|
|
186
187
|
"description": "Canvas stacking order; ties broken by array order",
|
|
@@ -256,12 +257,15 @@
|
|
|
256
257
|
"id",
|
|
257
258
|
"label",
|
|
258
259
|
"roots"
|
|
259
|
-
]
|
|
260
|
+
],
|
|
261
|
+
"additionalProperties": false
|
|
260
262
|
}
|
|
261
263
|
}
|
|
262
|
-
}
|
|
264
|
+
},
|
|
265
|
+
"additionalProperties": false
|
|
263
266
|
}
|
|
264
|
-
}
|
|
267
|
+
},
|
|
268
|
+
"additionalProperties": false
|
|
265
269
|
},
|
|
266
270
|
"server": {
|
|
267
271
|
"description": "Multiplayer server config (Colyseus), if this game registers a room",
|
|
@@ -305,7 +309,8 @@
|
|
|
305
309
|
},
|
|
306
310
|
"required": [
|
|
307
311
|
"room"
|
|
308
|
-
]
|
|
312
|
+
],
|
|
313
|
+
"additionalProperties": false
|
|
309
314
|
},
|
|
310
315
|
"resolution": {
|
|
311
316
|
"description": "Canvas resolution",
|
|
@@ -323,7 +328,8 @@
|
|
|
323
328
|
"required": [
|
|
324
329
|
"width",
|
|
325
330
|
"height"
|
|
326
|
-
]
|
|
331
|
+
],
|
|
332
|
+
"additionalProperties": false
|
|
327
333
|
},
|
|
328
334
|
"rendering": {
|
|
329
335
|
"description": "Construction-time renderer properties, which no world can declare after the fact. Everything a world CAN declare (tone mapping, output colour space, clear colour, shadow filter) lives on the world's own renderer config instead.",
|
|
@@ -350,7 +356,8 @@
|
|
|
350
356
|
},
|
|
351
357
|
"required": [
|
|
352
358
|
"allowInProduction"
|
|
353
|
-
]
|
|
359
|
+
],
|
|
360
|
+
"additionalProperties": false
|
|
354
361
|
},
|
|
355
362
|
"determinism": {
|
|
356
363
|
"description": "Seeded-RNG determinism contract (D15) — governs whether ctx.random is boot-seeded from a reproducible seed and whether the gameplay-rng-ban scan / dev-mode Math.random phase trap are active for this project.",
|
|
@@ -369,7 +376,8 @@
|
|
|
369
376
|
},
|
|
370
377
|
"required": [
|
|
371
378
|
"seededRandom"
|
|
372
|
-
]
|
|
379
|
+
],
|
|
380
|
+
"additionalProperties": false
|
|
373
381
|
},
|
|
374
382
|
"budget": {
|
|
375
383
|
"description": "Performance budget (W3d, F11 perf regression gates) asserted by `vgai perf --assert-budget`: a seeded, fixed-step headless run samples the engine profiler and fails (nonzero exit) when any metric here is exceeded. Only headlessly-measurable metrics are budgetable; real GPU frame time is not (SwiftShader).",
|
|
@@ -403,7 +411,8 @@
|
|
|
403
411
|
"exclusiveMinimum": 0,
|
|
404
412
|
"maximum": 9007199254740991
|
|
405
413
|
}
|
|
406
|
-
}
|
|
414
|
+
},
|
|
415
|
+
"additionalProperties": false
|
|
407
416
|
},
|
|
408
417
|
"learn": {
|
|
409
418
|
"description": "Optional learner-facing metadata (FT-5) — set on shipped example projects so the gallery/wizard/Learn-catalog registries can be generated from the manifest itself",
|
|
@@ -325,10 +325,8 @@ const AdapterRegionOverlaySchema = z
|
|
|
325
325
|
* the full REPLACEMENT, and its entries carry `include`/`mounts` themselves.
|
|
326
326
|
* Two homes for one fact is how a declaration goes stale in one of them.
|
|
327
327
|
*
|
|
328
|
-
* MEASURED BASIS
|
|
329
|
-
*
|
|
330
|
-
* without restating every region — which is what blocked the source-text R3F
|
|
331
|
-
* floor's retirement across the five shipped 3D projects.
|
|
328
|
+
* MEASURED BASIS: a replace-only region binding means no `nativeAdapter()`
|
|
329
|
+
* project can declare a single include without restating every region.
|
|
332
330
|
*/
|
|
333
331
|
const RegionIncludesSchema = z
|
|
334
332
|
.record(z.string().min(1), AdapterRegionOverlaySchema)
|
|
@@ -18,7 +18,7 @@ export type AdapterSurface = 'three' | 'canvas' | 'dom';
|
|
|
18
18
|
* site, not silently contribute nothing or silently default to an existing
|
|
19
19
|
* kind). Lives here — colocated with the kind vocabulary itself, not in a
|
|
20
20
|
* generic util module — so the three call sites that need it
|
|
21
|
-
* (`runtime/create-runtime.ts`'s mount loop, `editor/
|
|
21
|
+
* (`runtime/create-runtime.ts`'s mount loop, `editor/binding-resolver.ts`'s
|
|
22
22
|
* `resolveAllRoots`, `editor/play-mode.ts`'s `installMultiRootAuthoring`)
|
|
23
23
|
* import it from the same leaf module that defines `AdapterSurface`, keeping the
|
|
24
24
|
* type and its guard from drifting apart. The `never` parameter is the
|