@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,899 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount a `{ ingest }` root on the CANVAS surface — the sibling of
|
|
3
|
+
* `mount-three-ingest-root.ts` on the canvas substrate.
|
|
4
|
+
*
|
|
5
|
+
* `resolveIngest2DDescriptor` (ingest/resolve-canvas.ts) builds an `IngestGame2D`
|
|
6
|
+
* straight from a canvas-surface manifest root, and `mountIngestGame2D` runs
|
|
7
|
+
* the game's own modules in the editor's realm with the Pixi render trap
|
|
8
|
+
* installed. A canvas root says `canvas`; WHICH library draws on it is a fact
|
|
9
|
+
* about the live realm, never a manifest claim, so the mount RECOGNIZES it:
|
|
10
|
+
*
|
|
11
|
+
* - the host's pixi captured a frame -> the live canvas authoring adapter;
|
|
12
|
+
* - otherwise `authoring/canvas-runtime-recognition.ts` reads the runtimes'
|
|
13
|
+
* own public registries -> `PhaserLiveAuthoringAdapter` /
|
|
14
|
+
* `BabylonAuthoringAdapter`;
|
|
15
|
+
* - nothing recognized -> the mount FAILS BY NAME.
|
|
16
|
+
*
|
|
17
|
+
* Every branch installs the SAME full `AuthoringAdapter` contract — no
|
|
18
|
+
* separate editor data model, no fallback. A
|
|
19
|
+
* runtime the host cannot recognize is never presented as one it is not, nor
|
|
20
|
+
* as an editable tree the editor fabricated.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { measureAdapterReach } from '../host/adapter-reach';
|
|
24
|
+
import { nextPaint } from '../host/after-paint';
|
|
25
|
+
import { setActiveAuthoring } from '@volter/editor-core/authoring/active-adapter';
|
|
26
|
+
import { setActiveSystems } from '@volter/editor-core/authoring/active-systems';
|
|
27
|
+
import { BabylonAuthoringAdapter } from '../host/authoring/babylon-authoring-adapter';
|
|
28
|
+
import {
|
|
29
|
+
type BabylonRealmLike,
|
|
30
|
+
findBabylonEngine,
|
|
31
|
+
findPhaserGame,
|
|
32
|
+
type PhaserRealmLike,
|
|
33
|
+
} from '../host/authoring/canvas-runtime-recognition';
|
|
34
|
+
import {
|
|
35
|
+
type ContractScenesStories,
|
|
36
|
+
createContractScenesStories,
|
|
37
|
+
} from '../host/authoring/contract-scenes-stories';
|
|
38
|
+
import { clearMountFailureReports } from '@volter/editor-core/authoring/mount-failure-report';
|
|
39
|
+
import { PhaserLiveAuthoringAdapter } from '../host/authoring/phaser-live-authoring-adapter';
|
|
40
|
+
import { PixiAuthoringAdapter } from '../host/authoring/pixi-authoring-adapter';
|
|
41
|
+
import { createCreationSiteCanvasWriteTarget } from '../host/authoring/pixi-creation-site-write-target';
|
|
42
|
+
import { resolveCanvasPixiForEditor } from '../host/canvas-entry-runtime';
|
|
43
|
+
import {
|
|
44
|
+
capturePixiDisplayObjectThumbnail,
|
|
45
|
+
registerPresentedPixiApps,
|
|
46
|
+
} from '@volter/editor-core/canvas-preview-frames';
|
|
47
|
+
import { editorConsole } from '@volter/editor-core/editor-console';
|
|
48
|
+
import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
|
|
49
|
+
import { GAME_SURFACE_CONTAINMENT_CSS } from '../host/game-realm-page';
|
|
50
|
+
import {
|
|
51
|
+
clearGameSurface,
|
|
52
|
+
gameLoopGate,
|
|
53
|
+
setGameInputGate,
|
|
54
|
+
setGameSurface,
|
|
55
|
+
} from '../host/gated-globals';
|
|
56
|
+
import { authoringJournal } from '../host/history/json-history-resource';
|
|
57
|
+
import { acquireLiveDocument, liveDocumentContainer } from '@volter/editor-core/live-document';
|
|
58
|
+
import { getCurrentProject } from '@volter/editor-core/project-manager';
|
|
59
|
+
import { recordRootReadiness } from '@volter/editor-core/readiness';
|
|
60
|
+
import type { MeasuredLoop } from '../host/same-realm-loop-gate';
|
|
61
|
+
import { projectContractSystemAdapters } from '@volter/game-runtime/adapter/ingest/contract-system-adapters';
|
|
62
|
+
import type { RenderDebugWiring } from '@volter/game-runtime/dev/render-debug-adapter';
|
|
63
|
+
import type { IngestGame2D } from '@volter/game-runtime/pixi/ingest';
|
|
64
|
+
import { INGEST_GAME_2D_LOAD_ERROR_NAME, mountIngestGame2D } from '@volter/game-runtime/pixi/ingest';
|
|
65
|
+
import { createPhysics2DRegistry } from '@volter/game-runtime/pixi/physics-registry';
|
|
66
|
+
import { installPixiRenderPassBracket } from '@volter/game-runtime/pixi/render-pass-bracket';
|
|
67
|
+
import {
|
|
68
|
+
composePhysicsAdapters2D,
|
|
69
|
+
createPhysicsAdapter2D,
|
|
70
|
+
type PhysicsAdapter2D,
|
|
71
|
+
} from '@volter/game-runtime/pixi/system-adapters';
|
|
72
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter/authoring';
|
|
73
|
+
import { readGameReady } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
74
|
+
import { displayKeyedPhysics } from '@volter/editor-project/adapter/system-adapter';
|
|
75
|
+
import type { ResolvedAdapterRoot } from '@volter/editor-project/manifest/load';
|
|
76
|
+
import type { Application, Container } from 'pixi.js';
|
|
77
|
+
import { serializeEntry, setActiveIngest } from './active-ingest';
|
|
78
|
+
import { withDetectedDomSurface } from './authoring/ingest-dom-surface-authoring';
|
|
79
|
+
import { setCaptureWait } from './capture-wait-report';
|
|
80
|
+
import { ingestGameRealmWindow, readIngestGameContract } from './game-contract-realm';
|
|
81
|
+
import { repaintHeldSceneSwitch } from './held-scene-repaint';
|
|
82
|
+
import { claimUnresizedSurfaceBox } from './host-surface-box';
|
|
83
|
+
import { landIngestBootInEdit } from './ingest-boot-viewport';
|
|
84
|
+
import { installIngestCanvasScene } from './ingest-canvas-scene-document';
|
|
85
|
+
import { ingestHookEvidence, publishIngestHook } from './ingest-evidence-hook';
|
|
86
|
+
import { bindIngestLifecycle, ingestPlaying } from './ingest-play-control';
|
|
87
|
+
import { wireIngestSystems } from './ingest-render-debug';
|
|
88
|
+
import { LOOP_PROBE_ABSENT, recordMountCoverage } from './mount-coverage';
|
|
89
|
+
import { resolveIngest2DDescriptor } from './resolve-canvas';
|
|
90
|
+
import { getIngestGame2D } from './surface-canvas';
|
|
91
|
+
import { exitActiveIngest } from './unmount-ingest-root';
|
|
92
|
+
|
|
93
|
+
/** How this mount reached the game's runtime, in the words a capability
|
|
94
|
+
* warning hands the reader. Mechanism only — never a grade. */
|
|
95
|
+
const CAPTURE_MECHANISM = "this game imports the host's pixi directly, in the editor's own realm";
|
|
96
|
+
|
|
97
|
+
/** What the pixi branch's render-debug wiring hands back to the mount, so the
|
|
98
|
+
* session's ONE teardown path can end both halves of it. */
|
|
99
|
+
interface CanvasRenderDebugHandle {
|
|
100
|
+
readonly wiring: RenderDebugWiring | null;
|
|
101
|
+
/** Takes the bracket back off the renderer's runners. */
|
|
102
|
+
readonly uninstall: () => void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Publish the game's DECLARED system surface — the line this lane was missing.
|
|
107
|
+
*
|
|
108
|
+
* A canvas ingest used to hand `setActiveSystems({})` unconditionally, so
|
|
109
|
+
* `game.commands()`/`game.state()`/`game.waitSimTime()` had nothing to answer
|
|
110
|
+
* for EVERY canvas ingest, including one that declared a full contract. That is
|
|
111
|
+
* not the same as the honest empty: `command-listener.ts` already refuses those
|
|
112
|
+
* calls over a plane-less ingest by naming the missing plane, and that refusal
|
|
113
|
+
* stays exactly right for a game that declares nothing. What was wrong is that
|
|
114
|
+
* a game which DID declare could not be heard — a lane you can see but cannot
|
|
115
|
+
* DRIVE.
|
|
116
|
+
*
|
|
117
|
+
* `wireIngestSystems` (`ingest/ingest-render-debug.ts`) is the ONE assembly
|
|
118
|
+
* point, shared with the three lane, so a shim never learns which surface it is
|
|
119
|
+
* beside and no second assembly can publish a different surface from the same
|
|
120
|
+
* declaration.
|
|
121
|
+
*
|
|
122
|
+
* `renderDebug` is ENGINE-owned on both surfaces — no game declares it — and
|
|
123
|
+
* this lane reaches it through `renderer`: the captured Pixi renderer's own
|
|
124
|
+
* WebGL2 context is what the GL-level frame capture instruments, and its own
|
|
125
|
+
* `prerender`/`postrender` runners are the bracket. Passed `undefined` (the
|
|
126
|
+
* recognized Phaser/Babylon branch, where the host's pixi captured nothing) the
|
|
127
|
+
* slot stays absent, which is the honest answer and not a degraded one.
|
|
128
|
+
*/
|
|
129
|
+
function publishCanvasIngestSystems(
|
|
130
|
+
gameName: string,
|
|
131
|
+
renderer: unknown,
|
|
132
|
+
physics: PhysicsAdapter2D | null,
|
|
133
|
+
): CanvasRenderDebugHandle {
|
|
134
|
+
const contractSystems = readIngestGameContract()?.systems;
|
|
135
|
+
let uninstallBracket: (() => void) | null = null;
|
|
136
|
+
const { systems, renderDebug, debugCollisions } = wireIngestSystems({
|
|
137
|
+
contractSystems,
|
|
138
|
+
surface: 'canvas',
|
|
139
|
+
...(physics ? { physics } : {}),
|
|
140
|
+
...(renderer ? { canvasRuntime: { renderer } } : {}),
|
|
141
|
+
// The bracket is this surface's own seam. `wireIngestSystems` decides WHAT
|
|
142
|
+
// goes in the pass; the renderer's runners decide WHEN the pass is.
|
|
143
|
+
setRenderPassHooks: (hooks) => {
|
|
144
|
+
uninstallBracket?.();
|
|
145
|
+
uninstallBracket = hooks ? installPixiRenderPassBracket(renderer, hooks) : null;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
setActiveSystems(systems);
|
|
149
|
+
editorConsole.log(
|
|
150
|
+
contractSystems
|
|
151
|
+
? `canvas ingest: "${gameName}" declares the vgai game contract — ` +
|
|
152
|
+
`${contractSystems.commands?.length ?? 0} commands, ` +
|
|
153
|
+
`${contractSystems.state?.length ?? 0} state providers`
|
|
154
|
+
: `canvas ingest: "${gameName}" declares no vgai game contract — game.commands()/` +
|
|
155
|
+
'game.state() have nothing to answer and will refuse by naming that',
|
|
156
|
+
'ingest',
|
|
157
|
+
);
|
|
158
|
+
// A name two feeders both declare is REFUSED when called, by a coded error
|
|
159
|
+
// naming both. Said here too, because a refusal nobody sees until they call
|
|
160
|
+
// the name is a defect that ships.
|
|
161
|
+
for (const collision of debugCollisions) {
|
|
162
|
+
editorConsole.error(`canvas ingest: "${gameName}" — ${collision}; it will refuse`, 'ingest');
|
|
163
|
+
}
|
|
164
|
+
if (renderer) {
|
|
165
|
+
editorConsole.log(
|
|
166
|
+
renderDebug
|
|
167
|
+
? `canvas ingest: "${gameName}" render debugging is live — the Frame debugger captures ` +
|
|
168
|
+
"through the game's own Pixi renderer (draws are unattributed: Pixi's batcher " +
|
|
169
|
+
'merges display objects, so no display object is the author of a draw)'
|
|
170
|
+
: `canvas ingest: "${gameName}" has no render-debug capability — its renderer exposes ` +
|
|
171
|
+
'no real WebGL2 context, or no prerender/postrender runners to bracket',
|
|
172
|
+
'ingest',
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return { wiring: renderDebug, uninstall: () => uninstallBracket?.() };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* TWO failures, TWO sentences.
|
|
180
|
+
*
|
|
181
|
+
* `mountIngestGame2D` already distinguishes them — `IngestGame2DLoadError`
|
|
182
|
+
* when the game's own modules never ran, a spent capture window when they ran
|
|
183
|
+
* and nothing drew — and the mount's one catch used to flatten both into the
|
|
184
|
+
* capture trap's words: "the host's pixi captured no frame … or it bundles its
|
|
185
|
+
* own (un-shared) copy of pixi.js". That sentence names a module-IDENTITY
|
|
186
|
+
* defect and sends the reader to `vite-plugin-module-doorways.ts`. MEASURED on
|
|
187
|
+
* a packaged build whose real cause was a JSX-bearing `.js` module this host
|
|
188
|
+
* had no transform for: the console said un-shared pixi, the network said
|
|
189
|
+
* "Failed to fetch dynamically imported module … /src/game.js".
|
|
190
|
+
*/
|
|
191
|
+
function unrecognizedCanvasMountMessage(gameId: string, failure: unknown): string {
|
|
192
|
+
if (failure instanceof Error && failure.name === INGEST_GAME_2D_LOAD_ERROR_NAME) {
|
|
193
|
+
return (
|
|
194
|
+
`canvas ingest: game "${gameId}" never ran — its own modules failed to load, so nothing ` +
|
|
195
|
+
`could render and there is no canvas runtime in the realm to recognize (${failure})`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return (
|
|
199
|
+
`canvas ingest: game "${gameId}" mounted no recognized canvas runtime — the host's pixi ` +
|
|
200
|
+
'captured no frame, and the realm holds no Phaser game or Babylon.js engine on their ' +
|
|
201
|
+
`own public registries either (${failure})`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function rendererCanvasBelongsToHost(renderer: unknown, hostEl: HTMLElement): boolean {
|
|
206
|
+
const canvas = (renderer as { canvas?: unknown } | null)?.canvas;
|
|
207
|
+
return canvas instanceof Node && hostEl.contains(canvas);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The physics carrier THIS GAME declared, in the canvas surface's own
|
|
212
|
+
* (Container-keyed) vocabulary — or `null` when it declared none.
|
|
213
|
+
*
|
|
214
|
+
* The projection is the ONE validator: presence + typeof for the four members,
|
|
215
|
+
* plus the keying check that makes a node-id-keyed declaration on this surface
|
|
216
|
+
* `malformed` by name instead of a bound shape nothing here can call
|
|
217
|
+
* (`@volter/game-runtime/adapter/ingest/contract-system-adapters`). Both verdicts reach
|
|
218
|
+
* the reader through the coverage row for `system.physics`, so a refusal here
|
|
219
|
+
* is never silent.
|
|
220
|
+
*/
|
|
221
|
+
function declaredCanvasPhysics(): PhysicsAdapter2D | null {
|
|
222
|
+
return displayKeyedPhysics(
|
|
223
|
+
projectContractSystemAdapters(readIngestGameContract()?.systems, 'canvas').bound.physics,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** A canvas runtime the host RECOGNIZED in the live realm, with the structural
|
|
228
|
+
* authoring adapter that projects it. Nothing here is derived from the
|
|
229
|
+
* manifest, the mount route or the game's id. */
|
|
230
|
+
interface RecognizedCanvasRuntime {
|
|
231
|
+
readonly runtime: 'phaser' | 'babylon';
|
|
232
|
+
readonly adapter: AuthoringAdapter & { dispose(): void; refresh(): unknown };
|
|
233
|
+
/** Words a capability warning hands the reader — mechanism only. */
|
|
234
|
+
readonly mechanism: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Ask the REALM which non-pixi canvas runtime it actually contains, through
|
|
239
|
+
* `authoring/canvas-runtime-recognition.ts`'s reads of the runtimes' own public
|
|
240
|
+
* registries. `null` means the host recognizes nothing here — which fails the
|
|
241
|
+
* mount by name at the call site.
|
|
242
|
+
*/
|
|
243
|
+
function recognizeStructuralCanvasRuntime(
|
|
244
|
+
store: EditorShellStore,
|
|
245
|
+
realm: PhaserRealmLike & BabylonRealmLike,
|
|
246
|
+
): RecognizedCanvasRuntime | null {
|
|
247
|
+
const phaserGame = findPhaserGame(realm);
|
|
248
|
+
if (phaserGame) {
|
|
249
|
+
return {
|
|
250
|
+
runtime: 'phaser',
|
|
251
|
+
adapter: new PhaserLiveAuthoringAdapter(phaserGame, store),
|
|
252
|
+
mechanism:
|
|
253
|
+
"this game runs its own Phaser instance in the editor's own realm; the host reads " +
|
|
254
|
+
'that live game’s active scenes and display lists',
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const babylonEngine = findBabylonEngine(realm);
|
|
258
|
+
if (babylonEngine) {
|
|
259
|
+
return {
|
|
260
|
+
runtime: 'babylon',
|
|
261
|
+
adapter: new BabylonAuthoringAdapter(babylonEngine, store),
|
|
262
|
+
mechanism:
|
|
263
|
+
"this game runs its own Babylon.js engine in the editor's own realm; the host reads " +
|
|
264
|
+
'that live engine’s scenes and root nodes',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Publish a recognized non-pixi canvas runtime as the live ingest session —
|
|
272
|
+
* the same session/authoring/coverage/hook handoff the pixi branch performs,
|
|
273
|
+
* over that runtime's own structural adapter.
|
|
274
|
+
*
|
|
275
|
+
* Pause runs through the same-realm loop gate (`gated-globals.ts` installs it
|
|
276
|
+
* over the game module's scheduling entry points), which is a real hold rather
|
|
277
|
+
* than a button that lies. No probe has measured that hold on this route, so
|
|
278
|
+
* the loop verdict stays `null` — unmeasured is its own answer.
|
|
279
|
+
*/
|
|
280
|
+
function publishStructuralCanvasRuntime(
|
|
281
|
+
store: EditorShellStore,
|
|
282
|
+
game: IngestGame2D,
|
|
283
|
+
sessionId: string,
|
|
284
|
+
hostEl: HTMLElement,
|
|
285
|
+
recognized: RecognizedCanvasRuntime,
|
|
286
|
+
): void {
|
|
287
|
+
const rawAuthoring = recognized.adapter;
|
|
288
|
+
rawAuthoring.refresh();
|
|
289
|
+
const adapter = withDetectedDomSurface({
|
|
290
|
+
primary: rawAuthoring,
|
|
291
|
+
worldId: sessionId,
|
|
292
|
+
hostEl,
|
|
293
|
+
store,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const gate = gameLoopGate();
|
|
297
|
+
const loopFacts = (): { loop: MeasuredLoop | null; reason: string } =>
|
|
298
|
+
gate
|
|
299
|
+
? {
|
|
300
|
+
loop: null,
|
|
301
|
+
reason:
|
|
302
|
+
'the same-realm loop gate holds this game’s scheduling, but no probe has measured ' +
|
|
303
|
+
'that control on this route',
|
|
304
|
+
}
|
|
305
|
+
: { loop: null, reason: LOOP_PROBE_ABSENT };
|
|
306
|
+
const lifecycle = bindIngestLifecycle(
|
|
307
|
+
gate
|
|
308
|
+
? {
|
|
309
|
+
setPaused: (paused) => {
|
|
310
|
+
if (paused) gate.hold();
|
|
311
|
+
else gate.release();
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
: {
|
|
315
|
+
pauseGap: `${recognized.runtime} exposes no host-controlled loop gate`,
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
let disposeScene = () => {};
|
|
320
|
+
setActiveIngest({
|
|
321
|
+
kind: 'canvas',
|
|
322
|
+
session: {
|
|
323
|
+
store,
|
|
324
|
+
adapter: rawAuthoring,
|
|
325
|
+
hostEl,
|
|
326
|
+
lifecycle,
|
|
327
|
+
loop: loopFacts,
|
|
328
|
+
// The ONE teardown path for the surface this branch inherited (see the
|
|
329
|
+
// ownership block in `mountCanvasIngestRootInner`).
|
|
330
|
+
dispose: () => {
|
|
331
|
+
disposeScene();
|
|
332
|
+
rawAuthoring.dispose();
|
|
333
|
+
clearGameSurface();
|
|
334
|
+
hostEl.remove();
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
siblings: [],
|
|
338
|
+
worldId: sessionId,
|
|
339
|
+
});
|
|
340
|
+
disposeScene = installIngestCanvasScene(store);
|
|
341
|
+
|
|
342
|
+
setGameInputGate(() => store.playState === 'playing' && store.activeViewportTab === 'play');
|
|
343
|
+
setActiveAuthoring(adapter);
|
|
344
|
+
// No captured Pixi renderer on this branch — the host's pixi drew nothing, so
|
|
345
|
+
// there is no context to instrument, no runner pair to bracket, and no
|
|
346
|
+
// host-owned registry carrier to compose with either: what the game itself
|
|
347
|
+
// declared is the only physics this route can have.
|
|
348
|
+
publishCanvasIngestSystems(game.name, undefined, declaredCanvasPhysics());
|
|
349
|
+
landIngestBootInEdit(store);
|
|
350
|
+
store.notifyIngestEdit();
|
|
351
|
+
clearMountFailureReports();
|
|
352
|
+
|
|
353
|
+
recordMountCoverage({
|
|
354
|
+
reach: measureAdapterReach({ authoring: adapter }),
|
|
355
|
+
reachMechanism: recognized.mechanism,
|
|
356
|
+
loop: loopFacts,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
editorConsole.log(
|
|
360
|
+
`canvas ingest: recognized ${recognized.runtime} in the realm and captured "${game.name}"`,
|
|
361
|
+
'ingest',
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
publishIngestHook('__vgaiIngest2D', {
|
|
365
|
+
gameId: sessionId,
|
|
366
|
+
...ingestHookEvidence(adapter),
|
|
367
|
+
runtime: recognized.runtime,
|
|
368
|
+
// Live for the same reason the pixi branch's is: the game keeps building
|
|
369
|
+
// its tree after the mount, so a snapshot goes stale immediately.
|
|
370
|
+
get reflected() {
|
|
371
|
+
return rawAuthoring.refresh();
|
|
372
|
+
},
|
|
373
|
+
...(lifecycle.setPaused ? { setPaused: lifecycle.setPaused } : {}),
|
|
374
|
+
reach: measureAdapterReach({ authoring: adapter }),
|
|
375
|
+
adapter,
|
|
376
|
+
store,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function mountCanvasIngestRootInner(
|
|
381
|
+
store: EditorShellStore,
|
|
382
|
+
game: IngestGame2D,
|
|
383
|
+
sessionId: string,
|
|
384
|
+
): Promise<void> {
|
|
385
|
+
// D-V1: tear down WHATEVER kind is currently active, not just a prior
|
|
386
|
+
// pixi session — closes the cross-kind leak (F26 residual).
|
|
387
|
+
exitActiveIngest();
|
|
388
|
+
|
|
389
|
+
// The Game document exists only while a runtime does, so install it (and
|
|
390
|
+
// wait for its panel to commit) before reading the container it owns.
|
|
391
|
+
await acquireLiveDocument();
|
|
392
|
+
const gameContainer = liveDocumentContainer();
|
|
393
|
+
if (!gameContainer) {
|
|
394
|
+
editorConsole.error('Game container not mounted — cannot ingest (canvas)', 'ingest');
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
store.setPlayState('playing');
|
|
399
|
+
editorConsole.log(`Ingesting unmodified PixiJS game: ${game.name}`, 'ingest');
|
|
400
|
+
|
|
401
|
+
// PD-3: READINESS, RECORDED. The three lane's self-booting mount has always
|
|
402
|
+
// written this row (`authoring/ingest-root-adapter.ts`); the canvas lane
|
|
403
|
+
// never did, so a Pixi ingest was simply MISSING from the `readiness` facet —
|
|
404
|
+
// and a missing row is indistinguishable from a root nobody has asked about,
|
|
405
|
+
// which is the exact ambiguity the facet exists to remove.
|
|
406
|
+
//
|
|
407
|
+
// The mechanism is read the same way the three lane reads it, off the game's
|
|
408
|
+
// OWN contract, because `ReadinessMechanism` names HOW readiness is answered
|
|
409
|
+
// and there are exactly three answerers (see `readiness.ts`'s header). A lane
|
|
410
|
+
// name would be a fourth value on a different axis — which root mounted it is
|
|
411
|
+
// already knowable from the root itself.
|
|
412
|
+
const declaredReady = readGameReady(readIngestGameContract());
|
|
413
|
+
if (declaredReady.malformed !== null) {
|
|
414
|
+
// Same sentence the three lane says (`ingest-root-adapter.ts`): a broken
|
|
415
|
+
// declaration is loud, and the host measures instead.
|
|
416
|
+
editorConsole.error(
|
|
417
|
+
`Ingest game "${game.id}" ${declaredReady.malformed} — the host is measuring readiness ` +
|
|
418
|
+
'instead.',
|
|
419
|
+
'ingest',
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
const readinessMechanism = declaredReady.ready === null ? 'measured-wait' : 'contract-ready';
|
|
423
|
+
const readinessSource = declaredReady.ready === null ? 'measured' : 'declared';
|
|
424
|
+
recordRootReadiness({
|
|
425
|
+
rootId: game.id,
|
|
426
|
+
mechanism: readinessMechanism,
|
|
427
|
+
source: readinessSource,
|
|
428
|
+
state: 'waiting',
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// ## RESOURCE OWNERSHIP — the host surface for a canvas ingest
|
|
432
|
+
//
|
|
433
|
+
// OWNER: this mount. SHARERS: the game's realm (it sees `hostEl` as
|
|
434
|
+
// `document.body`, via `setGameSurface`) and the pixel doors (they read the
|
|
435
|
+
// game's `Application` through `registerPresentedPixiApps`). ONE teardown
|
|
436
|
+
// path: the session's `dispose()` below, which unregisters the app, clears
|
|
437
|
+
// the realm surface and removes `hostEl` — removing the surface removes the
|
|
438
|
+
// game's ENTIRE DOM with it. The two early-exit paths (a recognized non-pixi
|
|
439
|
+
// runtime, and nothing recognized) hand that same teardown on or run it
|
|
440
|
+
// before throwing; nothing else may end this surface.
|
|
441
|
+
//
|
|
442
|
+
// THE GAME'S PAGE IS THIS BOX, from before its first module runs — the exact
|
|
443
|
+
// adoption the three ingest mount performs
|
|
444
|
+
// (`authoring/ingest-root-adapter.ts`, `game-realm-page.ts` reason 1), and
|
|
445
|
+
// its absence here is why an ingested Pixi game's canvas landed at PAGE
|
|
446
|
+
// level, below the editor's fold: MEASURED on the `flappy` fixture, a
|
|
447
|
+
// `body > canvas` at y=915 while the Game pane held an empty 300x150
|
|
448
|
+
// placeholder — so every pixel door photographed the placeholder and every
|
|
449
|
+
// one of them was honestly blank.
|
|
450
|
+
//
|
|
451
|
+
// The `background` is the LETTERBOX. Because this lane never re-resolves the
|
|
452
|
+
// game's renderer, a game whose aspect differs from the pane's leaves margins
|
|
453
|
+
// (`ingest/host-surface-box.ts`), and margins with no background are alpha-0
|
|
454
|
+
// in every capture — a region whose appearance is whatever the viewer happens
|
|
455
|
+
// to composite it over, and which reads as 64% #000000 to the frame's own
|
|
456
|
+
// flatness check. Black is both the letterbox convention and an answer the
|
|
457
|
+
// screen and the photograph agree on. It is a DEFAULT, not a claim: this
|
|
458
|
+
// element is the game's `document.body` in-realm, so a game that paints its
|
|
459
|
+
// own page background still wins.
|
|
460
|
+
const hostEl = document.createElement('div');
|
|
461
|
+
hostEl.id = 'container';
|
|
462
|
+
hostEl.style.cssText = `position:absolute;inset:0;width:100%;height:100%;overflow:hidden;background:#000;${GAME_SURFACE_CONTAINMENT_CSS}`;
|
|
463
|
+
gameContainer.appendChild(hostEl);
|
|
464
|
+
setGameSurface(hostEl);
|
|
465
|
+
// Same scoped-CSS contract the three lane holds (ingest-root-adapter.ts):
|
|
466
|
+
// the surface is the scope root, and the project's declared page sheet is
|
|
467
|
+
// installed BEFORE the game's entry runs so its first paint is styled.
|
|
468
|
+
// Measured missing here: babylon-space-truckers' declared canvas{100%}
|
|
469
|
+
// sizing never loaded on the canvas lane, leaving the game a default
|
|
470
|
+
// 300x150 canvas in the pane corner (2026-08-28).
|
|
471
|
+
{
|
|
472
|
+
const { markGameCssScope } = await import('@volter/editor-sdk/session/game-css-scope');
|
|
473
|
+
markGameCssScope(hostEl);
|
|
474
|
+
const scopedCssProject = getCurrentProject();
|
|
475
|
+
if (scopedCssProject) {
|
|
476
|
+
const { ensureScopedGameStyles } = await import('@volter/editor-core/scoped-game-css');
|
|
477
|
+
await ensureScopedGameStyles(scopedCssProject.rootPath);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// F26: same cold-mount announcement the three adapter makes
|
|
482
|
+
// (ingest-root-adapter.ts) — a pixi game may declare the game contract and
|
|
483
|
+
// defer its session until ▶; games that ignore the flag behave as before.
|
|
484
|
+
(window as unknown as { __vgaiMountCold?: boolean }).__vgaiMountCold = true;
|
|
485
|
+
// THE namespace the game's own modules will resolve `pixi.js` to.
|
|
486
|
+
//
|
|
487
|
+
// `installSceneCapture2D` wraps `Application.prototype.render` on the
|
|
488
|
+
// namespace it is handed, and the engine states the invariant in its own
|
|
489
|
+
// header: the trap must be installed on the SAME `pixi.js` module instance
|
|
490
|
+
// the game uses, or the game "cannot be captured at all, and its mount fails
|
|
491
|
+
// by name". Under the packaged runtime the game's entry is served as
|
|
492
|
+
// `/@fs/<project>/…` by the PROJECT-rooted Vite (the only Vite there — the
|
|
493
|
+
// editor's own shell is a prebuilt static bundle), so the game's `pixi.js`
|
|
494
|
+
// is the project's and a shell namespace traps a class nobody calls.
|
|
495
|
+
// MEASURED with the shell's: the `bunnymark` ingest fixture opened on a
|
|
496
|
+
// packaged build failed by name after 8s of visible time, quoting that same
|
|
497
|
+
// invariant back — "the game never rendered, or it bundles its own
|
|
498
|
+
// (un-shared) copy of pixi.js".
|
|
499
|
+
const pixi = await resolveCanvasPixiForEditor();
|
|
500
|
+
let mount: Exclude<Awaited<ReturnType<typeof mountIngestGame2D>>, null>;
|
|
501
|
+
try {
|
|
502
|
+
const candidate = await mountIngestGame2D(pixi, game, {
|
|
503
|
+
...(game.captureTimeoutMs !== undefined ? { captureTimeoutMs: game.captureTimeoutMs } : {}),
|
|
504
|
+
// The editor can have several live Pixi Applications at once: prefab and
|
|
505
|
+
// screen previews render beside the game. The realm redirects the game's
|
|
506
|
+
// `document.body` into this host, so canvas containment is the honest
|
|
507
|
+
// ownership discriminator and prevents a preview's earlier frame from
|
|
508
|
+
// being mistaken for this ingest root.
|
|
509
|
+
acceptCapture: ({ renderer }) => rendererCanvasBelongsToHost(renderer, hostEl),
|
|
510
|
+
// The same writer the three lane installs (`ingest-root-adapter.ts`): the
|
|
511
|
+
// capture window is a budget of VISIBLE time and PARKS on a hidden tab, so
|
|
512
|
+
// without this the wait reads as a hung mount at every door.
|
|
513
|
+
onWait: (wait) => setCaptureWait(game.id, wait),
|
|
514
|
+
// A classic/module Babylon bundle cannot render through the host's Pixi
|
|
515
|
+
// namespace. When its carrier publishes a boot promise, await that
|
|
516
|
+
// concrete signal and dispatch structurally without first burning a
|
|
517
|
+
// visible-time Pixi timeout (which can remain parked forever in an
|
|
518
|
+
// unfocused editor window).
|
|
519
|
+
preferStructuralRuntime: async () => {
|
|
520
|
+
const realm = ingestGameRealmWindow() as unknown as PhaserRealmLike & BabylonRealmLike;
|
|
521
|
+
await realm.__vgaiBabylon?.ready;
|
|
522
|
+
return recognizeStructuralCanvasRuntime(store, realm) !== null;
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
if (candidate === null) {
|
|
526
|
+
const recognized = recognizeStructuralCanvasRuntime(
|
|
527
|
+
store,
|
|
528
|
+
ingestGameRealmWindow() as unknown as PhaserRealmLike & BabylonRealmLike,
|
|
529
|
+
);
|
|
530
|
+
if (recognized) {
|
|
531
|
+
publishStructuralCanvasRuntime(store, game, sessionId, hostEl, recognized);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
throw new Error(
|
|
535
|
+
`canvas ingest: game "${game.id}" reported a structural runtime, but it disappeared before dispatch`,
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
mount = candidate;
|
|
539
|
+
} catch (pixiFailure) {
|
|
540
|
+
// The host's pixi drew no capturable frame — but the game's OWN modules
|
|
541
|
+
// have already run (`mountIngestGame2D` calls `game.load()` before it waits
|
|
542
|
+
// for a frame), so the realm is now the place to ask what this game
|
|
543
|
+
// actually is. Recognized -> that runtime's structural authoring adapter,
|
|
544
|
+
// the same full contract the pixi branch installs. Recognized nothing ->
|
|
545
|
+
// fail by name below.
|
|
546
|
+
//
|
|
547
|
+
// `hostEl` STAYS: the game already appended its own DOM into it while it
|
|
548
|
+
// was the realm's page, so tearing it down here would strand a recognized
|
|
549
|
+
// runtime's canvas exactly the way the pixi branch used to be stranded.
|
|
550
|
+
// The recognized branch inherits the teardown; the unrecognized one runs
|
|
551
|
+
// it before throwing.
|
|
552
|
+
const recognized = recognizeStructuralCanvasRuntime(
|
|
553
|
+
store,
|
|
554
|
+
ingestGameRealmWindow() as unknown as PhaserRealmLike & BabylonRealmLike,
|
|
555
|
+
);
|
|
556
|
+
if (recognized) {
|
|
557
|
+
publishStructuralCanvasRuntime(store, game, sessionId, hostEl, recognized);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
clearGameSurface();
|
|
561
|
+
hostEl.remove();
|
|
562
|
+
// The row must not be left at `waiting` for a mount that is over: a parked
|
|
563
|
+
// wait and a dead one are the two states this facet exists to tell apart.
|
|
564
|
+
recordRootReadiness({
|
|
565
|
+
rootId: game.id,
|
|
566
|
+
mechanism: readinessMechanism,
|
|
567
|
+
source: readinessSource,
|
|
568
|
+
state: 'failed',
|
|
569
|
+
// Same split the three lane makes: a game that DECLARED ready and never
|
|
570
|
+
// produced a capturable render is a different defect from one that
|
|
571
|
+
// declared nothing and whose measured fallback simply expired.
|
|
572
|
+
failure:
|
|
573
|
+
declaredReady.ready === null ? 'no-readiness-declaration' : 'declared-ready-never-resolved',
|
|
574
|
+
});
|
|
575
|
+
throw new Error(unrecognizedCanvasMountMessage(game.id, pixiFailure));
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const stage = mount.stage as Container;
|
|
579
|
+
// The game's async setup may add display objects after its first captured frame —
|
|
580
|
+
// wait for the stage to settle. `nextPaint()` rather than a bare rAF because
|
|
581
|
+
// this mount is awaited by `vgai play`: in a hidden tab (every agent-worktree
|
|
582
|
+
// `vgai edit` opens one — see `after-paint.ts`) no frame ever arrives, so a
|
|
583
|
+
// bare chain hangs this bounded loop on iteration 1 and play stalls in its
|
|
584
|
+
// boot phase with nothing to report.
|
|
585
|
+
for (let i = 0; i < 90 && (stage.children?.length ?? 0) === 0; i++) {
|
|
586
|
+
await nextPaint();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// The captured `Application` — the game's own, trapped on its first render
|
|
590
|
+
// (`@volter/game-runtime/pixi/scene-capture`). Two things hang off it, and neither is
|
|
591
|
+
// available anywhere else.
|
|
592
|
+
const app = mount.capture.captured?.app as Application | undefined;
|
|
593
|
+
|
|
594
|
+
// (1) ADOPT ITS CANVAS. A game that appended into the realm's page is already
|
|
595
|
+
// inside `hostEl`; one whose module was not realm-shadowed (an in-tree
|
|
596
|
+
// fixture, imported through the editor's own bundle) still has its canvas
|
|
597
|
+
// wherever it put it. Reparent either way, and claim its CSS box — a game
|
|
598
|
+
// sizes its canvas in literal pixels while it believes it owns the window,
|
|
599
|
+
// and nothing else ever un-stamps that. The box is the UNRESIZED policy, not
|
|
600
|
+
// the three lane's fill: this mount never re-resolves the game's renderer, so
|
|
601
|
+
// filling would stretch it (`ingest/host-surface-box.ts` states both, and why
|
|
602
|
+
// they differ).
|
|
603
|
+
const gameCanvas = app?.canvas as HTMLCanvasElement | undefined;
|
|
604
|
+
if (gameCanvas) {
|
|
605
|
+
claimUnresizedSurfaceBox(gameCanvas);
|
|
606
|
+
if (gameCanvas.parentElement !== hostEl) hostEl.appendChild(gameCanvas);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// (2) MAKE IT PHOTOGRAPHABLE. A Pixi canvas is created without
|
|
610
|
+
// `preserveDrawingBuffer`, so a compositor reading it after paint gets
|
|
611
|
+
// nothing — the measured blank-capture defect `stories/story-pixi-preview.ts`
|
|
612
|
+
// records for the story lane and the canvas board. The same registry is the
|
|
613
|
+
// answer here: the pixel doors ask `presentedPixiFrame` for a registered
|
|
614
|
+
// canvas and it extracts through Pixi's own renderer instead. Registered here
|
|
615
|
+
// for the same reason the first-party canvas root registers at ITS mount
|
|
616
|
+
// (`authoring/canvas-design-mount.ts`) — an ingested game and a first-party
|
|
617
|
+
// canvas world are the same surface, and the doors must not be able to tell
|
|
618
|
+
// them apart.
|
|
619
|
+
const unregisterPresentedApp = registerPresentedPixiApps(app ? [app] : []);
|
|
620
|
+
|
|
621
|
+
// TWO possible owners of a display object's pose, and they own DIFFERENT
|
|
622
|
+
// objects: the host's registry-backed carrier answers for bodies the editor
|
|
623
|
+
// itself created, and the game's declared carrier answers for the ones its
|
|
624
|
+
// own simulation drives. The registry is EMPTY on this route (an ingested
|
|
625
|
+
// game never registers into it), which is exactly why handing the write
|
|
626
|
+
// target that one alone left freeze/commit a no-op on every ingest — a gizmo
|
|
627
|
+
// drag over a simulated body was stomped by the game's next frame with
|
|
628
|
+
// nothing anywhere saying so.
|
|
629
|
+
//
|
|
630
|
+
// Composed ownership-first (`composePhysicsAdapters2D`): the declared carrier
|
|
631
|
+
// is asked first because it is the one with a real claim here, and the
|
|
632
|
+
// registry stays as the fallback rather than being dropped, so a host-created
|
|
633
|
+
// body keeps behaving exactly as before.
|
|
634
|
+
const declaredPhysics = declaredCanvasPhysics();
|
|
635
|
+
const registryPhysics = createPhysicsAdapter2D(createPhysics2DRegistry());
|
|
636
|
+
const physicsAdapter = declaredPhysics
|
|
637
|
+
? composePhysicsAdapters2D([declaredPhysics, registryPhysics])
|
|
638
|
+
: registryPhysics;
|
|
639
|
+
// Every authored edit writes the GAME'S OWN SOURCE, at the line that
|
|
640
|
+
// constructed the object — the canvas half of ARCHITECTURE-CORE §Editor's
|
|
641
|
+
// ingest-authoring model, and the same destination the three ingest lane has
|
|
642
|
+
// (`structuralThree`). There is deliberately no branch here on whether a
|
|
643
|
+
// write is reachable: the backend answers that per edit, in the sentence the
|
|
644
|
+
// user reads, and an edit it cannot honestly anchor stays live-only WITH ITS
|
|
645
|
+
// REASON rather than falling back to a destination that is not the game's.
|
|
646
|
+
// The game's own declared SCENES, in the editor's stories vocabulary — the
|
|
647
|
+
// screens this game swaps between ARE its design-time states
|
|
648
|
+
// (`authoring/contract-scenes-stories.ts` states why the projection is
|
|
649
|
+
// world-level and why it omits `isolate`). `null` for a game that declared
|
|
650
|
+
// none, which the adapter passes on as an absent provider rather than an
|
|
651
|
+
// empty picker.
|
|
652
|
+
//
|
|
653
|
+
// The repaint is THIS MOUNT'S contribution to a scene switch, because it
|
|
654
|
+
// needs what only this mount owns: the captured `Application` to draw with
|
|
655
|
+
// and the hold state to gate on. A held mount's stopped ticker is also its
|
|
656
|
+
// renderer, so without this a scene switch in Edit moves `current()` under a
|
|
657
|
+
// frozen picture (`ingest/held-scene-repaint.ts` carries the measurement and
|
|
658
|
+
// the content-vs-presentation line it walks).
|
|
659
|
+
//
|
|
660
|
+
// Handed in as the projection's SEAM rather than wrapped around the object it
|
|
661
|
+
// returns: the picker's `apply` and the `open` protocol verb's `goToScene`
|
|
662
|
+
// are one path through that module, and a wrapper around the outer member
|
|
663
|
+
// would silently leave the other one unpainted.
|
|
664
|
+
// Annotated because the seam below reads `stories.active` — its own binding,
|
|
665
|
+
// one frame later — which makes the initializer's type circular otherwise.
|
|
666
|
+
const stories: ContractScenesStories | null = createContractScenesStories(
|
|
667
|
+
readIngestGameContract(),
|
|
668
|
+
{
|
|
669
|
+
onSwitchAccepted: (sceneId) =>
|
|
670
|
+
repaintHeldSceneSwitch(sceneId, {
|
|
671
|
+
isHeld: () => !ingestPlaying(),
|
|
672
|
+
currentScene: () => stories?.active('') ?? null,
|
|
673
|
+
render: () => app?.render(),
|
|
674
|
+
schedule: (cb) => requestAnimationFrame(cb),
|
|
675
|
+
}),
|
|
676
|
+
},
|
|
677
|
+
);
|
|
678
|
+
const rawAuthoring = new PixiAuthoringAdapter(stage, store, {
|
|
679
|
+
// The captured stage is the GAME's, so the adapter's namespace is the one
|
|
680
|
+
// the capture trap was installed on.
|
|
681
|
+
pixi,
|
|
682
|
+
// `sessionId` IS the world id (see this function's doc), which is exactly
|
|
683
|
+
// the subject a held surface's undo stack belongs to — it outlives every
|
|
684
|
+
// remount of this game.
|
|
685
|
+
journal: authoringJournal(sessionId),
|
|
686
|
+
target: createCreationSiteCanvasWriteTarget({
|
|
687
|
+
physics: physicsAdapter,
|
|
688
|
+
history: store.projectHistory,
|
|
689
|
+
}),
|
|
690
|
+
...(stories ? { stories } : {}),
|
|
691
|
+
// The captured game canvas is the screen↔stage mapping `pickable`/`rects`
|
|
692
|
+
// need — the same argument the first-party canvas mount passes
|
|
693
|
+
// (`authoring/canvas-design-mount.ts`). A resolver because a restarted
|
|
694
|
+
// game presents a new canvas under this same adapter; 1:1 is correct here
|
|
695
|
+
// because `claimUnresizedSurfaceBox` above keeps the canvas at its natural
|
|
696
|
+
// CSS size, so stage units are its client pixels.
|
|
697
|
+
surface: () => (app?.canvas as HTMLElement | undefined) ?? null,
|
|
698
|
+
capturePreview: (object, size) => capturePixiDisplayObjectThumbnail(object, { ...size, pixi }),
|
|
699
|
+
});
|
|
700
|
+
const stats = rawAuthoring.refresh();
|
|
701
|
+
const adapter = withDetectedDomSurface({
|
|
702
|
+
primary: rawAuthoring,
|
|
703
|
+
worldId: sessionId,
|
|
704
|
+
hostEl,
|
|
705
|
+
store,
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
// Assigned by `publishCanvasIngestSystems` below and released by the session's
|
|
709
|
+
// ONE teardown path — declared here because that path is written first.
|
|
710
|
+
let renderDebugHandle: CanvasRenderDebugHandle | null = null;
|
|
711
|
+
let disposeScene = () => {};
|
|
712
|
+
const gate = gameLoopGate();
|
|
713
|
+
const lifecycle = bindIngestLifecycle({
|
|
714
|
+
setPaused: (paused) => {
|
|
715
|
+
// The captured ticker and the same-realm scheduling gate are one
|
|
716
|
+
// fallback capability. Holding only either half can freeze the picture
|
|
717
|
+
// while timers keep simulating, or park timers while Pixi keeps drawing.
|
|
718
|
+
mount.setPaused(paused);
|
|
719
|
+
if (paused) gate?.hold();
|
|
720
|
+
else gate?.release();
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
setActiveIngest({
|
|
725
|
+
kind: 'canvas',
|
|
726
|
+
session: {
|
|
727
|
+
store,
|
|
728
|
+
adapter: rawAuthoring,
|
|
729
|
+
hostEl,
|
|
730
|
+
lifecycle,
|
|
731
|
+
// THE one teardown path (see the ownership block above): the pixel doors
|
|
732
|
+
// stop seeing this app, the realm stops standing in for a surface that no
|
|
733
|
+
// longer exists, and removing `hostEl` removes the game's whole DOM. A
|
|
734
|
+
// re-mount therefore starts from nothing — no stale registration can make
|
|
735
|
+
// the next capture read the previous game's canvas.
|
|
736
|
+
dispose: () => {
|
|
737
|
+
// Take the render-pass bracket off the renderer's runners and reject
|
|
738
|
+
// any armed capture BEFORE the game goes, so nothing outlives the mount
|
|
739
|
+
// holding a patched GL context — the same ordering the three lane's
|
|
740
|
+
// dispose uses (`authoring/ingest-root-adapter.ts`).
|
|
741
|
+
disposeScene();
|
|
742
|
+
renderDebugHandle?.uninstall();
|
|
743
|
+
renderDebugHandle?.wiring?.dispose();
|
|
744
|
+
unregisterPresentedApp();
|
|
745
|
+
mount.dispose();
|
|
746
|
+
clearGameSurface();
|
|
747
|
+
hostEl.remove();
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
siblings: [],
|
|
751
|
+
worldId: sessionId,
|
|
752
|
+
});
|
|
753
|
+
disposeScene = installIngestCanvasScene(store);
|
|
754
|
+
|
|
755
|
+
// R2c: PixiJS ingest fixtures (`ingest/games/**`) are just as
|
|
756
|
+
// unshadowed-by-default as the three.js ones — drive the same gate as
|
|
757
|
+
// {@link mountThreeIngestRoot} (see that function's doc comment).
|
|
758
|
+
setGameInputGate(() => store.playState === 'playing' && store.activeViewportTab === 'play');
|
|
759
|
+
|
|
760
|
+
setActiveAuthoring(adapter);
|
|
761
|
+
// The captured game's OWN renderer — the one object that carries both halves
|
|
762
|
+
// of render debugging on this surface (its real WebGL2 context, and its
|
|
763
|
+
// published render-pass runners). Nothing else in the editor has it.
|
|
764
|
+
// The composed carrier — the SAME object the write target above drives — is
|
|
765
|
+
// what the bag publishes, and only when the game declared one: publishing the
|
|
766
|
+
// host's empty registry carrier would light a green `system.physics` row over
|
|
767
|
+
// a capability no ingested game has.
|
|
768
|
+
renderDebugHandle = publishCanvasIngestSystems(
|
|
769
|
+
game.name,
|
|
770
|
+
mount.capture.captured?.renderer,
|
|
771
|
+
declaredPhysics ? physicsAdapter : null,
|
|
772
|
+
);
|
|
773
|
+
landIngestBootInEdit(store);
|
|
774
|
+
store.notifyIngestEdit();
|
|
775
|
+
clearMountFailureReports(); // D-W3: mount succeeded
|
|
776
|
+
// PD-3: the mount stood — but `contract-ready` may only be recorded when the
|
|
777
|
+
// declared promise actually ANSWERED. The three lane's `awaitWorldReady`
|
|
778
|
+
// (`authoring/ingest-root-adapter.ts`) is the precedent: a game that declares
|
|
779
|
+
// `window.vgaiGame.ready` is awaited exactly once, and a declared signal that
|
|
780
|
+
// REJECTS does not fail the mount (the frame is already captured) — it
|
|
781
|
+
// degrades to the measured wait, LOUDLY, and the row then reports `measured`,
|
|
782
|
+
// which is the truth about which answer stood. Recording `contract-ready`
|
|
783
|
+
// without consulting the promise — as this path first shipped — attributes
|
|
784
|
+
// the answer to a declaration nobody read.
|
|
785
|
+
let readinessStood: 'declared' | 'measured' = 'measured';
|
|
786
|
+
if (declaredReady.ready) {
|
|
787
|
+
try {
|
|
788
|
+
await declaredReady.ready();
|
|
789
|
+
readinessStood = 'declared';
|
|
790
|
+
} catch (err) {
|
|
791
|
+
editorConsole.error(
|
|
792
|
+
`Ingest game "${game.id}" declared \`window.vgaiGame.ready\` and it REJECTED ` +
|
|
793
|
+
`(${String(err)}) — the frame was already captured, so the host fell back to the ` +
|
|
794
|
+
'measured settle wait. This root now reports readiness as measured.',
|
|
795
|
+
'ingest',
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
recordRootReadiness({
|
|
800
|
+
rootId: game.id,
|
|
801
|
+
mechanism: readinessStood === 'declared' ? 'contract-ready' : 'measured-wait',
|
|
802
|
+
source: readinessStood,
|
|
803
|
+
state: 'ready',
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
recordMountCoverage({
|
|
807
|
+
reach: measureAdapterReach({ authoring: adapter }),
|
|
808
|
+
reachMechanism: CAPTURE_MECHANISM,
|
|
809
|
+
// The captured Application's real ticker IS the control the play surface
|
|
810
|
+
// above drives — but naming a mechanism is not measuring it, and this used
|
|
811
|
+
// to emit the bare word `'gated'` on the strength of the sentence below.
|
|
812
|
+
// That word is also the manifest's declared intent vocabulary, so the
|
|
813
|
+
// coverage report printed `loop ✓ gated` about a capability nothing had
|
|
814
|
+
// probed: stopping the ticker stops what Pixi drives, and says nothing
|
|
815
|
+
// about a game that simulates on its own `setInterval` — the exact split
|
|
816
|
+
// `same-realm-loop-gate.ts`'s header was written about. No verdict until
|
|
817
|
+
// `verifySameRealmLoopControl` runs on this route.
|
|
818
|
+
loop: () => ({
|
|
819
|
+
loop: null,
|
|
820
|
+
reason:
|
|
821
|
+
'the host stops and starts the captured PixiJS Application ticker directly, but no ' +
|
|
822
|
+
'probe has measured whether that holds everything this game schedules',
|
|
823
|
+
}),
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
editorConsole.log(
|
|
827
|
+
`pixi ingest: captured "${game.name}" (${stats.count} nodes, ${stats.sprites} sprites)`,
|
|
828
|
+
'ingest',
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
publishIngestHook('__vgaiIngest2D', {
|
|
832
|
+
gameId: sessionId,
|
|
833
|
+
...ingestHookEvidence(adapter),
|
|
834
|
+
drawCount: () => mount.capture.getDrawCount(),
|
|
835
|
+
// Live, for the same reason as the three.js hook: a pixi game keeps
|
|
836
|
+
// adding display objects after its first captured frame (the settle loop
|
|
837
|
+
// right before this only waits for the stage to become NON-EMPTY), so a
|
|
838
|
+
// mount-time snapshot goes stale immediately.
|
|
839
|
+
get reflected() {
|
|
840
|
+
return rawAuthoring.refresh();
|
|
841
|
+
},
|
|
842
|
+
setPaused: lifecycle.setPaused,
|
|
843
|
+
// D-A3: the MEASURED editor reach of a headless-verifiable healthy mount.
|
|
844
|
+
reach: measureAdapterReach({ authoring: adapter }),
|
|
845
|
+
adapter,
|
|
846
|
+
store,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Mount an already-resolved {@link IngestGame2D} descriptor — the canvas
|
|
852
|
+
* sibling of `mountThreeIngestRoot`. `sessionId` is always the world id — the
|
|
853
|
+
* id this session is known by in logs and the dev hook. Its edits are written
|
|
854
|
+
* into the game's own source at the creation site of the edited object; an edit
|
|
855
|
+
* that cannot be honestly anchored there stays live-only AND SAYS WHY — never a
|
|
856
|
+
* sidecar, never an overlay document.
|
|
857
|
+
*
|
|
858
|
+
* Serialized through the shared entry queue; see `active-ingest.ts`'s
|
|
859
|
+
* `serializeEntry`.
|
|
860
|
+
*/
|
|
861
|
+
export function mountCanvasIngestRoot(
|
|
862
|
+
store: EditorShellStore,
|
|
863
|
+
game: IngestGame2D,
|
|
864
|
+
sessionId: string,
|
|
865
|
+
): Promise<void> {
|
|
866
|
+
return serializeEntry(() => mountCanvasIngestRootInner(store, game, sessionId));
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Mount straight from a manifest's canvas-surface `{ ingest }` root — the
|
|
871
|
+
* canvas sibling of `mountThreeIngestRootFromManifest`. Builds the descriptor
|
|
872
|
+
* via `resolveIngest2DDescriptor` (ingest/resolve-canvas.ts) and mounts it exactly
|
|
873
|
+
* like any other {@link IngestGame2D}.
|
|
874
|
+
*/
|
|
875
|
+
export async function mountCanvasIngestRootFromManifest(
|
|
876
|
+
store: EditorShellStore,
|
|
877
|
+
world: ResolvedAdapterRoot,
|
|
878
|
+
projectRoot: string,
|
|
879
|
+
): Promise<void> {
|
|
880
|
+
const game = resolveIngest2DDescriptor(world, projectRoot);
|
|
881
|
+
return mountCanvasIngestRoot(store, game, world.id);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Mount an in-tree pixi fixture game by its discovered id
|
|
886
|
+
* (`ingest/surface-canvas.ts`'s glob discovery is the lookup). Canvas sibling
|
|
887
|
+
* of `mountThreeIngestRootById`.
|
|
888
|
+
*/
|
|
889
|
+
export async function mountCanvasIngestRootById(
|
|
890
|
+
store: EditorShellStore,
|
|
891
|
+
gameId: string,
|
|
892
|
+
): Promise<void> {
|
|
893
|
+
const game = getIngestGame2D(gameId);
|
|
894
|
+
if (!game) {
|
|
895
|
+
editorConsole.error(`Unknown pixi ingest game: ${gameId}`, 'ingest');
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
return mountCanvasIngestRoot(store, game, gameId);
|
|
899
|
+
}
|