@vgai/editor 0.2.0 → 0.4.0-canary.20260715.0
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/README.md +24 -0
- package/dist/assets/axe-C7YBtOuM.js +30 -0
- package/dist/assets/browserAll-DDtGGE48.js +1 -0
- package/dist/assets/create-runtime-DbTlBlKG.js +1 -0
- package/dist/assets/{game-BI111o5f.js → game-BE0r7DMI.js} +3 -3
- package/dist/assets/game-BdTHR2_X.js +1 -0
- package/dist/assets/{game-pt_1lQLv.js → game-C3iQGSqo.js} +1 -1
- package/dist/assets/game-CcHfYxjP.js +1 -0
- package/dist/assets/game-Cqzj_lpD.js +1 -0
- package/dist/assets/game-DSZdeCHu.js +1 -0
- package/dist/assets/game-Dch9sAPc.js +1 -0
- package/dist/assets/{game-xsOho_wg.js → game-SAFn90pA.js} +3 -3
- package/dist/assets/game-loop-DMIgSMV-.js +1 -0
- package/dist/assets/{game-source-CvidN7AB.js → game-source-Dk83w26t.js} +1 -1
- package/dist/assets/{index-Bp5cecD6.js → index-4idIsHHJ.js} +2 -2
- package/dist/assets/{index-DL7RcQ0n.js → index-C-LhOTBN.js} +1 -1
- package/dist/assets/{index-Cs8xwv3e.js → index-CDkxceqE.js} +1 -1
- package/dist/assets/index-CQEfi217.js +15412 -0
- package/dist/assets/index-Dvi-V0NE.css +1 -0
- package/dist/assets/ingest-siblings-s0P5I1Ub.js +1 -0
- package/dist/assets/mount-manifest-DXMOlF79.js +1 -0
- package/dist/assets/react-18-C9eHn8x1.js +1 -0
- package/dist/assets/webworkerAll-Fn0e-ouF.js +1 -0
- package/dist/assets/world-documents-GM0a9Kv7.js +1 -0
- package/dist/index.html +15 -13
- package/dist/ingest/racing-game/vgai.game.json +5 -4
- package/dist/ingest/simcity/vgai.game.json +5 -4
- package/dist/ingest/tanks/vgai.game.json +5 -4
- package/dist/project-scripts.bundle.js +3917 -0
- package/dist/scenes/untitled.vscn.json +2 -0
- package/dist-server/packaged.mjs +7697 -3435
- package/package.json +31 -4
- package/scripts/probe-reporter.ts +349 -0
- package/scripts/run-probe.ts +2260 -0
- package/server/asset-catalog-audit.ts +268 -0
- package/server/asset-catalog-v2.ts +146 -0
- package/server/asset-library-routes.ts +276 -58
- package/server/canonical-path.ts +1 -1
- package/server/dev.ts +148 -44
- package/server/editor-server.ts +888 -61
- package/server/editor-sse.ts +115 -4
- package/server/harness-chat-service.ts +796 -0
- package/server/local-asset-catalog.ts +157 -22
- package/server/packaged.ts +51 -21
- package/server/process-shutdown.ts +136 -0
- package/server/prod.ts +9 -6
- package/server/project-hmr-files.ts +41 -0
- package/server/project-operations.ts +343 -0
- package/server/project-optimize-deps-entries.ts +4 -4
- package/server/project-output-writer.ts +156 -0
- package/server/project-validation.ts +140 -0
- package/server/project-view.ts +8 -50
- package/server/server-utils.ts +75 -10
- package/server/session-registry.ts +12 -0
- package/src/EditorContext.tsx +107 -3
- package/src/action-registry.ts +115 -17
- package/src/adapter-resolver.ts +213 -478
- package/src/asset-events.ts +64 -1
- package/src/asset-preview.ts +906 -0
- package/src/asset-selection.ts +121 -0
- package/src/asset-workflow/asset-capabilities.ts +259 -0
- package/src/asset-workflow/asset-editor-errors.ts +46 -0
- package/src/asset-workflow/asset-import-pipeline.ts +176 -0
- package/src/asset-workflow/asset-library-query.ts +107 -0
- package/src/asset-workflow/asset-types.ts +192 -0
- package/src/asset-workflow/asset-workflow-quality.ts +65 -0
- package/src/asset-workflow/import-contract.ts +113 -0
- package/src/asset-workflow/model-conversion.ts +126 -0
- package/src/asset-workflow/model-inspection.ts +185 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +153 -0
- package/src/asset-workflow/project-asset-operations.ts +119 -0
- package/src/asset-workflow/thumbnail-system.ts +172 -0
- package/src/authoring/active-systems.ts +15 -0
- package/src/authoring/boundary-authoring-adapter.ts +16 -5
- package/src/authoring/composite-authoring-adapter.ts +305 -124
- package/src/authoring/design-time-layers.ts +300 -117
- package/src/authoring/edit-mode-authoring.ts +278 -293
- package/src/authoring/embed-only-adapter.ts +4 -2
- package/src/authoring/first-party-hierarchy-menu.ts +7 -2
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +5 -12
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +53 -126
- package/src/authoring/first-party-inspector/HeaderSection.tsx +54 -65
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +30 -11
- package/src/authoring/first-party-inspector/register.tsx +13 -2
- package/src/authoring/ingest-authoring-adapter.ts +142 -45
- package/src/authoring/mount-failure-report.ts +5 -5
- package/src/authoring/provenance.ts +41 -0
- package/src/authoring/react-dom-authoring-adapter.ts +92 -92
- package/src/authoring/react-inspector-section.tsx +489 -51
- package/src/authoring/react-world-authoring-adapter.ts +541 -306
- package/src/authoring/story-overlay.ts +1 -1
- package/src/authoring/story-types.ts +11 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +54 -25
- package/src/authoring/viewport-tool-context.ts +78 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +186 -0
- package/src/authoring/world-pan-state.ts +33 -17
- package/src/authoring/world-session-state.ts +1 -1
- package/src/authoring/world2d-authoring-adapter.ts +109 -52
- package/src/availability-tick.ts +56 -0
- package/src/browser-play-entry.ts +5 -5
- package/src/browser-project-scripts.ts +1 -0
- package/src/build-identity.ts +15 -0
- package/src/command-listener.ts +893 -19
- package/src/components/AlignToolbar.tsx +74 -63
- package/src/components/AppRoot.tsx +193 -26
- package/src/components/ApplicationMenus.tsx +272 -0
- package/src/components/AssetBrowser.tsx +1450 -293
- package/src/components/AssetEditorShell.tsx +141 -0
- package/src/components/AssetImportDetails.tsx +218 -0
- package/src/components/AssetLibraryPanel.tsx +14 -0
- package/src/components/CameraInfo.tsx +11 -20
- package/src/components/CenterDocuments.tsx +585 -0
- package/src/components/CommandPalette.tsx +75 -30
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +57 -112
- package/src/components/DebugConsolePanel.tsx +461 -0
- package/src/components/DebuggerPanel.tsx +165 -0
- package/src/components/DefaultEditorLayout.tsx +167 -231
- package/src/components/GameHierarchy.tsx +873 -290
- package/src/components/GamePerformanceOverlay.tsx +46 -0
- package/src/components/HarnessChatPanel.tsx +2352 -0
- package/src/components/HistoryPanel.tsx +226 -0
- package/src/components/Inspector.tsx +153 -99
- package/src/components/InspectorToolSection.tsx +6 -4
- package/src/components/MountFailureBanner.tsx +31 -48
- package/src/components/NetworkSection.tsx +25 -14
- package/src/components/OnlineAssetBrowser.tsx +794 -343
- package/src/components/OverlayReportBanner.tsx +38 -64
- package/src/components/PerformancePanel.tsx +206 -0
- package/src/components/PlayBar.tsx +93 -263
- package/src/components/PrefabSaveDialog.tsx +55 -127
- package/src/components/ProjectHeader.css +60 -0
- package/src/components/ProjectHeader.tsx +170 -105
- package/src/components/ProjectScreen.tsx +246 -184
- package/src/components/ReactCanvasControls.tsx +185 -0
- package/src/components/ResolutionPicker.tsx +85 -0
- package/src/components/RootActivateDialog.tsx +39 -68
- package/src/components/SaveDialog.tsx +55 -127
- package/src/components/SaveStatus.tsx +11 -29
- package/src/components/StartupLoadingScreen.tsx +96 -0
- package/src/components/StateWatchPanel.tsx +302 -0
- package/src/components/StatsOverlay.tsx +12 -5
- package/src/components/StatusBar.tsx +64 -0
- package/src/components/ThemeManagerDialog.css +89 -0
- package/src/components/ThemeManagerDialog.tsx +371 -0
- package/src/components/ToolHost.tsx +26 -15
- package/src/components/Toolbar.tsx +117 -214
- package/src/components/VgaiVoxelLogo.tsx +89 -0
- package/src/components/ViewportOverlay.tsx +97 -172
- package/src/components/ViewportPanel.tsx +130 -31
- package/src/components/WorkspaceDock.tsx +891 -0
- package/src/components/WorldSelectionOverlay.tsx +51 -63
- package/src/components/WorldTextEditor.tsx +9 -5
- package/src/components/asset-documents.tsx +390 -0
- package/src/components/asset-editor-persistence.ts +44 -0
- package/src/components/asset-selection-section.tsx +338 -0
- package/src/components/asset-viewers/AudioViewer.tsx +73 -21
- package/src/components/asset-viewers/GenericJsonViewer.tsx +26 -11
- package/src/components/asset-viewers/ImageViewer.tsx +43 -11
- package/src/components/asset-viewers/InputMapViewer.tsx +512 -134
- package/src/components/asset-viewers/MaterialAssetEditor.tsx +540 -0
- package/src/components/asset-viewers/ModelViewer.tsx +805 -121
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +177 -60
- package/src/components/asset-viewers/SourceAssetViewer.tsx +121 -0
- package/src/components/asset-workflow.css +506 -0
- package/src/components/build-documents.tsx +696 -0
- package/src/components/core-utilities.tsx +75 -0
- package/src/components/data-documents.tsx +364 -0
- package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
- package/src/components/debugger-panel-state.ts +57 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
- package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
- package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
- package/src/components/inspector-widgets/FontPicker.tsx +13 -21
- package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
- package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
- package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
- package/src/components/inspector-widgets/shared.tsx +90 -49
- package/src/components/inspectors/AnimationSection.tsx +39 -40
- package/src/components/inspectors/AudioSection.tsx +28 -39
- package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
- package/src/components/inspectors/CameraSection.tsx +14 -23
- package/src/components/inspectors/ComponentsSection.tsx +39 -43
- package/src/components/inspectors/JointsSection.tsx +34 -43
- package/src/components/inspectors/LightSection.tsx +90 -35
- package/src/components/inspectors/MaterialSection.tsx +137 -174
- package/src/components/inspectors/MeshSection.tsx +9 -17
- package/src/components/inspectors/NavigationSection.tsx +8 -15
- package/src/components/inspectors/ParticleSection.tsx +91 -76
- package/src/components/inspectors/PhysicsSection.tsx +42 -46
- package/src/components/inspectors/PostProcessingSection.tsx +28 -36
- package/src/components/inspectors/RenderingSection.tsx +12 -20
- package/src/components/inspectors/ShadowSection.tsx +7 -8
- package/src/components/inspectors/SplineSection.tsx +31 -42
- package/src/components/inspectors/TransformSection.tsx +14 -22
- package/src/components/patterns/Dialog.tsx +95 -0
- package/src/components/patterns/Fields.tsx +44 -0
- package/src/components/patterns/List.tsx +25 -0
- package/src/components/patterns/StateSurface.tsx +40 -0
- package/src/components/patterns/Surfaces.tsx +115 -0
- package/src/components/patterns/Tabs.tsx +80 -0
- package/src/components/patterns/Toolbar.tsx +51 -0
- package/src/components/patterns/Tree.tsx +72 -0
- package/src/components/primitives/Button.tsx +46 -0
- package/src/components/primitives/ColorInput.tsx +11 -47
- package/src/components/primitives/EditorIcon.tsx +99 -0
- package/src/components/primitives/FormControls.tsx +71 -0
- package/src/components/primitives/JsonInput.tsx +15 -2
- package/src/components/primitives/Layout.tsx +100 -0
- package/src/components/primitives/Menu.tsx +105 -0
- package/src/components/primitives/NumberInput.tsx +8 -35
- package/src/components/primitives/Panel.tsx +49 -44
- package/src/components/primitives/SectionHeader.tsx +10 -28
- package/src/components/primitives/Text.tsx +46 -0
- package/src/components/primitives/Tooltip.tsx +20 -12
- package/src/components/primitives/Vec3Input.tsx +3 -0
- package/src/components/primitives/banner-tones.ts +33 -0
- package/src/components/primitives/editor-icons.ts +102 -0
- package/src/components/primitives/panel-header-styles.ts +30 -0
- package/src/components/project-operation-documents.tsx +226 -0
- package/src/components/status-contributions.tsx +175 -0
- package/src/components/story-documents-register.ts +15 -0
- package/src/components/story-documents.tsx +1185 -0
- package/src/components/tool-documents.tsx +110 -0
- package/src/components/workspace-dock.css +293 -0
- package/src/components/workspace-history.ts +33 -0
- package/src/components/world-documents.tsx +145 -0
- package/src/components/xstate-documents.tsx +209 -0
- package/src/composite-screenshot.ts +248 -0
- package/src/design-system/index.ts +70 -0
- package/src/design-system-stories/Foundation.stories.tsx +183 -0
- package/src/design-system-stories/Patterns.stories.tsx +251 -0
- package/src/design-system-stories/Workspace.stories.tsx +538 -0
- package/src/doctor/report.ts +57 -13
- package/src/editor-api.ts +295 -31
- package/src/editor-compatibility.ts +200 -0
- package/src/editor-console.ts +35 -17
- package/src/editor-hotkeys.ts +85 -33
- package/src/editor-store.ts +538 -233
- package/src/editor-viewport.ts +160 -34
- package/src/entity-factory.ts +14 -3
- package/src/gizmo-registry.ts +21 -5
- package/src/harness-chat-editor-context.ts +37 -0
- package/src/harness-chat-indicator.ts +68 -0
- package/src/harness-chat-layout.ts +53 -0
- package/src/harness-chat-lifecycle.ts +170 -0
- package/src/harness-chat-reconnect.ts +85 -0
- package/src/harness-chat-types.ts +409 -0
- package/src/hierarchy-drop.ts +50 -0
- package/src/hierarchy-expansion-state.ts +100 -0
- package/src/hierarchy-menu-registry.ts +1 -1
- package/src/hierarchy-projection.ts +76 -0
- package/src/history/editor-session.ts +21 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-service.ts +904 -0
- package/src/history/json-history-resource.ts +165 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +266 -0
- package/src/history/project-root-history-backends.ts +53 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +108 -0
- package/src/history/source-history-backend.ts +374 -0
- package/src/history/types.ts +100 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hosted-example.ts +12 -2
- package/src/hotkeys.ts +89 -18
- package/src/ingest/discovery-react.ts +9 -10
- package/src/ingest/discovery.ts +4 -4
- package/src/ingest/discovery2d.ts +5 -5
- package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
- package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
- package/src/ingest/games/games-fps/vgai.game.json +3 -3
- package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
- package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
- package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
- package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
- package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
- package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
- package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
- package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
- package/src/ingest/types.ts +1 -1
- package/src/ingest-mode.ts +47 -52
- package/src/ingest-siblings.ts +23 -33
- package/src/initial-scene.ts +1 -2
- package/src/main.tsx +16 -8
- package/src/manifest-project.ts +10 -12
- package/src/model-thumbnail.ts +261 -26
- package/src/module-mode.ts +44 -50
- package/src/play-log-events.ts +26 -0
- package/src/play-mode.ts +495 -401
- package/src/play-scene-adoption.ts +23 -0
- package/src/playground/main.ts +68 -24
- package/src/playwright-shim.ts +473 -0
- package/src/project-manager.ts +90 -10
- package/src/project-operations.ts +112 -0
- package/src/project-story-discovery.ts +25 -0
- package/src/project-tool-discovery.ts +43 -0
- package/src/scene-serializer.ts +0 -6
- package/src/scene-sync.ts +7 -2
- package/src/script-loader.ts +3 -3
- package/src/skeleton-helper.ts +57 -0
- package/src/storage/seed.ts +68 -8
- package/src/stories/StoryPreviewMount.tsx +265 -0
- package/src/stories/action-stories.fixture.tsx +44 -0
- package/src/stories/button-stories.fixture.tsx +50 -0
- package/src/stories/compose-project-stories.ts +182 -0
- package/src/stories/loader-stories.fixture.tsx +53 -0
- package/src/stories/story-discovery.ts +130 -0
- package/src/stories/story-registry.ts +146 -0
- package/src/theme-css.ts +1049 -0
- package/src/theme-library.ts +238 -0
- package/src/theme-preference.ts +130 -0
- package/src/theme.ts +527 -0
- package/src/tool-loader.ts +80 -24
- package/src/ui-source/inspect.ts +27 -4
- package/src/ui-source/source-write-backend.ts +83 -22
- package/src/widgets/index.ts +9 -18
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-dock-controller.ts +64 -0
- package/src/workspace-document-ids.ts +5 -0
- package/src/workspace-document-registry.ts +367 -0
- package/src/workspace-state-persistence.ts +455 -0
- package/src/workspace-status-registry.ts +110 -0
- package/src/workspace-utility-commands.ts +49 -0
- package/src/workspace-utility-registry.ts +161 -0
- package/src/world-document-routing.ts +16 -0
- package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
- package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
- package/src/xstate-inspect/use-live-actor-state.ts +39 -0
- package/src/xstate-inspect/xstate-graph.ts +265 -0
- package/src/xstate-inspect/xstate-layout.ts +76 -0
- package/src/z-index.ts +26 -0
- package/template/.claude/skills/editor/SKILL.md +129 -90
- package/template/AGENTS.md +1110 -0
- package/template/CLAUDE.md +1 -512
- package/template/IDIOMS.md +50 -0
- package/template/check-idioms.ts +706 -0
- package/template/manifest-entry-modules-plugin.ts +53 -0
- package/template/package.json +13 -4
- package/template/public/scenes/default.scn2d.json +6 -0
- package/template/public/scenes/default.vscn.json +7 -1
- package/template/public/scenes/untitled.vscn.json +10 -0
- package/template/scripts/playtest.ts +40 -0
- package/template/server/loopback.ts +75 -0
- package/template/server/rooms/arena-room.ts +69 -1
- package/template/server/rooms/game-room.ts +68 -1
- package/template/src/data/README.md +9 -4
- package/template/src/data/data-ref.ts +35 -0
- package/template/src/data/tuning.schema.ts +1 -1
- package/template/src/main.ts +41 -62
- package/template/src/manifest-entry-modules.d.ts +3 -0
- package/template/src/operations/README.md +88 -0
- package/template/src/runtime/pixi-adapter.ts +24 -0
- package/template/src/scripts/components/data-spinner.ts +43 -2
- package/template/src/scripts/components/spin-lap-math.ts +27 -0
- package/template/src/tools/README.md +5 -3
- package/template/src/tools/example.tool.tsx +13 -7
- package/template/src/ui/Button.stories.tsx +66 -0
- package/template/src/ui/RebindPanel.tsx +2 -6
- package/template/src/ui/game.tsx +17 -0
- package/template/tests/acceptance/example.spec.ts +64 -0
- package/template/tests/logic/example.test.ts +31 -0
- package/template/tests/logic/room.test.ts +100 -0
- package/template/tests/tsconfig.json +30 -0
- package/template/validate-manifest.ts +5 -5
- package/template/validate-scenes.ts +50 -18
- package/template/vgai.game.json +20 -13
- package/template/vite.config.ts +77 -16
- package/vite-plugin-ui-oid.ts +261 -17
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
- package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
- package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
- package/dist/assets/browserAll-CEkWYlFk.js +0 -1
- package/dist/assets/create-runtime-DYIWr3va.js +0 -1
- package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
- package/dist/assets/game-8SDZPPgH.js +0 -1
- package/dist/assets/game-CevorxyV.js +0 -1
- package/dist/assets/game-Di7XlywD.js +0 -1
- package/dist/assets/game-DjnCaw8R.js +0 -1
- package/dist/assets/game-Q9A0aXSV.js +0 -1
- package/dist/assets/index-CPVV6pjw.js +0 -1
- package/dist/assets/index-CpOnYyeD.js +0 -213
- package/dist/assets/index-Dr8H_6Rl.js +0 -1
- package/dist/assets/index-_bCaipUk.js +0 -13650
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
- package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
- package/dist/assets/scene-query-BivAHhJu.js +0 -1
- package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
- package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
- package/src/authoring/scene-ui-layer.ts +0 -485
- package/src/authoring/ui-authoring-adapter.ts +0 -325
- package/src/authoring/ui-hierarchy-menu.ts +0 -40
- package/src/authoring/ui-inspector-sections.tsx +0 -156
- package/src/authoring/undo-timeline.ts +0 -145
- package/src/components/BottomPanel.tsx +0 -326
- package/src/components/BuildPanel.tsx +0 -455
- package/src/components/FolderTree.tsx +0 -179
- package/src/components/RightPanel.tsx +0 -166
- package/src/components/WorldContextMenu.tsx +0 -257
- package/src/components/asset-viewers/AssetViewer.tsx +0 -38
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
- package/src/scene-ui-mode/active-scene-ui.ts +0 -58
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
- package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
- package/src/ui-editor/inspector.tsx +0 -563
- package/src/ui-editor/node-ops.ts +0 -266
- package/src/ui-editor/overlay.tsx +0 -380
- package/src/ui-editor/palette.tsx +0 -114
- package/src/ui-editor/react-store.ts +0 -78
- package/src/ui-editor/timeline-panel.tsx +0 -443
- package/src/ui-editor/ui-edit-store.ts +0 -552
- package/src/ui-editor/ui-prefab.ts +0 -95
- package/template/project.json +0 -9
- package/template/src/scripts/main.ts +0 -94
- package/template/src/ui/DefaultHud.tsx +0 -40
- package/template/src/ui/game-state.tsx +0 -37
- package/template/src/ui/mount-default-hud.tsx +0 -19
- package/template/src/ui/mount-ui.ts +0 -15
- package/vite-plugin-react-world-provider.ts +0 -81
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
const MANIFEST_ENTRIES_ID = 'virtual:vgai-manifest-entries';
|
|
4
|
+
const RESOLVED_MANIFEST_ENTRIES_ID = `\0${MANIFEST_ENTRIES_ID}`;
|
|
5
|
+
|
|
6
|
+
function collectManifestEntries(manifest: unknown): string[] {
|
|
7
|
+
const rawWorlds =
|
|
8
|
+
manifest && typeof manifest === 'object'
|
|
9
|
+
? (manifest as { roots?: unknown }).roots
|
|
10
|
+
: undefined;
|
|
11
|
+
const entries = [
|
|
12
|
+
...new Set(
|
|
13
|
+
(Array.isArray(rawWorlds) ? rawWorlds : [])
|
|
14
|
+
.map((world) =>
|
|
15
|
+
world && typeof world === 'object' ? (world as { entry?: unknown }).entry : undefined,
|
|
16
|
+
)
|
|
17
|
+
.filter((entry): entry is string => typeof entry === 'string'),
|
|
18
|
+
),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
if (entry.startsWith('/') || entry.includes('\\') || entry.split('/').includes('..')) {
|
|
23
|
+
throw new Error(`vgai.game.json world entry must be project-relative: ${entry}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return entries;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Render imports for the manifest's exact `roots[].entry` declarations.
|
|
32
|
+
* The manifest—not a filename convention—is the only discovery source.
|
|
33
|
+
*/
|
|
34
|
+
export function renderManifestEntryModule(manifest: unknown): string {
|
|
35
|
+
const entries = collectManifestEntries(manifest);
|
|
36
|
+
const imports = entries.map(
|
|
37
|
+
(entry, index) => `import * as entry${index} from ${JSON.stringify(`/${entry}`)};`,
|
|
38
|
+
);
|
|
39
|
+
const members = entries.map((entry, index) => `${JSON.stringify(entry)}: entry${index}`);
|
|
40
|
+
return `${imports.join('\n')}\nexport const manifestEntryModules = {${members.join(',')}};\n`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function manifestEntryModulesPlugin(manifest: unknown): Plugin {
|
|
44
|
+
return {
|
|
45
|
+
name: 'vgai:manifest-entry-modules',
|
|
46
|
+
resolveId(id) {
|
|
47
|
+
return id === MANIFEST_ENTRIES_ID ? RESOLVED_MANIFEST_ENTRIES_ID : undefined;
|
|
48
|
+
},
|
|
49
|
+
load(id) {
|
|
50
|
+
return id === RESOLVED_MANIFEST_ENTRIES_ID ? renderManifestEntryModule(manifest) : undefined;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
package/template/package.json
CHANGED
|
@@ -11,15 +11,18 @@
|
|
|
11
11
|
"dev": "vgai edit .",
|
|
12
12
|
"game": "vite",
|
|
13
13
|
"build": "tsc && vite build",
|
|
14
|
-
"typecheck": "tsc --noEmit && tsc --noEmit -p server/tsconfig.json",
|
|
14
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p server/tsconfig.json && tsc --noEmit -p tests/tsconfig.json",
|
|
15
15
|
"emit-schemas": "npx tsx emit-schemas.ts",
|
|
16
16
|
"validate-scenes": "npx tsx validate-scenes.ts .",
|
|
17
17
|
"validate-manifest": "npx tsx validate-manifest.ts vgai.game.json",
|
|
18
|
-
"
|
|
18
|
+
"check-idioms": "npx tsx check-idioms.ts .",
|
|
19
|
+
"server": "npx tsx --tsconfig server/tsconfig.json server/main.ts",
|
|
20
|
+
"test": "vitest run tests/logic",
|
|
21
|
+
"test:acceptance": "vgai probe",
|
|
22
|
+
"playtest": "npx tsx scripts/playtest.ts"
|
|
19
23
|
},
|
|
20
24
|
"dependencies": {
|
|
21
25
|
"@vgai/engine": "*",
|
|
22
|
-
"@vgai/scene-ui": "*",
|
|
23
26
|
"@colyseus/schema": "^4.0.14",
|
|
24
27
|
"@colyseus/sdk": "^0.17.34",
|
|
25
28
|
"@dimforge/rapier3d-compat": "^0.14.0",
|
|
@@ -42,12 +45,18 @@
|
|
|
42
45
|
"@types/react": "^19.2.14",
|
|
43
46
|
"@types/react-dom": "^19.2.3",
|
|
44
47
|
"@types/three": "^0.170.0",
|
|
48
|
+
"@playwright/test": "^1.58.2",
|
|
49
|
+
"@vgai/probe": "*",
|
|
50
|
+
"@vgai/live": "*",
|
|
51
|
+
"@vgai/sdk": "*",
|
|
52
|
+
"@vgai/editor-sdk": "*",
|
|
45
53
|
"@vitejs/plugin-react": "^5.1.4",
|
|
46
54
|
"colyseus": "^0.17.8",
|
|
47
55
|
"@colyseus/ws-transport": "^0.17.9",
|
|
48
56
|
"storybook": "^9.1.20",
|
|
49
57
|
"tsx": "^4.23.0",
|
|
50
58
|
"typescript": "^5.6.0",
|
|
51
|
-
"vite": "^6.0.0"
|
|
59
|
+
"vite": "^6.0.0",
|
|
60
|
+
"vitest": "^3.2.6"
|
|
52
61
|
}
|
|
53
62
|
}
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
},
|
|
7
7
|
"entities": [
|
|
8
8
|
{
|
|
9
|
+
"id": "d43d8032-0e0c-4936-b996-87924d458668",
|
|
9
10
|
"name": "AmbientLight",
|
|
10
11
|
"light": { "type": "hemisphere", "color": "#ffffff", "groundColor": "#444444", "intensity": 0.5 }
|
|
11
12
|
},
|
|
12
13
|
{
|
|
14
|
+
"id": "d3231678-2f7f-4839-994b-1edf2abcc73c",
|
|
13
15
|
"name": "Sun",
|
|
14
16
|
"light": { "type": "directional", "color": "#ffffff", "intensity": 1 },
|
|
15
17
|
"transform": { "position": [5, 10, 5] },
|
|
@@ -20,6 +22,7 @@
|
|
|
20
22
|
}
|
|
21
23
|
},
|
|
22
24
|
{
|
|
25
|
+
"id": "2efb889f-5ca2-4715-a614-a379fdcb195b",
|
|
23
26
|
"name": "Ground",
|
|
24
27
|
"mesh": { "type": "box", "args": [20, 0.2, 20] },
|
|
25
28
|
"material": { "type": "standard", "color": "#888888" },
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
"shadow": { "enabled": true }
|
|
28
31
|
},
|
|
29
32
|
{
|
|
33
|
+
"id": "50a15029-7b16-4bb4-b013-b222b3ae0ad3",
|
|
30
34
|
"name": "Box",
|
|
31
35
|
"mesh": { "type": "box", "args": [1, 1, 1] },
|
|
32
36
|
"material": { "type": "standard", "color": "#4488ff" },
|
|
@@ -35,9 +39,11 @@
|
|
|
35
39
|
"components": { "DataSpinner": { "spinScale": 1 } }
|
|
36
40
|
},
|
|
37
41
|
{
|
|
42
|
+
"id": "af6855d6-9bf9-47f3-bcb1-d5e6843c97bd",
|
|
38
43
|
"name": "Camera",
|
|
39
44
|
"transform": { "position": [3, 3, 3] },
|
|
40
|
-
"camera": { "type": "perspective", "fov": 60, "near": 0.1, "far": 1000 }
|
|
45
|
+
"camera": { "type": "perspective", "fov": 60, "near": 0.1, "far": 1000 },
|
|
46
|
+
"components": { "SceneCamera": { "x": 3, "y": 3, "z": 3, "lookAtY": 0.5 } }
|
|
41
47
|
}
|
|
42
48
|
]
|
|
43
49
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"name": "Untitled",
|
|
4
4
|
"entities": [
|
|
5
5
|
{
|
|
6
|
+
"id": "cf60bb73-ace4-4078-a1a1-2a21af3b4cfb",
|
|
6
7
|
"name": "Camera",
|
|
7
8
|
"camera": {
|
|
8
9
|
"type": "perspective"
|
|
@@ -13,9 +14,18 @@
|
|
|
13
14
|
2,
|
|
14
15
|
5
|
|
15
16
|
]
|
|
17
|
+
},
|
|
18
|
+
"components": {
|
|
19
|
+
"SceneCamera": {
|
|
20
|
+
"x": 0,
|
|
21
|
+
"y": 2,
|
|
22
|
+
"z": 5,
|
|
23
|
+
"lookAtY": 0
|
|
24
|
+
}
|
|
16
25
|
}
|
|
17
26
|
},
|
|
18
27
|
{
|
|
28
|
+
"id": "49bbe8e0-0b0c-4780-88bb-d34790cce748",
|
|
19
29
|
"name": "Box",
|
|
20
30
|
"mesh": {
|
|
21
31
|
"type": "box"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inner-loop autoplay (see AGENTS.md's "Autoplay bots" — Level 2). A plain
|
|
3
|
+
* `@vgai/live` script: no test runner, no Playwright config, no fixture, no
|
|
4
|
+
* second browser. It ATTACHES to the live `vgai edit` session/tab you
|
|
5
|
+
* already have open (run `vgai edit .` first if you don't) and drives it
|
|
6
|
+
* with the SAME `GameClient` vocabulary `tests/acceptance/example.spec.ts`
|
|
7
|
+
* uses — compare the two side by side; the difference in ceremony is the
|
|
8
|
+
* whole point.
|
|
9
|
+
*
|
|
10
|
+
* Run: `npm run playtest` (or `npx tsx scripts/playtest.ts`). Nothing here
|
|
11
|
+
* is committed evidence — it's scratch space for "does my game work right
|
|
12
|
+
* now". Once an interaction proves a feature, graduate it into a spec under
|
|
13
|
+
* `tests/acceptance/` (`vgai probe --init` scaffolds the first one).
|
|
14
|
+
*
|
|
15
|
+
* Drives the template's shipped example component, `DataSpinner`
|
|
16
|
+
* (`src/scripts/components/data-spinner.ts`) — replace this with your own
|
|
17
|
+
* provider/command/action names as your first real edit.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { editor, game } from '@vgai/live';
|
|
21
|
+
|
|
22
|
+
await editor.play(); // enters play mode; the ack waits for the game's full async setup
|
|
23
|
+
|
|
24
|
+
// Fixture (declared cheat, D16) — jump to a known state. Never a substitute
|
|
25
|
+
// for the honest-input proof below.
|
|
26
|
+
await game.command('spinner.reset');
|
|
27
|
+
|
|
28
|
+
// Proof: the same honest input path a human's keys hit (see
|
|
29
|
+
// public/inputmaps/default.inputmap.json's 'attack' binding).
|
|
30
|
+
await game.input.hold('attack', { simSeconds: 4 });
|
|
31
|
+
|
|
32
|
+
await game.waitFor((s) => (s('spinner') as { lapsCompleted: number }).lapsCompleted >= 1, {
|
|
33
|
+
simSeconds: 10,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const spinner = (await game.state('spinner')) as { lapsCompleted: number };
|
|
37
|
+
console.log(`spinner lapsCompleted: ${spinner.lapsCompleted}`);
|
|
38
|
+
|
|
39
|
+
const shot = await game.screenshot('playtest-after-boosted-spin');
|
|
40
|
+
console.log(`screenshot: ${shot}`);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback Colyseus server harness for headless multiplayer tests — a
|
|
3
|
+
* near-verbatim copy of the engine repo's own
|
|
4
|
+
* `packages/editor/test/helpers/colyseus-loopback.ts` (Acceptance Driver
|
|
5
|
+
* build plan Task 4.4), shipped INTO every scaffolded project so a
|
|
6
|
+
* multiplayer game has a headless L1-equivalent (`docs/ACCEPTANCE-DRIVER-SPEC.md`
|
|
7
|
+
* §2.3's "Multiplayer lane") before `--players N` exists on `vgai probe`.
|
|
8
|
+
*
|
|
9
|
+
* Starts the REAL `startColyseus` bootstrap (`./colyseus-setup.js`) with the
|
|
10
|
+
* shipped room registry (`./rooms.js` — `ArenaRoom` + `GameRoom`) on an
|
|
11
|
+
* OS-assigned ephemeral port, in-process. No mocks: this is the exact same
|
|
12
|
+
* `Server` / `WebSocketTransport` a real `npm run server` would boot, just on
|
|
13
|
+
* `port: 0` so concurrent test runs never collide on `2567`.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const server = await startLoopbackColyseus();
|
|
18
|
+
* try {
|
|
19
|
+
* const client = new Client(server.url);
|
|
20
|
+
* const room = await client.joinOrCreate('game_room', {}, undefined, undefined, {
|
|
21
|
+
* allowDebugCommands: true,
|
|
22
|
+
* });
|
|
23
|
+
* // ...
|
|
24
|
+
* } finally {
|
|
25
|
+
* server.stop();
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { startColyseus } from './colyseus-setup.js';
|
|
31
|
+
import { rooms } from './rooms.js';
|
|
32
|
+
|
|
33
|
+
export interface LoopbackColyseus {
|
|
34
|
+
/** `ws://127.0.0.1:<port>` — the ephemeral port the server actually bound. */
|
|
35
|
+
url: string;
|
|
36
|
+
/** Tear down the server (closes the underlying HTTP/WS server). */
|
|
37
|
+
stop: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `@colyseus/core`'s matchmaker setup unconditionally `import("@pm2/io")`s and
|
|
42
|
+
* registers metrics, which makes `@pm2/io` probe `process.send` and start
|
|
43
|
+
* speaking the PM2 agent protocol (`axm:*` messages) over it. Under vitest's
|
|
44
|
+
* forked worker pool, `process.send` is real (the runner's own parent<->child
|
|
45
|
+
* channel), so those messages get relayed to the parent and crash its
|
|
46
|
+
* deserializer — nothing to do with this test's own logic. Patch `process.send`
|
|
47
|
+
* to drop only `axm:*` payloads for the harness's lifetime; everything else
|
|
48
|
+
* (vitest's own protocol) passes through untouched.
|
|
49
|
+
*/
|
|
50
|
+
function suppressPm2IoIpcNoise(): () => void {
|
|
51
|
+
const original = process.send?.bind(process);
|
|
52
|
+
if (!original) return () => {};
|
|
53
|
+
const patched = (message: unknown, ...rest: unknown[]): boolean => {
|
|
54
|
+
const type = (message as { type?: unknown } | null)?.type;
|
|
55
|
+
if (typeof type === 'string' && type.startsWith('axm:')) return true;
|
|
56
|
+
return (original as (...a: unknown[]) => boolean)(message, ...rest);
|
|
57
|
+
};
|
|
58
|
+
process.send = patched as NonNullable<typeof process.send>;
|
|
59
|
+
return () => {
|
|
60
|
+
process.send = original;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Boot the real Colyseus server on an ephemeral loopback port. */
|
|
65
|
+
export async function startLoopbackColyseus(): Promise<LoopbackColyseus> {
|
|
66
|
+
const restorePm2IoIpc = suppressPm2IoIpcNoise();
|
|
67
|
+
const handle = await startColyseus({ rooms, port: 0 });
|
|
68
|
+
return {
|
|
69
|
+
url: `ws://127.0.0.1:${handle.port}`,
|
|
70
|
+
stop: () => {
|
|
71
|
+
handle.stop();
|
|
72
|
+
restorePm2IoIpc();
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* - Configurable bot count (0–6), spawned on create, stored in the same players MapSchema.
|
|
18
18
|
* - Server picks behavior (idle/patrol/chase/flee) + target position.
|
|
19
19
|
* - Client handles smooth navmesh pathfinding.
|
|
20
|
+
*
|
|
21
|
+
* Also handles `__vgai:debugCommand` { name, args, requestId } — a
|
|
22
|
+
* `locus: 'server'` debug command's client leg (`docs/SYNTHETIC-PLAYER-SPEC.md`
|
|
23
|
+
* §3.1's "Multiplayer locus" note, Acceptance Driver build plan Task 4.2);
|
|
24
|
+
* see `DEBUG_COMMANDS`/`onCreate` below.
|
|
20
25
|
*/
|
|
21
26
|
|
|
22
27
|
import { Room, type Client } from 'colyseus';
|
|
@@ -111,6 +116,25 @@ function randomTagger(state: ArenaState): string {
|
|
|
111
116
|
return ids[Math.floor(Math.random() * ids.length)]!;
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
/**
|
|
120
|
+
* The `locus: 'server'` debug-command table (Acceptance Driver build plan
|
|
121
|
+
* Task 4.2) — mirrors `GameRoom`'s `DEBUG_COMMANDS` (`../game-room.ts`), one
|
|
122
|
+
* worked example per room. Mutates the AUTHORITATIVE (server) copy of state,
|
|
123
|
+
* never client prediction. Gated by `allowDebugCommands` — see `onCreate`.
|
|
124
|
+
*/
|
|
125
|
+
const DEBUG_COMMANDS: Record<
|
|
126
|
+
string,
|
|
127
|
+
(room: ArenaRoom, client: Client, args: unknown[]) => unknown
|
|
128
|
+
> = {
|
|
129
|
+
'give-score': (room, client, args) => {
|
|
130
|
+
const amount = typeof args[0] === 'number' ? args[0] : 1;
|
|
131
|
+
const player = room.state.players.get(client.sessionId);
|
|
132
|
+
if (!player) throw new Error(`give-score: no player for session "${client.sessionId}"`);
|
|
133
|
+
player.score += amount;
|
|
134
|
+
return { score: player.score };
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
114
138
|
// --- Room ---
|
|
115
139
|
|
|
116
140
|
export class ArenaRoom extends Room {
|
|
@@ -120,11 +144,55 @@ export class ArenaRoom extends Room {
|
|
|
120
144
|
private colorIndex = 0;
|
|
121
145
|
private npcBrains = new Map<string, NPCBrain>();
|
|
122
146
|
|
|
123
|
-
onCreate(options?: { botCount?: number }) {
|
|
147
|
+
onCreate(options?: { botCount?: number; allowDebugCommands?: boolean }) {
|
|
124
148
|
// Spawn NPC bots (count from room creator, default 2, max 6)
|
|
125
149
|
const npcCount = Math.max(0, Math.min(MAX_NPCS, options?.botCount ?? 2));
|
|
126
150
|
this.spawnNPCs(npcCount);
|
|
127
151
|
|
|
152
|
+
// Off by default (FROZEN DECISION: env-var OR room-option) — `vgai probe`/
|
|
153
|
+
// the loopback harness (`../loopback.ts`) opt in via the room option; a
|
|
154
|
+
// plain `npm run server` boot never accepts debug commands unless
|
|
155
|
+
// VGAI_ALLOW_DEBUG_COMMANDS is set in its environment.
|
|
156
|
+
const debugCommandsAllowed =
|
|
157
|
+
options?.allowDebugCommands === true || Boolean(process.env['VGAI_ALLOW_DEBUG_COMMANDS']);
|
|
158
|
+
|
|
159
|
+
this.onMessage(
|
|
160
|
+
'__vgai:debugCommand',
|
|
161
|
+
(client, message: { name: string; args?: unknown[]; requestId: string }) => {
|
|
162
|
+
if (!debugCommandsAllowed) {
|
|
163
|
+
client.send('__vgai:debugCommandResult', {
|
|
164
|
+
requestId: message.requestId,
|
|
165
|
+
ok: false,
|
|
166
|
+
error: 'debug commands disabled',
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const handler = DEBUG_COMMANDS[message.name];
|
|
171
|
+
if (!handler) {
|
|
172
|
+
client.send('__vgai:debugCommandResult', {
|
|
173
|
+
requestId: message.requestId,
|
|
174
|
+
ok: false,
|
|
175
|
+
error: `debug: no server command registered under "${message.name}"`,
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
const result = handler(this, client, message.args ?? []);
|
|
181
|
+
client.send('__vgai:debugCommandResult', {
|
|
182
|
+
requestId: message.requestId,
|
|
183
|
+
ok: true,
|
|
184
|
+
result,
|
|
185
|
+
});
|
|
186
|
+
} catch (err) {
|
|
187
|
+
client.send('__vgai:debugCommandResult', {
|
|
188
|
+
requestId: message.requestId,
|
|
189
|
+
ok: false,
|
|
190
|
+
error: err instanceof Error ? err.message : String(err),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
);
|
|
195
|
+
|
|
128
196
|
// Handle start_game from host
|
|
129
197
|
this.onMessage('start_game', (client) => {
|
|
130
198
|
if (client.sessionId !== this.state.hostId) return;
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Messages (client → server only):
|
|
9
9
|
* position { x, y, z } — client movement input
|
|
10
|
+
* __vgai:debugCommand { name, args, requestId } — a `locus: 'server'`
|
|
11
|
+
* debug command's client leg (`docs/SYNTHETIC-PLAYER-SPEC.md` §3.1's
|
|
12
|
+
* "Multiplayer locus" note, Acceptance Driver build plan Task 4.2); see
|
|
13
|
+
* `DEBUG_COMMANDS`/`onCreate` below.
|
|
10
14
|
*
|
|
11
15
|
* Everything else (players, orbs, scores) is synchronized via Schema
|
|
12
16
|
* state: new joiners receive the full state automatically, and ongoing
|
|
@@ -54,6 +58,25 @@ class GameState extends Schema {
|
|
|
54
58
|
|
|
55
59
|
let nextOrbId = 0;
|
|
56
60
|
|
|
61
|
+
/**
|
|
62
|
+
* The `locus: 'server'` debug-command table (Task 4.2) — one worked example,
|
|
63
|
+
* `give-score`, mirroring the client-side `DataSpinner.spinner.reset` fixture
|
|
64
|
+
* (`src/scripts/components/data-spinner.ts`): mutates the AUTHORITATIVE
|
|
65
|
+
* (server) copy of state, never client prediction. Gated by
|
|
66
|
+
* `allowDebugCommands` — see `onCreate` — so a production `npm run server`
|
|
67
|
+
* boot never accepts these unless explicitly opted in.
|
|
68
|
+
*/
|
|
69
|
+
const DEBUG_COMMANDS: Record<string, (room: GameRoom, client: Client, args: unknown[]) => unknown> =
|
|
70
|
+
{
|
|
71
|
+
'give-score': (room, client, args) => {
|
|
72
|
+
const amount = typeof args[0] === 'number' ? args[0] : 1;
|
|
73
|
+
const player = room.state.players.get(client.sessionId);
|
|
74
|
+
if (!player) throw new Error(`give-score: no player for session "${client.sessionId}"`);
|
|
75
|
+
player.score += amount;
|
|
76
|
+
return { score: player.score };
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
57
80
|
function randomSpawnPosition(): { x: number; y: number; z: number } {
|
|
58
81
|
const angle = Math.random() * Math.PI * 2;
|
|
59
82
|
return {
|
|
@@ -83,7 +106,14 @@ export class GameRoom extends Room {
|
|
|
83
106
|
// from `setSimulationInterval` — it exists in both runtimes.
|
|
84
107
|
private orbRespawnTicks: number[] = [];
|
|
85
108
|
|
|
86
|
-
onCreate() {
|
|
109
|
+
onCreate(options?: { allowDebugCommands?: boolean }) {
|
|
110
|
+
// Off by default (FROZEN DECISION: env-var OR room-option) — `vgai probe`/
|
|
111
|
+
// the loopback harness (`../loopback.ts`) opt in via the room option; a
|
|
112
|
+
// plain `npm run server` boot never accepts debug commands unless
|
|
113
|
+
// VGAI_ALLOW_DEBUG_COMMANDS is set in its environment.
|
|
114
|
+
const debugCommandsAllowed =
|
|
115
|
+
options?.allowDebugCommands === true || Boolean(process.env['VGAI_ALLOW_DEBUG_COMMANDS']);
|
|
116
|
+
|
|
87
117
|
// Handle client messages
|
|
88
118
|
this.onMessage('position', (client, position: { x: number; y: number; z: number }) => {
|
|
89
119
|
const player = this.state.players.get(client.sessionId);
|
|
@@ -94,6 +124,43 @@ export class GameRoom extends Room {
|
|
|
94
124
|
}
|
|
95
125
|
});
|
|
96
126
|
|
|
127
|
+
this.onMessage(
|
|
128
|
+
'__vgai:debugCommand',
|
|
129
|
+
(client, message: { name: string; args?: unknown[]; requestId: string }) => {
|
|
130
|
+
if (!debugCommandsAllowed) {
|
|
131
|
+
client.send('__vgai:debugCommandResult', {
|
|
132
|
+
requestId: message.requestId,
|
|
133
|
+
ok: false,
|
|
134
|
+
error: 'debug commands disabled',
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const handler = DEBUG_COMMANDS[message.name];
|
|
139
|
+
if (!handler) {
|
|
140
|
+
client.send('__vgai:debugCommandResult', {
|
|
141
|
+
requestId: message.requestId,
|
|
142
|
+
ok: false,
|
|
143
|
+
error: `debug: no server command registered under "${message.name}"`,
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const result = handler(this, client, message.args ?? []);
|
|
149
|
+
client.send('__vgai:debugCommandResult', {
|
|
150
|
+
requestId: message.requestId,
|
|
151
|
+
ok: true,
|
|
152
|
+
result,
|
|
153
|
+
});
|
|
154
|
+
} catch (err) {
|
|
155
|
+
client.send('__vgai:debugCommandResult', {
|
|
156
|
+
requestId: message.requestId,
|
|
157
|
+
ok: false,
|
|
158
|
+
error: err instanceof Error ? err.message : String(err),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
|
|
97
164
|
// Spawn initial orbs
|
|
98
165
|
for (let i = 0; i < MAX_ORBS; i++) {
|
|
99
166
|
this.spawnOrb();
|
|
@@ -8,7 +8,7 @@ copy that trio for each new asset and register it in `assets.ts` (one list,
|
|
|
8
8
|
feeding both `npm run emit-schemas` and the build-time check that fails
|
|
9
9
|
`npm run build` loud on invalid data or a dangling `"file#key"` ref; `vgai
|
|
10
10
|
doctor <folder>` runs the same checks). Never hardcode a tunable as a constant in
|
|
11
|
-
game code — see "Tunable values → a data asset" in this project's
|
|
11
|
+
game code — see "Tunable values → a data asset" in this project's AGENTS.md,
|
|
12
12
|
and `docs/DATA-TOOLS-DESIGN.md` in the engine checkout for the design.
|
|
13
13
|
|
|
14
14
|
Two blessed shapes, and only these (they're what the editor's DATA tab can
|
|
@@ -31,10 +31,15 @@ written; fix the sheet and re-import. `"$schema"` is never a CSV column —
|
|
|
31
31
|
it's carried over from the file automatically.
|
|
32
32
|
|
|
33
33
|
Cross-asset fields (one table's row pointing at another asset — an enemy's
|
|
34
|
-
drop pointing at an item) should use `dataRef('<target-stem>')`
|
|
35
|
-
|
|
34
|
+
drop pointing at an item) should use `dataRef('<target-stem>')` —
|
|
35
|
+
`import { dataRef } from './data-ref'` (see `./data-ref.ts`; never
|
|
36
|
+
`@engine/data/data-ref` or `@vgai/engine/data/data-ref` directly — a schema
|
|
37
|
+
file is reached from `vite.config.ts` at Vite config-load time, which only
|
|
38
|
+
bundles relative imports, so `./data-ref` is the one import spelling that
|
|
39
|
+
resolves there too) — in the schema instead of a bare `z.string()`: the
|
|
36
40
|
DATA tab then renders that field as a `stem#key` PICKER (listing the target
|
|
37
41
|
file's own keys) instead of free text, and `vgai doctor`/`npm run build`
|
|
38
42
|
catch it if the target file is ever renamed or deleted — not just if the key
|
|
39
43
|
inside it goes missing. Read it back with `getRef(targetHandle, row.field)`
|
|
40
|
-
(pass the target's own `defineData` handle, not a
|
|
44
|
+
(also from `./data-ref`; pass the target's own `defineData` handle, not a
|
|
45
|
+
string) from game code.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports the engine's `dataRef`/`getRef` (docs/DATA-TOOLS-DESIGN.md §2.2)
|
|
3
|
+
* — the ONE place a `*.schema.ts` should import them from. Import via
|
|
4
|
+
* `from './data-ref'` (a plain, project-internal relative import — every
|
|
5
|
+
* `*.schema.ts` lives right next to this file in `src/data/`), never
|
|
6
|
+
* `@engine/data/data-ref` or `@vgai/engine/data/data-ref` directly.
|
|
7
|
+
*
|
|
8
|
+
* Why this file has to exist at all, ENGINE-RELATIVE (not a bare specifier),
|
|
9
|
+
* same reasoning as `vite.config.ts`'s `vgaiDataCheck` import: this project's
|
|
10
|
+
* data-asset schemas (`src/data/*.schema.ts`) are reached from
|
|
11
|
+
* `vite.config.ts` itself (via `./src/data/assets`) when Vite loads its OWN
|
|
12
|
+
* config, so `npm run build`'s dangling-ref gate can run the EXACT executable
|
|
13
|
+
* Zod schema (§9.1) — and that config-load step only BUNDLES relative
|
|
14
|
+
* imports. A bare specifier is externalized and handed to plain node, which
|
|
15
|
+
* cannot load the engine's shipped TypeScript source either way:
|
|
16
|
+
* `@engine/...` isn't even a real package outside Vite's app-build alias
|
|
17
|
+
* (which doesn't exist yet while Vite is still loading its own config), and
|
|
18
|
+
* `@vgai/engine/...` IS a real, installed package, but its `exports` map
|
|
19
|
+
* points at raw `.ts` — a file plain node's ESM loader cannot parse without a
|
|
20
|
+
* loader hook (the same `ERR_UNKNOWN_FILE_EXTENSION` `vite.config.ts`'s own
|
|
21
|
+
* comment describes for `vgaiDataCheck`).
|
|
22
|
+
*
|
|
23
|
+
* A relative import is the only thing that resolves in every context that
|
|
24
|
+
* touches a `dataRef` field: `tsc`, the app's Vite build, Vite's config-load
|
|
25
|
+
* (the one bare specifiers break), the editor (which aliases `@engine/*` for
|
|
26
|
+
* its OWN app build, but never for config-load either), and `tsx` (`npm run
|
|
27
|
+
* emit-schemas`, `vgai doctor`). Routing every schema file through this ONE
|
|
28
|
+
* module — instead of each one spelling out its own checkout-relative path —
|
|
29
|
+
* means only this file's import needs rewriting per project: the scaffolder
|
|
30
|
+
* does it automatically (`create-vgai-project/src/scaffold.ts`'s
|
|
31
|
+
* `rewriteDataRefModule`, mirroring `rewriteViteConfig`), the same way it
|
|
32
|
+
* already rewrites `vite.config.ts`'s engine-relative import. Never hand-edit
|
|
33
|
+
* the import below — `vgai upgrade` re-syncs it too.
|
|
34
|
+
*/
|
|
35
|
+
export { dataRef, getRef, type DataRef } from '../../../../engine/src/data/data-ref';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* annotations `GameComponent.static schema` uses. They document the file,
|
|
16
16
|
* constrain edits, and (W2+) become editor widgets.
|
|
17
17
|
*
|
|
18
|
-
* Rule of thumb (project
|
|
18
|
+
* Rule of thumb (project AGENTS.md carries the doctrine): if a designer
|
|
19
19
|
* should be able to change it, it belongs here — NOT as a constant in a
|
|
20
20
|
* component. Per-entity placement config stays on component schemas.
|
|
21
21
|
*/
|