@gnsx/genesys.sdk 4.2.9

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 (185) hide show
  1. package/README.md +60 -0
  2. package/dist/src/asset-pack/eslint.config.js +58 -0
  3. package/dist/src/asset-pack/scripts/post-install.js +64 -0
  4. package/dist/src/asset-pack/src/index.js +1 -0
  5. package/dist/src/core/cli.js +303 -0
  6. package/dist/src/core/common.js +325 -0
  7. package/dist/src/core/index.js +6 -0
  8. package/dist/src/core/tools/build-project.js +456 -0
  9. package/dist/src/core/tools/index.js +2 -0
  10. package/dist/src/core/tools/new-asset-pack.js +153 -0
  11. package/dist/src/core/tools/new-project.js +293 -0
  12. package/dist/src/core/types.js +1 -0
  13. package/dist/src/dependencies.js +84 -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 +452 -0
  18. package/dist/src/electron/backend/logging.js +41 -0
  19. package/dist/src/electron/backend/main.js +369 -0
  20. package/dist/src/electron/backend/menu.js +196 -0
  21. package/dist/src/electron/backend/state.js +201 -0
  22. package/dist/src/electron/backend/telemetry.js +9 -0
  23. package/dist/src/electron/backend/tools/const.js +9 -0
  24. package/dist/src/electron/backend/tools/file-server.js +383 -0
  25. package/dist/src/electron/backend/tools/open-project.js +249 -0
  26. package/dist/src/electron/backend/window.js +161 -0
  27. package/dist/src/templates/eslint.config.js +58 -0
  28. package/dist/src/templates/scripts/genesys/build-project.js +42 -0
  29. package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
  30. package/dist/src/templates/scripts/genesys/common.js +36 -0
  31. package/dist/src/templates/scripts/genesys/const.js +9 -0
  32. package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
  33. package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
  34. package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
  35. package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
  36. package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
  37. package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
  38. package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
  39. package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
  40. package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
  41. package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
  42. package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
  43. package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
  44. package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
  45. package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
  46. package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
  47. package/dist/src/templates/scripts/genesys/migrate-scenes-and-prefabs.js +252 -0
  48. package/dist/src/templates/scripts/genesys/misc.js +32 -0
  49. package/dist/src/templates/scripts/genesys/mock.js +5 -0
  50. package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
  51. package/dist/src/templates/scripts/genesys/post-install.js +33 -0
  52. package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
  53. package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
  54. package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
  55. package/dist/src/templates/src/index.js +20 -0
  56. package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
  57. package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
  58. package/dist/src/templates/src/templates/firstPerson/src/player.js +55 -0
  59. package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
  60. package/dist/src/templates/src/templates/fps/src/game.js +30 -0
  61. package/dist/src/templates/src/templates/fps/src/player.js +60 -0
  62. package/dist/src/templates/src/templates/fps/src/weapon.js +54 -0
  63. package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
  64. package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
  65. package/dist/src/templates/src/templates/freeCamera/src/player.js +38 -0
  66. package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
  67. package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
  68. package/dist/src/templates/src/templates/sideScroller/src/game.js +102 -0
  69. package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
  70. package/dist/src/templates/src/templates/sideScroller/src/player.js +100 -0
  71. package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
  72. package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
  73. package/dist/src/templates/src/templates/thirdPerson/src/player.js +58 -0
  74. package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
  75. package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
  76. package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
  77. package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +188 -0
  78. package/dist/src/templates/src/templates/vehicle/src/player.js +97 -0
  79. package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +258 -0
  80. package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
  81. package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
  82. package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
  83. package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
  84. package/dist/src/templates/vite.config.js +46 -0
  85. package/package.json +181 -0
  86. package/scripts/post-install.ts +143 -0
  87. package/src/asset-pack/.gitattributes +89 -0
  88. package/src/asset-pack/.github/workflows/publish.yml +90 -0
  89. package/src/asset-pack/eslint.config.js +59 -0
  90. package/src/asset-pack/gitignore +11 -0
  91. package/src/asset-pack/scripts/post-install.ts +81 -0
  92. package/src/asset-pack/src/index.ts +0 -0
  93. package/src/asset-pack/tsconfig.json +34 -0
  94. package/src/templates/.cursor/mcp.json +20 -0
  95. package/src/templates/.cursorignore +2 -0
  96. package/src/templates/.gitattributes +89 -0
  97. package/src/templates/.vscode/settings.json +6 -0
  98. package/src/templates/AGENTS.md +104 -0
  99. package/src/templates/CLAUDE.md +1 -0
  100. package/src/templates/README.md +24 -0
  101. package/src/templates/eslint.config.js +60 -0
  102. package/src/templates/gitignore +11 -0
  103. package/src/templates/index.html +34 -0
  104. package/src/templates/pnpm-lock.yaml +3676 -0
  105. package/src/templates/scripts/genesys/build-project.ts +51 -0
  106. package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
  107. package/src/templates/scripts/genesys/common.ts +46 -0
  108. package/src/templates/scripts/genesys/const.ts +9 -0
  109. package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
  110. package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
  111. package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
  112. package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
  113. package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
  114. package/src/templates/scripts/genesys/doc-server.ts +16 -0
  115. package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
  116. package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
  117. package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
  118. package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
  119. package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
  120. package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
  121. package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
  122. package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
  123. package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
  124. package/src/templates/scripts/genesys/migrate-scenes-and-prefabs.ts +301 -0
  125. package/src/templates/scripts/genesys/misc.ts +42 -0
  126. package/src/templates/scripts/genesys/mock.ts +6 -0
  127. package/src/templates/scripts/genesys/place-actors.ts +179 -0
  128. package/src/templates/scripts/genesys/post-install.ts +39 -0
  129. package/src/templates/scripts/genesys/prefab.schema.json +85 -0
  130. package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
  131. package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
  132. package/src/templates/scripts/genesys/storageProvider.ts +182 -0
  133. package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
  134. package/src/templates/src/index.ts +22 -0
  135. package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
  136. package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
  137. package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
  138. package/src/templates/src/templates/firstPerson/src/player.ts +59 -0
  139. package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
  140. package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
  141. package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
  142. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
  143. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
  144. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
  145. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
  146. package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
  147. package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
  148. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
  149. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
  150. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
  151. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
  152. package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
  153. package/src/templates/src/templates/fps/src/game.ts +39 -0
  154. package/src/templates/src/templates/fps/src/player.ts +66 -0
  155. package/src/templates/src/templates/fps/src/weapon.ts +47 -0
  156. package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
  157. package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
  158. package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
  159. package/src/templates/src/templates/freeCamera/src/player.ts +40 -0
  160. package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
  161. package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
  162. package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
  163. package/src/templates/src/templates/sideScroller/src/game.ts +121 -0
  164. package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
  165. package/src/templates/src/templates/sideScroller/src/player.ts +123 -0
  166. package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
  167. package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
  168. package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
  169. package/src/templates/src/templates/thirdPerson/src/player.ts +58 -0
  170. package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
  171. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
  172. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
  173. package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
  174. package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
  175. package/src/templates/src/templates/vehicle/src/game.ts +43 -0
  176. package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +189 -0
  177. package/src/templates/src/templates/vehicle/src/player.ts +106 -0
  178. package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +264 -0
  179. package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
  180. package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
  181. package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
  182. package/src/templates/src/templates/vr-game/src/game.ts +66 -0
  183. package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
  184. package/src/templates/tsconfig.json +35 -0
  185. package/src/templates/vite.config.ts +52 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Migration script for Genesys scene, prefab, and material files.
3
+ *
4
+ * Migration order: Scenes → Prefabs → Materials
5
+ * This order is important because the engine cannot load new prefab data with old scene data.
6
+ *
7
+ * The migration runs TWICE:
8
+ * - Pass 1: Updates all files to the new format
9
+ * - Pass 2: Re-processes all files to ensure prefab instances in scenes are saved correctly
10
+ * (prefab instances are only serialized properly when both the prefab and scene
11
+ * data are at the same version)
12
+ *
13
+ * Usage: pnpm migrate (requires running `pnpm build` first to compile game classes)
14
+ */
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import * as ENGINE from '@gnsx/genesys.js';
18
+ import { getProjectRoot } from './common.js';
19
+ import { mockBrowserEnvironment } from './mock.js';
20
+ import { StorageProvider } from './storageProvider.js';
21
+ // Import game module to register game classes (tsx compiles TypeScript on the fly)
22
+ import '../../src/game.js';
23
+ // Set up browser environment for Node.js (provides document, window, etc.)
24
+ mockBrowserEnvironment();
25
+ // Set up storage provider for file operations
26
+ const storageProvider = new StorageProvider();
27
+ ENGINE.projectContext({ project: 'local-project', storageProvider: storageProvider });
28
+ const defaultWorldOptions = {
29
+ rendererDomElement: document.createElement('div'),
30
+ gameContainer: document.createElement('div'),
31
+ backgroundColor: 0x2E2E2E,
32
+ physicsOptions: {
33
+ engine: ENGINE.PhysicsEngine.Rapier,
34
+ gravity: ENGINE.MathHelpers.makeVector({ up: -9.81 }),
35
+ },
36
+ navigationOptions: {
37
+ engine: ENGINE.NavigationEngine.RecastNavigation,
38
+ },
39
+ useManifold: true
40
+ };
41
+ function findScenesAndPrefabs(dir, files = []) {
42
+ const entries = fs.readdirSync(dir);
43
+ for (const entry of entries) {
44
+ const fullPath = path.join(dir, entry);
45
+ const stat = fs.statSync(fullPath);
46
+ if (stat.isDirectory()) {
47
+ // Skip common directories that shouldn't be processed
48
+ if (entry === 'node_modules' || entry === 'dist' || entry === '.git' || entry === '.engine') {
49
+ continue;
50
+ }
51
+ findScenesAndPrefabs(fullPath, files);
52
+ }
53
+ else if (entry.endsWith('.genesys-scene')) {
54
+ files.push({ path: fullPath, type: 'scene' });
55
+ }
56
+ else if (entry.endsWith('.prefab.json')) {
57
+ files.push({ path: fullPath, type: 'prefab' });
58
+ }
59
+ else if (entry.endsWith('.material.json')) {
60
+ files.push({ path: fullPath, type: 'material' });
61
+ }
62
+ }
63
+ return files;
64
+ }
65
+ async function migrateSceneFile(data, filePath, relativePath) {
66
+ // Create a world and load into it
67
+ const world = new ENGINE.World(defaultWorldOptions);
68
+ if (ENGINE.isLegacyData(data)) {
69
+ // Use WorldSerializer for legacy data
70
+ await ENGINE.WorldSerializer.loadWorld(world, data);
71
+ }
72
+ else {
73
+ // Use Loader for new format
74
+ const loader = new ENGINE.Loader();
75
+ await loader.loadToInstanceAsync(data, world);
76
+ }
77
+ // Dump the world
78
+ const dumper = new ENGINE.Dumper();
79
+ const newData = dumper.dump(world);
80
+ // Write back to file
81
+ const newContent = JSON.stringify(newData, null, 2);
82
+ fs.writeFileSync(filePath, newContent, 'utf-8');
83
+ console.log(`✅ ${relativePath}`);
84
+ return true;
85
+ }
86
+ async function migratePrefabFile(data, filePath, relativePath) {
87
+ let instance;
88
+ if (ENGINE.isLegacyData(data)) {
89
+ // Use WorldSerializer for legacy data
90
+ console.log(`🔍 Migrating legacy prefab: ${relativePath}`);
91
+ instance = await ENGINE.WorldSerializer.loadActor(data);
92
+ console.log(`✅ ${relativePath}`);
93
+ }
94
+ else {
95
+ // Use Loader for new format
96
+ const loader = new ENGINE.Loader();
97
+ instance = await loader.loadAsync(data);
98
+ }
99
+ if (!instance) {
100
+ console.log(`⚠️ ${relativePath}: Loaded instance is null, skipping`);
101
+ return false;
102
+ }
103
+ // Dump using Dumper
104
+ const dumper = new ENGINE.Dumper({ flags: ENGINE.DumperFlags.AsPrefab });
105
+ const newData = dumper.dump(instance);
106
+ // Write back to file
107
+ const newContent = JSON.stringify(newData, null, 2);
108
+ fs.writeFileSync(filePath, newContent, 'utf-8');
109
+ console.log(`✅ ${relativePath}`);
110
+ return true;
111
+ }
112
+ async function migrateMaterialFile(data, filePath, relativePath) {
113
+ let material;
114
+ if (ENGINE.isLegacyData(data)) {
115
+ console.log(`🔍 Migrated legacy material: ${relativePath}`);
116
+ material = ENGINE.WorldSerializer.importObject(data);
117
+ console.log(`✅ ${relativePath}`);
118
+ }
119
+ else {
120
+ const loader = new ENGINE.Loader();
121
+ material = await loader.loadAsync(data);
122
+ }
123
+ if (!material) {
124
+ console.log(`⚠️ ${relativePath}: Loaded material is null, skipping`);
125
+ return false;
126
+ }
127
+ // Dump using Dumper
128
+ const dumper = new ENGINE.Dumper({ flags: ENGINE.DumperFlags.AsPrefab });
129
+ const newData = dumper.dump(material);
130
+ // Write back to file
131
+ const newContent = JSON.stringify(newData, null, 2);
132
+ fs.writeFileSync(filePath, newContent, 'utf-8');
133
+ console.log(`✅ ${relativePath}`);
134
+ return true;
135
+ }
136
+ async function migrateFile(fileInfo) {
137
+ const { path: filePath, type } = fileInfo;
138
+ const relativePath = path.relative(getProjectRoot(), filePath);
139
+ const content = fs.readFileSync(filePath, 'utf-8');
140
+ const data = JSON.parse(content);
141
+ // if (ENGINE.isUpdateToDateData(data)) {
142
+ // console.log(`ℹ️ ${relativePath}: Already up to date, skipping`);
143
+ // // return 'skipped';
144
+ // }
145
+ try {
146
+ if (type === 'scene') {
147
+ return await migrateSceneFile(data, filePath, relativePath) ? 'success' : 'failure';
148
+ }
149
+ else if (type === 'prefab') {
150
+ return await migratePrefabFile(data, filePath, relativePath) ? 'success' : 'failure';
151
+ }
152
+ else if (type === 'material') {
153
+ return await migrateMaterialFile(data, filePath, relativePath) ? 'success' : 'failure';
154
+ }
155
+ }
156
+ catch (error) {
157
+ console.error(`❌ ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
158
+ return 'failure';
159
+ }
160
+ finally {
161
+ console.log('');
162
+ }
163
+ return 'skipped';
164
+ }
165
+ async function runMigrationPass(sceneFiles, prefabFiles, materialFiles, passNumber) {
166
+ let successCount = 0;
167
+ let failCount = 0;
168
+ console.log(`\n${'='.repeat(60)}`);
169
+ console.log(`Migration Pass ${passNumber}`);
170
+ console.log(`${'='.repeat(60)}\n`);
171
+ // Migrate scenes first (must be done before prefabs to ensure engine can load new prefab data)
172
+ if (sceneFiles.length > 0) {
173
+ console.log('🌍 Migrating scenes...\n');
174
+ for (const fileInfo of sceneFiles) {
175
+ const success = await migrateFile(fileInfo);
176
+ if (success) {
177
+ successCount++;
178
+ }
179
+ else {
180
+ failCount++;
181
+ }
182
+ }
183
+ }
184
+ // Then migrate prefabs
185
+ if (prefabFiles.length > 0) {
186
+ console.log('\n📦 Migrating prefabs...\n');
187
+ for (const fileInfo of prefabFiles) {
188
+ const success = await migrateFile(fileInfo);
189
+ if (success) {
190
+ successCount++;
191
+ }
192
+ else {
193
+ failCount++;
194
+ }
195
+ }
196
+ }
197
+ // Finally migrate materials
198
+ if (materialFiles.length > 0) {
199
+ console.log('\n🎨 Migrating materials...\n');
200
+ for (const fileInfo of materialFiles) {
201
+ const success = await migrateFile(fileInfo);
202
+ if (success) {
203
+ successCount++;
204
+ }
205
+ else {
206
+ failCount++;
207
+ }
208
+ }
209
+ }
210
+ return { successCount, failCount };
211
+ }
212
+ async function main() {
213
+ // Game classes are registered via the top-level import of dist/src/game.js
214
+ // Make sure to run `pnpm build` first!
215
+ const projectRoot = getProjectRoot();
216
+ const assetsFolder = path.join(projectRoot, 'assets');
217
+ console.log(`📁 Scanning for scene and prefab files in: ${assetsFolder}\n`);
218
+ // Find all scene and prefab files in the assets folder
219
+ const files = findScenesAndPrefabs(assetsFolder);
220
+ if (files.length === 0) {
221
+ console.log('⚠️ No scene or prefab files found.');
222
+ return;
223
+ }
224
+ const prefabFiles = files.filter(f => f.type === 'prefab');
225
+ const sceneFiles = files.filter(f => f.type === 'scene');
226
+ const materialFiles = files.filter(f => f.type === 'material');
227
+ console.log(`Found ${files.length} files to migrate:`);
228
+ console.log(` - ${sceneFiles.length} scene file(s)`);
229
+ console.log(` - ${prefabFiles.length} prefab file(s)`);
230
+ console.log(` - ${materialFiles.length} material file(s)`);
231
+ // Run migration twice to ensure prefab instances in scenes are saved properly
232
+ // (prefab instances are only saved correctly when both prefab and scene data are the same version)
233
+ const pass1 = await runMigrationPass(sceneFiles, prefabFiles, materialFiles, 1);
234
+ const pass2 = await runMigrationPass(sceneFiles, prefabFiles, materialFiles, 2);
235
+ const totalSuccess = pass1.successCount + pass2.successCount;
236
+ const totalFail = pass1.failCount + pass2.failCount;
237
+ console.log(`\n${'='.repeat(60)}`);
238
+ console.log('Migration Summary:');
239
+ console.log(` Total files per pass: ${files.length}`);
240
+ console.log(` Pass 1 - Successful: ${pass1.successCount}, Failed: ${pass1.failCount}`);
241
+ console.log(` Pass 2 - Successful: ${pass2.successCount}, Failed: ${pass2.failCount}`);
242
+ console.log(` Total - Successful: ${totalSuccess}, Failed: ${totalFail}`);
243
+ console.log(`${'='.repeat(60)}`);
244
+ if (totalFail > 0) {
245
+ process.exit(1);
246
+ }
247
+ }
248
+ main().catch((error) => {
249
+ console.error('Unexpected error:', error);
250
+ console.error('Stack trace:', error.stack);
251
+ process.exit(1);
252
+ });
@@ -0,0 +1,32 @@
1
+ import path from 'path';
2
+ import * as ENGINE from '@gnsx/genesys.js';
3
+ import { getProjectRoot } from './common.js';
4
+ import { isSubclass } from './mcp/utils.js';
5
+ import { fixUpClassName, registerGameClasses } from './mcp/utils.js';
6
+ import { StorageProvider } from './storageProvider.js';
7
+ export async function generateCode(className, filePath, baseClassName) {
8
+ try {
9
+ baseClassName = fixUpClassName(baseClassName);
10
+ }
11
+ catch (error) {
12
+ // if the base class name is not found, register all game classes and try again
13
+ await registerGameClasses();
14
+ baseClassName = fixUpClassName(baseClassName);
15
+ }
16
+ using context = ENGINE.scopedProjectContext({ project: 'mcp-project', storageProvider: new StorageProvider() });
17
+ const testIsSubclass = (childName, parent) => {
18
+ const child = ENGINE.ClassRegistry.getRegistry().get(childName);
19
+ if (!child) {
20
+ return false;
21
+ }
22
+ return isSubclass(child, parent);
23
+ };
24
+ const fullFilePath = path.isAbsolute(filePath) ? filePath : path.join(getProjectRoot(), filePath);
25
+ let fileGenerated = false;
26
+ const isSubclassOfActor = testIsSubclass(baseClassName, ENGINE.Actor);
27
+ if (isSubclassOfActor) {
28
+ await ENGINE.WorldCommands.generateActorTemplateFile(className, fullFilePath, baseClassName);
29
+ fileGenerated = true;
30
+ }
31
+ return fileGenerated;
32
+ }
@@ -0,0 +1,5 @@
1
+ import { mockBrowserEnvironment as engineMock } from '@gnsx/genesys.js';
2
+ import { JSDOM } from 'jsdom';
3
+ export function mockBrowserEnvironment() {
4
+ engineMock(JSDOM);
5
+ }
@@ -0,0 +1,112 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+ import { ThreeEulerSchema, ThreeVector3Schema } from './mcp/search-actors.js';
4
+ import { loadWorld, registerGameClassesIfAnyNotRegistered } from './mcp/utils.js';
5
+ import { mockBrowserEnvironment } from './mock.js';
6
+ import '../src/game.js';
7
+ mockBrowserEnvironment();
8
+ function convertConstructorParams(value) {
9
+ // If not an object, return as is
10
+ if (!value || typeof value !== 'object') {
11
+ return value;
12
+ }
13
+ // Check if it's a Vector3
14
+ const vector3Result = ThreeVector3Schema.safeParse(value);
15
+ if (vector3Result.success) {
16
+ return new THREE.Vector3(vector3Result.data.x, vector3Result.data.y, vector3Result.data.z);
17
+ }
18
+ // Check if it's an Euler
19
+ const eulerResult = ThreeEulerSchema.safeParse(value);
20
+ if (eulerResult.success) {
21
+ return new THREE.Euler(eulerResult.data.x, eulerResult.data.y, eulerResult.data.z);
22
+ }
23
+ // Handle arrays
24
+ if (Array.isArray(value)) {
25
+ return value.map(convertConstructorParams);
26
+ }
27
+ // Handle objects
28
+ const result = {};
29
+ for (const [key, val] of Object.entries(value)) {
30
+ result[key] = convertConstructorParams(val);
31
+ }
32
+ return result;
33
+ }
34
+ export async function placePrimitive(args) {
35
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
36
+ const actors = ENGINE.WorldCommands.placePrimitives({
37
+ world: worldResource.world,
38
+ primitiveActors: args.primitiveActors,
39
+ });
40
+ return actors.map(actor => actor.uuid);
41
+ }
42
+ export async function removeActors(args) {
43
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
44
+ ENGINE.WorldCommands.removeActorsByUuids({
45
+ world: worldResource.world,
46
+ actorIds: args.actorIds,
47
+ });
48
+ }
49
+ export async function addGltf(args) {
50
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
51
+ const actors = await ENGINE.WorldCommands.placeGltfs({
52
+ world: worldResource.world,
53
+ gltfs: args.gltfs
54
+ });
55
+ return actors.map(actor => actor.uuid);
56
+ }
57
+ export async function placePrefab(args) {
58
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
59
+ const actors = await ENGINE.WorldCommands.placePrefabs({
60
+ world: worldResource.world,
61
+ prefabs: args.prefabs
62
+ });
63
+ return actors.map(actor => actor.uuid);
64
+ }
65
+ export async function placeJsClassActor(args) {
66
+ await registerGameClassesIfAnyNotRegistered(args.jsClasses.map(jsClass => jsClass.className));
67
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
68
+ const actors = [];
69
+ for (const { className, constructorParams, actorInfo } of args.jsClasses) {
70
+ try {
71
+ // Convert constructor parameters if they exist
72
+ const convertedParams = constructorParams ? convertConstructorParams(constructorParams) : [];
73
+ const actor = ENGINE.ClassRegistry.constructObject(className, false, ...convertedParams);
74
+ if (actorInfo) {
75
+ Object.assign(actor.editorData, actorInfo);
76
+ }
77
+ actors.push(actor);
78
+ }
79
+ catch (e) {
80
+ console.error(`Error constructing object ${className}`, e);
81
+ }
82
+ }
83
+ worldResource.world.addActors(...actors);
84
+ return actors.map(actor => actor.uuid);
85
+ }
86
+ export async function updateActors(args) {
87
+ const readonly = args.actorsToUpdate.length == 0;
88
+ using worldResource = await loadWorld(args.sceneName, { readonly, skipLoadingGLTF: true });
89
+ let count = 0;
90
+ for (const { uuid, transform, actorInfo } of args.actorsToUpdate) {
91
+ const actor = worldResource.world.getActorByUuid(uuid);
92
+ if (!actor) {
93
+ continue;
94
+ }
95
+ if (transform) {
96
+ if (transform.position) {
97
+ actor.setWorldPosition(transform.position);
98
+ }
99
+ if (transform.rotation) {
100
+ actor.setWorldRotation(transform.rotation);
101
+ }
102
+ if (transform.scale) {
103
+ actor.setWorldScale(transform.scale);
104
+ }
105
+ if (actorInfo) {
106
+ Object.assign(actor.editorData, actorInfo);
107
+ }
108
+ }
109
+ count += 1;
110
+ }
111
+ return count;
112
+ }
@@ -0,0 +1,33 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getProjectRoot } from './common.js';
4
+ async function main() {
5
+ const engineInstallFolder = path.join(getProjectRoot(), 'node_modules/@gnsx/genesys.js');
6
+ if (!fs.existsSync(engineInstallFolder)) {
7
+ return;
8
+ }
9
+ const copiedEngineFolder = path.join(getProjectRoot(), '.engine');
10
+ if (fs.existsSync(copiedEngineFolder)) {
11
+ fs.rmdirSync(copiedEngineFolder, { recursive: true });
12
+ }
13
+ fs.mkdirSync(copiedEngineFolder, { recursive: true });
14
+ const foldersToCopy = [
15
+ 'games/examples',
16
+ 'src'
17
+ ];
18
+ for (const folder of foldersToCopy) {
19
+ const engineFolderPath = path.join(engineInstallFolder, folder);
20
+ const localFolderPath = path.join(copiedEngineFolder, folder);
21
+ fs.cpSync(engineFolderPath, localFolderPath, { recursive: true });
22
+ }
23
+ // copy all *.md files
24
+ const files = fs.readdirSync(engineInstallFolder);
25
+ for (const file of files) {
26
+ if (file.endsWith('.md')) {
27
+ const engineFilePath = path.join(engineInstallFolder, file);
28
+ const localFilePath = path.join(copiedEngineFolder, file);
29
+ fs.copyFileSync(engineFilePath, localFilePath);
30
+ }
31
+ }
32
+ }
33
+ main();
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env tsx
2
+ import { readdir, readFile, stat, writeFile } from 'fs/promises';
3
+ import { join } from 'path';
4
+ /**
5
+ * Recursively finds all TypeScript files in a directory
6
+ */
7
+ async function findTsFiles(dir) {
8
+ const files = [];
9
+ try {
10
+ const entries = await readdir(dir);
11
+ for (const entry of entries) {
12
+ const fullPath = join(dir, entry);
13
+ const stats = await stat(fullPath);
14
+ if (stats.isDirectory()) {
15
+ const subFiles = await findTsFiles(fullPath);
16
+ files.push(...subFiles);
17
+ }
18
+ else if (entry.endsWith('.ts') || entry.endsWith('.tsx')) {
19
+ files.push(fullPath);
20
+ }
21
+ }
22
+ }
23
+ catch (error) {
24
+ console.warn(`Warning: Could not read directory ${dir}:`, error);
25
+ }
26
+ return files;
27
+ }
28
+ /**
29
+ * Removes comments from TypeScript code using regex
30
+ */
31
+ function removeComments(code) {
32
+ const originalLength = code.length;
33
+ // Remove single-line comments (// comments)
34
+ // This regex handles // comments but preserves URLs and string literals
35
+ let cleaned = code.replace(/^(\s*)\/\/.*$/gm, '$1');
36
+ // Remove multi-line comments (/* ... */ and /** ... */)
37
+ // This regex handles nested quotes and preserves strings
38
+ cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '');
39
+ // Clean up multiple consecutive empty lines (replace with single empty line)
40
+ cleaned = cleaned.replace(/\n\s*\n\s*\n/g, '\n\n');
41
+ // Trim trailing whitespace on each line
42
+ cleaned = cleaned.replace(/[ \t]+$/gm, '');
43
+ const hadComments = cleaned.length !== originalLength || cleaned !== code;
44
+ return { cleaned, hadComments };
45
+ }
46
+ /**
47
+ * Removes comments from a TypeScript file
48
+ */
49
+ async function removeCommentsFromFile(filePath) {
50
+ try {
51
+ const content = await readFile(filePath, 'utf8');
52
+ const { cleaned, hadComments } = removeComments(content);
53
+ if (!hadComments) {
54
+ return false; // No comments to remove
55
+ }
56
+ await writeFile(filePath, cleaned, 'utf8');
57
+ return true;
58
+ }
59
+ catch (error) {
60
+ console.error(`Error processing file ${filePath}:`, error);
61
+ return false;
62
+ }
63
+ }
64
+ async function main() {
65
+ const engineDir = '.engine';
66
+ console.log(`Searching for TypeScript files in ${engineDir}...`);
67
+ console.log(`Current working directory: ${process.cwd()}`);
68
+ try {
69
+ const tsFiles = await findTsFiles(engineDir);
70
+ console.log(`Found ${tsFiles.length} TypeScript files.`);
71
+ if (tsFiles.length > 0) {
72
+ console.log('First few files:');
73
+ tsFiles.slice(0, 5).forEach(file => console.log(` - ${file}`));
74
+ }
75
+ if (tsFiles.length === 0) {
76
+ console.log('No TypeScript files found in .engine folder.');
77
+ return;
78
+ }
79
+ let processedCount = 0;
80
+ let modifiedCount = 0;
81
+ for (const filePath of tsFiles) {
82
+ console.log(`Processing: ${filePath}`);
83
+ try {
84
+ const wasModified = await removeCommentsFromFile(filePath);
85
+ processedCount++;
86
+ if (wasModified) {
87
+ modifiedCount++;
88
+ console.log(' ✓ Comments removed');
89
+ }
90
+ else {
91
+ console.log(' - No comments found');
92
+ }
93
+ }
94
+ catch (error) {
95
+ console.error(` ✗ Error: ${error}`);
96
+ }
97
+ }
98
+ console.log('\nCompleted:');
99
+ console.log(`- Files processed: ${processedCount}`);
100
+ console.log(`- Files modified: ${modifiedCount}`);
101
+ console.log(`- Files skipped: ${processedCount - modifiedCount}`);
102
+ }
103
+ catch (error) {
104
+ console.error('Error:', error);
105
+ process.exit(1);
106
+ }
107
+ }
108
+ // Always run main if this file is executed directly
109
+ main().catch((error) => {
110
+ console.error('Unexpected error:', error);
111
+ console.error('Stack trace:', error.stack);
112
+ process.exit(1);
113
+ });