unreal-engine-mcp-server 0.5.0 → 0.5.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/.env.example +1 -1
- package/.github/release-drafter-config.yml +51 -0
- package/.github/workflows/greetings.yml +5 -1
- package/.github/workflows/labeler.yml +2 -1
- package/.github/workflows/publish-mcp.yml +2 -4
- package/.github/workflows/release-drafter.yml +3 -2
- package/.github/workflows/release.yml +3 -3
- package/CHANGELOG.md +109 -0
- package/CONTRIBUTING.md +1 -1
- package/GEMINI.md +115 -0
- package/Public/Plugin_setup_guide.mp4 +0 -0
- package/README.md +166 -200
- package/dist/automation/bridge.d.ts +1 -2
- package/dist/automation/bridge.js +24 -23
- package/dist/automation/connection-manager.d.ts +1 -0
- package/dist/automation/connection-manager.js +10 -0
- package/dist/automation/message-handler.js +5 -4
- package/dist/automation/request-tracker.d.ts +4 -0
- package/dist/automation/request-tracker.js +11 -3
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/graphql/loaders.d.ts +64 -0
- package/dist/graphql/loaders.js +117 -0
- package/dist/graphql/resolvers.d.ts +3 -3
- package/dist/graphql/resolvers.js +33 -30
- package/dist/graphql/server.js +3 -1
- package/dist/graphql/types.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -2
- package/dist/server-setup.d.ts +0 -1
- package/dist/server-setup.js +0 -40
- package/dist/tools/actors.d.ts +58 -24
- package/dist/tools/actors.js +22 -6
- package/dist/tools/assets.d.ts +19 -71
- package/dist/tools/assets.js +28 -22
- package/dist/tools/base-tool.d.ts +4 -4
- package/dist/tools/base-tool.js +1 -1
- package/dist/tools/blueprint.d.ts +45 -61
- package/dist/tools/blueprint.js +43 -14
- package/dist/tools/consolidated-tool-definitions.js +2 -1
- package/dist/tools/consolidated-tool-handlers.js +96 -110
- package/dist/tools/dynamic-handler-registry.d.ts +11 -9
- package/dist/tools/dynamic-handler-registry.js +17 -95
- package/dist/tools/editor.d.ts +19 -193
- package/dist/tools/editor.js +11 -2
- package/dist/tools/environment.d.ts +8 -14
- package/dist/tools/foliage.d.ts +18 -143
- package/dist/tools/foliage.js +4 -2
- package/dist/tools/handlers/actor-handlers.d.ts +1 -1
- package/dist/tools/handlers/actor-handlers.js +14 -13
- package/dist/tools/handlers/asset-handlers.js +454 -454
- package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
- package/dist/tools/handlers/sequence-handlers.js +24 -13
- package/dist/tools/introspection.d.ts +1 -1
- package/dist/tools/introspection.js +1 -1
- package/dist/tools/landscape.d.ts +16 -116
- package/dist/tools/landscape.js +7 -3
- package/dist/tools/level.d.ts +22 -103
- package/dist/tools/level.js +26 -18
- package/dist/tools/lighting.d.ts +54 -7
- package/dist/tools/lighting.js +9 -5
- package/dist/tools/materials.d.ts +1 -1
- package/dist/tools/materials.js +5 -1
- package/dist/tools/niagara.js +37 -2
- package/dist/tools/performance.d.ts +0 -1
- package/dist/tools/performance.js +0 -1
- package/dist/tools/physics.js +5 -1
- package/dist/tools/sequence.d.ts +24 -24
- package/dist/tools/sequence.js +13 -0
- package/dist/tools/ui.d.ts +0 -2
- package/dist/types/automation-responses.d.ts +115 -0
- package/dist/types/automation-responses.js +2 -0
- package/dist/types/responses.d.ts +249 -0
- package/dist/types/responses.js +2 -0
- package/dist/types/tool-interfaces.d.ts +135 -135
- package/dist/types/tool-types.d.ts +2 -0
- package/dist/unreal-bridge.js +4 -4
- package/dist/utils/command-validator.js +7 -5
- package/dist/utils/error-handler.d.ts +24 -2
- package/dist/utils/error-handler.js +58 -23
- package/dist/utils/normalize.d.ts +7 -4
- package/dist/utils/normalize.js +12 -10
- package/dist/utils/path-security.d.ts +2 -0
- package/dist/utils/path-security.js +24 -0
- package/dist/utils/response-factory.d.ts +4 -4
- package/dist/utils/response-factory.js +15 -21
- package/dist/utils/response-validator.js +88 -73
- package/dist/utils/unreal-command-queue.d.ts +2 -0
- package/dist/utils/unreal-command-queue.js +8 -1
- package/docs/Migration-Guide-v0.5.0.md +1 -9
- package/docs/handler-mapping.md +4 -2
- package/docs/testing-guide.md +2 -2
- package/package.json +12 -6
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
- package/scripts/run-all-tests.mjs +25 -20
- package/server.json +3 -2
- package/src/automation/bridge.ts +27 -25
- package/src/automation/connection-manager.ts +18 -0
- package/src/automation/message-handler.ts +33 -8
- package/src/automation/request-tracker.ts +39 -7
- package/src/config.ts +1 -1
- package/src/constants.ts +7 -0
- package/src/graphql/loaders.ts +244 -0
- package/src/graphql/resolvers.ts +47 -49
- package/src/graphql/server.ts +3 -1
- package/src/graphql/types.ts +3 -0
- package/src/index.ts +15 -2
- package/src/resources/assets.ts +5 -4
- package/src/server/tool-registry.ts +3 -3
- package/src/server-setup.ts +3 -37
- package/src/tools/actors.ts +77 -44
- package/src/tools/animation.ts +1 -0
- package/src/tools/assets.ts +76 -65
- package/src/tools/base-tool.ts +3 -3
- package/src/tools/blueprint.ts +170 -104
- package/src/tools/consolidated-tool-definitions.ts +2 -1
- package/src/tools/consolidated-tool-handlers.ts +129 -150
- package/src/tools/dynamic-handler-registry.ts +22 -140
- package/src/tools/editor.ts +43 -29
- package/src/tools/environment.ts +21 -27
- package/src/tools/foliage.ts +28 -25
- package/src/tools/handlers/actor-handlers.ts +16 -17
- package/src/tools/handlers/asset-handlers.ts +484 -484
- package/src/tools/handlers/sequence-handlers.ts +85 -62
- package/src/tools/introspection.ts +7 -7
- package/src/tools/landscape.ts +34 -28
- package/src/tools/level.ts +100 -80
- package/src/tools/lighting.ts +25 -20
- package/src/tools/materials.ts +9 -3
- package/src/tools/niagara.ts +44 -2
- package/src/tools/performance.ts +1 -2
- package/src/tools/physics.ts +7 -1
- package/src/tools/sequence.ts +42 -26
- package/src/tools/ui.ts +1 -3
- package/src/types/automation-responses.ts +119 -0
- package/src/types/responses.ts +355 -0
- package/src/types/tool-interfaces.ts +135 -135
- package/src/types/tool-types.ts +4 -0
- package/src/unreal-bridge.ts +71 -26
- package/src/utils/command-validator.ts +47 -5
- package/src/utils/error-handler.ts +128 -45
- package/src/utils/normalize.test.ts +162 -0
- package/src/utils/normalize.ts +38 -16
- package/src/utils/path-security.ts +43 -0
- package/src/utils/response-factory.ts +29 -24
- package/src/utils/response-validator.ts +103 -87
- package/src/utils/safe-json.test.ts +90 -0
- package/src/utils/unreal-command-queue.ts +13 -1
- package/src/utils/validation.test.ts +184 -0
- package/tests/test-animation.mjs +358 -33
- package/tests/test-asset-graph.mjs +311 -0
- package/tests/test-audio.mjs +314 -116
- package/tests/test-behavior-tree.mjs +327 -144
- package/tests/test-blueprint-graph.mjs +343 -12
- package/tests/test-control-editor.mjs +85 -53
- package/tests/test-graphql.mjs +58 -8
- package/tests/test-input.mjs +349 -0
- package/tests/test-inspect.mjs +291 -61
- package/tests/test-landscape.mjs +304 -48
- package/tests/test-lighting.mjs +428 -0
- package/tests/test-manage-level.mjs +70 -51
- package/tests/test-performance.mjs +539 -0
- package/tests/test-sequence.mjs +82 -46
- package/tests/test-system.mjs +72 -33
- package/tests/test-wasm.mjs +98 -8
- package/vitest.config.ts +35 -0
- package/.github/release-drafter.yml +0 -148
- package/dist/prompts/index.d.ts +0 -21
- package/dist/prompts/index.js +0 -217
- package/dist/tools/blueprint/helpers.d.ts +0 -29
- package/dist/tools/blueprint/helpers.js +0 -182
- package/src/prompts/index.ts +0 -249
- package/src/tools/blueprint/helpers.ts +0 -189
- package/tests/test-blueprint-events.mjs +0 -35
- package/tests/test-extra-tools.mjs +0 -38
- package/tests/test-render.mjs +0 -33
- package/tests/test-search-assets.mjs +0 -66
package/src/tools/level.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { BaseTool } from './base-tool.js';
|
|
2
|
-
import { ILevelTools } from '../types/tool-interfaces.js';
|
|
2
|
+
import { ILevelTools, StandardActionResponse } from '../types/tool-interfaces.js';
|
|
3
|
+
import { LevelResponse } from '../types/automation-responses.js';
|
|
4
|
+
import { wasmIntegration as _wasmIntegration } from '../wasm/index.js';
|
|
5
|
+
import { sanitizePath } from '../utils/path-security.js';
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_OPERATION_TIMEOUT_MS,
|
|
8
|
+
DEFAULT_ASSET_OP_TIMEOUT_MS,
|
|
9
|
+
LONG_RUNNING_OP_TIMEOUT_MS
|
|
10
|
+
} from '../constants.js';
|
|
3
11
|
|
|
4
12
|
type LevelExportRecord = { target: string; timestamp: number; note?: string };
|
|
5
13
|
type ManagedLevelRecord = {
|
|
@@ -38,6 +46,18 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
38
46
|
if (!formatted.startsWith('/Game/')) {
|
|
39
47
|
formatted = `/Game/${formatted.replace(/^\/+/, '')}`;
|
|
40
48
|
}
|
|
49
|
+
|
|
50
|
+
// Security validation
|
|
51
|
+
try {
|
|
52
|
+
formatted = sanitizePath(formatted);
|
|
53
|
+
} catch (e: unknown) {
|
|
54
|
+
// If sanitizePath fails, we should probably propagate that error,
|
|
55
|
+
// but normalizeLevelPath signature expects to return an object.
|
|
56
|
+
// For now, let's log and rethrow or fallback?
|
|
57
|
+
// Throwing is safer as it prevents operation on invalid path.
|
|
58
|
+
throw new Error(`Security validation failed for level path: ${e instanceof Error ? e.message : String(e)}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
formatted = formatted.replace(/\.umap$/i, '');
|
|
42
62
|
if (formatted.endsWith('/')) {
|
|
43
63
|
formatted = formatted.slice(0, -1);
|
|
@@ -205,10 +225,10 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
205
225
|
this.ensureRecord(normalized.path, { loaded: true, visible: true });
|
|
206
226
|
}
|
|
207
227
|
|
|
208
|
-
async listLevels() {
|
|
228
|
+
async listLevels(): Promise<StandardActionResponse> {
|
|
209
229
|
// Try to get actual levels from UE via automation bridge
|
|
210
230
|
try {
|
|
211
|
-
const response = await this.sendAutomationRequest('list_levels', {}, {
|
|
231
|
+
const response = await this.sendAutomationRequest<LevelResponse>('list_levels', {}, {
|
|
212
232
|
timeoutMs: 10000
|
|
213
233
|
});
|
|
214
234
|
|
|
@@ -222,6 +242,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
222
242
|
const finalLevels = [...ueLevels, ...managedOnly];
|
|
223
243
|
|
|
224
244
|
const result: Record<string, unknown> = {
|
|
245
|
+
...response,
|
|
225
246
|
success: true,
|
|
226
247
|
message: 'Levels listed from Unreal Engine',
|
|
227
248
|
levels: finalLevels,
|
|
@@ -232,12 +253,11 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
232
253
|
levels: finalLevels,
|
|
233
254
|
count: finalLevels.length
|
|
234
255
|
},
|
|
235
|
-
...response,
|
|
236
256
|
managedLevels: managed.levels,
|
|
237
257
|
managedLevelCount: managed.count
|
|
238
258
|
};
|
|
239
259
|
|
|
240
|
-
return result;
|
|
260
|
+
return result as StandardActionResponse;
|
|
241
261
|
}
|
|
242
262
|
} catch {
|
|
243
263
|
// Fall back to managed levels if automation bridge fails
|
|
@@ -247,12 +267,12 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
247
267
|
return this.listManagedLevels();
|
|
248
268
|
}
|
|
249
269
|
|
|
250
|
-
async getLevelSummary(levelPath?: string) {
|
|
270
|
+
async getLevelSummary(levelPath?: string): Promise<StandardActionResponse> {
|
|
251
271
|
const resolved = this.resolveLevelPath(levelPath);
|
|
252
272
|
if (!resolved) {
|
|
253
273
|
return { success: false, error: 'No level specified' };
|
|
254
274
|
}
|
|
255
|
-
return this.summarizeLevel(resolved);
|
|
275
|
+
return this.summarizeLevel(resolved) as StandardActionResponse;
|
|
256
276
|
}
|
|
257
277
|
|
|
258
278
|
registerLight(levelPath: string | undefined, info: { name: string; type: string; details?: Record<string, unknown> }) {
|
|
@@ -272,27 +292,27 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
272
292
|
});
|
|
273
293
|
}
|
|
274
294
|
|
|
275
|
-
async exportLevel(params: { levelPath?: string; exportPath: string; note?: string; timeoutMs?: number }) {
|
|
295
|
+
async exportLevel(params: { levelPath?: string; exportPath: string; note?: string; timeoutMs?: number }): Promise<StandardActionResponse> {
|
|
276
296
|
const resolved = this.resolveLevelPath(params.levelPath);
|
|
277
297
|
if (!resolved) {
|
|
278
298
|
return { success: false, error: 'No level specified for export' };
|
|
279
299
|
}
|
|
280
300
|
|
|
281
301
|
try {
|
|
282
|
-
const res = await this.sendAutomationRequest('manage_level', {
|
|
302
|
+
const res = await this.sendAutomationRequest<LevelResponse>('manage_level', {
|
|
283
303
|
action: 'export_level',
|
|
284
304
|
levelPath: resolved,
|
|
285
305
|
exportPath: params.exportPath
|
|
286
|
-
}, { timeoutMs: params.timeoutMs ??
|
|
306
|
+
}, { timeoutMs: params.timeoutMs ?? LONG_RUNNING_OP_TIMEOUT_MS });
|
|
287
307
|
|
|
288
|
-
if (
|
|
308
|
+
if (res?.success === false) {
|
|
289
309
|
return {
|
|
290
310
|
success: false,
|
|
291
|
-
error:
|
|
311
|
+
error: res.error || res.message || 'Export failed',
|
|
292
312
|
levelPath: resolved,
|
|
293
313
|
exportPath: params.exportPath,
|
|
294
314
|
details: res
|
|
295
|
-
};
|
|
315
|
+
} as StandardActionResponse;
|
|
296
316
|
}
|
|
297
317
|
|
|
298
318
|
return {
|
|
@@ -301,23 +321,23 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
301
321
|
levelPath: resolved,
|
|
302
322
|
exportPath: params.exportPath,
|
|
303
323
|
details: res
|
|
304
|
-
};
|
|
305
|
-
} catch (e:
|
|
306
|
-
return { success: false, error: `Export failed: ${e.message}` };
|
|
324
|
+
} as StandardActionResponse;
|
|
325
|
+
} catch (e: unknown) {
|
|
326
|
+
return { success: false, error: `Export failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
307
327
|
}
|
|
308
328
|
}
|
|
309
329
|
|
|
310
|
-
async importLevel(params: { packagePath: string; destinationPath?: string; streaming?: boolean; timeoutMs?: number }) {
|
|
330
|
+
async importLevel(params: { packagePath: string; destinationPath?: string; streaming?: boolean; timeoutMs?: number }): Promise<StandardActionResponse> {
|
|
311
331
|
const destination = params.destinationPath
|
|
312
332
|
? this.normalizeLevelPath(params.destinationPath)
|
|
313
333
|
: this.normalizeLevelPath(`/Game/Maps/Imported_${Math.floor(Date.now() / 1000)}`);
|
|
314
334
|
|
|
315
335
|
try {
|
|
316
|
-
const res = await this.sendAutomationRequest('manage_level', {
|
|
336
|
+
const res = await this.sendAutomationRequest<LevelResponse>('manage_level', {
|
|
317
337
|
action: 'import_level',
|
|
318
338
|
packagePath: params.packagePath,
|
|
319
339
|
destinationPath: destination.path
|
|
320
|
-
}, { timeoutMs: params.timeoutMs ??
|
|
340
|
+
}, { timeoutMs: params.timeoutMs ?? LONG_RUNNING_OP_TIMEOUT_MS });
|
|
321
341
|
|
|
322
342
|
if ((res as any)?.success === false) {
|
|
323
343
|
return {
|
|
@@ -325,7 +345,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
325
345
|
error: (res as any).error || (res as any).message || 'Import failed',
|
|
326
346
|
levelPath: destination.path,
|
|
327
347
|
details: res
|
|
328
|
-
};
|
|
348
|
+
} as StandardActionResponse;
|
|
329
349
|
}
|
|
330
350
|
|
|
331
351
|
return {
|
|
@@ -335,23 +355,23 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
335
355
|
partitioned: true,
|
|
336
356
|
streaming: Boolean(params.streaming),
|
|
337
357
|
details: res
|
|
338
|
-
};
|
|
339
|
-
} catch (e:
|
|
340
|
-
return { success: false, error: `Import failed: ${e.message}` };
|
|
358
|
+
} as StandardActionResponse;
|
|
359
|
+
} catch (e: unknown) {
|
|
360
|
+
return { success: false, error: `Import failed: ${e instanceof Error ? e.message : String(e)}` };
|
|
341
361
|
}
|
|
342
362
|
}
|
|
343
363
|
|
|
344
|
-
async saveLevelAs(params: { sourcePath?: string; targetPath: string }) {
|
|
364
|
+
async saveLevelAs(params: { sourcePath?: string; targetPath: string }): Promise<StandardActionResponse> {
|
|
345
365
|
const source = this.resolveLevelPath(params.sourcePath);
|
|
346
366
|
const target = this.normalizeLevelPath(params.targetPath);
|
|
347
367
|
|
|
348
368
|
// Delegate to automation bridge
|
|
349
369
|
try {
|
|
350
|
-
const response = await this.sendAutomationRequest('manage_level', {
|
|
370
|
+
const response = await this.sendAutomationRequest<LevelResponse>('manage_level', {
|
|
351
371
|
action: 'save_level_as',
|
|
352
372
|
savePath: target.path
|
|
353
373
|
}, {
|
|
354
|
-
timeoutMs:
|
|
374
|
+
timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
|
|
355
375
|
});
|
|
356
376
|
|
|
357
377
|
if (response.success === false) {
|
|
@@ -391,13 +411,13 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
391
411
|
success: true,
|
|
392
412
|
message: response.message || `Level saved as ${target.path}`,
|
|
393
413
|
levelPath: target.path
|
|
394
|
-
};
|
|
414
|
+
} as StandardActionResponse;
|
|
395
415
|
} catch (error) {
|
|
396
416
|
return { success: false, error: `Failed to save level as: ${error instanceof Error ? error.message : String(error)}` };
|
|
397
417
|
}
|
|
398
418
|
}
|
|
399
419
|
|
|
400
|
-
async deleteLevels(params: { levelPaths: string[] }) {
|
|
420
|
+
async deleteLevels(params: { levelPaths: string[] }): Promise<StandardActionResponse> {
|
|
401
421
|
const removed: string[] = [];
|
|
402
422
|
for (const path of params.levelPaths) {
|
|
403
423
|
const normalized = this.normalizeLevelPath(path).path;
|
|
@@ -411,14 +431,14 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
411
431
|
success: true,
|
|
412
432
|
message: removed.length ? `Deleted ${removed.length} managed level(s)` : 'No managed levels removed',
|
|
413
433
|
removed
|
|
414
|
-
};
|
|
434
|
+
} as StandardActionResponse;
|
|
415
435
|
}
|
|
416
436
|
|
|
417
437
|
async loadLevel(params: {
|
|
418
438
|
levelPath: string;
|
|
419
439
|
streaming?: boolean;
|
|
420
440
|
position?: [number, number, number];
|
|
421
|
-
}) {
|
|
441
|
+
}): Promise<StandardActionResponse> {
|
|
422
442
|
const normalizedPath = this.normalizeLevelPath(params.levelPath).path;
|
|
423
443
|
|
|
424
444
|
if (params.streaming) {
|
|
@@ -435,7 +455,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
435
455
|
message: `Streaming level loaded: ${params.levelPath}`,
|
|
436
456
|
levelPath: normalizedPath,
|
|
437
457
|
streaming: true
|
|
438
|
-
};
|
|
458
|
+
} as StandardActionResponse;
|
|
439
459
|
} catch (err) {
|
|
440
460
|
return {
|
|
441
461
|
success: false,
|
|
@@ -446,10 +466,10 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
446
466
|
} else {
|
|
447
467
|
// Try loading via automation bridge first (more robust)
|
|
448
468
|
try {
|
|
449
|
-
const response = await this.sendAutomationRequest('manage_level', {
|
|
469
|
+
const response = await this.sendAutomationRequest<LevelResponse>('manage_level', {
|
|
450
470
|
action: 'load',
|
|
451
471
|
levelPath: params.levelPath
|
|
452
|
-
}, { timeoutMs:
|
|
472
|
+
}, { timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS });
|
|
453
473
|
|
|
454
474
|
if (response.success) {
|
|
455
475
|
this.setCurrentLevel(normalizedPath);
|
|
@@ -459,12 +479,12 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
459
479
|
visible: true
|
|
460
480
|
});
|
|
461
481
|
return {
|
|
482
|
+
...response,
|
|
462
483
|
success: true,
|
|
463
484
|
message: `Level loaded: ${params.levelPath}`,
|
|
464
485
|
level: normalizedPath,
|
|
465
|
-
streaming: false
|
|
466
|
-
|
|
467
|
-
};
|
|
486
|
+
streaming: false
|
|
487
|
+
} as StandardActionResponse;
|
|
468
488
|
}
|
|
469
489
|
} catch (_e) {
|
|
470
490
|
// Fallback to console logic
|
|
@@ -492,7 +512,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
492
512
|
error: 'not_found',
|
|
493
513
|
message,
|
|
494
514
|
level: normalizedPath
|
|
495
|
-
};
|
|
515
|
+
} as StandardActionResponse;
|
|
496
516
|
}
|
|
497
517
|
}
|
|
498
518
|
} catch {
|
|
@@ -511,7 +531,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
511
531
|
message: `Level loaded: ${params.levelPath}`,
|
|
512
532
|
level: normalizedPath,
|
|
513
533
|
streaming: false
|
|
514
|
-
};
|
|
534
|
+
} as StandardActionResponse;
|
|
515
535
|
} catch (err) {
|
|
516
536
|
return {
|
|
517
537
|
success: false,
|
|
@@ -525,7 +545,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
525
545
|
async saveLevel(params: {
|
|
526
546
|
levelName?: string;
|
|
527
547
|
savePath?: string;
|
|
528
|
-
}) {
|
|
548
|
+
}): Promise<StandardActionResponse> {
|
|
529
549
|
try {
|
|
530
550
|
if (params.savePath && !params.savePath.startsWith('/Game/')) {
|
|
531
551
|
throw new Error(`Invalid save path: ${params.savePath}`);
|
|
@@ -537,8 +557,8 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
537
557
|
payload.savePath = params.savePath;
|
|
538
558
|
}
|
|
539
559
|
|
|
540
|
-
const response = await this.sendAutomationRequest('manage_level', payload, {
|
|
541
|
-
timeoutMs:
|
|
560
|
+
const response = await this.sendAutomationRequest<LevelResponse>('manage_level', payload, {
|
|
561
|
+
timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
|
|
542
562
|
});
|
|
543
563
|
|
|
544
564
|
if (response.success === false) {
|
|
@@ -546,9 +566,9 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
546
566
|
}
|
|
547
567
|
|
|
548
568
|
const result: Record<string, unknown> = {
|
|
569
|
+
...response,
|
|
549
570
|
success: true,
|
|
550
|
-
message: response.message || 'Level saved'
|
|
551
|
-
...response
|
|
571
|
+
message: response.message || 'Level saved'
|
|
552
572
|
};
|
|
553
573
|
|
|
554
574
|
if (response.skipped) {
|
|
@@ -564,7 +584,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
564
584
|
result.details = response.details;
|
|
565
585
|
}
|
|
566
586
|
|
|
567
|
-
return result;
|
|
587
|
+
return result as StandardActionResponse;
|
|
568
588
|
} catch (error) {
|
|
569
589
|
return { success: false, error: `Failed to save level: ${error instanceof Error ? error.message : String(error)}` };
|
|
570
590
|
}
|
|
@@ -574,17 +594,17 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
574
594
|
levelName: string;
|
|
575
595
|
template?: 'Empty' | 'Default' | 'VR' | 'TimeOfDay';
|
|
576
596
|
savePath?: string;
|
|
577
|
-
}) {
|
|
597
|
+
}): Promise<StandardActionResponse> {
|
|
578
598
|
const basePath = params.savePath || '/Game/Maps';
|
|
579
599
|
const isPartitioned = true; // default to World Partition for UE5
|
|
580
600
|
const fullPath = `${basePath}/${params.levelName}`;
|
|
581
601
|
|
|
582
602
|
try {
|
|
583
|
-
const response = await this.sendAutomationRequest('create_new_level', {
|
|
603
|
+
const response = await this.sendAutomationRequest<LevelResponse>('create_new_level', {
|
|
584
604
|
levelPath: fullPath,
|
|
585
605
|
useWorldPartition: isPartitioned
|
|
586
606
|
}, {
|
|
587
|
-
timeoutMs:
|
|
607
|
+
timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
|
|
588
608
|
});
|
|
589
609
|
|
|
590
610
|
if (response.success === false) {
|
|
@@ -593,17 +613,17 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
593
613
|
error: response.error || response.message || 'Failed to create level',
|
|
594
614
|
path: fullPath,
|
|
595
615
|
partitioned: isPartitioned
|
|
596
|
-
};
|
|
616
|
+
} as StandardActionResponse;
|
|
597
617
|
}
|
|
598
618
|
|
|
599
619
|
const result: Record<string, unknown> = {
|
|
620
|
+
...response,
|
|
600
621
|
success: true,
|
|
601
622
|
message: response.message || 'Level created',
|
|
602
623
|
path: response.levelPath || fullPath,
|
|
603
624
|
packagePath: response.packagePath ?? fullPath,
|
|
604
625
|
objectPath: response.objectPath,
|
|
605
|
-
partitioned: isPartitioned
|
|
606
|
-
...response
|
|
626
|
+
partitioned: isPartitioned
|
|
607
627
|
};
|
|
608
628
|
|
|
609
629
|
if (response.warnings) {
|
|
@@ -621,14 +641,14 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
621
641
|
createdAt: Date.now()
|
|
622
642
|
});
|
|
623
643
|
|
|
624
|
-
return result;
|
|
644
|
+
return result as StandardActionResponse;
|
|
625
645
|
} catch (error) {
|
|
626
646
|
return {
|
|
627
647
|
success: false,
|
|
628
648
|
error: `Failed to create level: ${error instanceof Error ? error.message : String(error)}`,
|
|
629
649
|
path: fullPath,
|
|
630
650
|
partitioned: isPartitioned
|
|
631
|
-
};
|
|
651
|
+
} as StandardActionResponse;
|
|
632
652
|
}
|
|
633
653
|
}
|
|
634
654
|
|
|
@@ -636,7 +656,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
636
656
|
parentLevel?: string;
|
|
637
657
|
subLevelPath: string;
|
|
638
658
|
streamingMethod?: 'Blueprint' | 'AlwaysLoaded';
|
|
639
|
-
}) {
|
|
659
|
+
}): Promise<StandardActionResponse> {
|
|
640
660
|
const parent = params.parentLevel ? this.resolveLevelPath(params.parentLevel) : this.currentLevelPath;
|
|
641
661
|
const sub = this.normalizeLevelPath(params.subLevelPath).path;
|
|
642
662
|
|
|
@@ -652,35 +672,35 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
652
672
|
|
|
653
673
|
// Attempt automation first (cleaner)
|
|
654
674
|
try {
|
|
655
|
-
let response = await this.sendAutomationRequest('manage_level', {
|
|
675
|
+
let response = await this.sendAutomationRequest<LevelResponse>('manage_level', {
|
|
656
676
|
action: 'add_sublevel',
|
|
657
677
|
levelPath: sub, // Backwards compat
|
|
658
678
|
subLevelPath: sub,
|
|
659
679
|
parentPath: parent,
|
|
660
680
|
streamingMethod: params.streamingMethod
|
|
661
|
-
}, { timeoutMs:
|
|
681
|
+
}, { timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS });
|
|
662
682
|
|
|
663
683
|
// Retry with .umap if package not found (Workaround for C++ strictness)
|
|
664
684
|
// Also retry if ADD_FAILED, as UEditorLevelUtils might have failed due to path resolution internally
|
|
665
685
|
if (response && (response.error === 'PACKAGE_NOT_FOUND' || response.error === 'ADD_FAILED') && !sub.endsWith('.umap')) {
|
|
666
686
|
const subWithExt = sub + '.umap';
|
|
667
|
-
response = await this.sendAutomationRequest('manage_level', {
|
|
687
|
+
response = await this.sendAutomationRequest<LevelResponse>('manage_level', {
|
|
668
688
|
action: 'add_sublevel',
|
|
669
689
|
levelPath: subWithExt,
|
|
670
690
|
subLevelPath: subWithExt,
|
|
671
691
|
parentPath: parent,
|
|
672
692
|
streamingMethod: params.streamingMethod
|
|
673
|
-
}, { timeoutMs:
|
|
693
|
+
}, { timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS });
|
|
674
694
|
}
|
|
675
695
|
|
|
676
696
|
if (response.success) {
|
|
677
697
|
this.ensureRecord(sub, { loaded: true, visible: true, streaming: true });
|
|
678
|
-
return response;
|
|
698
|
+
return response as StandardActionResponse;
|
|
679
699
|
} else if (response.error === 'UNKNOWN_ACTION') {
|
|
680
700
|
// Fallthrough to console fallback if action not implemented
|
|
681
701
|
} else {
|
|
682
702
|
// Return actual error if it's something else (e.g. execution failed)
|
|
683
|
-
return response;
|
|
703
|
+
return response as StandardActionResponse;
|
|
684
704
|
}
|
|
685
705
|
} catch (_e: any) {
|
|
686
706
|
// If connection failed, might fallback. But if we got a response, respect it.
|
|
@@ -688,7 +708,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
688
708
|
|
|
689
709
|
// Console fallback
|
|
690
710
|
// Try using LevelEditor.AddLevel command which is available in Editor context
|
|
691
|
-
const consoleResponse = await this.sendAutomationRequest('console_command', {
|
|
711
|
+
const consoleResponse = await this.sendAutomationRequest<LevelResponse>('console_command', {
|
|
692
712
|
command: `LevelEditor.AddLevel ${sub}`
|
|
693
713
|
});
|
|
694
714
|
|
|
@@ -698,7 +718,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
698
718
|
success: true,
|
|
699
719
|
message: `Sublevel added via console: ${sub}`,
|
|
700
720
|
data: { method: 'console' }
|
|
701
|
-
};
|
|
721
|
+
} as StandardActionResponse;
|
|
702
722
|
}
|
|
703
723
|
|
|
704
724
|
return {
|
|
@@ -707,7 +727,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
707
727
|
// Return the last relevant error + console error
|
|
708
728
|
message: 'Failed to add sublevel via automation or console.',
|
|
709
729
|
details: { consoleError: consoleResponse }
|
|
710
|
-
};
|
|
730
|
+
} as StandardActionResponse;
|
|
711
731
|
}
|
|
712
732
|
|
|
713
733
|
async streamLevel(params: {
|
|
@@ -716,7 +736,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
716
736
|
shouldBeLoaded: boolean;
|
|
717
737
|
shouldBeVisible?: boolean;
|
|
718
738
|
position?: [number, number, number];
|
|
719
|
-
}) {
|
|
739
|
+
}): Promise<StandardActionResponse> {
|
|
720
740
|
const rawPath = typeof params.levelPath === 'string' ? params.levelPath.trim() : '';
|
|
721
741
|
const levelPath = rawPath.length > 0 ? rawPath : undefined;
|
|
722
742
|
const providedName = typeof params.levelName === 'string' ? params.levelName.trim() : '';
|
|
@@ -727,13 +747,13 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
727
747
|
const shouldBeVisible = params.shouldBeVisible ?? params.shouldBeLoaded;
|
|
728
748
|
|
|
729
749
|
try {
|
|
730
|
-
const response = await this.sendAutomationRequest('stream_level', {
|
|
750
|
+
const response = await this.sendAutomationRequest<LevelResponse>('stream_level', {
|
|
731
751
|
levelPath: levelPath || '',
|
|
732
752
|
levelName: levelName || '',
|
|
733
753
|
shouldBeLoaded: params.shouldBeLoaded,
|
|
734
754
|
shouldBeVisible
|
|
735
755
|
}, {
|
|
736
|
-
timeoutMs:
|
|
756
|
+
timeoutMs: DEFAULT_ASSET_OP_TIMEOUT_MS
|
|
737
757
|
});
|
|
738
758
|
|
|
739
759
|
if (response.success === false) {
|
|
@@ -758,7 +778,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
758
778
|
handledResult.details = response.details;
|
|
759
779
|
}
|
|
760
780
|
|
|
761
|
-
return handledResult;
|
|
781
|
+
return handledResult as StandardActionResponse;
|
|
762
782
|
}
|
|
763
783
|
|
|
764
784
|
return {
|
|
@@ -768,7 +788,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
768
788
|
levelPath: levelPath,
|
|
769
789
|
loaded: params.shouldBeLoaded,
|
|
770
790
|
visible: shouldBeVisible
|
|
771
|
-
};
|
|
791
|
+
} as StandardActionResponse;
|
|
772
792
|
}
|
|
773
793
|
|
|
774
794
|
const result: Record<string, unknown> = {
|
|
@@ -787,7 +807,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
787
807
|
result.details = response.details;
|
|
788
808
|
}
|
|
789
809
|
|
|
790
|
-
return result;
|
|
810
|
+
return result as StandardActionResponse;
|
|
791
811
|
} catch (_error) {
|
|
792
812
|
// Fallback to console command
|
|
793
813
|
const levelIdentifier = levelName ?? levelPath ?? '';
|
|
@@ -804,7 +824,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
804
824
|
tileSize?: number;
|
|
805
825
|
distanceStreaming?: boolean;
|
|
806
826
|
streamingDistance?: number;
|
|
807
|
-
}) {
|
|
827
|
+
}): Promise<StandardActionResponse> {
|
|
808
828
|
const commands: string[] = [];
|
|
809
829
|
|
|
810
830
|
if (params.enableComposition) {
|
|
@@ -832,7 +852,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
832
852
|
position: [number, number];
|
|
833
853
|
connections?: string[];
|
|
834
854
|
}>;
|
|
835
|
-
}) {
|
|
855
|
+
}): Promise<StandardActionResponse> {
|
|
836
856
|
const command = `OpenLevelBlueprint ${params.eventType}`;
|
|
837
857
|
return this.bridge.executeConsoleCommand(command);
|
|
838
858
|
}
|
|
@@ -841,7 +861,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
841
861
|
name: string;
|
|
842
862
|
type: 'Persistent' | 'Streaming' | 'Lighting' | 'Gameplay';
|
|
843
863
|
parent?: string;
|
|
844
|
-
}) {
|
|
864
|
+
}): Promise<StandardActionResponse> {
|
|
845
865
|
const command = `CreateSubLevel ${params.name} ${params.type} ${params.parent || 'None'}`;
|
|
846
866
|
return this.bridge.executeConsoleCommand(command);
|
|
847
867
|
}
|
|
@@ -852,7 +872,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
852
872
|
gameMode?: string;
|
|
853
873
|
defaultPawn?: string;
|
|
854
874
|
killZ?: number;
|
|
855
|
-
}) {
|
|
875
|
+
}): Promise<StandardActionResponse> {
|
|
856
876
|
const commands: string[] = [];
|
|
857
877
|
|
|
858
878
|
if (params.gravity !== undefined) {
|
|
@@ -879,7 +899,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
879
899
|
async setLevelBounds(params: {
|
|
880
900
|
min: [number, number, number];
|
|
881
901
|
max: [number, number, number];
|
|
882
|
-
}) {
|
|
902
|
+
}): Promise<StandardActionResponse> {
|
|
883
903
|
const command = `SetLevelBounds ${params.min.join(',')} ${params.max.join(',')}`;
|
|
884
904
|
return this.bridge.executeConsoleCommand(command);
|
|
885
905
|
}
|
|
@@ -887,9 +907,9 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
887
907
|
async buildNavMesh(params: {
|
|
888
908
|
rebuildAll?: boolean;
|
|
889
909
|
selectedOnly?: boolean;
|
|
890
|
-
}) {
|
|
910
|
+
}): Promise<StandardActionResponse> {
|
|
891
911
|
try {
|
|
892
|
-
const response = await this.sendAutomationRequest('build_navigation_mesh', {
|
|
912
|
+
const response = await this.sendAutomationRequest<LevelResponse>('build_navigation_mesh', {
|
|
893
913
|
rebuildAll: params.rebuildAll ?? false,
|
|
894
914
|
selectedOnly: params.selectedOnly ?? false
|
|
895
915
|
}, {
|
|
@@ -924,7 +944,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
924
944
|
result.details = response.details;
|
|
925
945
|
}
|
|
926
946
|
|
|
927
|
-
return result;
|
|
947
|
+
return result as StandardActionResponse;
|
|
928
948
|
} catch (error) {
|
|
929
949
|
return {
|
|
930
950
|
success: false,
|
|
@@ -936,14 +956,14 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
936
956
|
async setLevelVisibility(params: {
|
|
937
957
|
levelName: string;
|
|
938
958
|
visible: boolean;
|
|
939
|
-
}) {
|
|
959
|
+
}): Promise<StandardActionResponse> {
|
|
940
960
|
const command = `SetLevelVisibility ${params.levelName} ${params.visible}`;
|
|
941
961
|
return this.bridge.executeConsoleCommand(command);
|
|
942
962
|
}
|
|
943
963
|
|
|
944
964
|
async setWorldOrigin(params: {
|
|
945
965
|
location: [number, number, number];
|
|
946
|
-
}) {
|
|
966
|
+
}): Promise<StandardActionResponse> {
|
|
947
967
|
const command = `SetWorldOriginLocation ${params.location.join(' ')}`;
|
|
948
968
|
return this.bridge.executeConsoleCommand(command);
|
|
949
969
|
}
|
|
@@ -953,7 +973,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
953
973
|
position: [number, number, number];
|
|
954
974
|
size: [number, number, number];
|
|
955
975
|
streamingDistance?: number;
|
|
956
|
-
}) {
|
|
976
|
+
}): Promise<StandardActionResponse> {
|
|
957
977
|
const command = `CreateStreamingVolume ${params.levelName} ${params.position.join(' ')} ${params.size.join(' ')} ${params.streamingDistance || 0}`;
|
|
958
978
|
return this.bridge.executeConsoleCommand(command);
|
|
959
979
|
}
|
|
@@ -962,7 +982,7 @@ export class LevelTools extends BaseTool implements ILevelTools {
|
|
|
962
982
|
levelName: string;
|
|
963
983
|
lodLevel: number;
|
|
964
984
|
distance: number;
|
|
965
|
-
}) {
|
|
985
|
+
}): Promise<StandardActionResponse> {
|
|
966
986
|
const command = `SetLevelLOD ${params.levelName} ${params.lodLevel} ${params.distance}`;
|
|
967
987
|
return this.bridge.executeConsoleCommand(command);
|
|
968
988
|
}
|