@vgai/editor 0.2.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/LICENSE +661 -0
- package/README.md +35 -0
- package/dist/a/b/c/test-nested.json +1 -0
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
- package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
- package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
- package/dist/assets/browserAll-CEkWYlFk.js +1 -0
- package/dist/assets/collision-world-DniMB0qq.glb +0 -0
- package/dist/assets/create-runtime-DYIWr3va.js +1 -0
- package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
- package/dist/assets/esbuild-BHljloGq.wasm +0 -0
- package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
- package/dist/assets/game-8SDZPPgH.js +1 -0
- package/dist/assets/game-BI111o5f.js +10 -0
- package/dist/assets/game-CevorxyV.js +1 -0
- package/dist/assets/game-Di1H0gFj.js +1 -0
- package/dist/assets/game-Di7XlywD.js +1 -0
- package/dist/assets/game-DjnCaw8R.js +1 -0
- package/dist/assets/game-Q9A0aXSV.js +1 -0
- package/dist/assets/game-pt_1lQLv.js +1 -0
- package/dist/assets/game-source-CvidN7AB.js +1 -0
- package/dist/assets/game-xsOho_wg.js +83 -0
- package/dist/assets/index-Bp5cecD6.js +12 -0
- package/dist/assets/index-CPVV6pjw.js +1 -0
- package/dist/assets/index-CpOnYyeD.js +213 -0
- package/dist/assets/index-Cs8xwv3e.js +1 -0
- package/dist/assets/index-DL7RcQ0n.js +27 -0
- package/dist/assets/index-Dr8H_6Rl.js +1 -0
- package/dist/assets/index-_bCaipUk.js +13650 -0
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
- package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
- package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
- package/dist/assets/scene-query-BivAHhJu.js +1 -0
- package/dist/assets/stats.module--VATS4Kh.js +1 -0
- package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
- package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
- package/dist/index.html +37 -0
- package/dist/ingest/racing-game/UPSTREAM.md +68 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/index.css +1 -0
- package/dist/ingest/racing-game/images/bronze.png +0 -0
- package/dist/ingest/racing-game/images/gold.png +0 -0
- package/dist/ingest/racing-game/images/silver.png +0 -0
- package/dist/ingest/racing-game/index.html +15 -0
- package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
- package/dist/ingest/racing-game/models/track-draco.glb +0 -0
- package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
- package/dist/ingest/racing-game/rg-entry.js +1 -0
- package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
- package/dist/ingest/racing-game/textures/cursor.svg +3 -0
- package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
- package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
- package/dist/ingest/racing-game/textures/mask.svg +11 -0
- package/dist/ingest/racing-game/textures/no_sound.png +0 -0
- package/dist/ingest/racing-game/three-r139.module.js +50617 -0
- package/dist/ingest/racing-game/vendor.js +179 -0
- package/dist/ingest/racing-game/vgai.game.json +20 -0
- package/dist/ingest/simcity/UPSTREAM.md +65 -0
- package/dist/ingest/simcity/favicon.ico +0 -0
- package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
- package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
- package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
- package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
- package/dist/ingest/simcity/icons/bulldozer.png +0 -0
- package/dist/ingest/simcity/icons/calendar.png +0 -0
- package/dist/ingest/simcity/icons/factory-color.png +0 -0
- package/dist/ingest/simcity/icons/factory.png +0 -0
- package/dist/ingest/simcity/icons/house-color.png +0 -0
- package/dist/ingest/simcity/icons/house.png +0 -0
- package/dist/ingest/simcity/icons/job.png +0 -0
- package/dist/ingest/simcity/icons/office.png +0 -0
- package/dist/ingest/simcity/icons/pause-color.png +0 -0
- package/dist/ingest/simcity/icons/pause.png +0 -0
- package/dist/ingest/simcity/icons/person.png +0 -0
- package/dist/ingest/simcity/icons/play-color.png +0 -0
- package/dist/ingest/simcity/icons/play.png +0 -0
- package/dist/ingest/simcity/icons/power-color.png +0 -0
- package/dist/ingest/simcity/icons/power-line-color.png +0 -0
- package/dist/ingest/simcity/icons/power.png +0 -0
- package/dist/ingest/simcity/icons/road-color.png +0 -0
- package/dist/ingest/simcity/icons/road.png +0 -0
- package/dist/ingest/simcity/icons/select-color.png +0 -0
- package/dist/ingest/simcity/icons/select.png +0 -0
- package/dist/ingest/simcity/icons/store-color.png +0 -0
- package/dist/ingest/simcity/index.html +94 -0
- package/dist/ingest/simcity/index.js +58 -0
- package/dist/ingest/simcity/main.css +233 -0
- package/dist/ingest/simcity/models/armored-truck.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
- package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
- package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
- package/dist/ingest/simcity/models/building-cafe.glb +0 -0
- package/dist/ingest/simcity/models/building-casino.glb +0 -0
- package/dist/ingest/simcity/models/building-cinema.glb +0 -0
- package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
- package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
- package/dist/ingest/simcity/models/building-office-big.glb +0 -0
- package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
- package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
- package/dist/ingest/simcity/models/building-office.glb +0 -0
- package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
- package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
- package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
- package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
- package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger.glb +0 -0
- package/dist/ingest/simcity/models/car-police.glb +0 -0
- package/dist/ingest/simcity/models/car-taxi.glb +0 -0
- package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
- package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
- package/dist/ingest/simcity/models/car-veteran.glb +0 -0
- package/dist/ingest/simcity/models/construction-small.glb +0 -0
- package/dist/ingest/simcity/models/data-center.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory.glb +0 -0
- package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
- package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
- package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
- package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
- package/dist/ingest/simcity/models/truck.glb +0 -0
- package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
- package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
- package/dist/ingest/simcity/textures/base.png +0 -0
- package/dist/ingest/simcity/textures/grid.png +0 -0
- package/dist/ingest/simcity/textures/specular.png +0 -0
- package/dist/ingest/simcity/three-r155.module.js +51466 -0
- package/dist/ingest/simcity/vgai.game.json +20 -0
- package/dist/ingest/tanks/UPSTREAM.md +69 -0
- package/dist/ingest/tanks/index.html +214 -0
- package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
- package/dist/ingest/tanks/tanks-entry.js +92 -0
- package/dist/ingest/tanks/three-r170.module.js +54571 -0
- package/dist/ingest/tanks/vgai.game.json +20 -0
- package/dist/inputmaps/default.inputmap.json +41 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/dist/scenes/untitled.vscn.json +29 -0
- package/dist/textures/waternormals.jpg +0 -0
- package/dist-server/packaged.mjs +18155 -0
- package/package.json +36 -0
- package/server/asset-library-routes.ts +833 -0
- package/server/canonical-path.ts +50 -0
- package/server/cloud-asset-catalog.ts +101 -0
- package/server/dev.ts +350 -0
- package/server/editor-server.ts +1537 -0
- package/server/editor-sse.ts +73 -0
- package/server/game-globals-shadow.ts +58 -0
- package/server/local-asset-catalog.ts +163 -0
- package/server/packaged.ts +571 -0
- package/server/prod.ts +75 -0
- package/server/project-optimize-deps-entries.ts +149 -0
- package/server/project-view.ts +65 -0
- package/server/server-utils.ts +430 -0
- package/server/session-registry.ts +93 -0
- package/src/EditorContext.tsx +87 -0
- package/src/achieved-tier.ts +126 -0
- package/src/action-registry.ts +268 -0
- package/src/adapter-resolver.ts +1858 -0
- package/src/asset-events.ts +100 -0
- package/src/authoring/active-adapter.ts +98 -0
- package/src/authoring/active-systems.ts +50 -0
- package/src/authoring/boundary-authoring-adapter.ts +178 -0
- package/src/authoring/composite-authoring-adapter.ts +891 -0
- package/src/authoring/design-time-layers.ts +776 -0
- package/src/authoring/edit-mode-authoring.ts +643 -0
- package/src/authoring/embed-only-adapter.ts +57 -0
- package/src/authoring/ephemeral-persistence.ts +26 -0
- package/src/authoring/eyedropper-session.ts +69 -0
- package/src/authoring/first-party-hierarchy-menu.ts +74 -0
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
- package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
- package/src/authoring/first-party-inspector/register.tsx +108 -0
- package/src/authoring/ingest-authoring-adapter.ts +475 -0
- package/src/authoring/ingest-capture.ts +94 -0
- package/src/authoring/ingest-game-adapter.ts +291 -0
- package/src/authoring/ingest-iframe-adapter.ts +38 -0
- package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
- package/src/authoring/ingest-overlay.ts +193 -0
- package/src/authoring/layered-pick.ts +87 -0
- package/src/authoring/mount-failure-report.ts +112 -0
- package/src/authoring/overlay-report.ts +52 -0
- package/src/authoring/project-scenes.ts +111 -0
- package/src/authoring/react-dom-authoring-adapter.ts +795 -0
- package/src/authoring/react-inspector-section.tsx +1041 -0
- package/src/authoring/react-world-authoring-adapter.ts +2400 -0
- package/src/authoring/root-activation.ts +61 -0
- package/src/authoring/scene-ui-layer.ts +485 -0
- package/src/authoring/story-overlay.ts +48 -0
- package/src/authoring/ui-authoring-adapter.ts +325 -0
- package/src/authoring/ui-hierarchy-menu.ts +40 -0
- package/src/authoring/ui-inspector-sections.tsx +156 -0
- package/src/authoring/undo-timeline.ts +145 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
- package/src/authoring/viewport-pick-context.ts +31 -0
- package/src/authoring/viewport-raycast.ts +80 -0
- package/src/authoring/world-overlay.ts +76 -0
- package/src/authoring/world-pan-state.ts +115 -0
- package/src/authoring/world-session-state.ts +102 -0
- package/src/authoring/world2d-authoring-adapter.ts +313 -0
- package/src/browser-external-watch.ts +98 -0
- package/src/browser-play-entry.ts +112 -0
- package/src/browser-project-scripts.ts +112 -0
- package/src/browser-transpile.ts +246 -0
- package/src/browser-watch-reload.ts +100 -0
- package/src/command-listener.ts +326 -0
- package/src/components/AlignToolbar.tsx +189 -0
- package/src/components/AppRoot.tsx +73 -0
- package/src/components/AssetBrowser.tsx +502 -0
- package/src/components/BottomPanel.tsx +326 -0
- package/src/components/BuildPanel.tsx +455 -0
- package/src/components/CameraInfo.tsx +56 -0
- package/src/components/CommandPalette.tsx +265 -0
- package/src/components/ConsolePanel.tsx +272 -0
- package/src/components/DataPanel.tsx +1268 -0
- package/src/components/DefaultEditorLayout.tsx +509 -0
- package/src/components/FolderTree.tsx +179 -0
- package/src/components/GameHierarchy.tsx +1349 -0
- package/src/components/Inspector.tsx +671 -0
- package/src/components/InspectorToolSection.tsx +74 -0
- package/src/components/MountFailureBanner.tsx +110 -0
- package/src/components/NetworkSection.tsx +109 -0
- package/src/components/OnlineAssetBrowser.tsx +448 -0
- package/src/components/OverlayReportBanner.tsx +122 -0
- package/src/components/PlayBar.tsx +356 -0
- package/src/components/PrefabSaveDialog.tsx +201 -0
- package/src/components/ProjectHeader.tsx +174 -0
- package/src/components/ProjectScreen.tsx +1004 -0
- package/src/components/RightPanel.tsx +166 -0
- package/src/components/RootActivateDialog.tsx +105 -0
- package/src/components/SaveDialog.tsx +197 -0
- package/src/components/SaveStatus.tsx +69 -0
- package/src/components/StatsOverlay.tsx +35 -0
- package/src/components/ToolHost.tsx +80 -0
- package/src/components/Toolbar.tsx +307 -0
- package/src/components/ViewportOverlay.tsx +234 -0
- package/src/components/ViewportPanel.tsx +369 -0
- package/src/components/WorldContextMenu.tsx +257 -0
- package/src/components/WorldSelectionOverlay.tsx +2003 -0
- package/src/components/WorldTextEditor.tsx +86 -0
- package/src/components/asset-viewers/AssetViewer.tsx +38 -0
- package/src/components/asset-viewers/AudioViewer.tsx +149 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
- package/src/components/asset-viewers/ImageViewer.tsx +65 -0
- package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
- package/src/components/asset-viewers/ModelViewer.tsx +180 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
- package/src/components/data-csv.ts +301 -0
- package/src/components/entity-name-commit.ts +18 -0
- package/src/components/inspector-property-grouping.ts +65 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
- package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
- package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
- package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
- package/src/components/inspector-widgets/FontPicker.tsx +196 -0
- package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
- package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
- package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
- package/src/components/inspector-widgets/color-utils.ts +202 -0
- package/src/components/inspector-widgets/gallery-main.tsx +167 -0
- package/src/components/inspector-widgets/index.ts +22 -0
- package/src/components/inspector-widgets/shared.tsx +135 -0
- package/src/components/inspectors/AnimationSection.tsx +143 -0
- package/src/components/inspectors/AudioSection.tsx +215 -0
- package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
- package/src/components/inspectors/CameraSection.tsx +105 -0
- package/src/components/inspectors/ComponentsSection.tsx +358 -0
- package/src/components/inspectors/JointsSection.tsx +255 -0
- package/src/components/inspectors/LightSection.tsx +116 -0
- package/src/components/inspectors/MaterialSection.tsx +1189 -0
- package/src/components/inspectors/MeshSection.tsx +99 -0
- package/src/components/inspectors/NavigationSection.tsx +58 -0
- package/src/components/inspectors/ParticleSection.tsx +681 -0
- package/src/components/inspectors/PhysicsSection.tsx +326 -0
- package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
- package/src/components/inspectors/RenderingSection.tsx +109 -0
- package/src/components/inspectors/ShadowSection.tsx +84 -0
- package/src/components/inspectors/SplineSection.tsx +191 -0
- package/src/components/inspectors/TransformSection.tsx +191 -0
- package/src/components/inspectors/json-schema-fields.ts +281 -0
- package/src/components/inspectors/multi-edit-utils.ts +49 -0
- package/src/components/inspectors/zod-fields.ts +184 -0
- package/src/components/primitives/ColorInput.tsx +109 -0
- package/src/components/primitives/JsonInput.tsx +98 -0
- package/src/components/primitives/NumberInput.tsx +164 -0
- package/src/components/primitives/Panel.tsx +68 -0
- package/src/components/primitives/SectionHeader.tsx +45 -0
- package/src/components/primitives/Tooltip.tsx +82 -0
- package/src/components/primitives/Vec3Input.tsx +62 -0
- package/src/components/world-overlay-gestures.ts +1438 -0
- package/src/doctor/data-checks.ts +285 -0
- package/src/doctor/report.ts +386 -0
- package/src/editor-api.ts +700 -0
- package/src/editor-console.ts +194 -0
- package/src/editor-hotkeys.ts +441 -0
- package/src/editor-layers.ts +2 -0
- package/src/editor-mode.ts +28 -0
- package/src/editor-store.ts +2225 -0
- package/src/editor-viewport.ts +1924 -0
- package/src/entity-factory.ts +221 -0
- package/src/game-globals-prelude.ts +27 -0
- package/src/gated-globals.ts +131 -0
- package/src/generated/setup-by-example.generated.ts +53 -0
- package/src/gizmo-registry.ts +608 -0
- package/src/handle-system.ts +221 -0
- package/src/hierarchy-menu-registry.ts +70 -0
- package/src/hierarchy-rows.ts +73 -0
- package/src/hosted-example.ts +160 -0
- package/src/hotkeys.ts +142 -0
- package/src/ingest/discovery-react.ts +132 -0
- package/src/ingest/discovery.ts +205 -0
- package/src/ingest/discovery2d.ts +204 -0
- package/src/ingest/esm-entry-resolution.ts +102 -0
- package/src/ingest/games/bloom-composer/game.js +99 -0
- package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
- package/src/ingest/games/embed-bundle/embed.html +10 -0
- package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
- package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
- package/src/ingest/games/games-fps/game.js +479 -0
- package/src/ingest/games/games-fps/vgai.game.json +24 -0
- package/src/ingest/games/iframe-reachable/game-source.js +58 -0
- package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
- package/src/ingest/games/interactive-cubes/game.js +139 -0
- package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
- package/src/ingest/games/mismatched-three/fake-three.js +38 -0
- package/src/ingest/games/mismatched-three/game.js +10 -0
- package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
- package/src/ingest/games/own-three-bundled/game.js +43 -0
- package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
- package/src/ingest/games/pointerlock-maze/game.js +288 -0
- package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
- package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
- package/src/ingest/games-2d/bunnymark/game.ts +88 -0
- package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy/game.ts +148 -0
- package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
- package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
- package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
- package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
- package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
- package/src/ingest/host-namespace-registry.ts +196 -0
- package/src/ingest/types.ts +127 -0
- package/src/ingest-hierarchy-rows.ts +207 -0
- package/src/ingest-mode.ts +2092 -0
- package/src/ingest-siblings.ts +466 -0
- package/src/initial-scene.ts +194 -0
- package/src/inspector-section-registry.ts +117 -0
- package/src/interactive-edit-scope.ts +34 -0
- package/src/main.tsx +23 -0
- package/src/manifest-project.ts +43 -0
- package/src/model-thumbnail.ts +156 -0
- package/src/module-mode.ts +513 -0
- package/src/navmesh-handler.ts +94 -0
- package/src/packaged-runtime.ts +49 -0
- package/src/play-mode.ts +1148 -0
- package/src/playground/main.ts +206 -0
- package/src/project-manager.ts +173 -0
- package/src/scene-diff.ts +112 -0
- package/src/scene-serializer.ts +144 -0
- package/src/scene-sync.ts +587 -0
- package/src/scene-ui-mode/active-scene-ui.ts +58 -0
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
- package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
- package/src/script-loader.ts +143 -0
- package/src/storage/fsa-storage.ts +55 -0
- package/src/storage/handle-storage.ts +150 -0
- package/src/storage/handle-store.ts +116 -0
- package/src/storage/http-storage.ts +137 -0
- package/src/storage/index.ts +147 -0
- package/src/storage/mem-storage.ts +135 -0
- package/src/storage/opfs-storage.ts +25 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/seed.ts +106 -0
- package/src/storage/types.ts +110 -0
- package/src/tool-loader.ts +291 -0
- package/src/ui-editor/inspector.tsx +563 -0
- package/src/ui-editor/node-ops.ts +266 -0
- package/src/ui-editor/overlay.tsx +380 -0
- package/src/ui-editor/palette.tsx +114 -0
- package/src/ui-editor/react-store.ts +78 -0
- package/src/ui-editor/timeline-panel.tsx +443 -0
- package/src/ui-editor/ui-edit-store.ts +552 -0
- package/src/ui-editor/ui-prefab.ts +95 -0
- package/src/ui-source/inspect.ts +544 -0
- package/src/ui-source/oid-transform.ts +133 -0
- package/src/ui-source/source-write-backend.ts +203 -0
- package/src/ui-source/tailwind-map.ts +174 -0
- package/src/ui-source/writer.ts +1175 -0
- package/src/widgets/index.ts +73 -0
- package/template/.claude/skills/editor/SKILL.md +252 -0
- package/template/CLAUDE.md +512 -0
- package/template/LICENSE +30 -0
- package/template/emit-schemas.ts +42 -0
- package/template/index.html +17 -0
- package/template/package.json +53 -0
- package/template/project.json +9 -0
- package/template/public/audio/ambient-arena.wav +0 -0
- package/template/public/audio/ambient-music.wav +0 -0
- package/template/public/audio/ambient-nature.wav +0 -0
- package/template/public/audio/ambient-wind.wav +0 -0
- package/template/public/inputmaps/default.inputmap.json +41 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/template/public/scenes/default.vscn.json +43 -0
- package/template/public/scenes/untitled.vscn.json +29 -0
- package/template/public/textures/waternormals.jpg +0 -0
- package/template/server/colyseus-setup.ts +85 -0
- package/template/server/main.ts +34 -0
- package/template/server/rooms/arena-room.ts +420 -0
- package/template/server/rooms/game-room.ts +162 -0
- package/template/server/rooms.ts +15 -0
- package/template/server/tsconfig.json +19 -0
- package/template/src/data/README.md +40 -0
- package/template/src/data/assets.ts +31 -0
- package/template/src/data/tuning.data.json +5 -0
- package/template/src/data/tuning.schema.json +21 -0
- package/template/src/data/tuning.schema.ts +34 -0
- package/template/src/data/tuning.ts +24 -0
- package/template/src/main.ts +157 -0
- package/template/src/scripts/components/data-spinner.ts +45 -0
- package/template/src/scripts/components/scene-camera.ts +40 -0
- package/template/src/scripts/main.ts +94 -0
- package/template/src/scripts/registry.ts +46 -0
- package/template/src/tools/README.md +14 -0
- package/template/src/tools/example.tool.tsx +91 -0
- package/template/src/tools/spin.tool.tsx +101 -0
- package/template/src/ui/Button.tsx +27 -0
- package/template/src/ui/DefaultHud.tsx +40 -0
- package/template/src/ui/KeyHint.tsx +25 -0
- package/template/src/ui/Panel.tsx +52 -0
- package/template/src/ui/RebindPanel.tsx +129 -0
- package/template/src/ui/Slider.tsx +28 -0
- package/template/src/ui/StatusDot.tsx +22 -0
- package/template/src/ui/Toggle.tsx +19 -0
- package/template/src/ui/game-state.tsx +37 -0
- package/template/src/ui/index.ts +7 -0
- package/template/src/ui/mount-default-hud.tsx +19 -0
- package/template/src/ui/mount-ui.ts +15 -0
- package/template/tsconfig.json +36 -0
- package/template/validate-asset-content.ts +78 -0
- package/template/validate-manifest.ts +55 -0
- package/template/validate-scenes.ts +211 -0
- package/template/vgai.game.json +21 -0
- package/template/vite.config.ts +128 -0
- package/vite-plugin-example-assets.ts +60 -0
- package/vite-plugin-game-globals.ts +46 -0
- package/vite-plugin-game-static.ts +71 -0
- package/vite-plugin-project-game-static.ts +84 -0
- package/vite-plugin-react-game-static.ts +98 -0
- package/vite-plugin-react-world-provider.ts +81 -0
- package/vite-plugin-react-world-runtime.ts +76 -0
- package/vite-plugin-ui-oid.ts +561 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-adapter auto-mount route (T3.4 slice 2, docs/ADAPTER-AUTHORING-
|
|
3
|
+
* DESIGN.md §2/§3 slice 2) — the `{ module }` sibling of `ingest-mode.ts`'s
|
|
4
|
+
* `tryManifestIngestRoute`/`tryManifestIngestRoute2D`.
|
|
5
|
+
*
|
|
6
|
+
* A manifest-backed project whose FIRST world declares a custom `{ module }`
|
|
7
|
+
* adapter auto-mounts it through the generic `GameAdapter.mount(HostContext)`
|
|
8
|
+
* contract — `createGameRuntime` (the SAME engine-side host `play-mode.ts`'s
|
|
9
|
+
* legacy single-world Play branch uses) — then installs the MOUNTED game's
|
|
10
|
+
* OWN `AuthoringAdapter` (`MountedGame.authoring`) as the active authoring
|
|
11
|
+
* override (`setActiveAuthoring`).
|
|
12
|
+
*
|
|
13
|
+
* FINDING (report prominently — see this slice's "Report back"): this is a
|
|
14
|
+
* gap `play-mode.ts`'s existing `resolveWorldAdapter` call (its single-world
|
|
15
|
+
* Play branch) does NOT close. That branch always calls
|
|
16
|
+
* `installPlayAuthoring(store)`, which drives the hierarchy/inspector from
|
|
17
|
+
* `getActiveAuthoring(store)` — i.e. the FIRST-PARTY `.vscn`-over-`store`
|
|
18
|
+
* adapter — never from `session.mounted.authoring`. That's the right choice
|
|
19
|
+
* for `default-three`/`default-pixi`/`default-react` worlds (their mounted
|
|
20
|
+
* scene mirrors the store's OWN document), but it is WRONG for a `{ module }`
|
|
21
|
+
* world: a custom adapter builds its scene from its own code, so driving the
|
|
22
|
+
* inspector from the store's `.vscn` document would show an unrelated (empty,
|
|
23
|
+
* for a fresh CLI-on-folder project) tree instead of the module's real
|
|
24
|
+
* objects. `play-mode.ts` is out of scope for this slice (sibling agent's
|
|
25
|
+
* region), so rather than leave `{ module }` worlds unreachable through the
|
|
26
|
+
* real editor UI at all, this file gives them their OWN auto-mount route —
|
|
27
|
+
* mirroring ingest mode's already-established shape (auto-mount on load,
|
|
28
|
+
* install the mounted adapter as the override, swap the viewport onto the
|
|
29
|
+
* live scene) instead of Play mode's.
|
|
30
|
+
*
|
|
31
|
+
* This route reuses the SAME generic, adapter-driven `GameHierarchy`/
|
|
32
|
+
* `Inspector` panels ingest mode renders — the hierarchy panel (A2) is
|
|
33
|
+
* unconditionally contract-only; the inspector (C2, `components/
|
|
34
|
+
* Inspector.tsx`) matches registered sections per SELECTED NODE via
|
|
35
|
+
* `inspector-section-registry.ts`, and a bare `{ module }` override is never
|
|
36
|
+
* a first-party `VgaiSceneAuthoringAdapter`, so its nodes never match the
|
|
37
|
+
* first-party registrations and always render the generic built-in blocks —
|
|
38
|
+
* so a module adapter's hierarchy/inspector/gizmo reach the SAME editor UI
|
|
39
|
+
* ingest games do, with NO
|
|
40
|
+
* new panel code. The one piece those panels need that ingest's OWN capture
|
|
41
|
+
* machinery provides for free — `userData.entityId` tags on every live
|
|
42
|
+
* `Object3D`, which the viewport's gizmo/selection binds on
|
|
43
|
+
* (`ViewportPanel.tsx`'s `viewport.objectMap = store.objectMap`, built by
|
|
44
|
+
* `EditorStore.enterPlayScene` from that tag) — this route tags itself, right
|
|
45
|
+
* after mount, straight from the adapter's OWN `HierarchyProvider` (walking
|
|
46
|
+
* `mounted.scene` and asking `idForObject3D` for each object) so a `{ module
|
|
47
|
+
* }` adapter author does not have to hand-roll that tagging themselves.
|
|
48
|
+
*
|
|
49
|
+
* D-Y1 (docs/EXTERNAL-REACT-AND-MODULE-DESIGN.md §1/§4 slice S1) adds the
|
|
50
|
+
* WORLDS-PATH sibling below (`enterModuleModeWorldsPath`): a single-world
|
|
51
|
+
* manifest whose first world is `kind: 'pixijs' | 'react'` with a `{ module }`
|
|
52
|
+
* adapter no longer dead-ends in the legacy `enterModuleModeFromManifestWorld`
|
|
53
|
+
* route above (whose `HostContext`/`mounted.scene` assumptions are threejs-
|
|
54
|
+
* only — the exact gap the D-X6 guard, still below, catches). The wave-2
|
|
55
|
+
* ground-truth study found `resolveAllWorlds` ALREADY resolves a `{ module }`
|
|
56
|
+
* world for every kind (its `'module'` switch case is kind-agnostic) and
|
|
57
|
+
* ALREADY hands it back as the correctly-kinded `WorldMountSpec` — so the fix
|
|
58
|
+
* is routing, not an engine change: mirror `ingest-mode.ts`'s
|
|
59
|
+
* `enterIngestModeReact` recipe (one-element `resolveAllWorlds` + worlds-path
|
|
60
|
+
* `createGameRuntime({ container, worlds })`) instead of the legacy single-
|
|
61
|
+
* adapter path. `tryManifestModuleRoute` below picks between the two based on
|
|
62
|
+
* `firstWorld.kind` — threejs module worlds keep calling
|
|
63
|
+
* `enterModuleModeFromManifestWorld` byte-for-byte; the D-X6 guard stays
|
|
64
|
+
* reachable (and its own tests unmodified) for any caller that still invokes
|
|
65
|
+
* that function directly with a non-three world.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
import type { AuthoringAdapter } from '@engine/adapter';
|
|
69
|
+
import type { ResolvedGameManifest, ResolvedWorldEntry } from '@engine/manifest/load';
|
|
70
|
+
import type { GameSession, WorldMountSpec } from '@engine/runtime/create-runtime';
|
|
71
|
+
import { setUserData } from '@engine/scene/user-data';
|
|
72
|
+
import type * as THREE from 'three';
|
|
73
|
+
import {
|
|
74
|
+
computeAchievedTier,
|
|
75
|
+
computeTierShortfall,
|
|
76
|
+
formatTierShortfallMessage,
|
|
77
|
+
type TierShortfallReport,
|
|
78
|
+
} from './achieved-tier';
|
|
79
|
+
import { resolveAllWorlds, resolveWorldAdapter } from './adapter-resolver';
|
|
80
|
+
import { setActiveAuthoring } from './authoring/active-adapter';
|
|
81
|
+
import { setActiveSystems } from './authoring/active-systems';
|
|
82
|
+
import { makeEmbedOnlyAdapter } from './authoring/embed-only-adapter';
|
|
83
|
+
import { formatMountFailureMessage, setMountFailureReport } from './authoring/mount-failure-report';
|
|
84
|
+
import { editorConsole } from './editor-console';
|
|
85
|
+
import { getEditorMode } from './editor-mode';
|
|
86
|
+
import type { EditorStore } from './editor-store';
|
|
87
|
+
import { waitForGameContainer } from './ingest-mode';
|
|
88
|
+
import { fetchGameManifest } from './manifest-project';
|
|
89
|
+
import { getGameContainer } from './play-mode';
|
|
90
|
+
import { getCurrentProject } from './project-manager';
|
|
91
|
+
|
|
92
|
+
interface ModuleSession {
|
|
93
|
+
store: EditorStore;
|
|
94
|
+
stop: () => void;
|
|
95
|
+
/** Absent on the worlds-path route (D-Y1 below) — that route owns no
|
|
96
|
+
* standalone canvas of its own; `createGameRuntime`'s worlds-path session
|
|
97
|
+
* builds and tears down its own per-world surface(s) directly inside the
|
|
98
|
+
* game container. Present (and removed on exit) only for the legacy
|
|
99
|
+
* threejs single-adapter path's own hand-created canvas. */
|
|
100
|
+
canvas?: HTMLCanvasElement | undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let _session: ModuleSession | null = null;
|
|
104
|
+
|
|
105
|
+
/** True if a `{ module }` adapter world is currently auto-mounted. */
|
|
106
|
+
export function isModuleModeActive(): boolean {
|
|
107
|
+
return _session !== null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Tag every live object a module adapter's hierarchy actually knows about with
|
|
111
|
+
* `userData.entityId` (the SAME key `IngestAuthoringAdapter`'s own capture
|
|
112
|
+
* uses) so the viewport's existing objectMap-keyed gizmo/selection binds to
|
|
113
|
+
* this adapter's objects with no adapter-author effort. Best-effort: an
|
|
114
|
+
* adapter with no `hierarchy` (opaque) has nothing to walk. */
|
|
115
|
+
function tagObjectsWithEntityIds(
|
|
116
|
+
scene: THREE.Scene,
|
|
117
|
+
idForObject3D: (o: THREE.Object3D) => string | null,
|
|
118
|
+
): void {
|
|
119
|
+
scene.traverse((o) => {
|
|
120
|
+
const id = idForObject3D(o);
|
|
121
|
+
if (id) setUserData(o, 'entityId', id);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Selection is editor-global UI state (the SAME rule `IngestAuthoringAdapter`'s
|
|
127
|
+
* own doc comment states) — a custom `{ module }` adapter has no `EditorStore`
|
|
128
|
+
* reference to delegate to (by design: `HostContext` is editor-neutral), so
|
|
129
|
+
* the hierarchy panel's row click (`adapter.selection?.set([node.id])`) would
|
|
130
|
+
* silently no-op without this. Wrap the mounted adapter with a store-backed
|
|
131
|
+
* `SelectionProvider` UNLESS it already declares its own (an adapter author
|
|
132
|
+
* who genuinely wants custom multi-select semantics is not overridden). This
|
|
133
|
+
* costs the adapter author nothing — they never see `EditorStore` at all.
|
|
134
|
+
*/
|
|
135
|
+
export function withStoreSelection(
|
|
136
|
+
authoring: AuthoringAdapter,
|
|
137
|
+
store: EditorStore,
|
|
138
|
+
): AuthoringAdapter {
|
|
139
|
+
if (authoring.selection) return authoring;
|
|
140
|
+
return {
|
|
141
|
+
...authoring,
|
|
142
|
+
selection: {
|
|
143
|
+
get: () => [...store.selectedEntityIds],
|
|
144
|
+
set: (ids) => store.selectMultiple(ids),
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Mount a manifest's `{ module }` world straight through `GameAdapter.mount`
|
|
151
|
+
* and install it as the editor's live authoring override. `world.capabilities
|
|
152
|
+
* .local` (always resolved/explicit for a module world — the loader requires
|
|
153
|
+
* it, `manifest/load.ts`'s `resolveCapabilities`) is the declared tier; the
|
|
154
|
+
* ACTUAL achieved tier is recomputed here (in addition to
|
|
155
|
+
* `resolveWorldAdapter`'s own internal, warn-only check, T3.4 slice 1) purely
|
|
156
|
+
* to expose the structured values on the `__vgaiModule` dev/e2e hook below —
|
|
157
|
+
* this does not log a second shortfall line, it only reads the same pure
|
|
158
|
+
* computation slice 1 already logs from inside the resolver.
|
|
159
|
+
*/
|
|
160
|
+
export async function enterModuleModeFromManifestWorld(
|
|
161
|
+
store: EditorStore,
|
|
162
|
+
world: ResolvedWorldEntry,
|
|
163
|
+
projectRoot: string,
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
const gameContainer = getGameContainer();
|
|
166
|
+
if (!gameContainer) {
|
|
167
|
+
editorConsole.error('Game container not mounted — cannot auto-mount module adapter', 'adapter');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
store.setPlayState('playing');
|
|
172
|
+
editorConsole.log(`Mounting custom adapter module for world "${world.id}"`, 'adapter');
|
|
173
|
+
|
|
174
|
+
// `resolveWorldAdapter`'s `module` branch (T3.4 slice 1) already: path-guards
|
|
175
|
+
// + shape-checks the module, then wraps its `mount` with the achieved-tier
|
|
176
|
+
// warn-only check (`withAchievedTierCheck`) — reused verbatim here, not
|
|
177
|
+
// duplicated.
|
|
178
|
+
const adapter = await resolveWorldAdapter(world, projectRoot);
|
|
179
|
+
|
|
180
|
+
const canvas = document.createElement('canvas');
|
|
181
|
+
canvas.style.cssText = 'width:100%;height:100%;display:block;';
|
|
182
|
+
gameContainer.appendChild(canvas);
|
|
183
|
+
const w = gameContainer.clientWidth || 800;
|
|
184
|
+
const h = gameContainer.clientHeight || 600;
|
|
185
|
+
|
|
186
|
+
const { createGameRuntime } = await import('@engine/runtime/create-runtime');
|
|
187
|
+
const session = await createGameRuntime({ canvas, width: w, height: h, adapter });
|
|
188
|
+
const mounted = session.mounted;
|
|
189
|
+
|
|
190
|
+
// D-X6 honesty guard (docs/EXTERNAL-FOLDER-INGEST-DESIGN.md §2/§6 slice S4):
|
|
191
|
+
// this route only wires up THREE.Scene-shaped worlds — everything below
|
|
192
|
+
// (`tagObjectsWithEntityIds`, `store.enterPlayScene`) assumes `mounted.scene`
|
|
193
|
+
// exists. A pixi/react `{ module }` world's mounted result has NO `.scene`
|
|
194
|
+
// at all (`MountedPixiWorld`/`MountedReactWorld`, `@engine/adapter` —
|
|
195
|
+
// `guardModuleShape` only checks the ADAPTER's `id`/`mount` shape, never
|
|
196
|
+
// the resolved MOUNT result's kind), so continuing silently would pass
|
|
197
|
+
// `undefined` into `enterPlayScene` — exactly the silent dead end D-X6
|
|
198
|
+
// replaces with a loud, named error (anti-shim / degrade-loudly). Trust
|
|
199
|
+
// the mounted result's OWN self-declared `kind` tag over the manifest's
|
|
200
|
+
// `world.kind` — it is what the module ACTUALLY returned; an absent `kind`
|
|
201
|
+
// (a pre-T7.5 module adapter that never had to declare one) is treated as
|
|
202
|
+
// the historical threejs-only default, so existing module fixtures are
|
|
203
|
+
// unaffected.
|
|
204
|
+
const mountedKind = (mounted as unknown as { kind?: string }).kind;
|
|
205
|
+
if (mountedKind !== undefined && mountedKind !== 'threejs') {
|
|
206
|
+
canvas.remove();
|
|
207
|
+
store.setPlayState('stopped');
|
|
208
|
+
throw new Error(
|
|
209
|
+
`enterModuleModeFromManifestWorld: world "${world.id}" (kind: "${mountedKind}") is a ` +
|
|
210
|
+
'{ module } adapter whose mounted result is not a threejs world — v1 module-mount ' +
|
|
211
|
+
'route only wires up THREE.Scene-shaped worlds into enterPlayScene ' +
|
|
212
|
+
'(docs/EXTERNAL-FOLDER-INGEST-DESIGN.md D-X6). Pixi/react { module } worlds have no ' +
|
|
213
|
+
'mount plumbing yet — kind-parameterized HostContext + per-kind module mounting is ' +
|
|
214
|
+
'wave 2, gated on its own design doc. This throws loudly instead of silently passing ' +
|
|
215
|
+
'`undefined` into enterPlayScene.',
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
_session = {
|
|
220
|
+
store,
|
|
221
|
+
canvas,
|
|
222
|
+
stop: () => session.stop(),
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
let activeAuthoring: AuthoringAdapter;
|
|
226
|
+
if (mounted.authoring) {
|
|
227
|
+
tagObjectsWithEntityIds(mounted.scene as THREE.Scene, (o) =>
|
|
228
|
+
mounted.authoring!.hierarchy.idForObject3D(o),
|
|
229
|
+
);
|
|
230
|
+
activeAuthoring = withStoreSelection(mounted.authoring, store);
|
|
231
|
+
} else {
|
|
232
|
+
// Honest floor for a module adapter that declares no authoring surface at
|
|
233
|
+
// all — same embed-only ceiling ingest's own capture-failed path uses.
|
|
234
|
+
activeAuthoring = makeEmbedOnlyAdapter(store, `${adapter.id} (no authoring surface)`);
|
|
235
|
+
}
|
|
236
|
+
setActiveAuthoring(activeAuthoring);
|
|
237
|
+
setActiveSystems(mounted.systems ?? {});
|
|
238
|
+
store.enterPlayScene(mounted.scene as THREE.Scene);
|
|
239
|
+
store.setActiveViewportTab('scene');
|
|
240
|
+
store.notifyIngestEdit();
|
|
241
|
+
setMountFailureReport(null); // D-W3: mount succeeded
|
|
242
|
+
|
|
243
|
+
const achievedTier = computeAchievedTier(mounted);
|
|
244
|
+
const shortfall: TierShortfallReport | null = computeTierShortfall(
|
|
245
|
+
world.id,
|
|
246
|
+
world.capabilities.local,
|
|
247
|
+
achievedTier,
|
|
248
|
+
);
|
|
249
|
+
// Belt-and-suspenders: resolveWorldAdapter's own wrapping already warned once
|
|
250
|
+
// via editorConsole (slice 1) — this does not warn again, only formats the
|
|
251
|
+
// SAME message onto the structured dev/e2e hook below for assertions.
|
|
252
|
+
if (shortfall) {
|
|
253
|
+
editorConsole.log(
|
|
254
|
+
`(already warned by the resolver) ${formatTierShortfallMessage(shortfall)}`,
|
|
255
|
+
'adapter',
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
(window as unknown as Record<string, unknown>)['__vgaiModule'] = {
|
|
260
|
+
worldId: world.id,
|
|
261
|
+
adapterId: adapter.id,
|
|
262
|
+
declaredTier: world.capabilities.local,
|
|
263
|
+
achievedTier,
|
|
264
|
+
...(shortfall ? { tierShortfall: shortfall } : {}),
|
|
265
|
+
// DEV-only: the ACTIVE authoring adapter (the store-backed-selection
|
|
266
|
+
// wrapper, when applied) + store, so e2e/proof can drive the SAME
|
|
267
|
+
// providers the hierarchy/inspector/gizmo use (mirrors `__vgaiIngest`).
|
|
268
|
+
...(import.meta.env.DEV ? { adapter: activeAuthoring, store } : {}),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Tear down the auto-mounted module session (mirrors `ingest-mode.ts`'s
|
|
273
|
+
* `exitIngestMode`). Not called by any route today (no e2e navigates away
|
|
274
|
+
* from a module-mounted project mid-session) — provided for symmetry/hygiene
|
|
275
|
+
* and any future caller (e.g. a project switch). */
|
|
276
|
+
export function exitModuleMode(): void {
|
|
277
|
+
const s = _session;
|
|
278
|
+
if (!s) return;
|
|
279
|
+
_session = null;
|
|
280
|
+
setActiveAuthoring(null);
|
|
281
|
+
setActiveSystems(null);
|
|
282
|
+
setMountFailureReport(null); // D-W3: no session -> no banner to show (mirrors ingest-mode.ts's exitIngestMode)
|
|
283
|
+
try {
|
|
284
|
+
s.store.exitPlayScene();
|
|
285
|
+
} catch {
|
|
286
|
+
/* scene may not have been swapped if mount failed */
|
|
287
|
+
}
|
|
288
|
+
s.store.setPlayState('stopped');
|
|
289
|
+
try {
|
|
290
|
+
s.stop();
|
|
291
|
+
} catch {
|
|
292
|
+
/* ignore */
|
|
293
|
+
}
|
|
294
|
+
s.canvas?.remove();
|
|
295
|
+
delete (window as unknown as Record<string, unknown>)['__vgaiModule'];
|
|
296
|
+
editorConsole.log('Module adapter mode stopped', 'adapter');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// D-Y1 (docs/EXTERNAL-REACT-AND-MODULE-DESIGN.md §1/§4 slice S1) —
|
|
301
|
+
// worlds-path routing for non-three `{ module }` worlds
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Mount a single-world manifest's `kind: 'pixijs' | 'react'` `{ module }`
|
|
306
|
+
* world through the WORLDS PATH — the D-Y1 sibling of
|
|
307
|
+
* `enterModuleModeFromManifestWorld` above, mirroring `ingest-mode.ts`'s
|
|
308
|
+
* `enterIngestModeReact` recipe exactly: one-element `resolveAllWorlds` +
|
|
309
|
+
* worlds-path `createGameRuntime({ container, worlds })`, which hands the
|
|
310
|
+
* module adapter its own native `World2DHost`/`ReactWorldHost` — no
|
|
311
|
+
* `HostContext`, no `.scene` assumption, no engine-side change needed (the
|
|
312
|
+
* wave-2 study's §0 finding: `resolveAllWorlds`'s `'module'` case is already
|
|
313
|
+
* kind-agnostic and already returns the correctly-kinded `WorldMountSpec`).
|
|
314
|
+
*
|
|
315
|
+
* Only THIS one world mounts (mirrors the legacy path's own "only ever the
|
|
316
|
+
* first world" behavior, `tryManifestModuleRoute` below): `manifest`'s other
|
|
317
|
+
* worlds (if any) are discarded via the one-element `{ ...manifest, worlds:
|
|
318
|
+
* [world] }` passed to `resolveAllWorlds` — a real multi-world manifest whose
|
|
319
|
+
* FIRST world is a non-three module adapter is out of scope here exactly as
|
|
320
|
+
* it always was for the legacy route.
|
|
321
|
+
*
|
|
322
|
+
* `withAchievedTierCheck` (already wrapped inside `resolveModuleAdapter`,
|
|
323
|
+
* reached identically through `resolveAllWorlds` -> `resolveWorldAdapter` ->
|
|
324
|
+
* the `'module'` case) runs completely unmodified here — an over-claiming
|
|
325
|
+
* module world still gets its shortfall logged loudly by the resolver itself;
|
|
326
|
+
* this function neither bypasses nor re-derives that check, only reads the
|
|
327
|
+
* SAME pure `computeAchievedTier`/`computeTierShortfall` result again to
|
|
328
|
+
* expose it on the `__vgaiModule` dev/e2e hook below (byte-identical
|
|
329
|
+
* "belt-and-suspenders" comment to the legacy path above).
|
|
330
|
+
*
|
|
331
|
+
* Authoring: whatever `mounted.authoring` the module adapter itself returns
|
|
332
|
+
* becomes the active override (store-selection-wrapped via `withStoreSelection`
|
|
333
|
+
* — the SAME policy the legacy path applies, kind-agnostic since it only
|
|
334
|
+
* touches `AuthoringAdapter.selection`); no authoring surface at all gets the
|
|
335
|
+
* same honest embed-only floor `enterModuleModeFromManifestWorld` uses. There
|
|
336
|
+
* is deliberately NO `tagObjectsWithEntityIds`/`store.enterPlayScene` call
|
|
337
|
+
* here (unlike the legacy threejs path): a pixi/react module world has no
|
|
338
|
+
* THREE scene for the editor viewport to adopt — its own canvas/DOM layer
|
|
339
|
+
* renders directly inside the game container, and the hierarchy/inspector
|
|
340
|
+
* panels are driven purely through the `AuthoringAdapter` seam (per this
|
|
341
|
+
* file's header doc comment: panel choice is adapter/node-identity-keyed,
|
|
342
|
+
* not scene-adoption-keyed) — exactly like `enterIngestModeReact`.
|
|
343
|
+
*/
|
|
344
|
+
/**
|
|
345
|
+
* Mount a single `{ module }` world (any kind) through the SCOPED worlds-path
|
|
346
|
+
* recipe — a one-element `resolveAllWorlds` + worlds-path `createGameRuntime`
|
|
347
|
+
* — with NO global side effects of its own (no `setActiveAuthoring`, no
|
|
348
|
+
* `store`/viewport writes, no dev hooks). Extracted (D-V4,
|
|
349
|
+
* docs/WAVE5-MULTIWORLD-INGEST-DESIGN.md) so BOTH this file's own
|
|
350
|
+
* `enterModuleModeWorldsPath` below (which layers store/authoring/dev-hook
|
|
351
|
+
* wiring on top, mounting into the WHOLE shared game container) and a
|
|
352
|
+
* composite's `{ module }` SIBLING mount (`ingest-siblings.ts`, which mounts
|
|
353
|
+
* into its own isolated layer div beside an ingest world and adds none of
|
|
354
|
+
* that global wiring) share the identical resolve+mount step, rather than
|
|
355
|
+
* duplicating it.
|
|
356
|
+
*/
|
|
357
|
+
export async function mountModuleWorldRuntime(
|
|
358
|
+
manifest: ResolvedGameManifest,
|
|
359
|
+
world: ResolvedWorldEntry,
|
|
360
|
+
projectRoot: string,
|
|
361
|
+
container: HTMLElement,
|
|
362
|
+
width: number,
|
|
363
|
+
height: number,
|
|
364
|
+
): Promise<{ session: GameSession; specs: WorldMountSpec[] }> {
|
|
365
|
+
const { createGameRuntime } = await import('@engine/runtime/create-runtime');
|
|
366
|
+
const specs = await resolveAllWorlds({ ...manifest, worlds: [world] }, projectRoot);
|
|
367
|
+
const session = await createGameRuntime({ container, worlds: specs, width, height });
|
|
368
|
+
return { session, specs };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export async function enterModuleModeWorldsPath(
|
|
372
|
+
store: EditorStore,
|
|
373
|
+
manifest: ResolvedGameManifest,
|
|
374
|
+
world: ResolvedWorldEntry,
|
|
375
|
+
projectRoot: string,
|
|
376
|
+
): Promise<void> {
|
|
377
|
+
const gameContainer = getGameContainer();
|
|
378
|
+
if (!gameContainer) {
|
|
379
|
+
editorConsole.error('Game container not mounted — cannot auto-mount module adapter', 'adapter');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
store.setPlayState('playing');
|
|
384
|
+
editorConsole.log(
|
|
385
|
+
`Mounting custom adapter module for world "${world.id}" (kind: ${world.kind}, worlds path)`,
|
|
386
|
+
'adapter',
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const w = gameContainer.clientWidth || 800;
|
|
390
|
+
const h = gameContainer.clientHeight || 600;
|
|
391
|
+
const { session, specs } = await mountModuleWorldRuntime(
|
|
392
|
+
manifest,
|
|
393
|
+
world,
|
|
394
|
+
projectRoot,
|
|
395
|
+
gameContainer,
|
|
396
|
+
w,
|
|
397
|
+
h,
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const worldInstance = session.game.worlds[0]!;
|
|
401
|
+
const mounted = worldInstance.mounted;
|
|
402
|
+
const adapterId = specs[0]!.adapter.id;
|
|
403
|
+
|
|
404
|
+
_session = { store, stop: () => session.stop() };
|
|
405
|
+
|
|
406
|
+
let activeAuthoring: AuthoringAdapter;
|
|
407
|
+
if (mounted.authoring) {
|
|
408
|
+
activeAuthoring = withStoreSelection(mounted.authoring, store);
|
|
409
|
+
} else {
|
|
410
|
+
// Honest floor for a module adapter that declares no authoring surface at
|
|
411
|
+
// all — same embed-only ceiling the legacy path (and ingest) uses.
|
|
412
|
+
activeAuthoring = makeEmbedOnlyAdapter(store, `${adapterId} (no authoring surface)`);
|
|
413
|
+
}
|
|
414
|
+
setActiveAuthoring(activeAuthoring);
|
|
415
|
+
setActiveSystems(mounted.systems ?? {});
|
|
416
|
+
store.setActiveViewportTab('game');
|
|
417
|
+
store.notifyIngestEdit();
|
|
418
|
+
setMountFailureReport(null); // D-W3: mount succeeded
|
|
419
|
+
|
|
420
|
+
const achievedTier = computeAchievedTier(mounted);
|
|
421
|
+
const shortfall: TierShortfallReport | null = computeTierShortfall(
|
|
422
|
+
world.id,
|
|
423
|
+
world.capabilities.local,
|
|
424
|
+
achievedTier,
|
|
425
|
+
);
|
|
426
|
+
// Belt-and-suspenders: resolveWorldAdapter's own wrapping already warned once
|
|
427
|
+
// via editorConsole (T3.4 slice 1) — this does not warn again, only formats
|
|
428
|
+
// the SAME message onto the structured dev/e2e hook below for assertions.
|
|
429
|
+
if (shortfall) {
|
|
430
|
+
editorConsole.log(
|
|
431
|
+
`(already warned by the resolver) ${formatTierShortfallMessage(shortfall)}`,
|
|
432
|
+
'adapter',
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
(window as unknown as Record<string, unknown>)['__vgaiModule'] = {
|
|
437
|
+
worldId: world.id,
|
|
438
|
+
adapterId,
|
|
439
|
+
declaredTier: world.capabilities.local,
|
|
440
|
+
achievedTier,
|
|
441
|
+
...(shortfall ? { tierShortfall: shortfall } : {}),
|
|
442
|
+
...(import.meta.env.DEV ? { adapter: activeAuthoring, store } : {}),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Route 0 of `ingest-mode.ts`'s `autoLaunchIngestFromURL` (checked before the
|
|
448
|
+
* ingest routes — see that function's doc comment): a manifest-backed project
|
|
449
|
+
* whose FIRST world declares a `{ module }` adapter auto-mounts it. Same
|
|
450
|
+
* "applied or not" contract as `tryManifestIngestRoute`.
|
|
451
|
+
*
|
|
452
|
+
* D-Y1: dispatches on `firstWorld.kind` — threejs keeps calling
|
|
453
|
+
* `enterModuleModeFromManifestWorld` byte-for-byte (its own D-X6 guard stays
|
|
454
|
+
* as the backstop for any OTHER path that might still funnel a non-three
|
|
455
|
+
* mount into it); `pixijs`/`react` route through the new
|
|
456
|
+
* `enterModuleModeWorldsPath` above instead.
|
|
457
|
+
*
|
|
458
|
+
* D-V2 (docs/WAVE5-MULTIWORLD-INGEST-DESIGN.md, F24 composite): if the
|
|
459
|
+
* manifest declares ANY `{ ingest }` world (anywhere in `worlds`, not just
|
|
460
|
+
* first), this route declines — composites are owned by the ingest routes
|
|
461
|
+
* (`ingest-mode.ts`'s `tryManifestIngestRoute*`), which mount the one ingest
|
|
462
|
+
* world through their own enter path and any `{ module }` SIBLING through
|
|
463
|
+
* the scoped runtime (`ingest-siblings.ts`, D-V4) instead of this
|
|
464
|
+
* single-world route's global-side-effect-laden mount. Without this guard, a
|
|
465
|
+
* composite whose FIRST world happens to be a `{ module }` sibling (array
|
|
466
|
+
* order is manifest order, independent of which world is the primary
|
|
467
|
+
* ingest) would be mis-mounted here as if it were a standalone module world.
|
|
468
|
+
* This route's own `firstWorld.adapter.type !== 'module'` behavior for a
|
|
469
|
+
* non-composite manifest is otherwise untouched.
|
|
470
|
+
*/
|
|
471
|
+
export async function tryManifestModuleRoute(store: EditorStore): Promise<boolean> {
|
|
472
|
+
if (getEditorMode() === 'browser') return false; // no dev server to serve /vgai.game.json / /@fs/ from
|
|
473
|
+
const manifest = await fetchGameManifest().catch(() => null);
|
|
474
|
+
if (manifest?.worlds.some((w) => w.adapter.type === 'ingest')) return false;
|
|
475
|
+
const firstWorld = manifest?.worlds[0];
|
|
476
|
+
if (firstWorld === undefined || firstWorld.adapter.type !== 'module') return false;
|
|
477
|
+
|
|
478
|
+
const project = getCurrentProject();
|
|
479
|
+
if (!project) {
|
|
480
|
+
editorConsole.error(
|
|
481
|
+
'Manifest declares a { module } world but no project is open — cannot auto-mount',
|
|
482
|
+
'adapter',
|
|
483
|
+
);
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
const ready = await waitForGameContainer();
|
|
487
|
+
if (!ready) {
|
|
488
|
+
editorConsole.error(
|
|
489
|
+
'Game container never mounted — cannot auto-mount module adapter',
|
|
490
|
+
'adapter',
|
|
491
|
+
);
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
if (firstWorld.kind === 'pixijs' || firstWorld.kind === 'react') {
|
|
496
|
+
await enterModuleModeWorldsPath(store, manifest!, firstWorld, project.rootPath);
|
|
497
|
+
} else {
|
|
498
|
+
await enterModuleModeFromManifestWorld(store, firstWorld, project.rootPath);
|
|
499
|
+
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
editorConsole.error(`Manifest { module } auto-mount failed: ${err}`, 'adapter');
|
|
502
|
+
// D-W3 (docs/WAVE4-FTUE-HARDENING-DESIGN.md §D-W3, F9) — the fourth of the
|
|
503
|
+
// four manifest-route catch blocks (three/pixi/react in ingest-mode.ts,
|
|
504
|
+
// this one for `{ module }` worlds); same loud-banner-slot population.
|
|
505
|
+
setMountFailureReport({
|
|
506
|
+
worldId: firstWorld.id,
|
|
507
|
+
kind: firstWorld.kind,
|
|
508
|
+
identity: firstWorld.adapter.identity,
|
|
509
|
+
message: formatMountFailureMessage(err),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { initNavigation, NavMeshManager } from '@engine/ai/navigation';
|
|
2
|
+
import type * as THREE from 'three';
|
|
3
|
+
import { saveFile } from './editor-api';
|
|
4
|
+
import { editorConsole } from './editor-console';
|
|
5
|
+
import type { EditorStore } from './editor-store';
|
|
6
|
+
import type { EditorViewport } from './editor-viewport';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Set up navmesh bake/clear event listeners. Returns a dispose function.
|
|
10
|
+
*/
|
|
11
|
+
export function setupNavMeshHandlers(
|
|
12
|
+
store: EditorStore,
|
|
13
|
+
viewport: EditorViewport,
|
|
14
|
+
scene: THREE.Scene,
|
|
15
|
+
): () => void {
|
|
16
|
+
let navMeshManager: NavMeshManager | null = null;
|
|
17
|
+
|
|
18
|
+
function collectNavigationIds(role: 'walkable' | 'obstacle'): Set<string> {
|
|
19
|
+
const ids = new Set<string>();
|
|
20
|
+
for (const e of store.allEntities()) {
|
|
21
|
+
if (e.navigation?.role === role && e.id) ids.add(e.id);
|
|
22
|
+
}
|
|
23
|
+
return ids;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const onBake = async () => {
|
|
27
|
+
try {
|
|
28
|
+
await initNavigation();
|
|
29
|
+
} catch (err) {
|
|
30
|
+
editorConsole.error(`Failed to init recast-navigation WASM: ${err}`, 'navigation');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const walkableIds = collectNavigationIds('walkable');
|
|
35
|
+
if (walkableIds.size === 0) {
|
|
36
|
+
editorConsole.warn(
|
|
37
|
+
'No walkable entities found — tag meshes as "walkable" in the Navigation section',
|
|
38
|
+
'navigation',
|
|
39
|
+
);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const meshes: THREE.Mesh[] = [];
|
|
44
|
+
for (const id of walkableIds) {
|
|
45
|
+
const obj = store.objectMap.get(id);
|
|
46
|
+
if (!obj) continue;
|
|
47
|
+
obj.traverse((child) => {
|
|
48
|
+
if ((child as THREE.Mesh).isMesh) meshes.push(child as THREE.Mesh);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (meshes.length === 0) return;
|
|
52
|
+
|
|
53
|
+
if (!navMeshManager) navMeshManager = new NavMeshManager();
|
|
54
|
+
const envNav = store.environment?.navigation;
|
|
55
|
+
const success = navMeshManager!.buildFromMeshes(meshes, envNav);
|
|
56
|
+
if (!success) {
|
|
57
|
+
editorConsole.error('NavMesh bake failed', 'navigation');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const debugMesh = navMeshManager!.getDebugMesh(scene);
|
|
62
|
+
if (debugMesh) viewport.setNavMeshHelper(debugMesh);
|
|
63
|
+
|
|
64
|
+
store.markNavMeshBaked();
|
|
65
|
+
|
|
66
|
+
if (store.savePath) {
|
|
67
|
+
const navmeshPath = store.savePath.replace('.vscn.json', '.navmesh');
|
|
68
|
+
const data = navMeshManager!.exportData();
|
|
69
|
+
let binary = '';
|
|
70
|
+
for (let i = 0; i < data.length; i++) binary += String.fromCharCode(data[i]!);
|
|
71
|
+
const base64 = btoa(binary);
|
|
72
|
+
saveFile(navmeshPath, base64, 'base64').then((ok) => {
|
|
73
|
+
if (!ok) editorConsole.error('Failed to save .navmesh file', 'navigation');
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const onClear = () => {
|
|
79
|
+
if (navMeshManager) {
|
|
80
|
+
navMeshManager.dispose(scene);
|
|
81
|
+
navMeshManager = null;
|
|
82
|
+
}
|
|
83
|
+
viewport.setNavMeshHelper(null);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
window.addEventListener('editor:bake-navmesh', onBake);
|
|
87
|
+
window.addEventListener('editor:clear-navmesh', onClear);
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
window.removeEventListener('editor:bake-navmesh', onBake);
|
|
91
|
+
window.removeEventListener('editor:clear-navmesh', onClear);
|
|
92
|
+
if (navMeshManager) navMeshManager.dispose(scene);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime detection of the PACKAGED editor server (`packages/editor/server/
|
|
3
|
+
* packaged.ts` — a `@vgai/editor` npm package with NO monorepo checkout on
|
|
4
|
+
* disk) from editor CLIENT code. Needed because a react world's mount
|
|
5
|
+
* (`adapter-resolver.ts`'s `resolveReactWorldMountRuntime`) must resolve its
|
|
6
|
+
* `react`/`react-dom/client`/`GameProvider`-fallback wrapper from the
|
|
7
|
+
* PROJECT's own module graph ONLY under this runtime — see that function's
|
|
8
|
+
* doc comment, and `packaged.ts`'s header doc comment ("React-world OID
|
|
9
|
+
* authoring parity"), for the dual-React-instance bug this closes. Under
|
|
10
|
+
* dev/Electron, this must resolve `false` so the existing static-import path
|
|
11
|
+
* stays byte-unchanged (one shared Vite instance already collapses both
|
|
12
|
+
* react copies via `resolve.dedupe`).
|
|
13
|
+
*
|
|
14
|
+
* Reads the server's own `packaged` flag off `/__editor/project`'s response
|
|
15
|
+
* (computed server-side via `isMonorepoScaffoldRoot`, `server-utils.ts`)
|
|
16
|
+
* rather than probing a URL that only resolves in packaged mode: an
|
|
17
|
+
* unconditional dynamic `import()` probe would 404 (and log an
|
|
18
|
+
* unsuppressible browser console error) on EVERY react-world mount under
|
|
19
|
+
* dev/Electron — the exact reasoning `adapter-resolver.ts`'s
|
|
20
|
+
* `loadProjectUIRegistry` doc comment already recorded for `hasUiRegistry`
|
|
21
|
+
* ("the client must not probe /@fs for it").
|
|
22
|
+
*
|
|
23
|
+
* Memoized for the session (module-level, not per-call): the runtime a
|
|
24
|
+
* server process is running under cannot change without a restart, so this
|
|
25
|
+
* fetches `/__editor/project` at most once per page load.
|
|
26
|
+
*
|
|
27
|
+
* A plain inline `fetch()`, not `editor-api.ts`'s `getCurrentServerProject`
|
|
28
|
+
* — same reasoning `loadProjectUIRegistry` documents: this module (via
|
|
29
|
+
* `adapter-resolver.ts`) must stay importable by headless Node/vitest
|
|
30
|
+
* suites, and `editor-api.ts`'s module top level pulls in `./storage`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
let cachedIsPackaged: Promise<boolean> | null = null;
|
|
34
|
+
|
|
35
|
+
export function isPackagedRuntime(): Promise<boolean> {
|
|
36
|
+
if (!cachedIsPackaged) {
|
|
37
|
+
cachedIsPackaged = fetch('/__editor/project')
|
|
38
|
+
.then((res) => res.json())
|
|
39
|
+
.then((data: { project?: { packaged?: boolean } | null }) => Boolean(data.project?.packaged))
|
|
40
|
+
.catch(() => false);
|
|
41
|
+
}
|
|
42
|
+
return cachedIsPackaged;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Test-only: reset the memoized result between tests (mirrors
|
|
46
|
+
* `setCachedProjectUIRegistry`'s test-seam shape). */
|
|
47
|
+
export function resetPackagedRuntimeCacheForTest(): void {
|
|
48
|
+
cachedIsPackaged = null;
|
|
49
|
+
}
|