unreal-engine-mcp-server 0.4.0 → 0.4.4
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.production +1 -1
- package/.github/copilot-instructions.md +45 -0
- package/.github/workflows/publish-mcp.yml +3 -2
- package/README.md +21 -5
- package/dist/index.js +124 -31
- package/dist/prompts/index.d.ts +10 -3
- package/dist/prompts/index.js +186 -7
- package/dist/resources/actors.d.ts +19 -1
- package/dist/resources/actors.js +55 -64
- package/dist/resources/assets.js +46 -62
- package/dist/resources/levels.d.ts +21 -3
- package/dist/resources/levels.js +29 -54
- package/dist/tools/actors.d.ts +3 -14
- package/dist/tools/actors.js +246 -302
- package/dist/tools/animation.d.ts +57 -102
- package/dist/tools/animation.js +429 -450
- package/dist/tools/assets.d.ts +13 -2
- package/dist/tools/assets.js +52 -44
- package/dist/tools/audio.d.ts +22 -13
- package/dist/tools/audio.js +467 -121
- package/dist/tools/blueprint.d.ts +32 -13
- package/dist/tools/blueprint.js +699 -448
- package/dist/tools/build_environment_advanced.d.ts +0 -1
- package/dist/tools/build_environment_advanced.js +190 -45
- package/dist/tools/consolidated-tool-definitions.js +78 -252
- package/dist/tools/consolidated-tool-handlers.js +506 -133
- package/dist/tools/debug.d.ts +72 -10
- package/dist/tools/debug.js +167 -31
- package/dist/tools/editor.d.ts +9 -2
- package/dist/tools/editor.js +30 -44
- package/dist/tools/foliage.d.ts +34 -15
- package/dist/tools/foliage.js +97 -107
- package/dist/tools/introspection.js +19 -21
- package/dist/tools/landscape.d.ts +1 -2
- package/dist/tools/landscape.js +311 -168
- package/dist/tools/level.d.ts +3 -28
- package/dist/tools/level.js +642 -192
- package/dist/tools/lighting.d.ts +14 -3
- package/dist/tools/lighting.js +236 -123
- package/dist/tools/materials.d.ts +25 -7
- package/dist/tools/materials.js +102 -79
- package/dist/tools/niagara.d.ts +10 -12
- package/dist/tools/niagara.js +74 -94
- package/dist/tools/performance.d.ts +12 -4
- package/dist/tools/performance.js +38 -79
- package/dist/tools/physics.d.ts +34 -10
- package/dist/tools/physics.js +364 -292
- package/dist/tools/rc.js +97 -23
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +125 -22
- package/dist/tools/ui.d.ts +31 -4
- package/dist/tools/ui.js +83 -66
- package/dist/tools/visual.d.ts +11 -0
- package/dist/tools/visual.js +245 -30
- package/dist/types/tool-types.d.ts +0 -6
- package/dist/types/tool-types.js +1 -8
- package/dist/unreal-bridge.d.ts +32 -2
- package/dist/unreal-bridge.js +621 -127
- package/dist/utils/elicitation.d.ts +57 -0
- package/dist/utils/elicitation.js +104 -0
- package/dist/utils/error-handler.d.ts +0 -33
- package/dist/utils/error-handler.js +4 -111
- package/dist/utils/http.d.ts +2 -22
- package/dist/utils/http.js +12 -75
- package/dist/utils/normalize.d.ts +4 -4
- package/dist/utils/normalize.js +15 -7
- package/dist/utils/python-output.d.ts +18 -0
- package/dist/utils/python-output.js +290 -0
- package/dist/utils/python.d.ts +2 -0
- package/dist/utils/python.js +4 -0
- package/dist/utils/response-validator.js +28 -2
- package/dist/utils/result-helpers.d.ts +27 -0
- package/dist/utils/result-helpers.js +147 -0
- package/dist/utils/safe-json.d.ts +0 -2
- package/dist/utils/safe-json.js +0 -43
- package/dist/utils/validation.d.ts +16 -0
- package/dist/utils/validation.js +70 -7
- package/mcp-config-example.json +2 -2
- package/package.json +10 -9
- package/server.json +37 -14
- package/src/index.ts +130 -33
- package/src/prompts/index.ts +211 -13
- package/src/resources/actors.ts +59 -44
- package/src/resources/assets.ts +48 -51
- package/src/resources/levels.ts +35 -45
- package/src/tools/actors.ts +269 -313
- package/src/tools/animation.ts +556 -539
- package/src/tools/assets.ts +53 -43
- package/src/tools/audio.ts +507 -113
- package/src/tools/blueprint.ts +778 -462
- package/src/tools/build_environment_advanced.ts +266 -64
- package/src/tools/consolidated-tool-definitions.ts +90 -264
- package/src/tools/consolidated-tool-handlers.ts +630 -121
- package/src/tools/debug.ts +176 -33
- package/src/tools/editor.ts +35 -37
- package/src/tools/foliage.ts +110 -104
- package/src/tools/introspection.ts +24 -22
- package/src/tools/landscape.ts +334 -181
- package/src/tools/level.ts +683 -182
- package/src/tools/lighting.ts +244 -123
- package/src/tools/materials.ts +114 -83
- package/src/tools/niagara.ts +87 -81
- package/src/tools/performance.ts +49 -88
- package/src/tools/physics.ts +393 -299
- package/src/tools/rc.ts +102 -24
- package/src/tools/sequence.ts +136 -28
- package/src/tools/ui.ts +101 -70
- package/src/tools/visual.ts +250 -29
- package/src/types/tool-types.ts +0 -9
- package/src/unreal-bridge.ts +658 -140
- package/src/utils/elicitation.ts +129 -0
- package/src/utils/error-handler.ts +4 -159
- package/src/utils/http.ts +16 -115
- package/src/utils/normalize.ts +20 -10
- package/src/utils/python-output.ts +351 -0
- package/src/utils/python.ts +3 -0
- package/src/utils/response-validator.ts +25 -2
- package/src/utils/result-helpers.ts +193 -0
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/validation.ts +94 -7
- package/tests/run-unreal-tool-tests.mjs +720 -0
- package/tsconfig.json +2 -2
- package/dist/python-utils.d.ts +0 -29
- package/dist/python-utils.js +0 -54
- package/dist/types/index.d.ts +0 -323
- package/dist/types/index.js +0 -28
- package/dist/utils/cache-manager.d.ts +0 -64
- package/dist/utils/cache-manager.js +0 -176
- package/dist/utils/errors.d.ts +0 -133
- package/dist/utils/errors.js +0 -256
- package/src/python/editor_compat.py +0 -181
- package/src/python-utils.ts +0 -57
- package/src/types/index.ts +0 -414
- package/src/utils/cache-manager.ts +0 -213
- package/src/utils/errors.ts +0 -312
package/src/tools/foliage.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Foliage tools for Unreal Engine
|
|
2
2
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
3
|
+
import { bestEffortInterpretedText, coerceBoolean, coerceNumber, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
|
|
3
4
|
|
|
4
5
|
export class FoliageTools {
|
|
5
6
|
constructor(private bridge: UnrealBridge) {}
|
|
@@ -205,31 +206,39 @@ print('RESULT:' + json.dumps(res))
|
|
|
205
206
|
`.trim();
|
|
206
207
|
|
|
207
208
|
const pyResp = await this.bridge.executePython(py);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
209
|
+
const interpreted = interpretStandardResult(pyResp, {
|
|
210
|
+
successMessage: `Foliage type '${name}' processed`,
|
|
211
|
+
failureMessage: 'Add foliage type failed'
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (!interpreted.success) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
error: coerceString(interpreted.payload.note) ?? interpreted.error ?? 'Add foliage type failed',
|
|
218
|
+
note: coerceString(interpreted.payload.note) ?? bestEffortInterpretedText(interpreted)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const payload = interpreted.payload as Record<string, unknown>;
|
|
223
|
+
const created = coerceBoolean(payload.created, false) ?? false;
|
|
224
|
+
const exists = coerceBoolean(payload.exists_after, false) ?? created;
|
|
225
|
+
const method = coerceString(payload.method) ?? 'Unknown';
|
|
226
|
+
const assetPath = coerceString(payload.asset_path);
|
|
227
|
+
const usedMesh = coerceString(payload.used_mesh);
|
|
228
|
+
const note = coerceString(payload.note);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
created,
|
|
233
|
+
exists,
|
|
234
|
+
method,
|
|
235
|
+
assetPath,
|
|
236
|
+
usedMesh,
|
|
237
|
+
note,
|
|
238
|
+
message: exists
|
|
239
|
+
? `Foliage type '${name}' ready (${method})`
|
|
240
|
+
: `Created foliage '${name}' but verification did not find it yet`
|
|
241
|
+
};
|
|
233
242
|
}
|
|
234
243
|
|
|
235
244
|
// Paint foliage by placing HISM instances (editor-only)
|
|
@@ -275,26 +284,34 @@ px, py, pz = ${pos[0]}, ${pos[1]}, ${pos[2]}
|
|
|
275
284
|
radius = float(${brush}) / 2.0
|
|
276
285
|
|
|
277
286
|
try:
|
|
278
|
-
|
|
279
|
-
|
|
287
|
+
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
|
|
288
|
+
if not actor_subsystem:
|
|
289
|
+
raise RuntimeError('EditorActorSubsystem unavailable. Enable Editor Scripting Utilities plugin.')
|
|
280
290
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
all_actors = actor_subsystem.get_all_level_actors()
|
|
292
|
+
|
|
293
|
+
# Find or create a container actor using modern EditorActorSubsystem
|
|
294
|
+
label = f"FoliageContainer_{foliage_type_name}"
|
|
295
|
+
container = None
|
|
296
|
+
for a in all_actors:
|
|
297
|
+
try:
|
|
298
|
+
if a and a.get_actor_label() == label:
|
|
299
|
+
container = a
|
|
300
|
+
break
|
|
301
|
+
except Exception:
|
|
302
|
+
pass
|
|
303
|
+
|
|
304
|
+
if not container:
|
|
305
|
+
container = actor_subsystem.spawn_actor_from_class(
|
|
306
|
+
unreal.StaticMeshActor,
|
|
307
|
+
unreal.Vector(px, py, pz)
|
|
308
|
+
)
|
|
291
309
|
if not container:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
pass
|
|
310
|
+
raise RuntimeError('Failed to spawn foliage container actor via EditorActorSubsystem')
|
|
311
|
+
try:
|
|
312
|
+
container.set_actor_label(label)
|
|
313
|
+
except Exception:
|
|
314
|
+
pass
|
|
298
315
|
|
|
299
316
|
# Resolve mesh from FoliageType asset
|
|
300
317
|
mesh = None
|
|
@@ -322,12 +339,12 @@ try:
|
|
|
322
339
|
r = random.random() * radius
|
|
323
340
|
x, y, z = px + math.cos(ang) * r, py + math.sin(ang) * r, pz
|
|
324
341
|
try:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
342
|
+
# Spawn static mesh actor at position using modern subsystem
|
|
343
|
+
inst_actor = actor_subsystem.spawn_actor_from_class(
|
|
344
|
+
unreal.StaticMeshActor,
|
|
345
|
+
unreal.Vector(x, y, z),
|
|
346
|
+
unreal.Rotator(0, random.random()*360.0, 0)
|
|
347
|
+
)
|
|
331
348
|
if inst_actor and mesh:
|
|
332
349
|
# Set mesh on the actor's component
|
|
333
350
|
try:
|
|
@@ -355,30 +372,35 @@ print('RESULT:' + json.dumps(res))
|
|
|
355
372
|
`.trim();
|
|
356
373
|
|
|
357
374
|
const pyResp = await this.bridge.executePython(py);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
375
|
+
const interpreted = interpretStandardResult(pyResp, {
|
|
376
|
+
successMessage: `Painted foliage for '${foliageType}'`,
|
|
377
|
+
failureMessage: 'Paint foliage failed'
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (!interpreted.success) {
|
|
381
|
+
return {
|
|
382
|
+
success: false,
|
|
383
|
+
error: coerceString(interpreted.payload.note) ?? interpreted.error ?? 'Paint foliage failed',
|
|
384
|
+
note: coerceString(interpreted.payload.note) ?? bestEffortInterpretedText(interpreted)
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const payload = interpreted.payload as Record<string, unknown>;
|
|
389
|
+
const added = coerceNumber(payload.added) ?? 0;
|
|
390
|
+
const actor = coerceString(payload.actor);
|
|
391
|
+
const component = coerceString(payload.component);
|
|
392
|
+
const usedMesh = coerceString(payload.used_mesh);
|
|
393
|
+
const note = coerceString(payload.note);
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
success: true,
|
|
397
|
+
added,
|
|
398
|
+
actor,
|
|
399
|
+
component,
|
|
400
|
+
usedMesh,
|
|
401
|
+
note,
|
|
402
|
+
message: `Painted ${added} instances for '${foliageType}' around (${pos[0]}, ${pos[1]}, ${pos[2]})`
|
|
403
|
+
};
|
|
382
404
|
}
|
|
383
405
|
|
|
384
406
|
// Create instanced mesh
|
|
@@ -393,7 +415,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
393
415
|
enableCulling?: boolean;
|
|
394
416
|
cullDistance?: number;
|
|
395
417
|
}) {
|
|
396
|
-
|
|
418
|
+
const commands: string[] = [];
|
|
397
419
|
|
|
398
420
|
commands.push(`CreateInstancedStaticMesh ${params.name} ${params.meshPath}`);
|
|
399
421
|
|
|
@@ -411,9 +433,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
411
433
|
commands.push(`SetInstanceCullDistance ${params.name} ${params.cullDistance}`);
|
|
412
434
|
}
|
|
413
435
|
|
|
414
|
-
|
|
415
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
416
|
-
}
|
|
436
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
417
437
|
|
|
418
438
|
return { success: true, message: `Instanced mesh ${params.name} created with ${params.instances.length} instances` };
|
|
419
439
|
}
|
|
@@ -424,7 +444,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
424
444
|
lodDistances?: number[];
|
|
425
445
|
screenSize?: number[];
|
|
426
446
|
}) {
|
|
427
|
-
|
|
447
|
+
const commands: string[] = [];
|
|
428
448
|
|
|
429
449
|
if (params.lodDistances) {
|
|
430
450
|
commands.push(`SetFoliageLODDistances ${params.foliageType} ${params.lodDistances.join(' ')}`);
|
|
@@ -434,9 +454,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
434
454
|
commands.push(`SetFoliageLODScreenSize ${params.foliageType} ${params.screenSize.join(' ')}`);
|
|
435
455
|
}
|
|
436
456
|
|
|
437
|
-
|
|
438
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
439
|
-
}
|
|
457
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
440
458
|
|
|
441
459
|
return { success: true, message: 'Foliage LOD settings updated' };
|
|
442
460
|
}
|
|
@@ -450,7 +468,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
450
468
|
seed?: number;
|
|
451
469
|
tileSize?: number;
|
|
452
470
|
}) {
|
|
453
|
-
|
|
471
|
+
const commands: string[] = [];
|
|
454
472
|
|
|
455
473
|
commands.push(`CreateProceduralFoliageVolume ${params.volumeName} ${params.position.join(' ')} ${params.size.join(' ')}`);
|
|
456
474
|
|
|
@@ -468,9 +486,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
468
486
|
|
|
469
487
|
commands.push(`GenerateProceduralFoliage ${params.volumeName}`);
|
|
470
488
|
|
|
471
|
-
|
|
472
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
473
|
-
}
|
|
489
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
474
490
|
|
|
475
491
|
return { success: true, message: `Procedural foliage volume ${params.volumeName} created` };
|
|
476
492
|
}
|
|
@@ -482,7 +498,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
482
498
|
collisionProfile?: string;
|
|
483
499
|
generateOverlapEvents?: boolean;
|
|
484
500
|
}) {
|
|
485
|
-
|
|
501
|
+
const commands: string[] = [];
|
|
486
502
|
|
|
487
503
|
if (params.collisionEnabled !== undefined) {
|
|
488
504
|
commands.push(`SetFoliageCollision ${params.foliageType} ${params.collisionEnabled}`);
|
|
@@ -496,9 +512,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
496
512
|
commands.push(`SetFoliageOverlapEvents ${params.foliageType} ${params.generateOverlapEvents}`);
|
|
497
513
|
}
|
|
498
514
|
|
|
499
|
-
|
|
500
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
501
|
-
}
|
|
515
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
502
516
|
|
|
503
517
|
return { success: true, message: 'Foliage collision settings updated' };
|
|
504
518
|
}
|
|
@@ -515,7 +529,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
515
529
|
windStrength?: number;
|
|
516
530
|
windSpeed?: number;
|
|
517
531
|
}) {
|
|
518
|
-
|
|
532
|
+
const commands: string[] = [];
|
|
519
533
|
|
|
520
534
|
commands.push(`CreateGrassSystem ${params.name}`);
|
|
521
535
|
|
|
@@ -533,9 +547,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
533
547
|
commands.push(`SetGrassWindSpeed ${params.name} ${params.windSpeed}`);
|
|
534
548
|
}
|
|
535
549
|
|
|
536
|
-
|
|
537
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
538
|
-
}
|
|
550
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
539
551
|
|
|
540
552
|
return { success: true, message: `Grass system ${params.name} created` };
|
|
541
553
|
}
|
|
@@ -577,7 +589,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
577
589
|
updateMesh?: boolean;
|
|
578
590
|
newMeshPath?: string;
|
|
579
591
|
}) {
|
|
580
|
-
|
|
592
|
+
const commands: string[] = [];
|
|
581
593
|
|
|
582
594
|
if (params.updateTransforms) {
|
|
583
595
|
commands.push(`UpdateFoliageTransforms ${params.foliageType}`);
|
|
@@ -589,9 +601,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
589
601
|
|
|
590
602
|
commands.push(`RefreshFoliage ${params.foliageType}`);
|
|
591
603
|
|
|
592
|
-
|
|
593
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
594
|
-
}
|
|
604
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
595
605
|
|
|
596
606
|
return { success: true, message: 'Foliage instances updated' };
|
|
597
607
|
}
|
|
@@ -602,7 +612,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
602
612
|
spawnArea: 'Landscape' | 'StaticMesh' | 'BSP' | 'Foliage' | 'All';
|
|
603
613
|
excludeAreas?: Array<[number, number, number, number]>; // [x, y, z, radius]
|
|
604
614
|
}) {
|
|
605
|
-
|
|
615
|
+
const commands: string[] = [];
|
|
606
616
|
|
|
607
617
|
commands.push(`CreateFoliageSpawner ${params.name} ${params.spawnArea}`);
|
|
608
618
|
|
|
@@ -612,9 +622,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
612
622
|
}
|
|
613
623
|
}
|
|
614
624
|
|
|
615
|
-
|
|
616
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
617
|
-
}
|
|
625
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
618
626
|
|
|
619
627
|
return { success: true, message: `Foliage spawner ${params.name} created` };
|
|
620
628
|
}
|
|
@@ -643,9 +651,7 @@ print('RESULT:' + json.dumps(res))
|
|
|
643
651
|
|
|
644
652
|
commands.push('RebuildFoliageTree');
|
|
645
653
|
|
|
646
|
-
|
|
647
|
-
await this.bridge.executeConsoleCommand(cmd);
|
|
648
|
-
}
|
|
654
|
+
await this.bridge.executeConsoleCommands(commands);
|
|
649
655
|
|
|
650
656
|
return { success: true, message: 'Foliage optimized' };
|
|
651
657
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UnrealBridge } from '../unreal-bridge.js';
|
|
2
2
|
import { Logger } from '../utils/logger.js';
|
|
3
|
+
import { bestEffortInterpretedText, interpretStandardResult } from '../utils/result-helpers.js';
|
|
3
4
|
|
|
4
5
|
export interface ObjectInfo {
|
|
5
6
|
class: string;
|
|
@@ -76,33 +77,34 @@ export class IntrospectionTools {
|
|
|
76
77
|
* Parse Python execution result with better error handling
|
|
77
78
|
*/
|
|
78
79
|
private parsePythonResult(resp: any, operationName: string): any {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
80
|
+
const interpreted = interpretStandardResult(resp, {
|
|
81
|
+
successMessage: `${operationName} succeeded`,
|
|
82
|
+
failureMessage: `${operationName} failed`
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (interpreted.success) {
|
|
86
|
+
return {
|
|
87
|
+
...interpreted.payload,
|
|
88
|
+
success: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const output = bestEffortInterpretedText(interpreted) ?? '';
|
|
93
|
+
if (output) {
|
|
94
|
+
this.log.error(`Failed to parse ${operationName} result: ${output}`);
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (out.includes('ModuleNotFoundError')) {
|
|
96
|
+
|
|
97
|
+
if (output.includes('ModuleNotFoundError')) {
|
|
99
98
|
return { success: false, error: 'Reflection module not available.' };
|
|
100
99
|
}
|
|
101
|
-
if (
|
|
100
|
+
if (output.includes('AttributeError')) {
|
|
102
101
|
return { success: false, error: 'Reflection API method not found. Check Unreal Engine version compatibility.' };
|
|
103
102
|
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
error: `${interpreted.error ?? `${operationName} did not return a valid result`}: ${output.substring(0, 200)}`
|
|
107
|
+
};
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
/**
|