unreal-engine-mcp-server 0.3.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.env.production +1 -1
  2. package/.github/copilot-instructions.md +45 -0
  3. package/.github/workflows/publish-mcp.yml +1 -1
  4. package/README.md +22 -7
  5. package/dist/index.js +137 -46
  6. package/dist/prompts/index.d.ts +10 -3
  7. package/dist/prompts/index.js +186 -7
  8. package/dist/resources/actors.d.ts +19 -1
  9. package/dist/resources/actors.js +55 -64
  10. package/dist/resources/assets.d.ts +3 -2
  11. package/dist/resources/assets.js +117 -109
  12. package/dist/resources/levels.d.ts +21 -3
  13. package/dist/resources/levels.js +31 -56
  14. package/dist/tools/actors.d.ts +3 -14
  15. package/dist/tools/actors.js +246 -302
  16. package/dist/tools/animation.d.ts +57 -102
  17. package/dist/tools/animation.js +429 -450
  18. package/dist/tools/assets.d.ts +13 -2
  19. package/dist/tools/assets.js +58 -46
  20. package/dist/tools/audio.d.ts +22 -13
  21. package/dist/tools/audio.js +467 -121
  22. package/dist/tools/blueprint.d.ts +32 -13
  23. package/dist/tools/blueprint.js +699 -448
  24. package/dist/tools/build_environment_advanced.d.ts +0 -1
  25. package/dist/tools/build_environment_advanced.js +236 -87
  26. package/dist/tools/consolidated-tool-definitions.d.ts +232 -15
  27. package/dist/tools/consolidated-tool-definitions.js +124 -255
  28. package/dist/tools/consolidated-tool-handlers.js +749 -766
  29. package/dist/tools/debug.d.ts +72 -10
  30. package/dist/tools/debug.js +170 -36
  31. package/dist/tools/editor.d.ts +9 -2
  32. package/dist/tools/editor.js +30 -44
  33. package/dist/tools/foliage.d.ts +34 -15
  34. package/dist/tools/foliage.js +97 -107
  35. package/dist/tools/introspection.js +19 -21
  36. package/dist/tools/landscape.d.ts +1 -2
  37. package/dist/tools/landscape.js +311 -168
  38. package/dist/tools/level.d.ts +3 -28
  39. package/dist/tools/level.js +642 -192
  40. package/dist/tools/lighting.d.ts +14 -3
  41. package/dist/tools/lighting.js +236 -123
  42. package/dist/tools/materials.d.ts +25 -7
  43. package/dist/tools/materials.js +102 -79
  44. package/dist/tools/niagara.d.ts +10 -12
  45. package/dist/tools/niagara.js +74 -94
  46. package/dist/tools/performance.d.ts +12 -4
  47. package/dist/tools/performance.js +38 -79
  48. package/dist/tools/physics.d.ts +34 -10
  49. package/dist/tools/physics.js +364 -292
  50. package/dist/tools/rc.js +98 -24
  51. package/dist/tools/sequence.d.ts +1 -0
  52. package/dist/tools/sequence.js +146 -24
  53. package/dist/tools/ui.d.ts +31 -4
  54. package/dist/tools/ui.js +83 -66
  55. package/dist/tools/visual.d.ts +11 -0
  56. package/dist/tools/visual.js +245 -30
  57. package/dist/types/tool-types.d.ts +0 -6
  58. package/dist/types/tool-types.js +1 -8
  59. package/dist/unreal-bridge.d.ts +32 -2
  60. package/dist/unreal-bridge.js +621 -127
  61. package/dist/utils/elicitation.d.ts +57 -0
  62. package/dist/utils/elicitation.js +104 -0
  63. package/dist/utils/error-handler.d.ts +0 -33
  64. package/dist/utils/error-handler.js +4 -111
  65. package/dist/utils/http.d.ts +2 -22
  66. package/dist/utils/http.js +12 -75
  67. package/dist/utils/normalize.d.ts +4 -4
  68. package/dist/utils/normalize.js +15 -7
  69. package/dist/utils/python-output.d.ts +18 -0
  70. package/dist/utils/python-output.js +290 -0
  71. package/dist/utils/python.d.ts +2 -0
  72. package/dist/utils/python.js +4 -0
  73. package/dist/utils/response-validator.d.ts +6 -1
  74. package/dist/utils/response-validator.js +66 -13
  75. package/dist/utils/result-helpers.d.ts +27 -0
  76. package/dist/utils/result-helpers.js +147 -0
  77. package/dist/utils/safe-json.d.ts +0 -2
  78. package/dist/utils/safe-json.js +0 -43
  79. package/dist/utils/validation.d.ts +16 -0
  80. package/dist/utils/validation.js +70 -7
  81. package/mcp-config-example.json +2 -2
  82. package/package.json +11 -10
  83. package/server.json +37 -14
  84. package/src/index.ts +146 -50
  85. package/src/prompts/index.ts +211 -13
  86. package/src/resources/actors.ts +59 -44
  87. package/src/resources/assets.ts +123 -102
  88. package/src/resources/levels.ts +37 -47
  89. package/src/tools/actors.ts +269 -313
  90. package/src/tools/animation.ts +556 -539
  91. package/src/tools/assets.ts +59 -45
  92. package/src/tools/audio.ts +507 -113
  93. package/src/tools/blueprint.ts +778 -462
  94. package/src/tools/build_environment_advanced.ts +312 -106
  95. package/src/tools/consolidated-tool-definitions.ts +136 -267
  96. package/src/tools/consolidated-tool-handlers.ts +871 -795
  97. package/src/tools/debug.ts +179 -38
  98. package/src/tools/editor.ts +35 -37
  99. package/src/tools/foliage.ts +110 -104
  100. package/src/tools/introspection.ts +24 -22
  101. package/src/tools/landscape.ts +334 -181
  102. package/src/tools/level.ts +683 -182
  103. package/src/tools/lighting.ts +244 -123
  104. package/src/tools/materials.ts +114 -83
  105. package/src/tools/niagara.ts +87 -81
  106. package/src/tools/performance.ts +49 -88
  107. package/src/tools/physics.ts +393 -299
  108. package/src/tools/rc.ts +103 -25
  109. package/src/tools/sequence.ts +157 -30
  110. package/src/tools/ui.ts +101 -70
  111. package/src/tools/visual.ts +250 -29
  112. package/src/types/tool-types.ts +0 -9
  113. package/src/unreal-bridge.ts +658 -140
  114. package/src/utils/elicitation.ts +129 -0
  115. package/src/utils/error-handler.ts +4 -159
  116. package/src/utils/http.ts +16 -115
  117. package/src/utils/normalize.ts +20 -10
  118. package/src/utils/python-output.ts +351 -0
  119. package/src/utils/python.ts +3 -0
  120. package/src/utils/response-validator.ts +68 -17
  121. package/src/utils/result-helpers.ts +193 -0
  122. package/src/utils/safe-json.ts +0 -50
  123. package/src/utils/validation.ts +94 -7
  124. package/tests/run-unreal-tool-tests.mjs +720 -0
  125. package/tsconfig.json +2 -2
  126. package/dist/python-utils.d.ts +0 -29
  127. package/dist/python-utils.js +0 -54
  128. package/dist/tools/tool-definitions.d.ts +0 -4919
  129. package/dist/tools/tool-definitions.js +0 -1065
  130. package/dist/tools/tool-handlers.d.ts +0 -47
  131. package/dist/tools/tool-handlers.js +0 -863
  132. package/dist/types/index.d.ts +0 -323
  133. package/dist/types/index.js +0 -28
  134. package/dist/utils/cache-manager.d.ts +0 -64
  135. package/dist/utils/cache-manager.js +0 -176
  136. package/dist/utils/errors.d.ts +0 -133
  137. package/dist/utils/errors.js +0 -256
  138. package/src/python/editor_compat.py +0 -181
  139. package/src/python-utils.ts +0 -57
  140. package/src/tools/tool-definitions.ts +0 -1081
  141. package/src/tools/tool-handlers.ts +0 -973
  142. package/src/types/index.ts +0 -414
  143. package/src/utils/cache-manager.ts +0 -213
  144. package/src/utils/errors.ts +0 -312
@@ -2,7 +2,6 @@ import { UnrealBridge } from '../unreal-bridge.js';
2
2
  export declare class DebugVisualizationTools {
3
3
  private bridge;
4
4
  constructor(bridge: UnrealBridge);
5
- private _executeCommand;
6
5
  private pyDraw;
7
6
  drawDebugLine(params: {
8
7
  start: [number, number, number];
@@ -12,10 +11,17 @@ export declare class DebugVisualizationTools {
12
11
  thickness?: number;
13
12
  }): Promise<{
14
13
  success: boolean;
15
- error?: undefined;
14
+ action: string;
15
+ warnings: string[] | undefined;
16
+ details: string[] | undefined;
17
+ rawOutput: string | undefined;
18
+ raw: unknown;
19
+ message?: string;
20
+ error?: string;
16
21
  } | {
17
22
  success: boolean;
18
23
  error: string;
24
+ action: string;
19
25
  }>;
20
26
  drawDebugBox(params: {
21
27
  center: [number, number, number];
@@ -26,10 +32,17 @@ export declare class DebugVisualizationTools {
26
32
  thickness?: number;
27
33
  }): Promise<{
28
34
  success: boolean;
29
- error?: undefined;
35
+ action: string;
36
+ warnings: string[] | undefined;
37
+ details: string[] | undefined;
38
+ rawOutput: string | undefined;
39
+ raw: unknown;
40
+ message?: string;
41
+ error?: string;
30
42
  } | {
31
43
  success: boolean;
32
44
  error: string;
45
+ action: string;
33
46
  }>;
34
47
  drawDebugSphere(params: {
35
48
  center: [number, number, number];
@@ -40,10 +53,17 @@ export declare class DebugVisualizationTools {
40
53
  thickness?: number;
41
54
  }): Promise<{
42
55
  success: boolean;
43
- error?: undefined;
56
+ action: string;
57
+ warnings: string[] | undefined;
58
+ details: string[] | undefined;
59
+ rawOutput: string | undefined;
60
+ raw: unknown;
61
+ message?: string;
62
+ error?: string;
44
63
  } | {
45
64
  success: boolean;
46
65
  error: string;
66
+ action: string;
47
67
  }>;
48
68
  drawDebugCapsule(params: {
49
69
  center: [number, number, number];
@@ -54,10 +74,17 @@ export declare class DebugVisualizationTools {
54
74
  duration?: number;
55
75
  }): Promise<{
56
76
  success: boolean;
57
- error?: undefined;
77
+ action: string;
78
+ warnings: string[] | undefined;
79
+ details: string[] | undefined;
80
+ rawOutput: string | undefined;
81
+ raw: unknown;
82
+ message?: string;
83
+ error?: string;
58
84
  } | {
59
85
  success: boolean;
60
86
  error: string;
87
+ action: string;
61
88
  }>;
62
89
  drawDebugCone(params: {
63
90
  origin: [number, number, number];
@@ -70,10 +97,17 @@ export declare class DebugVisualizationTools {
70
97
  duration?: number;
71
98
  }): Promise<{
72
99
  success: boolean;
73
- error?: undefined;
100
+ action: string;
101
+ warnings: string[] | undefined;
102
+ details: string[] | undefined;
103
+ rawOutput: string | undefined;
104
+ raw: unknown;
105
+ message?: string;
106
+ error?: string;
74
107
  } | {
75
108
  success: boolean;
76
109
  error: string;
110
+ action: string;
77
111
  }>;
78
112
  drawDebugString(params: {
79
113
  location: [number, number, number];
@@ -83,10 +117,17 @@ export declare class DebugVisualizationTools {
83
117
  fontSize?: number;
84
118
  }): Promise<{
85
119
  success: boolean;
86
- error?: undefined;
120
+ action: string;
121
+ warnings: string[] | undefined;
122
+ details: string[] | undefined;
123
+ rawOutput: string | undefined;
124
+ raw: unknown;
125
+ message?: string;
126
+ error?: string;
87
127
  } | {
88
128
  success: boolean;
89
129
  error: string;
130
+ action: string;
90
131
  }>;
91
132
  drawDebugArrow(params: {
92
133
  start: [number, number, number];
@@ -97,10 +138,17 @@ export declare class DebugVisualizationTools {
97
138
  thickness?: number;
98
139
  }): Promise<{
99
140
  success: boolean;
100
- error?: undefined;
141
+ action: string;
142
+ warnings: string[] | undefined;
143
+ details: string[] | undefined;
144
+ rawOutput: string | undefined;
145
+ raw: unknown;
146
+ message?: string;
147
+ error?: string;
101
148
  } | {
102
149
  success: boolean;
103
150
  error: string;
151
+ action: string;
104
152
  }>;
105
153
  drawDebugPoint(params: {
106
154
  location: [number, number, number];
@@ -109,10 +157,17 @@ export declare class DebugVisualizationTools {
109
157
  duration?: number;
110
158
  }): Promise<{
111
159
  success: boolean;
112
- error?: undefined;
160
+ action: string;
161
+ warnings: string[] | undefined;
162
+ details: string[] | undefined;
163
+ rawOutput: string | undefined;
164
+ raw: unknown;
165
+ message?: string;
166
+ error?: string;
113
167
  } | {
114
168
  success: boolean;
115
169
  error: string;
170
+ action: string;
116
171
  }>;
117
172
  drawDebugCoordinateSystem(params: {
118
173
  location: [number, number, number];
@@ -132,10 +187,17 @@ export declare class DebugVisualizationTools {
132
187
  duration?: number;
133
188
  }): Promise<{
134
189
  success: boolean;
135
- error?: undefined;
190
+ action: string;
191
+ warnings: string[] | undefined;
192
+ details: string[] | undefined;
193
+ rawOutput: string | undefined;
194
+ raw: unknown;
195
+ message?: string;
196
+ error?: string;
136
197
  } | {
137
198
  success: boolean;
138
199
  error: string;
200
+ action: string;
139
201
  }>;
140
202
  clearDebugDrawings(): Promise<any>;
141
203
  showCollision(params: {
@@ -1,41 +1,87 @@
1
+ import { bestEffortInterpretedText, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
2
+ import { parseStandardResult } from '../utils/python-output.js';
1
3
  export class DebugVisualizationTools {
2
4
  bridge;
3
5
  constructor(bridge) {
4
6
  this.bridge = bridge;
5
7
  }
6
- // Execute console command (kept for legacy operations)
7
- async _executeCommand(command) {
8
- return this.bridge.httpCall('/remote/object/call', 'PUT', {
9
- objectPath: '/Script/Engine.Default__KismetSystemLibrary',
10
- functionName: 'ExecuteConsoleCommand',
11
- parameters: {
12
- WorldContextObject: null,
13
- Command: command,
14
- SpecificPlayer: null
15
- },
16
- generateTransaction: false
17
- });
18
- }
19
8
  // Helper to draw via Python SystemLibrary with the editor world
20
- async pyDraw(scriptBody) {
9
+ async pyDraw(scriptBody, meta) {
10
+ const action = (meta?.action || 'debug_draw').replace(/\\/g, '\\\\').replace(/'/g, "\\'");
11
+ const payloadObject = meta?.params ?? {};
12
+ const payloadJson = JSON.stringify(payloadObject).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
13
+ const indentedBody = scriptBody
14
+ .split(/\r?\n/)
15
+ .map(line => ` ${line}`)
16
+ .join('\n');
21
17
  const script = `
22
18
  import unreal
23
- # Use modern UnrealEditorSubsystem instead of deprecated EditorLevelLibrary
19
+ import json
20
+
21
+ payload = json.loads('${payloadJson}')
24
22
  ues = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
25
- if ues:
26
- world = ues.get_editor_world()
27
- else:
28
- # Fallback to deprecated API if subsystem not available
29
- world = unreal.EditorLevelLibrary.get_editor_world()
30
- ${scriptBody}
23
+ if not ues:
24
+ raise Exception('UnrealEditorSubsystem not available')
25
+ world = ues.get_editor_world()
26
+ if not world:
27
+ raise Exception('Editor world unavailable')
28
+ try:
29
+ ${indentedBody}
30
+ print('DEBUG_DRAW:' + json.dumps({'action': '${action}', 'params': payload}))
31
+ print('RESULT:' + json.dumps({'success': True, 'action': '${action}', 'params': payload}))
32
+ except Exception as e:
33
+ print('DEBUG_DRAW_ERROR:' + str(e))
34
+ print('RESULT:' + json.dumps({'success': False, 'action': '${action}', 'error': str(e)}))
31
35
  `.trim()
32
36
  .replace(/\r?\n/g, '\n');
33
37
  try {
34
- await this.bridge.executePython(script);
35
- return { success: true };
38
+ const response = await this.bridge.executePython(script);
39
+ let interpreted = interpretStandardResult(response, {
40
+ successMessage: `${action} executed`,
41
+ failureMessage: `${action} failed`
42
+ });
43
+ const parsed = parseStandardResult(response);
44
+ const parsedPayload = parsed.data ?? {};
45
+ const parsedSuccessValue = parsedPayload.success;
46
+ const normalizedSuccess = typeof parsedSuccessValue === 'string'
47
+ ? ['true', '1', 'yes'].includes(parsedSuccessValue.toLowerCase())
48
+ : parsedSuccessValue === true;
49
+ if (!interpreted.success && normalizedSuccess) {
50
+ interpreted = {
51
+ ...interpreted,
52
+ success: true,
53
+ error: undefined,
54
+ message: interpreted.message || `${action} executed`,
55
+ payload: { ...parsedPayload }
56
+ };
57
+ }
58
+ const finalSuccess = interpreted.success || normalizedSuccess;
59
+ const resolvedAction = coerceString(interpreted.payload.action) ?? action;
60
+ const fallbackOutput = typeof parsed.text === 'string' ? parsed.text : '';
61
+ const rawOutput = bestEffortInterpretedText(interpreted) ?? (fallbackOutput ? fallbackOutput : undefined);
62
+ if (finalSuccess) {
63
+ return {
64
+ ...interpreted.payload,
65
+ success: true,
66
+ action: resolvedAction,
67
+ warnings: interpreted.warnings,
68
+ details: interpreted.details,
69
+ rawOutput,
70
+ raw: parsed.raw ?? interpreted.raw
71
+ };
72
+ }
73
+ return {
74
+ success: false,
75
+ action: resolvedAction,
76
+ error: interpreted.error ?? `${resolvedAction} failed`,
77
+ warnings: interpreted.warnings,
78
+ details: interpreted.details,
79
+ rawOutput,
80
+ raw: parsed.raw ?? interpreted.raw
81
+ };
36
82
  }
37
83
  catch (e) {
38
- return { success: false, error: String(e) };
84
+ return { success: false, error: String(e), action: meta?.action || 'debug_draw' };
39
85
  }
40
86
  }
41
87
  // Draw debug line using Python SystemLibrary
@@ -52,7 +98,16 @@ end = unreal.Vector(${ex}, ${ey}, ${ez})
52
98
  color = unreal.LinearColor(${sr}/255.0, ${sg}/255.0, ${sb}/255.0, ${sa}/255.0)
53
99
  unreal.SystemLibrary.draw_debug_line(world, start, end, color, ${duration}, ${thickness})
54
100
  `;
55
- return this.pyDraw(script);
101
+ return this.pyDraw(script, {
102
+ action: 'debug_line',
103
+ params: {
104
+ start: params.start,
105
+ end: params.end,
106
+ color,
107
+ duration,
108
+ thickness
109
+ }
110
+ });
56
111
  }
57
112
  // Draw debug box using Python SystemLibrary
58
113
  async drawDebugBox(params) {
@@ -71,7 +126,17 @@ rot = unreal.Rotator(${rp}, ${ry}, ${rr})
71
126
  color = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)
72
127
  unreal.SystemLibrary.draw_debug_box(world, center, extent, color, rot, ${duration}, ${thickness})
73
128
  `;
74
- return this.pyDraw(script);
129
+ return this.pyDraw(script, {
130
+ action: 'debug_box',
131
+ params: {
132
+ center: params.center,
133
+ extent: params.extent,
134
+ rotation,
135
+ color,
136
+ duration,
137
+ thickness
138
+ }
139
+ });
75
140
  }
76
141
  // Draw debug sphere using Python SystemLibrary
77
142
  async drawDebugSphere(params) {
@@ -86,7 +151,17 @@ center = unreal.Vector(${cx}, ${cy}, ${cz})
86
151
  color = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)
87
152
  unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segments}, color, ${duration}, ${thickness})
88
153
  `;
89
- return this.pyDraw(script);
154
+ return this.pyDraw(script, {
155
+ action: 'debug_sphere',
156
+ params: {
157
+ center: params.center,
158
+ radius: params.radius,
159
+ segments,
160
+ color,
161
+ duration,
162
+ thickness
163
+ }
164
+ });
90
165
  }
91
166
  // The rest keep console-command fallbacks or editor helpers as before
92
167
  async drawDebugCapsule(params) {
@@ -97,7 +172,17 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
97
172
  const [rp, ry, rr] = rotation;
98
173
  const [cr, cg, cb, ca] = color;
99
174
  const script = `\ncenter = unreal.Vector(${cx}, ${cy}, ${cz})\nrot = unreal.Rotator(${rp}, ${ry}, ${rr})\ncolor = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)\nunreal.SystemLibrary.draw_debug_capsule(world, center, ${params.halfHeight}, ${params.radius}, rot, color, ${duration}, 1.0)\n`;
100
- return this.pyDraw(script);
175
+ return this.pyDraw(script, {
176
+ action: 'debug_capsule',
177
+ params: {
178
+ center: params.center,
179
+ halfHeight: params.halfHeight,
180
+ radius: params.radius,
181
+ rotation,
182
+ color,
183
+ duration
184
+ }
185
+ });
101
186
  }
102
187
  async drawDebugCone(params) {
103
188
  const color = params.color || [255, 0, 255, 255];
@@ -106,7 +191,19 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
106
191
  const [dx, dy, dz] = params.direction;
107
192
  const [cr, cg, cb, ca] = color;
108
193
  const script = `\norigin = unreal.Vector(${ox}, ${oy}, ${oz})\ndir = unreal.Vector(${dx}, ${dy}, ${dz})\ncolor = unreal.LinearColor(${cr}/255.0, ${cg}/255.0, ${cb}/255.0, ${ca}/255.0)\nunreal.SystemLibrary.draw_debug_cone(world, origin, dir, ${params.length}, ${params.angleWidth}, ${params.angleHeight}, ${params.numSides || 12}, color, ${duration}, 1.0)\n`;
109
- return this.pyDraw(script);
194
+ return this.pyDraw(script, {
195
+ action: 'debug_cone',
196
+ params: {
197
+ origin: params.origin,
198
+ direction: params.direction,
199
+ length: params.length,
200
+ angleWidth: params.angleWidth,
201
+ angleHeight: params.angleHeight,
202
+ numSides: params.numSides || 12,
203
+ color,
204
+ duration
205
+ }
206
+ });
110
207
  }
111
208
  async drawDebugString(params) {
112
209
  const color = params.color || [255, 255, 255, 255];
@@ -114,7 +211,16 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
114
211
  const [x, y, z] = params.location;
115
212
  const [r, g, b, a] = color;
116
213
  const script = `\nloc = unreal.Vector(${x}, ${y}, ${z})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_string(world, loc, "${params.text.replace(/"/g, '\\"')}", None, color, ${duration})\n`;
117
- return this.pyDraw(script);
214
+ return this.pyDraw(script, {
215
+ action: 'debug_string',
216
+ params: {
217
+ location: params.location,
218
+ text: params.text,
219
+ color,
220
+ duration,
221
+ fontSize: params.fontSize
222
+ }
223
+ });
118
224
  }
119
225
  async drawDebugArrow(params) {
120
226
  const color = params.color || [0, 255, 255, 255];
@@ -124,7 +230,17 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
124
230
  const [ex, ey, ez] = params.end;
125
231
  const [r, g, b, a] = color;
126
232
  const script = `\nstart = unreal.Vector(${sx}, ${sy}, ${sz})\nend = unreal.Vector(${ex}, ${ey}, ${ez})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_arrow(world, start, end, ${params.arrowSize || 10.0}, color, ${duration}, ${thickness})\n`;
127
- return this.pyDraw(script);
233
+ return this.pyDraw(script, {
234
+ action: 'debug_arrow',
235
+ params: {
236
+ start: params.start,
237
+ end: params.end,
238
+ arrowSize: params.arrowSize || 10.0,
239
+ color,
240
+ duration,
241
+ thickness
242
+ }
243
+ });
128
244
  }
129
245
  async drawDebugPoint(params) {
130
246
  const size = params.size || 10.0;
@@ -133,7 +249,15 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
133
249
  const [x, y, z] = params.location;
134
250
  const [r, g, b, a] = color;
135
251
  const script = `\nloc = unreal.Vector(${x}, ${y}, ${z})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_point(world, loc, ${size}, color, ${duration})\n`;
136
- return this.pyDraw(script);
252
+ return this.pyDraw(script, {
253
+ action: 'debug_point',
254
+ params: {
255
+ location: params.location,
256
+ size,
257
+ color,
258
+ duration
259
+ }
260
+ });
137
261
  }
138
262
  async drawDebugCoordinateSystem(params) {
139
263
  const rotation = params.rotation || [0, 0, 0];
@@ -153,7 +277,19 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
153
277
  const [rp, ry, rr] = params.rotation;
154
278
  const [r, g, b, a] = color;
155
279
  const script = `\norigin = unreal.Vector(${ox}, ${oy}, ${oz})\nrot = unreal.Rotator(${rp}, ${ry}, ${rr})\ncolor = unreal.LinearColor(${r}/255.0, ${g}/255.0, ${b}/255.0, ${a}/255.0)\nunreal.SystemLibrary.draw_debug_frustum(world, origin, rot, ${params.fov}, ${aspectRatio}, ${nearPlane}, ${farPlane}, color, ${duration})\n`;
156
- return this.pyDraw(script);
280
+ return this.pyDraw(script, {
281
+ action: 'debug_frustum',
282
+ params: {
283
+ origin: params.origin,
284
+ rotation: params.rotation,
285
+ fov: params.fov,
286
+ aspectRatio,
287
+ nearPlane,
288
+ farPlane,
289
+ color,
290
+ duration
291
+ }
292
+ });
157
293
  }
158
294
  async clearDebugDrawings() {
159
295
  return this.bridge.executeConsoleCommand('FlushPersistentDebugLines');
@@ -167,9 +303,7 @@ unreal.SystemLibrary.draw_debug_sphere(world, center, ${params.radius}, ${segmen
167
303
  else {
168
304
  commands.push('show Collision 0');
169
305
  }
170
- for (const cmd of commands) {
171
- await this.bridge.executeConsoleCommand(cmd);
172
- }
306
+ await this.bridge.executeConsoleCommands(commands);
173
307
  return { success: true, message: `Collision visualization ${params.enabled ? 'enabled' : 'disabled'}` };
174
308
  }
175
309
  async showBounds(params) {
@@ -42,12 +42,19 @@ export declare class EditorTools {
42
42
  }>;
43
43
  buildLighting(): Promise<{
44
44
  success: boolean;
45
- message: any;
45
+ message: string;
46
46
  error?: undefined;
47
+ details?: undefined;
48
+ } | {
49
+ success: boolean;
50
+ error: string;
51
+ details: string | undefined;
52
+ message?: undefined;
47
53
  } | {
48
54
  success: boolean;
49
- error: any;
55
+ error: string;
50
56
  message?: undefined;
57
+ details?: undefined;
51
58
  }>;
52
59
  setViewportCamera(location?: {
53
60
  x: number;
@@ -1,4 +1,5 @@
1
1
  import { toVec3Object, toRotObject } from '../utils/normalize.js';
2
+ import { bestEffortInterpretedText, coerceString, interpretStandardResult } from '../utils/result-helpers.js';
2
3
  export class EditorTools {
3
4
  bridge;
4
5
  constructor(bridge) {
@@ -65,28 +66,13 @@ else:
65
66
  print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
66
67
  `.trim();
67
68
  const resp = await this.bridge.executePython(pythonCmd);
68
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
69
- const m = out.match(/RESULT:({.*})/);
70
- if (m) {
71
- try {
72
- const parsed = JSON.parse(m[1]);
73
- if (parsed.success) {
74
- const method = parsed.method || 'LevelEditorSubsystem';
75
- return { success: true, message: `PIE started (via ${method})` };
76
- }
77
- }
78
- catch {
79
- try {
80
- // Fallback: handle non-JSON python dict-style output
81
- const sanitized = m[1].replace(/'/g, '"').replace(/\bTrue\b/g, 'true').replace(/\bFalse\b/g, 'false');
82
- const parsed = JSON.parse(sanitized);
83
- if (parsed.success) {
84
- const method = parsed.method || 'LevelEditorSubsystem';
85
- return { success: true, message: `PIE started (via ${method})` };
86
- }
87
- }
88
- catch { }
89
- }
69
+ const interpreted = interpretStandardResult(resp, {
70
+ successMessage: 'PIE started',
71
+ failureMessage: 'Failed to start PIE'
72
+ });
73
+ if (interpreted.success) {
74
+ const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
75
+ return { success: true, message: `PIE started (via ${method})` };
90
76
  }
91
77
  // If not verified, fall through to fallback
92
78
  }
@@ -125,20 +111,18 @@ else:
125
111
  print('RESULT:' + json.dumps({'success': False, 'error': 'LevelEditorSubsystem not available'}))
126
112
  `.trim();
127
113
  const resp = await this.bridge.executePython(pythonCmd);
128
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
129
- const m = out.match(/RESULT:({.*})/);
130
- if (m) {
131
- try {
132
- const parsed = JSON.parse(m[1].replace(/'/g, '"'));
133
- if (parsed.success) {
134
- const method = parsed.method || 'LevelEditorSubsystem';
135
- return { success: true, message: `PIE stopped via ${method}` };
136
- }
137
- }
138
- catch { }
114
+ const interpreted = interpretStandardResult(resp, {
115
+ successMessage: 'PIE stopped successfully',
116
+ failureMessage: 'Failed to stop PIE'
117
+ });
118
+ if (interpreted.success) {
119
+ const method = coerceString(interpreted.payload.method) ?? 'LevelEditorSubsystem';
120
+ return { success: true, message: `PIE stopped via ${method}` };
139
121
  }
140
- // Default success message if parsing fails
141
- return { success: true, message: 'PIE stopped successfully' };
122
+ if (interpreted.error) {
123
+ return { success: false, error: interpreted.error };
124
+ }
125
+ return { success: false, error: 'Failed to stop PIE' };
142
126
  }
143
127
  catch {
144
128
  // Fallback to console command
@@ -193,16 +177,18 @@ except Exception as e:
193
177
  print('RESULT:' + json.dumps({'success': False, 'error': str(e)}))
194
178
  `.trim();
195
179
  const resp = await this.bridge.executePython(py);
196
- const out = typeof resp === 'string' ? resp : JSON.stringify(resp);
197
- const m = out.match(/RESULT:({.*})/);
198
- if (m) {
199
- try {
200
- const parsed = JSON.parse(m[1].replace(/'/g, '"'));
201
- return parsed.success ? { success: true, message: parsed.message } : { success: false, error: parsed.error };
202
- }
203
- catch { }
180
+ const interpreted = interpretStandardResult(resp, {
181
+ successMessage: 'Lighting build started',
182
+ failureMessage: 'Failed to build lighting'
183
+ });
184
+ if (interpreted.success) {
185
+ return { success: true, message: interpreted.message };
204
186
  }
205
- return { success: true, message: 'Lighting build started' };
187
+ return {
188
+ success: false,
189
+ error: interpreted.error ?? 'Failed to build lighting',
190
+ details: bestEffortInterpretedText(interpreted)
191
+ };
206
192
  }
207
193
  catch (err) {
208
194
  return { success: false, error: `Failed to build lighting: ${err}` };
@@ -14,22 +14,32 @@ export declare class FoliageTools {
14
14
  groundSlope?: number;
15
15
  }): Promise<{
16
16
  success: boolean;
17
- error: any;
17
+ error: string;
18
+ note?: undefined;
19
+ created?: undefined;
20
+ exists?: undefined;
21
+ method?: undefined;
22
+ assetPath?: undefined;
23
+ usedMesh?: undefined;
24
+ message?: undefined;
25
+ } | {
26
+ success: boolean;
27
+ error: string;
28
+ note: string | undefined;
18
29
  created?: undefined;
19
30
  exists?: undefined;
20
31
  method?: undefined;
21
32
  assetPath?: undefined;
22
33
  usedMesh?: undefined;
23
- note?: undefined;
24
34
  message?: undefined;
25
35
  } | {
26
36
  success: boolean;
27
- created: any;
28
- exists: any;
29
- method: any;
30
- assetPath: any;
31
- usedMesh: any;
32
- note: any;
37
+ created: boolean;
38
+ exists: boolean;
39
+ method: string;
40
+ assetPath: string | undefined;
41
+ usedMesh: string | undefined;
42
+ note: string | undefined;
33
43
  message: string;
34
44
  error?: undefined;
35
45
  }>;
@@ -41,20 +51,29 @@ export declare class FoliageTools {
41
51
  eraseMode?: boolean;
42
52
  }): Promise<{
43
53
  success: boolean;
44
- error: any;
54
+ error: string;
55
+ note?: undefined;
56
+ added?: undefined;
57
+ actor?: undefined;
58
+ component?: undefined;
59
+ usedMesh?: undefined;
60
+ message?: undefined;
61
+ } | {
62
+ success: boolean;
63
+ error: string;
64
+ note: string | undefined;
45
65
  added?: undefined;
46
66
  actor?: undefined;
47
67
  component?: undefined;
48
68
  usedMesh?: undefined;
49
- note?: undefined;
50
69
  message?: undefined;
51
70
  } | {
52
71
  success: boolean;
53
- added: any;
54
- actor: any;
55
- component: any;
56
- usedMesh: any;
57
- note: any;
72
+ added: number;
73
+ actor: string | undefined;
74
+ component: string | undefined;
75
+ usedMesh: string | undefined;
76
+ note: string | undefined;
58
77
  message: string;
59
78
  error?: undefined;
60
79
  }>;