@directivegames/genesys.sdk 3.2.2

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.
Files changed (181) hide show
  1. package/README.md +60 -0
  2. package/dist/src/asset-pack/eslint.config.js +43 -0
  3. package/dist/src/asset-pack/scripts/postinstall.js +64 -0
  4. package/dist/src/asset-pack/src/index.js +1 -0
  5. package/dist/src/core/cli.js +306 -0
  6. package/dist/src/core/common.js +324 -0
  7. package/dist/src/core/index.js +6 -0
  8. package/dist/src/core/tools/build-project.js +450 -0
  9. package/dist/src/core/tools/index.js +2 -0
  10. package/dist/src/core/tools/new-asset-pack.js +150 -0
  11. package/dist/src/core/tools/new-project.js +292 -0
  12. package/dist/src/core/types.js +1 -0
  13. package/dist/src/dependencies.js +82 -0
  14. package/dist/src/electron/IpcSerializableError.js +38 -0
  15. package/dist/src/electron/api.js +7 -0
  16. package/dist/src/electron/backend/actions.js +56 -0
  17. package/dist/src/electron/backend/handler.js +441 -0
  18. package/dist/src/electron/backend/logging.js +41 -0
  19. package/dist/src/electron/backend/main.js +315 -0
  20. package/dist/src/electron/backend/menu.js +208 -0
  21. package/dist/src/electron/backend/state.js +201 -0
  22. package/dist/src/electron/backend/tools/const.js +9 -0
  23. package/dist/src/electron/backend/tools/file-server.js +383 -0
  24. package/dist/src/electron/backend/tools/open-project.js +261 -0
  25. package/dist/src/electron/backend/window.js +161 -0
  26. package/dist/src/templates/eslint.config.js +43 -0
  27. package/dist/src/templates/scripts/genesys/build-project.js +42 -0
  28. package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
  29. package/dist/src/templates/scripts/genesys/common.js +36 -0
  30. package/dist/src/templates/scripts/genesys/const.js +9 -0
  31. package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
  32. package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
  33. package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
  34. package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
  35. package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
  36. package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
  37. package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
  38. package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
  39. package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
  40. package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
  41. package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
  42. package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
  43. package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
  44. package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
  45. package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
  46. package/dist/src/templates/scripts/genesys/misc.js +32 -0
  47. package/dist/src/templates/scripts/genesys/mock.js +5 -0
  48. package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
  49. package/dist/src/templates/scripts/genesys/post-install.js +25 -0
  50. package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
  51. package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
  52. package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
  53. package/dist/src/templates/src/index.js +20 -0
  54. package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
  55. package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
  56. package/dist/src/templates/src/templates/firstPerson/src/player.js +60 -0
  57. package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
  58. package/dist/src/templates/src/templates/fps/src/game.js +30 -0
  59. package/dist/src/templates/src/templates/fps/src/player.js +64 -0
  60. package/dist/src/templates/src/templates/fps/src/weapon.js +62 -0
  61. package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
  62. package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
  63. package/dist/src/templates/src/templates/freeCamera/src/player.js +43 -0
  64. package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
  65. package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
  66. package/dist/src/templates/src/templates/sideScroller/src/game.js +103 -0
  67. package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
  68. package/dist/src/templates/src/templates/sideScroller/src/player.js +105 -0
  69. package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
  70. package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
  71. package/dist/src/templates/src/templates/thirdPerson/src/player.js +63 -0
  72. package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
  73. package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
  74. package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
  75. package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +189 -0
  76. package/dist/src/templates/src/templates/vehicle/src/player.js +102 -0
  77. package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +259 -0
  78. package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
  79. package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
  80. package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
  81. package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
  82. package/dist/src/templates/vite.config.js +46 -0
  83. package/package.json +176 -0
  84. package/scripts/post-install.ts +143 -0
  85. package/src/asset-pack/.gitattributes +89 -0
  86. package/src/asset-pack/eslint.config.js +45 -0
  87. package/src/asset-pack/gitignore +11 -0
  88. package/src/asset-pack/scripts/postinstall.ts +81 -0
  89. package/src/asset-pack/src/index.ts +0 -0
  90. package/src/asset-pack/tsconfig.json +34 -0
  91. package/src/templates/.cursor/mcp.json +20 -0
  92. package/src/templates/.cursorignore +2 -0
  93. package/src/templates/.gitattributes +89 -0
  94. package/src/templates/.vscode/settings.json +6 -0
  95. package/src/templates/AGENTS.md +86 -0
  96. package/src/templates/CLAUDE.md +1 -0
  97. package/src/templates/README.md +24 -0
  98. package/src/templates/eslint.config.js +45 -0
  99. package/src/templates/gitignore +11 -0
  100. package/src/templates/index.html +34 -0
  101. package/src/templates/pnpm-lock.yaml +3676 -0
  102. package/src/templates/scripts/genesys/build-project.ts +51 -0
  103. package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
  104. package/src/templates/scripts/genesys/common.ts +46 -0
  105. package/src/templates/scripts/genesys/const.ts +9 -0
  106. package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
  107. package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
  108. package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
  109. package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
  110. package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
  111. package/src/templates/scripts/genesys/doc-server.ts +16 -0
  112. package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
  113. package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
  114. package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
  115. package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
  116. package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
  117. package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
  118. package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
  119. package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
  120. package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
  121. package/src/templates/scripts/genesys/misc.ts +42 -0
  122. package/src/templates/scripts/genesys/mock.ts +6 -0
  123. package/src/templates/scripts/genesys/place-actors.ts +179 -0
  124. package/src/templates/scripts/genesys/post-install.ts +30 -0
  125. package/src/templates/scripts/genesys/prefab.schema.json +85 -0
  126. package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
  127. package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
  128. package/src/templates/scripts/genesys/storageProvider.ts +182 -0
  129. package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
  130. package/src/templates/src/index.ts +22 -0
  131. package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
  132. package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
  133. package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
  134. package/src/templates/src/templates/firstPerson/src/player.ts +63 -0
  135. package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
  136. package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
  137. package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
  138. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
  139. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
  140. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
  141. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
  142. package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
  143. package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
  144. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
  145. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
  146. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
  147. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
  148. package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
  149. package/src/templates/src/templates/fps/src/game.ts +39 -0
  150. package/src/templates/src/templates/fps/src/player.ts +69 -0
  151. package/src/templates/src/templates/fps/src/weapon.ts +54 -0
  152. package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
  153. package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
  154. package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
  155. package/src/templates/src/templates/freeCamera/src/player.ts +45 -0
  156. package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
  157. package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
  158. package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
  159. package/src/templates/src/templates/sideScroller/src/game.ts +122 -0
  160. package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
  161. package/src/templates/src/templates/sideScroller/src/player.ts +125 -0
  162. package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
  163. package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
  164. package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
  165. package/src/templates/src/templates/thirdPerson/src/player.ts +61 -0
  166. package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
  167. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
  168. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
  169. package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
  170. package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
  171. package/src/templates/src/templates/vehicle/src/game.ts +43 -0
  172. package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +191 -0
  173. package/src/templates/src/templates/vehicle/src/player.ts +109 -0
  174. package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +266 -0
  175. package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
  176. package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
  177. package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
  178. package/src/templates/src/templates/vr-game/src/game.ts +66 -0
  179. package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
  180. package/src/templates/tsconfig.json +35 -0
  181. package/src/templates/vite.config.ts +52 -0
@@ -0,0 +1,205 @@
1
+ import fs, { readFileSync } from 'fs';
2
+ import path, { resolve } from 'path';
3
+ import * as ENGINE from 'genesys.js';
4
+ import * as THREE from 'three';
5
+ import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
6
+ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
7
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
8
+ import { mockBrowserEnvironment } from './mock.js';
9
+ mockBrowserEnvironment();
10
+ // Custom error types for better error handling
11
+ class GLBLoadError extends Error {
12
+ filePath;
13
+ constructor(message, filePath) {
14
+ super(message);
15
+ this.filePath = filePath;
16
+ this.name = 'GLBLoadError';
17
+ }
18
+ }
19
+ class BoundingBoxCalculationError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'BoundingBoxCalculationError';
23
+ }
24
+ }
25
+ function verifyPath(path) {
26
+ if (path.startsWith(ENGINE.PROJECT_PATH_PREFIX) || path.startsWith(ENGINE.ENGINE_PATH_PREFIX)) {
27
+ throw new Error(`Expecting a valid path, not a project or engine path: ${path}`);
28
+ }
29
+ }
30
+ /**
31
+ * Helper function to load and parse a GLB file
32
+ * @param glbFilePath - Path to the GLB file
33
+ * @returns Promise resolving to the loaded GLTF scene
34
+ */
35
+ async function loadGLBFile(glbFilePath) {
36
+ verifyPath(glbFilePath);
37
+ // Resolve the absolute path
38
+ const absolutePath = resolve(glbFilePath);
39
+ try {
40
+ // Read the GLB file
41
+ const glbData = readFileSync(absolutePath);
42
+ const arrayBuffer = glbData.buffer.slice(glbData.byteOffset, glbData.byteOffset + glbData.byteLength);
43
+ // Load the GLB using GLTFLoader
44
+ const loader = new GLTFLoader();
45
+ loader.setMeshoptDecoder(MeshoptDecoder);
46
+ const dracoLoader = new DRACOLoader();
47
+ dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
48
+ loader.setDRACOLoader(dracoLoader);
49
+ const gltf = await loader.parseAsync(arrayBuffer, '');
50
+ if (!gltf.scene) {
51
+ throw new BoundingBoxCalculationError('No scene found in GLB file');
52
+ }
53
+ return gltf;
54
+ }
55
+ catch (error) {
56
+ if (error instanceof Error) {
57
+ if (error.name === 'GLBLoadError' || error.name === 'BoundingBoxCalculationError') {
58
+ throw error;
59
+ }
60
+ throw new GLBLoadError(`Failed to load GLB file: ${error.message}`, glbFilePath);
61
+ }
62
+ throw new GLBLoadError('Unknown error loading GLB file', glbFilePath);
63
+ }
64
+ }
65
+ /**
66
+ * Helper function to extract bounding box data from a Three.js Box3
67
+ * @param boundingBox - Three.js Box3 object
68
+ * @returns BoundingBox interface object
69
+ */
70
+ function extractBoundingBoxData(boundingBox) {
71
+ return ENGINE.DescriptionHelper.dumpBoundingBox(boundingBox);
72
+ }
73
+ /**
74
+ * Calculates the bounding box of a GLB mesh with applied transformations
75
+ * @param glbFilePath - Path to the GLB file
76
+ * @param transform - Transformation to apply (position, rotation, scale)
77
+ * @returns Promise resolving to the calculated bounding box
78
+ */
79
+ async function calculateGLBBoundingBox(glbFilePath, transform) {
80
+ const gltf = await loadGLBFile(glbFilePath);
81
+ // Create a new object to apply transformations
82
+ const transformedObject = new THREE.Object3D();
83
+ transformedObject.add(gltf.scene);
84
+ // Apply transformations
85
+ transform.position && transformedObject.position.set(...transform.position);
86
+ transform.rotation && transformedObject.rotation.set(...transform.rotation);
87
+ transform.scale && transformedObject.scale.set(...transform.scale);
88
+ // Update world matrix to ensure transformations are applied
89
+ transformedObject.updateMatrixWorld(true);
90
+ const boundingBox = ENGINE.GLTFMeshComponent.calcBoundingBoxFromGLTF(gltf);
91
+ return extractBoundingBoxData(boundingBox);
92
+ }
93
+ /**
94
+ * Calculates the original bounding box of a GLB mesh without any transformations
95
+ * @param glbFilePath - Path to the GLB file
96
+ * @returns Promise resolving to the original mesh bounding box
97
+ */
98
+ async function calculateGLBOriginalBoundingBox(glbFilePath) {
99
+ const gltf = await loadGLBFile(glbFilePath);
100
+ const boundingBox = ENGINE.GLTFMeshComponent.calcBoundingBoxFromGLTF(gltf);
101
+ return extractBoundingBoxData(boundingBox);
102
+ }
103
+ /**
104
+ * Helper function to create a default transform
105
+ */
106
+ function createTransform(position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1]) {
107
+ return { position, rotation, scale };
108
+ }
109
+ /**
110
+ * Utility function to convert degrees to radians
111
+ */
112
+ function degreesToRadians(degrees) {
113
+ return degrees * (Math.PI / 180);
114
+ }
115
+ /**
116
+ * Fetches the bounding box data from the manifest file, and returns the bounding box data
117
+ * It internally keeps a bounding box manifest as a cache, and only recalculates the bounding box data if the file has been modified since last calculation
118
+ * @param manifestFile - Path to the manifest JSON file, must be a valid path, not `@project` or `@engine` path
119
+ * @param gltfPaths - Dictionary of gltf key to GLTF file paths, key in theory can be any ID of a gltf mesh.
120
+ */
121
+ async function fetchBoundingBoxData(manifestFile, gltfPaths) {
122
+ verifyPath(manifestFile);
123
+ manifestFile = resolve(manifestFile);
124
+ try {
125
+ // Read existing manifest or create empty one
126
+ let manifest = {};
127
+ try {
128
+ if (fs.existsSync(manifestFile)) {
129
+ const manifestContent = fs.readFileSync(manifestFile, 'utf-8');
130
+ manifest = JSON.parse(manifestContent);
131
+ console.log(`Loaded existing manifest with ${Object.keys(manifest).length} entries`);
132
+ }
133
+ else {
134
+ console.log('Creating new manifest file');
135
+ }
136
+ }
137
+ catch (error) {
138
+ console.warn(`Failed to read existing manifest, starting fresh: ${error instanceof Error ? error.message : String(error)}`);
139
+ manifest = {};
140
+ }
141
+ // Process each GLTF file
142
+ const updatedManifest = { ...manifest };
143
+ let processedCount = 0;
144
+ let skippedCount = 0;
145
+ for (const [key, gltfPath] of Object.entries(gltfPaths)) {
146
+ try {
147
+ // Resolve absolute path for consistent comparison
148
+ const absolutePath = resolve(gltfPath);
149
+ // Check if file exists
150
+ if (!fs.existsSync(absolutePath)) {
151
+ console.warn(`Warning: File not found: ${gltfPath}`);
152
+ continue;
153
+ }
154
+ // Get file stats to check modification time
155
+ const stats = fs.statSync(absolutePath);
156
+ const currentTimestamp = stats.mtime.toISOString();
157
+ // Check if we need to update this file
158
+ const existingEntry = manifest[key];
159
+ if (existingEntry && existingEntry.timestamp === currentTimestamp) {
160
+ console.log(`Skipping ${gltfPath} (unchanged)`);
161
+ skippedCount++;
162
+ continue;
163
+ }
164
+ // Calculate bounding box for new/modified file
165
+ console.log(`Processing ${gltfPath}...`);
166
+ const boundingBox = await calculateGLBOriginalBoundingBox(absolutePath);
167
+ // Update manifest entry
168
+ updatedManifest[key] = {
169
+ bounding_box: boundingBox,
170
+ timestamp: currentTimestamp
171
+ };
172
+ processedCount++;
173
+ console.log(`✓ Updated bounding box for ${gltfPath}`);
174
+ }
175
+ catch (error) {
176
+ const errorMessage = error instanceof Error ? error.message : String(error);
177
+ console.error(`Failed to process ${gltfPath}: ${errorMessage}`);
178
+ // Continue processing other files even if one fails
179
+ }
180
+ }
181
+ // Save updated manifest if any changes were made
182
+ if (processedCount > 0) {
183
+ // Ensure directory exists
184
+ const manifestDir = path.dirname(manifestFile);
185
+ if (!fs.existsSync(manifestDir)) {
186
+ fs.mkdirSync(manifestDir, { recursive: true });
187
+ }
188
+ // Write manifest with pretty formatting
189
+ fs.writeFileSync(manifestFile, JSON.stringify(updatedManifest, null, 2), 'utf-8');
190
+ console.log(`\n✓ Manifest updated: ${processedCount} files processed, ${skippedCount} files skipped`);
191
+ console.log(`Manifest saved to: ${manifestFile}`);
192
+ }
193
+ else {
194
+ console.log(`\n✓ No updates needed: ${skippedCount} files already up to date`);
195
+ }
196
+ return Object.fromEntries(Object.entries(gltfPaths).map(([key, absPath]) => [key, updatedManifest[key].bounding_box]));
197
+ }
198
+ catch (error) {
199
+ const errorMessage = error instanceof Error ? error.message : String(error);
200
+ throw new Error(`Failed to update bounding box manifest: ${errorMessage}`);
201
+ }
202
+ }
203
+ // Export the main functions for use as a module
204
+ export { calculateGLBBoundingBox, calculateGLBOriginalBoundingBox, createTransform, degreesToRadians, fetchBoundingBoxData, extractBoundingBoxData, GLBLoadError, BoundingBoxCalculationError };
205
+ export { BoundingBoxSchema } from 'genesys.js';
@@ -0,0 +1,36 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { z } from 'zod';
5
+ export const isDev = process.env.NODE_ENV === 'development' ||
6
+ process.env.NODE_ENV === 'dev' ||
7
+ process.argv.includes('--dev');
8
+ export function mockEsModule() {
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ global.__filename = __filename;
12
+ global.__dirname = __dirname;
13
+ }
14
+ mockEsModule();
15
+ export function getProjectRoot() {
16
+ let currentDir = __dirname;
17
+ while (true) {
18
+ if (fs.existsSync(path.join(currentDir, 'package.json'))) {
19
+ return currentDir;
20
+ }
21
+ const parentDir = path.dirname(currentDir);
22
+ if (parentDir === currentDir) {
23
+ throw new Error('Project root not found');
24
+ }
25
+ currentDir = parentDir;
26
+ }
27
+ }
28
+ export const TransformSchema = z.object({
29
+ position: z.array(z.number()).length(3).optional().describe('Position as [x, y, z]'),
30
+ rotation: z.array(z.number()).length(3).optional().describe('Rotation in radians as [x, y, z]'),
31
+ scale: z.array(z.number()).length(3).optional().describe('Scale as [x, y, z], use this to scale the actor up or down'),
32
+ });
33
+ export const ActorInfoSchema = z.object({
34
+ displayName: z.string().describe('Display name of the actor'),
35
+ description: z.string().optional().describe('Description of the actor, including its purpose, how to use it, etc.'),
36
+ });
@@ -0,0 +1,9 @@
1
+ // MUST be kept in sync with https://github.com/directivegames/genesys.ai/blob/develop/src/const.ts
2
+ export const SCENE_EXTENSION = '.genesys-scene';
3
+ export const PUBLIC_FOLDER = 'public';
4
+ export const PROJECT_PREFIX = '@project';
5
+ export const ENGINE_PREFIX = '@engine';
6
+ export const DEFAULT_GAME_NAME = 'game.ts';
7
+ export const DEFAULT_GAME_BUNDLE_NAME = 'game.js';
8
+ export const BUILT_PROJECT_FOLDER = '.dist';
9
+ export const JS_CLASSES_DIR_NAME = '@js-classes';
@@ -0,0 +1,8 @@
1
+ import * as ENGINE from 'genesys.js';
2
+ import { defaultWorldOptions } from '../mcp/utils.js';
3
+ function main() {
4
+ const world = new ENGINE.World(defaultWorldOptions);
5
+ ENGINE.GameBuilder.createDefaultEditorScene(world);
6
+ console.log(JSON.stringify(world.asExportedObject(), null, 2));
7
+ }
8
+ main();
@@ -0,0 +1,116 @@
1
+ // pnpm exec tsx ./scripts/genesys/dev/generate-manifest.ts
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as ENGINE from 'genesys.js';
5
+ import { ManifestPath } from './storage-provider.js';
6
+ /**
7
+ * Recursively scans a directory and returns file information
8
+ */
9
+ function scanDirectory(dirPath, basePath = '') {
10
+ const items = [];
11
+ if (!fs.existsSync(dirPath)) {
12
+ return items;
13
+ }
14
+ try {
15
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
16
+ for (const entry of entries) {
17
+ const fullPath = path.join(dirPath, entry.name);
18
+ const relativePath = basePath ? path.join(basePath, entry.name) : entry.name;
19
+ const stats = fs.statSync(fullPath);
20
+ const item = {
21
+ name: entry.name,
22
+ path: relativePath.replace(/\\/g, '/'), // Normalize to forward slashes
23
+ size: stats.size,
24
+ modifiedTime: stats.mtime,
25
+ isDirectory: entry.isDirectory(),
26
+ contentType: getContentType(entry.name)
27
+ };
28
+ items.push(item);
29
+ // Recursively scan subdirectories
30
+ if (entry.isDirectory()) {
31
+ const subItems = scanDirectory(fullPath, relativePath);
32
+ items.push(...subItems);
33
+ }
34
+ }
35
+ }
36
+ catch (error) {
37
+ console.warn(`Failed to scan directory ${dirPath}:`, error);
38
+ }
39
+ return items;
40
+ }
41
+ /**
42
+ * Gets content type based on file extension
43
+ */
44
+ function getContentType(fileName) {
45
+ const ext = path.extname(fileName).toLowerCase();
46
+ const contentTypes = {
47
+ '.json': 'application/json',
48
+ '.js': 'application/javascript',
49
+ '.ts': 'application/typescript',
50
+ '.html': 'text/html',
51
+ '.css': 'text/css',
52
+ '.png': 'image/png',
53
+ '.jpg': 'image/jpeg',
54
+ '.jpeg': 'image/jpeg',
55
+ '.gif': 'image/gif',
56
+ '.webp': 'image/webp',
57
+ '.svg': 'image/svg+xml',
58
+ '.glb': 'model/gltf-binary',
59
+ '.gltf': 'model/gltf+json',
60
+ '.wav': 'audio/wav',
61
+ '.mp3': 'audio/mpeg',
62
+ '.ogg': 'audio/ogg',
63
+ '.genesys-scene': 'application/json',
64
+ '.genesys-project': 'application/json'
65
+ };
66
+ return contentTypes[ext];
67
+ }
68
+ /**
69
+ * Groups files by directory path for efficient lookup
70
+ */
71
+ function groupFilesByDirectory(files) {
72
+ const grouped = {};
73
+ // Add root directory
74
+ grouped[''] = [];
75
+ for (const file of files) {
76
+ const dir = path.dirname(file.path).replace(/\\/g, '/');
77
+ const normalizedDir = dir === '.' ? '' : dir;
78
+ if (!grouped[normalizedDir]) {
79
+ grouped[normalizedDir] = [];
80
+ }
81
+ grouped[normalizedDir].push(file);
82
+ }
83
+ return grouped;
84
+ }
85
+ /**
86
+ * Generates the file manifest
87
+ */
88
+ export async function generateManifest() {
89
+ console.log('Generating file manifest...');
90
+ const manifest = {
91
+ generated: new Date().toISOString(),
92
+ projectFiles: {},
93
+ engineFiles: {}
94
+ };
95
+ // Scan project files
96
+ console.log('Scanning project files...');
97
+ const projectRoot = process.cwd();
98
+ const builtProjectRoot = path.join(projectRoot, ENGINE.BUILT_PROJECT_FOLDER);
99
+ const projectFiles = scanDirectory(builtProjectRoot);
100
+ manifest.projectFiles = groupFilesByDirectory(projectFiles);
101
+ // Scan engine files
102
+ console.log('Scanning engine files...');
103
+ const engineAssetsRoot = path.join(projectRoot, 'node_modules', 'genesys.js', 'assets');
104
+ if (fs.existsSync(engineAssetsRoot)) {
105
+ const engineFiles = scanDirectory(engineAssetsRoot, 'assets');
106
+ manifest.engineFiles = groupFilesByDirectory(engineFiles);
107
+ console.log(`Engine files: ${Object.values(manifest.engineFiles).flat().length}`);
108
+ }
109
+ // Write manifest to public directory so it's accessible to the dev server
110
+ const manifestPath = path.join(projectRoot, ManifestPath);
111
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
112
+ console.log(`File manifest generated: ${manifestPath}`);
113
+ console.log(`Project files: ${Object.values(manifest.projectFiles).flat().length}`);
114
+ console.log(`Engine files: ${Object.values(manifest.engineFiles).flat().length}`);
115
+ }
116
+ generateManifest().catch(console.error);
@@ -0,0 +1,39 @@
1
+ import * as ENGINE from 'genesys.js';
2
+ import { DevStorageProvider } from './storage-provider.js';
3
+ // Ensure process is available globally with minimal implementation
4
+ if (typeof globalThis.process === 'undefined') {
5
+ globalThis.process = {
6
+ cwd: () => '/',
7
+ env: {},
8
+ };
9
+ }
10
+ // Initialize the game - can be called manually
11
+ export async function launchGame() {
12
+ const container = document.getElementById('game-container');
13
+ if (!container) {
14
+ console.error('Game container not found!');
15
+ return;
16
+ }
17
+ try {
18
+ // Create the storage provider for Vite dev environment
19
+ const storageProvider = new DevStorageProvider();
20
+ ENGINE.projectContext({
21
+ project: 'dev-game',
22
+ storageProvider,
23
+ });
24
+ // Create the game runtime
25
+ const gameRuntime = new ENGINE.GameRuntime({
26
+ provider: storageProvider,
27
+ container: container,
28
+ gameId: 'dev-game',
29
+ buildProject: false,
30
+ skipClearGameClasses: false
31
+ });
32
+ console.log('Starting game runtime...');
33
+ // Start the game with the default scene
34
+ await gameRuntime.startGame();
35
+ }
36
+ catch (error) {
37
+ console.error('Failed to initialize game:', error);
38
+ }
39
+ }
@@ -0,0 +1,188 @@
1
+ import * as ENGINE from 'genesys.js';
2
+ export const ManifestPath = 'dist/file-manifest.json';
3
+ export class DevStorageProvider {
4
+ manifestCache = null;
5
+ manifestPromise = null;
6
+ async loadManifest() {
7
+ if (this.manifestCache) {
8
+ return this.manifestCache;
9
+ }
10
+ if (this.manifestPromise) {
11
+ return this.manifestPromise;
12
+ }
13
+ this.manifestPromise = this.fetchManifest();
14
+ this.manifestCache = await this.manifestPromise;
15
+ this.manifestPromise = null;
16
+ return this.manifestCache;
17
+ }
18
+ async fetchManifest() {
19
+ try {
20
+ const response = await fetch(`/${ManifestPath}`);
21
+ if (!response.ok) {
22
+ throw new Error(`Failed to load manifest: ${response.status} ${response.statusText}`);
23
+ }
24
+ return await response.json();
25
+ }
26
+ catch (error) {
27
+ console.warn('Failed to load file manifest, falling back to empty manifest:', error);
28
+ return {
29
+ generated: new Date().toISOString(),
30
+ projectFiles: {},
31
+ engineFiles: {}
32
+ };
33
+ }
34
+ }
35
+ async resolvePath(path, expiry) {
36
+ if (path.isResolved()) {
37
+ return path;
38
+ }
39
+ let resolvedUrl;
40
+ if (path.initialPath.startsWith(ENGINE.PROJECT_PATH_PREFIX)) {
41
+ resolvedUrl = path.initialPath.replace(ENGINE.PROJECT_PATH_PREFIX, ENGINE.BUILT_PROJECT_FOLDER);
42
+ }
43
+ else if (path.initialPath.startsWith(ENGINE.ENGINE_PATH_PREFIX)) {
44
+ resolvedUrl = path.initialPath.replace(ENGINE.ENGINE_PATH_PREFIX, '/node_modules/genesys.js');
45
+ }
46
+ else if (path.initialPath.startsWith('/')) {
47
+ resolvedUrl = `/node_modules/genesys.js${path.initialPath}`;
48
+ }
49
+ else if (path.initialPath.startsWith('http') || path.initialPath.startsWith('https')) {
50
+ resolvedUrl = path.initialPath;
51
+ }
52
+ else {
53
+ // Paths without prefix are treated as engine paths
54
+ resolvedUrl = `/node_modules/genesys.js/${path.initialPath}`;
55
+ }
56
+ path.resolvePath(resolvedUrl, ENGINE.AssetPathEncodeState.Unknown);
57
+ return path;
58
+ }
59
+ async downloadFileAsBuffer(path) {
60
+ const resolvedPath = await this.resolvePath(path);
61
+ const response = await fetch(resolvedPath.getResolvedPath());
62
+ this.checkResponse(path, response);
63
+ return response.arrayBuffer();
64
+ }
65
+ async downloadFileAsJson(path) {
66
+ const resolvedPath = await this.resolvePath(path);
67
+ const response = await fetch(resolvedPath.getResolvedPath());
68
+ this.checkResponse(path, response);
69
+ return response.json();
70
+ }
71
+ async downloadFileAsText(path) {
72
+ const resolvedPath = await this.resolvePath(path);
73
+ const response = await fetch(resolvedPath.getResolvedPath());
74
+ this.checkResponse(path, response);
75
+ return response.text();
76
+ }
77
+ async uploadFile(path, content, options) {
78
+ throw new Error('ViteDevStorageProvider does not support uploading files.');
79
+ }
80
+ async listFiles(path, recursive, includeHiddenFiles) {
81
+ try {
82
+ const manifest = await this.loadManifest();
83
+ const initialPath = path.initialPath;
84
+ const targetPath = this.normalizePathForManifest(initialPath);
85
+ // Determine which file collection to use
86
+ // Paths without prefix are treated as engine assets
87
+ const isEngineAsset = initialPath.startsWith(ENGINE.ENGINE_PATH_PREFIX);
88
+ const isProjectAsset = initialPath.startsWith(ENGINE.PROJECT_PATH_PREFIX);
89
+ if (!isEngineAsset && !isProjectAsset) {
90
+ throw new Error(`Invalid path: ${path}`);
91
+ }
92
+ const fileCollection = isEngineAsset ? manifest.engineFiles : manifest.projectFiles;
93
+ const files = [];
94
+ const directories = [];
95
+ if (recursive) {
96
+ // For recursive listing, include all files that start with the target path
97
+ for (const [dirPath, items] of Object.entries(fileCollection)) {
98
+ if (dirPath === targetPath || dirPath.startsWith(targetPath + '/') || (targetPath === '' && !dirPath.includes('/'))) {
99
+ for (const item of items) {
100
+ const engineFileItem = this.convertToEngineFileItem(item, isEngineAsset);
101
+ if (!includeHiddenFiles && item.name.startsWith('.')) {
102
+ continue;
103
+ }
104
+ if (item.isDirectory) {
105
+ directories.push(engineFileItem);
106
+ }
107
+ else {
108
+ files.push(engineFileItem);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ else {
115
+ // For non-recursive listing, only include direct children
116
+ const directItems = fileCollection[targetPath] || [];
117
+ for (const item of directItems) {
118
+ const engineFileItem = this.convertToEngineFileItem(item, isEngineAsset);
119
+ if (!includeHiddenFiles && item.name.startsWith('.')) {
120
+ continue;
121
+ }
122
+ // Only include direct children (not nested)
123
+ const itemDirPath = item.path.substring(0, item.path.lastIndexOf('/'));
124
+ const normalizedItemDirPath = itemDirPath === '.' || itemDirPath === '' ? '' : itemDirPath;
125
+ if (normalizedItemDirPath === targetPath) {
126
+ if (item.isDirectory) {
127
+ directories.push(engineFileItem);
128
+ }
129
+ else {
130
+ files.push(engineFileItem);
131
+ }
132
+ }
133
+ }
134
+ }
135
+ return { files, directories };
136
+ }
137
+ catch (error) {
138
+ console.warn(`Failed to list files for path ${path.initialPath}:`, error);
139
+ return { files: [], directories: [] };
140
+ }
141
+ }
142
+ async exists(path) {
143
+ try {
144
+ const resolvedPath = await this.resolvePath(path);
145
+ const response = await fetch(resolvedPath.getResolvedPath(), { method: 'HEAD' });
146
+ return response.ok;
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ }
152
+ async buildCurrentProject(runTsc) {
153
+ // For development, we don't need to build the project
154
+ // Vite handles the compilation
155
+ return true;
156
+ }
157
+ normalizePathForManifest(pathStr) {
158
+ // Remove @project/ or @engine/ prefix
159
+ let normalized = pathStr.replace(ENGINE.PROJECT_PATH_PREFIX, '').replace(ENGINE.ENGINE_PATH_PREFIX, '');
160
+ // Remove leading slash if present
161
+ if (normalized.startsWith('/')) {
162
+ normalized = normalized.substring(1);
163
+ }
164
+ // Remove trailing slash if present
165
+ if (normalized.endsWith('/')) {
166
+ normalized = normalized.substring(0, normalized.length - 1);
167
+ }
168
+ return normalized;
169
+ }
170
+ convertToEngineFileItem(manifestItem, isEngineAsset) {
171
+ const prefix = isEngineAsset ? ENGINE.ENGINE_PATH_PREFIX : ENGINE.PROJECT_PATH_PREFIX;
172
+ const path = `${prefix}/${manifestItem.path}`;
173
+ return {
174
+ name: manifestItem.name,
175
+ path: path,
176
+ absolutePath: path,
177
+ size: manifestItem.size,
178
+ modifiedTime: new Date(manifestItem.modifiedTime),
179
+ isDirectory: manifestItem.isDirectory,
180
+ contentType: manifestItem.contentType
181
+ };
182
+ }
183
+ checkResponse(path, response) {
184
+ if (!response.ok) {
185
+ throw new Error(`Failed to fetch ${path.initialPath}: ${response.status} ${response.statusText}`);
186
+ }
187
+ }
188
+ }
@@ -0,0 +1,67 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { SCENE_EXTENSION } from '../const.js';
4
+ import { loadWorld } from '../mcp/utils.js';
5
+ import { mockBrowserEnvironment } from '../mock.js';
6
+ // Mock browser environment as required by the engine
7
+ mockBrowserEnvironment();
8
+ async function findGenesysSceneFiles(dir) {
9
+ const sceneFiles = [];
10
+ try {
11
+ const items = fs.readdirSync(dir, { withFileTypes: true });
12
+ for (const item of items) {
13
+ const fullPath = path.join(dir, item.name);
14
+ if (item.isDirectory()) {
15
+ // Recursively search subdirectories
16
+ const subFiles = await findGenesysSceneFiles(fullPath);
17
+ sceneFiles.push(...subFiles);
18
+ }
19
+ else if (item.isFile() && item.name.endsWith(SCENE_EXTENSION)) {
20
+ sceneFiles.push(fullPath);
21
+ }
22
+ }
23
+ }
24
+ catch (error) {
25
+ console.error(`Error reading directory ${dir}:`, error);
26
+ }
27
+ return sceneFiles;
28
+ }
29
+ async function updateSceneFile(sceneFilePath) {
30
+ console.log(`Processing: ${sceneFilePath}`);
31
+ try {
32
+ // Use the loadWorld utility with proper disposal using 'using' keyword
33
+ using worldResource = await loadWorld(sceneFilePath, {
34
+ readonly: false, // Allow saving changes
35
+ skipLoadingGLTF: true // Skip GLTF loading for faster processing
36
+ });
37
+ console.log(`✓ Successfully updated: ${sceneFilePath}`);
38
+ }
39
+ catch (error) {
40
+ console.error(`✗ Failed to update ${sceneFilePath}:`, error);
41
+ }
42
+ }
43
+ async function main() {
44
+ console.log('Searching for .genesys-scene files in src folder...');
45
+ const srcDir = path.join(process.cwd(), 'src');
46
+ if (!fs.existsSync(srcDir)) {
47
+ console.error('src directory not found');
48
+ return;
49
+ }
50
+ const sceneFiles = await findGenesysSceneFiles(srcDir);
51
+ if (sceneFiles.length === 0) {
52
+ console.log('No .genesys-scene files found in src folder');
53
+ return;
54
+ }
55
+ console.log(`Found ${sceneFiles.length} .genesys-scene files:`);
56
+ sceneFiles.forEach(file => console.log(` - ${path.relative(process.cwd(), file)}`));
57
+ console.log('\nUpdating scene files...');
58
+ // Process each scene file
59
+ for (const sceneFile of sceneFiles) {
60
+ await updateSceneFile(sceneFile);
61
+ }
62
+ console.log('\nScene update process completed!');
63
+ }
64
+ main().catch(error => {
65
+ console.error('Script failed:', error);
66
+ process.exit(1);
67
+ });