unreal-engine-mcp-server 0.2.1

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 (155) hide show
  1. package/.dockerignore +57 -0
  2. package/.env.production +25 -0
  3. package/.eslintrc.json +54 -0
  4. package/.github/workflows/publish-mcp.yml +75 -0
  5. package/Dockerfile +54 -0
  6. package/LICENSE +21 -0
  7. package/Public/icon.png +0 -0
  8. package/README.md +209 -0
  9. package/claude_desktop_config_example.json +13 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.js +7 -0
  12. package/dist/index.d.ts +31 -0
  13. package/dist/index.js +484 -0
  14. package/dist/prompts/index.d.ts +14 -0
  15. package/dist/prompts/index.js +38 -0
  16. package/dist/python-utils.d.ts +29 -0
  17. package/dist/python-utils.js +54 -0
  18. package/dist/resources/actors.d.ts +13 -0
  19. package/dist/resources/actors.js +83 -0
  20. package/dist/resources/assets.d.ts +23 -0
  21. package/dist/resources/assets.js +245 -0
  22. package/dist/resources/levels.d.ts +17 -0
  23. package/dist/resources/levels.js +94 -0
  24. package/dist/tools/actors.d.ts +51 -0
  25. package/dist/tools/actors.js +459 -0
  26. package/dist/tools/animation.d.ts +196 -0
  27. package/dist/tools/animation.js +579 -0
  28. package/dist/tools/assets.d.ts +21 -0
  29. package/dist/tools/assets.js +304 -0
  30. package/dist/tools/audio.d.ts +170 -0
  31. package/dist/tools/audio.js +416 -0
  32. package/dist/tools/blueprint.d.ts +144 -0
  33. package/dist/tools/blueprint.js +652 -0
  34. package/dist/tools/build_environment_advanced.d.ts +66 -0
  35. package/dist/tools/build_environment_advanced.js +484 -0
  36. package/dist/tools/consolidated-tool-definitions.d.ts +2598 -0
  37. package/dist/tools/consolidated-tool-definitions.js +607 -0
  38. package/dist/tools/consolidated-tool-handlers.d.ts +2 -0
  39. package/dist/tools/consolidated-tool-handlers.js +1050 -0
  40. package/dist/tools/debug.d.ts +185 -0
  41. package/dist/tools/debug.js +265 -0
  42. package/dist/tools/editor.d.ts +88 -0
  43. package/dist/tools/editor.js +365 -0
  44. package/dist/tools/engine.d.ts +30 -0
  45. package/dist/tools/engine.js +36 -0
  46. package/dist/tools/foliage.d.ts +155 -0
  47. package/dist/tools/foliage.js +525 -0
  48. package/dist/tools/introspection.d.ts +98 -0
  49. package/dist/tools/introspection.js +683 -0
  50. package/dist/tools/landscape.d.ts +158 -0
  51. package/dist/tools/landscape.js +375 -0
  52. package/dist/tools/level.d.ts +110 -0
  53. package/dist/tools/level.js +362 -0
  54. package/dist/tools/lighting.d.ts +159 -0
  55. package/dist/tools/lighting.js +1179 -0
  56. package/dist/tools/materials.d.ts +34 -0
  57. package/dist/tools/materials.js +146 -0
  58. package/dist/tools/niagara.d.ts +145 -0
  59. package/dist/tools/niagara.js +289 -0
  60. package/dist/tools/performance.d.ts +163 -0
  61. package/dist/tools/performance.js +412 -0
  62. package/dist/tools/physics.d.ts +189 -0
  63. package/dist/tools/physics.js +784 -0
  64. package/dist/tools/rc.d.ts +110 -0
  65. package/dist/tools/rc.js +363 -0
  66. package/dist/tools/sequence.d.ts +112 -0
  67. package/dist/tools/sequence.js +675 -0
  68. package/dist/tools/tool-definitions.d.ts +4919 -0
  69. package/dist/tools/tool-definitions.js +891 -0
  70. package/dist/tools/tool-handlers.d.ts +47 -0
  71. package/dist/tools/tool-handlers.js +830 -0
  72. package/dist/tools/ui.d.ts +171 -0
  73. package/dist/tools/ui.js +337 -0
  74. package/dist/tools/visual.d.ts +29 -0
  75. package/dist/tools/visual.js +67 -0
  76. package/dist/types/env.d.ts +10 -0
  77. package/dist/types/env.js +18 -0
  78. package/dist/types/index.d.ts +323 -0
  79. package/dist/types/index.js +28 -0
  80. package/dist/types/tool-types.d.ts +274 -0
  81. package/dist/types/tool-types.js +13 -0
  82. package/dist/unreal-bridge.d.ts +126 -0
  83. package/dist/unreal-bridge.js +992 -0
  84. package/dist/utils/cache-manager.d.ts +64 -0
  85. package/dist/utils/cache-manager.js +176 -0
  86. package/dist/utils/error-handler.d.ts +66 -0
  87. package/dist/utils/error-handler.js +243 -0
  88. package/dist/utils/errors.d.ts +133 -0
  89. package/dist/utils/errors.js +256 -0
  90. package/dist/utils/http.d.ts +26 -0
  91. package/dist/utils/http.js +135 -0
  92. package/dist/utils/logger.d.ts +12 -0
  93. package/dist/utils/logger.js +32 -0
  94. package/dist/utils/normalize.d.ts +17 -0
  95. package/dist/utils/normalize.js +49 -0
  96. package/dist/utils/response-validator.d.ts +34 -0
  97. package/dist/utils/response-validator.js +121 -0
  98. package/dist/utils/safe-json.d.ts +4 -0
  99. package/dist/utils/safe-json.js +97 -0
  100. package/dist/utils/stdio-redirect.d.ts +2 -0
  101. package/dist/utils/stdio-redirect.js +20 -0
  102. package/dist/utils/validation.d.ts +50 -0
  103. package/dist/utils/validation.js +173 -0
  104. package/mcp-config-example.json +14 -0
  105. package/package.json +63 -0
  106. package/server.json +60 -0
  107. package/src/cli.ts +7 -0
  108. package/src/index.ts +543 -0
  109. package/src/prompts/index.ts +51 -0
  110. package/src/python/editor_compat.py +181 -0
  111. package/src/python-utils.ts +57 -0
  112. package/src/resources/actors.ts +92 -0
  113. package/src/resources/assets.ts +251 -0
  114. package/src/resources/levels.ts +83 -0
  115. package/src/tools/actors.ts +480 -0
  116. package/src/tools/animation.ts +713 -0
  117. package/src/tools/assets.ts +305 -0
  118. package/src/tools/audio.ts +548 -0
  119. package/src/tools/blueprint.ts +736 -0
  120. package/src/tools/build_environment_advanced.ts +526 -0
  121. package/src/tools/consolidated-tool-definitions.ts +619 -0
  122. package/src/tools/consolidated-tool-handlers.ts +1093 -0
  123. package/src/tools/debug.ts +368 -0
  124. package/src/tools/editor.ts +360 -0
  125. package/src/tools/engine.ts +32 -0
  126. package/src/tools/foliage.ts +652 -0
  127. package/src/tools/introspection.ts +778 -0
  128. package/src/tools/landscape.ts +523 -0
  129. package/src/tools/level.ts +410 -0
  130. package/src/tools/lighting.ts +1316 -0
  131. package/src/tools/materials.ts +148 -0
  132. package/src/tools/niagara.ts +312 -0
  133. package/src/tools/performance.ts +549 -0
  134. package/src/tools/physics.ts +924 -0
  135. package/src/tools/rc.ts +437 -0
  136. package/src/tools/sequence.ts +791 -0
  137. package/src/tools/tool-definitions.ts +907 -0
  138. package/src/tools/tool-handlers.ts +941 -0
  139. package/src/tools/ui.ts +499 -0
  140. package/src/tools/visual.ts +60 -0
  141. package/src/types/env.ts +27 -0
  142. package/src/types/index.ts +414 -0
  143. package/src/types/tool-types.ts +343 -0
  144. package/src/unreal-bridge.ts +1118 -0
  145. package/src/utils/cache-manager.ts +213 -0
  146. package/src/utils/error-handler.ts +320 -0
  147. package/src/utils/errors.ts +312 -0
  148. package/src/utils/http.ts +184 -0
  149. package/src/utils/logger.ts +30 -0
  150. package/src/utils/normalize.ts +54 -0
  151. package/src/utils/response-validator.ts +145 -0
  152. package/src/utils/safe-json.ts +112 -0
  153. package/src/utils/stdio-redirect.ts +18 -0
  154. package/src/utils/validation.ts +212 -0
  155. package/tsconfig.json +33 -0
@@ -0,0 +1,459 @@
1
+ export class ActorTools {
2
+ bridge;
3
+ constructor(bridge) {
4
+ this.bridge = bridge;
5
+ }
6
+ async spawn(params) {
7
+ // Validate classPath
8
+ if (!params.classPath || typeof params.classPath !== 'string') {
9
+ throw new Error(`Invalid classPath: ${params.classPath}`);
10
+ }
11
+ // Auto-map common shape names to proper asset paths
12
+ const shapeMapping = {
13
+ 'cube': '/Engine/BasicShapes/Cube',
14
+ 'sphere': '/Engine/BasicShapes/Sphere',
15
+ 'cylinder': '/Engine/BasicShapes/Cylinder',
16
+ 'cone': '/Engine/BasicShapes/Cone',
17
+ 'plane': '/Engine/BasicShapes/Plane',
18
+ 'torus': '/Engine/BasicShapes/Torus',
19
+ 'box': '/Engine/BasicShapes/Cube', // Common alias
20
+ 'ball': '/Engine/BasicShapes/Sphere', // Common alias
21
+ };
22
+ // Check if classPath is just a simple shape name (case-insensitive)
23
+ const lowerPath = params.classPath.toLowerCase();
24
+ if (shapeMapping[lowerPath]) {
25
+ params.classPath = shapeMapping[lowerPath];
26
+ }
27
+ // Auto-detect and handle asset paths (like /Engine/BasicShapes/Cube)
28
+ // The Python code will automatically spawn a StaticMeshActor and assign the mesh
29
+ // So we don't reject asset paths anymore - let Python handle them intelligently
30
+ // Only reject obviously invalid patterns
31
+ if (params.classPath === 'InvalidActorClass' ||
32
+ params.classPath === 'NoSlash' ||
33
+ params.classPath.startsWith('/Invalid/') ||
34
+ params.classPath.startsWith('/NotExist/')) {
35
+ throw new Error(`Invalid actor class: ${params.classPath}`);
36
+ }
37
+ // Try Python API first for better control and naming
38
+ try {
39
+ return await this.spawnViaPython(params);
40
+ }
41
+ catch (pythonErr) {
42
+ // Check if this is a known failure that shouldn't fall back
43
+ const errorStr = String(pythonErr).toLowerCase();
44
+ if (errorStr.includes('abstract') || errorStr.includes('class not found')) {
45
+ // Don't try console fallback for abstract or non-existent classes
46
+ throw pythonErr;
47
+ }
48
+ // Check if the error is because of PIE mode
49
+ if (String(pythonErr).includes('Play In Editor mode')) {
50
+ // Don't fall back to console if we're in PIE mode
51
+ throw pythonErr;
52
+ }
53
+ // Fallback to console if Python fails for other reasons
54
+ // Only log if not a known/expected error
55
+ if (!String(pythonErr).includes('No valid result from Python')) {
56
+ console.error('Python spawn failed, falling back to console:', pythonErr);
57
+ }
58
+ return this.spawnViaConsole(params);
59
+ }
60
+ }
61
+ async spawnViaPython(params) {
62
+ try {
63
+ // Normalize and validate location
64
+ const loc = params.location ?? { x: 0, y: 0, z: 100 };
65
+ if (loc === null) {
66
+ throw new Error('Invalid location: null is not allowed');
67
+ }
68
+ if (typeof loc !== 'object' ||
69
+ typeof loc.x !== 'number' ||
70
+ typeof loc.y !== 'number' ||
71
+ typeof loc.z !== 'number') {
72
+ throw new Error('Invalid location: must have numeric x, y, z properties');
73
+ }
74
+ // Normalize and validate rotation
75
+ const rot = params.rotation ?? { pitch: 0, yaw: 0, roll: 0 };
76
+ if (rot === null) {
77
+ throw new Error('Invalid rotation: null is not allowed');
78
+ }
79
+ if (typeof rot !== 'object' ||
80
+ typeof rot.pitch !== 'number' ||
81
+ typeof rot.yaw !== 'number' ||
82
+ typeof rot.roll !== 'number') {
83
+ throw new Error('Invalid rotation: must have numeric pitch, yaw, roll properties');
84
+ }
85
+ // Resolve the class path
86
+ const fullClassPath = this.resolveActorClass(params.classPath);
87
+ let className = params.classPath;
88
+ // Extract simple class name for naming the actor
89
+ if (fullClassPath.includes('.')) {
90
+ className = fullClassPath.split('.').pop() || params.classPath;
91
+ }
92
+ const pythonCmd = `
93
+ import unreal
94
+ import json
95
+
96
+ result = {"success": False, "message": "", "actor_name": ""}
97
+
98
+ # Check if editor is in play mode first
99
+ try:
100
+ les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
101
+ if les and les.is_in_play_in_editor():
102
+ result["message"] = "Cannot spawn actors while in Play In Editor mode. Please stop PIE first."
103
+ print(f"RESULT:{json.dumps(result)}")
104
+ # Exit early from this script
105
+ raise SystemExit(0)
106
+ except SystemExit:
107
+ # Re-raise the SystemExit to exit properly
108
+ raise
109
+ except:
110
+ pass # Continue if we can't check PIE state
111
+
112
+ # List of abstract classes that cannot be spawned
113
+ abstract_classes = ['PlaneReflectionCapture', 'ReflectionCapture', 'Actor', 'Pawn', 'Character']
114
+
115
+ # Check for abstract classes
116
+ if "${params.classPath}" in abstract_classes:
117
+ result["message"] = f"Cannot spawn ${params.classPath}: class is abstract and cannot be instantiated"
118
+ print(f"RESULT:{json.dumps(result)}")
119
+ else:
120
+ try:
121
+ # Get the world using the modern subsystem API
122
+ editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
123
+ world = editor_subsystem.get_editor_world() if hasattr(editor_subsystem, 'get_editor_world') else None
124
+ if not world:
125
+ # Try LevelEditorSubsystem as fallback
126
+ level_subsystem = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
127
+ if hasattr(level_subsystem, 'get_editor_world'):
128
+ world = level_subsystem.get_editor_world()
129
+
130
+ # Handle content paths (assets) vs class names
131
+ class_path = "${params.classPath}"
132
+ location = unreal.Vector(${loc.x}, ${loc.y}, ${loc.z})
133
+ rotation = unreal.Rotator(${rot.pitch}, ${rot.yaw}, ${rot.roll})
134
+ actor = None
135
+
136
+ # Check if this is a content path (starts with /Game or /Engine)
137
+ if class_path.startswith('/Game') or class_path.startswith('/Engine'):
138
+ # This is a content asset path - try to load and spawn it
139
+ try:
140
+ # For blueprint classes or static meshes
141
+ asset = unreal.EditorAssetLibrary.load_asset(class_path)
142
+ if asset:
143
+ # If it's a blueprint class
144
+ if isinstance(asset, unreal.Blueprint):
145
+ actor_class = asset.generated_class()
146
+ if actor_class:
147
+ # Use modern EditorActorSubsystem API
148
+ actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
149
+ actor = actor_subsys.spawn_actor_from_class(
150
+ actor_class,
151
+ location,
152
+ rotation
153
+ )
154
+ # If it's a static mesh, spawn a StaticMeshActor and assign the mesh
155
+ elif isinstance(asset, unreal.StaticMesh):
156
+ # Use modern EditorActorSubsystem API
157
+ actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
158
+ actor = actor_subsys.spawn_actor_from_class(
159
+ unreal.StaticMeshActor,
160
+ location,
161
+ rotation
162
+ )
163
+ if actor:
164
+ mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
165
+ if mesh_component:
166
+ mesh_component.set_static_mesh(asset)
167
+ # Make it movable so physics can be applied
168
+ mesh_component.set_editor_property('mobility', unreal.ComponentMobility.MOVABLE)
169
+ except Exception as load_err:
170
+ # If asset loading fails, try basic shapes from Engine content
171
+ shape_map = {
172
+ 'cube': '/Engine/BasicShapes/Cube',
173
+ 'sphere': '/Engine/BasicShapes/Sphere',
174
+ 'cylinder': '/Engine/BasicShapes/Cylinder',
175
+ 'cone': '/Engine/BasicShapes/Cone',
176
+ 'plane': '/Engine/BasicShapes/Plane',
177
+ 'torus': '/Engine/BasicShapes/Torus'
178
+ }
179
+
180
+ # Check if it's a basic shape name or path
181
+ mesh_path = None
182
+ lower_path = class_path.lower()
183
+ for shape_name, shape_path in shape_map.items():
184
+ if shape_name in lower_path:
185
+ mesh_path = shape_path
186
+ break
187
+
188
+ if mesh_path:
189
+ # Use Engine's basic shape
190
+ shape_mesh = unreal.EditorAssetLibrary.load_asset(mesh_path)
191
+ if shape_mesh:
192
+ # Use modern EditorActorSubsystem API
193
+ actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
194
+ actor = actor_subsys.spawn_actor_from_class(
195
+ unreal.StaticMeshActor,
196
+ location,
197
+ rotation
198
+ )
199
+ if actor:
200
+ mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
201
+ if mesh_component:
202
+ mesh_component.set_static_mesh(shape_mesh)
203
+ # Make it movable so physics can be applied
204
+ mesh_component.set_editor_property('mobility', unreal.ComponentMobility.MOVABLE)
205
+
206
+ # If not a content path or content spawn failed, try as a class name
207
+ if not actor:
208
+ if class_path == "StaticMeshActor":
209
+ # Use modern EditorActorSubsystem API
210
+ actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
211
+ actor = actor_subsys.spawn_actor_from_class(
212
+ unreal.StaticMeshActor,
213
+ location,
214
+ rotation
215
+ )
216
+
217
+ if actor:
218
+ # Assign a default mesh (cube)
219
+ cube_mesh = unreal.EditorAssetLibrary.load_asset('/Engine/BasicShapes/Cube')
220
+ if cube_mesh:
221
+ mesh_component = actor.get_component_by_class(unreal.StaticMeshComponent)
222
+ if mesh_component:
223
+ mesh_component.set_static_mesh(cube_mesh)
224
+ # Make it movable so physics can be applied
225
+ mesh_component.set_editor_property('mobility', unreal.ComponentMobility.MOVABLE)
226
+
227
+ elif class_path == "CameraActor":
228
+ # Use modern EditorActorSubsystem API
229
+ actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
230
+ actor = actor_subsys.spawn_actor_from_class(
231
+ unreal.CameraActor,
232
+ location,
233
+ rotation
234
+ )
235
+ else:
236
+ # Try to get the class by name for other actors (e.g., PointLight)
237
+ actor_class = None
238
+ if hasattr(unreal, class_path):
239
+ actor_class = getattr(unreal, class_path)
240
+
241
+ if actor_class:
242
+ # Use modern EditorActorSubsystem API
243
+ actor_subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
244
+ actor = actor_subsys.spawn_actor_from_class(
245
+ actor_class,
246
+ location,
247
+ rotation
248
+ )
249
+
250
+ # Set the actor label and return result
251
+ if actor:
252
+ import time
253
+ timestamp = int(time.time() * 1000) % 10000
254
+ base_name = class_path.split('/')[-1] if '/' in class_path else class_path
255
+ actor_name = f"{base_name}_{timestamp}"
256
+ actor.set_actor_label(actor_name)
257
+ result["success"] = True
258
+ result["message"] = f"Spawned {actor_name} at ({location.x}, {location.y}, {location.z})"
259
+ result["actor_name"] = actor_name
260
+ else:
261
+ result["message"] = f"Failed to spawn actor from: {class_path}. Try using /Engine/BasicShapes/Cube or StaticMeshActor"
262
+
263
+ except Exception as e:
264
+ result["message"] = f"Error spawning actor: {e}"
265
+
266
+ print(f"RESULT:{json.dumps(result)}")
267
+ `.trim();
268
+ const response = await this.bridge.executePython(pythonCmd);
269
+ // Extract output from Python response
270
+ let outputStr = '';
271
+ if (typeof response === 'object' && response !== null) {
272
+ // Check if it has LogOutput (standard Python execution response)
273
+ if (response.LogOutput && Array.isArray(response.LogOutput)) {
274
+ // Concatenate all log outputs
275
+ outputStr = response.LogOutput
276
+ .map((log) => log.Output || '')
277
+ .join('');
278
+ }
279
+ else if ('result' in response) {
280
+ outputStr = String(response.result);
281
+ }
282
+ else if ('ReturnValue' in response && typeof response.ReturnValue === 'string') {
283
+ outputStr = response.ReturnValue;
284
+ }
285
+ else {
286
+ outputStr = JSON.stringify(response);
287
+ }
288
+ }
289
+ else {
290
+ outputStr = String(response || '');
291
+ }
292
+ // Parse the result from Python output
293
+ const resultMatch = outputStr.match(/RESULT:({.*})/);
294
+ if (resultMatch) {
295
+ try {
296
+ const result = JSON.parse(resultMatch[1]);
297
+ if (!result.success) {
298
+ throw new Error(result.message || 'Spawn failed');
299
+ }
300
+ return result;
301
+ }
302
+ catch {
303
+ // If we can't parse, check for common success patterns
304
+ if (outputStr.includes('Spawned')) {
305
+ return { success: true, message: outputStr };
306
+ }
307
+ throw new Error(`Failed to parse Python result: ${outputStr}`);
308
+ }
309
+ }
310
+ else {
311
+ // Check output for success/failure patterns
312
+ if (outputStr.includes('Failed') || outputStr.includes('Error') || outputStr.includes('not found')) {
313
+ throw new Error(outputStr || 'Spawn failed');
314
+ }
315
+ // Default fallback - but this shouldn't report success for failed operations
316
+ // Only report success if Python execution was successful and no error markers
317
+ if (response?.ReturnValue === true && !outputStr.includes('abstract')) {
318
+ return { success: true, message: `Actor spawned: ${className} at ${loc.x},${loc.y},${loc.z}` };
319
+ }
320
+ else {
321
+ throw new Error(`Failed to spawn ${className}: No valid result from Python`);
322
+ }
323
+ }
324
+ }
325
+ catch (err) {
326
+ throw new Error(`Failed to spawn actor via Python: ${err}`);
327
+ }
328
+ }
329
+ async spawnViaConsole(params) {
330
+ try {
331
+ // Check if editor is in play mode first
332
+ try {
333
+ const pieCheckPython = `
334
+ import unreal
335
+ les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
336
+ if les and les.is_in_play_in_editor():
337
+ print("PIE_ACTIVE")
338
+ else:
339
+ print("PIE_INACTIVE")
340
+ `.trim();
341
+ const pieCheckResult = await this.bridge.executePython(pieCheckPython);
342
+ const outputStr = typeof pieCheckResult === 'string' ? pieCheckResult : JSON.stringify(pieCheckResult);
343
+ if (outputStr.includes('PIE_ACTIVE')) {
344
+ throw new Error('Cannot spawn actors while in Play In Editor mode. Please stop PIE first.');
345
+ }
346
+ }
347
+ catch (pieErr) {
348
+ // If the error is about PIE, throw it
349
+ if (String(pieErr).includes('Play In Editor')) {
350
+ throw pieErr;
351
+ }
352
+ // Otherwise ignore and continue
353
+ }
354
+ // List of known abstract classes that cannot be spawned
355
+ const abstractClasses = ['PlaneReflectionCapture', 'ReflectionCapture', 'Actor'];
356
+ // Check if this is an abstract class
357
+ if (abstractClasses.includes(params.classPath)) {
358
+ throw new Error(`Cannot spawn ${params.classPath}: class is abstract and cannot be instantiated`);
359
+ }
360
+ // Get the console-friendly class name
361
+ const spawnClass = this.getConsoleClassName(params.classPath);
362
+ // Use summon command with location if provided
363
+ const loc = params.location || { x: 0, y: 0, z: 100 };
364
+ const command = `summon ${spawnClass} ${loc.x} ${loc.y} ${loc.z}`;
365
+ await this.bridge.httpCall('/remote/object/call', 'PUT', {
366
+ objectPath: '/Script/Engine.Default__KismetSystemLibrary',
367
+ functionName: 'ExecuteConsoleCommand',
368
+ parameters: {
369
+ WorldContextObject: null,
370
+ Command: command,
371
+ SpecificPlayer: null
372
+ },
373
+ generateTransaction: false
374
+ });
375
+ // Console commands don't reliably report success/failure
376
+ // We can't guarantee this actually worked, so indicate uncertainty
377
+ return {
378
+ success: true,
379
+ message: `Actor spawn attempted via console: ${spawnClass} at ${loc.x},${loc.y},${loc.z}`,
380
+ note: 'Console spawn result uncertain - verify in editor'
381
+ };
382
+ }
383
+ catch (err) {
384
+ throw new Error(`Failed to spawn actor: ${err}`);
385
+ }
386
+ }
387
+ resolveActorClass(classPath) {
388
+ // Map common names to full Unreal class paths
389
+ const classMap = {
390
+ 'PointLight': '/Script/Engine.PointLight',
391
+ 'DirectionalLight': '/Script/Engine.DirectionalLight',
392
+ 'SpotLight': '/Script/Engine.SpotLight',
393
+ 'RectLight': '/Script/Engine.RectLight',
394
+ 'SkyLight': '/Script/Engine.SkyLight',
395
+ 'StaticMeshActor': '/Script/Engine.StaticMeshActor',
396
+ 'PlayerStart': '/Script/Engine.PlayerStart',
397
+ 'Camera': '/Script/Engine.CameraActor',
398
+ 'CameraActor': '/Script/Engine.CameraActor',
399
+ 'Pawn': '/Script/Engine.DefaultPawn',
400
+ 'Character': '/Script/Engine.Character',
401
+ 'TriggerBox': '/Script/Engine.TriggerBox',
402
+ 'TriggerSphere': '/Script/Engine.TriggerSphere',
403
+ 'BlockingVolume': '/Script/Engine.BlockingVolume',
404
+ 'PostProcessVolume': '/Script/Engine.PostProcessVolume',
405
+ 'LightmassImportanceVolume': '/Script/Engine.LightmassImportanceVolume',
406
+ 'NavMeshBoundsVolume': '/Script/Engine.NavMeshBoundsVolume',
407
+ 'ExponentialHeightFog': '/Script/Engine.ExponentialHeightFog',
408
+ 'AtmosphericFog': '/Script/Engine.AtmosphericFog',
409
+ 'SphereReflectionCapture': '/Script/Engine.SphereReflectionCapture',
410
+ 'BoxReflectionCapture': '/Script/Engine.BoxReflectionCapture',
411
+ // PlaneReflectionCapture is abstract and cannot be spawned
412
+ 'DecalActor': '/Script/Engine.DecalActor'
413
+ };
414
+ // Check if it's a simple name that needs mapping
415
+ if (classMap[classPath]) {
416
+ return classMap[classPath];
417
+ }
418
+ // Check if it already looks like a full path
419
+ if (classPath.startsWith('/Script/') || classPath.startsWith('/Game/')) {
420
+ return classPath;
421
+ }
422
+ // Check for Blueprint paths
423
+ if (classPath.includes('Blueprint') || classPath.includes('BP_')) {
424
+ // Ensure it has the proper prefix
425
+ if (!classPath.startsWith('/Game/')) {
426
+ return '/Game/' + classPath;
427
+ }
428
+ return classPath;
429
+ }
430
+ // Default: assume it's an engine class
431
+ return '/Script/Engine.' + classPath;
432
+ }
433
+ getConsoleClassName(classPath) {
434
+ // Normalize class path for console 'summon'
435
+ const input = classPath;
436
+ // Engine classes: reduce '/Script/Engine.ClassName' to 'ClassName'
437
+ if (input.startsWith('/Script/Engine.')) {
438
+ return input.replace('/Script/Engine.', '');
439
+ }
440
+ // If it's already a simple class name (no path) and not a /Game asset, strip optional _C and return
441
+ if (!input.startsWith('/Game/') && !input.includes('/')) {
442
+ if (input.endsWith('_C'))
443
+ return input.slice(0, -2);
444
+ return input;
445
+ }
446
+ // Blueprint assets under /Game: ensure '/Game/Path/Asset.Asset_C'
447
+ if (input.startsWith('/Game/')) {
448
+ // Remove any existing ".Something" suffix to rebuild normalized class ref
449
+ const pathWithoutSuffix = input.split('.')[0];
450
+ const parts = pathWithoutSuffix.split('/');
451
+ const assetName = parts[parts.length - 1].replace(/_C$/, '');
452
+ const normalized = `${pathWithoutSuffix}.${assetName}_C`;
453
+ return normalized;
454
+ }
455
+ // Fallback: return input unchanged
456
+ return input;
457
+ }
458
+ }
459
+ //# sourceMappingURL=actors.js.map
@@ -0,0 +1,196 @@
1
+ import { UnrealBridge } from '../unreal-bridge.js';
2
+ export declare class AnimationTools {
3
+ private bridge;
4
+ constructor(bridge: UnrealBridge);
5
+ /**
6
+ * Create Animation Blueprint
7
+ */
8
+ createAnimationBlueprint(params: {
9
+ name: string;
10
+ skeletonPath: string;
11
+ savePath?: string;
12
+ }): Promise<{
13
+ success: boolean;
14
+ message: string;
15
+ error: string | undefined;
16
+ path?: undefined;
17
+ } | {
18
+ success: boolean;
19
+ message: string;
20
+ path: string;
21
+ error?: undefined;
22
+ } | {
23
+ success: boolean;
24
+ error: string;
25
+ message?: undefined;
26
+ path?: undefined;
27
+ }>;
28
+ /**
29
+ * Add State Machine to Animation Blueprint
30
+ */
31
+ addStateMachine(params: {
32
+ blueprintPath: string;
33
+ machineName: string;
34
+ states: Array<{
35
+ name: string;
36
+ animation?: string;
37
+ isEntry?: boolean;
38
+ isExit?: boolean;
39
+ }>;
40
+ transitions?: Array<{
41
+ sourceState: string;
42
+ targetState: string;
43
+ condition?: string;
44
+ }>;
45
+ }): Promise<{
46
+ success: boolean;
47
+ message: string;
48
+ error?: undefined;
49
+ } | {
50
+ success: boolean;
51
+ error: string;
52
+ message?: undefined;
53
+ }>;
54
+ /**
55
+ * Create Animation Montage
56
+ */
57
+ createMontage(params: {
58
+ name: string;
59
+ animationSequence: string;
60
+ savePath?: string;
61
+ sections?: Array<{
62
+ name: string;
63
+ startTime: number;
64
+ endTime: number;
65
+ }>;
66
+ notifies?: Array<{
67
+ name: string;
68
+ time: number;
69
+ }>;
70
+ }): Promise<{
71
+ success: boolean;
72
+ message: string;
73
+ path: string;
74
+ error?: undefined;
75
+ } | {
76
+ success: boolean;
77
+ error: string;
78
+ message?: undefined;
79
+ path?: undefined;
80
+ }>;
81
+ /**
82
+ * Create Blend Space
83
+ */
84
+ createBlendSpace(params: {
85
+ name: string;
86
+ skeletonPath: string;
87
+ savePath?: string;
88
+ dimensions: 1 | 2;
89
+ horizontalAxis?: {
90
+ name: string;
91
+ minValue: number;
92
+ maxValue: number;
93
+ };
94
+ verticalAxis?: {
95
+ name: string;
96
+ minValue: number;
97
+ maxValue: number;
98
+ };
99
+ samples?: Array<{
100
+ animation: string;
101
+ x: number;
102
+ y?: number;
103
+ }>;
104
+ }): Promise<{
105
+ success: boolean;
106
+ message: string;
107
+ path: string;
108
+ error?: undefined;
109
+ } | {
110
+ success: boolean;
111
+ error: string;
112
+ message?: undefined;
113
+ path?: undefined;
114
+ }>;
115
+ /**
116
+ * Setup Control Rig
117
+ */
118
+ setupControlRig(params: {
119
+ name: string;
120
+ skeletonPath: string;
121
+ savePath?: string;
122
+ controls?: Array<{
123
+ name: string;
124
+ type: 'Transform' | 'Float' | 'Bool' | 'Vector';
125
+ bone?: string;
126
+ defaultValue?: any;
127
+ }>;
128
+ }): Promise<{
129
+ success: boolean;
130
+ message: string;
131
+ error: string;
132
+ path?: undefined;
133
+ } | {
134
+ success: boolean;
135
+ message: string;
136
+ path: string;
137
+ error?: undefined;
138
+ } | {
139
+ success: boolean;
140
+ error: string;
141
+ message?: undefined;
142
+ path?: undefined;
143
+ }>;
144
+ /**
145
+ * Create Level Sequence (for cinematics)
146
+ */
147
+ createLevelSequence(params: {
148
+ name: string;
149
+ savePath?: string;
150
+ frameRate?: number;
151
+ duration?: number;
152
+ tracks?: Array<{
153
+ actorName: string;
154
+ trackType: 'Transform' | 'Animation' | 'Camera' | 'Event';
155
+ keyframes?: Array<{
156
+ time: number;
157
+ value: any;
158
+ }>;
159
+ }>;
160
+ }): Promise<{
161
+ success: boolean;
162
+ message: string;
163
+ path: string;
164
+ error?: undefined;
165
+ } | {
166
+ success: boolean;
167
+ error: string;
168
+ message?: undefined;
169
+ path?: undefined;
170
+ }>;
171
+ /**
172
+ * Play Animation on Actor
173
+ */
174
+ playAnimation(params: {
175
+ actorName: string;
176
+ animationType: 'Montage' | 'Sequence' | 'BlendSpace';
177
+ animationPath: string;
178
+ playRate?: number;
179
+ loop?: boolean;
180
+ blendInTime?: number;
181
+ blendOutTime?: number;
182
+ }): Promise<{
183
+ success: boolean;
184
+ message: any;
185
+ error?: undefined;
186
+ } | {
187
+ success: boolean;
188
+ error: any;
189
+ message?: undefined;
190
+ }>;
191
+ /**
192
+ * Helper function to execute console commands
193
+ */
194
+ private _executeCommand;
195
+ }
196
+ //# sourceMappingURL=animation.d.ts.map