@vgai/engine 0.5.13 → 0.5.15
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/dist/adapter/adapter-module.d.ts +447 -0
- package/dist/adapter/adapter-module.d.ts.map +1 -0
- package/dist/adapter/adapter-module.js +308 -0
- package/dist/adapter/authoring.d.ts +357 -10
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +87 -1
- package/dist/adapter/constraint.d.ts +9 -3
- package/dist/adapter/constraint.d.ts.map +1 -1
- package/dist/adapter/finders/finder-result.d.ts +28 -0
- package/dist/adapter/finders/finder-result.d.ts.map +1 -0
- package/dist/adapter/finders/finder-result.js +10 -0
- package/dist/adapter/finders/index.d.ts +38 -0
- package/dist/adapter/finders/index.d.ts.map +1 -0
- package/dist/adapter/finders/index.js +41 -0
- package/dist/adapter/finders/prefabs-from-stories.d.ts +64 -0
- package/dist/adapter/finders/prefabs-from-stories.d.ts.map +1 -0
- package/dist/adapter/finders/prefabs-from-stories.js +92 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts +67 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -0
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +226 -0
- package/dist/adapter/first-party-systems.d.ts.map +1 -1
- package/dist/adapter/first-party-systems.js +29 -0
- package/dist/adapter/index.d.ts +7 -3
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +5 -2
- package/dist/adapter/ingest/contract-system-adapters.d.ts +24 -1
- package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -1
- package/dist/adapter/ingest/contract-system-adapters.js +100 -34
- package/dist/adapter/ingest/game-contract.d.ts +188 -2
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +107 -0
- package/dist/adapter/ingest/mount-readiness.d.ts +84 -0
- package/dist/adapter/ingest/mount-readiness.d.ts.map +1 -0
- package/dist/adapter/ingest/mount-readiness.js +81 -0
- package/dist/adapter/ingest/scene-capture.d.ts +91 -2
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +110 -18
- package/dist/adapter/ingest/structural-ids.d.ts +11 -5
- package/dist/adapter/ingest/structural-ids.d.ts.map +1 -1
- package/dist/adapter/ingest/structural-ids.js +18 -9
- package/dist/adapter/ingest/upstream-pin.d.ts +4 -2
- package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -1
- package/dist/adapter/ingest/upstream-pin.js +4 -2
- package/dist/adapter/ingest/visible-capture-window.d.ts +116 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -0
- package/dist/adapter/ingest/visible-capture-window.js +141 -0
- package/dist/adapter/loop-gate-report.d.ts +3 -4
- package/dist/adapter/loop-gate-report.d.ts.map +1 -1
- package/dist/adapter/loop-gate-report.js +3 -4
- package/dist/adapter/root-adapter.d.ts +22 -2
- package/dist/adapter/root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -1
- package/dist/adapter/setup-three-root-adapter.js +15 -0
- package/dist/adapter/system-adapter.d.ts +161 -2
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-adapter.js +18 -1
- package/dist/animation/camera-ownership.d.ts +4 -2
- package/dist/animation/camera-ownership.d.ts.map +1 -1
- package/dist/animation/camera-ownership.js +58 -1
- package/dist/animation/runtime-inspection.d.ts +70 -0
- package/dist/animation/runtime-inspection.d.ts.map +1 -0
- package/dist/animation/runtime-inspection.js +25 -0
- package/dist/animation/xstate-animation-binding.d.ts +9 -1
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +48 -1
- package/dist/asset-formats/material.d.ts +1 -1
- package/dist/asset-formats/mesh.d.ts +1 -1
- package/dist/asset-formats/particles.d.ts +13 -0
- package/dist/asset-formats/particles.d.ts.map +1 -1
- package/dist/asset-formats/particles.js +28 -0
- package/dist/behavior/xstate-inspection.d.ts +24 -0
- package/dist/behavior/xstate-inspection.d.ts.map +1 -0
- package/dist/behavior/xstate-inspection.js +25 -0
- package/dist/core/game-loop.d.ts +7 -5
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +39 -18
- package/dist/core/types.d.ts +11 -12
- package/dist/core/types.d.ts.map +1 -1
- package/dist/data/data-asset.js +1 -1
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +5 -1
- package/dist/dev/render-debug-adapter.d.ts +19 -2
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +10 -0
- package/dist/ecs/user-data.d.ts +10 -2
- package/dist/ecs/user-data.d.ts.map +1 -1
- package/dist/ecs/user-data.js +6 -2
- package/dist/input/binding-identity.d.ts +10 -0
- package/dist/input/binding-identity.d.ts.map +1 -0
- package/dist/input/binding-identity.js +36 -0
- package/dist/input/input-manager.d.ts +18 -185
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +78 -68
- package/dist/manifest/load.d.ts +14 -0
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +6 -1
- package/dist/manifest/schema.d.ts +18 -0
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +65 -8
- package/dist/pixi/authoring.d.ts +27 -0
- package/dist/pixi/authoring.d.ts.map +1 -1
- package/dist/pixi/authoring.js +10 -3
- package/dist/pixi/index.d.ts +1 -1
- package/dist/pixi/index.d.ts.map +1 -1
- package/dist/pixi/index.js +1 -1
- package/dist/pixi/ingest.d.ts +8 -0
- package/dist/pixi/ingest.d.ts.map +1 -1
- package/dist/pixi/ingest.js +10 -1
- package/dist/pixi/render-pass-bracket.d.ts +56 -0
- package/dist/pixi/render-pass-bracket.d.ts.map +1 -0
- package/dist/pixi/render-pass-bracket.js +98 -0
- package/dist/pixi/scene-capture.d.ts +32 -1
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/pixi/scene-capture.js +21 -9
- package/dist/pixi/system-adapters.d.ts +29 -0
- package/dist/pixi/system-adapters.d.ts.map +1 -1
- package/dist/pixi/system-adapters.js +27 -0
- package/dist/render/particles-factory.d.ts +16 -2
- package/dist/render/particles-factory.d.ts.map +1 -1
- package/dist/render/particles-factory.js +88 -5
- package/dist/render/soft-particle-depth.d.ts +149 -0
- package/dist/render/soft-particle-depth.d.ts.map +1 -0
- package/dist/render/soft-particle-depth.js +305 -0
- package/dist/render/voxel-surface.d.ts +117 -0
- package/dist/render/voxel-surface.d.ts.map +1 -0
- package/dist/render/voxel-surface.js +263 -0
- package/dist/runtime/create-runtime.js +7 -0
- package/dist/runtime/debug-bridge.d.ts +15 -15
- package/dist/runtime/debug-bridge.d.ts.map +1 -1
- package/dist/runtime/debug-bridge.js +27 -26
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +1 -2
- package/dist/setup/setup-particles.d.ts.map +1 -1
- package/dist/setup/setup-particles.js +9 -0
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +42 -3
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +201 -11
- package/dist-config/config.js.map +1 -1
- package/package.json +4 -2
- package/schemas/engine-capabilities.json +12 -12
- package/schemas/vgai-project.schema.json +42 -3
- package/src/adapter/adapter-module.ts +607 -0
- package/src/adapter/authoring.ts +440 -10
- package/src/adapter/constraint.ts +10 -3
- package/src/adapter/finders/finder-result.ts +29 -0
- package/src/adapter/finders/index.ts +62 -0
- package/src/adapter/finders/prefabs-from-stories.ts +142 -0
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +304 -0
- package/src/adapter/first-party-systems.ts +30 -0
- package/src/adapter/index.ts +32 -1
- package/src/adapter/ingest/contract-system-adapters.ts +116 -32
- package/src/adapter/ingest/game-contract.ts +265 -2
- package/src/adapter/ingest/mount-readiness.ts +140 -0
- package/src/adapter/ingest/scene-capture.ts +199 -22
- package/src/adapter/ingest/structural-ids.ts +21 -9
- package/src/adapter/ingest/upstream-pin.ts +4 -2
- package/src/adapter/ingest/visible-capture-window.ts +213 -0
- package/src/adapter/loop-gate-report.ts +3 -4
- package/src/adapter/root-adapter.ts +22 -2
- package/src/adapter/setup-three-root-adapter.ts +16 -0
- package/src/adapter/system-adapter.ts +188 -2
- package/src/animation/camera-ownership.ts +68 -3
- package/src/animation/runtime-inspection.ts +104 -0
- package/src/animation/xstate-animation-binding.ts +53 -1
- package/src/asset-formats/particles.ts +31 -0
- package/src/behavior/xstate-inspection.ts +51 -0
- package/src/core/game-loop.ts +39 -17
- package/src/core/types.ts +11 -12
- package/src/data/data-asset.ts +1 -1
- package/src/dev/instruments.ts +5 -1
- package/src/dev/render-debug-adapter.ts +22 -2
- package/src/ecs/user-data.ts +10 -2
- package/src/input/binding-identity.ts +37 -0
- package/src/input/input-manager.ts +93 -68
- package/src/manifest/load.ts +23 -2
- package/src/manifest/schema.ts +77 -8
- package/src/pixi/authoring.ts +37 -5
- package/src/pixi/index.ts +2 -0
- package/src/pixi/ingest.ts +15 -2
- package/src/pixi/render-pass-bracket.ts +117 -0
- package/src/pixi/scene-capture.ts +63 -13
- package/src/pixi/system-adapters.ts +41 -0
- package/src/render/particles-factory.ts +89 -6
- package/src/render/soft-particle-depth.ts +336 -0
- package/src/render/voxel-surface.ts +358 -0
- package/src/runtime/create-runtime.ts +7 -0
- package/src/runtime/debug-bridge.ts +32 -31
- package/src/runtime/game.ts +1 -2
- package/src/setup/setup-particles.ts +9 -0
- package/src/world3d-react/r3f-adapter.tsx +48 -2
- package/src/world3d-react/rapier-physics-bridge.tsx +249 -11
package/dist/adapter/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* host → RootAdapter ← { SetupThreeRootAdapter, IngestRootAdapter, … }
|
|
5
5
|
* editor → AuthoringAdapter ← { LiveThreeAuthoringAdapter, ReactRootAuthoringAdapter, … }
|
|
6
|
-
* game → SystemAdapters (physics/networking/navigation/audio/debug)
|
|
6
|
+
* game → SystemAdapters (physics/networking/navigation/audio/camera/debug)
|
|
7
7
|
*
|
|
8
8
|
* The first-party Rapier/Colyseus stack is ONE implementer of these
|
|
9
9
|
* interfaces, not the engine's vocabulary..
|
|
@@ -23,11 +23,15 @@
|
|
|
23
23
|
* `@engine/adapter/first-party-systems` createNavigationAdapter
|
|
24
24
|
*/
|
|
25
25
|
export type { AdapterSurface } from './adapter-surface';
|
|
26
|
-
export type { AssetDropContext, AssetDropProvider, AssetSubjectProvider, AuthoringAdapter, AuthoringAssetSubject, AuthoringCapabilities, AuthoringProvenance, BoxEditProvider, BoxEditReferencePoint, ColorSampleProvider, CreationSiteProvider, DOMRectLike, EditorNode, EditorNodeRole, HierarchyProvider, InspectorProvider, NodeCreationSite, PersistenceProvider, PickProvider, PropertyDescriptor, RectProvider, RelatedSubjectLink, RelatedSubjectsProvider, SelectionProvider, SelectionResolution, StoriesProvider, StoryRef, StructureProvider, TextProvider, TransformChannel, TransformEditability, TransformObservation, TransformProvider, } from './authoring';
|
|
26
|
+
export type { AssetDropContext, AssetDropProvider, AssetSubjectProvider, AuthoringAdapter, AuthoringAssetSubject, AuthoringCapabilities, AuthoringProvenance, AuthoringProviderKey, BoxEditProvider, BoxEditReferencePoint, ColorSampleProvider, ComponentInstanceApplyResult, ComponentInstanceDescription, ComponentInstanceOverride, ComponentInstancesProvider, CreationSiteProvider, DOMRectLike, EditorNode, EditorNodeRole, HierarchyProvider, InspectorProvider, NodeCreationSite, PersistenceProvider, PickProvider, PropertyDescriptor, RectProvider, RelatedSubjectLink, RelatedSubjectsProvider, SelectionProvider, SelectionResolution, SpatialDragHandle, SpatialHandleGuide, SpatialHandleLayer, SpatialHandlesProvider, SpatialPoint3, StoriesProvider, StoryRef, StructureProvider, TextProvider, TransformChannel, TransformEditability, TransformObservation, TransformProvider, WriteAck, WriteAnchorKind, } from './authoring';
|
|
27
|
+
export { AUTHORING_PROVIDER_KEYS, emptyWriteAnchorKindCounts, measureAuthoringProviders, WRITE_ANCHOR_KINDS, } from './authoring';
|
|
27
28
|
export type { ColyseusNetworkingConfig } from './colyseus-networking-adapter';
|
|
28
29
|
export type { CanvasHostContext, DomHostContext, HostContextBase, HostContextFor, HostSurface, ThreeHostContext, } from './host-context';
|
|
29
30
|
export type { MountedPixiRoot, MountedReactRoot, MountedRoot, MountedRootBase, MountedThreeRoot, RootAdapter, RootStateObserver, SurfaceAdapter, SurfaceAdapterFor, } from './root-adapter';
|
|
30
31
|
export type { MountedSetupThreeRoot, SetupThreeRootConfig } from './setup-three-root-adapter';
|
|
31
|
-
export type { AudioAdapter, AudioDebugEvent, AudioGraphNode, AudioMeterFrame, AudioMeterHandle, AudioTransportState, ConnectionState, NavCrowdAgentState, NavigationAdapter, NavPoint, NetConditioning, NetMessageEvent, NetPeer, NetPlayerIdentity, NetRates, NetServerConfig, NetworkingAdapter, PhysicsAdapter, RenderDebugAdapter, ReplicationStats, RoomInfo, SystemAdapters, Unsubscribe, } from './system-adapter';
|
|
32
|
+
export type { AudioAdapter, AudioDebugEvent, AudioGraphNode, AudioMeterFrame, AudioMeterHandle, AudioRecordingHandle, AudioTransportState, CameraAdapter, CameraRuntimeCamera, CameraRuntimeSnapshot, CameraRuntimeTransition, ConnectionState, NavCrowdAgentState, NavigationAdapter, NavPoint, NetConditioning, NetMessageEvent, NetPeer, NetPlayerIdentity, NetRates, NetServerConfig, NetworkingAdapter, PhysicsAdapter, PhysicsCarrier, PhysicsColliderShape, PhysicsColliderSnapshot, PhysicsJointSnapshot, PhysicsJointType, RenderDebugAdapter, ReplicationStats, RoomInfo, SystemAdapters, Unsubscribe, } from './system-adapter';
|
|
33
|
+
/** The two narrowings of the tagged `SystemAdapters['physics']` union — see
|
|
34
|
+
* `PhysicsAdapter`'s comment for why it is tagged at all. */
|
|
35
|
+
export { displayKeyedPhysics, nodeKeyedPhysics } from './system-adapter';
|
|
32
36
|
export type { Transform, TransformOwner } from './transform';
|
|
33
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,4BAA4B,EAC5B,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,EAC1B,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,EACR,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,cAAc,EACd,WAAW,EACX,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,YAAY,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAC1B;8DAC8D;AAC9D,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* host → RootAdapter ← { SetupThreeRootAdapter, IngestRootAdapter, … }
|
|
5
5
|
* editor → AuthoringAdapter ← { LiveThreeAuthoringAdapter, ReactRootAuthoringAdapter, … }
|
|
6
|
-
* game → SystemAdapters (physics/networking/navigation/audio/debug)
|
|
6
|
+
* game → SystemAdapters (physics/networking/navigation/audio/camera/debug)
|
|
7
7
|
*
|
|
8
8
|
* The first-party Rapier/Colyseus stack is ONE implementer of these
|
|
9
9
|
* interfaces, not the engine's vocabulary..
|
|
@@ -22,4 +22,7 @@
|
|
|
22
22
|
* `@engine/adapter/colyseus-networking-adapter` createColyseusNetworkingAdapter
|
|
23
23
|
* `@engine/adapter/first-party-systems` createNavigationAdapter
|
|
24
24
|
*/
|
|
25
|
-
export {};
|
|
25
|
+
export { AUTHORING_PROVIDER_KEYS, emptyWriteAnchorKindCounts, measureAuthoringProviders, WRITE_ANCHOR_KINDS, } from './authoring';
|
|
26
|
+
/** The two narrowings of the tagged `SystemAdapters['physics']` union — see
|
|
27
|
+
* `PhysicsAdapter`'s comment for why it is tagged at all. */
|
|
28
|
+
export { displayKeyedPhysics, nodeKeyedPhysics } from './system-adapter';
|
|
@@ -25,6 +25,20 @@
|
|
|
25
25
|
* other direction: a wrong "present" claim is as much a defect as a wrong
|
|
26
26
|
* "empty" one.
|
|
27
27
|
*
|
|
28
|
+
* ## Why `physics` also gets a KEYING check
|
|
29
|
+
*
|
|
30
|
+
* The physics slot is the one whose vocabulary follows the SURFACE
|
|
31
|
+
* ({@link PHYSICS_KEYING_BY_SURFACE}): a three-surface game keys by host node
|
|
32
|
+
* id, a canvas-surface game by the display object it already holds. Both shapes
|
|
33
|
+
* carry the same four member names, so presence-and-typeof cannot tell them
|
|
34
|
+
* apart — a carrier keyed for the other surface would pass every check here and
|
|
35
|
+
* then be called with values it has none of, which is the "green coverage row
|
|
36
|
+
* bought with four methods that have no caller" defect this whole slot was left
|
|
37
|
+
* open to avoid. So a mount that KNOWS its surface says so, and a mismatch is
|
|
38
|
+
* `malformed` by name. A caller with no surface fact (`undefined`) does not
|
|
39
|
+
* check — it has nothing to check against, and inventing a default would be a
|
|
40
|
+
* guess wearing a verdict.
|
|
41
|
+
*
|
|
28
42
|
* Nothing here calls a declared method. Presence and shape only.
|
|
29
43
|
*/
|
|
30
44
|
import type { SystemAdapters } from '../system-adapter';
|
|
@@ -32,6 +46,12 @@ import type { VgaiGameSystemAdapters, VgaiGameSystems } from './game-contract';
|
|
|
32
46
|
/** The slots a game may declare through the contract (`debug` is projected from
|
|
33
47
|
* `commands`/`state` instead — see {@link VgaiGameSystemAdapters}). */
|
|
34
48
|
export type ContractSystemSlot = keyof VgaiGameSystemAdapters;
|
|
49
|
+
/**
|
|
50
|
+
* The render surface a declaration is being read ON — the same three words
|
|
51
|
+
* `IngestKind`/`AdapterSurface` already use, so nothing has to be declared
|
|
52
|
+
* twice.
|
|
53
|
+
*/
|
|
54
|
+
export type ContractSurface = 'three' | 'canvas' | 'dom';
|
|
35
55
|
/** Fixed slot order, so two reports of the same mount are diffable line for
|
|
36
56
|
* line (the same rule the coverage report's row order follows). */
|
|
37
57
|
export declare const CONTRACT_SYSTEM_SLOTS: readonly ContractSystemSlot[];
|
|
@@ -59,6 +79,9 @@ export interface ContractSystemAdapterProjection {
|
|
|
59
79
|
* declared no `systemAdapters` at all projects to three empty lists — which is
|
|
60
80
|
* the "nobody answered" case, and is deliberately NOT the same as declaring
|
|
61
81
|
* every slot empty.
|
|
82
|
+
*
|
|
83
|
+
* `surface` is the mount's own surface fact when it has one; see the module
|
|
84
|
+
* header for what it buys and why its absence checks nothing.
|
|
62
85
|
*/
|
|
63
|
-
export declare function projectContractSystemAdapters(systems: VgaiGameSystems | undefined | null): ContractSystemAdapterProjection;
|
|
86
|
+
export declare function projectContractSystemAdapters(systems: VgaiGameSystems | undefined | null, surface?: ContractSurface | undefined): ContractSystemAdapterProjection;
|
|
64
87
|
//# sourceMappingURL=contract-system-adapters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-system-adapters.d.ts","sourceRoot":"","sources":["../../../src/adapter/ingest/contract-system-adapters.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"contract-system-adapters.d.ts","sourceRoot":"","sources":["../../../src/adapter/ingest/contract-system-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,sBAAsB,EAAuB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEpG;wEACwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,MAAM,sBAAsB,CAAC;AA+B9D;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAezD;oEACoE;AACpE,eAAO,MAAM,qBAAqB,EAAE,SAAS,kBAAkB,EAO9D,CAAC;AAEF,yEAAyE;AACzE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,iEAAiE;AACjE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,+BAA+B;IAC9C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAClE,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACnD,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,SAAS,2BAA2B,EAAE,CAAC;CAC5D;AA8FD;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,EAC3C,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,GACpC,+BAA+B,CAoBjC"}
|
|
@@ -25,6 +25,20 @@
|
|
|
25
25
|
* other direction: a wrong "present" claim is as much a defect as a wrong
|
|
26
26
|
* "empty" one.
|
|
27
27
|
*
|
|
28
|
+
* ## Why `physics` also gets a KEYING check
|
|
29
|
+
*
|
|
30
|
+
* The physics slot is the one whose vocabulary follows the SURFACE
|
|
31
|
+
* ({@link PHYSICS_KEYING_BY_SURFACE}): a three-surface game keys by host node
|
|
32
|
+
* id, a canvas-surface game by the display object it already holds. Both shapes
|
|
33
|
+
* carry the same four member names, so presence-and-typeof cannot tell them
|
|
34
|
+
* apart — a carrier keyed for the other surface would pass every check here and
|
|
35
|
+
* then be called with values it has none of, which is the "green coverage row
|
|
36
|
+
* bought with four methods that have no caller" defect this whole slot was left
|
|
37
|
+
* open to avoid. So a mount that KNOWS its surface says so, and a mismatch is
|
|
38
|
+
* `malformed` by name. A caller with no surface fact (`undefined`) does not
|
|
39
|
+
* check — it has nothing to check against, and inventing a default would be a
|
|
40
|
+
* guess wearing a verdict.
|
|
41
|
+
*
|
|
28
42
|
* Nothing here calls a declared method. Presence and shape only.
|
|
29
43
|
*/
|
|
30
44
|
/**
|
|
@@ -50,8 +64,21 @@ const REQUIRED_MEMBERS = {
|
|
|
50
64
|
],
|
|
51
65
|
navigation: ['hasNavMesh', 'findPath', 'debugMesh'],
|
|
52
66
|
audio: ['setMuted', 'isMuted'],
|
|
67
|
+
camera: ['snapshot', 'subscribe'],
|
|
53
68
|
renderDebug: ['captureFrame'],
|
|
54
69
|
};
|
|
70
|
+
/**
|
|
71
|
+
* THE surface→physics-vocabulary rule, stated once.
|
|
72
|
+
*
|
|
73
|
+
* `null` for `dom`: that surface has no physics consumer at all, so nothing
|
|
74
|
+
* here can say which keying would be right for it and the check stands down
|
|
75
|
+
* rather than inventing an answer.
|
|
76
|
+
*/
|
|
77
|
+
const PHYSICS_KEYING_BY_SURFACE = {
|
|
78
|
+
three: 'node-id',
|
|
79
|
+
canvas: 'display',
|
|
80
|
+
dom: null,
|
|
81
|
+
};
|
|
55
82
|
/** Fixed slot order, so two reports of the same mount are diffable line for
|
|
56
83
|
* line (the same rule the coverage report's row order follows). */
|
|
57
84
|
export const CONTRACT_SYSTEM_SLOTS = [
|
|
@@ -59,6 +86,7 @@ export const CONTRACT_SYSTEM_SLOTS = [
|
|
|
59
86
|
'networking',
|
|
60
87
|
'navigation',
|
|
61
88
|
'audio',
|
|
89
|
+
'camera',
|
|
62
90
|
'renderDebug',
|
|
63
91
|
];
|
|
64
92
|
const EMPTY_PROJECTION = {
|
|
@@ -78,13 +106,77 @@ function missingMembers(slot, value) {
|
|
|
78
106
|
}
|
|
79
107
|
return missing;
|
|
80
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The reason a declared physics carrier cannot serve `surface`, or `null` when
|
|
111
|
+
* it can (including when the caller supplied no surface to check against).
|
|
112
|
+
*
|
|
113
|
+
* The keying a carrier actually has is its tag's own answer, and an ABSENT tag
|
|
114
|
+
* IS `'node-id'` — see `PhysicsAdapter`'s comment for why the original
|
|
115
|
+
* vocabulary is the untagged one.
|
|
116
|
+
*/
|
|
117
|
+
function physicsKeyingMismatch(value, surface) {
|
|
118
|
+
const want = surface ? PHYSICS_KEYING_BY_SURFACE[surface] : null;
|
|
119
|
+
if (want === null)
|
|
120
|
+
return null;
|
|
121
|
+
const got = value.keyedBy === 'display' ? 'display' : 'node-id';
|
|
122
|
+
if (got === want)
|
|
123
|
+
return null;
|
|
124
|
+
return (`declared a ${got}-keyed physics carrier on the ${surface} surface, whose gizmo path ` +
|
|
125
|
+
`addresses bodies by ${want} — the four members are all present but nothing on this lane ` +
|
|
126
|
+
`can call them with values they accept (a ${surface}-surface carrier declares ` +
|
|
127
|
+
`\`keyedBy: '${want}'\`)`);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* THE per-slot decision, in the order the module header states: is it declared
|
|
131
|
+
* at all, is it the positively-answered empty, does it carry the members its
|
|
132
|
+
* interface requires, and (physics only) is it keyed for this surface.
|
|
133
|
+
*
|
|
134
|
+
* Split out of {@link projectContractSystemAdapters} so the projection itself
|
|
135
|
+
* is a loop over verdicts — one place decides, one place files.
|
|
136
|
+
*/
|
|
137
|
+
function classifyDeclaredSlot(slot, value, surface) {
|
|
138
|
+
if (value === undefined || value === null)
|
|
139
|
+
return { kind: 'skip' };
|
|
140
|
+
if (typeof value !== 'object') {
|
|
141
|
+
return {
|
|
142
|
+
kind: 'malformed',
|
|
143
|
+
reason: `declared as ${typeof value}; expected an adapter object or { present: false, evidence }`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (isEmptyRecord(value)) {
|
|
147
|
+
const evidence = value.evidence;
|
|
148
|
+
// An empty claim with no evidence is exactly the "wrong empty claim" the
|
|
149
|
+
// bar calls a defect — it asserts an absence nobody can re-check.
|
|
150
|
+
if (typeof evidence !== 'string' || evidence.trim() === '') {
|
|
151
|
+
return {
|
|
152
|
+
kind: 'malformed',
|
|
153
|
+
reason: 'declared `present: false` with no `evidence` string to back the absence',
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return { kind: 'empty', evidence };
|
|
157
|
+
}
|
|
158
|
+
const missing = missingMembers(slot, value);
|
|
159
|
+
if (missing.length > 0) {
|
|
160
|
+
return {
|
|
161
|
+
kind: 'malformed',
|
|
162
|
+
reason: `declared an adapter missing required member(s): ${missing.join(', ')}`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const mismatch = slot === 'physics' ? physicsKeyingMismatch(value, surface) : null;
|
|
166
|
+
if (mismatch !== null)
|
|
167
|
+
return { kind: 'malformed', reason: mismatch };
|
|
168
|
+
return { kind: 'bound' };
|
|
169
|
+
}
|
|
81
170
|
/**
|
|
82
171
|
* Read the declared slots and sort each into exactly one verdict. A game that
|
|
83
172
|
* declared no `systemAdapters` at all projects to three empty lists — which is
|
|
84
173
|
* the "nobody answered" case, and is deliberately NOT the same as declaring
|
|
85
174
|
* every slot empty.
|
|
175
|
+
*
|
|
176
|
+
* `surface` is the mount's own surface fact when it has one; see the module
|
|
177
|
+
* header for what it buys and why its absence checks nothing.
|
|
86
178
|
*/
|
|
87
|
-
export function projectContractSystemAdapters(systems) {
|
|
179
|
+
export function projectContractSystemAdapters(systems, surface) {
|
|
88
180
|
const declared = systems?.systemAdapters;
|
|
89
181
|
if (!declared)
|
|
90
182
|
return EMPTY_PROJECTION;
|
|
@@ -92,39 +184,13 @@ export function projectContractSystemAdapters(systems) {
|
|
|
92
184
|
const empty = [];
|
|
93
185
|
const malformed = [];
|
|
94
186
|
for (const slot of CONTRACT_SYSTEM_SLOTS) {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (isEmptyRecord(value)) {
|
|
106
|
-
const evidence = value.evidence;
|
|
107
|
-
if (typeof evidence !== 'string' || evidence.trim() === '') {
|
|
108
|
-
// An empty claim with no evidence is exactly the "wrong empty claim"
|
|
109
|
-
// the bar calls a defect — it asserts an absence nobody can re-check.
|
|
110
|
-
malformed.push({
|
|
111
|
-
slot,
|
|
112
|
-
reason: 'declared `present: false` with no `evidence` string to back the absence',
|
|
113
|
-
});
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
empty.push({ slot, evidence });
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
const missing = missingMembers(slot, value);
|
|
120
|
-
if (missing.length > 0) {
|
|
121
|
-
malformed.push({
|
|
122
|
-
slot,
|
|
123
|
-
reason: `declared an adapter missing required member(s): ${missing.join(', ')}`,
|
|
124
|
-
});
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
bound[slot] = value;
|
|
187
|
+
const verdict = classifyDeclaredSlot(slot, declared[slot], surface);
|
|
188
|
+
if (verdict.kind === 'bound')
|
|
189
|
+
bound[slot] = declared[slot];
|
|
190
|
+
else if (verdict.kind === 'empty')
|
|
191
|
+
empty.push({ slot, evidence: verdict.evidence });
|
|
192
|
+
else if (verdict.kind === 'malformed')
|
|
193
|
+
malformed.push({ slot, reason: verdict.reason });
|
|
128
194
|
}
|
|
129
195
|
return {
|
|
130
196
|
bound: bound,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Origin (root), F17+F21 (lifecycle), WO-SYS1 (systems), WO-SYS2
|
|
19
19
|
* (`systems.systemAdapters`).
|
|
20
20
|
*/
|
|
21
|
-
import type { AudioAdapter, NavigationAdapter, NetworkingAdapter,
|
|
21
|
+
import type { AudioAdapter, CameraAdapter, NavigationAdapter, NetworkingAdapter, PhysicsCarrier, RenderDebugAdapter } from '../system-adapter';
|
|
22
22
|
/**
|
|
23
23
|
* Session lifecycle endpoints. Declaring `start` means "I support cold
|
|
24
24
|
* mount": when the host sets `window.__vgaiMountCold` before the game's entry
|
|
@@ -34,6 +34,43 @@ export interface VgaiGameLifecycle {
|
|
|
34
34
|
pause?(): void;
|
|
35
35
|
resume?(): void;
|
|
36
36
|
}
|
|
37
|
+
/** One of the game's own scenes, as {@link VgaiGameScenes} lists it. `id` is the
|
|
38
|
+
* game's OWN identifier for it — the string `current()` reports and `goTo()`
|
|
39
|
+
* takes — and `label` is what a person reads. */
|
|
40
|
+
export interface VgaiGameScene {
|
|
41
|
+
id: string;
|
|
42
|
+
label: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The game's SCENES: the whole-world states it swaps between — screens, levels,
|
|
46
|
+
* rooms — named by the game itself.
|
|
47
|
+
*
|
|
48
|
+
* A multi-screen game's screens ARE its scenes, and the editor already has a
|
|
49
|
+
* vocabulary for "the design-time states a world can be put into": stories.
|
|
50
|
+
* So this is projected onto the ordinary `StoriesProvider`
|
|
51
|
+
* (`adapter/authoring.ts`) by the host's canvas ingest mount
|
|
52
|
+
* (`packages/editor/src/authoring/contract-scenes-stories.ts`), WORLD-LEVEL —
|
|
53
|
+
* the same scope the react world adapter's stories have — so the inspector's
|
|
54
|
+
* story picker lists a game's screens and switching one runs the game's own
|
|
55
|
+
* navigation call. Nothing else is invented: there is no host-side scene model,
|
|
56
|
+
* no ordering rule, and no "no scene" state.
|
|
57
|
+
*
|
|
58
|
+
* Presence-only, like the rest of this contract. A game that declares nothing
|
|
59
|
+
* here reports no stories provider at all — a named absence in the coverage
|
|
60
|
+
* report, never an empty picker.
|
|
61
|
+
*/
|
|
62
|
+
export interface VgaiGameScenes {
|
|
63
|
+
/** Every scene a player can be sent to right now, in the game's own order. */
|
|
64
|
+
list(): VgaiGameScene[];
|
|
65
|
+
/** The scene the game currently has up, by its own id; `null` when it has
|
|
66
|
+
* none yet (a game whose entry has not resolved, a boot screen the game does
|
|
67
|
+
* not name). */
|
|
68
|
+
current(): string | null;
|
|
69
|
+
/** Put the game in that scene the way the game's OWN navigation does. May be
|
|
70
|
+
* async — a scene commonly loads its own assets first — and the host awaits
|
|
71
|
+
* whatever it returns before reporting the switch. */
|
|
72
|
+
goTo(id: string): Promise<unknown> | unknown;
|
|
73
|
+
}
|
|
37
74
|
/**
|
|
38
75
|
* One invokable verb the game exposes. Deliberately the SAME listing shape a
|
|
39
76
|
* first-party game gets from `ctx.debug.registerCommand`
|
|
@@ -157,12 +194,46 @@ export interface VgaiGameSystemEmpty {
|
|
|
157
194
|
* for which wins.
|
|
158
195
|
*/
|
|
159
196
|
export interface VgaiGameSystemAdapters {
|
|
160
|
-
|
|
197
|
+
/**
|
|
198
|
+
* In the SURFACE'S OWN vocabulary: a three-surface game declares the node-id
|
|
199
|
+
* keyed `PhysicsAdapter`, a canvas-surface game the display-object keyed
|
|
200
|
+
* `PhysicsAdapter2D` (`keyedBy: 'display'` — the tag is what makes the two
|
|
201
|
+
* tellable apart, and `contract-system-adapters.ts` reports a carrier keyed
|
|
202
|
+
* for the wrong surface as MALFORMED by name rather than binding a shape
|
|
203
|
+
* nothing on that lane can call).
|
|
204
|
+
*/
|
|
205
|
+
physics?: PhysicsCarrier | VgaiGameSystemEmpty;
|
|
161
206
|
networking?: NetworkingAdapter | VgaiGameSystemEmpty;
|
|
162
207
|
navigation?: NavigationAdapter | VgaiGameSystemEmpty;
|
|
163
208
|
audio?: AudioAdapter | VgaiGameSystemEmpty;
|
|
209
|
+
camera?: CameraAdapter | VgaiGameSystemEmpty;
|
|
164
210
|
renderDebug?: RenderDebugAdapter | VgaiGameSystemEmpty;
|
|
165
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* The game's own READINESS signal — "my world is built; what you see now is
|
|
214
|
+
* the game".
|
|
215
|
+
*
|
|
216
|
+
* A host-mounted (exported-composition) root needs none of this: the host runs
|
|
217
|
+
* the mount, so mount completion IS readiness and the host answers the question
|
|
218
|
+
* itself. A SELF-BOOTING game owns its own boot, and the host has no way to see
|
|
219
|
+
* the end of it — which is why every reader used to measure instead: poll the
|
|
220
|
+
* draw count, watch `scene.children.length` stop growing, wait a fixed window.
|
|
221
|
+
* Each of those is a guess at a fact the game's author can state, which is
|
|
222
|
+
* exactly the shape zero inference names (ARCHITECTURE-CORE §The editor
|
|
223
|
+
* protocol), so the fix is this declaration slot rather than a better poll.
|
|
224
|
+
*
|
|
225
|
+
* Either form is legal, because both are how games already write it: a promise
|
|
226
|
+
* the game resolves when its boot finishes, or a function returning one (a
|
|
227
|
+
* callback that resolves/returns when ready). The host normalizes both through
|
|
228
|
+
* {@link readGameReady} and awaits exactly once per mount.
|
|
229
|
+
*
|
|
230
|
+
* Declaring nothing is a supported, first-class answer: the measured waits
|
|
231
|
+
* remain as the documented fallback. What changes is that they stop being
|
|
232
|
+
* SILENT — a root with no declaration reports `source: 'measured'` wherever
|
|
233
|
+
* readiness is reported, so "nobody stated it" is a visible fact rather than
|
|
234
|
+
* an invisible default.
|
|
235
|
+
*/
|
|
236
|
+
export type VgaiGameReady = Promise<unknown> | (() => Promise<unknown> | unknown);
|
|
166
237
|
export interface VgaiGameContract {
|
|
167
238
|
/** Bump only on breaking shape changes; additive endpoints keep version 1. */
|
|
168
239
|
contractVersion: 1;
|
|
@@ -173,7 +244,43 @@ export interface VgaiGameContract {
|
|
|
173
244
|
* verifies it actually contains the captured canvas before adopting.
|
|
174
245
|
*/
|
|
175
246
|
root?: HTMLElement;
|
|
247
|
+
/**
|
|
248
|
+
* The canvas the game PRESENTS on — the one whose pixels are "what the game
|
|
249
|
+
* looks like".
|
|
250
|
+
*
|
|
251
|
+
* `root` above answers a different question (which element owns the game's
|
|
252
|
+
* whole DOM), and a game routinely has more than one canvas inside it: an
|
|
253
|
+
* offscreen buffer it composites from, a minimap, a 2D overlay. The host's
|
|
254
|
+
* standing answer was "the first `<canvas>` in DOM order", which is a guess
|
|
255
|
+
* about authoring order, and the game's author knows the real answer.
|
|
256
|
+
*
|
|
257
|
+
* Declaring nothing keeps the measured answer, reported as `measured` (see
|
|
258
|
+
* `packages/editor/src/presentation-surface.ts`, the one reader).
|
|
259
|
+
*/
|
|
260
|
+
presentation?: HTMLCanvasElement;
|
|
261
|
+
/**
|
|
262
|
+
* The game's own WORLD — the `THREE.Scene` the editor should adopt.
|
|
263
|
+
*
|
|
264
|
+
* Without it the host adopts whichever scene renders first that it did not
|
|
265
|
+
* draw itself (`scene-capture.ts`), which is a good measured default and a
|
|
266
|
+
* permanent, silent commitment: a splash screen, a shadow pre-pass or a
|
|
267
|
+
* render-to-texture warm-up that draws one frame earlier is adopted as the
|
|
268
|
+
* game forever. Declaring this makes the choice the game's, not a race's.
|
|
269
|
+
*
|
|
270
|
+
* Typed `object` rather than `THREE.Scene` on purpose — this file is the
|
|
271
|
+
* contract's shape and imports no renderer library; the host checks
|
|
272
|
+
* structurally (`isScene`) in {@link readGameWorld}.
|
|
273
|
+
*/
|
|
274
|
+
world?: object;
|
|
275
|
+
/** The game's own readiness signal — see {@link VgaiGameReady}. */
|
|
276
|
+
ready?: VgaiGameReady;
|
|
176
277
|
lifecycle?: VgaiGameLifecycle;
|
|
278
|
+
/**
|
|
279
|
+
* The game's own scenes. Absent means "this game declared no scene surface" —
|
|
280
|
+
* the mount then supplies no stories provider at all, and the coverage report
|
|
281
|
+
* says so by name.
|
|
282
|
+
*/
|
|
283
|
+
scenes?: VgaiGameScenes;
|
|
177
284
|
/**
|
|
178
285
|
* The game's own systems. Absent means "this game declared no system
|
|
179
286
|
* surface" — every dependent editor surface then shows a named absence, not
|
|
@@ -193,4 +300,83 @@ export interface VgaiGameContract {
|
|
|
193
300
|
* happens to be asking.
|
|
194
301
|
*/
|
|
195
302
|
export declare function readGameContract(scope?: Window | null | undefined): VgaiGameContract | null;
|
|
303
|
+
/** What {@link readGameScenes} found. Both fields `null` ⇒ the game declared no
|
|
304
|
+
* scene surface at all, which is a different answer from declaring an unusable
|
|
305
|
+
* one (the same distinction `contract-system-adapters.ts` draws between an
|
|
306
|
+
* omitted slot and a malformed one). */
|
|
307
|
+
export interface GameScenesReading {
|
|
308
|
+
/** The callable surface, or `null` when nothing usable was declared. */
|
|
309
|
+
readonly scenes: VgaiGameScenes | null;
|
|
310
|
+
/** Why a DECLARED surface was refused, in the words the host prints. */
|
|
311
|
+
readonly malformed: string | null;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Read the declared {@link VgaiGameScenes}, refusing a malformed one BY NAME.
|
|
315
|
+
*
|
|
316
|
+
* The shape check is the same honesty `projectContractSystemAdapters` applies to
|
|
317
|
+
* a declared system slot: a `scenes: {}` would otherwise register as a
|
|
318
|
+
* capability and light up a story picker over an object with no methods. Nothing
|
|
319
|
+
* here CALLS a declared endpoint — presence and type only.
|
|
320
|
+
*/
|
|
321
|
+
export declare function readGameScenes(contract: VgaiGameContract | null | undefined): GameScenesReading;
|
|
322
|
+
/** What {@link readGameReady} found. `ready: null` with `malformed: null` ⇒ the
|
|
323
|
+
* game declared no readiness signal, which is a different answer from
|
|
324
|
+
* declaring an unusable one — the same distinction {@link readGameScenes}
|
|
325
|
+
* draws, and the reason the readiness facet can report `declared` vs
|
|
326
|
+
* `measured` honestly instead of collapsing both into "not ready yet". */
|
|
327
|
+
export interface GameReadyReading {
|
|
328
|
+
/** The normalized signal: awaiting it once resolves when the game says it is
|
|
329
|
+
* ready. `null` when nothing usable was declared. */
|
|
330
|
+
readonly ready: (() => Promise<unknown>) | null;
|
|
331
|
+
/** Why a DECLARED signal was refused, in the words the host prints. */
|
|
332
|
+
readonly malformed: string | null;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Read the declared {@link VgaiGameReady}, refusing a malformed one BY NAME.
|
|
336
|
+
*
|
|
337
|
+
* Both legal forms normalize to one call: a promise is wrapped, a function is
|
|
338
|
+
* invoked lazily (once, when the host asks — which in the ingest mount is only
|
|
339
|
+
* AFTER the first captured render, so a `ready` that itself triggers the boot
|
|
340
|
+
* would deadlock the capture window; games self-boot on this lane),
|
|
341
|
+
* and whatever the function returns is coerced through `Promise.resolve` —
|
|
342
|
+
* a synchronous return means "already ready", which is a real declaration.
|
|
343
|
+
*
|
|
344
|
+
* A rejected/throwing signal is NOT swallowed here: the host awaits it and its
|
|
345
|
+
* failure is what separates "crashed before ready" from "never became ready"
|
|
346
|
+
* in the mount-failure report (`mount-readiness.ts`).
|
|
347
|
+
*/
|
|
348
|
+
export declare function readGameReady(contract: VgaiGameContract | null | undefined): GameReadyReading;
|
|
349
|
+
/** What {@link readGameWorld} found — same two-field honesty as
|
|
350
|
+
* {@link readGameScenes}. */
|
|
351
|
+
export interface GameWorldReading {
|
|
352
|
+
readonly world: object | null;
|
|
353
|
+
readonly malformed: string | null;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Read the declared {@link VgaiGameContract.world}, refusing a malformed one BY
|
|
357
|
+
* NAME.
|
|
358
|
+
*
|
|
359
|
+
* STRUCTURAL (`isScene === true`), for two reasons: this file imports no
|
|
360
|
+
* renderer library, and an `instanceof THREE.Scene` across the realm the game
|
|
361
|
+
* runs in is exactly the check that silently answers `false` for a good scene —
|
|
362
|
+
* turning a correct declaration into an unexplained fall-back to first-render.
|
|
363
|
+
*/
|
|
364
|
+
export declare function readGameWorld(contract: VgaiGameContract | null | undefined): GameWorldReading;
|
|
365
|
+
/** What {@link readGamePresentation} found — same two-field honesty as
|
|
366
|
+
* {@link readGameScenes}. */
|
|
367
|
+
export interface GamePresentationReading {
|
|
368
|
+
readonly canvas: HTMLCanvasElement | null;
|
|
369
|
+
readonly malformed: string | null;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Read the declared {@link VgaiGameContract.presentation} canvas, refusing a
|
|
373
|
+
* malformed one BY NAME.
|
|
374
|
+
*
|
|
375
|
+
* The shape check is STRUCTURAL (`tagName === 'CANVAS'` plus a `getContext`),
|
|
376
|
+
* not `instanceof`: the contract is read out of the realm the game runs in, and
|
|
377
|
+
* a realm-crossing `instanceof` is exactly the check that silently answers
|
|
378
|
+
* `false` for a perfectly good element. A wrong declaration must be refused by
|
|
379
|
+
* name, never by accident.
|
|
380
|
+
*/
|
|
381
|
+
export declare function readGamePresentation(contract: VgaiGameContract | null | undefined): GamePresentationReading;
|
|
196
382
|
//# sourceMappingURL=game-contract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"game-contract.d.ts","sourceRoot":"","sources":["../../../src/adapter/ingest/game-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACjC,IAAI,IAAI,OAAO,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,sBAAsB,EAAE,CAAC;IAC7B,SAAS,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;IAC7B,mFAAmF;IACnF,KAAK,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAChC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,OAAO,EAAE,KAAK,CAAC;IACf;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,cAAc,GAAG,mBAAmB,CAAC;IAC/C,UAAU,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,CAAC;IACrD,UAAU,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,CAAC;IACrD,KAAK,CAAC,EAAE,YAAY,GAAG,mBAAmB,CAAC;IAC3C,WAAW,CAAC,EAAE,kBAAkB,GAAG,mBAAmB,CAAC;CACxD;AAED,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,eAAe,EAAE,CAAC,CAAC;IACnB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,GAAE,MAAM,GAAG,IAAI,GAAG,SAAyD,GAC/E,gBAAgB,GAAG,IAAI,CAKzB"}
|
|
1
|
+
{"version":3,"file":"game-contract.d.ts","sourceRoot":"","sources":["../../../src/adapter/ingest/game-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,3 +36,110 @@ export function readGameContract(scope = typeof window === 'undefined' ? null :
|
|
|
36
36
|
return null;
|
|
37
37
|
return declared;
|
|
38
38
|
}
|
|
39
|
+
/** The endpoints a scene surface must carry: every one of them is called by the
|
|
40
|
+
* projection, so a declaration missing any cannot be honoured. */
|
|
41
|
+
const SCENES_REQUIRED_MEMBERS = ['list', 'current', 'goTo'];
|
|
42
|
+
/**
|
|
43
|
+
* Read the declared {@link VgaiGameScenes}, refusing a malformed one BY NAME.
|
|
44
|
+
*
|
|
45
|
+
* The shape check is the same honesty `projectContractSystemAdapters` applies to
|
|
46
|
+
* a declared system slot: a `scenes: {}` would otherwise register as a
|
|
47
|
+
* capability and light up a story picker over an object with no methods. Nothing
|
|
48
|
+
* here CALLS a declared endpoint — presence and type only.
|
|
49
|
+
*/
|
|
50
|
+
export function readGameScenes(contract) {
|
|
51
|
+
const declared = contract?.scenes;
|
|
52
|
+
if (declared === undefined || declared === null)
|
|
53
|
+
return { scenes: null, malformed: null };
|
|
54
|
+
if (typeof declared !== 'object') {
|
|
55
|
+
return {
|
|
56
|
+
scenes: null,
|
|
57
|
+
malformed: `declared as ${typeof declared}; expected an object with list(), current() and goTo()`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const missing = SCENES_REQUIRED_MEMBERS.filter((member) => typeof declared[member] !== 'function');
|
|
61
|
+
if (missing.length > 0) {
|
|
62
|
+
return {
|
|
63
|
+
scenes: null,
|
|
64
|
+
malformed: `declared a scenes surface missing required member(s): ${missing.join(', ')}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { scenes: declared, malformed: null };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read the declared {@link VgaiGameReady}, refusing a malformed one BY NAME.
|
|
71
|
+
*
|
|
72
|
+
* Both legal forms normalize to one call: a promise is wrapped, a function is
|
|
73
|
+
* invoked lazily (once, when the host asks — which in the ingest mount is only
|
|
74
|
+
* AFTER the first captured render, so a `ready` that itself triggers the boot
|
|
75
|
+
* would deadlock the capture window; games self-boot on this lane),
|
|
76
|
+
* and whatever the function returns is coerced through `Promise.resolve` —
|
|
77
|
+
* a synchronous return means "already ready", which is a real declaration.
|
|
78
|
+
*
|
|
79
|
+
* A rejected/throwing signal is NOT swallowed here: the host awaits it and its
|
|
80
|
+
* failure is what separates "crashed before ready" from "never became ready"
|
|
81
|
+
* in the mount-failure report (`mount-readiness.ts`).
|
|
82
|
+
*/
|
|
83
|
+
export function readGameReady(contract) {
|
|
84
|
+
const declared = contract?.ready;
|
|
85
|
+
if (declared === undefined || declared === null)
|
|
86
|
+
return { ready: null, malformed: null };
|
|
87
|
+
if (typeof declared === 'function') {
|
|
88
|
+
const fn = declared;
|
|
89
|
+
return { ready: () => Promise.resolve(fn()), malformed: null };
|
|
90
|
+
}
|
|
91
|
+
if (typeof declared.then === 'function') {
|
|
92
|
+
const thenable = declared;
|
|
93
|
+
return { ready: () => Promise.resolve(thenable), malformed: null };
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
ready: null,
|
|
97
|
+
malformed: `declared \`ready\` as ${typeof declared}; expected a promise or a function returning one`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Read the declared {@link VgaiGameContract.world}, refusing a malformed one BY
|
|
102
|
+
* NAME.
|
|
103
|
+
*
|
|
104
|
+
* STRUCTURAL (`isScene === true`), for two reasons: this file imports no
|
|
105
|
+
* renderer library, and an `instanceof THREE.Scene` across the realm the game
|
|
106
|
+
* runs in is exactly the check that silently answers `false` for a good scene —
|
|
107
|
+
* turning a correct declaration into an unexplained fall-back to first-render.
|
|
108
|
+
*/
|
|
109
|
+
export function readGameWorld(contract) {
|
|
110
|
+
const declared = contract?.world;
|
|
111
|
+
if (declared === undefined || declared === null)
|
|
112
|
+
return { world: null, malformed: null };
|
|
113
|
+
if (typeof declared !== 'object' || declared.isScene !== true) {
|
|
114
|
+
return {
|
|
115
|
+
world: null,
|
|
116
|
+
malformed: `declared \`world\` as ${typeof declared}; expected the game's own THREE.Scene`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return { world: declared, malformed: null };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Read the declared {@link VgaiGameContract.presentation} canvas, refusing a
|
|
123
|
+
* malformed one BY NAME.
|
|
124
|
+
*
|
|
125
|
+
* The shape check is STRUCTURAL (`tagName === 'CANVAS'` plus a `getContext`),
|
|
126
|
+
* not `instanceof`: the contract is read out of the realm the game runs in, and
|
|
127
|
+
* a realm-crossing `instanceof` is exactly the check that silently answers
|
|
128
|
+
* `false` for a perfectly good element. A wrong declaration must be refused by
|
|
129
|
+
* name, never by accident.
|
|
130
|
+
*/
|
|
131
|
+
export function readGamePresentation(contract) {
|
|
132
|
+
const declared = contract?.presentation;
|
|
133
|
+
if (declared === undefined || declared === null)
|
|
134
|
+
return { canvas: null, malformed: null };
|
|
135
|
+
const el = declared;
|
|
136
|
+
if (typeof el !== 'object' || el.tagName !== 'CANVAS' || typeof el.getContext !== 'function') {
|
|
137
|
+
return {
|
|
138
|
+
canvas: null,
|
|
139
|
+
malformed: `declared \`presentation\` as ${typeof declared === 'object'
|
|
140
|
+
? String(el.tagName ?? 'object')
|
|
141
|
+
: typeof declared}; expected the game's own <canvas> element`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return { canvas: declared, malformed: null };
|
|
145
|
+
}
|