@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,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite sibling mounts — D-V2/D-V3/D-V4/D-V7 (residual), extended by D-B1/
|
|
3
|
+
* D-B3 (Slice B): when a manifest declares exactly
|
|
4
|
+
* one `{ ingest }` world alongside OTHER roots ("siblings"),
|
|
5
|
+
* `ingest/mount-ingest-root.ts`'s composite routes mount the ingest world through their
|
|
6
|
+
* existing enter path and then call {@link mountIngestSiblings} here to mount
|
|
7
|
+
* every OTHER world in manifest order (already z-sorted by the loader),
|
|
8
|
+
* degrading loudly per world rather than silently ignoring them (the F24/F9
|
|
9
|
+
* anti-pattern this closes):
|
|
10
|
+
*
|
|
11
|
+
* - a builtin `dom` world — a bare (no `<WorldProvider>`) DOM layer, D-V3.
|
|
12
|
+
* There is no native `Game` beside an ingest world, so wrapping one would
|
|
13
|
+
* fabricate first-party data (anti-shim) — the sibling's own `useWorldState`/
|
|
14
|
+
* `useGame` calls are simply unavailable, and this says so once per world.
|
|
15
|
+
* Has no loop at all, so its `SiblingMount.setPaused` stays `undefined`
|
|
16
|
+
* (D-B1) — never a fabricated no-op.
|
|
17
|
+
* - `{ module }` (any kind) OR a builtin `three`/`canvas` world
|
|
18
|
+
* (D-B3 — the residual this closes: a second `{ ingest }` world is
|
|
19
|
+
* structurally unreachable here, D-V2 already fails the whole route
|
|
20
|
+
* before any sibling loop runs, so `resolveAllRoots`'s one-element call
|
|
21
|
+
* below never hits its `MULTI_WORLD_ALLOWED_IDENTITIES` guard, which only
|
|
22
|
+
* applies for `roots.length > 1`) — a SCOPED single-world runtime, D-V4:
|
|
23
|
+
* the exact resolve+mount `module-mode.ts`'s `mountModuleRootRuntime`
|
|
24
|
+
* proves out, reused via `mountModuleRootRuntime`, but with NONE of that
|
|
25
|
+
* route's global side effects (no `setActiveAuthoring`/`setActiveSystems`,
|
|
26
|
+
* no viewport-tab switch, no `__vgaiModule` hook, no play-surface writes).
|
|
27
|
+
* Its `SiblingMount.setPaused` (D-B1) is wired straight from the scoped
|
|
28
|
+
* `GameSession.pause()`/`.resume()` `mountModuleRootRuntime` hands back —
|
|
29
|
+
* the SAME honest, already-existing capability `play-mode.ts`'s own
|
|
30
|
+
* `pausePlayMode`/`resumePlayMode` call on the PRIMARY route's session
|
|
31
|
+
* (`create-runtime.ts`'s `Game.play.pause()`/`.resume()`, gated per
|
|
32
|
+
* `pausable` world) — never a fabricated pause.
|
|
33
|
+
* - anything else — `rejectUnmountableSibling`'s named, loud non-goal for a
|
|
34
|
+
* genuinely unroutable shape (there is none known today; every currently
|
|
35
|
+
* resolvable identity is handled above): a per-world mount-failure entry,
|
|
36
|
+
* never silence.
|
|
37
|
+
*
|
|
38
|
+
* A failing sibling NEVER unmounts the ingest world: each failure is caught
|
|
39
|
+
* individually and appended to the (D-V5) multi-entry mount-failure report;
|
|
40
|
+
* the loop continues to the next sibling. Every successfully-mounted
|
|
41
|
+
* sibling's handle is returned so `ingest/mount-ingest-root.ts` can store it on the
|
|
42
|
+
* unified `_active` slot, dispose it (reverse order) on exit, and fan pause/
|
|
43
|
+
* resume out to it (D-B2, `getIngestPlayControl()`).
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import {
|
|
47
|
+
addMountFailureReport,
|
|
48
|
+
formatMountFailureMessage,
|
|
49
|
+
} from '@volter/editor-core/authoring/mount-failure-report';
|
|
50
|
+
import { withStoreSelection } from '../host/authoring/mounted-authoring';
|
|
51
|
+
import { makeNoAuthoringAdapter } from '@volter/editor-core/authoring/no-authoring-adapter';
|
|
52
|
+
import { PixiAuthoringAdapter } from '../host/authoring/pixi-authoring-adapter';
|
|
53
|
+
import { createLiveCanvasWriteTarget } from '../host/authoring/pixi-live-write-target';
|
|
54
|
+
import { resolveCanvasPixiForEditor } from '../host/canvas-entry-runtime';
|
|
55
|
+
import { CrashNullBoundary } from '@volter/editor-core/crash-null-boundary';
|
|
56
|
+
import { editorConsole } from '@volter/editor-core/editor-console';
|
|
57
|
+
import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
|
|
58
|
+
import { authoringJournal } from '../host/history/json-history-resource';
|
|
59
|
+
import { activeRealmServices } from '../host/realm-services';
|
|
60
|
+
import { resolveReactAdapterRootComponent } from '../host/roots/react-root';
|
|
61
|
+
import { sourceWriteBackendIfPrimed } from '@volter/editor-core/ui-source/tier-source-write-backend';
|
|
62
|
+
import { DomAuthoringAdapter } from '../react/dom-authoring-adapter';
|
|
63
|
+
import { ReactRootAuthoringAdapter, walkOidTree } from '../react/react-world-authoring-adapter';
|
|
64
|
+
import { beginProjectMountEpoch } from '@volter/editor-sdk/session/project-module-url';
|
|
65
|
+
import { createPhysics2DRegistry } from '@volter/game-runtime/pixi/physics-registry';
|
|
66
|
+
import { createPhysicsAdapter2D } from '@volter/game-runtime/pixi/system-adapters';
|
|
67
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter';
|
|
68
|
+
import { declaredRoots } from '@volter/editor-project/adapter/manifest-interpreter';
|
|
69
|
+
import type { ResolvedAdapterRoot, ResolvedGameManifest } from '@volter/editor-project/manifest/load';
|
|
70
|
+
import { structuralThree } from '../three/authoring/three-authoring-adapter';
|
|
71
|
+
import { createElement } from 'react';
|
|
72
|
+
import { flushSync } from 'react-dom';
|
|
73
|
+
import { createRoot } from 'react-dom/client';
|
|
74
|
+
import { mountModuleRootRuntime } from './module-mode';
|
|
75
|
+
|
|
76
|
+
/** One mounted sibling world's handle — stored on `ingest/active-ingest.ts`'s unified
|
|
77
|
+
* `_active` slot and disposed (reverse order) by `exitActiveIngest()`. */
|
|
78
|
+
export interface SiblingMount {
|
|
79
|
+
readonly worldId: string;
|
|
80
|
+
/**
|
|
81
|
+
* D-B1 (Slice B): an OPTIONAL, honest pause capability — present only when
|
|
82
|
+
* this sibling's own mounted session exposes a REAL pause (today: every
|
|
83
|
+
* `mountScopedRuntimeSibling` kind, via its scoped
|
|
84
|
+
* `GameSession.pause()`/`.resume()`). Absent (never a fake no-op) for a
|
|
85
|
+
* kind with no loop to gate at all — the bare `default-react` sibling.
|
|
86
|
+
* `getIngestPlayControl()` (`ingest/mount-ingest-root.ts`, D-B2) calls this
|
|
87
|
+
* `?.`-optionally after its own primary-surface call, mirroring the
|
|
88
|
+
* reverse-order dispose fan-out below.
|
|
89
|
+
*/
|
|
90
|
+
setPaused?(paused: boolean): void;
|
|
91
|
+
/**
|
|
92
|
+
* An OPTIONAL, HONEST authoring surface for this sibling — present iff `store`
|
|
93
|
+
* was supplied to {@link mountIngestSiblings} (every production caller
|
|
94
|
+
* supplies one; the parameter stays optional only so the one pre-existing test
|
|
95
|
+
* that calls this module directly without a store keeps compiling/running
|
|
96
|
+
* unchanged). When present, it is NEVER a fabricated stand-in:
|
|
97
|
+
* - a scoped-runtime sibling (`{ module }`, or a builtin
|
|
98
|
+
* `three`/`canvas` world) gets its OWN mounted
|
|
99
|
+
* `AuthoringAdapter` (`mounted.authoring`), store-selection-wrapped
|
|
100
|
+
* (`withStoreSelection`) — or, when it exposes none, the SAME named
|
|
101
|
+
* no-authoring floor the primary module route uses
|
|
102
|
+
* (`makeNoAuthoringAdapter`), so the world still appears in the hierarchy
|
|
103
|
+
* as a group with its honest label, never silently omitted.
|
|
104
|
+
* - a bare `default-react` sibling gets a {@link ReactRootAuthoringAdapter}
|
|
105
|
+
* over its own layer DOM root — an un-instrumented entry (no
|
|
106
|
+
* `data-oid`) yields an EMPTY subtree under its group node, never
|
|
107
|
+
* fabricated nodes.
|
|
108
|
+
* `ingest/mount-ingest-root.ts`'s composite routes promote to a `CompositeAuthoringAdapter`
|
|
109
|
+
* over the primary + every sibling's `authoring` once any sibling mounts
|
|
110
|
+
* (D-E2); a `SiblingMount` with no `authoring` (store omitted) simply
|
|
111
|
+
* cannot participate in that promotion.
|
|
112
|
+
*/
|
|
113
|
+
readonly authoring?: AuthoringAdapter;
|
|
114
|
+
dispose(): void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Position/size/z-order every sibling layer shares — same absolute-fill
|
|
118
|
+
* convention `create-runtime.ts`'s own react-world DOM layer uses
|
|
119
|
+
* (`mountOneReactRoot`), so a sibling stacks exactly like a native world
|
|
120
|
+
* would have. `pointerEvents` differs per sibling kind below (module roots
|
|
121
|
+
* own real canvases that need real hit-testing; the react sibling's layer
|
|
122
|
+
* stays `none` by the same HUD-overlay convention `mountOneReactRoot` uses,
|
|
123
|
+
* its rendered content opts back in per-element). */
|
|
124
|
+
function styleSiblingLayer(
|
|
125
|
+
layer: HTMLElement,
|
|
126
|
+
world: ResolvedAdapterRoot,
|
|
127
|
+
pointerEvents: string,
|
|
128
|
+
): void {
|
|
129
|
+
layer.style.position = 'absolute';
|
|
130
|
+
layer.style.top = '0';
|
|
131
|
+
layer.style.left = '0';
|
|
132
|
+
layer.style.width = '100%';
|
|
133
|
+
layer.style.height = '100%';
|
|
134
|
+
layer.style.zIndex = String(world.zOrder ?? 0);
|
|
135
|
+
layer.style.pointerEvents = pointerEvents;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* D-V3: mount a `default-react` sibling as a bare DOM layer — no
|
|
140
|
+
* `<WorldProvider>` wrap (anti-shim: there is no native `Game` to hand it).
|
|
141
|
+
* Reuses `binding-resolver.ts`'s exact entry-loading step
|
|
142
|
+
* (`resolveReactAdapterRootComponent` — the same D4 `scene`-forbidden/
|
|
143
|
+
* `entry`-required checks and default-export validation
|
|
144
|
+
* `resolveDefaultReactAdapter` itself uses) rather than duplicating it.
|
|
145
|
+
*
|
|
146
|
+
* If the entry component throws at mount/first render (most commonly: it
|
|
147
|
+
* calls `useGame`/`useWorldState` with no provider in the tree), the error
|
|
148
|
+
* boundary above catches it, this function removes the layer it just
|
|
149
|
+
* created, and RETHROWS so the caller's uniform per-sibling catch
|
|
150
|
+
* (`mountOneIngestSibling`'s caller, {@link mountIngestSiblings}) can turn it
|
|
151
|
+
* into a named mount-failure-report entry for THIS world, exactly like any
|
|
152
|
+
* other sibling failure — the ingest world stays mounted regardless.
|
|
153
|
+
*/
|
|
154
|
+
async function mountDefaultReactSibling(
|
|
155
|
+
world: ResolvedAdapterRoot,
|
|
156
|
+
gameContainer: HTMLElement,
|
|
157
|
+
projectRoot: string,
|
|
158
|
+
store: EditorShellStore | undefined,
|
|
159
|
+
): Promise<SiblingMount> {
|
|
160
|
+
// A sibling react layer is mounted on its own, outside a composition
|
|
161
|
+
// resolve, so it opens its OWN mount epoch — it shares no project module
|
|
162
|
+
// graph with anything else by construction.
|
|
163
|
+
const realm = await activeRealmServices(projectRoot, beginProjectMountEpoch());
|
|
164
|
+
const Entry = await resolveReactAdapterRootComponent(world, realm);
|
|
165
|
+
|
|
166
|
+
const layer = document.createElement('div');
|
|
167
|
+
layer.dataset['vgaiRootSurface'] = 'true';
|
|
168
|
+
styleSiblingLayer(layer, world, 'none');
|
|
169
|
+
gameContainer.appendChild(layer);
|
|
170
|
+
|
|
171
|
+
// D-V3: one structured note per world naming the degrade — never silent
|
|
172
|
+
// about the missing bridge.
|
|
173
|
+
editorConsole.log(
|
|
174
|
+
`react sibling "${world.id}": no native Game beside an ingest world — useWorldState unavailable`,
|
|
175
|
+
'ingest',
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const root = createRoot(layer);
|
|
179
|
+
let caught: unknown = null;
|
|
180
|
+
// `flushSync` forces the initial mount (render + commit + error-boundary
|
|
181
|
+
// recovery, if the entry throws) to complete SYNCHRONOUSLY, so `caught` is
|
|
182
|
+
// reliably populated by the time this call returns — react-dom's default
|
|
183
|
+
// scheduler does not guarantee that for a bare `root.render()` call (a
|
|
184
|
+
// production API, not a test-only affordance: this host needs the throw/
|
|
185
|
+
// no-throw outcome immediately, to decide whether to keep the layer or
|
|
186
|
+
// tear it down and report a named failure).
|
|
187
|
+
flushSync(() => {
|
|
188
|
+
root.render(
|
|
189
|
+
createElement(
|
|
190
|
+
CrashNullBoundary,
|
|
191
|
+
{
|
|
192
|
+
onCaught: (error: unknown) => {
|
|
193
|
+
caught = error;
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
createElement(Entry),
|
|
197
|
+
),
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (caught !== null) {
|
|
202
|
+
root.unmount();
|
|
203
|
+
layer.remove();
|
|
204
|
+
throw caught instanceof Error ? caught : new Error(String(caught));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Once mounted, probe the REAL committed layer with `walkOidTree` — a
|
|
208
|
+
// partially-stamped tree counts as stamped (the walk's transparent-skip
|
|
209
|
+
// contract already handles instrumentation gaps), so this is a single "any
|
|
210
|
+
// node at all?" check, not a completeness one. A non-empty tree keeps the
|
|
211
|
+
// exact D-E1 construction below (a STAMPED first-party entry — the
|
|
212
|
+
// sanctioned JSX write-back seam). A genuinely EMPTY tree (no `data-oid`
|
|
213
|
+
// reachable at all — e.g. an ingest-REACT sibling swept into the D-Y4
|
|
214
|
+
// foreign-game exclusion, or an entry that rendered nothing yet) falls back
|
|
215
|
+
// to `DomAuthoringAdapter`: a LIVE-ONLY surface over the live DOM, no
|
|
216
|
+
// `SourceWriteBackend` of any kind — never a write seam over source the
|
|
217
|
+
// stamping pipeline declined to instrument. Only computed when a store was
|
|
218
|
+
// supplied (see `SiblingMount.authoring`'s doc comment) — never fabricated.
|
|
219
|
+
let authoring: ReactRootAuthoringAdapter | DomAuthoringAdapter | undefined;
|
|
220
|
+
if (store) {
|
|
221
|
+
const stamped = walkOidTree(layer).nodes.size > 0;
|
|
222
|
+
if (stamped) {
|
|
223
|
+
// D-E1: the exact play-mode react-branch construction (`play-mode.ts`
|
|
224
|
+
// `installMultiRootAuthoring` :341-347) over THIS sibling's own layer
|
|
225
|
+
// DOM root — no `<WorldProvider>`/`DomHostContext` required. Whether the
|
|
226
|
+
// recorder exists is what the HOST serves, never how the editor shell was
|
|
227
|
+
// built — `ui-source/tier-source-write-backend.ts`, and the packaged-
|
|
228
|
+
// editor bug the old `import.meta.env.DEV` gate carried here.
|
|
229
|
+
const writeBackend = sourceWriteBackendIfPrimed('An ingest sibling’s react layer');
|
|
230
|
+
authoring = new ReactRootAuthoringAdapter(layer, store, {
|
|
231
|
+
...(writeBackend ? { writeBackend } : {}),
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
authoring = new DomAuthoringAdapter(layer, store, {
|
|
235
|
+
journal: authoringJournal(world.id),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
worldId: world.id,
|
|
242
|
+
...(authoring ? { authoring } : {}),
|
|
243
|
+
dispose(): void {
|
|
244
|
+
root.unmount();
|
|
245
|
+
layer.remove();
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* D-V4/D-B3: mount a `{ module }` sibling (any kind) OR a native
|
|
252
|
+
* builtin `three`/`canvas` sibling through the SCOPED single-world
|
|
253
|
+
* runtime `module-mode.ts`'s `mountModuleRootRuntime` provides — the exact
|
|
254
|
+
* resolve+mount the primary module route performs, reused here with none
|
|
255
|
+
* of that route's global side effects: no `setActiveAuthoring`/
|
|
256
|
+
* `setActiveSystems`, no viewport-tab switch, no `__vgaiModule` hook, no
|
|
257
|
+
* mount-failure-report clear. Mounts into its OWN layer div (never straight
|
|
258
|
+
* into the shared `gameContainer` the way the primary/single-world routes
|
|
259
|
+
* do) so its internal canvas/DOM stacking never collides with the ingest
|
|
260
|
+
* world's own canvas/DOM layer already in that container.
|
|
261
|
+
*
|
|
262
|
+
* `mountModuleRootRuntime` is kind-agnostic BY CONSTRUCTION here, not just
|
|
263
|
+
* for `{ module }` adapters: it calls `resolveAllRoots` with a ONE-element
|
|
264
|
+
* `roots` array, and `resolveAllRoots`'s `MULTI_WORLD_ALLOWED_IDENTITIES`
|
|
265
|
+
* scope guard only fires for `roots.length > 1` — so `resolveRootBinding`'s
|
|
266
|
+
* full identity switch (including `three`/`canvas`) is reached
|
|
267
|
+
* directly, unmodified, with the guard never consulted (D-B3's ground-truth
|
|
268
|
+
* finding; `MULTI_WORLD_ALLOWED_IDENTITIES` itself stays byte-identical).
|
|
269
|
+
*
|
|
270
|
+
* D-B1: the returned `SiblingMount.setPaused` calls the scoped
|
|
271
|
+
* `GameSession.pause()`/`.resume()` `mountModuleRootRuntime` hands back —
|
|
272
|
+
* the SAME honest, already-existing capability `play-mode.ts`'s own
|
|
273
|
+
* `pausePlayMode`/`resumePlayMode` call on the PRIMARY route's session
|
|
274
|
+
* (`create-runtime.ts`'s `Game.play.pause()`/`.resume()`, gated per
|
|
275
|
+
* `pausable` world within this scoped, single-world `Game`) — never a
|
|
276
|
+
* fabricated no-op.
|
|
277
|
+
*
|
|
278
|
+
* D-G1 (closing the residual this doc comment used to name): the
|
|
279
|
+
* mounted world's authoring surface converges with play-mode's own
|
|
280
|
+
* multi-world construction (`play-mode.ts`'s `installMultiRootAuthoring`,
|
|
281
|
+
* verbatim per branch):
|
|
282
|
+
* 1. `mounted.authoring` present (a `{ module }` adapter that ships its own
|
|
283
|
+
* surface, with its own persistence if any) — kept byte-for-byte,
|
|
284
|
+
* store-selection-wrapped, exactly as before this slice. NEVER wrapped
|
|
285
|
+
* in an editor-owned adapter: the module's own choice wins.
|
|
286
|
+
* 2. Else `worldInstance.kind === 'three'` (a native `default-three`
|
|
287
|
+
* sibling, or a `{ module }` sibling that mounted a three world with no
|
|
288
|
+
* authoring of its own) — a real three authoring adapter over
|
|
289
|
+
* `worldInstance.threeScene()` with structural identity and creation-site
|
|
290
|
+
* persistence (`structuralThree`), whose edits are LIVE-ONLY unless
|
|
291
|
+
* every consent gate is open.
|
|
292
|
+
* 3. Else `kind === 'canvas'` — the canvas analog through
|
|
293
|
+
* `PixiAuthoringAdapter`, physics wired via
|
|
294
|
+
* `createPhysicsAdapter2D(worldInstance.physics2d ??
|
|
295
|
+
* createPhysics2DRegistry())` exactly like play-mode's own branch.
|
|
296
|
+
* 4. Else (no mounted.authoring, not a first-party-authorable kind) — the
|
|
297
|
+
* named `makeNoAuthoringAdapter` floor, unchanged.
|
|
298
|
+
* D-G3: `composite-authoring-adapter.ts`'s persistence routing (OR-dirty,
|
|
299
|
+
* per-child save, `' + '`-joined destinations) needs NO changes — it keys on
|
|
300
|
+
* `capabilities.persist`, and a live-only child simply never joins that set.
|
|
301
|
+
*/
|
|
302
|
+
async function mountScopedRuntimeSibling(
|
|
303
|
+
manifest: ResolvedGameManifest,
|
|
304
|
+
world: ResolvedAdapterRoot,
|
|
305
|
+
gameContainer: HTMLElement,
|
|
306
|
+
projectRoot: string,
|
|
307
|
+
store: EditorShellStore | undefined,
|
|
308
|
+
): Promise<SiblingMount> {
|
|
309
|
+
const layer = document.createElement('div');
|
|
310
|
+
styleSiblingLayer(layer, world, 'auto');
|
|
311
|
+
gameContainer.appendChild(layer);
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
const w = gameContainer.clientWidth || 800;
|
|
315
|
+
const h = gameContainer.clientHeight || 600;
|
|
316
|
+
const { session } = await mountModuleRootRuntime(manifest, world, projectRoot, layer, w, h);
|
|
317
|
+
// D-G1: `session.game.roots[0]` is the SAME `RootInstance` shape
|
|
318
|
+
// play-mode iterates (`kind`, `threeScene()`, `canvasRoot()`, `physics2d`) —
|
|
319
|
+
// a single-element `resolveAllRoots` call always registers exactly one.
|
|
320
|
+
const worldInstance = session.game.roots[0]!;
|
|
321
|
+
const mountedAuthoring = worldInstance.mounted.authoring;
|
|
322
|
+
let authoring: AuthoringAdapter | undefined;
|
|
323
|
+
if (store) {
|
|
324
|
+
if (mountedAuthoring) {
|
|
325
|
+
// D-E1 (byte-stable): the module's own surface (and its own
|
|
326
|
+
// persistence, if any) wins — never wrap an editor-owned adapter
|
|
327
|
+
// around it.
|
|
328
|
+
authoring = withStoreSelection(mountedAuthoring, store);
|
|
329
|
+
} else if (worldInstance.kind === 'three') {
|
|
330
|
+
// `frameControl: 'host'` from this mount's own tick-ownership fact:
|
|
331
|
+
// the sibling's scoped `GameSession` drives the frame and this
|
|
332
|
+
// adapter is handed no `loop` handle that could stop it, so a
|
|
333
|
+
// mid-session gizmo edit must refuse rather than be overwritten on
|
|
334
|
+
// the next frame (omitting it fails OPEN — see
|
|
335
|
+
// `ThreeAuthoringOptions.frameControl`). The session's own
|
|
336
|
+
// `pause()`/`resume()` is deliberately NOT wired in as `loop`: it is
|
|
337
|
+
// gated per-world by `pausable`, so a `pausable: false` sibling would
|
|
338
|
+
// silently be back to the same overwritten edit.
|
|
339
|
+
authoring = structuralThree(store, worldInstance.threeScene(), {
|
|
340
|
+
frameControl: 'host',
|
|
341
|
+
journal: authoringJournal(world.id),
|
|
342
|
+
});
|
|
343
|
+
} else if (worldInstance.kind === 'canvas') {
|
|
344
|
+
if (
|
|
345
|
+
worldInstance.mounted.kind !== 'canvas' ||
|
|
346
|
+
worldInstance.mounted.substrate.name !== 'pixi'
|
|
347
|
+
) {
|
|
348
|
+
authoring =
|
|
349
|
+
worldInstance.mounted.authoring ??
|
|
350
|
+
makeNoAuthoringAdapter(
|
|
351
|
+
store,
|
|
352
|
+
`${world.id} (${worldInstance.mounted.kind === 'canvas' ? worldInstance.mounted.substrate.name : 'unknown'} authoring unavailable)`,
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
const physics = createPhysicsAdapter2D(
|
|
356
|
+
worldInstance.physics2d ?? createPhysics2DRegistry(),
|
|
357
|
+
);
|
|
358
|
+
authoring = new PixiAuthoringAdapter(worldInstance.canvasRoot(), store, {
|
|
359
|
+
// A module root's own source mounts this stage, so it is the
|
|
360
|
+
// project graph's under the packaged runtime — see
|
|
361
|
+
// `../vite-plugin-module-doorways.ts`.
|
|
362
|
+
pixi: await resolveCanvasPixiForEditor(),
|
|
363
|
+
target: createLiveCanvasWriteTarget({ physics }),
|
|
364
|
+
journal: authoringJournal(world.id),
|
|
365
|
+
// The sibling's own layer IS its surface: `mountModuleRootRuntime`
|
|
366
|
+
// creates this world's canvas inside it, filling it exactly.
|
|
367
|
+
surface: () => layer,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
authoring = makeNoAuthoringAdapter(store, `${world.id} (no authoring surface)`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
worldId: world.id,
|
|
376
|
+
...(authoring ? { authoring } : {}),
|
|
377
|
+
setPaused(paused: boolean): void {
|
|
378
|
+
if (paused) session.pause();
|
|
379
|
+
else session.resume();
|
|
380
|
+
},
|
|
381
|
+
dispose(): void {
|
|
382
|
+
session.stop();
|
|
383
|
+
layer.remove();
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
} catch (err) {
|
|
387
|
+
layer.remove();
|
|
388
|
+
throw err;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* D-V7 (updated by D-B3): a sibling reaching here is neither a builtin `dom`
|
|
394
|
+
* world nor routable through the scoped runtime above (`{ module }` of any
|
|
395
|
+
* kind, or a builtin `three`/`canvas` world) — a genuinely unroutable shape.
|
|
396
|
+
* No such shape is known to exist today (every currently-resolvable adapter
|
|
397
|
+
* identity is handled above); this is a named, loud non-goal for a
|
|
398
|
+
* hypothetical future one, never silently ignored (the F24 anti-pattern) and
|
|
399
|
+
* never half-mounted. (A second `{ ingest }` world is structurally
|
|
400
|
+
* unreachable here — D-V2's route-level guard already fails the whole
|
|
401
|
+
* composite before any sibling loop runs.)
|
|
402
|
+
*/
|
|
403
|
+
function rejectUnmountableSibling(world: ResolvedAdapterRoot): never {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`sibling world "${world.id}" (kind: ${world.surface}, adapter identity: ` +
|
|
406
|
+
`"${world.adapter.identity}") has no known composite-sibling mount path — every ` +
|
|
407
|
+
'currently-resolvable identity (three/canvas/dom/module) is ' +
|
|
408
|
+
'routed above; this is reached only for a genuinely unroutable shape.',
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Which mount shape a sibling world takes, by adapter identity alone.
|
|
413
|
+
*
|
|
414
|
+
* Split out of {@link mountOneIngestSibling} so the routing TRUTH is
|
|
415
|
+
* checkable without mounting anything: the builtin `canvas` identity used to
|
|
416
|
+
* fall through to {@link rejectUnmountableSibling} while this module's own
|
|
417
|
+
* doc comment and that function's thrown message both named it as routed.
|
|
418
|
+
* There was no mechanism gap behind that — `mountScopedRuntimeSibling` calls
|
|
419
|
+
* `resolveAllRoots` with a ONE-element array, so `resolveRootBinding`'s full
|
|
420
|
+
* identity switch (including `canvas`) is reached unmodified, and the mount's
|
|
421
|
+
* own authoring branch already had a `kind === 'canvas'` case waiting for a
|
|
422
|
+
* world that could never arrive.
|
|
423
|
+
*
|
|
424
|
+
* `null` ⇒ genuinely unroutable (the caller rejects loudly). */
|
|
425
|
+
export function siblingMountRoute(
|
|
426
|
+
world: ResolvedAdapterRoot,
|
|
427
|
+
): 'scoped-runtime' | 'react-layer' | null {
|
|
428
|
+
if (world.adapter.type === 'module') return 'scoped-runtime';
|
|
429
|
+
if (world.adapter.identity === 'three' || world.adapter.identity === 'canvas') {
|
|
430
|
+
return 'scoped-runtime';
|
|
431
|
+
}
|
|
432
|
+
if (world.adapter.identity === 'dom') return 'react-layer';
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function mountOneIngestSibling(
|
|
437
|
+
manifest: ResolvedGameManifest,
|
|
438
|
+
world: ResolvedAdapterRoot,
|
|
439
|
+
gameContainer: HTMLElement,
|
|
440
|
+
projectRoot: string,
|
|
441
|
+
store: EditorShellStore | undefined,
|
|
442
|
+
): Promise<SiblingMount> {
|
|
443
|
+
switch (siblingMountRoute(world)) {
|
|
444
|
+
case 'scoped-runtime':
|
|
445
|
+
return mountScopedRuntimeSibling(manifest, world, gameContainer, projectRoot, store);
|
|
446
|
+
case 'react-layer':
|
|
447
|
+
return mountDefaultReactSibling(world, gameContainer, projectRoot, store);
|
|
448
|
+
default:
|
|
449
|
+
rejectUnmountableSibling(world);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* D-V2: mount every world in `manifest.roots` OTHER than the primary ingest
|
|
455
|
+
* world (`ingestRootId`), in manifest order (already z-sorted by the
|
|
456
|
+
* loader). A failing sibling never unmounts the ingest world or stops the
|
|
457
|
+
* loop — it is caught, reported (D-V5's multi-entry mount-failure report,
|
|
458
|
+
* deduped by worldId), and the loop continues to the next sibling.
|
|
459
|
+
*/
|
|
460
|
+
export async function mountIngestSiblings(
|
|
461
|
+
manifest: ResolvedGameManifest,
|
|
462
|
+
ingestRootId: string,
|
|
463
|
+
gameContainer: HTMLElement,
|
|
464
|
+
projectRoot: string,
|
|
465
|
+
// D-E1: optional ONLY so the one pre-existing direct caller
|
|
466
|
+
// (`ingest-composite-sibling-pause.test.ts`, which never inspects
|
|
467
|
+
// `SiblingMount.authoring`) keeps compiling/running unchanged — every
|
|
468
|
+
// production caller (`ingest/mount-ingest-root.ts`'s composite routes) supplies one.
|
|
469
|
+
store?: EditorShellStore,
|
|
470
|
+
): Promise<SiblingMount[]> {
|
|
471
|
+
const mounted: SiblingMount[] = [];
|
|
472
|
+
for (const world of declaredRoots(manifest)) {
|
|
473
|
+
if (world.id === ingestRootId) continue;
|
|
474
|
+
try {
|
|
475
|
+
mounted.push(await mountOneIngestSibling(manifest, world, gameContainer, projectRoot, store));
|
|
476
|
+
} catch (err) {
|
|
477
|
+
editorConsole.error(`Composite sibling "${world.id}" failed to mount: ${err}`, 'ingest');
|
|
478
|
+
addMountFailureReport({
|
|
479
|
+
worldId: world.id,
|
|
480
|
+
kind: world.surface,
|
|
481
|
+
identity: world.adapter.identity,
|
|
482
|
+
message: formatMountFailureMessage(err),
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return mounted;
|
|
487
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The INGEST LANE's status facet — what `session.reportFacet('ingest')`
|
|
3
|
+
* publishes about a mounted unmodified game (`live-ingest-facet.ts` builds it).
|
|
4
|
+
*
|
|
5
|
+
* The derivation of the SESSION's reported play state is NOT here: it names no
|
|
6
|
+
* lane and is the host's (`@editor/reported-play-state.ts`, asked of the
|
|
7
|
+
* live-session registry). This module is only the lane's own description of
|
|
8
|
+
* what it mounted.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { CapabilityCoverageReport } from '../host/coverage/capability-coverage';
|
|
12
|
+
import type { MeasuredLoop } from '../host/same-realm-loop-gate';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The live ingest session, as the control API reports it. Every field is read
|
|
16
|
+
* from the session itself — never inferred from the manifest or the route.
|
|
17
|
+
*
|
|
18
|
+
* The facet's mere PRESENCE is the mount truth: `ingest/active-ingest.ts` assigns its
|
|
19
|
+
* `_active` slot only after a mount actually succeeded, and clears it on every
|
|
20
|
+
* teardown and every failure path. There is deliberately no `mounted: true`
|
|
21
|
+
* field — a field that can never be false is not evidence.
|
|
22
|
+
*/
|
|
23
|
+
export interface IngestStatusFacet {
|
|
24
|
+
/** The mounted world's manifest id. */
|
|
25
|
+
readonly worldId: string;
|
|
26
|
+
/** The session's render substrate (`ingest/active-ingest.ts`'s `IngestKind`). */
|
|
27
|
+
readonly kind: string;
|
|
28
|
+
/** `IngestPlayControl.playing` — whether the game's loop was last told to run. */
|
|
29
|
+
readonly playing: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The MEASURED loop verdict WITH its evidence
|
|
32
|
+
* (`same-realm-loop-gate.ts`'s `MeasuredLoop`), or `null` when no probe
|
|
33
|
+
* has run yet.
|
|
34
|
+
*
|
|
35
|
+
* Never inferred from the manifest — and now structurally unable to be: the
|
|
36
|
+
* manifest declares the SAME two words as an author's intent
|
|
37
|
+
* (`@volter/editor-project/manifest/schema`'s `loop`), so a bare string here left a
|
|
38
|
+
* declaration and a measurement indistinguishable to every reader of this
|
|
39
|
+
* facet. Evidence is producible only by the probe.
|
|
40
|
+
*/
|
|
41
|
+
readonly loop: MeasuredLoop | null;
|
|
42
|
+
/** Why — the probe's own words, so a reader can see what was measured rather
|
|
43
|
+
* than take the one-word verdict on faith. */
|
|
44
|
+
readonly loopReason: string | null;
|
|
45
|
+
/** Whether a single-frame step is a real operation on this mount. */
|
|
46
|
+
readonly canStep: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* WHAT THE EDITOR CANNOT DO with this game, and the mechanism that would fix
|
|
49
|
+
* each gap — the derived seam report (`coverage/capability-coverage.ts`). Rides
|
|
50
|
+
* along on the facet so a CLI reader gets the same answer the editor console
|
|
51
|
+
* printed at mount, instead of "the editor just has less" arriving as silence
|
|
52
|
+
* in every door at once.
|
|
53
|
+
*
|
|
54
|
+
* Re-derived on every read (the loop verdict, the ownership answer and the
|
|
55
|
+
* registered adapters all move after mount), so it is never a stale claim.
|
|
56
|
+
*
|
|
57
|
+
* `null` means NOT MEASURED — the coverage inputs are recorded by the three
|
|
58
|
+
* mount paths, so a pixi/react ingest has no report yet rather than a
|
|
59
|
+
* fabricated clean one. Absence of a report is not a report of no gaps.
|
|
60
|
+
*/
|
|
61
|
+
readonly coverage: CapabilityCoverageReport | null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live session as the control API reports it — the assembly half of
|
|
3
|
+
* `ingest/ingest-status.ts`.
|
|
4
|
+
*
|
|
5
|
+
* It lives beside that module rather than inside it because `ingest-status.ts`
|
|
6
|
+
* is deliberately PURE (it derives a reported play state from facts a caller
|
|
7
|
+
* hands it, and imports no session state, which is what makes its whole
|
|
8
|
+
* decision unit-testable with no browser). This half is the opposite by
|
|
9
|
+
* nature: it reads the live singletons.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { measuredLoop } from '../host/same-realm-loop-gate';
|
|
13
|
+
import { activeIngest } from './active-ingest';
|
|
14
|
+
import { getIngestPlayControl, ingestPlaying } from './ingest-play-control';
|
|
15
|
+
import type { IngestStatusFacet } from './ingest-status';
|
|
16
|
+
import { ingestCoverageReport } from './mount-coverage';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* S-1 (the SimCity ledger's false-alive status): the live ingest session as the
|
|
20
|
+
* control API reports it, or `null` when nothing is ingested.
|
|
21
|
+
*
|
|
22
|
+
* Read straight off the session slot, which is assigned only after a mount
|
|
23
|
+
* SUCCEEDS and cleared by every teardown and failure path — so a non-null facet
|
|
24
|
+
* is itself the mount evidence, and a failed mount can no longer present itself
|
|
25
|
+
* as a live one. `loop` is the MEASURED verdict or `null`; it is never inferred
|
|
26
|
+
* from the route.
|
|
27
|
+
*/
|
|
28
|
+
export function ingestStatusFacet(): IngestStatusFacet | null {
|
|
29
|
+
const active = activeIngest();
|
|
30
|
+
if (!active) return null;
|
|
31
|
+
// The MEASURED verdict. `null` means no verdict exists yet (the probe runs
|
|
32
|
+
// at pause), never "this route does not gate".
|
|
33
|
+
const mount = active.kind === 'three' ? active.session.mount : undefined;
|
|
34
|
+
const canvasLoop = active.kind === 'canvas' ? active.session.loop?.() : undefined;
|
|
35
|
+
const loop = measuredLoop(mount?.realmLoopVerdict?.()) ?? canvasLoop?.loop ?? null;
|
|
36
|
+
const loopReason = mount?.realmLoopVerdict?.()?.reason ?? canvasLoop?.reason ?? null;
|
|
37
|
+
return {
|
|
38
|
+
worldId: active.worldId,
|
|
39
|
+
kind: active.kind,
|
|
40
|
+
playing: ingestPlaying(),
|
|
41
|
+
loop,
|
|
42
|
+
loopReason,
|
|
43
|
+
canStep: getIngestPlayControl()?.canStep ?? false,
|
|
44
|
+
coverage: ingestCoverageReport(),
|
|
45
|
+
};
|
|
46
|
+
}
|