@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.
- package/README.md +60 -0
- package/dist/src/asset-pack/eslint.config.js +43 -0
- package/dist/src/asset-pack/scripts/postinstall.js +64 -0
- package/dist/src/asset-pack/src/index.js +1 -0
- package/dist/src/core/cli.js +306 -0
- package/dist/src/core/common.js +324 -0
- package/dist/src/core/index.js +6 -0
- package/dist/src/core/tools/build-project.js +450 -0
- package/dist/src/core/tools/index.js +2 -0
- package/dist/src/core/tools/new-asset-pack.js +150 -0
- package/dist/src/core/tools/new-project.js +292 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/dependencies.js +82 -0
- package/dist/src/electron/IpcSerializableError.js +38 -0
- package/dist/src/electron/api.js +7 -0
- package/dist/src/electron/backend/actions.js +56 -0
- package/dist/src/electron/backend/handler.js +441 -0
- package/dist/src/electron/backend/logging.js +41 -0
- package/dist/src/electron/backend/main.js +315 -0
- package/dist/src/electron/backend/menu.js +208 -0
- package/dist/src/electron/backend/state.js +201 -0
- package/dist/src/electron/backend/tools/const.js +9 -0
- package/dist/src/electron/backend/tools/file-server.js +383 -0
- package/dist/src/electron/backend/tools/open-project.js +261 -0
- package/dist/src/electron/backend/window.js +161 -0
- package/dist/src/templates/eslint.config.js +43 -0
- package/dist/src/templates/scripts/genesys/build-project.js +42 -0
- package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
- package/dist/src/templates/scripts/genesys/common.js +36 -0
- package/dist/src/templates/scripts/genesys/const.js +9 -0
- package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
- package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
- package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
- package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
- package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
- package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
- package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
- package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
- package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
- package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
- package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
- package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
- package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
- package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
- package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
- package/dist/src/templates/scripts/genesys/misc.js +32 -0
- package/dist/src/templates/scripts/genesys/mock.js +5 -0
- package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
- package/dist/src/templates/scripts/genesys/post-install.js +25 -0
- package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
- package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
- package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
- package/dist/src/templates/src/index.js +20 -0
- package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
- package/dist/src/templates/src/templates/firstPerson/src/player.js +60 -0
- package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/fps/src/game.js +30 -0
- package/dist/src/templates/src/templates/fps/src/player.js +64 -0
- package/dist/src/templates/src/templates/fps/src/weapon.js +62 -0
- package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
- package/dist/src/templates/src/templates/freeCamera/src/player.js +43 -0
- package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
- package/dist/src/templates/src/templates/sideScroller/src/game.js +103 -0
- package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
- package/dist/src/templates/src/templates/sideScroller/src/player.js +105 -0
- package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
- package/dist/src/templates/src/templates/thirdPerson/src/player.js +63 -0
- package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
- package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
- package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +189 -0
- package/dist/src/templates/src/templates/vehicle/src/player.js +102 -0
- package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +259 -0
- package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
- package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
- package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
- package/dist/src/templates/vite.config.js +46 -0
- package/package.json +176 -0
- package/scripts/post-install.ts +143 -0
- package/src/asset-pack/.gitattributes +89 -0
- package/src/asset-pack/eslint.config.js +45 -0
- package/src/asset-pack/gitignore +11 -0
- package/src/asset-pack/scripts/postinstall.ts +81 -0
- package/src/asset-pack/src/index.ts +0 -0
- package/src/asset-pack/tsconfig.json +34 -0
- package/src/templates/.cursor/mcp.json +20 -0
- package/src/templates/.cursorignore +2 -0
- package/src/templates/.gitattributes +89 -0
- package/src/templates/.vscode/settings.json +6 -0
- package/src/templates/AGENTS.md +86 -0
- package/src/templates/CLAUDE.md +1 -0
- package/src/templates/README.md +24 -0
- package/src/templates/eslint.config.js +45 -0
- package/src/templates/gitignore +11 -0
- package/src/templates/index.html +34 -0
- package/src/templates/pnpm-lock.yaml +3676 -0
- package/src/templates/scripts/genesys/build-project.ts +51 -0
- package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
- package/src/templates/scripts/genesys/common.ts +46 -0
- package/src/templates/scripts/genesys/const.ts +9 -0
- package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
- package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
- package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
- package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
- package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
- package/src/templates/scripts/genesys/doc-server.ts +16 -0
- package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
- package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
- package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
- package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
- package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
- package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
- package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
- package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
- package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
- package/src/templates/scripts/genesys/misc.ts +42 -0
- package/src/templates/scripts/genesys/mock.ts +6 -0
- package/src/templates/scripts/genesys/place-actors.ts +179 -0
- package/src/templates/scripts/genesys/post-install.ts +30 -0
- package/src/templates/scripts/genesys/prefab.schema.json +85 -0
- package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
- package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
- package/src/templates/scripts/genesys/storageProvider.ts +182 -0
- package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
- package/src/templates/src/index.ts +22 -0
- package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
- package/src/templates/src/templates/firstPerson/src/player.ts +63 -0
- package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
- package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/fps/src/game.ts +39 -0
- package/src/templates/src/templates/fps/src/player.ts +69 -0
- package/src/templates/src/templates/fps/src/weapon.ts +54 -0
- package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
- package/src/templates/src/templates/freeCamera/src/player.ts +45 -0
- package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
- package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
- package/src/templates/src/templates/sideScroller/src/game.ts +122 -0
- package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
- package/src/templates/src/templates/sideScroller/src/player.ts +125 -0
- package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
- package/src/templates/src/templates/thirdPerson/src/player.ts +61 -0
- package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
- package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
- package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
- package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
- package/src/templates/src/templates/vehicle/src/game.ts +43 -0
- package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +191 -0
- package/src/templates/src/templates/vehicle/src/player.ts +109 -0
- package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +266 -0
- package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
- package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
- package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
- package/src/templates/src/templates/vr-game/src/game.ts +66 -0
- package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
- package/src/templates/tsconfig.json +35 -0
- package/src/templates/vite.config.ts +52 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as ENGINE from 'genesys.js';
|
|
4
|
+
import { findFilesByPredicate, getProjectFolderAndFile, runCommandAsync } from '../index.js';
|
|
5
|
+
function getTscBinaryPath(projectFolder, logger) {
|
|
6
|
+
// Look in the project's node_modules for tsc
|
|
7
|
+
const projectBinaryPath = path.join(projectFolder, 'node_modules', '.bin', process.platform === 'win32' ? 'tsc.cmd' : 'tsc');
|
|
8
|
+
if (fs.existsSync(projectBinaryPath)) {
|
|
9
|
+
logger.log(`Found tsc binary at: ${projectBinaryPath}`);
|
|
10
|
+
return projectBinaryPath;
|
|
11
|
+
}
|
|
12
|
+
// Fallback to pnpm exec
|
|
13
|
+
logger.log('tsc not found in project node_modules, using pnpm exec');
|
|
14
|
+
return 'pnpm exec tsc';
|
|
15
|
+
}
|
|
16
|
+
function getEsbuildBinaryPath(projectFolder, logger, isPackaged, resourcesPath) {
|
|
17
|
+
// In packaged app, esbuild binary is in app.asar.unpacked
|
|
18
|
+
if (isPackaged) {
|
|
19
|
+
const platform = process.platform;
|
|
20
|
+
const arch = process.arch;
|
|
21
|
+
// Construct the platform-specific binary path
|
|
22
|
+
let binaryName = 'esbuild';
|
|
23
|
+
let platformFolder = '';
|
|
24
|
+
if (platform === 'win32') {
|
|
25
|
+
binaryName = 'esbuild.exe';
|
|
26
|
+
platformFolder = `win32-${arch}`;
|
|
27
|
+
}
|
|
28
|
+
else if (platform === 'darwin') {
|
|
29
|
+
platformFolder = `darwin-${arch}`;
|
|
30
|
+
}
|
|
31
|
+
else if (platform === 'linux') {
|
|
32
|
+
platformFolder = `linux-${arch}`;
|
|
33
|
+
}
|
|
34
|
+
// Try to find the binary in app.asar.unpacked
|
|
35
|
+
const unpackedPath = path.join(resourcesPath, 'app.asar.unpacked', 'node_modules', '@esbuild', platformFolder, binaryName);
|
|
36
|
+
if (fs.existsSync(unpackedPath)) {
|
|
37
|
+
logger.log(`Found esbuild binary at: ${unpackedPath}`);
|
|
38
|
+
return unpackedPath;
|
|
39
|
+
}
|
|
40
|
+
logger.warn('esbuild binary not found in app.asar.unpacked, trying SDK\'s node_modules');
|
|
41
|
+
}
|
|
42
|
+
// In dev mode or as fallback, look in the project's node_modules
|
|
43
|
+
const projectBinaryPath = path.join(projectFolder, 'node_modules', '.bin', process.platform === 'win32' ? 'esbuild.cmd' : 'esbuild');
|
|
44
|
+
if (fs.existsSync(projectBinaryPath)) {
|
|
45
|
+
logger.log(`Found esbuild binary in project at: ${projectBinaryPath}`);
|
|
46
|
+
return projectBinaryPath;
|
|
47
|
+
}
|
|
48
|
+
// When running as CLI (not packaged), also check the SDK's own node_modules
|
|
49
|
+
// resourcesPath points to the SDK root in CLI mode
|
|
50
|
+
const sdkBinaryPath = path.join(resourcesPath, 'node_modules', '.bin', process.platform === 'win32' ? 'esbuild.cmd' : 'esbuild');
|
|
51
|
+
if (fs.existsSync(sdkBinaryPath)) {
|
|
52
|
+
logger.log(`Found esbuild binary in SDK at: ${sdkBinaryPath}`);
|
|
53
|
+
return sdkBinaryPath;
|
|
54
|
+
}
|
|
55
|
+
// Last resort - use pnpm dlx
|
|
56
|
+
return 'pnpm dlx esbuild';
|
|
57
|
+
}
|
|
58
|
+
function gatherAssets(data) {
|
|
59
|
+
const assets = [];
|
|
60
|
+
for (const [k, v] of Object.entries(data)) {
|
|
61
|
+
if (typeof v === 'string' && v.startsWith(ENGINE.PROJECT_PATH_PREFIX)) {
|
|
62
|
+
assets.push(v);
|
|
63
|
+
}
|
|
64
|
+
if (typeof v === 'object' && v !== null) {
|
|
65
|
+
assets.push(...gatherAssets(v));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return assets;
|
|
69
|
+
}
|
|
70
|
+
export function findFilesWithExtension(dir, extension) {
|
|
71
|
+
// Normalize extension (ensure it starts with dot)
|
|
72
|
+
const ext = extension.startsWith('.') ? extension : `.${extension}`;
|
|
73
|
+
let results = [];
|
|
74
|
+
const files = fs.readdirSync(dir);
|
|
75
|
+
for (const file of files) {
|
|
76
|
+
const filePath = path.join(dir, file);
|
|
77
|
+
const stat = fs.statSync(filePath);
|
|
78
|
+
if (stat.isDirectory()) {
|
|
79
|
+
results = results.concat(findFilesWithExtension(filePath, ext));
|
|
80
|
+
}
|
|
81
|
+
else if (filePath.endsWith(ext)) {
|
|
82
|
+
results.push(filePath);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
async function copyAssets(projectPath, distFolder, alwaysCopyAssets, logger) {
|
|
88
|
+
try {
|
|
89
|
+
// Find all files that can contain asset paths in distFolder
|
|
90
|
+
const beginTime = Date.now();
|
|
91
|
+
const jsonFiles = findFilesWithExtension(distFolder, 'json');
|
|
92
|
+
const jsFiles = findFilesWithExtension(distFolder, 'js');
|
|
93
|
+
const htmlFiles = findFilesWithExtension(distFolder, 'html');
|
|
94
|
+
const cssFiles = findFilesWithExtension(distFolder, 'css');
|
|
95
|
+
const sceneFiles = findFilesWithExtension(distFolder, ENGINE.SCENE_NAME_EXT);
|
|
96
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
97
|
+
logger.log(`Stats: findFilesWithExtension done in ${durationSec}s`);
|
|
98
|
+
const assets = new Set();
|
|
99
|
+
const processedAssets = new Set();
|
|
100
|
+
// include the scene files in the json files
|
|
101
|
+
jsonFiles.push(...sceneFiles);
|
|
102
|
+
// Add always copy assets
|
|
103
|
+
for (const asset of alwaysCopyAssets) {
|
|
104
|
+
let sourceAssetPath = asset;
|
|
105
|
+
const withProjectPrefix = asset.startsWith(ENGINE.PROJECT_PATH_PREFIX);
|
|
106
|
+
if (withProjectPrefix) {
|
|
107
|
+
sourceAssetPath = asset.replace(ENGINE.PROJECT_PATH_PREFIX, projectPath);
|
|
108
|
+
}
|
|
109
|
+
else if (!path.isAbsolute(asset)) {
|
|
110
|
+
sourceAssetPath = path.join(projectPath, asset);
|
|
111
|
+
}
|
|
112
|
+
logger.log('Handling alwaysCopyAssets entry:', asset, sourceAssetPath);
|
|
113
|
+
if (fs.statSync(sourceAssetPath).isDirectory()) {
|
|
114
|
+
// If it's a directory, add all files in it
|
|
115
|
+
const files = findFilesByPredicate(sourceAssetPath, () => true);
|
|
116
|
+
for (const file of files) {
|
|
117
|
+
const relativePath = path.relative(projectPath, file).replace(/\\/g, '/');
|
|
118
|
+
assets.add(`${ENGINE.PROJECT_PATH_PREFIX}/${relativePath}`);
|
|
119
|
+
}
|
|
120
|
+
logger.log(`Added ${files.length} files from alwaysCopyAssets directory ${sourceAssetPath}`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
if (!withProjectPrefix) {
|
|
124
|
+
assets.add(`${ENGINE.PROJECT_PATH_PREFIX}/${path.relative(projectPath, sourceAssetPath).replace(/\\/g, '/')}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
assets.add(asset);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Helper function to discover assets from a file
|
|
132
|
+
async function discoverAssetsFromFile(filePath) {
|
|
133
|
+
const discoveredAssets = [];
|
|
134
|
+
try {
|
|
135
|
+
if (filePath.endsWith('.json') || filePath.includes(ENGINE.SCENE_NAME_EXT)) {
|
|
136
|
+
const jsonData = JSON.parse(await fs.promises.readFile(filePath, 'utf8'));
|
|
137
|
+
discoveredAssets.push(...gatherAssets(jsonData));
|
|
138
|
+
}
|
|
139
|
+
else if (filePath.endsWith('.js')) {
|
|
140
|
+
const regex = new RegExp(String.raw `(["'])${ENGINE.PROJECT_PATH_PREFIX}\/.*?\1`, 'g');
|
|
141
|
+
const jsData = await fs.promises.readFile(filePath, 'utf8');
|
|
142
|
+
const matches = jsData.matchAll(regex);
|
|
143
|
+
for (const match of matches) {
|
|
144
|
+
discoveredAssets.push(match[0].replace(/["']/g, ''));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (filePath.endsWith('.html') || filePath.endsWith('.css')) {
|
|
148
|
+
// For HTML and CSS files, look for asset paths in various contexts
|
|
149
|
+
const regex = new RegExp(String.raw `${ENGINE.PROJECT_PATH_PREFIX}\/[^"'\s)]+`, 'g');
|
|
150
|
+
const fileData = await fs.promises.readFile(filePath, 'utf8');
|
|
151
|
+
const matches = fileData.matchAll(regex);
|
|
152
|
+
for (const match of matches) {
|
|
153
|
+
discoveredAssets.push(match[0]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
logger.error(`Failed to read file ${filePath}: ${error}`);
|
|
159
|
+
}
|
|
160
|
+
return discoveredAssets;
|
|
161
|
+
}
|
|
162
|
+
// Recursively discover all assets
|
|
163
|
+
async function discoverAllAssets(initialFiles) {
|
|
164
|
+
const filesToProcess = [...initialFiles];
|
|
165
|
+
while (filesToProcess.length > 0) {
|
|
166
|
+
const currentFile = filesToProcess.shift();
|
|
167
|
+
if (processedAssets.has(currentFile)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
processedAssets.add(currentFile);
|
|
171
|
+
const discoveredAssets = await discoverAssetsFromFile(currentFile);
|
|
172
|
+
for (const asset of discoveredAssets) {
|
|
173
|
+
if (!assets.has(asset)) {
|
|
174
|
+
assets.add(asset);
|
|
175
|
+
// Check if this asset is a file that might contain references to other assets
|
|
176
|
+
const sourceAssetPath = asset.replace(ENGINE.PROJECT_PATH_PREFIX, projectPath);
|
|
177
|
+
try {
|
|
178
|
+
await fs.promises.access(sourceAssetPath);
|
|
179
|
+
// Only process JSON, JS, HTML, CSS, and scene files that might contain asset references
|
|
180
|
+
if (sourceAssetPath.endsWith('.json') ||
|
|
181
|
+
sourceAssetPath.endsWith('.js') ||
|
|
182
|
+
sourceAssetPath.endsWith('.html') ||
|
|
183
|
+
sourceAssetPath.endsWith('.css') ||
|
|
184
|
+
sourceAssetPath.includes(ENGINE.SCENE_NAME_EXT)) {
|
|
185
|
+
filesToProcess.push(sourceAssetPath);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
logger.error(`copyAssets: source asset ${asset} does not exist, referenced from ${currentFile}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Start discovery from initial files
|
|
196
|
+
{
|
|
197
|
+
const beginTime = Date.now();
|
|
198
|
+
await discoverAllAssets([...jsonFiles, ...jsFiles, ...htmlFiles, ...cssFiles]);
|
|
199
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
200
|
+
logger.log(`Stats: Assets discovered in ${durationSec}s`);
|
|
201
|
+
}
|
|
202
|
+
/*
|
|
203
|
+
// Find and add lightmap files and navmesh files that may not be directly referenced
|
|
204
|
+
// Disabled for now, see https://directive.slack.com/archives/C08M839GVBP/p1755865316511099
|
|
205
|
+
try {
|
|
206
|
+
const beginTime = Date.now();
|
|
207
|
+
const engineFiles = [
|
|
208
|
+
...findFilesWithExtension(projectPath, ENGINE.LIGHTMAP_JSON_EXT),
|
|
209
|
+
...findFilesWithExtension(projectPath, ENGINE.LIGHTMAP_BIN_EXT),
|
|
210
|
+
...findFilesWithExtension(projectPath, ENGINE.NAVMESH_EXT),
|
|
211
|
+
];
|
|
212
|
+
for (const file of engineFiles) {
|
|
213
|
+
// Convert absolute path to @project relative path
|
|
214
|
+
const relativePath = path.relative(projectPath, file);
|
|
215
|
+
const assetPath = `${ENGINE.PROJECT_PATH_PREFIX}/${relativePath.replace(/\\/g, '/')}`;
|
|
216
|
+
assets.add(assetPath);
|
|
217
|
+
logger.log(`Found engine file: ${assetPath}`);
|
|
218
|
+
}
|
|
219
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
220
|
+
logger.log(`Stats: lightmap and navmesh files discovered in ${durationSec}s`);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
logger.error(`Failed to scan for engine files: ${error}`);
|
|
223
|
+
}
|
|
224
|
+
*/
|
|
225
|
+
// Copy all assets in parallel
|
|
226
|
+
const copyPromises = Array.from(assets).map(async (asset) => {
|
|
227
|
+
try {
|
|
228
|
+
const sourceAssetPath = asset.replace(ENGINE.PROJECT_PATH_PREFIX, projectPath);
|
|
229
|
+
// Check if source exists
|
|
230
|
+
try {
|
|
231
|
+
await fs.promises.access(sourceAssetPath);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// No need to log again here, it's already logged in the discoverAllAssets function
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (asset.includes(ENGINE.SCENE_NAME_EXT)) {
|
|
238
|
+
// Do not copy the scene files since they have been minified and copied in an early step (see the buildProject function)
|
|
239
|
+
// If we copy them again the minification will be undone
|
|
240
|
+
// Also even if the copy is skipped the path map still needs to be set in order to have the correct scene file path in their referencers (for instance game.js)
|
|
241
|
+
logger.verbose(`Skip copying ${sourceAssetPath} as it's been minified and copied in an early step`);
|
|
242
|
+
}
|
|
243
|
+
else if (fs.statSync(sourceAssetPath).isDirectory()) {
|
|
244
|
+
// directory, skip it
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const destinationAssetPath = asset.replace(ENGINE.PROJECT_PATH_PREFIX, distFolder);
|
|
248
|
+
logger.verbose(`Copying ${sourceAssetPath} to ${destinationAssetPath}`);
|
|
249
|
+
await fs.promises.mkdir(path.dirname(destinationAssetPath), { recursive: true });
|
|
250
|
+
await fs.promises.copyFile(sourceAssetPath, destinationAssetPath);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
logger.error(`Failed to copy asset ${asset}: ${error}`);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
{
|
|
258
|
+
const beginTime = Date.now();
|
|
259
|
+
await Promise.all(copyPromises);
|
|
260
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
261
|
+
logger.verbose(`Stats: Assets copied in ${durationSec}s`);
|
|
262
|
+
}
|
|
263
|
+
// Collect all files that need path updates (initial dist files + copied asset files that can contain references)
|
|
264
|
+
const filesToUpdate = [...jsonFiles, ...jsFiles, ...htmlFiles, ...cssFiles];
|
|
265
|
+
// Add copied asset files that might contain asset references
|
|
266
|
+
for (const asset of assets) {
|
|
267
|
+
const destinationAssetPath = asset.replace(ENGINE.PROJECT_PATH_PREFIX, distFolder);
|
|
268
|
+
if (destinationAssetPath.endsWith('.json') ||
|
|
269
|
+
destinationAssetPath.endsWith('.js') ||
|
|
270
|
+
destinationAssetPath.endsWith('.html') ||
|
|
271
|
+
destinationAssetPath.endsWith('.css') ||
|
|
272
|
+
destinationAssetPath.includes(ENGINE.SCENE_NAME_EXT)) {
|
|
273
|
+
filesToUpdate.push(destinationAssetPath);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
logger.error(`Failed to copy assets: ${error}`);
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function buildSceneData(sceneData) {
|
|
283
|
+
function removeEditorData(data) {
|
|
284
|
+
if (typeof data === 'object' && data !== null) {
|
|
285
|
+
delete data.editorData;
|
|
286
|
+
for (const v of Object.values(data)) {
|
|
287
|
+
removeEditorData(v);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
removeEditorData(sceneData);
|
|
292
|
+
return sceneData;
|
|
293
|
+
}
|
|
294
|
+
export async function buildProject(projectPath, runTsc, logger, handler) {
|
|
295
|
+
const { folder: projectFolder, file: projectFile } = getProjectFolderAndFile(projectPath, logger);
|
|
296
|
+
try {
|
|
297
|
+
logger.log(`Building project at ${projectPath}`);
|
|
298
|
+
if (!fs.existsSync(projectFolder)) {
|
|
299
|
+
return {
|
|
300
|
+
success: false,
|
|
301
|
+
message: `Project does not exist: ${projectPath}`,
|
|
302
|
+
path: projectPath
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const sourceFolder = path.join(projectFolder, 'src');
|
|
306
|
+
const excludedFolders = ['node_modules', '.git'];
|
|
307
|
+
const gameFiles = findFilesByPredicate(sourceFolder, (filePath) => path.basename(filePath) === ENGINE.DEFAULT_GAME_NAME && !filePath.includes(ENGINE.BUILT_PROJECT_FOLDER), excludedFolders);
|
|
308
|
+
const sceneFiles = findFilesByPredicate(projectFolder, (filePath) => path.basename(filePath).includes(ENGINE.SCENE_NAME_EXT) && !filePath.includes(ENGINE.BUILT_PROJECT_FOLDER), excludedFolders);
|
|
309
|
+
if (!gameFiles.length) {
|
|
310
|
+
return {
|
|
311
|
+
success: false,
|
|
312
|
+
message: `Game file ${ENGINE.DEFAULT_GAME_NAME} does not exist`,
|
|
313
|
+
path: projectPath
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (gameFiles.length > 1) {
|
|
317
|
+
return {
|
|
318
|
+
success: false,
|
|
319
|
+
message: `Multiple game files found: ${gameFiles.join(', ')}`,
|
|
320
|
+
path: projectPath
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (!projectFile) {
|
|
324
|
+
return {
|
|
325
|
+
success: false,
|
|
326
|
+
message: `Project file ${ENGINE.GAME_PROJECT_FILE_EXT} does not exist`,
|
|
327
|
+
path: projectPath
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const autoImportPath = path.join(sourceFolder, 'auto-imports.ts');
|
|
331
|
+
if (fs.existsSync(autoImportPath)) {
|
|
332
|
+
let importText = '';
|
|
333
|
+
const sourceFiles = findFilesWithExtension(sourceFolder, 'ts');
|
|
334
|
+
for (const sourceFile of sourceFiles) {
|
|
335
|
+
if (sourceFile.includes('auto-imports.ts')) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (sourceFile.includes(ENGINE.DEFAULT_GAME_NAME)) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const importPath = path.relative(sourceFolder, sourceFile).replaceAll('\\', '/');
|
|
342
|
+
importText += `import './${importPath}';\n`;
|
|
343
|
+
}
|
|
344
|
+
fs.writeFileSync(autoImportPath, importText);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
logger.warn(`Auto-imports file ${autoImportPath} does not exist`);
|
|
348
|
+
}
|
|
349
|
+
const distFolder = path.join(projectFolder, ENGINE.BUILT_PROJECT_FOLDER);
|
|
350
|
+
if (fs.existsSync(distFolder)) {
|
|
351
|
+
const beginTime = Date.now();
|
|
352
|
+
fs.rmSync(distFolder, { recursive: true });
|
|
353
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
354
|
+
logger.log(`Stats: Dist folder removed in ${durationSec}s`);
|
|
355
|
+
}
|
|
356
|
+
fs.mkdirSync(distFolder, { recursive: true });
|
|
357
|
+
if (runTsc) {
|
|
358
|
+
logger.log('Running TypeScript type checking');
|
|
359
|
+
const beginTime = Date.now();
|
|
360
|
+
// Get the direct path to tsc binary (avoids pnpm exec overhead)
|
|
361
|
+
const tscBinary = getTscBinaryPath(projectFolder, logger);
|
|
362
|
+
// Build the command - quote the binary path only if it's not pnpm exec
|
|
363
|
+
const tscParameters = '--noEmit --incremental';
|
|
364
|
+
const tscCommand = tscBinary.startsWith('pnpm')
|
|
365
|
+
? `${tscBinary} ${tscParameters}`
|
|
366
|
+
: `"${tscBinary}" ${tscParameters}`;
|
|
367
|
+
logger.log(`Using tsc: ${tscBinary}`);
|
|
368
|
+
// Only type check, don't emit files - esbuild will handle transpilation
|
|
369
|
+
// --incremental speeds up subsequent builds by caching results
|
|
370
|
+
await runCommandAsync(tscCommand, projectFolder, logger);
|
|
371
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
372
|
+
logger.log(`Stats: TypeScript type checking completed in ${durationSec}s`);
|
|
373
|
+
}
|
|
374
|
+
{
|
|
375
|
+
const gameBundleFilePath = path.join(distFolder, ENGINE.DEFAULT_GAME_BUNDLE_NAME);
|
|
376
|
+
logger.log(`Building game bundle from ${gameFiles[0]}`);
|
|
377
|
+
const beginTime = Date.now();
|
|
378
|
+
// Get the direct path to esbuild binary (avoids pnpm exec overhead)
|
|
379
|
+
const esbuildBinary = getEsbuildBinaryPath(projectFolder, logger, handler.app.isPackaged, handler.app.resourcesPath);
|
|
380
|
+
// Build the command - quote the binary path only if it's not pnpm exec
|
|
381
|
+
const buildParameters = `--bundle --platform=browser --minify --keep-names --outfile="${gameBundleFilePath}" --external:genesys.js --external:three --format=cjs`;
|
|
382
|
+
const buildCommand = esbuildBinary.startsWith('pnpm')
|
|
383
|
+
? `${esbuildBinary} "${gameFiles[0]}" ${buildParameters}`
|
|
384
|
+
: `"${esbuildBinary}" "${gameFiles[0]}" ${buildParameters}`;
|
|
385
|
+
logger.log(`Using esbuild: ${esbuildBinary}`);
|
|
386
|
+
const { error, stdout, stderr } = await runCommandAsync(buildCommand, projectFolder, logger);
|
|
387
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
388
|
+
logger.log(`Stats: Game bundle built in ${durationSec}s`);
|
|
389
|
+
if (error && error.code !== 0) {
|
|
390
|
+
await handler.ui.showErrorDialog('Failed to build project', stderr);
|
|
391
|
+
throw new Error(`Building game bundle failed: ${stderr || error.message}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// inject the prefabs into the scene
|
|
395
|
+
for (const sceneFile of sceneFiles) {
|
|
396
|
+
const sceneData = JSON.parse(fs.readFileSync(sceneFile, 'utf8'));
|
|
397
|
+
if (!sceneData.$version || sceneData.$version < ENGINE.SerializationVersion.V1) {
|
|
398
|
+
sceneData.prefabs = {};
|
|
399
|
+
for (const actor of sceneData.actors) {
|
|
400
|
+
const prefabName = actor.prefabName;
|
|
401
|
+
if (prefabName) {
|
|
402
|
+
const prefabPath = prefabName.replace(ENGINE.PROJECT_PATH_PREFIX, projectFolder);
|
|
403
|
+
if (!fs.existsSync(prefabPath)) {
|
|
404
|
+
throw new Error(`Prefab ${prefabPath} does not exist`);
|
|
405
|
+
}
|
|
406
|
+
const prefabData = JSON.parse(fs.readFileSync(prefabPath, 'utf8'));
|
|
407
|
+
sceneData.prefabs[prefabName] = prefabData;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const relativePath = path.relative(projectFolder, sceneFile);
|
|
412
|
+
const sceneDestinationPath = path.join(distFolder, relativePath);
|
|
413
|
+
fs.mkdirSync(path.dirname(sceneDestinationPath), { recursive: true });
|
|
414
|
+
fs.writeFileSync(sceneDestinationPath, JSON.stringify(buildSceneData(sceneData)));
|
|
415
|
+
}
|
|
416
|
+
// copy the project file to the dist folder
|
|
417
|
+
fs.copyFileSync(path.join(projectFolder, projectFile), path.join(distFolder, projectFile));
|
|
418
|
+
const projectConfig = JSON.parse(fs.readFileSync(path.join(projectFolder, projectFile), 'utf8'));
|
|
419
|
+
{
|
|
420
|
+
const beginTime = Date.now();
|
|
421
|
+
const alwaysCopyAssets = [];
|
|
422
|
+
if (Array.isArray(projectConfig.alwaysCopyAssets)) {
|
|
423
|
+
alwaysCopyAssets.push(...projectConfig.alwaysCopyAssets);
|
|
424
|
+
}
|
|
425
|
+
else if (typeof projectConfig.alwaysCopyAssets === 'string') {
|
|
426
|
+
alwaysCopyAssets.push(projectConfig.alwaysCopyAssets);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
logger.warn('project.json alwaysCopyAssets is not an array or string');
|
|
430
|
+
}
|
|
431
|
+
await copyAssets(projectFolder, distFolder, alwaysCopyAssets, logger);
|
|
432
|
+
const durationSec = (Date.now() - beginTime) / 1000;
|
|
433
|
+
logger.log(`Stats: copyAssets done in ${durationSec}s`);
|
|
434
|
+
}
|
|
435
|
+
logger.log(`Project built successfully at ${distFolder}`);
|
|
436
|
+
return {
|
|
437
|
+
success: true,
|
|
438
|
+
message: `Project built successfully at ${distFolder}`,
|
|
439
|
+
path: projectPath
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
return {
|
|
444
|
+
success: false,
|
|
445
|
+
message: `Failed to build project: ${error}`,
|
|
446
|
+
path: projectPath,
|
|
447
|
+
error: error,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as ENGINE from 'genesys.js';
|
|
4
|
+
import { ALL_DEPENDENCIES } from '../../dependencies.js';
|
|
5
|
+
import { copyAsync, existsAsync, getEngineVersion, getProjectRoot, mkdirAsync, readdirAsync, runCommandAsync, writeFileAsync } from '../common.js';
|
|
6
|
+
import { IgnoredFiles } from '../index.js';
|
|
7
|
+
export const packProjectFiles = {
|
|
8
|
+
packageJson: {
|
|
9
|
+
name: '',
|
|
10
|
+
version: '0.0.1',
|
|
11
|
+
scripts: {
|
|
12
|
+
'build': 'tsc',
|
|
13
|
+
'pack': 'pnpm build && pnpm pack',
|
|
14
|
+
'lint': 'eslint . --fix --ext .ts,.tsx',
|
|
15
|
+
'postinstall': 'tsx scripts/postinstall.ts'
|
|
16
|
+
},
|
|
17
|
+
keywords: [],
|
|
18
|
+
type: 'module',
|
|
19
|
+
files: [
|
|
20
|
+
'dist',
|
|
21
|
+
'src',
|
|
22
|
+
'assets',
|
|
23
|
+
'scripts'
|
|
24
|
+
],
|
|
25
|
+
main: 'dist/src/index.js',
|
|
26
|
+
peerDependencies: {
|
|
27
|
+
'genesys.js': `${getEngineVersion()}`,
|
|
28
|
+
'three': ALL_DEPENDENCIES['three'],
|
|
29
|
+
},
|
|
30
|
+
devDependencies: {
|
|
31
|
+
'@types/three': ALL_DEPENDENCIES['@types/three'],
|
|
32
|
+
'nanoid': ALL_DEPENDENCIES['nanoid'],
|
|
33
|
+
'ajv': ALL_DEPENDENCIES['ajv'],
|
|
34
|
+
'esbuild': ALL_DEPENDENCIES['esbuild'],
|
|
35
|
+
'canvas': '3.1.0',
|
|
36
|
+
'get-port': '7.1.0',
|
|
37
|
+
'zod-to-json-schema': '3.24.6',
|
|
38
|
+
'ws': '8.18.2',
|
|
39
|
+
'@types/node': '22.15.21',
|
|
40
|
+
'@types/jsdom': '21.1.7',
|
|
41
|
+
'@modelcontextprotocol/sdk': '1.22.0',
|
|
42
|
+
'tsx': '4.20.3', // pin the tsx version to avoid import issues of the @webxr-input-profiles/motion-controllers module in game projects
|
|
43
|
+
'zod': '3.24.4',
|
|
44
|
+
'jsdom': '26.1.0',
|
|
45
|
+
'typescript': '5.8.3',
|
|
46
|
+
'@typescript-eslint/parser': '8.29.1',
|
|
47
|
+
'@typescript-eslint/utils': '8.38.0',
|
|
48
|
+
'eslint': '9.24.0',
|
|
49
|
+
'@types/ws': '8.18.1',
|
|
50
|
+
'ts-morph': '26.0.0',
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
codeWorkspace: {
|
|
54
|
+
folders: [
|
|
55
|
+
{
|
|
56
|
+
path: '.'
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
settings: {
|
|
60
|
+
['editor.tabSize']: 2
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
export function getAssetPackTemplatePath(isPackaged, resourcesPath, projectRoot) {
|
|
65
|
+
const packageName = 'src/asset-pack';
|
|
66
|
+
if (isPackaged) {
|
|
67
|
+
return path.join(resourcesPath, 'vendor', packageName);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return path.join(projectRoot, packageName);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export async function newAssetPack(parentPath, packName, logger, handler) {
|
|
74
|
+
const packPath = path.join(parentPath, packName);
|
|
75
|
+
// Validate pack name - only allow English characters, numbers, hyphens, underscores, and dots
|
|
76
|
+
const validNameRegex = /^[a-zA-Z0-9_.-]+$/;
|
|
77
|
+
if (!validNameRegex.test(packName)) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
message: `Invalid pack name "${packName}". Pack name can only contain English characters (a-z, A-Z), numbers (0-9), hyphens (-), underscores (_), and dots (.).`,
|
|
81
|
+
path: packPath
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const createAssetPackMessage = `Creating asset pack at ${packPath}`;
|
|
85
|
+
logger.log(createAssetPackMessage);
|
|
86
|
+
handler?.ui.showLoadingOverlay(`Creating asset pack...\n${createAssetPackMessage}`);
|
|
87
|
+
try {
|
|
88
|
+
if (await existsAsync(packPath)) {
|
|
89
|
+
// Check if directory is empty
|
|
90
|
+
const files = (await readdirAsync(packPath)).filter(file => !IgnoredFiles.includes(file));
|
|
91
|
+
if (files.length > 0) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
message: `The directory ${packPath} already contains files. Please choose an empty directory.`,
|
|
95
|
+
path: packPath
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
await mkdirAsync(packPath, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
// create asset pack directories
|
|
103
|
+
logger.log('Creating asset pack directories...');
|
|
104
|
+
await Promise.all([
|
|
105
|
+
mkdirAsync(path.join(packPath, `${ENGINE.ASSETS_FOLDER}/models`), { recursive: true }),
|
|
106
|
+
mkdirAsync(path.join(packPath, `${ENGINE.ASSETS_FOLDER}/textures`), { recursive: true }),
|
|
107
|
+
mkdirAsync(path.join(packPath, `${ENGINE.ASSETS_FOLDER}/sounds`), { recursive: true })
|
|
108
|
+
]);
|
|
109
|
+
const packageJson = { ...packProjectFiles.packageJson }; // Create a copy to avoid mutation
|
|
110
|
+
packageJson.name = packName;
|
|
111
|
+
// generate package.json and code-workspace file
|
|
112
|
+
logger.log('Generating package.json and code-workspace file...');
|
|
113
|
+
await Promise.all([
|
|
114
|
+
writeFileAsync(path.join(packPath, 'package.json'), JSON.stringify(packageJson, null, 2)),
|
|
115
|
+
writeFileAsync(path.join(packPath, `${packName}.code-workspace`), JSON.stringify(packProjectFiles.codeWorkspace, null, 2)),
|
|
116
|
+
]);
|
|
117
|
+
// Copy template files
|
|
118
|
+
logger.log('Copying template files...');
|
|
119
|
+
const templatePath = getAssetPackTemplatePath(handler.app.isPackaged, handler.app.resourcesPath, getProjectRoot());
|
|
120
|
+
await copyAsync(templatePath, packPath, { recursive: true });
|
|
121
|
+
// Rename gitignore to .gitignore (npm excludes .gitignore files from packages)
|
|
122
|
+
const gitignoreSrc = path.join(packPath, 'gitignore');
|
|
123
|
+
const gitignoreDst = path.join(packPath, '.gitignore');
|
|
124
|
+
if (await existsAsync(gitignoreSrc)) {
|
|
125
|
+
await fs.promises.rename(gitignoreSrc, gitignoreDst);
|
|
126
|
+
}
|
|
127
|
+
// install dependencies
|
|
128
|
+
logger.log('Running pnpm install...');
|
|
129
|
+
await runCommandAsync('pnpm install', packPath, logger);
|
|
130
|
+
// build asset pack
|
|
131
|
+
logger.log('Running pnpm build...');
|
|
132
|
+
await runCommandAsync('pnpm build', packPath, logger);
|
|
133
|
+
// open the asset pack in the file explorer
|
|
134
|
+
handler?.os.openPath(packPath);
|
|
135
|
+
logger.log('Asset pack created successfully');
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
message: `Asset pack created successfully at ${packPath}`,
|
|
139
|
+
path: packPath
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
message: `Failed to create asset pack: ${error}`,
|
|
146
|
+
path: packPath,
|
|
147
|
+
error: error,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|