@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
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* than silently simulating (or rendering) the wrong shape.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
27
|
-
import
|
|
26
|
+
import type { ColliderDescriptor } from '../asset-formats/collider';
|
|
27
|
+
import { DEFAULTS } from '../defaults';
|
|
28
28
|
|
|
29
29
|
/** Axes are considered "uniform enough" within this tolerance (float error only). */
|
|
30
30
|
const UNIFORM_SCALE_EPSILON = 1e-4;
|
|
@@ -51,7 +51,7 @@ export interface ColliderWorldDimensions {
|
|
|
51
51
|
* non-uniform world scale (beyond {@link UNIFORM_SCALE_EPSILON}).
|
|
52
52
|
*/
|
|
53
53
|
export function computeColliderWorldDimensions(
|
|
54
|
-
collider:
|
|
54
|
+
collider: ColliderDescriptor,
|
|
55
55
|
worldScale: readonly [number, number, number],
|
|
56
56
|
entityLabel: string,
|
|
57
57
|
): ColliderWorldDimensions {
|
|
@@ -13,7 +13,7 @@ export interface PhysicsRefs {
|
|
|
13
13
|
*
|
|
14
14
|
* Replaces the old bitecs RigidBodyRef/ColliderRef SoA components. Two indexes:
|
|
15
15
|
* - `Object3D → {body, collider}` for the postPhysics transform writer and for
|
|
16
|
-
*
|
|
16
|
+
* game code/editor to reach an entity's physics.
|
|
17
17
|
* - `colliderHandle → Object3D` reverse index for collision/trigger dispatch
|
|
18
18
|
* (Rapier collision events report collider handles).
|
|
19
19
|
*/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Container } from 'pixi.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* AuthoringAdapter-2D — the
|
|
5
|
-
*
|
|
6
|
-
* (no fabricated
|
|
7
|
-
* inspect + edit
|
|
4
|
+
* AuthoringAdapter-2D — the Pixi analog of the editor AuthoringAdapter /
|
|
5
|
+
* LiveThreeAuthoringAdapter, implemented DIRECTLY over a live PixiJS display tree
|
|
6
|
+
* (no fabricated document — the anti-shim rule). Used by the editor to
|
|
7
|
+
* inspect + edit Pixi display-tree entities (first-party OR an ingested unmodified game),
|
|
8
8
|
* with stable structural-path ids so edits re-bind after the tree rebuilds.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -29,7 +29,7 @@ export interface Property2D {
|
|
|
29
29
|
value: unknown;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/**
|
|
32
|
+
/** An in-memory edit overlay keyed by structural-path id (2D). */
|
|
33
33
|
export interface Override2D {
|
|
34
34
|
label?: string;
|
|
35
35
|
alpha?: number;
|
|
@@ -62,8 +62,16 @@ export class AuthoringAdapter2D {
|
|
|
62
62
|
this.refresh();
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Walk the live tree assigning deterministic structural-path ids.
|
|
67
|
+
*
|
|
68
|
+
* Separate from {@link refresh} because the two are wanted at different rates: the ids have to be
|
|
69
|
+
* current whenever the tree is READ (a world that mounts its content asynchronously — a
|
|
70
|
+
* translated port waiting on its resources, anything behind Suspense — commits real nodes after
|
|
71
|
+
* the adapter was handed its root, and an unindexed node is one the hierarchy cannot name), while
|
|
72
|
+
* re-applying the edit overlay is a WRITE and must happen only when something asked for it.
|
|
73
|
+
*/
|
|
74
|
+
reindex(): { count: number; sprites: number } {
|
|
67
75
|
this.byId.clear();
|
|
68
76
|
let count = 0;
|
|
69
77
|
let sprites = 0;
|
|
@@ -80,11 +88,16 @@ export class AuthoringAdapter2D {
|
|
|
80
88
|
this.root.children.forEach((c, i) => {
|
|
81
89
|
visit(c as Container, `${i}`);
|
|
82
90
|
});
|
|
83
|
-
// Reapply any saved overlay edits to the live tree.
|
|
84
|
-
for (const [id, ov] of Object.entries(this.overlay)) this.applyOverride(id, ov);
|
|
85
91
|
return { count, sprites };
|
|
86
92
|
}
|
|
87
93
|
|
|
94
|
+
/** {@link reindex}, then reapply every saved overlay edit to the live tree. */
|
|
95
|
+
refresh(): { count: number; sprites: number } {
|
|
96
|
+
const counts = this.reindex();
|
|
97
|
+
for (const [id, ov] of Object.entries(this.overlay)) this.applyOverride(id, ov);
|
|
98
|
+
return counts;
|
|
99
|
+
}
|
|
100
|
+
|
|
88
101
|
roots(): EditorNode2D[] {
|
|
89
102
|
return this.root.children.map((c) => this.toNode(c as Container));
|
|
90
103
|
}
|
|
@@ -186,7 +199,7 @@ export class AuthoringAdapter2D {
|
|
|
186
199
|
* it into the in-memory overlay so a later `serializeOverlay()`/save preserves it
|
|
187
200
|
* and a later `refresh()` re-applies it again. Ids absent from the current tree
|
|
188
201
|
* are skipped (orphaned, not an error) — the 2D analog of
|
|
189
|
-
* `
|
|
202
|
+
* `LiveThreeAuthoringAdapter.applyOverlay` (T3.2 slice 3). Used both for the initial
|
|
190
203
|
* reload (constructor `opts.overlay`) and for `PersistenceProvider.applyExternal`
|
|
191
204
|
* (re-apply a changed overlay file without a full remount).
|
|
192
205
|
*/
|
|
@@ -217,7 +230,7 @@ export class AuthoringAdapter2D {
|
|
|
217
230
|
for (const [id, ov] of Object.entries(this.overlay)) this.applyOverride(id, ov);
|
|
218
231
|
this.dirty = true;
|
|
219
232
|
}
|
|
220
|
-
/** Clear the dirty flag after a successful save (mirrors
|
|
233
|
+
/** Clear the dirty flag after a successful save (mirrors LiveThreeAuthoringAdapter). */
|
|
221
234
|
markClean(): void {
|
|
222
235
|
this.dirty = false;
|
|
223
236
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pixi — the PixiJS surface substrate.
|
|
3
|
+
*
|
|
4
|
+
* PixiJS is opt-in: the engine core never imports it, so a three-only bundle
|
|
5
|
+
* never pays for it. What lives here is what the host needs to mount and
|
|
6
|
+
* inspect a PixiJS game it did not write — the render-call capture trap, the
|
|
7
|
+
* in-realm and iframe mounts, the live display-tree authoring adapter, and the
|
|
8
|
+
* Rapier-2D handle registry the editor's transform coordination reads.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
AuthoringAdapter2D,
|
|
13
|
+
type EditorNode2D,
|
|
14
|
+
type Overlay2D,
|
|
15
|
+
type Override2D,
|
|
16
|
+
type Property2D,
|
|
17
|
+
type Transform2DValue,
|
|
18
|
+
} from './authoring';
|
|
19
|
+
export {
|
|
20
|
+
type ExtraDepsResolution2D,
|
|
21
|
+
type IngestGame2D,
|
|
22
|
+
type IngestMount2D,
|
|
23
|
+
mountIngestGame2D,
|
|
24
|
+
} from './ingest';
|
|
25
|
+
export {
|
|
26
|
+
type EmbedMount2D,
|
|
27
|
+
type IframeReachableMount2D,
|
|
28
|
+
type IframeReachableMultiOpts,
|
|
29
|
+
mountIngestGame2DEmbed,
|
|
30
|
+
mountIngestGame2DIframeReachable,
|
|
31
|
+
mountIngestGame2DIframeReachableMulti,
|
|
32
|
+
} from './ingest-iframe';
|
|
33
|
+
export {
|
|
34
|
+
createPhysics2DRegistry,
|
|
35
|
+
type Physics2DRefs,
|
|
36
|
+
type Physics2DRegistry,
|
|
37
|
+
} from './physics-registry';
|
|
38
|
+
export {
|
|
39
|
+
type CapturedRuntime2D,
|
|
40
|
+
installSceneCapture2D,
|
|
41
|
+
type SceneCapture2DHandle,
|
|
42
|
+
} from './scene-capture';
|
|
43
|
+
export { createPhysicsAdapter2D, type PhysicsAdapter2D } from './system-adapters';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Pixi iframe ingest tiers — the PixiJS analogs of the 3D ingest-iframe adapters.
|
|
3
3
|
*
|
|
4
4
|
* - OPAQUE EMBED (tier 0-1): a prebuilt/self-contained bundle runs in a sandboxed
|
|
5
5
|
* iframe; hosted + sized + disposed, NOT introspectable.
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type CapturedRuntime2D,
|
|
13
13
|
installSceneCapture2D,
|
|
14
14
|
type SceneCapture2DHandle,
|
|
15
|
-
} from './scene-capture
|
|
15
|
+
} from './scene-capture';
|
|
16
16
|
|
|
17
17
|
export interface EmbedMount2D {
|
|
18
18
|
iframe: HTMLIFrameElement;
|
|
@@ -77,13 +77,13 @@ export interface IframeReachableMultiOpts {
|
|
|
77
77
|
*/
|
|
78
78
|
extraDeps?: Record<string, Record<string, unknown>>;
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
80
|
+
* Bare-specifier -> VERBATIM URL importmap entries (the
|
|
81
|
+
* project-node_modules fallback for a specifier that isn't in the
|
|
82
|
+
* host-namespace registry but IS installed in the open project's own
|
|
83
|
+
* `node_modules`). Written into the importmap DIRECTLY alongside the
|
|
84
|
+
* `extraDeps` blob-namespace entries — no host-side import, no blob
|
|
85
|
+
* wrapping, no decision logic here: this function just merges whatever
|
|
86
|
+
* `Record<specifier, url>` the caller (editor-side
|
|
87
87
|
* `host-namespace-registry.ts`) already decided. The URL's own transitive
|
|
88
88
|
* `import 'pixi.js'` resolves through this SAME importmap back to the host
|
|
89
89
|
* pixi trap (§0.3), keeping module identity converged without a second pixi
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { installSceneCapture2D, type SceneCapture2DHandle } from './scene-capture
|
|
1
|
+
import { installSceneCapture2D, type SceneCapture2DHandle } from './scene-capture';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* A manifest's `extraDeps` resolve to TWO kinds of importmap entry:
|
|
5
|
+
* `namespaces` are today's registry-known bare specifiers
|
|
6
|
+
* (host-namespace-registry.ts), re-exported via a blob module
|
|
7
|
+
* (`ingest-iframe-2d.ts`'s `makeHostNamespaceReexportUrl`); `urls` are the
|
|
8
|
+
* D-Z3 fallback — a specifier that isn't registered but IS present in the
|
|
9
|
+
* open project's own `node_modules`, mapped DIRECTLY to its
|
|
10
10
|
* `/project-game-static/node_modules/<spec>/<esm entry>` URL (no host-side
|
|
11
11
|
* import, no blob — the dep executes in the iframe realm and its own
|
|
12
12
|
* transitive `import 'pixi.js'` resolves through the same importmap back to
|
|
@@ -21,7 +21,7 @@ export interface ExtraDepsResolution2D {
|
|
|
21
21
|
urls: Record<string, string>;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/** A registered unmodified PixiJS game (the
|
|
24
|
+
/** A registered unmodified PixiJS game (the Pixi analog of `IngestGame`). */
|
|
25
25
|
export interface IngestGame2D {
|
|
26
26
|
id: string;
|
|
27
27
|
name: string;
|
|
@@ -32,18 +32,19 @@ export interface IngestGame2D {
|
|
|
32
32
|
tier?: 'shared' | 'iframe-reachable' | 'opaque-embed';
|
|
33
33
|
captureTimeoutMs?: number;
|
|
34
34
|
/**
|
|
35
|
-
* IFRAME-REACHABLE-MULTI fields (Track P
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* `
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* editor-side
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
35
|
+
* IFRAME-REACHABLE-MULTI fields (Track P): an externalized built bundle
|
|
36
|
+
* mounted via `mountIngestGame2DIframeReachableMulti`
|
|
37
|
+
* (`./ingest-iframe-2d.ts`) instead of `load()` — populated only when `tier`
|
|
38
|
+
* is `'iframe-reachable'` AND `load` is absent. Mirror
|
|
39
|
+
* `IframeReachableMultiOpts`'s fields, EXCEPT `extraDeps`/`pixiNamespace` are
|
|
40
|
+
* THUNKS here, not resolved values — the same "lazy, only invoked at mount
|
|
41
|
+
* time" shape `load` already uses, so building this descriptor
|
|
42
|
+
* (`resolveIngest2DDescriptor` / the canvas surface's
|
|
43
|
+
* `ingest/surface-canvas.ts`, editor-side — Wave 0.6 replaced the three
|
|
44
|
+
* per-surface `discovery*.ts` modules with one manifest-driven registry) never
|
|
45
|
+
* has to be async just to wire up which specifiers/ version-skew URL a game
|
|
46
|
+
* needs; only actually MOUNTING it (the ingest-mode mount bridge) pays for the
|
|
47
|
+
* dynamic import of `@pixi/sound`/`gsap`/spine/ a standalone pixi build.
|
|
47
48
|
*/
|
|
48
49
|
bundleUrl?: string;
|
|
49
50
|
/** <base href> for the iframe (see `IframeReachableMultiOpts.baseHref`). */
|
|
@@ -65,14 +66,13 @@ export interface IngestGame2D {
|
|
|
65
66
|
/** DOM the game expects before boot (see `IframeReachableMultiOpts.bodyHtml`). */
|
|
66
67
|
bodyHtml?: string;
|
|
67
68
|
/**
|
|
68
|
-
* Lazily read an OPAQUE prebuilt bundle's self-contained HTML (D-W2
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* `resolveIngest2DDescriptor` (adapter-resolver.ts) builds one that reads
|
|
69
|
+
* Lazily read an OPAQUE prebuilt bundle's self-contained HTML (D-W2) — the pixi
|
|
70
|
+
* twin of `IngestGame.embedHtml`. Always a lazy thunk here (no in-tree Pixi
|
|
71
|
+
* opaque-embed fixture exists yet to need a plain-string form, unlike the three
|
|
72
|
+
* side): `resolveIngest2DDescriptor` (adapter-resolver.ts) builds one that reads
|
|
73
73
|
* the declared `entryHtml` file's bytes through the `/project-game-static/`
|
|
74
|
-
* route, only when the game is actually mounted. The host runs the
|
|
75
|
-
*
|
|
74
|
+
* route, only when the game is actually mounted. The host runs the resolved HTML
|
|
75
|
+
* in a sandboxed iframe at the embed-only floor (Tier 0-1),
|
|
76
76
|
* `mountIngestGame2DEmbed` (`./ingest-iframe-2d.ts`).
|
|
77
77
|
*/
|
|
78
78
|
embedHtml?: () => Promise<string>;
|
|
@@ -105,7 +105,7 @@ export async function mountIngestGame2D(
|
|
|
105
105
|
await game.load?.();
|
|
106
106
|
} catch (err) {
|
|
107
107
|
capture.uninstall();
|
|
108
|
-
throw new Error(`
|
|
108
|
+
throw new Error(`pixi ingest: game "${game.id}" failed to load: ${err}`);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const timeout = opts.captureTimeoutMs ?? game.captureTimeoutMs ?? 8000;
|
|
@@ -9,7 +9,7 @@ export interface Physics2DRefs {
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Maps PixiJS display objects to their Rapier 2D bodies/colliders and back —
|
|
12
|
-
* the
|
|
12
|
+
* the Pixi analog of `PhysicsRegistry`. Two indexes:
|
|
13
13
|
* - `Container → {body, collider}` for the postPhysics transform writer.
|
|
14
14
|
* - `colliderHandle → Container` reverse index for collision/trigger dispatch.
|
|
15
15
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Scene capture for
|
|
2
|
+
* Scene capture for the Pixi surface — the PixiJS analog of the three.js render
|
|
3
3
|
* accessor-trap (`adapter/ingest/scene-capture.ts`).
|
|
4
4
|
*
|
|
5
5
|
* An unmodified PixiJS game owns its own `Application`, `stage`, renderer, and
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* way (degrade to iframe-reachable or opaque-embed tiers — see the spec).
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
/** A live
|
|
21
|
+
/** A live Pixi runtime captured from an external PixiJS game. */
|
|
22
22
|
export interface CapturedRuntime2D {
|
|
23
23
|
/** The game's root stage Container. */
|
|
24
24
|
stage: unknown;
|
|
@@ -92,7 +92,7 @@ export function installSceneCapture2D(pixiNamespace: unknown): SceneCapture2DHan
|
|
|
92
92
|
if (i >= 0) waiters.splice(i, 1);
|
|
93
93
|
reject(
|
|
94
94
|
new Error(
|
|
95
|
-
`
|
|
95
|
+
`pixi scene capture timed out after ${timeoutMs}ms — the game never ` +
|
|
96
96
|
`rendered, or it bundles its own (un-shared) copy of pixi.js.`,
|
|
97
97
|
),
|
|
98
98
|
);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Container } from 'pixi.js';
|
|
2
|
-
import type { Physics2DRegistry } from './
|
|
2
|
+
import type { Physics2DRegistry } from './physics-registry';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* PhysicsAdapter2D — the
|
|
5
|
+
* PhysicsAdapter2D — the Pixi analog of the 3D PhysicsAdapter (rapier-physics-adapter).
|
|
6
6
|
* Lets the editor coordinate editing of a Rapier-2D-driven entity WITHOUT owning the
|
|
7
7
|
* simulation: report transform ownership, then freeze → commit → unfreeze so a drag
|
|
8
8
|
* sticks instead of being stomped by the next physics step.
|
|
@@ -8,15 +8,40 @@ function lineAt(source: string, offset: number): number {
|
|
|
8
8
|
return source.slice(0, offset).split('\n').length;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* The project's own `dom`-root adapter implementation, which is ALLOWED to
|
|
13
|
+
* create a React root because creating it is the whole job.
|
|
14
|
+
*
|
|
15
|
+
* The rule here is "a React root must come from a declared manifest root, not
|
|
16
|
+
* from game code mounting a second lifecycle beside it". That held trivially
|
|
17
|
+
* while the only `createRoot` in existence lived in the engine. It stopped
|
|
18
|
+
* holding when the `react-root` capability moved INTO the project (`vgai add
|
|
19
|
+
* react-root`): its `createRoot` is precisely what serves a declared `dom`
|
|
20
|
+
* root — the opposite of bypassing the manifest.
|
|
21
|
+
*
|
|
22
|
+
* Matched on the capability's DIRECTORY rather than a filename, so that
|
|
23
|
+
* editing the copied source — the entire point of the copied-source model —
|
|
24
|
+
* cannot re-arm the rule against itself. A Vue or Svelte replacement never
|
|
25
|
+
* trips it at all: only React DOM's own API is detected.
|
|
26
|
+
*/
|
|
27
|
+
const DOM_ROOT_ADAPTER_DIR = /(^|[/\\])src[/\\]lib[/\\]react-root[/\\]/;
|
|
28
|
+
|
|
11
29
|
/**
|
|
12
30
|
* Find project-owned React DOM root creation. VGAI hosts React through
|
|
13
31
|
* manifest adapter roots, so any of these calls in game source creates an
|
|
14
|
-
* unmanaged second lifecycle and is a hard validation error
|
|
32
|
+
* unmanaged second lifecycle and is a hard validation error — except in the
|
|
33
|
+
* project's own dom-root adapter (see {@link DOM_ROOT_ADAPTER_DIR}).
|
|
34
|
+
*
|
|
35
|
+
* The exemption lives HERE, in the detector, rather than in either caller:
|
|
36
|
+
* the editor's validate-on-change watcher and `vgai validate` both scan the
|
|
37
|
+
* same tree, and a rule that means two different things depending on who asks
|
|
38
|
+
* is worse than no rule.
|
|
15
39
|
*/
|
|
16
40
|
export function detectUnmanagedReactRoots(
|
|
17
41
|
source: string,
|
|
18
42
|
file: string,
|
|
19
43
|
): UnmanagedReactRootFinding[] {
|
|
44
|
+
if (DOM_ROOT_ADAPTER_DIR.test(file)) return [];
|
|
20
45
|
const findings: UnmanagedReactRootFinding[] = [];
|
|
21
46
|
const aliases = new Map<string, 'createRoot' | 'hydrateRoot'>();
|
|
22
47
|
|
package/src/react/use-data.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* React hooks for data assets — the tool-hooks half
|
|
3
|
-
*
|
|
2
|
+
* React hooks for data assets — the tool-hooks half, shipped with W4 (dock
|
|
3
|
+
* tools).
|
|
4
4
|
*
|
|
5
5
|
* Lives under `packages/engine/src/react/` deliberately: this is the ONE
|
|
6
6
|
* directory of the engine allowed to value-import react (colocated with
|
|
@@ -72,7 +72,7 @@ export async function writeData(
|
|
|
72
72
|
if (!projectRelativePath.startsWith('src/data/') || !projectRelativePath.endsWith('.data.json')) {
|
|
73
73
|
throw new Error(
|
|
74
74
|
`writeData: invalid path ${JSON.stringify(projectRelativePath)} — data assets live at ` +
|
|
75
|
-
'src/data/**/*.data.json, addressed project-relative
|
|
75
|
+
'src/data/**/*.data.json, addressed project-relative, ' +
|
|
76
76
|
"e.g. writeData('src/data/tuning.data.json', next).",
|
|
77
77
|
);
|
|
78
78
|
}
|
|
@@ -109,8 +109,8 @@ export async function writeData(
|
|
|
109
109
|
} catch (err) {
|
|
110
110
|
throw new Error(
|
|
111
111
|
'writeData: could not reach the editor dev server (/__editor/data-file) — this helper is ' +
|
|
112
|
-
'editor-tool plumbing
|
|
113
|
-
'editor. Game code must not write data assets: they are immutable at runtime
|
|
112
|
+
'editor-tool plumbing and only works inside the running ' +
|
|
113
|
+
'editor. Game code must not write data assets: they are immutable at runtime — ' +
|
|
114
114
|
`keep runtime state in your game's own store. (${String(err)})`,
|
|
115
115
|
);
|
|
116
116
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* W3.1
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* about the currently-selected ENTITY and the components authored on it —
|
|
6
|
-
* the gap a real consumer (`critter-types-w3b`'s `critter-type.tool.tsx`,
|
|
7
|
-
* §9.4) hit hard: with no way to read a component's own authored config, it
|
|
8
|
-
* had to recover species from `node.label` (a display string, not data).
|
|
2
|
+
* W3.1 — `useSelection()`, the third tool-hooks contract member (alongside
|
|
3
|
+
* `useData`/`writeData` from `use-data.ts`). Where those two hooks are about
|
|
4
|
+
* DATA ASSETS, this one is about the currently-selected ENTITY.
|
|
9
5
|
*
|
|
10
6
|
* Scope, v1:
|
|
11
7
|
* - INSPECTOR-placement tools only (`placement: 'inspector'`,
|
|
@@ -14,17 +10,9 @@
|
|
|
14
10
|
* (`ToolHost`/`BottomPanel`) have no adapter/selection in scope and never
|
|
15
11
|
* get a provider. Calling `useSelection()` there throws a teaching error,
|
|
16
12
|
* contained by the tool's `ToolErrorBoundary` (the editor survives).
|
|
17
|
-
* - READ-only. `
|
|
18
|
-
*
|
|
19
|
-
* `
|
|
20
|
-
* returns. Per-entity WRITE-from-tool is deferred with `useTunable` (§10).
|
|
21
|
-
* - Honest degrades, not errors, when the adapter is thinner than the vgai
|
|
22
|
-
* first-party one: no `components` provider at all ⇒ `components: []`;
|
|
23
|
-
* a `components` provider present but with no optional `config()`
|
|
24
|
-
* accessor ⇒ each entry still appears (from `list()`) but with
|
|
25
|
-
* `config: null`. Both mirror the `AuthoringAdapter` optional-provider
|
|
26
|
-
* doctrine (§4) — an ingest/UI adapter that never implements `components`
|
|
27
|
-
* was already legal, and stays legal.
|
|
13
|
+
* - READ-only. Write through `InspectorProvider.set` / `writeData` (the
|
|
14
|
+
* existing paths), never by mutating what this hook returns. Per-entity
|
|
15
|
+
* WRITE-from-tool is deferred with `useTunable` (§10).
|
|
28
16
|
*
|
|
29
17
|
* Liveness — two existing paths reused, no new mechanism (§4's "reuse that
|
|
30
18
|
* path" rule):
|
|
@@ -32,12 +20,11 @@
|
|
|
32
20
|
* `useSyncExternalStore(store.subscribe, store.getSnapshot)` re-renders
|
|
33
21
|
* the panel, each matched `Section` gets the new `nodeId` prop, and
|
|
34
22
|
* `InspectorToolSection` re-creates this provider's context value.
|
|
35
|
-
* 2.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* snapshot/version of its own, `subscribe` is fire-only) and recomputes.
|
|
23
|
+
* 2. Edits while the SAME node stays selected: `adapter.subscribe` fires —
|
|
24
|
+
* this hook keeps a local version counter bumped per notification
|
|
25
|
+
* (exactly `game-state.tsx`'s `useRootObservation` pattern: the adapter
|
|
26
|
+
* has no snapshot/version of its own, `subscribe` is fire-only) and
|
|
27
|
+
* recomputes.
|
|
41
28
|
*/
|
|
42
29
|
|
|
43
30
|
import {
|
|
@@ -76,22 +63,13 @@ export function EditorSelectionProvider({
|
|
|
76
63
|
return <SelectionContext.Provider value={value}>{children}</SelectionContext.Provider>;
|
|
77
64
|
}
|
|
78
65
|
|
|
79
|
-
/** One component attached to the selected entity, with its authored config
|
|
80
|
-
* if the adapter can produce one (see the module doc's degrade rules). */
|
|
81
|
-
export interface SelectedComponent {
|
|
82
|
-
type: string;
|
|
83
|
-
config: Record<string, unknown> | null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
66
|
export interface EditorSelection {
|
|
87
67
|
nodeId: string | null;
|
|
88
68
|
node: EditorNode | null;
|
|
89
|
-
components: SelectedComponent[];
|
|
90
69
|
}
|
|
91
70
|
|
|
92
71
|
/**
|
|
93
|
-
* The selected entity, live
|
|
94
|
-
* with that component's own authored config (read-only — see module doc).
|
|
72
|
+
* The selected entity, live (read-only — see module doc).
|
|
95
73
|
*
|
|
96
74
|
* Throws outside an `EditorSelectionProvider` — inspector tools always render
|
|
97
75
|
* inside one; a dock tool or game-code caller does not, and the error names
|
|
@@ -102,7 +80,7 @@ export function useSelection(): EditorSelection {
|
|
|
102
80
|
if (!ctx) {
|
|
103
81
|
throw new Error(
|
|
104
82
|
'useSelection: no editor selection in context — this hook only works inside an editor-hosted ' +
|
|
105
|
-
"INSPECTOR tool (placement: 'inspector'
|
|
83
|
+
"INSPECTOR tool (placement: 'inspector'). Dock tools and " +
|
|
106
84
|
'game code have no selection to read.',
|
|
107
85
|
);
|
|
108
86
|
}
|
|
@@ -110,7 +88,7 @@ export function useSelection(): EditorSelection {
|
|
|
110
88
|
|
|
111
89
|
// Local version counter bumped per adapter notification — the adapter has
|
|
112
90
|
// no snapshot/version of its own, `subscribe` is fire-only (same pattern
|
|
113
|
-
// as `
|
|
91
|
+
// as `useRootObservation`'s `versionRef`, game-state.tsx).
|
|
114
92
|
const versionRef = useRef(0);
|
|
115
93
|
const version = useSyncExternalStore(
|
|
116
94
|
(onStoreChange) =>
|
|
@@ -123,12 +101,7 @@ export function useSelection(): EditorSelection {
|
|
|
123
101
|
|
|
124
102
|
return useMemo(() => {
|
|
125
103
|
const node = nodeId ? adapter.hierarchy.node(nodeId) : null;
|
|
126
|
-
|
|
127
|
-
const components: SelectedComponent[] = types.map(({ type }) => ({
|
|
128
|
-
type,
|
|
129
|
-
config: nodeId ? (adapter.components?.config?.(nodeId, type) ?? null) : null,
|
|
130
|
-
}));
|
|
131
|
-
return { nodeId, node, components };
|
|
104
|
+
return { nodeId, node };
|
|
132
105
|
// `version` has no value of its own — only its CHANGE matters, as the
|
|
133
106
|
// liveness signal described above.
|
|
134
107
|
}, [adapter, nodeId, version]);
|