@volter/editor-game 0.5.65
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/LICENSE +661 -0
- package/NOTICE +23 -0
- package/contributions/asset-budget-asset.menu.ts +26 -0
- package/contributions/asset-budget.action.ts +18 -0
- package/contributions/asset-budget.document.tsx +21 -0
- package/contributions/asset-budget.menu.ts +22 -0
- package/contributions/audio-unlock.service.ts +17 -0
- package/contributions/audio.utility.tsx +15 -0
- package/contributions/autoplay.service.ts +48 -0
- package/contributions/bridge.command.ts +172 -0
- package/contributions/build-profiles.document.tsx +21 -0
- package/contributions/build-progress.status.tsx +45 -0
- package/contributions/build.action.ts +26 -0
- package/contributions/build.command.ts +27 -0
- package/contributions/build.header.tsx +37 -0
- package/contributions/build.menu.ts +31 -0
- package/contributions/build.service.ts +31 -0
- package/contributions/connection.status.tsx +43 -0
- package/contributions/coverage.service.ts +118 -0
- package/contributions/edit-mode-audio.service.ts +23 -0
- package/contributions/edit-mode-networking.service.ts +30 -0
- package/contributions/game-document.service.ts +25 -0
- package/contributions/game-eval.command.ts +210 -0
- package/contributions/game.layout.ts +19 -0
- package/contributions/gameplay.command.ts +255 -0
- package/contributions/generation.service.ts +96 -0
- package/contributions/generations.status.tsx +53 -0
- package/contributions/ingest.service.ts +80 -0
- package/contributions/instances.command.ts +75 -0
- package/contributions/navmesh.menu.ts +39 -0
- package/contributions/navmesh.service.ts +19 -0
- package/contributions/network.utility.tsx +17 -0
- package/contributions/play.command.ts +355 -0
- package/contributions/profiler.action.ts +16 -0
- package/contributions/profiler.menu.ts +22 -0
- package/contributions/profiler.utility.tsx +17 -0
- package/contributions/react/component-board.service.ts +21 -0
- package/contributions/react/design-time-mount.service.ts +60 -0
- package/contributions/react/pasteboard.action.ts +41 -0
- package/contributions/react/react-inspector.service.ts +85 -0
- package/contributions/react/story-documents.service.ts +44 -0
- package/contributions/scene-document.service.ts +32 -0
- package/contributions/state-watch.action.ts +17 -0
- package/contributions/state-watch.menu.ts +23 -0
- package/contributions/state-watch.utility.tsx +20 -0
- package/contributions/team-playtest.service.ts +124 -0
- package/contributions/three/camera-runtime.inspector.tsx +34 -0
- package/contributions/three/component-board.service.ts +24 -0
- package/contributions/three/component-verbs.command.ts +110 -0
- package/contributions/three/component-verbs.service.ts +92 -0
- package/contributions/three/constraints.inspector.tsx +34 -0
- package/contributions/three/model-asset-sections.service.ts +26 -0
- package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
- package/contributions/three/story-documents.service.ts +31 -0
- package/contributions/three/three-authoring.service.ts +66 -0
- package/contributions/transport.header.tsx +19 -0
- package/contributions/xstate-behavior.action.ts +42 -0
- package/contributions/xstate-behavior.document.tsx +73 -0
- package/contributions/xstate-behavior.inspector.tsx +28 -0
- package/contributions/xstate-behavior.menu.ts +23 -0
- package/package.json +144 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
- package/src/asset-budget/asset-budget-model.ts +799 -0
- package/src/asset-budget/basis-encoder.ts +165 -0
- package/src/asset-budget/gltf-io.ts +154 -0
- package/src/asset-budget/gltf-optimize.ts +384 -0
- package/src/asset-budget/image-dims.ts +78 -0
- package/src/asset-budget/optimize-apply.ts +221 -0
- package/src/audio/AudioDebuggerPanel.tsx +457 -0
- package/src/audio/audio-debugger-model.ts +87 -0
- package/src/bridge/call.ts +60 -0
- package/src/bridge/dispatch.ts +459 -0
- package/src/bridge/live-frames.ts +25 -0
- package/src/bridge/screenshot.ts +316 -0
- package/src/build/BuildProfilesPanel.tsx +476 -0
- package/src/build/build-session.ts +247 -0
- package/src/build/format-bytes.ts +14 -0
- package/src/command-results.ts +36 -0
- package/src/coverage/live-authoring-surface.ts +30 -0
- package/src/coverage/live-project-verbs.ts +162 -0
- package/src/coverage/native-system-coverage.ts +143 -0
- package/src/coverage/root-coverage.ts +79 -0
- package/src/coverage/session-coverage.ts +193 -0
- package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
- package/src/edit-mode/edit-mode-audio.ts +56 -0
- package/src/edit-mode/edit-mode-networking.ts +112 -0
- package/src/game-document/DevicePresetPicker.tsx +84 -0
- package/src/game-document/GameCaptureFrameButton.tsx +45 -0
- package/src/game-document/GameDocument.tsx +215 -0
- package/src/game-document/GamePanel.tsx +662 -0
- package/src/game-document/InstanceInspectorPicker.tsx +183 -0
- package/src/game-document/crowd-debug.ts +183 -0
- package/src/game-document/device-preview.ts +336 -0
- package/src/game-document/game-view-store.ts +107 -0
- package/src/game-document/physics-debug.ts +187 -0
- package/src/generation/GenerationActivity.tsx +421 -0
- package/src/generation/GenerationGallery.css +231 -0
- package/src/generation/generation-documents.tsx +338 -0
- package/src/generation/generation-jobs.ts +128 -0
- package/src/generation/generation-presentation.ts +257 -0
- package/src/host/adapter-reach.ts +445 -0
- package/src/host/adapter-runtime-bindings.ts +303 -0
- package/src/host/after-paint.ts +112 -0
- package/src/host/api/configurations.ts +66 -0
- package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
- package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
- package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
- package/src/host/authoring/contract-scenes-stories.ts +204 -0
- package/src/host/authoring/creation-site-related.ts +55 -0
- package/src/host/authoring/ephemeral-persistence.ts +29 -0
- package/src/host/authoring/gesture-persist.ts +84 -0
- package/src/host/authoring/ingest-data-writer.ts +232 -0
- package/src/host/authoring/ingest-source-persistence.ts +826 -0
- package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
- package/src/host/authoring/mounted-authoring.ts +41 -0
- package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
- package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
- package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
- package/src/host/authoring/pixi-isolation-assets.ts +25 -0
- package/src/host/authoring/pixi-live-write-target.ts +979 -0
- package/src/host/authoring/pixi-source-identity.ts +141 -0
- package/src/host/authoring/pixi-still-presentation.ts +71 -0
- package/src/host/authoring/pixi-structure-history.ts +237 -0
- package/src/host/authoring/pixi-transform-channels.ts +205 -0
- package/src/host/authoring/selection-remount-handoff.ts +23 -0
- package/src/host/authoring/source-persistence-backend.ts +373 -0
- package/src/host/authoring/source-refresh-revisions.ts +81 -0
- package/src/host/authoring/struct-write-pipe.ts +143 -0
- package/src/host/auto-frame-window.ts +89 -0
- package/src/host/binding-resolver.ts +393 -0
- package/src/host/browser-transpile.ts +631 -0
- package/src/host/canvas-entry-runtime.ts +95 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
- package/src/host/components/HeaderTelemetry.tsx +341 -0
- package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
- package/src/host/components/ResolutionPicker.tsx +89 -0
- package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
- package/src/host/components/frame-debugger-model.ts +579 -0
- package/src/host/components/header-telemetry-model.ts +74 -0
- package/src/host/components/scene-document.tsx +447 -0
- package/src/host/components/utility-view-state.ts +87 -0
- package/src/host/components/world-root-stage-binding.tsx +133 -0
- package/src/host/components/world-root-stage.ts +1046 -0
- package/src/host/coverage/authoring-read-probe.ts +583 -0
- package/src/host/coverage/capability-coverage.ts +1587 -0
- package/src/host/coverage/coverage-accounting.ts +261 -0
- package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
- package/src/host/coverage/project-verb-coverage.ts +148 -0
- package/src/host/coverage/system-adapter-coverage.ts +373 -0
- package/src/host/design-system-stories/StoryLayout.tsx +104 -0
- package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
- package/src/host/document-preview-three.ts +110 -0
- package/src/host/entry-adjudication.ts +89 -0
- package/src/host/game-location-guard.ts +150 -0
- package/src/host/game-module-access.ts +196 -0
- package/src/host/game-realm-page.ts +358 -0
- package/src/host/game-realm-reclaim.ts +55 -0
- package/src/host/game-realm-storage.ts +104 -0
- package/src/host/gameplay-export.ts +288 -0
- package/src/host/gameplay-recording.ts +717 -0
- package/src/host/gated-globals.ts +1511 -0
- package/src/host/history/json-history-resource.ts +254 -0
- package/src/host/ingest/registry.ts +261 -0
- package/src/host/instance-extract-actions.ts +120 -0
- package/src/host/instance-fork-actions.ts +120 -0
- package/src/host/play-control-hook.ts +26 -0
- package/src/host/playwright-shim.ts +479 -0
- package/src/host/projection/dom.ts +295 -0
- package/src/host/projection/pixi.ts +288 -0
- package/src/host/r3f-entry-runtime.ts +77 -0
- package/src/host/react-mount-runtime.ts +162 -0
- package/src/host/realm-services.ts +148 -0
- package/src/host/recording-preview.ts +106 -0
- package/src/host/roots/module-root.ts +203 -0
- package/src/host/roots/react-root.ts +181 -0
- package/src/host/same-realm-loop-gate.ts +544 -0
- package/src/host/scene-view-drawability.ts +69 -0
- package/src/host/sdk/tools.ts +31 -0
- package/src/host/served-bundle-runtime-modules.ts +331 -0
- package/src/host/server-log-bridge.ts +60 -0
- package/src/host/staged-projects.ts +24 -0
- package/src/host/stories/mounted-story-viewport-source.ts +64 -0
- package/src/host/stories/story-arg-descriptors.ts +50 -0
- package/src/host/stories/story-media-presence.ts +152 -0
- package/src/host/surface-content.ts +87 -0
- package/src/host/take-named-export.ts +23 -0
- package/src/host/three-ingest-runtime.ts +76 -0
- package/src/host/types-fastnoise-lite.d.ts +7 -0
- package/src/host/types-mikktspace.d.ts +20 -0
- package/src/host/types-troika-three-text.d.ts +7 -0
- package/src/host/use-active-performance-source.ts +53 -0
- package/src/host/viewport-pose-memory.ts +48 -0
- package/src/host/viewport-root-presentation.ts +40 -0
- package/src/ingest/active-ingest.ts +251 -0
- package/src/ingest/active-scene-navigation.ts +43 -0
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
- package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
- package/src/ingest/capture-wait-report.ts +122 -0
- package/src/ingest/deferred-ingest-play.ts +216 -0
- package/src/ingest/deferred-ingest-session.ts +23 -0
- package/src/ingest/discovery-public-ingest.ts +242 -0
- package/src/ingest/dom-stub-mark.ts +7 -0
- package/src/ingest/entry-load.ts +56 -0
- package/src/ingest/game-contract-realm.ts +34 -0
- package/src/ingest/game-pointer-lock.ts +89 -0
- package/src/ingest/held-scene-repaint.ts +73 -0
- package/src/ingest/host-surface-box.ts +174 -0
- package/src/ingest/ingest-boot-viewport.ts +54 -0
- package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
- package/src/ingest/ingest-canvas-scene.ts +50 -0
- package/src/ingest/ingest-evidence-hook.ts +131 -0
- package/src/ingest/ingest-frame-snapshot.ts +183 -0
- package/src/ingest/ingest-play-commands.ts +134 -0
- package/src/ingest/ingest-play-control.ts +299 -0
- package/src/ingest/ingest-render-debug.ts +320 -0
- package/src/ingest/ingest-siblings.ts +487 -0
- package/src/ingest/ingest-status.ts +62 -0
- package/src/ingest/live-ingest-facet.ts +46 -0
- package/src/ingest/module-mode.ts +229 -0
- package/src/ingest/mount-canvas-ingest-root.ts +899 -0
- package/src/ingest/mount-coverage.ts +296 -0
- package/src/ingest/mount-dom-ingest-root.ts +282 -0
- package/src/ingest/mount-ingest-root.ts +744 -0
- package/src/ingest/mount-three-ingest-root.ts +366 -0
- package/src/ingest/resolve-canvas.ts +47 -0
- package/src/ingest/resolve-three.ts +123 -0
- package/src/ingest/served-bundle.ts +109 -0
- package/src/ingest/served-html-boot.ts +292 -0
- package/src/ingest/surface-canvas.ts +84 -0
- package/src/ingest/surface-dom.ts +84 -0
- package/src/ingest/surface-three.ts +128 -0
- package/src/ingest/types.ts +76 -0
- package/src/ingest/unmount-ingest-root.ts +224 -0
- package/src/navmesh/navmesh-actions.ts +27 -0
- package/src/navmesh/navmesh-handler.ts +237 -0
- package/src/navmesh/navmesh-workflow-store.ts +78 -0
- package/src/network/NetworkInspectorPanel.tsx +644 -0
- package/src/network/network-inspector-model.ts +225 -0
- package/src/play/play-boot-stall.ts +118 -0
- package/src/play/play-log-events.ts +26 -0
- package/src/play/play-mode.ts +2553 -0
- package/src/play/play-recording.ts +335 -0
- package/src/play/react-play-live-authoring.ts +165 -0
- package/src/play-bar/PlayBar.tsx +488 -0
- package/src/play-bar/PlayerCountPicker.tsx +100 -0
- package/src/profiler/FrameDebuggerPanel.tsx +458 -0
- package/src/profiler/PerformancePanel.tsx +1068 -0
- package/src/profiler/ProfilerPanel.tsx +53 -0
- package/src/profiler/frame-debugger-store.ts +97 -0
- package/src/profiler/main-thread-busy.ts +90 -0
- package/src/react/design-time-react-mount.ts +782 -0
- package/src/react/dom-authoring-adapter.ts +764 -0
- package/src/react/pasteboard-materialize.ts +154 -0
- package/src/react/react-inspector-section.tsx +2496 -0
- package/src/react/react-world-authoring-adapter.ts +3795 -0
- package/src/react/story-documents/story-args-section.ts +19 -0
- package/src/react/story-documents/story-documents.tsx +1380 -0
- package/src/react/story-paint-bounds.ts +94 -0
- package/src/react/ui-board-document.tsx +101 -0
- package/src/react/ui-board-title.ts +9 -0
- package/src/react/ui-component-board.ts +72 -0
- package/src/services/audio-pose-guard.ts +81 -0
- package/src/services/game-audio-unlock.ts +48 -0
- package/src/state-watch/StateWatchPanel.tsx +535 -0
- package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
- package/src/three/authoring/constraint-inspector-section.tsx +189 -0
- package/src/three/authoring/design-time-renderer.ts +189 -0
- package/src/three/authoring/model-asset-inspector-section.css +41 -0
- package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
- package/src/three/authoring/oid-source-persistence.ts +557 -0
- package/src/three/authoring/r3f-design-session.ts +1174 -0
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
- package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
- package/src/three/authoring/spatial-audio-handles.ts +215 -0
- package/src/three/authoring/spatial-collider-handles.ts +229 -0
- package/src/three/authoring/spatial-joint-handles.ts +114 -0
- package/src/three/authoring/spatial-light-handles.ts +178 -0
- package/src/three/authoring/spatial-lod-handles.ts +93 -0
- package/src/three/authoring/spatial-particle-handles.ts +368 -0
- package/src/three/authoring/three-authoring-adapter.ts +1953 -0
- package/src/three/authoring/three-scene-identity.ts +19 -0
- package/src/three/authoring/three-spatial-handles.ts +161 -0
- package/src/three/authoring/typed-three-inspector.ts +528 -0
- package/src/three/component-verbs/extract-menu.ts +71 -0
- package/src/three/component-verbs/fork-menu.ts +78 -0
- package/src/three/component-verbs/internals-menu.ts +91 -0
- package/src/three/story-documents/three-story-documents.tsx +607 -0
- package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
- package/src/three/three-board/board-framing.ts +412 -0
- package/src/three/three-board/board-layout.ts +401 -0
- package/src/three/three-board/board-scene.ts +901 -0
- package/src/three/three-board/three-component-board.ts +62 -0
- package/src/xstate/XStateBehaviorSection.tsx +130 -0
- package/src/xstate/XStateMachineInspector.tsx +566 -0
- package/src/xstate/character-animation-machine.fixture.ts +74 -0
- package/src/xstate/live-behaviors.ts +106 -0
- package/src/xstate/use-live-actor-state.ts +44 -0
- package/src/xstate/xstate-graph.ts +235 -0
- package/src/xstate/xstate-layout.ts +76 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE INSTRUMENT: what a mount MEASURED about itself, assembled from the live
|
|
3
|
+
* session slot and handed to the pure derivation in `coverage/capability-coverage.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is a measurement or the plumbing of one — the mount-time
|
|
6
|
+
* one-shot probe (the editor capabilities reached),
|
|
7
|
+
* the live re-read of the loop verdict, the once-per-mount
|
|
8
|
+
* console door, and the report the status facet and the `__vgaiIngest` hook
|
|
9
|
+
* both serve. Mount PLUMBING lives in the `mount-*-ingest-root.ts` siblings
|
|
10
|
+
* and deliberately not here: a reader asking "what did the editor actually
|
|
11
|
+
* measure about this game, and where does that number come from" should have
|
|
12
|
+
* exactly this file open.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { AdapterReach } from '../host/adapter-reach';
|
|
16
|
+
import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
|
|
17
|
+
import { ingestDataWriterNow } from '../host/authoring/ingest-data-writer';
|
|
18
|
+
import { ingestOwnershipNow } from '../host/authoring/ingest-source-persistence';
|
|
19
|
+
import {
|
|
20
|
+
type CapabilityCoverageReport,
|
|
21
|
+
createCapabilityCoverageConsole,
|
|
22
|
+
type DataWriterFacts,
|
|
23
|
+
deriveCapabilityCoverage,
|
|
24
|
+
measureGameContract,
|
|
25
|
+
SYSTEM_ADAPTER_SLOTS,
|
|
26
|
+
type SystemAdapterMeasurement,
|
|
27
|
+
type WriteReachFacts,
|
|
28
|
+
} from '../host/coverage/capability-coverage';
|
|
29
|
+
import { inspectSystemAdapterSeam } from '@volter/editor-core/coverage/system-seam-evidence';
|
|
30
|
+
import { editorConsole } from '@volter/editor-core/editor-console';
|
|
31
|
+
import {
|
|
32
|
+
type MeasuredLoop,
|
|
33
|
+
measuredLoop,
|
|
34
|
+
type SameRealmLoopVerdict,
|
|
35
|
+
} from '../host/same-realm-loop-gate';
|
|
36
|
+
import {
|
|
37
|
+
type ContractSurface,
|
|
38
|
+
projectContractSystemAdapters,
|
|
39
|
+
} from '@volter/game-runtime/adapter/ingest/contract-system-adapters';
|
|
40
|
+
import type { VgaiGameContract } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
41
|
+
import type { SystemAdapters } from '@volter/editor-project/adapter/system-adapter';
|
|
42
|
+
import { activeIngest } from './active-ingest';
|
|
43
|
+
import type { IngestMount } from './authoring/ingest-root-adapter';
|
|
44
|
+
import { activeIngestContract } from './ingest-play-control';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The half of {@link deriveCapabilityCoverage}'s facts that is a property of THIS
|
|
48
|
+
* mount and cannot be re-read later: the editor capabilities this mount
|
|
49
|
+
* reached, with the mechanism that reached them. Everything else the
|
|
50
|
+
* coverage report needs (the loop verdict, the declared
|
|
51
|
+
* contract — its systems surface included — and the ownership answer) is read
|
|
52
|
+
* live at report time, because all of them move after the mount and a frozen
|
|
53
|
+
* copy would start lying.
|
|
54
|
+
*/
|
|
55
|
+
export interface MountCoverageInputs {
|
|
56
|
+
readonly reach: AdapterReach | null;
|
|
57
|
+
/** How this mount reached the game's runtime, in the mount's own words,
|
|
58
|
+
* carried into the warning a missing capability prints. */
|
|
59
|
+
readonly reachMechanism: string | null;
|
|
60
|
+
/** Read on demand because a loop verdict may arrive only after the first
|
|
61
|
+
* pause probe. Every surface supplies its own real control verdict, and a
|
|
62
|
+
* route with no gate at all supplies the reason there will never be one. */
|
|
63
|
+
readonly loop: () => { loop: MeasuredLoop | null; reason: string | null };
|
|
64
|
+
/**
|
|
65
|
+
* How much of the mounted world an edit can actually be written back to, read
|
|
66
|
+
* LIVE for the same reason the loop verdict is: a world keeps streaming
|
|
67
|
+
* objects in, and a count frozen at first capture would start lying.
|
|
68
|
+
*
|
|
69
|
+
* ABSENT ⇒ this route has no three authoring adapter to ask (a DOM or
|
|
70
|
+
* canvas ingest), and the persistence row says only what ownership answered —
|
|
71
|
+
* never a reach claim nobody measured.
|
|
72
|
+
*/
|
|
73
|
+
readonly writeReach?: (() => WriteReachFacts) | undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The console door for the coverage report (`coverage/capability-coverage.ts`) —
|
|
78
|
+
* gaps as one warning block, the rest as one info block, ONCE per mount.
|
|
79
|
+
*/
|
|
80
|
+
const _coverageConsole = createCapabilityCoverageConsole((block) => {
|
|
81
|
+
if (block.level === 'warn') editorConsole.warn(block.message, 'ingest');
|
|
82
|
+
else editorConsole.log(block.message, 'ingest');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/** Distinguishes consecutive mounts of the SAME world for the console door's
|
|
86
|
+
* once-per-mount guard — remounting a game is a new answer, not a repeat. */
|
|
87
|
+
let _mountSeq = 0;
|
|
88
|
+
let _mountToken = '';
|
|
89
|
+
|
|
90
|
+
/** Open a new coverage answer for `sessionId`: a new mount is a new report even
|
|
91
|
+
* for the same world, and the console door's once-guard keys on this token. */
|
|
92
|
+
export function beginMountCoverage(sessionId: string): void {
|
|
93
|
+
_mountToken = `${sessionId}#${++_mountSeq}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The loop facts for a three-route mount, read live.
|
|
98
|
+
*
|
|
99
|
+
* A verdict is a MEASUREMENT, produced by the gate's own probe at the first
|
|
100
|
+
* pause — and it travels WITH the evidence it was concluded from
|
|
101
|
+
* ({@link MeasuredLoop}), which is what keeps it distinguishable from the
|
|
102
|
+
* manifest's declared intent word. Where the mount installed no gate at all the
|
|
103
|
+
* honest answer is "no verdict will ever be measured here", never the
|
|
104
|
+
* pending-probe sentence, which promises a measurement that does not come.
|
|
105
|
+
*/
|
|
106
|
+
export function threeMountLoopFacts(mount: IngestMount): {
|
|
107
|
+
loop: MeasuredLoop | null;
|
|
108
|
+
reason: string | null;
|
|
109
|
+
} {
|
|
110
|
+
const loop = measuredLoop(mount.realmLoopVerdict?.());
|
|
111
|
+
const reason = mount.realmLoopVerdict?.()?.reason ?? null;
|
|
112
|
+
if (loop !== null || reason !== null) return { loop, reason };
|
|
113
|
+
if (mount.realmLoopVerdict) return { loop: null, reason: null };
|
|
114
|
+
return { loop: null, reason: LOOP_PROBE_ABSENT };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The loop facts for a native-React (`dom`) mount — its SIBLING to
|
|
119
|
+
* {@link threeMountLoopFacts}, and the reason it exists as a named function
|
|
120
|
+
* rather than a literal at the mount site.
|
|
121
|
+
*
|
|
122
|
+
* This route used to assert `loop: 'gated'` outright, on the strength of the
|
|
123
|
+
* sentence below. That sentence is true and it is still printed, but it
|
|
124
|
+
* describes a MECHANISM, not a measurement of one: no probe on this route has
|
|
125
|
+
* ever held this game's scheduling and watched whether anything ran anyway
|
|
126
|
+
* (`ingest/same-realm-loop-gate.ts`'s `verifySameRealmLoopControl` is the only
|
|
127
|
+
* thing that can answer that, and nothing calls it here). The word it emitted
|
|
128
|
+
* was the manifest's DECLARED intent vocabulary wearing a measurement's
|
|
129
|
+
* clothes, so the coverage report printed `loop ✓ gated` about every
|
|
130
|
+
* native-React ingest — including one whose own `setInterval` the host's play
|
|
131
|
+
* pause never touches.
|
|
132
|
+
*
|
|
133
|
+
* Same shape and same reason as `mount-canvas-ingest-root.ts`'s two branches,
|
|
134
|
+
* which reach the identical conclusion about their own unprobed controls.
|
|
135
|
+
*/
|
|
136
|
+
export function domMountLoopFacts(): { loop: null; reason: string } {
|
|
137
|
+
return {
|
|
138
|
+
loop: null,
|
|
139
|
+
reason:
|
|
140
|
+
"⏸ calls the host runtime's own play pause on this route, but no probe has measured " +
|
|
141
|
+
"whether that reaches this game's frames — an unmodified React game that schedules its " +
|
|
142
|
+
'own timers is not held by the host loop',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** What a mount with no loop gate at all can honestly say about its frames. */
|
|
147
|
+
export const LOOP_PROBE_ABSENT =
|
|
148
|
+
'this mount installs no loop gate, so no verdict will ever be measured here — nothing in ' +
|
|
149
|
+
"the editor holds this game's frames";
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The terminal state of every `SystemAdapters` slot for a live mount.
|
|
153
|
+
*
|
|
154
|
+
* TWO independent sources, deliberately: `bound` is read off the editor's own
|
|
155
|
+
* live registry (the very object its panels call, so a slot that "registered"
|
|
156
|
+
* but never reached the registry reports honestly), while `empty`/`malformed`
|
|
157
|
+
* come from the game's declared carrier. Nothing consults the route or the
|
|
158
|
+
* game's id, and no slot is bound and empty at once — a bound adapter wins,
|
|
159
|
+
* because the registry is the stronger measurement.
|
|
160
|
+
*
|
|
161
|
+
* Exported and pure over its inputs so the whole table is unit-testable with no
|
|
162
|
+
* browser and no live session.
|
|
163
|
+
*/
|
|
164
|
+
export function measureSystemAdapters(
|
|
165
|
+
active: SystemAdapters,
|
|
166
|
+
contract: VgaiGameContract | null,
|
|
167
|
+
/** The mount's surface when the caller has one — see
|
|
168
|
+
* `contract-system-adapters.ts`'s header for the one slot it decides
|
|
169
|
+
* (`physics`, whose vocabulary follows the surface). */
|
|
170
|
+
surface?: ContractSurface | undefined,
|
|
171
|
+
): readonly SystemAdapterMeasurement[] {
|
|
172
|
+
const projection = projectContractSystemAdapters(contract?.systems, surface);
|
|
173
|
+
const empty = new Map(projection.empty.map((e) => [e.slot as string, e.evidence]));
|
|
174
|
+
const malformed = new Map(projection.malformed.map((m) => [m.slot as string, m.reason]));
|
|
175
|
+
return SYSTEM_ADAPTER_SLOTS.map((slot): SystemAdapterMeasurement => {
|
|
176
|
+
if (active[slot] != null) {
|
|
177
|
+
return {
|
|
178
|
+
slot,
|
|
179
|
+
state: 'bound',
|
|
180
|
+
proof: inspectSystemAdapterSeam({
|
|
181
|
+
slot,
|
|
182
|
+
adapter: active[slot] as NonNullable<SystemAdapters[typeof slot]>,
|
|
183
|
+
subject: 'game',
|
|
184
|
+
}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const evidence = empty.get(slot);
|
|
188
|
+
if (evidence !== undefined) return { slot, state: 'empty', evidence };
|
|
189
|
+
const reason = malformed.get(slot);
|
|
190
|
+
if (reason !== undefined) return { slot, state: 'malformed', evidence: reason };
|
|
191
|
+
return { slot, state: 'unanswered' };
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* THE coverage report for the live mount — re-derived on every read (see
|
|
197
|
+
* {@link MountCoverageInputs} for what is frozen at mount and what is not).
|
|
198
|
+
* `null` when nothing is mounted, or when the mounted route recorded no
|
|
199
|
+
* coverage inputs: not measured is its own answer, never a clean one.
|
|
200
|
+
*
|
|
201
|
+
* The contract comes from `activeIngestContract` — the same read
|
|
202
|
+
* `getIngestPlayControl` acts on, so a game that declared its contract
|
|
203
|
+
* somewhere the host cannot see reports the same gap the editor actually has,
|
|
204
|
+
* and one that declared it through a host-added contract shim beside the game
|
|
205
|
+
* gets credit for exactly what the play control can use.
|
|
206
|
+
*/
|
|
207
|
+
/**
|
|
208
|
+
* The declared data writer's state, in the coverage vocabulary. `absent` — the
|
|
209
|
+
* common case — is `null`, not a gap: a game with no data file to write is not
|
|
210
|
+
* missing anything.
|
|
211
|
+
*/
|
|
212
|
+
function dataWriterFacts(): DataWriterFacts | null {
|
|
213
|
+
const state = ingestDataWriterNow();
|
|
214
|
+
switch (state.state) {
|
|
215
|
+
case 'absent':
|
|
216
|
+
return null;
|
|
217
|
+
case 'loading':
|
|
218
|
+
return { state: 'loading' };
|
|
219
|
+
case 'ready':
|
|
220
|
+
return { state: 'ready', dataFile: state.writer.dataFile };
|
|
221
|
+
default:
|
|
222
|
+
return { state: 'failed', reason: state.reason };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function ingestCoverageReport(): CapabilityCoverageReport | null {
|
|
227
|
+
const active = activeIngest();
|
|
228
|
+
const inputs = active?.coverage;
|
|
229
|
+
if (!active || !inputs) return null;
|
|
230
|
+
const ownership = ingestOwnershipNow();
|
|
231
|
+
const loop = inputs.loop();
|
|
232
|
+
const contract = activeIngestContract();
|
|
233
|
+
return deriveCapabilityCoverage({
|
|
234
|
+
worldId: active.worldId,
|
|
235
|
+
reach: inputs.reach,
|
|
236
|
+
// The session's own render substrate IS the surface kind the coverage
|
|
237
|
+
// derivation needs (`IngestKind` and `AdapterSurface` are the same three
|
|
238
|
+
// words by construction), so nothing has to be declared twice.
|
|
239
|
+
surface: active.kind,
|
|
240
|
+
reachMechanism: inputs.reachMechanism,
|
|
241
|
+
loop: loop.loop,
|
|
242
|
+
loopReason: loop.reason,
|
|
243
|
+
contract: measureGameContract(contract),
|
|
244
|
+
// `active.kind` IS the surface (`IngestKind` and `ContractSurface` are the
|
|
245
|
+
// same three words by construction), so the physics slot's keying is
|
|
246
|
+
// checked against the surface this mount actually is.
|
|
247
|
+
systemAdapters: measureSystemAdapters(getActiveSystems(), contract, active.kind),
|
|
248
|
+
ownership: ownership
|
|
249
|
+
? {
|
|
250
|
+
writable: ownership.writable,
|
|
251
|
+
reason: ownership.reason ?? null,
|
|
252
|
+
recorder: ownership.recorder ?? null,
|
|
253
|
+
}
|
|
254
|
+
: null,
|
|
255
|
+
dataWriter: dataWriterFacts(),
|
|
256
|
+
// The `project.*` family is about the opened PROJECT, not this mount, and
|
|
257
|
+
// it has one producer for both lanes (`@volter/editor-game/coverage/live-project-verbs.ts`).
|
|
258
|
+
// Reporting it here too would print two verdicts for one capability.
|
|
259
|
+
projectVerbs: [],
|
|
260
|
+
writeReach: inputs.writeReach?.() ?? null,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Record this mount's frozen coverage inputs and say the result out loud, once.
|
|
266
|
+
*
|
|
267
|
+
* Called from every mount path right after the session is published, so the
|
|
268
|
+
* report describes a mount that exists.
|
|
269
|
+
*/
|
|
270
|
+
export function recordMountCoverage(inputs: MountCoverageInputs): void {
|
|
271
|
+
const active = activeIngest();
|
|
272
|
+
if (!active) return;
|
|
273
|
+
active.coverage = inputs;
|
|
274
|
+
const report = ingestCoverageReport();
|
|
275
|
+
if (report) _coverageConsole.report(_mountToken, report);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Report the MEASURED loop verdict for a three ingest mount (honesty
|
|
280
|
+
* gate, ARCHITECTURE-CORE §Editor).
|
|
281
|
+
*
|
|
282
|
+
* `loop` is a claim about whether anything in this editor actually controls the
|
|
283
|
+
* game's frames, so it is only ever printed from the gate's own probe
|
|
284
|
+
* (`ingest/same-realm-loop-gate.ts`) — never inferred from the manifest. A
|
|
285
|
+
* route that does no gating at all passes `undefined` and this says nothing,
|
|
286
|
+
* which is also honest.
|
|
287
|
+
*/
|
|
288
|
+
export function reportMeasuredLoop(
|
|
289
|
+
worldId: string,
|
|
290
|
+
verdict: SameRealmLoopVerdict | undefined,
|
|
291
|
+
): void {
|
|
292
|
+
if (!verdict) return;
|
|
293
|
+
const line = `Ingest "${worldId}" loop: ${verdict.loop} — ${verdict.reason}`;
|
|
294
|
+
if (verdict.loop === 'gated') editorConsole.log(line, 'ingest');
|
|
295
|
+
else editorConsole.warn(line, 'adapter');
|
|
296
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount a `{ ingest }` root on the DOM surface — an unmodified native-React
|
|
3
|
+
* game, mounted through the EXACT SAME react-world host stack `default-react`/
|
|
4
|
+
* multi-world react roots use, NOT the capture-based three/canvas bridge (there
|
|
5
|
+
* is no scene to capture; the mounted tree IS React, D-N1).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { measureAdapterReach } from '../host/adapter-reach';
|
|
9
|
+
import { setActiveAuthoring } from '@volter/editor-core/authoring/active-adapter';
|
|
10
|
+
import { setActiveSystems } from '@volter/editor-core/authoring/active-systems';
|
|
11
|
+
import { clearMountFailureReports } from '@volter/editor-core/authoring/mount-failure-report';
|
|
12
|
+
import { resolveAllRoots } from '../host/binding-resolver';
|
|
13
|
+
import { editorConsole } from '@volter/editor-core/editor-console';
|
|
14
|
+
import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
|
|
15
|
+
import { setGameInputGate } from '../host/gated-globals';
|
|
16
|
+
import { authoringJournal } from '../host/history/json-history-resource';
|
|
17
|
+
import { acquireLiveDocument, liveDocumentContainer } from '@volter/editor-core/live-document';
|
|
18
|
+
import { DomAuthoringAdapter } from '../react/dom-authoring-adapter';
|
|
19
|
+
import type { ResolvedAdapterRoot, ResolvedGameManifest } from '@volter/editor-project/manifest/load';
|
|
20
|
+
import { serializeEntry, setActiveIngest } from './active-ingest';
|
|
21
|
+
import { landIngestBootInEdit } from './ingest-boot-viewport';
|
|
22
|
+
import { ingestHookEvidence, publishIngestHook, reactDomEvidence } from './ingest-evidence-hook';
|
|
23
|
+
import { bindIngestLifecycle } from './ingest-play-control';
|
|
24
|
+
import { domMountLoopFacts, type MountCoverageInputs, recordMountCoverage } from './mount-coverage';
|
|
25
|
+
import { getIngestGameDom, ingestGameDomProjectRoot } from './surface-dom';
|
|
26
|
+
import { exitActiveIngest } from './unmount-ingest-root';
|
|
27
|
+
|
|
28
|
+
/** How a react ingest reaches the game: it mounts through the host's own
|
|
29
|
+
* native React runtime, so whatever the editor cannot do here is the
|
|
30
|
+
* adapter's own missing surface, not an unreachable realm. */
|
|
31
|
+
const REACT_MECHANISM =
|
|
32
|
+
"this world mounts in the host's native React runtime, so the editor is limited only by " +
|
|
33
|
+
'what its authoring adapter provides';
|
|
34
|
+
|
|
35
|
+
/** The coverage inputs both react routes record — identical by construction,
|
|
36
|
+
* because both mount through the same native runtime. */
|
|
37
|
+
function domMountCoverage(adapter: DomAuthoringAdapter): MountCoverageInputs {
|
|
38
|
+
return {
|
|
39
|
+
reach: measureAdapterReach({ authoring: adapter }),
|
|
40
|
+
reachMechanism: REACT_MECHANISM,
|
|
41
|
+
loop: domMountLoopFacts,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Mount a discovered native-React fixture game (`ingest/surface-dom.ts`'s glob
|
|
47
|
+
* discovery is the lookup).
|
|
48
|
+
*
|
|
49
|
+
* Reuses `resolveAllRoots` + universal `createGameRuntime` rather than
|
|
50
|
+
* hand-building a `DomHostContext`/`Game`: a
|
|
51
|
+
* single-element `roots` array gets EVERY host concern for free (the
|
|
52
|
+
* absolutely-positioned DOM-root layer, a real `Game`, dispose via
|
|
53
|
+
* `session.stop()`) with zero reinvention — `resolveAllRoots`'s
|
|
54
|
+
* `MULTI_WORLD_ALLOWED_IDENTITIES` gate only fires for `roots.length > 1`
|
|
55
|
+
* (see that function's doc comment), so a ONE-element manifest containing an
|
|
56
|
+
* `ingest-react` world resolves cleanly through it despite that identity
|
|
57
|
+
* being excluded from real multi-world manifests.
|
|
58
|
+
*
|
|
59
|
+
* D-N8 v2 / D-K4: the active authoring override is a real
|
|
60
|
+
* `DomAuthoringAdapter` — a structural-path DOM walker over the mounted
|
|
61
|
+
* react tree, applying edits live (never to source, see that file's doc
|
|
62
|
+
* comment). The published `reach` is MEASURED off this real adapter
|
|
63
|
+
* (`adapter-reach.ts`), not hard-coded, so every capability it does not
|
|
64
|
+
* provide keeps warning.
|
|
65
|
+
*/
|
|
66
|
+
async function mountDomIngestRootInner(store: EditorShellStore, folderId: string): Promise<void> {
|
|
67
|
+
const game = getIngestGameDom(folderId);
|
|
68
|
+
if (!game) {
|
|
69
|
+
editorConsole.error(`Unknown react ingest game: ${folderId}`, 'ingest');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const projectRoot = ingestGameDomProjectRoot(folderId);
|
|
73
|
+
if (!projectRoot) {
|
|
74
|
+
editorConsole.error(
|
|
75
|
+
`React ingest "${folderId}": no __VGAI_ENGINE_ROOT__ available to resolve its entry`,
|
|
76
|
+
'ingest',
|
|
77
|
+
);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// The Game document exists only while a runtime does, so install it (and
|
|
81
|
+
// wait for its panel to commit) before reading the container it owns.
|
|
82
|
+
await acquireLiveDocument();
|
|
83
|
+
const gameContainer = liveDocumentContainer();
|
|
84
|
+
if (!gameContainer) {
|
|
85
|
+
editorConsole.error('Game container not mounted — cannot ingest (react)', 'ingest');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// D-V1: tear down WHATEVER kind is currently active, not just a prior
|
|
89
|
+
// react session — closes the cross-kind leak (F26 residual).
|
|
90
|
+
exitActiveIngest();
|
|
91
|
+
|
|
92
|
+
store.setPlayState('playing');
|
|
93
|
+
editorConsole.log(`Ingesting unmodified native-React game: ${game.manifest.name}`, 'ingest');
|
|
94
|
+
|
|
95
|
+
// F26: same cold-mount announcement as the other ingest routes — the react
|
|
96
|
+
// world's entry executes in the editor realm and may declare the game
|
|
97
|
+
// contract to defer its session until ▶. Opt-in, never demanded.
|
|
98
|
+
(window as unknown as { __vgaiMountCold?: boolean }).__vgaiMountCold = true;
|
|
99
|
+
const { createGameRuntime } = await import('@volter/game-runtime/runtime/create-runtime');
|
|
100
|
+
const specs = await resolveAllRoots(game.manifest, projectRoot);
|
|
101
|
+
const w = gameContainer.clientWidth;
|
|
102
|
+
const h = gameContainer.clientHeight;
|
|
103
|
+
const session = await createGameRuntime({
|
|
104
|
+
container: gameContainer,
|
|
105
|
+
roots: specs,
|
|
106
|
+
width: w,
|
|
107
|
+
height: h,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// D-K4: construct the real authoring adapter over the mounted world's live
|
|
111
|
+
// DOM root (its style edits are live-only — there is no source path here and
|
|
112
|
+
// no sidecar any more). The adapter has its own `selection` provider
|
|
113
|
+
// (store-backed, same shape as `ReactRootAuthoringAdapter.selection`), so —
|
|
114
|
+
// unlike a `{ module }` adapter with none — it is installed directly, never
|
|
115
|
+
// `withStoreSelection`-wrapped.
|
|
116
|
+
const root = session.game.roots[0]!.reactRoot();
|
|
117
|
+
// A held surface: its journal is the WORLD's, so undo survives a remount of
|
|
118
|
+
// this game (`../history/json-history-resource.ts`).
|
|
119
|
+
const adapter = new DomAuthoringAdapter(root, store, {
|
|
120
|
+
journal: authoringJournal(folderId),
|
|
121
|
+
});
|
|
122
|
+
// Unlike the capture routes, a react ingest mounts through the NATIVE
|
|
123
|
+
// runtime — its Game.play pause/resume IS the honest control (D10). Named
|
|
124
|
+
// (rather than inlined below) so `setPausedPresent`'s D-L1 `typeof`
|
|
125
|
+
// check reads the SAME real control the session actually installs, never
|
|
126
|
+
// an independent literal.
|
|
127
|
+
const setPaused = (p: boolean) => (p ? session.game.play.pause() : session.game.play.resume());
|
|
128
|
+
const lifecycle = bindIngestLifecycle({ setPaused });
|
|
129
|
+
|
|
130
|
+
setActiveIngest({
|
|
131
|
+
kind: 'dom',
|
|
132
|
+
session: {
|
|
133
|
+
store,
|
|
134
|
+
lifecycle,
|
|
135
|
+
stop: () => session.stop(),
|
|
136
|
+
adapter,
|
|
137
|
+
},
|
|
138
|
+
siblings: [],
|
|
139
|
+
worldId: folderId,
|
|
140
|
+
});
|
|
141
|
+
// Same play-mode input gate every other same-realm ingest route drives (R2c).
|
|
142
|
+
// The former per-surface `games-2d`/`games-react` fixture
|
|
143
|
+
// trees into the single `ingest/games` tree, which IS what
|
|
144
|
+
// `game-globals-shadow.ts` roots its shadow at — so a native-React fixture's
|
|
145
|
+
// own listeners are shadowed on the same terms as every other surface's now
|
|
146
|
+
// (this comment used to record the opposite, from the R-N6 residual).
|
|
147
|
+
setGameInputGate(() => store.playState === 'playing' && store.activeViewportTab === 'play');
|
|
148
|
+
|
|
149
|
+
setActiveAuthoring(adapter);
|
|
150
|
+
setActiveSystems({});
|
|
151
|
+
landIngestBootInEdit(store);
|
|
152
|
+
store.notifyIngestEdit();
|
|
153
|
+
clearMountFailureReports(); // D-W3: mount succeeded
|
|
154
|
+
|
|
155
|
+
const coverage = domMountCoverage(adapter);
|
|
156
|
+
recordMountCoverage(coverage);
|
|
157
|
+
|
|
158
|
+
publishIngestHook('__vgaiIngestReact', {
|
|
159
|
+
gameId: folderId,
|
|
160
|
+
worldId: game.worldId,
|
|
161
|
+
...ingestHookEvidence(adapter),
|
|
162
|
+
noAuthoring: false,
|
|
163
|
+
// D-K4: MEASURED off the real adapter (`adapter-reach.ts`) — a
|
|
164
|
+
// hierarchy+inspector+persistence adapter with no `transforms` reports a
|
|
165
|
+
// standing `transforms` gap, never a hard-coded claim.
|
|
166
|
+
reach: coverage.reach,
|
|
167
|
+
// D-L1: the doctor MOUNTED-bar evidence accessor —
|
|
168
|
+
// see `reactDomEvidence`'s own doc comment.
|
|
169
|
+
domEvidence: () => reactDomEvidence(root, adapter),
|
|
170
|
+
// D-L1: honest `typeof === 'function'` check on the REAL session
|
|
171
|
+
// control this route just installed above — never an independent
|
|
172
|
+
// literal `true` (mirrors `__vgaiSiblingMounts`'s identical discipline).
|
|
173
|
+
setPausedPresent: typeof lifecycle.setPaused === 'function',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* D-Y2 (slice S2) — the manifest-route sibling of {@link mountDomIngestRoot}: an
|
|
179
|
+
* EXTERNAL-FOLDER `ingest-react` world (the opened project's OWN
|
|
180
|
+
* `vgai.project.json`, not the in-tree `ingest/games/<id>` fixture registry)
|
|
181
|
+
* enters the same react ingest session. Exactly the same roots-path recipe
|
|
182
|
+
* (one-element `resolveAllRoots` + roots-path `createGameRuntime`) and the same
|
|
183
|
+
* `DomAuthoringAdapter` D-K4 wiring — only the SOURCE of
|
|
184
|
+
* `world`/`projectRoot` differs (the opened project's manifest +
|
|
185
|
+
* `project.rootPath`, via `mount-ingest-root.ts`'s react route, instead of
|
|
186
|
+
* `getIngestGameDom`/`ingestGameDomProjectRoot`'s vendored-fixture lookup).
|
|
187
|
+
* `resolveIngestReactAdapter`'s own guards (`entry` required, no `scene`) run
|
|
188
|
+
* unchanged inside `resolveAllRoots` — this function
|
|
189
|
+
* adds no new validation of its own, mirroring
|
|
190
|
+
* `mountCanvasIngestRootFromManifest`'s relationship to its vendored sibling.
|
|
191
|
+
*/
|
|
192
|
+
export async function mountDomIngestRootFromManifest(
|
|
193
|
+
store: EditorShellStore,
|
|
194
|
+
manifest: ResolvedGameManifest,
|
|
195
|
+
world: ResolvedAdapterRoot,
|
|
196
|
+
projectRoot: string,
|
|
197
|
+
): Promise<void> {
|
|
198
|
+
// The Game document exists only while a runtime does, so install it (and
|
|
199
|
+
// wait for its panel to commit) before reading the container it owns.
|
|
200
|
+
await acquireLiveDocument();
|
|
201
|
+
const gameContainer = liveDocumentContainer();
|
|
202
|
+
if (!gameContainer) {
|
|
203
|
+
editorConsole.error('Game container not mounted — cannot ingest (react)', 'ingest');
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// D-V1: tear down WHATEVER kind is currently active, not just a prior
|
|
207
|
+
// react session — closes the cross-kind leak (F26 residual).
|
|
208
|
+
exitActiveIngest();
|
|
209
|
+
|
|
210
|
+
store.setPlayState('playing');
|
|
211
|
+
editorConsole.log(
|
|
212
|
+
`Ingesting unmodified native-React game (external folder): ${world.id}`,
|
|
213
|
+
'ingest',
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// F26: same cold-mount announcement the vendored route makes — an
|
|
217
|
+
// external-folder game may declare the game contract too.
|
|
218
|
+
(window as unknown as { __vgaiMountCold?: boolean }).__vgaiMountCold = true;
|
|
219
|
+
const { createGameRuntime } = await import('@volter/game-runtime/runtime/create-runtime');
|
|
220
|
+
const specs = await resolveAllRoots({ ...manifest, roots: [world] }, projectRoot);
|
|
221
|
+
const w = gameContainer.clientWidth;
|
|
222
|
+
const h = gameContainer.clientHeight;
|
|
223
|
+
const session = await createGameRuntime({
|
|
224
|
+
container: gameContainer,
|
|
225
|
+
roots: specs,
|
|
226
|
+
width: w,
|
|
227
|
+
height: h,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// D-K4: same adapter construction as the vendored route.
|
|
231
|
+
const root = session.game.roots[0]!.reactRoot();
|
|
232
|
+
const adapter = new DomAuthoringAdapter(root, store, {
|
|
233
|
+
journal: authoringJournal(world.id),
|
|
234
|
+
});
|
|
235
|
+
// Same honest control as the vendored route (D10): a manifest-routed react
|
|
236
|
+
// ingest mounts through the NATIVE runtime, so Game.play pause/resume IS
|
|
237
|
+
// real. Named (D-L1) so `setPausedPresent` checks the SAME real control.
|
|
238
|
+
const setPaused = (p: boolean) => (p ? session.game.play.pause() : session.game.play.resume());
|
|
239
|
+
const lifecycle = bindIngestLifecycle({ setPaused });
|
|
240
|
+
|
|
241
|
+
setActiveIngest({
|
|
242
|
+
kind: 'dom',
|
|
243
|
+
session: {
|
|
244
|
+
store,
|
|
245
|
+
lifecycle,
|
|
246
|
+
stop: () => session.stop(),
|
|
247
|
+
adapter,
|
|
248
|
+
},
|
|
249
|
+
siblings: [],
|
|
250
|
+
worldId: world.id,
|
|
251
|
+
});
|
|
252
|
+
setGameInputGate(() => store.playState === 'playing' && store.activeViewportTab === 'play');
|
|
253
|
+
|
|
254
|
+
setActiveAuthoring(adapter);
|
|
255
|
+
setActiveSystems({});
|
|
256
|
+
landIngestBootInEdit(store);
|
|
257
|
+
store.notifyIngestEdit();
|
|
258
|
+
clearMountFailureReports(); // D-W3: mount succeeded
|
|
259
|
+
|
|
260
|
+
const coverage = domMountCoverage(adapter);
|
|
261
|
+
recordMountCoverage(coverage);
|
|
262
|
+
|
|
263
|
+
publishIngestHook('__vgaiIngestReact', {
|
|
264
|
+
worldId: world.id,
|
|
265
|
+
...ingestHookEvidence(adapter),
|
|
266
|
+
noAuthoring: false,
|
|
267
|
+
// D-K4: see mountDomIngestRoot's identical comment — MEASURED, never
|
|
268
|
+
// hard-coded.
|
|
269
|
+
reach: coverage.reach,
|
|
270
|
+
// D-L1: see mountDomIngestRoot's identical comment.
|
|
271
|
+
domEvidence: () => reactDomEvidence(root, adapter),
|
|
272
|
+
setPausedPresent: typeof lifecycle.setPaused === 'function',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Mount a discovered native-React fixture game by its folder id. Serialized
|
|
278
|
+
* through the shared entry queue; see `active-ingest.ts`'s `serializeEntry`.
|
|
279
|
+
*/
|
|
280
|
+
export function mountDomIngestRoot(store: EditorShellStore, folderId: string): Promise<void> {
|
|
281
|
+
return serializeEntry(() => mountDomIngestRootInner(store, folderId));
|
|
282
|
+
}
|