@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,211 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { collectAssetPaths } from '@vgai/engine/scene/asset-paths';
|
|
4
|
+
import {
|
|
5
|
+
type ComponentRegistry,
|
|
6
|
+
isGameComponentClass,
|
|
7
|
+
} from '@vgai/engine/scene/component-registry';
|
|
8
|
+
import { parsePrefabFile, parseSceneFile, SceneParseError } from '@vgai/engine/scene/parse';
|
|
9
|
+
import type { SceneEntity } from '@vgai/engine/scene/scene-types';
|
|
10
|
+
import { ASSET_CONTENT_EXTENSIONS, validateAssetContentFile } from './validate-asset-content';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Usage: npx tsx validate-scenes.ts [folder] [--components]
|
|
14
|
+
//
|
|
15
|
+
// Single source of truth for scene validation, shipped IN the project template so
|
|
16
|
+
// every scaffolded game gets a working `npm run validate-scenes` (the engine is
|
|
17
|
+
// imported via the published `@vgai/engine/*` subpath exports, which resolve in
|
|
18
|
+
// both this monorepo and a generated project — unlike the old `../scripts/...`
|
|
19
|
+
// path that only existed inside the monorepo).
|
|
20
|
+
//
|
|
21
|
+
// Validates all .vscn.json and .prefab.json files found recursively under
|
|
22
|
+
// <folder>/public/ — plus all .mat.json/.inputmap.json/
|
|
23
|
+
// .instances.json files, CONTENT-parsed through the engine's asset-content
|
|
24
|
+
// Zod schemas the runtime rejects them with (previously these formats were
|
|
25
|
+
// only existence-checked when a scene referenced them; a present-but-
|
|
26
|
+
// malformed asset passed this command and failed at load).
|
|
27
|
+
// Defaults to the current working directory.
|
|
28
|
+
//
|
|
29
|
+
// Checks:
|
|
30
|
+
// 1. Structural validity (Zod schema parse) — scenes, prefabs, AND the
|
|
31
|
+
// fetched-asset formats (.mat.json/.inputmap.json/.instances.json)
|
|
32
|
+
// 2. Asset references point to files that exist on disk
|
|
33
|
+
// 3. Components exist in the registry (if --components flag provided)
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
const rootDir = resolve(args[0] ?? '.');
|
|
38
|
+
const publicDir = join(rootDir, 'public');
|
|
39
|
+
|
|
40
|
+
if (!existsSync(publicDir)) {
|
|
41
|
+
console.error(`No public/ directory found in ${rootDir}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Optionally load component registry for component-level validation.
|
|
46
|
+
// Pass --components to enable (requires boilerplate examples to be available).
|
|
47
|
+
const validateComponents = args.includes('--components');
|
|
48
|
+
let componentRegistry: ComponentRegistry | null = null;
|
|
49
|
+
|
|
50
|
+
if (validateComponents) {
|
|
51
|
+
// Import the project's component registry.
|
|
52
|
+
// The registry path is relative to the root folder being validated.
|
|
53
|
+
const registryPath = join(rootDir, 'src/scripts/registry');
|
|
54
|
+
try {
|
|
55
|
+
const mod = await import(registryPath);
|
|
56
|
+
componentRegistry = mod.componentRegistry;
|
|
57
|
+
if (!componentRegistry) {
|
|
58
|
+
console.error(`Registry at ${registryPath} does not export "componentRegistry"`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.error(
|
|
63
|
+
`--components requires a project with src/scripts/registry.ts exporting componentRegistry.\n` +
|
|
64
|
+
` Looked in: ${registryPath}\n` +
|
|
65
|
+
` Error: ${e}`,
|
|
66
|
+
);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
function collectFiles(dir: string, ext: string): string[] {
|
|
74
|
+
const result: string[] = [];
|
|
75
|
+
if (!existsSync(dir)) return result;
|
|
76
|
+
for (const entry of readdirSync(dir)) {
|
|
77
|
+
const full = join(dir, entry);
|
|
78
|
+
if (statSync(full).isDirectory()) {
|
|
79
|
+
result.push(...collectFiles(full, ext));
|
|
80
|
+
} else if (entry.endsWith(ext)) {
|
|
81
|
+
result.push(full);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function validateEntity(
|
|
88
|
+
entity: SceneEntity,
|
|
89
|
+
registry: ComponentRegistry,
|
|
90
|
+
file: string,
|
|
91
|
+
errors: string[],
|
|
92
|
+
): void {
|
|
93
|
+
if (entity.components) {
|
|
94
|
+
for (const [name, data] of Object.entries(entity.components)) {
|
|
95
|
+
const Klass = registry[name];
|
|
96
|
+
if (!Klass) {
|
|
97
|
+
errors.push(`${file}: entity "${entity.name}" has unknown component "${name}"`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
// GameComponent v2: a registry entry is a GameComponentClass whose editable
|
|
101
|
+
// fields are declared on its `static schema` (a Zod object). Derive the valid
|
|
102
|
+
// field names from the schema's shape. Components with no schema (tag
|
|
103
|
+
// components like Player) accept no fields.
|
|
104
|
+
const validFields =
|
|
105
|
+
isGameComponentClass(Klass) && Klass.schema ? Object.keys(Klass.schema.shape) : [];
|
|
106
|
+
for (const field of Object.keys(data)) {
|
|
107
|
+
if (!validFields.includes(field)) {
|
|
108
|
+
errors.push(
|
|
109
|
+
`${file}: entity "${entity.name}" component "${name}" has unknown field "${field}" (valid: ${validFields.join(', ') || '(none)'})`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (entity.children) {
|
|
116
|
+
for (const child of entity.children) {
|
|
117
|
+
validateEntity(child, registry, file, errors);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function validateAssetPaths(assetPaths: Set<string>, file: string, errors: string[]): void {
|
|
123
|
+
for (const assetPath of assetPaths) {
|
|
124
|
+
const diskPath = join(publicDir, assetPath.replace(/^\//, ''));
|
|
125
|
+
if (!existsSync(diskPath)) {
|
|
126
|
+
errors.push(`${file}: broken asset reference "${assetPath}" — file not found`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
const errors: string[] = [];
|
|
134
|
+
|
|
135
|
+
const sceneFiles = collectFiles(publicDir, '.vscn.json');
|
|
136
|
+
const prefabFiles = collectFiles(publicDir, '.prefab.json');
|
|
137
|
+
const totalFiles = sceneFiles.length + prefabFiles.length;
|
|
138
|
+
|
|
139
|
+
// Content-parse the three fetched-asset formats through their engine Zod
|
|
140
|
+
// schemas (previously only existence-checked, and only when referenced by
|
|
141
|
+
// a scene — a present-but-malformed .mat/.inputmap file passed
|
|
142
|
+
// this command and failed at runtime fetch).
|
|
143
|
+
const assetContentFiles = ASSET_CONTENT_EXTENSIONS.flatMap((ext) => collectFiles(publicDir, ext));
|
|
144
|
+
for (const path of assetContentFiles) {
|
|
145
|
+
errors.push(...validateAssetContentFile(path));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Validate scene files
|
|
149
|
+
for (const path of sceneFiles) {
|
|
150
|
+
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
151
|
+
|
|
152
|
+
let data;
|
|
153
|
+
try {
|
|
154
|
+
data = parseSceneFile(raw);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
if (e instanceof SceneParseError) {
|
|
157
|
+
for (const issue of e.issues) {
|
|
158
|
+
errors.push(`${path}: ${issue.path.join('.')}: ${issue.message}`);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
errors.push(`${path}: ${e}`);
|
|
162
|
+
}
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (componentRegistry) {
|
|
167
|
+
for (const entity of data.entities) {
|
|
168
|
+
validateEntity(entity, componentRegistry, path, errors);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const assetPaths = collectAssetPaths(data.entities, data.environment);
|
|
173
|
+
validateAssetPaths(assetPaths, path, errors);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Validate prefab files
|
|
177
|
+
for (const path of prefabFiles) {
|
|
178
|
+
const raw = JSON.parse(readFileSync(path, 'utf-8'));
|
|
179
|
+
|
|
180
|
+
let data;
|
|
181
|
+
try {
|
|
182
|
+
data = parsePrefabFile(raw);
|
|
183
|
+
} catch (e) {
|
|
184
|
+
if (e instanceof SceneParseError) {
|
|
185
|
+
for (const issue of e.issues) {
|
|
186
|
+
errors.push(`${path}: ${issue.path.join('.')}: ${issue.message}`);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
errors.push(`${path}: ${e}`);
|
|
190
|
+
}
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const assetPaths = collectAssetPaths([data.root]);
|
|
195
|
+
validateAssetPaths(assetPaths, path, errors);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
if (errors.length > 0) {
|
|
201
|
+
console.error(`Scene validation failed with ${errors.length} error(s):\n`);
|
|
202
|
+
for (const err of errors) {
|
|
203
|
+
console.error(` ${err}`);
|
|
204
|
+
}
|
|
205
|
+
process.exit(1);
|
|
206
|
+
} else {
|
|
207
|
+
console.log(
|
|
208
|
+
`Validated ${totalFiles} scene/prefab file(s) and ${assetContentFiles.length} asset file(s) ` +
|
|
209
|
+
`(.mat/.inputmap/.instances) in ${rootDir} — all OK`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifestVersion": 1,
|
|
3
|
+
"name": "my-game",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"engine": {
|
|
6
|
+
"version": "0.2.0"
|
|
7
|
+
},
|
|
8
|
+
"worlds": [
|
|
9
|
+
{
|
|
10
|
+
"id": "main",
|
|
11
|
+
"kind": "threejs",
|
|
12
|
+
"adapter": "default",
|
|
13
|
+
"entry": "src/scripts/main.ts",
|
|
14
|
+
"scene": "scenes/default.vscn.json"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"resolution": {
|
|
18
|
+
"width": 1280,
|
|
19
|
+
"height": 720
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
// ENGINE-RELATIVE import, deliberately NOT '@vgai/engine/data/vite-plugin-data'
|
|
4
|
+
// (the scaffolder rewrites this path to each project's engine location): Vite's
|
|
5
|
+
// config loader (esbuild) only BUNDLES relative imports — a bare specifier is
|
|
6
|
+
// externalized and loaded by plain node at config time, which cannot load the
|
|
7
|
+
// engine's shipped TypeScript (ERR_UNKNOWN_FILE_EXTENSION: .ts).
|
|
8
|
+
import { vgaiDataCheck } from '../../engine/src/data/vite-plugin-data';
|
|
9
|
+
// Config-relative TS imports get bundled the same way — that is what lets the
|
|
10
|
+
// project's executable Zod schemas reach the build-time check below.
|
|
11
|
+
import { dataAssets } from './src/data/assets';
|
|
12
|
+
|
|
13
|
+
// P2P builds (launched with `?net=p2p-host` / `?net=p2p-join`) route the Colyseus
|
|
14
|
+
// packages onto @vgai/p2p-colyseus's browser-safe compat shims: `browser.ts` exposes
|
|
15
|
+
// `configureP2PColyseusClient`, and `platform.ts` stands in for the node-only
|
|
16
|
+
// `colyseus`/`@colyseus/core` server runtime so `../server/rooms` can be imported
|
|
17
|
+
// into the browser for the in-page host.
|
|
18
|
+
//
|
|
19
|
+
// Default builds must NOT alias — they resolve the REAL Colyseus SDK from
|
|
20
|
+
// node_modules so `net.connect('ws://…')` speaks the real Colyseus wire protocol
|
|
21
|
+
// to a `npm run server` Colyseus server. The compat client's `ws://` branch speaks
|
|
22
|
+
// its own JSON envelope protocol, so aliasing unconditionally silently breaks every
|
|
23
|
+
// non-P2P multiplayer game. Gate per docs/P2P-COLYSEUS-MIGRATION.md Rule 5:
|
|
24
|
+
// "Alias Colyseus packages to @vgai/p2p-colyseus only in P2P builds."
|
|
25
|
+
const p2pBuild = process.env.VGAI_P2P === '1' || process.env.VGAI_P2P === 'true';
|
|
26
|
+
|
|
27
|
+
const p2pAliases = p2pBuild
|
|
28
|
+
? {
|
|
29
|
+
'@colyseus/sdk': path.resolve(__dirname, '../../p2p-colyseus/src/browser.ts'),
|
|
30
|
+
'@colyseus/schema': path.resolve(__dirname, '../../p2p-colyseus/src/schema.ts'),
|
|
31
|
+
'@colyseus/core': path.resolve(__dirname, '../../p2p-colyseus/src/platform.ts'),
|
|
32
|
+
colyseus: path.resolve(__dirname, '../../p2p-colyseus/src/platform.ts'),
|
|
33
|
+
}
|
|
34
|
+
: {};
|
|
35
|
+
|
|
36
|
+
export default defineConfig({
|
|
37
|
+
// Build-path validation (docs/DATA-TOOLS-DESIGN.md §6.7, build-only): every
|
|
38
|
+
// registered data asset must still parse through its schema (the EXACT
|
|
39
|
+
// parse defineData runs at load), every `"file#key"` ref must resolve, and
|
|
40
|
+
// no `src/tools/` module may reach the shipped bundle (§4: tools are
|
|
41
|
+
// editor-only — never serve testers a build with dev/cheat surfaces).
|
|
42
|
+
plugins: [
|
|
43
|
+
vgaiDataCheck({
|
|
44
|
+
root: __dirname,
|
|
45
|
+
assets: dataAssets.map((a) => ({
|
|
46
|
+
schema: a.schema,
|
|
47
|
+
sourcePath: `src/data/${a.name}.data.json`,
|
|
48
|
+
})),
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
resolve: {
|
|
52
|
+
alias: {
|
|
53
|
+
'@engine': path.resolve(__dirname, '../../engine/src'),
|
|
54
|
+
...p2pAliases,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
optimizeDeps: {
|
|
58
|
+
// Pre-bundle deps that are only reached through the dynamically imported
|
|
59
|
+
// example modules (or injected by the JSX transform, like the React
|
|
60
|
+
// runtimes) so Vite does not discover them at runtime, re-optimize, and
|
|
61
|
+
// hard-reload the standalone runtime the first time a game selects them —
|
|
62
|
+
// that mid-session reload resets `window.__vgaiScene`/the menu out from
|
|
63
|
+
// under whatever is driving the page (the e2e dep-optimize-reload flake the
|
|
64
|
+
// 04 spec hit: the first cold select of `editor-tutorial` reloaded on
|
|
65
|
+
// `react/jsx-dev-runtime`). Verified against a cold scaffold: with these
|
|
66
|
+
// listed, a full sweep of the switcher games triggers zero page reloads.
|
|
67
|
+
include: [
|
|
68
|
+
'gsap',
|
|
69
|
+
'react/jsx-runtime',
|
|
70
|
+
'react/jsx-dev-runtime',
|
|
71
|
+
'react-dom/client',
|
|
72
|
+
'postprocessing',
|
|
73
|
+
'three.quarks',
|
|
74
|
+
'quarks.core',
|
|
75
|
+
'@dimforge/rapier3d-compat',
|
|
76
|
+
],
|
|
77
|
+
// In a default (non-P2P) build, `main.ts`'s `?net=p2p-host` branch statically
|
|
78
|
+
// references `import('../server/rooms')` — server room classes that pull the
|
|
79
|
+
// node-only `colyseus`/`@colyseus/core` runtime (which imports `@pm2/io`). That
|
|
80
|
+
// branch never executes without `?net=p2p-host`, but vite's dep pre-bundler
|
|
81
|
+
// still follows the dynamic import while scanning and crashes trying to bundle
|
|
82
|
+
// the server runtime for the browser. Excluding the server-only packages makes
|
|
83
|
+
// scanning them a no-op; the branch stays inert at runtime. In P2P builds the
|
|
84
|
+
// alias above already routes these names to browser-safe shims, so no exclude.
|
|
85
|
+
...(p2pBuild ? {} : { exclude: ['colyseus', '@colyseus/core'] }),
|
|
86
|
+
},
|
|
87
|
+
server: {
|
|
88
|
+
port: 5180,
|
|
89
|
+
// Fail loudly instead of silently rebinding to 5181+ if 5180 is still
|
|
90
|
+
// held by a lingering prior instance. Without this, a stale server left
|
|
91
|
+
// over from an earlier e2e spec file answers the NEW server's readiness
|
|
92
|
+
// probe (which targets the fixed 5180 URL) while the real new instance
|
|
93
|
+
// sits on a different port nobody is talking to -- a hazard previously
|
|
94
|
+
// observed causing a CI e2e spec to hang waiting on the wrong server. See
|
|
95
|
+
// packages/editor/e2e/helpers/server.ts's `startStandaloneServer`.
|
|
96
|
+
strictPort: true,
|
|
97
|
+
// VGAI_WATCH_POLL=1 (or =<ms>): poll for file changes instead of relying
|
|
98
|
+
// on inotify — REQUIRED on WSL when the project lives on a Windows drive
|
|
99
|
+
// (/mnt/c, drvfs: inotify never fires there), or edits to
|
|
100
|
+
// src/data/*.data.json silently never hot-reload into the running game
|
|
101
|
+
// (docs/DATA-TOOLS-DESIGN.md §2.3). Same env var the EDITOR dev server
|
|
102
|
+
// honors, so one knob covers both surfaces. Safe to enable here: this
|
|
103
|
+
// standalone server only watches the project tree, which is small.
|
|
104
|
+
...(process.env.VGAI_WATCH_POLL
|
|
105
|
+
? {
|
|
106
|
+
watch: {
|
|
107
|
+
usePolling: true,
|
|
108
|
+
interval:
|
|
109
|
+
Number(process.env.VGAI_WATCH_POLL) > 1 ? Number(process.env.VGAI_WATCH_POLL) : 1000,
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
: {}),
|
|
113
|
+
allowedHosts:
|
|
114
|
+
process.env.VITE_ALLOWED_HOSTS === 'all'
|
|
115
|
+
? true
|
|
116
|
+
: process.env.VITE_ALLOWED_HOSTS?.split(',').filter(Boolean),
|
|
117
|
+
},
|
|
118
|
+
build: {
|
|
119
|
+
target: 'es2022',
|
|
120
|
+
// Same story as optimizeDeps.exclude above, but for the PRODUCTION build:
|
|
121
|
+
// rollup follows `main.ts`'s `import('../server/rooms')` and dies resolving
|
|
122
|
+
// the node-only colyseus runtime (`@pm2/io`) for the browser. Externalizing
|
|
123
|
+
// it leaves a bare dynamic `import('colyseus')` in the never-executed
|
|
124
|
+
// ?net=p2p-host branch (guarded by `configureP2PColyseusClient`, which only
|
|
125
|
+
// exists in aliased P2P builds). P2P builds resolve via the alias instead.
|
|
126
|
+
...(p2pBuild ? {} : { rollupOptions: { external: ['colyseus', '@colyseus/core'] } }),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vite-plugin-example-assets — copy every `examples/<id>/public/*` (repo root)
|
|
3
|
+
* into the `--mode web` build's output under `<outDir>/examples/<id>/...`, so
|
|
4
|
+
* a bundled example's relative asset fetches (e.g. `models/LittlestTokyo.glb`)
|
|
5
|
+
* resolve once namespaced by `setAssetPrefix('/examples/<id>/')` (see
|
|
6
|
+
* `packages/editor/src/browser-play-entry.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Since each `examples/<id>/` (T3.5/D11 — docs/EXAMPLES-AS-PROJECTS-DESIGN.md
|
|
9
|
+
* §4) is now an independent sibling package, not a shared package tree, each example's
|
|
10
|
+
* OWN `public/` would otherwise collide at a flat deploy root (two examples
|
|
11
|
+
* both shipping a `models/` folder). Namespacing under `/examples/<id>/`
|
|
12
|
+
* mirrors `scripts/bundle-project-scripts.mjs`'s per-example dispatch id.
|
|
13
|
+
*
|
|
14
|
+
* Build-only, `--mode web` only (see `projectScriptsBundlePlugin` in
|
|
15
|
+
* vite.config.ts — same gating). Copies in `closeBundle` (not `buildStart` —
|
|
16
|
+
* the build's `outDir` doesn't exist yet at that point; not `writeBundle`
|
|
17
|
+
* either, to run strictly after Vite's own `publicDir` copy so our
|
|
18
|
+
* `examples/` subtree is never raced/clobbered) — verified empirically via
|
|
19
|
+
* `npm run build:web` producing `dist/examples/<id>/...`.
|
|
20
|
+
*/
|
|
21
|
+
import { cpSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
|
|
22
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
23
|
+
import type { Plugin, ResolvedConfig } from 'vite';
|
|
24
|
+
|
|
25
|
+
export function exampleAssetsPlugin(repoRoot: string): Plugin {
|
|
26
|
+
let resolvedConfig: ResolvedConfig;
|
|
27
|
+
return {
|
|
28
|
+
name: 'vgai:example-assets',
|
|
29
|
+
apply: 'build',
|
|
30
|
+
configResolved(config) {
|
|
31
|
+
resolvedConfig = config;
|
|
32
|
+
},
|
|
33
|
+
closeBundle() {
|
|
34
|
+
const examplesDir = resolve(repoRoot, 'examples');
|
|
35
|
+
const outDir = isAbsolute(resolvedConfig.build.outDir)
|
|
36
|
+
? resolvedConfig.build.outDir
|
|
37
|
+
: resolve(resolvedConfig.root, resolvedConfig.build.outDir);
|
|
38
|
+
|
|
39
|
+
if (!existsSync(examplesDir)) return;
|
|
40
|
+
const folderIds = readdirSync(examplesDir, { withFileTypes: true })
|
|
41
|
+
.filter((d) => d.isDirectory())
|
|
42
|
+
.map((d) => d.name)
|
|
43
|
+
.sort();
|
|
44
|
+
|
|
45
|
+
for (const id of folderIds) {
|
|
46
|
+
const publicDir = join(examplesDir, id, 'public');
|
|
47
|
+
const manifest = join(examplesDir, id, 'vgai.game.json');
|
|
48
|
+
const dest = join(outDir, 'examples', id);
|
|
49
|
+
// some examples ship no assets of their own
|
|
50
|
+
if (existsSync(publicDir)) cpSync(publicDir, dest, { recursive: true });
|
|
51
|
+
// The manifest is what `?project=<id>` routing resolves a deep link
|
|
52
|
+
// against (hosted-example.ts) — stage it beside the public/ contents.
|
|
53
|
+
if (existsSync(manifest)) {
|
|
54
|
+
mkdirSync(dest, { recursive: true });
|
|
55
|
+
cpSync(manifest, join(dest, 'vgai.game.json'));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `vgai-game-globals` transform — prepends `GAME_GLOBALS_PRELUDE` to game
|
|
3
|
+
* source so raw `window`/`document` input listeners resolve to the gated proxies
|
|
4
|
+
* (`gated-globals.ts`) instead of the real globals, making them focus-gated.
|
|
5
|
+
*
|
|
6
|
+
* Shared by BOTH Vite configs, which is the point:
|
|
7
|
+
* - `server/dev.ts` registers it over an opened project's root PLUS the
|
|
8
|
+
* same-realm ingest-fixture roots;
|
|
9
|
+
* - the repo-root `vite.config.ts` registers it over the ingest-fixture roots
|
|
10
|
+
* alone, which is what the hosted `--mode web` static build needs. Before
|
|
11
|
+
* this existed the plugin lived only in `dev.ts`, so a same-realm ingested
|
|
12
|
+
* game ran UNSHADOWED in the hosted editor — its input listeners fired
|
|
13
|
+
* regardless of editor focus. The browser-side analogue of this seam is
|
|
14
|
+
* `applyGameGlobalsShadow` (`browser-transpile.ts`, A3).
|
|
15
|
+
*
|
|
16
|
+
* `dev.ts` builds its Vite server with `configFile: <repo-root>/vite.config.ts`
|
|
17
|
+
* AND adds its own plugins, so in dev BOTH registrations are live and both
|
|
18
|
+
* `transform` hooks see the ingest-fixture modules. The prelude check below is
|
|
19
|
+
* what makes that safe: whichever runs first prepends, the other sees the
|
|
20
|
+
* prelude already there and no-ops. Prepending twice would shadow the shadow —
|
|
21
|
+
* the inner `const window` would resolve to the outer `const window` rather than
|
|
22
|
+
* to the gated proxy — and `const` redeclaration in one scope is a SyntaxError
|
|
23
|
+
* besides.
|
|
24
|
+
*/
|
|
25
|
+
import type { Plugin } from 'vite';
|
|
26
|
+
import { shouldShadowGameGlobals } from './server/game-globals-shadow';
|
|
27
|
+
// The DOM-free prelude module, NOT `src/gated-globals.ts`: this file is reachable
|
|
28
|
+
// from `tsconfig.server.json` (via `server/dev.ts`), which compiles without the
|
|
29
|
+
// DOM lib and so cannot see `window`/`document`/`EventListenerOrEventListenerObject`.
|
|
30
|
+
import { GAME_GLOBALS_PRELUDE } from './src/game-globals-prelude';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `getRoots` is a thunk, not a snapshot: `dev.ts` mutates its root set when a
|
|
34
|
+
* project opens after boot (`onProjectOpened`), and the transform must see that.
|
|
35
|
+
*/
|
|
36
|
+
export function gameGlobalsShadowPlugin(getRoots: () => Iterable<string>): Plugin {
|
|
37
|
+
return {
|
|
38
|
+
name: 'vgai-game-globals',
|
|
39
|
+
transform(code: string, id: string) {
|
|
40
|
+
const file = id.split('?')[0]!;
|
|
41
|
+
if (!shouldShadowGameGlobals(file, getRoots())) return null;
|
|
42
|
+
if (code.startsWith(GAME_GLOBALS_PRELUDE)) return null; // already shadowed
|
|
43
|
+
return { code: GAME_GLOBALS_PRELUDE + code, map: null };
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vite-plugin-game-static — serve vendored real-game build output VERBATIM (no vite
|
|
3
|
+
* transform) so an externalized game bundle keeps its bare `import 'pixi.js'` specifiers
|
|
4
|
+
* (resolved by the iframe importmap, not rewritten to /node_modules/.vite/deps) and its
|
|
5
|
+
* AssetPack `.json` spritesheets stay raw JSON (not converted to ESM modules).
|
|
6
|
+
*
|
|
7
|
+
* Requests to `/pixi-game-static/<id>/<path>` are served from
|
|
8
|
+
* `vendor/pixi-games/_host/<id>-dist/<path>` with a correct content-type and zero
|
|
9
|
+
* transformation. This is pure HOST wiring — it never touches the vendored game source.
|
|
10
|
+
*/
|
|
11
|
+
import { readFile } from 'node:fs/promises';
|
|
12
|
+
import { extname, join, normalize, resolve } from 'node:path';
|
|
13
|
+
import type { Plugin } from 'vite';
|
|
14
|
+
|
|
15
|
+
// Exported so `vite-plugin-project-game-static.ts` (D-X2, the external-folder
|
|
16
|
+
// verbatim-serving sibling of this plugin) reuses the SAME MIME table rather
|
|
17
|
+
// than drifting a second copy — both plugins serve the identical class of
|
|
18
|
+
// content (a pixi bundle + AssetPack JSON + image/audio/font assets), just
|
|
19
|
+
// rooted at a different directory.
|
|
20
|
+
export const MIME: Record<string, string> = {
|
|
21
|
+
'.mjs': 'text/javascript',
|
|
22
|
+
'.js': 'text/javascript',
|
|
23
|
+
'.json': 'application/json',
|
|
24
|
+
'.png': 'image/png',
|
|
25
|
+
'.webp': 'image/webp',
|
|
26
|
+
'.jpg': 'image/jpeg',
|
|
27
|
+
'.jpeg': 'image/jpeg',
|
|
28
|
+
'.svg': 'image/svg+xml',
|
|
29
|
+
'.mp3': 'audio/mpeg',
|
|
30
|
+
'.ogg': 'audio/ogg',
|
|
31
|
+
'.wav': 'audio/wav',
|
|
32
|
+
'.woff2': 'font/woff2',
|
|
33
|
+
'.woff': 'font/woff',
|
|
34
|
+
'.ttf': 'font/ttf',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function gameStaticPlugin(repoRoot: string): Plugin {
|
|
38
|
+
const distRoot = resolve(repoRoot, 'vendor/pixi-games/_host');
|
|
39
|
+
return {
|
|
40
|
+
name: 'vgai-game-static',
|
|
41
|
+
configureServer(server) {
|
|
42
|
+
server.middlewares.use('/pixi-game-static', async (req, res, next) => {
|
|
43
|
+
try {
|
|
44
|
+
const url = (req.url ?? '').split('?')[0] ?? '';
|
|
45
|
+
// url is like /<id>/<path...>
|
|
46
|
+
const m = /^\/([^/]+)\/(.+)$/.exec(url);
|
|
47
|
+
if (!m) return next();
|
|
48
|
+
// Both groups are guaranteed present by the regex match above
|
|
49
|
+
// (two required capture groups) — `noUncheckedIndexedAccess`
|
|
50
|
+
// types tuple-destructured array elements as possibly-undefined
|
|
51
|
+
// regardless, so assert what the match already proved.
|
|
52
|
+
const id = m[1]!;
|
|
53
|
+
const rel = m[2]!;
|
|
54
|
+
const filePath = normalize(join(distRoot, `${id}-dist`, rel));
|
|
55
|
+
// path-traversal guard: stay inside distRoot
|
|
56
|
+
if (!filePath.startsWith(distRoot)) {
|
|
57
|
+
res.statusCode = 403;
|
|
58
|
+
return res.end('forbidden');
|
|
59
|
+
}
|
|
60
|
+
const buf = await readFile(filePath);
|
|
61
|
+
res.setHeader('Content-Type', MIME[extname(filePath)] ?? 'application/octet-stream');
|
|
62
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
63
|
+
res.statusCode = 200;
|
|
64
|
+
res.end(buf);
|
|
65
|
+
} catch {
|
|
66
|
+
next();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vite-plugin-project-game-static — D-X2
|
|
3
|
+
* (docs/EXTERNAL-FOLDER-INGEST-DESIGN.md §2/§6 slice S1): the external-folder
|
|
4
|
+
* sibling of `vite-plugin-game-static.ts`. Serves the CURRENTLY OPEN
|
|
5
|
+
* project's own folder VERBATIM (no vite transform) at
|
|
6
|
+
* `/project-game-static/<path>` -> `<projectRoot>/<path>`, so an external
|
|
7
|
+
* pixi game's manifest can point `bundleUrl`/`assetBaseUrl`/`pixiModuleUrl`
|
|
8
|
+
* at a route that keeps its bare `import 'pixi.js'` un-rewritten (resolved by
|
|
9
|
+
* the iframe importmap instead) and its AssetPack `.json` spritesheets raw —
|
|
10
|
+
* the SAME R-X1 rationale `vite-plugin-game-static.ts`'s header records for
|
|
11
|
+
* the vendored-dist route, just rooted at an arbitrary user folder instead of
|
|
12
|
+
* `vendor/pixi-games/_host`.
|
|
13
|
+
*
|
|
14
|
+
* `getProjectRoot` is a THUNK, not a captured string: `open-project` can
|
|
15
|
+
* switch the current project live (`server/dev.ts`'s `onProjectOpened`
|
|
16
|
+
* callback), and this route must follow the CURRENT project root, not a
|
|
17
|
+
* boot-time snapshot — every request re-reads it. No project open (thunk
|
|
18
|
+
* returns `undefined`) -> `next()`, never a 5xx.
|
|
19
|
+
*
|
|
20
|
+
* `configureServer`-only (local dev server; hosted/browser mode is out of
|
|
21
|
+
* scope by construction — both manifest ingest routes already short-circuit
|
|
22
|
+
* on `getEditorMode() === 'browser'` before ever building a URL that would
|
|
23
|
+
* point here, `ingest-mode.ts:944,994`).
|
|
24
|
+
*/
|
|
25
|
+
import { readFile } from 'node:fs/promises';
|
|
26
|
+
import { extname, join, normalize, resolve, sep } from 'node:path';
|
|
27
|
+
import type { Plugin } from 'vite';
|
|
28
|
+
import { MIME } from './vite-plugin-game-static';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resolve `rel` against `projectRoot`, refusing anything that escapes it.
|
|
32
|
+
* Plain `startsWith(root)` would wrongly admit a sibling folder that merely
|
|
33
|
+
* shares `root` as a string prefix (e.g. root `/a/b` and filePath `/a/bc/evil`)
|
|
34
|
+
* — the boundary must fall on a real path separator (or be an exact match).
|
|
35
|
+
* Returns `null` for an escape attempt.
|
|
36
|
+
*/
|
|
37
|
+
function resolveInsideProjectRoot(projectRoot: string, rel: string): string | null {
|
|
38
|
+
const root = resolve(projectRoot);
|
|
39
|
+
const filePath = normalize(join(root, rel));
|
|
40
|
+
if (filePath === root || filePath.startsWith(root + sep)) return filePath;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** `req.url`'s path portion (query stripped), project-root-relative (leading
|
|
45
|
+
* slash removed). Empty for the bare mount path (`/project-game-static`
|
|
46
|
+
* itself, with nothing after it). */
|
|
47
|
+
function requestRelPath(url: string | undefined): string {
|
|
48
|
+
// `.split('?')` always yields at least one element — `noUncheckedIndexedAccess`
|
|
49
|
+
// types index access as possibly-undefined regardless, so fall back to ''.
|
|
50
|
+
const path = (url ?? '').split('?')[0] ?? '';
|
|
51
|
+
return path.startsWith('/') ? path.slice(1) : path;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function projectGameStaticPlugin(getProjectRoot: () => string | undefined): Plugin {
|
|
55
|
+
return {
|
|
56
|
+
name: 'vgai-project-game-static',
|
|
57
|
+
configureServer(server) {
|
|
58
|
+
server.middlewares.use('/project-game-static', async (req, res, next) => {
|
|
59
|
+
const projectRoot = getProjectRoot();
|
|
60
|
+
if (!projectRoot) return next(); // no project open — nothing to serve
|
|
61
|
+
|
|
62
|
+
const rel = requestRelPath(req.url);
|
|
63
|
+
if (!rel) return next();
|
|
64
|
+
|
|
65
|
+
const filePath = resolveInsideProjectRoot(projectRoot, rel);
|
|
66
|
+
if (!filePath) {
|
|
67
|
+
res.statusCode = 403;
|
|
68
|
+
res.end('forbidden');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const buf = await readFile(filePath);
|
|
74
|
+
res.setHeader('Content-Type', MIME[extname(filePath)] ?? 'application/octet-stream');
|
|
75
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
76
|
+
res.statusCode = 200;
|
|
77
|
+
res.end(buf);
|
|
78
|
+
} catch {
|
|
79
|
+
next();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|