@vgai/engine 0.5.2 → 0.5.3
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 +18 -11
- package/package.json +10 -6
- package/schemas/engine-api.json +1 -68
- package/schemas/engine-api.md +1 -32
- package/schemas/engine-capabilities.json +30 -42
- package/schemas/{vgai-game.schema.json → vgai-project.schema.json} +52 -34
- package/src/adapter/adapter-surface.ts +5 -5
- package/src/adapter/authoring.ts +168 -226
- package/src/adapter/colyseus-networking-adapter.ts +44 -5
- package/src/adapter/first-party-systems.ts +156 -42
- package/src/adapter/host-context.ts +177 -46
- package/src/adapter/index.ts +47 -51
- package/src/adapter/ingest/game-contract.ts +2 -2
- package/src/adapter/ingest/scene-capture.ts +18 -19
- package/src/adapter/ingest/structural-ids.ts +127 -0
- package/src/adapter/ingest/upstream-pin.ts +9 -12
- package/src/adapter/loop-gate-report.ts +11 -11
- package/src/adapter/rapier-physics-adapter.ts +27 -9
- package/src/adapter/root-adapter.ts +217 -0
- package/src/adapter/{vgai-scene-game-adapter.ts → setup-three-root-adapter.ts} +173 -351
- package/src/adapter/system-adapter.ts +80 -63
- package/src/ai/navigation.ts +1 -1
- package/src/animation/animation-clock.ts +1 -1
- package/src/animation/camera-ownership.ts +1 -2
- package/src/animation/cubic-spline-interpolant.ts +132 -0
- package/src/animation/theatre-clock-binding.ts +2 -2
- package/src/animation/theatre-object-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +75 -5
- package/src/{scene/schema → asset-formats}/camera.ts +2 -5
- package/src/{scene/schema → asset-formats}/collider.ts +2 -5
- package/src/asset-formats/index.ts +54 -0
- package/src/{scene/schema → asset-formats}/instances.ts +10 -6
- package/src/{scene/schema → asset-formats}/light.ts +3 -6
- package/src/{scene/schema → asset-formats}/material.ts +4 -7
- package/src/{scene/schema → asset-formats}/mesh.ts +4 -7
- package/src/asset-formats/parse.ts +39 -0
- package/src/{scene/schema → asset-formats}/particles.ts +3 -6
- package/src/{scene/schema/environment.ts → asset-formats/render-env.ts} +23 -86
- package/src/{scene/schema → asset-formats}/tuples.ts +1 -1
- package/src/{scene/asset-loaders.ts → asset-loaders.ts} +8 -9
- package/src/asset-parse-error.ts +33 -0
- package/src/{scene/asset-registry.ts → asset-registry.ts} +1 -1
- package/src/assets.ts +1 -1
- package/src/audio/wav-encode.ts +9 -9
- package/src/canvas-react/engine-bridge.ts +59 -0
- package/src/canvas-react/index.ts +50 -0
- package/src/canvas-react/pixi-primitive.tsx +202 -0
- package/src/canvas-react/pixi-react-adapter.tsx +290 -0
- package/src/canvas-react/pixi-react-root-factory.tsx +88 -0
- package/src/canvas-react/world-context.ts +328 -0
- package/src/core/frame-pacing.ts +100 -0
- package/src/core/game-loop.ts +50 -28
- package/src/core/seeded-random.ts +7 -7
- package/src/core/sim-clock.ts +388 -0
- package/src/core/system-runner.ts +17 -63
- package/src/core/types.ts +34 -15
- package/src/data/data-asset.ts +3 -3
- package/src/data/data-check-core.ts +6 -7
- package/src/data/data-ref.ts +11 -11
- package/src/data/vite-plugin-data.ts +10 -10
- package/src/{scene/defaults.ts → defaults.ts} +18 -40
- package/src/dev/render-debug-adapter.ts +1 -1
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/ecs/scene-index.ts +439 -0
- package/src/ecs/scene-query.ts +43 -0
- package/src/{scene → ecs}/user-data.ts +17 -36
- package/src/index.ts +7 -9
- package/src/input/input-manager.ts +29 -32
- package/src/input/input-types.ts +2 -2
- package/src/input/schema.ts +5 -5
- package/src/loader.ts +57 -0
- package/src/manifest/editor-port.ts +69 -0
- package/src/manifest/filename.ts +49 -0
- package/src/manifest/index.ts +8 -2
- package/src/manifest/load-file.ts +11 -0
- package/src/manifest/load.ts +65 -77
- package/src/manifest/locate.ts +55 -0
- package/src/manifest/schema.ts +400 -233
- package/src/{scene → physics}/collider-dimensions.ts +3 -3
- package/src/physics/physics-registry.ts +1 -1
- package/src/{world2d/authoring-2d.ts → pixi/authoring.ts} +24 -11
- package/src/pixi/index.ts +43 -0
- package/src/{world2d/ingest-iframe-2d.ts → pixi/ingest-iframe.ts} +9 -9
- package/src/{world2d/ingest2d.ts → pixi/ingest.ts} +28 -28
- package/src/{world2d/physics2d-registry.ts → pixi/physics-registry.ts} +1 -1
- package/src/{world2d/scene-capture-2d.ts → pixi/scene-capture.ts} +3 -3
- package/src/{world2d/system-adapters-2d.ts → pixi/system-adapters.ts} +2 -2
- package/src/react/unmanaged-root-detector.ts +26 -1
- package/src/react/use-data.ts +5 -5
- package/src/react/use-selection.tsx +15 -42
- package/src/react/{game-state.tsx → world-state.tsx} +44 -46
- package/src/render/auto-batcher.ts +1 -2
- package/src/{scene → render}/instance-mesh.ts +1 -1
- package/src/{scene → render}/light-camera-factory.ts +14 -13
- package/src/render/lod.ts +17 -0
- package/src/{scene → render}/material-factory.ts +8 -7
- package/src/{scene → render}/particles-factory.ts +62 -12
- package/src/render/render-batch-system.ts +14 -41
- package/src/render/render-features.ts +1 -1
- package/src/render/render-settings.ts +1 -2
- package/src/render/spark-renderer-lifecycle.ts +1 -1
- package/src/runtime/create-runtime.ts +352 -463
- package/src/runtime/debug-bridge.ts +148 -93
- package/src/runtime/debug-registry.ts +79 -54
- package/src/runtime/dev-layers.ts +40 -0
- package/src/runtime/frame-selector-cache.ts +4 -4
- package/src/runtime/game.ts +618 -369
- package/src/runtime/gameplay-rng-trap.ts +6 -7
- package/src/runtime/input-router.ts +11 -11
- package/src/runtime/mount-game.ts +54 -55
- package/src/runtime/mount-manifest.ts +154 -150
- package/src/runtime/presentation.ts +141 -0
- package/src/runtime/render-audio-control.ts +64 -53
- package/src/runtime/render-control.ts +45 -51
- package/src/runtime/render-seed.ts +3 -4
- package/src/runtime/state-bridge.ts +17 -18
- package/src/runtime/types.ts +94 -61
- package/src/setup/setup-renderer.ts +11 -6
- package/src/world3d-react/engine-bridge.ts +46 -33
- package/src/world3d-react/index.ts +31 -26
- package/src/world3d-react/r3f-adapter.tsx +211 -96
- package/src/world3d-react/r3f-root-factory.tsx +91 -0
- package/src/world3d-react/renderer-config.ts +137 -0
- package/src/world3d-react/world-context.ts +141 -111
- package/schemas/entity2d.schema.json +0 -468
- package/schemas/prefab.schema.json +0 -9992
- package/schemas/scn2d.schema.json +0 -494
- package/schemas/vscn.schema.json +0 -10851
- package/src/adapter/game-adapter.ts +0 -164
- package/src/adapter/ingest/overlay-applier.ts +0 -207
- package/src/adapter/ingest/overlay-apply.ts +0 -168
- package/src/adapter/ingest/overlay-file.ts +0 -126
- package/src/adapter/ingest/overlay-report.ts +0 -176
- package/src/animation/gsap-registration.ts +0 -184
- package/src/audio/audio-introspection.ts +0 -290
- package/src/audio/index.ts +0 -39
- package/src/audio/tone-clock-binding.ts +0 -98
- package/src/audio/tone-context.ts +0 -175
- package/src/audio/tone-offline-render.ts +0 -167
- package/src/ecs/component-manager.ts +0 -814
- package/src/ecs/game-component.ts +0 -260
- package/src/ecs/hmr-swap-report.ts +0 -65
- package/src/physics/trigger-dispatch.ts +0 -97
- package/src/react/root-adapter.tsx +0 -49
- package/src/scene/asset-paths.ts +0 -121
- package/src/scene/asset-ref-check.ts +0 -248
- package/src/scene/component-registry.ts +0 -51
- package/src/scene/parse.ts +0 -204
- package/src/scene/scene-apply.ts +0 -407
- package/src/scene/scene-diff-schema.ts +0 -115
- package/src/scene/scene-diff-types.ts +0 -29
- package/src/scene/scene-loader.ts +0 -1526
- package/src/scene/scene-query.ts +0 -63
- package/src/scene/scene-types.ts +0 -33
- package/src/scene/scene-version.ts +0 -40
- package/src/scene/schema/animation.ts +0 -46
- package/src/scene/schema/audio.ts +0 -25
- package/src/scene/schema/entity-ref.ts +0 -78
- package/src/scene/schema/entity.ts +0 -189
- package/src/scene/schema/index.ts +0 -51
- package/src/scene/schema/joint.ts +0 -26
- package/src/scene/schema/physics.ts +0 -49
- package/src/scene/schema/scene-file.ts +0 -292
- package/src/scene/schema/shadow.ts +0 -24
- package/src/scene/schema/spline.ts +0 -21
- package/src/world2d/asset-paths2d.ts +0 -44
- package/src/world2d/capture-to-scene2d.ts +0 -52
- package/src/world2d/collision-2d.ts +0 -99
- package/src/world2d/entity2d-asset.ts +0 -22
- package/src/world2d/index.ts +0 -91
- package/src/world2d/physics2d-transform.ts +0 -173
- package/src/world2d/physics2d-units.ts +0 -10
- package/src/world2d/pixi-game-adapter.ts +0 -439
- package/src/world2d/pixi-surface.ts +0 -78
- package/src/world2d/scene2d-identity.ts +0 -49
- package/src/world2d/scene2d-loader.ts +0 -433
- package/src/world2d/schema/entity2d.ts +0 -163
- package/src/world2d/schema/physics2d.ts +0 -64
- package/src/world2d/schema/sprite.ts +0 -99
- package/src/world2d/schema/tilemap.ts +0 -39
- package/src/world2d/schema/tuples2d.ts +0 -25
- package/src/world2d/transform-writer-2d.ts +0 -42
- package/src/world2d/types.ts +0 -74
- package/src/world3d-react/behavior.tsx +0 -146
- /package/src/{scene → render}/mesh-shadow.ts +0 -0
package/README.md
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
# @vgai/engine
|
|
2
2
|
|
|
3
|
-
The vgai game engine: the game loop and phase scheduler
|
|
4
|
-
|
|
5
|
-
tree), Rapier physics integration, the
|
|
6
|
-
Zod schemas
|
|
3
|
+
The vgai game engine: the game loop and phase scheduler (the world node
|
|
4
|
+
*is* the entity — no ECS, no mirror
|
|
5
|
+
tree), Rapier physics integration, the manifest and asset-format loaders
|
|
6
|
+
with their Zod schemas, animation
|
|
7
7
|
(native `THREE.AnimationMixer` actions bound to XState state machines via
|
|
8
8
|
`bindXStateAnimation` — see `src/animation/xstate-animation-binding.ts`),
|
|
9
9
|
audio, input, the adapter seam
|
|
10
10
|
(`src/adapter/` — how the editor hosts non-native and unmodified games),
|
|
11
|
-
and the
|
|
11
|
+
and the Pixi substrate for canvas-surface games (`src/pixi/`).
|
|
12
|
+
|
|
13
|
+
A world is authored as TSX/R3F **source**, and the source is the
|
|
14
|
+
document — there is no scene file and no schema for one; the manifest
|
|
15
|
+
schema is strict, so an unrecognized key is rejected loudly rather than
|
|
16
|
+
ignored.
|
|
12
17
|
|
|
13
18
|
**License: Apache-2.0** (see [LICENSE](./LICENSE)). Your game code that
|
|
14
19
|
imports this package carries no copyleft obligations.
|
|
@@ -38,10 +43,9 @@ The bare package exposes the small game/setup surface:
|
|
|
38
43
|
|
|
39
44
|
```ts
|
|
40
45
|
import {
|
|
41
|
-
GameComponent,
|
|
42
46
|
fromSetup,
|
|
43
47
|
loadGameManifest,
|
|
44
|
-
|
|
48
|
+
mountManifestRoots,
|
|
45
49
|
type GameContext,
|
|
46
50
|
} from '@vgai/engine';
|
|
47
51
|
```
|
|
@@ -60,11 +64,14 @@ control integration is used.
|
|
|
60
64
|
|
|
61
65
|
- Public Learn/manual: https://vgai-learn.pages.dev
|
|
62
66
|
- Hosted editor: https://vgai-editor.pages.dev
|
|
63
|
-
- In a source checkout, read
|
|
67
|
+
- In a source checkout, read `docs/ARCHITECTURE-CORE.md` first—the target
|
|
64
68
|
architecture and current implementation are marked separately.
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
-
|
|
69
|
+
- Schemas, in two homes (every field `.describe()`d; `npm run
|
|
70
|
+
generate-schema` emits JSON Schema for autocomplete):
|
|
71
|
+
`src/asset-formats/` (the fetched assets the render
|
|
72
|
+
path reads: `.mat.json`, `.inputmap.json`, meshes, lights, cameras,
|
|
73
|
+
colliders, particles, render-env), and `src/manifest/schema.ts`
|
|
74
|
+
(`vgai.project.json`).
|
|
68
75
|
- The adapter contracts: `src/adapter/`.
|
|
69
76
|
- Source-checkout tests are headless with real dependencies: `npm run
|
|
70
77
|
test:unit` from the repository root.
|
package/package.json
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
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.3",
|
|
6
6
|
"description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
|
|
7
|
-
"keywords": [
|
|
7
|
+
"keywords": [
|
|
8
|
+
"game-engine",
|
|
9
|
+
"three.js",
|
|
10
|
+
"pixijs",
|
|
11
|
+
"react",
|
|
12
|
+
"ai-agents"
|
|
13
|
+
],
|
|
8
14
|
"type": "module",
|
|
9
15
|
"homepage": "https://vgai-learn.pages.dev",
|
|
10
16
|
"repository": {
|
|
@@ -35,11 +41,9 @@
|
|
|
35
41
|
"dependencies": {
|
|
36
42
|
"@dimforge/rapier2d-compat": "^0.14.0",
|
|
37
43
|
"@dimforge/rapier3d-compat": "^0.14.0",
|
|
38
|
-
"@pixi/
|
|
44
|
+
"@pixi/react": "8.0.5",
|
|
39
45
|
"@recast-navigation/three": "^0.43.0",
|
|
40
46
|
"@sparkjsdev/spark": "^2.1.0",
|
|
41
|
-
"@theatre/core": "0.7.2",
|
|
42
|
-
"gsap": "^3.12.0",
|
|
43
47
|
"loglayer": "^9.1.0",
|
|
44
48
|
"n8ao": "^1.10.1",
|
|
45
49
|
"pixi.js": "^8.6.0",
|
|
@@ -47,7 +51,6 @@
|
|
|
47
51
|
"recast-navigation": "^0.43.0",
|
|
48
52
|
"three": "^0.180.0",
|
|
49
53
|
"three.quarks": "^0.16.0",
|
|
50
|
-
"tone": "^15.1.22",
|
|
51
54
|
"xstate": "^5.32.4",
|
|
52
55
|
"zod": "^4.3.6"
|
|
53
56
|
},
|
|
@@ -68,6 +71,7 @@
|
|
|
68
71
|
}
|
|
69
72
|
},
|
|
70
73
|
"devDependencies": {
|
|
74
|
+
"@theatre/core": "0.7.2",
|
|
71
75
|
"@types/react": "^19.2.14",
|
|
72
76
|
"@types/react-dom": "^19.2.3",
|
|
73
77
|
"react": "^19.2.4",
|
package/schemas/engine-api.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "gameLogic",
|
|
22
|
-
"description": "General gameplay behavior.
|
|
22
|
+
"description": "General gameplay behavior."
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"name": "animation",
|
|
@@ -53,72 +53,5 @@
|
|
|
53
53
|
"ANIMATION": "animation",
|
|
54
54
|
"PRE_RENDER": "preRender",
|
|
55
55
|
"RENDER": "render"
|
|
56
|
-
},
|
|
57
|
-
"gameComponent": {
|
|
58
|
-
"description": "Base class for game components attached to entities. The Three.js Object3D IS the entity — there is no separate entity id.",
|
|
59
|
-
"defaultPhase": "gameLogic",
|
|
60
|
-
"statics": [
|
|
61
|
-
{
|
|
62
|
-
"name": "phase",
|
|
63
|
-
"type": "SystemPhaseName",
|
|
64
|
-
"default": "gameLogic",
|
|
65
|
-
"description": "Which phase this component's update runs in. Override in subclasses."
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"name": "schema",
|
|
69
|
-
"type": "z.ZodObject | undefined",
|
|
70
|
-
"default": null,
|
|
71
|
-
"description": "Optional Zod object schema for authored fields. When set, scene data is validated + defaulted through `schema.parse(data)` before assignment."
|
|
72
|
-
}
|
|
73
|
-
],
|
|
74
|
-
"fields": [
|
|
75
|
-
{
|
|
76
|
-
"name": "object3D",
|
|
77
|
-
"type": "THREE.Object3D",
|
|
78
|
-
"description": "The Object3D this component is attached to. The Object3D IS the entity."
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"name": "rigidBody",
|
|
82
|
-
"type": "RAPIER.RigidBody | null",
|
|
83
|
-
"description": "The entity's Rapier rigid body, if it has physics."
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
"name": "collider",
|
|
87
|
-
"type": "RAPIER.Collider | null",
|
|
88
|
-
"description": "The entity's Rapier collider, if it has physics."
|
|
89
|
-
}
|
|
90
|
-
],
|
|
91
|
-
"hooks": [
|
|
92
|
-
{
|
|
93
|
-
"name": "init",
|
|
94
|
-
"optional": true,
|
|
95
|
-
"signature": "init?(ctx: GameContext): void | Promise<void>",
|
|
96
|
-
"description": "Called once after the entity is fully constructed (object3D, physics, etc.)."
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"name": "update",
|
|
100
|
-
"optional": false,
|
|
101
|
-
"signature": "update(dt: number, ctx: GameContext): void",
|
|
102
|
-
"description": "Called every fixed timestep, in the component's phase. Required (abstract)."
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"name": "dispose",
|
|
106
|
-
"optional": true,
|
|
107
|
-
"signature": "dispose?(ctx: GameContext): void",
|
|
108
|
-
"description": "Called when the entity is destroyed or the component is removed."
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
"name": "onTriggerEnter",
|
|
112
|
-
"optional": true,
|
|
113
|
-
"signature": "onTriggerEnter?(other: THREE.Object3D, ctx: GameContext): void",
|
|
114
|
-
"description": "Called when a sensor overlap with `other` begins."
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"name": "onTriggerExit",
|
|
118
|
-
"optional": true,
|
|
119
|
-
"signature": "onTriggerExit?(other: THREE.Object3D, ctx: GameContext): void",
|
|
120
|
-
"description": "Called when a sensor overlap with `other` ends."
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
56
|
}
|
|
124
57
|
}
|
package/schemas/engine-api.md
CHANGED
|
@@ -15,39 +15,8 @@ Each frame, systems run in this exact order (from `PHASE_ORDER` in `packages/eng
|
|
|
15
15
|
| 2 | `prePhysics` | `SystemPhase.PRE_PHYSICS` | Apply forces/intents to rigid bodies before the physics step. |
|
|
16
16
|
| 3 | `physics` | `SystemPhase.PHYSICS` | Step the Rapier physics world (fixed timestep). |
|
|
17
17
|
| 4 | `postPhysics` | `SystemPhase.POST_PHYSICS` | Read physics results back; sync transforms from bodies to Object3D. |
|
|
18
|
-
| 5 | `gameLogic` | `SystemPhase.GAME_LOGIC` | General gameplay behavior.
|
|
18
|
+
| 5 | `gameLogic` | `SystemPhase.GAME_LOGIC` | General gameplay behavior. |
|
|
19
19
|
| 6 | `animation` | `SystemPhase.ANIMATION` | Advance animation graphs / mixers. |
|
|
20
20
|
| 7 | `preRender` | `SystemPhase.PRE_RENDER` | Final per-frame fixups (cameras, HUD state) before drawing. |
|
|
21
21
|
| 8 | `render` | `SystemPhase.RENDER` | Draw the frame. |
|
|
22
22
|
|
|
23
|
-
## GameComponent Contract (v2)
|
|
24
|
-
|
|
25
|
-
Base class for game components attached to entities. The Three.js Object3D IS the entity — there is no separate entity id.
|
|
26
|
-
|
|
27
|
-
Default phase: `gameLogic`
|
|
28
|
-
|
|
29
|
-
### Static members
|
|
30
|
-
|
|
31
|
-
| Member | Type | Default | Description |
|
|
32
|
-
| ------ | ---- | ------- | ----------- |
|
|
33
|
-
| `phase` | `SystemPhaseName` | `"gameLogic"` | Which phase this component's update runs in. Override in subclasses. |
|
|
34
|
-
| `schema` | `z.ZodObject | undefined` | `null` | Optional Zod object schema for authored fields. When set, scene data is validated + defaulted through `schema.parse(data)` before assignment. |
|
|
35
|
-
|
|
36
|
-
### Instance fields (wired by ComponentManager)
|
|
37
|
-
|
|
38
|
-
| Field | Type | Description |
|
|
39
|
-
| ----- | ---- | ----------- |
|
|
40
|
-
| `object3D` | `THREE.Object3D` | The Object3D this component is attached to. The Object3D IS the entity. |
|
|
41
|
-
| `rigidBody` | `RAPIER.RigidBody | null` | The entity's Rapier rigid body, if it has physics. |
|
|
42
|
-
| `collider` | `RAPIER.Collider | null` | The entity's Rapier collider, if it has physics. |
|
|
43
|
-
|
|
44
|
-
### Lifecycle / hooks
|
|
45
|
-
|
|
46
|
-
| Hook | Required | Signature | Description |
|
|
47
|
-
| ---- | -------- | --------- | ----------- |
|
|
48
|
-
| `init` | no | `init?(ctx: GameContext): void | Promise<void>` | Called once after the entity is fully constructed (object3D, physics, etc.). |
|
|
49
|
-
| `update` | yes | `update(dt: number, ctx: GameContext): void` | Called every fixed timestep, in the component's phase. Required (abstract). |
|
|
50
|
-
| `dispose` | no | `dispose?(ctx: GameContext): void` | Called when the entity is destroyed or the component is removed. |
|
|
51
|
-
| `onTriggerEnter` | no | `onTriggerEnter?(other: THREE.Object3D, ctx: GameContext): void` | Called when a sensor overlap with `other` begins. |
|
|
52
|
-
| `onTriggerExit` | no | `onTriggerExit?(other: THREE.Object3D, ctx: GameContext): void` | Called when a sensor overlap with `other` ends. |
|
|
53
|
-
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
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/
|
|
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
|
-
"schemaDirPath": "packages/engine/src/
|
|
5
|
-
"schemaDirHash": "
|
|
4
|
+
"schemaDirPath": "packages/engine/src/asset-formats/",
|
|
5
|
+
"schemaDirHash": "3f8728d85cc14c17acbdababa414b51f40d73cee30434e83cf23b01518db06a6",
|
|
6
6
|
"reviewedAt": "2026-07-01",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -11,13 +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/
|
|
15
|
-
"packages/engine/src/world2d/scene2d-loader.ts:168-179 (2D bodyDesc: same four stock constructors)"
|
|
14
|
+
"packages/engine/src/adapter/setup-three-root-adapter.ts (the Rapier home: world init + step; no ccd flag is ever set)"
|
|
16
15
|
],
|
|
17
|
-
"notes": "Grepped packages/engine/src for ccd/CCD/setCcdEnabled: zero matches
|
|
16
|
+
"notes": "Grepped packages/engine/src for ccd/CCD/setCcdEnabled: zero matches. The engine constructs no rigid bodies itself — games build their own descs via ctx.RAPIER — and nothing engine-side ever enables ccd. Workaround: keep dynamic-body per-step travel small relative to the thinnest collider, or add intermediate colliders; a game may also call setCcdEnabled on its own bodies directly.",
|
|
18
17
|
"evidenceHashes": {
|
|
19
|
-
"packages/engine/src/
|
|
20
|
-
"packages/engine/src/world2d/scene2d-loader.ts": "f0f74eefd9cce16bbe7826225a52f2b0407ab141641dcdd370bcff1d091bce69"
|
|
18
|
+
"packages/engine/src/adapter/setup-three-root-adapter.ts": "fd3035e557db23e002e52707145a6e17bb3ad15767da74ec8fbafd40879d1d34"
|
|
21
19
|
}
|
|
22
20
|
},
|
|
23
21
|
{
|
|
@@ -26,13 +24,11 @@
|
|
|
26
24
|
"status": "unsupported",
|
|
27
25
|
"claim": "No enforced limit on entity or physics-body count; practical envelope is untested.",
|
|
28
26
|
"evidence": [
|
|
29
|
-
"packages/engine/src/setup/setup-physics.ts (RAPIER.World construction: no capacity/count config)"
|
|
30
|
-
"packages/engine/src/scene/scene-loader.ts (entity/body creation: no count guard)"
|
|
27
|
+
"packages/engine/src/setup/setup-physics.ts (RAPIER.World construction: no capacity/count config)"
|
|
31
28
|
],
|
|
32
29
|
"notes": "Grepped for maxBodies/MAX_ENTITIES/entity limit/body limit/entityCount: no matches. No invented numbers — nothing caps entity/body count and no perf test here establishes a safe ceiling. Treat large counts (hundreds+ dynamic bodies) as unvalidated, not 'supported at scale'.",
|
|
33
30
|
"evidenceHashes": {
|
|
34
|
-
"packages/engine/src/setup/setup-physics.ts": "0540c502f420018b24dac8b77c486f30f905f0a05b02ead7ed3899e5aa612ee5"
|
|
35
|
-
"packages/engine/src/scene/scene-loader.ts": "07fb9086e95959fd84e71ccc57581bb00d1f6eca1137853492b6a569755e8db5"
|
|
31
|
+
"packages/engine/src/setup/setup-physics.ts": "0540c502f420018b24dac8b77c486f30f905f0a05b02ead7ed3899e5aa612ee5"
|
|
36
32
|
}
|
|
37
33
|
},
|
|
38
34
|
{
|
|
@@ -46,8 +42,8 @@
|
|
|
46
42
|
],
|
|
47
43
|
"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.",
|
|
48
44
|
"evidenceHashes": {
|
|
49
|
-
"packages/engine/src/input/input-types.ts": "
|
|
50
|
-
"packages/engine/src/input/input-manager.ts": "
|
|
45
|
+
"packages/engine/src/input/input-types.ts": "62d9005609a72b55a8214c869d1b27825b1a17b9389f3f3f5d0b04725331b382",
|
|
46
|
+
"packages/engine/src/input/input-manager.ts": "22f0f3cc21a22b3cd3111b2c017ae9a7a430d48156c5bd5d12ed82d6f79ddab8"
|
|
51
47
|
}
|
|
52
48
|
},
|
|
53
49
|
{
|
|
@@ -62,62 +58,54 @@
|
|
|
62
58
|
"notes": "XState remains the only transition language. Parallel regions use distinct layer names. The binding filters native clip tracks, uses AnimationUtils.makeClipAdditive, can register with ctx.systems, and exposes live owner inspection data until disposal.",
|
|
63
59
|
"evidenceHashes": {
|
|
64
60
|
"packages/engine/src/animation/xstate-animation-meta.ts": "80376ac74819803616c4b396244a971cb9a6b2859c33409d032363bfeb3db44f",
|
|
65
|
-
"packages/engine/src/animation/xstate-animation-binding.ts": "
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"id": "component-fields-no-nested-objects",
|
|
70
|
-
"area": "scene-schema",
|
|
71
|
-
"status": "unsupported",
|
|
72
|
-
"claim": "Authored `components.<Name>` values in .vscn.json/.prefab.json must be a scalar or homogeneous scalar array — nested objects/arrays-of-objects are rejected before a component's own static schema sees them.",
|
|
73
|
-
"evidence": [
|
|
74
|
-
"packages/engine/src/scene/schema/entity.ts:46-54 (ComponentScalarSchema/ComponentValueSchema/ComponentDataSchema/ComponentsSchema)",
|
|
75
|
-
"packages/engine/src/scene/schema/entity.ts:89 (`components: ComponentsSchema.optional()` on SceneEntitySchema, validated at scene-parse time)",
|
|
76
|
-
"packages/engine/src/scene/component-registry.ts:21-37 (applyComponents' `Klass.schema.parse(data)` runs on data that already passed ComponentDataSchema)"
|
|
77
|
-
],
|
|
78
|
-
"notes": "The ceiling: `z.union([number, string, boolean, number[], string[], boolean[]])`. A GameComponent's own static `schema` CAN declare z.object() fields, but the scene-file parse rejects a nested value first, so authored JSON never carries one that far. Workaround: flatten (`targetX`/`targetY`/`targetZ` instead of `target:{x,y,z}`).",
|
|
79
|
-
"evidenceHashes": {
|
|
80
|
-
"packages/engine/src/scene/schema/entity.ts": "2b73fec245362168b1824ee06b820c801e7590de95ab1f11176fadad54991f21",
|
|
81
|
-
"packages/engine/src/scene/component-registry.ts": "7ea93439c56fdcd1db915893ebb0f3cabfc08112dbf24c15c76df64a063713e4"
|
|
61
|
+
"packages/engine/src/animation/xstate-animation-binding.ts": "d23739a5820f8e7a36707b5d0a9de2feba0c7200bd9f56de876eded62ce7e9d9"
|
|
82
62
|
}
|
|
83
63
|
},
|
|
84
64
|
{
|
|
85
65
|
"id": "ingestion-pixi-tiers",
|
|
86
66
|
"area": "ingestion",
|
|
87
67
|
"status": "supported-with-tiers",
|
|
88
|
-
"claim": "Unmodified OSS PixiJS games run via the
|
|
68
|
+
"claim": "Unmodified OSS PixiJS games run via the canvas ingest seam: manifest 'iframe-reachable' + bundleUrl — in-realm capture, editor edits, overlay persistence. extraDeps must be registry-known (unknown throws); capture failure degrades loudly.",
|
|
89
69
|
"evidence": [
|
|
90
|
-
"
|
|
70
|
+
"vendor/games/verify-unaltered.mjs (zero-diff vendoring gate)",
|
|
71
|
+
"packages/engine/src/manifest/schema.ts"
|
|
91
72
|
],
|
|
92
|
-
"notes": "Track P shipped 2026-07-03; proven: bubbo-bubbo/puzzling-potions/flappy-pixi. Tiers/limits:
|
|
73
|
+
"notes": "Track P shipped 2026-07-03; proven: bubbo-bubbo/puzzling-potions/flappy-pixi. Tiers/limits: ARCHITECTURE-CORE.md.",
|
|
93
74
|
"evidenceHashes": {
|
|
94
|
-
"
|
|
75
|
+
"vendor/games/verify-unaltered.mjs": "c41ce285aaa26a8fd0e6f5e20385b434d6fc73fc3f39ac1a61204d7141166201",
|
|
76
|
+
"packages/engine/src/manifest/schema.ts": "63d3f6fdbe3e9323ecbd4910c540d8a77e57d95e9044016424b46ca15a5c0bea"
|
|
95
77
|
}
|
|
96
78
|
},
|
|
97
79
|
{
|
|
98
80
|
"id": "ingestion-react-dom",
|
|
99
81
|
"area": "ingestion",
|
|
100
82
|
"status": "supported-with-caveats",
|
|
101
|
-
"claim": "Unmodified native-React DOM games run via the manifest `ingest-react` identity: vendored byte-for-byte (vendor/
|
|
83
|
+
"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). Embed-only in v1 — hosted, sized, disposed; NO authoring (the editor never writes JSX back into an unmodified game, D-N4).",
|
|
102
84
|
"evidence": [
|
|
103
|
-
"
|
|
85
|
+
"vendor/games/verify-unaltered.mjs (zero-diff vendoring gate)",
|
|
86
|
+
"packages/editor/src/adapter-resolver.ts"
|
|
104
87
|
],
|
|
105
|
-
"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.
|
|
88
|
+
"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.",
|
|
106
89
|
"evidenceHashes": {
|
|
107
|
-
"
|
|
90
|
+
"vendor/games/verify-unaltered.mjs": "c41ce285aaa26a8fd0e6f5e20385b434d6fc73fc3f39ac1a61204d7141166201",
|
|
91
|
+
"packages/editor/src/adapter-resolver.ts": "ce88071c9a4061df0a3b7edef24031bf81704ae2c3ede62ed6bf43d4f34ac556"
|
|
108
92
|
}
|
|
109
93
|
},
|
|
110
94
|
{
|
|
111
95
|
"id": "engine-supports-summary",
|
|
112
96
|
"area": "summary",
|
|
113
97
|
"status": "supported",
|
|
114
|
-
"claim": "For contrast, the engine DOES support: discrete Rapier bodies (fixed/kinematic/dynamic) with triggers; phase-ordered
|
|
98
|
+
"claim": "For contrast, the engine DOES support: discrete Rapier bodies (fixed/kinematic/dynamic) with triggers; phase-ordered systems; Zod-validated fetched asset formats; XState-driven animation state machines with masked, weighted, additive layers and 1D/2D blend trees; boolean+thresholded-analog input actions; direct (unwrapped) Colyseus multiplayer.",
|
|
115
99
|
"evidence": [
|
|
116
|
-
"packages/engine/schemas/engine-api.json (auto-generated positive/imperative-surface manifest beside this file)"
|
|
100
|
+
"packages/engine/schemas/engine-api.json (auto-generated positive/imperative-surface manifest beside this file)",
|
|
101
|
+
"packages/engine/src/animation/xstate-animation-binding.ts",
|
|
102
|
+
"packages/engine/src/animation/blend-node.ts"
|
|
117
103
|
],
|
|
118
104
|
"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.",
|
|
119
105
|
"evidenceHashes": {
|
|
120
|
-
"packages/engine/schemas/engine-api.json": "
|
|
106
|
+
"packages/engine/schemas/engine-api.json": "cdfd395fab88575a13c74451051e6c86f8b07980152120f00caa62098ecf7917",
|
|
107
|
+
"packages/engine/src/animation/xstate-animation-binding.ts": "d23739a5820f8e7a36707b5d0a9de2feba0c7200bd9f56de876eded62ce7e9d9",
|
|
108
|
+
"packages/engine/src/animation/blend-node.ts": "9e1d2018acf138bb984fd0d2c7ff1045ef00689e613abf531453af0a1ba06b2e"
|
|
121
109
|
}
|
|
122
110
|
}
|
|
123
111
|
]
|
|
@@ -20,12 +20,6 @@
|
|
|
20
20
|
"description": "Stable reverse-domain application id used by packaged targets",
|
|
21
21
|
"type": "string"
|
|
22
22
|
},
|
|
23
|
-
"editorPort": {
|
|
24
|
-
"description": "Stable preferred local editor port; launchers may fall forward when occupied",
|
|
25
|
-
"type": "integer",
|
|
26
|
-
"minimum": 1024,
|
|
27
|
-
"maximum": 65535
|
|
28
|
-
},
|
|
29
23
|
"version": {
|
|
30
24
|
"type": "string",
|
|
31
25
|
"description": "The game's own version (used for orphan detection)"
|
|
@@ -58,13 +52,14 @@
|
|
|
58
52
|
"type": "string"
|
|
59
53
|
},
|
|
60
54
|
"adapter": {
|
|
55
|
+
"description": "This root's adapter and sole rendering/lifecycle discriminator",
|
|
61
56
|
"anyOf": [
|
|
62
57
|
{
|
|
63
58
|
"type": "string",
|
|
64
59
|
"enum": [
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
60
|
+
"three",
|
|
61
|
+
"canvas",
|
|
62
|
+
"dom"
|
|
68
63
|
]
|
|
69
64
|
},
|
|
70
65
|
{
|
|
@@ -77,9 +72,9 @@
|
|
|
77
72
|
"surface": {
|
|
78
73
|
"type": "string",
|
|
79
74
|
"enum": [
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
75
|
+
"three",
|
|
76
|
+
"canvas",
|
|
77
|
+
"dom"
|
|
83
78
|
],
|
|
84
79
|
"description": "Native surface exposed by this custom adapter contract"
|
|
85
80
|
}
|
|
@@ -96,9 +91,9 @@
|
|
|
96
91
|
"surface": {
|
|
97
92
|
"type": "string",
|
|
98
93
|
"enum": [
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
94
|
+
"three",
|
|
95
|
+
"canvas",
|
|
96
|
+
"dom"
|
|
102
97
|
],
|
|
103
98
|
"description": "Native surface captured from the unmodified game"
|
|
104
99
|
},
|
|
@@ -116,7 +111,7 @@
|
|
|
116
111
|
"description": "Capture mechanics rung this ingested root uses (§4) — also the root`s natural local capability tier"
|
|
117
112
|
},
|
|
118
113
|
"entryHtml": {
|
|
119
|
-
"description": "Entry HTML path, project-relative. WIRED for opaque-embed (D-W2
|
|
114
|
+
"description": "Entry HTML path, project-relative. WIRED for opaque-embed (D-W2): the file`s bytes are read verbatim through the project-static route and hosted in a sandboxed iframe (embed-only, no scene introspection), for both canvas and three roots. Legal but NOT wired to a mount for iframe-reachable (a named error at resolve names this — use `bundleUrl` instead, the wired reachable route).",
|
|
120
115
|
"type": "string"
|
|
121
116
|
},
|
|
122
117
|
"assets": {
|
|
@@ -137,11 +132,11 @@
|
|
|
137
132
|
}
|
|
138
133
|
},
|
|
139
134
|
"captureTimeoutMs": {
|
|
140
|
-
"description": "
|
|
135
|
+
"description": "How long to wait for capture before REPORTING the degrade loudly — not how long to keep listening. On the self-hosted-three route the devtools subscription is permanent, so a renderer appearing later still upgrades a reported-embed-only mount in place; the host clamps this value so a long one cannot block the editor on a question the upgrade path answers anyway.",
|
|
141
136
|
"type": "number"
|
|
142
137
|
},
|
|
143
138
|
"bundleUrl": {
|
|
144
|
-
"description": "Absolute URL of the externalized built game bundle — the WIRED iframe-reachable mount route (iframe-reachable only), for BOTH
|
|
139
|
+
"description": "Absolute URL of the externalized built game bundle — the WIRED iframe-reachable mount route (iframe-reachable only), for BOTH canvas (Track P, ingest-iframe-2d.ts's mountIngestGame2DIframeReachableMulti) and three (D-W1, ingest-iframe-reachable-adapter.ts's mountIngestGameIframeReachableBundle). PRECONDITION: the bundle must externalize its root`s own runtime as a bare, un-rewritten import (`pixi.js` for canvas, `three` for three — `vgai bundle` does this for you) so it resolves through the iframe importmap to the HOST instance the capture trap is installed on; a bundle with an inlined runtime is unreachable and degrades to the embed-only floor. Exactly one of bundleUrl/entryHtml is required when strategy is iframe-reachable.",
|
|
145
140
|
"type": "string"
|
|
146
141
|
},
|
|
147
142
|
"baseHref": {
|
|
@@ -149,18 +144,18 @@
|
|
|
149
144
|
"type": "string"
|
|
150
145
|
},
|
|
151
146
|
"assetBaseUrl": {
|
|
152
|
-
"description": "Host-side basePath forced onto the trapped runtime's own asset resolution — pixi's `Assets.init` for a
|
|
147
|
+
"description": "Host-side basePath forced onto the trapped runtime's own asset resolution — pixi's `Assets.init` for a canvas root, three's `DefaultLoadingManager.setURLModifier` for a three root (D-W1) — absolute, or root-relative (resolved against the editor page's own origin at mount time, ingest-mode.ts, so a static manifest never has to know the dev-server port) (iframe-reachable only — a document.write iframe keeps the parent window.location).",
|
|
153
148
|
"type": "string"
|
|
154
149
|
},
|
|
155
150
|
"extraDeps": {
|
|
156
|
-
"description": "Bare specifiers of this root's externalized deps beyond its own runtime (pixi.js/three — e.g. '@pixi/sound', 'gsap'). Resolved by the editor's host-namespace registry into module namespaces when known (D-P1 — the manifest carries data, not code); an unregistered specifier falls back to the open project's own node_modules (D-Z3
|
|
151
|
+
"description": "Bare specifiers of this root's externalized deps beyond its own runtime (pixi.js/three — e.g. '@pixi/sound', 'gsap'). Resolved by the editor's host-namespace registry into module namespaces when known (D-P1 — the manifest carries data, not code); an unregistered specifier falls back to the open project's own node_modules (D-Z3), served as a verbatim iframe importmap URL. Throws a named error if neither resolves it (iframe-reachable only).",
|
|
157
152
|
"type": "array",
|
|
158
153
|
"items": {
|
|
159
154
|
"type": "string"
|
|
160
155
|
}
|
|
161
156
|
},
|
|
162
157
|
"pixiModuleUrl": {
|
|
163
|
-
"description": "PIXI-ONLY: standalone matching-major pixi ESM URL to trap instead of the host pixi, for version-skewed games (e.g. a v6 game on a v8 host); this field's PRESENCE is how the skew is expressed (D-P2 — no separate strategy/tier for version skew) (iframe-reachable only). Declaring this on a `kind: \"
|
|
158
|
+
"description": "PIXI-ONLY: standalone matching-major pixi ESM URL to trap instead of the host pixi, for version-skewed games (e.g. a v6 game on a v8 host); this field's PRESENCE is how the skew is expressed (D-P2 — no separate strategy/tier for version skew) (iframe-reachable only). Declaring this on a `kind: \"three\"` root is schema-legal (this field is kind-agnostic here) but throws a NAMED not-supported error at resolve — three has no version-skew mount this wave (D-W7: a hypothetical `threeModuleUrl` is explicitly parked, no consumer exists).",
|
|
164
159
|
"type": "string"
|
|
165
160
|
},
|
|
166
161
|
"bodyHtml": {
|
|
@@ -180,20 +175,15 @@
|
|
|
180
175
|
],
|
|
181
176
|
"additionalProperties": false
|
|
182
177
|
}
|
|
183
|
-
]
|
|
184
|
-
"description": "This root's adapter and sole rendering/lifecycle discriminator"
|
|
185
|
-
},
|
|
186
|
-
"scene": {
|
|
187
|
-
"description": ".vscn.json / .scn2d.json scene path (default adapters only — ingest adapters keep their own formats)",
|
|
188
|
-
"type": "string"
|
|
178
|
+
]
|
|
189
179
|
},
|
|
190
180
|
"entry": {
|
|
191
|
-
"description": "Module exporting setup()/adapter (
|
|
181
|
+
"description": "Module exporting a world root — a TSX/R3F component or a setup()/adapter (three), or a React component (dom). Required for every built-in adapter root.",
|
|
192
182
|
"type": "string"
|
|
193
183
|
},
|
|
194
184
|
"zOrder": {
|
|
195
185
|
"default": 0,
|
|
196
|
-
"description": "Canvas stacking order
|
|
186
|
+
"description": "Canvas stacking order; ties broken by array order",
|
|
197
187
|
"type": "integer",
|
|
198
188
|
"minimum": -9007199254740991,
|
|
199
189
|
"maximum": 9007199254740991
|
|
@@ -203,6 +193,11 @@
|
|
|
203
193
|
"description": "Whether play-mode pause/step applies to this root",
|
|
204
194
|
"type": "boolean"
|
|
205
195
|
},
|
|
196
|
+
"dev": {
|
|
197
|
+
"default": false,
|
|
198
|
+
"description": "Marks this root as a DEV LAYER: the game's own dev GUI, not shipped game content. Four readers act on it. `mountManifestRoots` mounts it only when dev layers are enabled (`devLayersEnabled`, runtime/dev-layers.ts), so a production build carries no dev GUI; the host stacks every dev root ABOVE every non-dev root (a dev layer sits topmost, and as a DOM layer it steals no input from the game while closed); the play compositor EXCLUDES dev layers from a capture unless the capture asks for them; and the editor mounts a dev root as edit-time CHROME rather than authorable content (no hierarchy entry, no OID stamping, no JSX write-back, not selectable).",
|
|
199
|
+
"type": "boolean"
|
|
200
|
+
},
|
|
206
201
|
"loop": {
|
|
207
202
|
"default": "gated",
|
|
208
203
|
"description": "gated: host-driven loop (default). self-driven: this root drives its own loop (the \"composited, unsynchronized\" tier) — an axis independent of capability tier",
|
|
@@ -249,7 +244,7 @@
|
|
|
249
244
|
],
|
|
250
245
|
"additionalProperties": false
|
|
251
246
|
},
|
|
252
|
-
"description": "The Game's explicit, non-empty adapter-root composition. There is no implicit root."
|
|
247
|
+
"description": "The Game's explicit, non-empty adapter-root composition. There is no implicit root. Root ids are unique, and a project declares at most ONE world root per medium — one `three`, one `dom`, one canvas-surface root (first-party canvas, canvas ingest and canvas module are the same medium). A `dev: true` root is counted in its own band: a dev layer is chrome stacked above the game, not content competing for the medium, so a game may declare a `dom` HUD and a `dom` dev layer — but not two of either."
|
|
253
248
|
},
|
|
254
249
|
"authoring": {
|
|
255
250
|
"description": "Editor-only authoring metadata; never runtime ownership",
|
|
@@ -314,6 +309,29 @@
|
|
|
314
309
|
"module": {
|
|
315
310
|
"description": "Module path for the Colyseus room bootstrap; defaults to the scaffolded server bootstrap",
|
|
316
311
|
"type": "string"
|
|
312
|
+
},
|
|
313
|
+
"relay": {
|
|
314
|
+
"description": "Deploy-baked managed-relay credentials. NEVER hand-authored in source — written only into the staged manifest by `vgai deploy` and fetched at runtime by the standalone client",
|
|
315
|
+
"type": "object",
|
|
316
|
+
"properties": {
|
|
317
|
+
"signalingUrl": {
|
|
318
|
+
"description": "Managed relay signaling URL to prefer over the client default; omitted when the deploy did not pin one",
|
|
319
|
+
"type": "string"
|
|
320
|
+
},
|
|
321
|
+
"accessToken": {
|
|
322
|
+
"type": "string",
|
|
323
|
+
"description": "Room-scoped managed-relay access token minted at `vgai deploy` and read by the standalone client (main.ts)"
|
|
324
|
+
},
|
|
325
|
+
"expiresAt": {
|
|
326
|
+
"type": "number",
|
|
327
|
+
"description": "Token expiry as epoch milliseconds; the client falls back to a tokenless join once elapsed"
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
"required": [
|
|
331
|
+
"accessToken",
|
|
332
|
+
"expiresAt"
|
|
333
|
+
],
|
|
334
|
+
"additionalProperties": false
|
|
317
335
|
}
|
|
318
336
|
},
|
|
319
337
|
"required": [
|
|
@@ -352,12 +370,12 @@
|
|
|
352
370
|
]
|
|
353
371
|
},
|
|
354
372
|
"determinism": {
|
|
355
|
-
"description": "Seeded-RNG determinism contract (D15
|
|
373
|
+
"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.",
|
|
356
374
|
"type": "object",
|
|
357
375
|
"properties": {
|
|
358
376
|
"seededRandom": {
|
|
359
377
|
"type": "boolean",
|
|
360
|
-
"description": "Declares that ALL gameplay RNG in this project flows through ctx.random (the named-stream seeded PRNG
|
|
378
|
+
"description": "Declares that ALL gameplay RNG in this project flows through ctx.random (the named-stream seeded PRNG) rather than raw Math.random/Date.now/performance.now. Three runtime enforcers key off this flag: the boot-time seeding reader (mount-manifest.ts seeds ctx.random from defaultSeed/?vgai-seed=/explicit config, in that precedence, only when true), the gameplay-rng-ban burn-down scan (test/gameplay-rng-ban.test.ts — lints this project's src/ for raw RNG/wall-clock calls), and the dev-mode Math.random phase trap (runtime/gameplay-rng-trap.ts — warns once per call site during a gameplay frame, never throws). Default false: an undeclared project gets no determinism contract at all (that IS the opt-out — no dead field)."
|
|
361
379
|
},
|
|
362
380
|
"defaultSeed": {
|
|
363
381
|
"description": "The seed ctx.random boots from when seededRandom is true, unless overridden by ?vgai-seed=<int> (the query param always wins over this manifest default) or an even higher-precedence explicit config value a host passes directly. Optional — omit to fall back to the runtime's own fixed default seed.",
|
|
@@ -385,7 +403,7 @@
|
|
|
385
403
|
"exclusiveMinimum": 0
|
|
386
404
|
},
|
|
387
405
|
"maxEntities": {
|
|
388
|
-
"description": "Maximum allowed entity-tagged scene-graph nodes (nodes carrying userData.entityId) summed across the game's
|
|
406
|
+
"description": "Maximum allowed entity-tagged scene-graph nodes (nodes carrying userData.entityId) summed across the game's three/canvas worlds at the end of a `vgai perf` run. Exact and deterministic under a seed — no tolerance band.",
|
|
389
407
|
"type": "integer",
|
|
390
408
|
"minimum": 0,
|
|
391
409
|
"maximum": 9007199254740991
|
|
@@ -476,5 +494,5 @@
|
|
|
476
494
|
"roots"
|
|
477
495
|
],
|
|
478
496
|
"additionalProperties": false,
|
|
479
|
-
"description": "Game manifest (vgai.
|
|
497
|
+
"description": "Game manifest (vgai.project.json) — the complete v2 project configuration"
|
|
480
498
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `AdapterSurface` — the kinds of render surface a world can be (T7.1/T7.3/T6.2).
|
|
3
3
|
*
|
|
4
|
-
* Moved here
|
|
5
|
-
* so `adapter/
|
|
4
|
+
* Moved here from `runtime/game.ts`
|
|
5
|
+
* so `adapter/root-adapter.ts`'s kind-tagged `MountedRoot` types can name it
|
|
6
6
|
* without a value-level import cycle (`runtime/game.ts` type-imports from
|
|
7
|
-
* `adapter/
|
|
7
|
+
* `adapter/root-adapter.ts` already). This is a leaf module — it imports
|
|
8
8
|
* nothing — so anything may import it with zero risk of a cycle.
|
|
9
9
|
* `runtime/game.ts` re-exports this SAME type (`export type { AdapterSurface }`),
|
|
10
10
|
* so no existing `import type { AdapterSurface } from '../runtime/game'` call site
|
|
11
11
|
* needed to change.
|
|
12
12
|
*/
|
|
13
|
-
export type AdapterSurface = '
|
|
13
|
+
export type AdapterSurface = 'three' | 'canvas' | 'dom';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Exhaustiveness guard for `AdapterSurface` dispatch (v4 architecture-review
|
|
@@ -19,7 +19,7 @@ export type AdapterSurface = 'threejs' | 'pixijs' | 'react';
|
|
|
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
21
|
* (`runtime/create-runtime.ts`'s mount loop, `editor/adapter-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
|
|
25
25
|
* compile-time half of the guard (TS refuses to call this with anything the
|