@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,301 @@
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
+
16
+ import fs from 'fs';
17
+ import path from 'path';
18
+
19
+ import * as ENGINE from '@gnsx/genesys.js';
20
+
21
+ import { getProjectRoot } from './common.js';
22
+ import { mockBrowserEnvironment } from './mock.js';
23
+ import { StorageProvider } from './storageProvider.js';
24
+
25
+ // Import game module to register game classes (tsx compiles TypeScript on the fly)
26
+ import '../../src/game.js';
27
+
28
+ // Set up browser environment for Node.js (provides document, window, etc.)
29
+ mockBrowserEnvironment();
30
+
31
+ // Set up storage provider for file operations
32
+ const storageProvider = new StorageProvider();
33
+ ENGINE.projectContext({ project: 'local-project', storageProvider: storageProvider });
34
+
35
+
36
+ interface FileInfo {
37
+ path: string;
38
+ type: 'scene' | 'prefab' | 'material';
39
+ }
40
+
41
+ const defaultWorldOptions = {
42
+ rendererDomElement: document.createElement('div'),
43
+ gameContainer: document.createElement('div'),
44
+ backgroundColor: 0x2E2E2E,
45
+ physicsOptions: {
46
+ engine: ENGINE.PhysicsEngine.Rapier,
47
+ gravity: ENGINE.MathHelpers.makeVector({ up: -9.81 }),
48
+ },
49
+ navigationOptions: {
50
+ engine: ENGINE.NavigationEngine.RecastNavigation,
51
+ },
52
+ useManifold: true
53
+ };
54
+
55
+ function findScenesAndPrefabs(dir: string, files: FileInfo[] = []): FileInfo[] {
56
+ const entries = fs.readdirSync(dir);
57
+
58
+ for (const entry of entries) {
59
+ const fullPath = path.join(dir, entry);
60
+ const stat = fs.statSync(fullPath);
61
+
62
+ if (stat.isDirectory()) {
63
+ // Skip common directories that shouldn't be processed
64
+ if (entry === 'node_modules' || entry === 'dist' || entry === '.git' || entry === '.engine') {
65
+ continue;
66
+ }
67
+ findScenesAndPrefabs(fullPath, files);
68
+ } else if (entry.endsWith('.genesys-scene')) {
69
+ files.push({ path: fullPath, type: 'scene' });
70
+ } else if (entry.endsWith('.prefab.json')) {
71
+ files.push({ path: fullPath, type: 'prefab' });
72
+ } else if (entry.endsWith('.material.json')) {
73
+ files.push({ path: fullPath, type: 'material' });
74
+ }
75
+ }
76
+
77
+ return files;
78
+ }
79
+
80
+ async function migrateSceneFile(data: any, filePath: string, relativePath: string): Promise<boolean> {
81
+ // Create a world and load into it
82
+ const world = new ENGINE.World(defaultWorldOptions);
83
+
84
+ if (ENGINE.isLegacyData(data)) {
85
+ // Use WorldSerializer for legacy data
86
+ await ENGINE.WorldSerializer.loadWorld(world, data);
87
+ } else {
88
+ // Use Loader for new format
89
+ const loader = new ENGINE.Loader();
90
+ await loader.loadToInstanceAsync(data, world);
91
+ }
92
+
93
+ // Dump the world
94
+ const dumper = new ENGINE.Dumper();
95
+ const newData = dumper.dump(world);
96
+
97
+ // Write back to file
98
+ const newContent = JSON.stringify(newData, null, 2);
99
+ fs.writeFileSync(filePath, newContent, 'utf-8');
100
+
101
+ console.log(`✅ ${relativePath}`);
102
+ return true;
103
+ }
104
+
105
+ async function migratePrefabFile(data: any, filePath: string, relativePath: string): Promise<boolean> {
106
+ let instance: any;
107
+
108
+ if (ENGINE.isLegacyData(data)) {
109
+ // Use WorldSerializer for legacy data
110
+ console.log(`🔍 Migrating legacy prefab: ${relativePath}`);
111
+ instance = await ENGINE.WorldSerializer.loadActor(data);
112
+ console.log(`✅ ${relativePath}`);
113
+ } else {
114
+ // Use Loader for new format
115
+ const loader = new ENGINE.Loader();
116
+ instance = await loader.loadAsync(data);
117
+ }
118
+
119
+ if (!instance) {
120
+ console.log(`⚠️ ${relativePath}: Loaded instance is null, skipping`);
121
+ return false;
122
+ }
123
+
124
+ // Dump using Dumper
125
+ const dumper = new ENGINE.Dumper({flags: ENGINE.DumperFlags.AsPrefab});
126
+ const newData = dumper.dump(instance);
127
+
128
+ // Write back to file
129
+ const newContent = JSON.stringify(newData, null, 2);
130
+ fs.writeFileSync(filePath, newContent, 'utf-8');
131
+
132
+ console.log(`✅ ${relativePath}`);
133
+ return true;
134
+ }
135
+
136
+ async function migrateMaterialFile(data: any, filePath: string, relativePath: string): Promise<boolean> {
137
+ let material: any;
138
+
139
+ if (ENGINE.isLegacyData(data)) {
140
+ console.log(`🔍 Migrated legacy material: ${relativePath}`);
141
+ material = ENGINE.WorldSerializer.importObject(data);
142
+ console.log(`✅ ${relativePath}`);
143
+ } else {
144
+ const loader = new ENGINE.Loader();
145
+ material = await loader.loadAsync(data);
146
+ }
147
+
148
+ if (!material) {
149
+ console.log(`⚠️ ${relativePath}: Loaded material is null, skipping`);
150
+ return false;
151
+ }
152
+
153
+ // Dump using Dumper
154
+ const dumper = new ENGINE.Dumper({flags:ENGINE.DumperFlags.AsPrefab});
155
+ const newData = dumper.dump(material);
156
+ // Write back to file
157
+ const newContent = JSON.stringify(newData, null, 2);
158
+ fs.writeFileSync(filePath, newContent, 'utf-8');
159
+
160
+ console.log(`✅ ${relativePath}`);
161
+ return true;
162
+ }
163
+
164
+ async function migrateFile(fileInfo: FileInfo): Promise<'success' | 'failure' | 'skipped'> {
165
+ const { path: filePath, type } = fileInfo;
166
+ const relativePath = path.relative(getProjectRoot(), filePath);
167
+
168
+ const content = fs.readFileSync(filePath, 'utf-8');
169
+ const data = JSON.parse(content);
170
+ // if (ENGINE.isUpdateToDateData(data)) {
171
+ // console.log(`ℹ️ ${relativePath}: Already up to date, skipping`);
172
+ // // return 'skipped';
173
+ // }
174
+
175
+ try {
176
+ if (type === 'scene') {
177
+ return await migrateSceneFile(data, filePath, relativePath) ? 'success' : 'failure';
178
+ } else if (type === 'prefab') {
179
+ return await migratePrefabFile(data, filePath, relativePath) ? 'success' : 'failure';
180
+ } else if (type === 'material') {
181
+ return await migrateMaterialFile(data, filePath, relativePath) ? 'success' : 'failure';
182
+ }
183
+ } catch (error) {
184
+ console.error(`❌ ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
185
+ return 'failure';
186
+ } finally {
187
+ console.log('');
188
+ }
189
+ return 'skipped';
190
+ }
191
+
192
+ interface MigrationResult {
193
+ successCount: number;
194
+ failCount: number;
195
+ }
196
+
197
+ async function runMigrationPass(
198
+ sceneFiles: FileInfo[],
199
+ prefabFiles: FileInfo[],
200
+ materialFiles: FileInfo[],
201
+ passNumber: number
202
+ ): Promise<MigrationResult> {
203
+ let successCount = 0;
204
+ let failCount = 0;
205
+
206
+ console.log(`\n${'='.repeat(60)}`);
207
+ console.log(`Migration Pass ${passNumber}`);
208
+ console.log(`${'='.repeat(60)}\n`);
209
+
210
+ // Migrate scenes first (must be done before prefabs to ensure engine can load new prefab data)
211
+ if (sceneFiles.length > 0) {
212
+ console.log('🌍 Migrating scenes...\n');
213
+ for (const fileInfo of sceneFiles) {
214
+ const success = await migrateFile(fileInfo);
215
+ if (success) {
216
+ successCount++;
217
+ } else {
218
+ failCount++;
219
+ }
220
+ }
221
+ }
222
+
223
+ // Then migrate prefabs
224
+ if (prefabFiles.length > 0) {
225
+ console.log('\n📦 Migrating prefabs...\n');
226
+ for (const fileInfo of prefabFiles) {
227
+ const success = await migrateFile(fileInfo);
228
+ if (success) {
229
+ successCount++;
230
+ } else {
231
+ failCount++;
232
+ }
233
+ }
234
+ }
235
+
236
+ // Finally migrate materials
237
+ if (materialFiles.length > 0) {
238
+ console.log('\n🎨 Migrating materials...\n');
239
+ for (const fileInfo of materialFiles) {
240
+ const success = await migrateFile(fileInfo);
241
+ if (success) {
242
+ successCount++;
243
+ } else {
244
+ failCount++;
245
+ }
246
+ }
247
+ }
248
+
249
+ return { successCount, failCount };
250
+ }
251
+
252
+ async function main() {
253
+ // Game classes are registered via the top-level import of dist/src/game.js
254
+ // Make sure to run `pnpm build` first!
255
+
256
+ const projectRoot = getProjectRoot();
257
+ const assetsFolder = path.join(projectRoot, 'assets');
258
+ console.log(`📁 Scanning for scene and prefab files in: ${assetsFolder}\n`);
259
+
260
+ // Find all scene and prefab files in the assets folder
261
+ const files = findScenesAndPrefabs(assetsFolder);
262
+
263
+ if (files.length === 0) {
264
+ console.log('⚠️ No scene or prefab files found.');
265
+ return;
266
+ }
267
+
268
+ const prefabFiles = files.filter(f => f.type === 'prefab');
269
+ const sceneFiles = files.filter(f => f.type === 'scene');
270
+ const materialFiles = files.filter(f => f.type === 'material');
271
+ console.log(`Found ${files.length} files to migrate:`);
272
+ console.log(` - ${sceneFiles.length} scene file(s)`);
273
+ console.log(` - ${prefabFiles.length} prefab file(s)`);
274
+ console.log(` - ${materialFiles.length} material file(s)`);
275
+
276
+ // Run migration twice to ensure prefab instances in scenes are saved properly
277
+ // (prefab instances are only saved correctly when both prefab and scene data are the same version)
278
+ const pass1 = await runMigrationPass(sceneFiles, prefabFiles, materialFiles, 1);
279
+ const pass2 = await runMigrationPass(sceneFiles, prefabFiles, materialFiles, 2);
280
+
281
+ const totalSuccess = pass1.successCount + pass2.successCount;
282
+ const totalFail = pass1.failCount + pass2.failCount;
283
+
284
+ console.log(`\n${'='.repeat(60)}`);
285
+ console.log('Migration Summary:');
286
+ console.log(` Total files per pass: ${files.length}`);
287
+ console.log(` Pass 1 - Successful: ${pass1.successCount}, Failed: ${pass1.failCount}`);
288
+ console.log(` Pass 2 - Successful: ${pass2.successCount}, Failed: ${pass2.failCount}`);
289
+ console.log(` Total - Successful: ${totalSuccess}, Failed: ${totalFail}`);
290
+ console.log(`${'='.repeat(60)}`);
291
+
292
+ if (totalFail > 0) {
293
+ process.exit(1);
294
+ }
295
+ }
296
+
297
+ main().catch((error) => {
298
+ console.error('Unexpected error:', error);
299
+ console.error('Stack trace:', error.stack);
300
+ process.exit(1);
301
+ });
@@ -0,0 +1,42 @@
1
+ import path from 'path';
2
+
3
+ import * as ENGINE from '@gnsx/genesys.js';
4
+
5
+ import { getProjectRoot } from './common.js';
6
+ import { isSubclass } from './mcp/utils.js';
7
+ import { fixUpClassName, registerGameClasses } from './mcp/utils.js';
8
+ import { StorageProvider } from './storageProvider.js';
9
+
10
+
11
+ export async function generateCode(className: string, filePath: string, baseClassName: string): Promise<boolean> {
12
+ try {
13
+ baseClassName = fixUpClassName(baseClassName);
14
+ }
15
+ catch (error) {
16
+ // if the base class name is not found, register all game classes and try again
17
+ await registerGameClasses();
18
+ baseClassName = fixUpClassName(baseClassName);
19
+ }
20
+
21
+ using context = ENGINE.scopedProjectContext({project: 'mcp-project', storageProvider: new StorageProvider()});
22
+
23
+ const testIsSubclass = (childName: string, parent: Function) => {
24
+ const child = ENGINE.ClassRegistry.getRegistry().get(childName);
25
+ if (!child) {
26
+ return false;
27
+ }
28
+ return isSubclass(child, parent);
29
+ };
30
+
31
+ const fullFilePath = path.isAbsolute(filePath) ? filePath : path.join(getProjectRoot(), filePath);
32
+
33
+ let fileGenerated = false;
34
+ const isSubclassOfActor = testIsSubclass(baseClassName, ENGINE.Actor);
35
+ if (isSubclassOfActor) {
36
+ await ENGINE.WorldCommands.generateActorTemplateFile(className, fullFilePath, baseClassName);
37
+ fileGenerated = true;
38
+ }
39
+
40
+ return fileGenerated;
41
+ }
42
+
@@ -0,0 +1,6 @@
1
+ import { mockBrowserEnvironment as engineMock } from '@gnsx/genesys.js';
2
+ import { JSDOM } from 'jsdom';
3
+
4
+ export function mockBrowserEnvironment() {
5
+ engineMock(JSDOM);
6
+ }
@@ -0,0 +1,179 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+
4
+ import { ThreeEulerSchema, ThreeVector3Schema } from './mcp/search-actors.js';
5
+ import { loadWorld, registerGameClassesIfAnyNotRegistered } from './mcp/utils.js';
6
+ import { mockBrowserEnvironment } from './mock.js';
7
+
8
+ import '../src/game.js';
9
+
10
+
11
+ mockBrowserEnvironment();
12
+
13
+ function convertConstructorParams(value: any): any {
14
+ // If not an object, return as is
15
+ if (!value || typeof value !== 'object') {
16
+ return value;
17
+ }
18
+
19
+ // Check if it's a Vector3
20
+ const vector3Result = ThreeVector3Schema.safeParse(value);
21
+ if (vector3Result.success) {
22
+ return new THREE.Vector3(
23
+ vector3Result.data.x,
24
+ vector3Result.data.y,
25
+ vector3Result.data.z
26
+ );
27
+ }
28
+
29
+ // Check if it's an Euler
30
+ const eulerResult = ThreeEulerSchema.safeParse(value);
31
+ if (eulerResult.success) {
32
+ return new THREE.Euler(
33
+ eulerResult.data.x,
34
+ eulerResult.data.y,
35
+ eulerResult.data.z
36
+ );
37
+ }
38
+
39
+ // Handle arrays
40
+ if (Array.isArray(value)) {
41
+ return value.map(convertConstructorParams);
42
+ }
43
+
44
+ // Handle objects
45
+ const result: Record<string, any> = {};
46
+ for (const [key, val] of Object.entries(value)) {
47
+ result[key] = convertConstructorParams(val);
48
+ }
49
+ return result;
50
+ }
51
+
52
+
53
+ export async function placePrimitive(args: {
54
+ sceneName: string;
55
+ primitiveActors: ENGINE.WorldCommands.PrimitiveActorArgs[];
56
+ }): Promise<string[]> {
57
+
58
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
59
+
60
+ const actors = ENGINE.WorldCommands.placePrimitives({
61
+ world: worldResource.world,
62
+ primitiveActors: args.primitiveActors,
63
+ });
64
+
65
+ return actors.map(actor => actor.uuid);
66
+ }
67
+
68
+
69
+ export async function removeActors(args: {
70
+ sceneName: string;
71
+ actorIds: string[];
72
+ }) {
73
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
74
+
75
+ ENGINE.WorldCommands.removeActorsByUuids({
76
+ world: worldResource.world,
77
+ actorIds: args.actorIds,
78
+ });
79
+ }
80
+
81
+ export async function addGltf(args: {
82
+ sceneName: string;
83
+ gltfs: ENGINE.WorldCommands.GltfArgs[];
84
+ }) {
85
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
86
+
87
+ const actors = await ENGINE.WorldCommands.placeGltfs({
88
+ world: worldResource.world,
89
+ gltfs: args.gltfs
90
+ });
91
+
92
+ return actors.map(actor => actor.uuid);
93
+ }
94
+
95
+ export async function placePrefab(args: {
96
+ sceneName: string;
97
+ prefabs: ENGINE.WorldCommands.PrefabArgs[];
98
+ }) {
99
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
100
+
101
+ const actors = await ENGINE.WorldCommands.placePrefabs({
102
+ world: worldResource.world,
103
+ prefabs: args.prefabs
104
+ });
105
+
106
+ return actors.map(actor => actor.uuid);
107
+ }
108
+
109
+ export async function placeJsClassActor(args: {
110
+ sceneName: string;
111
+ jsClasses: {
112
+ className: string;
113
+ constructorParams?: Record<string, any>[]
114
+ actorInfo?: ENGINE.WorldCommands.ActorMiscInfo;
115
+ }[]
116
+ }): Promise<string[]> {
117
+ await registerGameClassesIfAnyNotRegistered(
118
+ args.jsClasses.map(jsClass => jsClass.className)
119
+ );
120
+ using worldResource = await loadWorld(args.sceneName, { skipLoadingGLTF: true });
121
+
122
+ const actors: ENGINE.Actor[] = [];
123
+
124
+ for (const { className, constructorParams, actorInfo } of args.jsClasses) {
125
+ try {
126
+ // Convert constructor parameters if they exist
127
+ const convertedParams = constructorParams ? convertConstructorParams(constructorParams) : [];
128
+
129
+ const actor = ENGINE.ClassRegistry.constructObject(className, false, ...convertedParams);
130
+ if (actorInfo) {
131
+ Object.assign(actor.editorData, actorInfo);
132
+ }
133
+ actors.push(actor);
134
+ } catch (e) {
135
+ console.error(`Error constructing object ${className}`, e);
136
+ }
137
+ }
138
+
139
+ worldResource.world.addActors(...actors);
140
+ return actors.map(actor => actor.uuid);
141
+ }
142
+
143
+
144
+ export async function updateActors(args: {
145
+ sceneName: string;
146
+ actorsToUpdate: {
147
+ uuid: string;
148
+ transform?: ENGINE.WorldCommands.Transform;
149
+ actorInfo?: ENGINE.WorldCommands.ActorMiscInfo;
150
+ }[];
151
+ }): Promise<number> {
152
+ const readonly = args.actorsToUpdate.length == 0;
153
+ using worldResource = await loadWorld(args.sceneName, { readonly, skipLoadingGLTF: true });
154
+ let count = 0;
155
+
156
+ for (const { uuid, transform, actorInfo } of args.actorsToUpdate) {
157
+ const actor = worldResource.world.getActorByUuid(uuid);
158
+ if (!actor) {
159
+ continue;
160
+ }
161
+ if (transform) {
162
+ if (transform.position) {
163
+ actor.setWorldPosition(transform.position);
164
+ }
165
+ if (transform.rotation) {
166
+ actor.setWorldRotation(transform.rotation);
167
+ }
168
+ if (transform.scale) {
169
+ actor.setWorldScale(transform.scale);
170
+ }
171
+ if (actorInfo) {
172
+ Object.assign(actor.editorData, actorInfo);
173
+ }
174
+ }
175
+ count += 1;
176
+ }
177
+ return count;
178
+ }
179
+
@@ -0,0 +1,39 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import { getProjectRoot } from './common.js';
5
+
6
+
7
+ async function main() {
8
+ const engineInstallFolder = path.join(getProjectRoot(), 'node_modules/@gnsx/genesys.js');
9
+ if (!fs.existsSync(engineInstallFolder)) {
10
+ return;
11
+ }
12
+ const copiedEngineFolder = path.join(getProjectRoot(), '.engine');
13
+ if (fs.existsSync(copiedEngineFolder)) {
14
+ fs.rmdirSync(copiedEngineFolder, { recursive: true });
15
+ }
16
+ fs.mkdirSync(copiedEngineFolder, { recursive: true });
17
+
18
+ const foldersToCopy: string[] = [
19
+ 'games/examples',
20
+ 'src'
21
+ ];
22
+ for (const folder of foldersToCopy) {
23
+ const engineFolderPath = path.join(engineInstallFolder, folder);
24
+ const localFolderPath = path.join(copiedEngineFolder, folder);
25
+ fs.cpSync(engineFolderPath, localFolderPath, { recursive: true });
26
+ }
27
+
28
+ // copy all *.md files
29
+ const files = fs.readdirSync(engineInstallFolder);
30
+ for (const file of files) {
31
+ if (file.endsWith('.md')) {
32
+ const engineFilePath = path.join(engineInstallFolder, file);
33
+ const localFilePath = path.join(copiedEngineFolder, file);
34
+ fs.copyFileSync(engineFilePath, localFilePath);
35
+ }
36
+ }
37
+ }
38
+
39
+ main();
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema",
3
+ "$id": "genesys.js.prefab.schema.json",
4
+ "type": "object",
5
+ "properties": {
6
+ "$version": {
7
+ "type": "integer"
8
+ },
9
+ "class": {
10
+ "$ref": "class_string"
11
+ },
12
+ "ctor": {
13
+ "$ref": "actor_ctor"
14
+ },
15
+ "properties": {
16
+ "$ref": "actor_properties"
17
+ }
18
+ },
19
+ "definitions": {
20
+ "actor_ctor": {
21
+ "type": "object",
22
+ "properties": {
23
+ "rootComponent": {
24
+ "$ref": "component"
25
+ }
26
+ }
27
+ },
28
+ "actor_properties": {
29
+ "type": "object"
30
+ },
31
+ "component_ctor": {
32
+ "type": "object"
33
+ },
34
+ "component_properties": {
35
+ "type": "object",
36
+ "properties": {
37
+ "position": {
38
+ "$ref": "type_vector3"
39
+ },
40
+ "rotation": {
41
+ "$ref": "type_euler"
42
+ },
43
+ "scale": {
44
+ "$ref": "type_euler"
45
+ }
46
+ }
47
+ },
48
+ "type_vector3": {
49
+ "type": "array",
50
+ "minItems": 4,
51
+ "maxItems": 4,
52
+ "examples": [[0, 0, 0, "v"]]
53
+ },
54
+ "type_euler": {
55
+ "type": "array",
56
+ "minItems": 4,
57
+ "maxItems": 4,
58
+ "examples": [[0, 0, 0, "e"]]
59
+ },
60
+ "component": {
61
+ "type": "object",
62
+ "properties": {
63
+ "class": {
64
+ "$ref": "class_string"
65
+ },
66
+ "ctor": {
67
+ "$ref": "component_ctor"
68
+ },
69
+ "properties": {
70
+ "$ref": "component_properties"
71
+ },
72
+ "children": {
73
+ "type": "array",
74
+ "items": {
75
+ "$ref": "component"
76
+ }
77
+ }
78
+ }
79
+ },
80
+ "class_string": {
81
+ "type": "string",
82
+ "pattern": "(GAME|ENGINE)\\..*"
83
+ }
84
+ }
85
+ }