@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,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DEBUG-SEAM verbs of the session wire (`@volter/editor-sdk/commands`, a
|
|
3
|
+
* `workspace.command` contribution): reading a running game's declared state
|
|
4
|
+
* and commands, and actuating its input, time scale, seed and tick loop.
|
|
5
|
+
*
|
|
6
|
+
* All eight read/actuate the LIVE play session's game-scoped debug registry
|
|
7
|
+
* (`SystemAdapters.debug`, installed by `setActiveSystems` on play enter) or
|
|
8
|
+
* its InputManager/GameLoop (via `getPlayRuntimeAccess`). A stopped session
|
|
9
|
+
* answers with a structured failure, never a fabricated success; a running
|
|
10
|
+
* game that exposes NO debug adapter (a non-first-party mount) degrades to
|
|
11
|
+
* the honest empty/null shape.
|
|
12
|
+
*
|
|
13
|
+
* Play mode and the active-systems registry are the game skew's own modules,
|
|
14
|
+
* still housed in the editor until Play leaves the host (WORK.md §The
|
|
15
|
+
* workbench, G); they are reached through the `@editor/*` alias the editor's
|
|
16
|
+
* Vite serves to every contribution, and become this package's own imports
|
|
17
|
+
* when they move. Nothing here is host API.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
|
|
21
|
+
import type { CommandContribution } from '@volter/editor-sdk/commands';
|
|
22
|
+
import { notPlayingResult, structuredErrorResult } from '../src/command-results';
|
|
23
|
+
import { getPlayRuntimeAccess, isPlayModeActive } from '../src/play/play-mode';
|
|
24
|
+
|
|
25
|
+
export const point = 'workspace.command';
|
|
26
|
+
|
|
27
|
+
export const commands: CommandContribution['commands'] = {
|
|
28
|
+
'list-gameplay-state': {
|
|
29
|
+
derivedRefresh: 'if-content-changed',
|
|
30
|
+
handle: () => {
|
|
31
|
+
const debug = getActiveSystems().debug;
|
|
32
|
+
if (!debug) {
|
|
33
|
+
return isPlayModeActive() ? { ok: true, data: { providers: [] } } : notPlayingResult();
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, data: { providers: debug.providers() } };
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
'inspect-gameplay-state': {
|
|
39
|
+
derivedRefresh: 'if-content-changed',
|
|
40
|
+
handle: (cmd) => {
|
|
41
|
+
const debug = getActiveSystems().debug;
|
|
42
|
+
if (!debug) {
|
|
43
|
+
return isPlayModeActive() ? { ok: true, data: { state: null } } : notPlayingResult();
|
|
44
|
+
}
|
|
45
|
+
const keys = cmd['keys'] as string[] | undefined;
|
|
46
|
+
try {
|
|
47
|
+
const state: Record<string, unknown> = {};
|
|
48
|
+
if (keys && keys.length > 0) {
|
|
49
|
+
for (const key of keys) state[key] = debug.state(key);
|
|
50
|
+
} else {
|
|
51
|
+
Object.assign(state, debug.stateAll());
|
|
52
|
+
}
|
|
53
|
+
return { ok: true, data: { state } };
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return structuredErrorResult(err);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
'list-debug-commands': {
|
|
60
|
+
derivedRefresh: 'if-content-changed',
|
|
61
|
+
handle: () => {
|
|
62
|
+
const debug = getActiveSystems().debug;
|
|
63
|
+
if (!debug) {
|
|
64
|
+
return isPlayModeActive() ? { ok: true, data: { commands: [] } } : notPlayingResult();
|
|
65
|
+
}
|
|
66
|
+
return { ok: true, data: { commands: debug.commands() } };
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
'invoke-debug-command': {
|
|
70
|
+
derivedRefresh: 'always',
|
|
71
|
+
handle: async (cmd) => {
|
|
72
|
+
const debug = getActiveSystems().debug;
|
|
73
|
+
if (!debug) {
|
|
74
|
+
if (!isPlayModeActive()) return notPlayingResult();
|
|
75
|
+
// Honest not-registered shape: no adapter means NOTHING is registered.
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
error: `debug: no command registered under "${cmd['name']}" — this game exposes no debug adapter`,
|
|
79
|
+
data: { code: 'DEBUG_COMMAND_NOT_REGISTERED', registered: [] },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const result = await debug.invoke(cmd['name'] as string, (cmd['args'] as unknown[]) ?? []);
|
|
84
|
+
return { ok: true, data: { result } };
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return structuredErrorResult(err);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
'inject-input': {
|
|
91
|
+
derivedRefresh: 'none',
|
|
92
|
+
handle: (cmd) => {
|
|
93
|
+
const runtime = getPlayRuntimeAccess();
|
|
94
|
+
if (!runtime) {
|
|
95
|
+
return { ok: false, error: 'not in play mode — start play before injecting input' };
|
|
96
|
+
}
|
|
97
|
+
// D15/T-D15.5 (review objection 2's fix) — resolved through the SAME
|
|
98
|
+
// per-world `DebugRegistry.getVirtualInputTarget(worldId?)` the debug
|
|
99
|
+
// bridge's `window.__vgai.input.*` calls, instead of the old
|
|
100
|
+
// `Game.input` (always the DEFAULT world only) — an explicit
|
|
101
|
+
// `cmd['worldId']` reaches that world specifically; omitted resolves
|
|
102
|
+
// to the same default both doors now share. Throws
|
|
103
|
+
// `DEBUG_INPUT_WORLD_NOT_FOUND` (caught below) for an explicit,
|
|
104
|
+
// unregistered world id.
|
|
105
|
+
let input: ReturnType<typeof runtime.getInputTarget>;
|
|
106
|
+
try {
|
|
107
|
+
input = runtime.getInputTarget(cmd['worldId'] as string | undefined);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return structuredErrorResult(err);
|
|
110
|
+
}
|
|
111
|
+
if (!input) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: 'this play session has no first-party InputManager to inject into',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
// Action-level is primary (frozen decision, spec §3.2): the honest
|
|
119
|
+
// gated path — a gated actuation surfaces {delivered:false, reason},
|
|
120
|
+
// never a false ok. The four legacy kinds map to the persistent
|
|
121
|
+
// test-source injectors (read back only through declared test_*
|
|
122
|
+
// bindings, so `delivered` here means "the source was written").
|
|
123
|
+
if (cmd['kind'] === 'action') {
|
|
124
|
+
const atTick = cmd['atTick'];
|
|
125
|
+
// D15/T-D15.5 — `atTick` defers to InputManager.scheduleActionAtTick
|
|
126
|
+
// instead of an immediate setVirtualAction. scheduleActionAtTick
|
|
127
|
+
// throws InputActionError/a valueType mismatch (same as
|
|
128
|
+
// setVirtualAction) or InputTickError ('TICK_ALREADY_PASSED',
|
|
129
|
+
// data.currentTick) — all caught by this handler's outer try/catch
|
|
130
|
+
// below via structuredErrorResult.
|
|
131
|
+
if (typeof atTick === 'number') {
|
|
132
|
+
input.scheduleActionAtTick(
|
|
133
|
+
atTick,
|
|
134
|
+
cmd['action'] as string,
|
|
135
|
+
cmd['value'] as boolean | number | { x: number; y: number },
|
|
136
|
+
);
|
|
137
|
+
return { ok: true, data: { scheduled: true, tick: atTick } };
|
|
138
|
+
}
|
|
139
|
+
const outcome = input.setVirtualAction(
|
|
140
|
+
cmd['action'] as string,
|
|
141
|
+
cmd['value'] as boolean | number | { x: number; y: number },
|
|
142
|
+
);
|
|
143
|
+
return { ok: true, data: outcome };
|
|
144
|
+
}
|
|
145
|
+
const sourceId = cmd['sourceId'] as string;
|
|
146
|
+
switch (cmd['kind']) {
|
|
147
|
+
case 'axis':
|
|
148
|
+
input.injectAxis(sourceId, cmd['value'] as number);
|
|
149
|
+
break;
|
|
150
|
+
case 'vector2':
|
|
151
|
+
input.injectVector2(sourceId, cmd['value'] as { x: number; y: number });
|
|
152
|
+
break;
|
|
153
|
+
case 'pointerDelta':
|
|
154
|
+
input.injectPointerDelta(sourceId, cmd['value'] as { x: number; y: number });
|
|
155
|
+
break;
|
|
156
|
+
case 'pointerPosition':
|
|
157
|
+
input.injectPointerPosition(sourceId, cmd['value'] as { x: number; y: number });
|
|
158
|
+
break;
|
|
159
|
+
default:
|
|
160
|
+
return { ok: false, error: `unknown injection kind "${cmd['kind']}"` };
|
|
161
|
+
}
|
|
162
|
+
return { ok: true, data: { delivered: true } };
|
|
163
|
+
} catch (err) {
|
|
164
|
+
return structuredErrorResult(err);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
'set-time-scale': {
|
|
169
|
+
derivedRefresh: 'none',
|
|
170
|
+
handle: (cmd) => {
|
|
171
|
+
const runtime = getPlayRuntimeAccess();
|
|
172
|
+
if (!runtime) {
|
|
173
|
+
return { ok: false, error: 'not in play mode — start play before setting time-scale' };
|
|
174
|
+
}
|
|
175
|
+
runtime.loop.timeScale = cmd['timeScale'] as number;
|
|
176
|
+
// Read back after the loop's own [0,8] clamp so the caller sees the
|
|
177
|
+
// value actually applied.
|
|
178
|
+
return { ok: true, data: { timeScale: runtime.loop.timeScale } };
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
// D15/T-D15.6 — the `play.seed.set` relay verb: reaches the live
|
|
182
|
+
// session's `ctx.random.reseed(seed)` through `getPlayRuntimeAccess()`'s
|
|
183
|
+
// `random` accessor. Gated on `determinismDeclared` (the running
|
|
184
|
+
// project's manifest `determinism.seededRandom`, per T4.1: reseeding a
|
|
185
|
+
// project with no documented determinism contract would silently imply
|
|
186
|
+
// one exists) — the structured `DETERMINISM_NOT_DECLARED` marker the SDK
|
|
187
|
+
// maps to its own declared error code.
|
|
188
|
+
'set-seed': {
|
|
189
|
+
derivedRefresh: 'none',
|
|
190
|
+
handle: (cmd) => {
|
|
191
|
+
const runtime = getPlayRuntimeAccess();
|
|
192
|
+
if (!runtime) {
|
|
193
|
+
return { ok: false, error: 'not in play mode — start play before setting the seed' };
|
|
194
|
+
}
|
|
195
|
+
if (!runtime.determinismDeclared) {
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
error:
|
|
199
|
+
"this project's manifest does not declare determinism.seededRandom — " +
|
|
200
|
+
'play.seed.set has no documented contract to reseed against (D15)',
|
|
201
|
+
data: { code: 'DETERMINISM_NOT_DECLARED' },
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (!runtime.random) {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
error: 'this play session has no first-party ctx.random to reseed',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
runtime.random.reseed(cmd['seed'] as number);
|
|
211
|
+
// Read back the live, post-reseed value (mirrors set-time-scale's own
|
|
212
|
+
// read-after-apply convention immediately above).
|
|
213
|
+
return { ok: true, data: { seed: runtime.random.seed } };
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
// D15/T-D15.4 — the `play.runTicks` relay verb: reaches the live
|
|
217
|
+
// session's `GameInternal.runTicks` through `getPlayRuntimeAccess()`'s
|
|
218
|
+
// `runTicks` accessor (itself backed by the SAME
|
|
219
|
+
// `DebugRegistry.getRunTicksTarget()` the bridge's
|
|
220
|
+
// `window.__vgai.runTicks` calls — one implementation, byte-identical
|
|
221
|
+
// behavior across doors, D17). `runTicks` itself throws a structured
|
|
222
|
+
// `DebugError` (`RUN_TICKS_PAUSED`) while paused — never a silent no-op —
|
|
223
|
+
// which `structuredErrorResult` below carries through as `data.code`.
|
|
224
|
+
'run-ticks': {
|
|
225
|
+
derivedRefresh: 'none',
|
|
226
|
+
handle: async (cmd) => {
|
|
227
|
+
const runtime = getPlayRuntimeAccess();
|
|
228
|
+
if (!runtime) {
|
|
229
|
+
return { ok: false, error: 'not in play mode — start play before running ticks' };
|
|
230
|
+
}
|
|
231
|
+
if (!runtime.runTicks) {
|
|
232
|
+
return {
|
|
233
|
+
ok: false,
|
|
234
|
+
error: 'this play session has no first-party Game to run ticks on',
|
|
235
|
+
data: { code: 'RUN_TICKS_UNAVAILABLE' },
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
const n = cmd['n'] as number;
|
|
240
|
+
const render = cmd['render'] as 'last' | 'all' | 'none' | undefined;
|
|
241
|
+
// The settled-aware driver (`runtime/run-ticks-settled.ts`): a tick never races a
|
|
242
|
+
// scene remount's async commit — byte-identical with the bridge's `runTicksSettled`
|
|
243
|
+
// door (D17). With no world-settled probe registered it is `runTicks` exactly.
|
|
244
|
+
if (runtime.runTicksSettled) {
|
|
245
|
+
await runtime.runTicksSettled(n, render ? { render } : undefined);
|
|
246
|
+
} else {
|
|
247
|
+
runtime.runTicks(n, render ? { render } : undefined);
|
|
248
|
+
}
|
|
249
|
+
return { ok: true };
|
|
250
|
+
} catch (err) {
|
|
251
|
+
return structuredErrorResult(err);
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE GENERATION ESTATE (`@volter/editor-sdk/services`, a `workspace.service`
|
|
3
|
+
* contribution): the paid-provider lane's editor surface — the Generations
|
|
4
|
+
* drawer gallery over the durable job ledger, the Create-generation document
|
|
5
|
+
* and the per-job result document.
|
|
6
|
+
*
|
|
7
|
+
* They were host modules (`generation-jobs.ts`, `components/GenerationActivity
|
|
8
|
+
* .tsx`, `components/generation-documents.tsx`, `components/generation-
|
|
9
|
+
* presentation.ts`), which put a metered service's ledger, its provider
|
|
10
|
+
* vocabulary and its billing strings inside a base library for making IDEs
|
|
11
|
+
* (WORK.md §The open-source launch, phase 1 unit 6). Nothing here is generic
|
|
12
|
+
* over document kinds or surfaces; every line is about one lane.
|
|
13
|
+
*
|
|
14
|
+
* TWO DOORS, both already the host's:
|
|
15
|
+
* - the drawer TAB is `registerWorkspaceUtility` — a `.utility` contribution
|
|
16
|
+
* would be the shorter spelling, but that point carries no attention BADGE
|
|
17
|
+
* and the tab's whole job is to say how many results are unseen, so the
|
|
18
|
+
* registry call is made here directly (the story documents' service does
|
|
19
|
+
* the same, for the same reason);
|
|
20
|
+
* - the two DOCUMENTS open through `document-open-registry.ts` under the
|
|
21
|
+
* `generation` VIEW ADDRESS — the kind `@volter/editor-sdk`'s `EditorView`
|
|
22
|
+
* already names — so `editor-view-presentation.ts` addresses a generation
|
|
23
|
+
* without importing one, and a build without this package answers `null`
|
|
24
|
+
* to the address instead of pretending.
|
|
25
|
+
*
|
|
26
|
+
* The SERVER half (`packages/editor/server/generation-jobs.ts`, the
|
|
27
|
+
* reconciler, `routes/generations.ts`) is still the editor's: there is no
|
|
28
|
+
* server-side service point yet (WORKBENCH.md §Contribution points, `service`
|
|
29
|
+
* — "the SERVER side … is still the editor's"). This module talks to those
|
|
30
|
+
* routes over HTTP exactly as it did when it lived in the host.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { registerDocumentOpener } from '@volter/editor-core/document-open-registry';
|
|
34
|
+
import { CONTRIBUTED_WORKSPACE_UTILITIES } from '@volter/editor-core/workspace-core-utilities';
|
|
35
|
+
import { registerWorkspaceUtility } from '@volter/editor-core/workspace-utility-registry';
|
|
36
|
+
import { GenerationActivity } from '../src/generation/GenerationActivity';
|
|
37
|
+
import {
|
|
38
|
+
openGenerationCreateDocument,
|
|
39
|
+
openGenerationDocument,
|
|
40
|
+
} from '../src/generation/generation-documents';
|
|
41
|
+
import {
|
|
42
|
+
generationJobsSnapshot,
|
|
43
|
+
generationJobsVersion,
|
|
44
|
+
generationUnreadCount,
|
|
45
|
+
refreshGenerationJobs,
|
|
46
|
+
startGenerationJobActivity,
|
|
47
|
+
subscribeGenerationJobs,
|
|
48
|
+
} from '../src/generation/generation-jobs';
|
|
49
|
+
|
|
50
|
+
export const point = 'workspace.service';
|
|
51
|
+
|
|
52
|
+
/** The `generation` view address (`EditorView['document']`, `@volter/editor-sdk`).
|
|
53
|
+
* `id` is a job id, or `create` for the submit document. */
|
|
54
|
+
const GENERATION_VIEW_ADDRESS = 'generation';
|
|
55
|
+
|
|
56
|
+
function generationsBadge() {
|
|
57
|
+
const count = generationUnreadCount(generationJobsSnapshot().jobs);
|
|
58
|
+
return count > 0 ? ({ count, severity: 'info' } as const) : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function start(): () => void {
|
|
62
|
+
const stops = [
|
|
63
|
+
// The ledger POLL, and its cadence, are this lane's own: it was
|
|
64
|
+
// `EditorContext.tsx`'s `useEffect`, so the host held a 2-second timer for
|
|
65
|
+
// a metered service. Deliberately NOT `session.onSample` — that door is the
|
|
66
|
+
// five-second VITALS tick for derivations of the session, and hanging a
|
|
67
|
+
// remote-ledger fetch on it would slow a visible result by 3s for no
|
|
68
|
+
// measured reason. The service owns the interval and ends it on stop.
|
|
69
|
+
startGenerationJobActivity(),
|
|
70
|
+
registerWorkspaceUtility({
|
|
71
|
+
...CONTRIBUTED_WORKSPACE_UTILITIES.generations,
|
|
72
|
+
order: 15,
|
|
73
|
+
Content: GenerationActivity,
|
|
74
|
+
badge: generationsBadge,
|
|
75
|
+
subscribeBadge: subscribeGenerationJobs,
|
|
76
|
+
badgeSnapshot: generationJobsVersion,
|
|
77
|
+
visibleByDefault: false,
|
|
78
|
+
closeable: true,
|
|
79
|
+
}),
|
|
80
|
+
registerDocumentOpener<{ readonly id: string }>({
|
|
81
|
+
id: GENERATION_VIEW_ADDRESS,
|
|
82
|
+
owner: '@volter/editor-game/generation',
|
|
83
|
+
open: (_store, request) => {
|
|
84
|
+
if (request.id === 'create') return openGenerationCreateDocument();
|
|
85
|
+
const job = generationJobsSnapshot().jobs.find((entry) => entry.id === request.id);
|
|
86
|
+
return job ? openGenerationDocument(job) : null;
|
|
87
|
+
},
|
|
88
|
+
// A job the page has not fetched yet is not a missing job: refresh the
|
|
89
|
+
// ledger once and the address resolves.
|
|
90
|
+
settle: refreshGenerationJobs,
|
|
91
|
+
}),
|
|
92
|
+
];
|
|
93
|
+
return () => {
|
|
94
|
+
for (const stop of stops) stop();
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATIONS in the status bar (`workspace.status`) — permanent access to the
|
|
3
|
+
* paid-provider lane's results, including when there are none yet.
|
|
4
|
+
*
|
|
5
|
+
* It was `status-contributions.tsx`'s `GenerationsStatus`, a host module, so
|
|
6
|
+
* the base host named the generation ledger to paint a pill. The pill is the
|
|
7
|
+
* same; what changed is who owns it (WORK.md §The open-source launch, phase 1
|
|
8
|
+
* unit 6 — the generation estate is a service lane, not a base library's
|
|
9
|
+
* concern).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { CONTRIBUTED_WORKSPACE_UTILITIES } from '@volter/editor-core/workspace-core-utilities';
|
|
13
|
+
import { editorHost } from '@volter/editor-sdk/host';
|
|
14
|
+
import { Button } from '@volter/editor-sdk/widgets';
|
|
15
|
+
import { useSyncExternalStore } from 'react';
|
|
16
|
+
import {
|
|
17
|
+
generationJobIsActive,
|
|
18
|
+
generationJobsSnapshot,
|
|
19
|
+
generationUnreadCount,
|
|
20
|
+
subscribeGenerationJobs,
|
|
21
|
+
} from '../src/generation/generation-jobs';
|
|
22
|
+
|
|
23
|
+
export const point = 'workspace.status';
|
|
24
|
+
export const title = 'Generations';
|
|
25
|
+
export const align = 'left';
|
|
26
|
+
export const order = 24;
|
|
27
|
+
|
|
28
|
+
export default function GenerationsStatus() {
|
|
29
|
+
const { jobs } = useSyncExternalStore(
|
|
30
|
+
subscribeGenerationJobs,
|
|
31
|
+
generationJobsSnapshot,
|
|
32
|
+
generationJobsSnapshot,
|
|
33
|
+
);
|
|
34
|
+
const unseen = generationUnreadCount(jobs);
|
|
35
|
+
const running = jobs.filter(generationJobIsActive).length;
|
|
36
|
+
return (
|
|
37
|
+
<Button
|
|
38
|
+
type="button"
|
|
39
|
+
variant="ghost"
|
|
40
|
+
size="compact"
|
|
41
|
+
className="vgai-status-action"
|
|
42
|
+
data-testid="status-generations"
|
|
43
|
+
aria-label={`Generations, ${unseen} unseen, ${running} running`}
|
|
44
|
+
title={`${unseen} unseen generation results, ${running} running — open Generations`}
|
|
45
|
+
onClick={() =>
|
|
46
|
+
editorHost().workspace.showUtility(CONTRIBUTED_WORKSPACE_UTILITIES.generations.id)
|
|
47
|
+
}
|
|
48
|
+
>
|
|
49
|
+
Generations · {unseen}
|
|
50
|
+
{running > 0 ? ` · ${running} running` : ''}
|
|
51
|
+
</Button>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE INGEST LANE, started (`@volter/editor-sdk/services`, a
|
|
3
|
+
* `workspace.service` contribution).
|
|
4
|
+
*
|
|
5
|
+
* Three jobs, and the order matters:
|
|
6
|
+
*
|
|
7
|
+
* 1. IMPORTING the lane's modules is what runs their registrations. A package
|
|
8
|
+
* module runs only when something imports it, and the lane's live-session
|
|
9
|
+
* registration, its `ingest`/`ingestCaptureWait` state facets and its
|
|
10
|
+
* `onShellStore` binding are module-load side effects of
|
|
11
|
+
* `mount-ingest-root.ts`. The static import below is therefore load-bearing
|
|
12
|
+
* — `vgai status` carries `ingest: null` / `ingestCaptureWait: null` before
|
|
13
|
+
* contribution pass. A product that composes no `@volter/editor-game` reports
|
|
14
|
+
* neither key, which is the honest state: no lane claims to mount
|
|
15
|
+
* unmodified games.
|
|
16
|
+
*
|
|
17
|
+
* 2. AUTO-LAUNCH on `project.onReady`. The two boot chains (`the world root's stage`,
|
|
18
|
+
* `NonThreeAuthoringBootstrap`) used to call `autoLaunchIngest(store)` by
|
|
19
|
+
* name; they now only run the project-ready hook, and this is its first
|
|
20
|
+
* registrant (WORK.md §P4). That hook is LATCHED per project, because a
|
|
21
|
+
* contribution pass can finish on either side of the boot chain;
|
|
22
|
+
* `autoLaunchIngest`'s own dedupe makes the second arrival a no-op. And
|
|
23
|
+
* `hosted-example.ts`'s auto-play decision still runs AFTER the hook, so
|
|
24
|
+
* `isIngestActive()` is answered by a mount this hook already made.
|
|
25
|
+
*
|
|
26
|
+
* 3. REGISTERING THIS LANE'S ADAPTER DECLARATIONS. A repo-vendored game does
|
|
27
|
+
* not ship its own `vgai.adapter.ts` — by the REALM rule its host-realm
|
|
28
|
+
* declaration lives in the in-tree registry (`@editor/ingest/registry`),
|
|
29
|
+
* which the host used to import directly and therefore carried in every
|
|
30
|
+
* editor boot, a `models` build that mounts no unmodified game included.
|
|
31
|
+
* The host now owns only the PRECEDENCE and asks a registered source
|
|
32
|
+
* (`@editor/project-adapter`'s `registerAdapterDefinitionSource`); this lane owns the table,
|
|
33
|
+
* because these are ITS declarations for content IT mounts. The host WAITS
|
|
34
|
+
* for this registration when a project's manifest declares an ingest root
|
|
35
|
+
* — a contract fact it reads before any contribution loads — so the
|
|
36
|
+
* registration arriving on the contribution pass is on time by
|
|
37
|
+
* construction, never a re-mount after a wrong one.
|
|
38
|
+
*
|
|
39
|
+
* The store comes from `@editor/shell-store-door`, the same arrival
|
|
40
|
+
* `mount-ingest-root.ts` binds itself on — read at launch time rather than
|
|
41
|
+
* captured, so neither ordering can leave the launch holding `null`.
|
|
42
|
+
*/
|
|
43
|
+
import { editorConsole } from '@volter/editor-core/editor-console';
|
|
44
|
+
import { importIngestAdapterModule, ingestAdapterModulePath } from '../src/host/ingest/registry';
|
|
45
|
+
import { registerAdapterDefinitionSource } from '@volter/editor-core/project-adapter';
|
|
46
|
+
import { shellStoreForHost } from '@volter/editor-core/shell-store-door';
|
|
47
|
+
import { editorHost } from '@volter/editor-sdk/host';
|
|
48
|
+
import { autoLaunchIngest } from '../src/ingest/mount-ingest-root';
|
|
49
|
+
|
|
50
|
+
export const point = 'workspace.service';
|
|
51
|
+
|
|
52
|
+
export function start(): () => void {
|
|
53
|
+
const stopSource = registerAdapterDefinitionSource({
|
|
54
|
+
id: 'in-tree-ingest-registry',
|
|
55
|
+
owner: '@volter/editor-game/contributions/ingest.service.ts',
|
|
56
|
+
modulePathFor: (rootId) => ingestAdapterModulePath(rootId),
|
|
57
|
+
// A module that exists and throws is left to throw: `defineAdapter`
|
|
58
|
+
// validates at evaluation time, and the host names that failure by path
|
|
59
|
+
// rather than swallowing it into the native default.
|
|
60
|
+
importModule: async (rootId) => await importIngestAdapterModule(rootId),
|
|
61
|
+
});
|
|
62
|
+
const stopReady = editorHost().project.onReady(async () => {
|
|
63
|
+
const store = shellStoreForHost();
|
|
64
|
+
if (!store) {
|
|
65
|
+
// The store arrives with `AppRoot`, long before any boot chain reaches
|
|
66
|
+
// ready, so this cannot happen quietly — say so rather than skipping an
|
|
67
|
+
// auto-launch the manifest asked for.
|
|
68
|
+
editorConsole.error(
|
|
69
|
+
'Ingest auto-launch skipped: the editor shell store had not arrived at project ready.',
|
|
70
|
+
'ingest',
|
|
71
|
+
);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
await autoLaunchIngest(store);
|
|
75
|
+
});
|
|
76
|
+
return () => {
|
|
77
|
+
stopReady();
|
|
78
|
+
stopSource();
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The INSTANCE verbs of the session wire (`@volter/editor-sdk/commands`, a
|
|
3
|
+
* `workspace.command` contribution): how many instances of the game play
|
|
4
|
+
* split-screen, and which exist.
|
|
5
|
+
*
|
|
6
|
+
* Play mode and the live instance registry are the game skew's own modules,
|
|
7
|
+
* still housed in the editor until Play leaves the host (WORK.md §The
|
|
8
|
+
* workbench, G); they are reached through the `@editor/*` alias the editor's
|
|
9
|
+
* Vite serves to every contribution, and become this package's own imports
|
|
10
|
+
* when they move. Nothing here is host API.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { liveInstanceIds } from '@volter/editor-core/authoring/active-systems';
|
|
14
|
+
import type { CommandContribution } from '@volter/editor-sdk/commands';
|
|
15
|
+
import {
|
|
16
|
+
instanceEntries,
|
|
17
|
+
isPlayModeActive,
|
|
18
|
+
setDesiredExtraInstances,
|
|
19
|
+
setDesiredInstanceNames,
|
|
20
|
+
} from '../src/play/play-mode';
|
|
21
|
+
|
|
22
|
+
export const point = 'workspace.command';
|
|
23
|
+
|
|
24
|
+
const notPlaying = () => ({
|
|
25
|
+
ok: false,
|
|
26
|
+
error: 'not in play mode — start play before using the debug seam',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const commands: CommandContribution['commands'] = {
|
|
30
|
+
// Multiplayer authoring: how many instances of this game to show
|
|
31
|
+
// split-screen (total, including the primary). The GamePanel reconciles by
|
|
32
|
+
// mounting/unmounting extra instances to match. Requires a live play
|
|
33
|
+
// session — there is no primary to sit beside otherwise — and reads as a
|
|
34
|
+
// no-op-that-lies if silently ignored, so it fails loudly when stopped.
|
|
35
|
+
'set-instance-count': {
|
|
36
|
+
derivedRefresh: 'always',
|
|
37
|
+
handle: (cmd) => {
|
|
38
|
+
if (!isPlayModeActive()) return notPlaying();
|
|
39
|
+
// `names` (optional) labels the instances (index 0 = primary); its length
|
|
40
|
+
// is then the total count. Otherwise `count` is the total, with default
|
|
41
|
+
// "Player N" labels.
|
|
42
|
+
const names = cmd['names'];
|
|
43
|
+
if (Array.isArray(names) && names.every((n) => typeof n === 'string')) {
|
|
44
|
+
if (names.length < 1) {
|
|
45
|
+
return { ok: false, error: 'set-instance-count "names" must have at least one entry.' };
|
|
46
|
+
}
|
|
47
|
+
setDesiredInstanceNames(names as string[]);
|
|
48
|
+
return { ok: true };
|
|
49
|
+
}
|
|
50
|
+
const count = cmd['count'];
|
|
51
|
+
if (typeof count !== 'number' || !Number.isFinite(count) || count < 1) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
error: `set-instance-count requires a whole "count" >= 1 (or a "names" array), got ${String(count)}.`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
setDesiredExtraInstances(Math.floor(count) - 1);
|
|
58
|
+
return { ok: true };
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
// The instance-enumeration op — the ONE session-wire question that must
|
|
62
|
+
// NOT route through the bridge: it answers "which instances exist", and the
|
|
63
|
+
// bridge RESOLVES an instance (and refuses when several are live), so
|
|
64
|
+
// asking it "which exist" is the exact ambiguity it throws on. Returns the
|
|
65
|
+
// live mount ids so `@volter/editor-live`'s `game.instances()` can hand back one
|
|
66
|
+
// addressed `GameClient` per id. No play gate: `liveInstanceIds()` is `[]`
|
|
67
|
+
// when nothing is mounted, which is the honest answer, not a precondition
|
|
68
|
+
// failure. `named` adds the friendly labels a HUD/driver shows ("Player
|
|
69
|
+
// 2"), from the play-mode instances (ids without a play-mode name — e.g. an
|
|
70
|
+
// ingest mount — simply do not appear in `named`).
|
|
71
|
+
'list-instances': {
|
|
72
|
+
derivedRefresh: 'if-content-changed',
|
|
73
|
+
handle: () => ({ ok: true, data: { instances: liveInstanceIds(), named: instanceEntries() } }),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug ▸ Bake/Clear NavMesh — the navmesh session verbs as menu items.
|
|
3
|
+
*
|
|
4
|
+
* Both dispatch the window events `../src/navmesh/navmesh-handler.ts` listens
|
|
5
|
+
* on (started by `navmesh.service.ts`), through `../src/navmesh/navmesh-actions.ts`:
|
|
6
|
+
* the handler and the trigger import the SAME names, so a rename cannot
|
|
7
|
+
* silently unwire the control. The whole cluster — handler, event names,
|
|
8
|
+
* workflow store — is this package's now (P6b); no host module reads it.
|
|
9
|
+
*
|
|
10
|
+
* `disabled` and the Bake label are asked at render, and the host samples them
|
|
11
|
+
* on its availability tick while a menu is open, so they follow the mounted
|
|
12
|
+
* game's `NavigationAdapter` and a bake in flight.
|
|
13
|
+
*/
|
|
14
|
+
import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
|
|
15
|
+
import type { MenuContribution } from '@volter/editor-sdk/chrome';
|
|
16
|
+
import { dispatchNavMeshBake, dispatchNavMeshClear } from '../src/navmesh/navmesh-actions';
|
|
17
|
+
import { navBakeBusy, navMeshBaked } from '../src/navmesh/navmesh-workflow-store';
|
|
18
|
+
|
|
19
|
+
export const point = 'workspace.menu';
|
|
20
|
+
|
|
21
|
+
export const menu: MenuContribution = {
|
|
22
|
+
menu: 'debug',
|
|
23
|
+
items: [
|
|
24
|
+
{
|
|
25
|
+
id: 'navmesh-bake',
|
|
26
|
+
label: () => (navMeshBaked() ? 'Rebake NavMesh' : 'Bake NavMesh'),
|
|
27
|
+
testId: 'navmesh-bake',
|
|
28
|
+
disabled: () => navBakeBusy() || !getActiveSystems().navigation?.bake,
|
|
29
|
+
execute: dispatchNavMeshBake,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'navmesh-clear',
|
|
33
|
+
label: 'Clear NavMesh',
|
|
34
|
+
testId: 'navmesh-clear',
|
|
35
|
+
disabled: () => navBakeBusy() || !navMeshBaked() || !getActiveSystems().navigation?.clear,
|
|
36
|
+
execute: dispatchNavMeshClear,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE NAVMESH SESSION HANDLER (`@volter/editor-sdk/services`, a
|
|
3
|
+
* `workspace.service` contribution): the listener behind Debug ▸ Bake /
|
|
4
|
+
* Clear NavMesh (`navmesh.menu.ts`), which bakes the authoring scene's
|
|
5
|
+
* walkable-tagged meshes through the mounted game's `NavigationAdapter` and
|
|
6
|
+
* shows the result as the viewport's `navmesh` helper.
|
|
7
|
+
*
|
|
8
|
+
* It was a mount effect in the host's one-and-only scene panel, so a
|
|
9
|
+
* build with no `@volter/editor-game` still carried it and that panel owned its
|
|
10
|
+
* lifetime. A service instead: the contribution pass starts it and stops it
|
|
11
|
+
* before the next one, and no stage names a lane.
|
|
12
|
+
*/
|
|
13
|
+
import { setupNavMeshHandlers } from '../src/navmesh/navmesh-handler';
|
|
14
|
+
|
|
15
|
+
export const point = 'workspace.service';
|
|
16
|
+
|
|
17
|
+
export function start(): () => void {
|
|
18
|
+
return setupNavMeshHandlers();
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Network inspector as a drawer tab (W3b): replicated-state tree,
|
|
3
|
+
* message log, rate sparklines and the conditioner over the inspected
|
|
4
|
+
* session's `NetworkingAdapter`. On offer only while such an adapter is
|
|
5
|
+
* registered — a tab over nothing would be a measurement nobody made.
|
|
6
|
+
*/
|
|
7
|
+
import { editorHost } from '@volter/editor-sdk/host';
|
|
8
|
+
import { NetworkInspectorPanel } from '../src/network/NetworkInspectorPanel';
|
|
9
|
+
|
|
10
|
+
export const point = 'workspace.utility';
|
|
11
|
+
export const title = 'Network';
|
|
12
|
+
export const order = 25;
|
|
13
|
+
export const available = (): boolean => editorHost().systems.inspectedNetworking() !== null;
|
|
14
|
+
|
|
15
|
+
export default function NetworkUtility() {
|
|
16
|
+
return <NetworkInspectorPanel />;
|
|
17
|
+
}
|