flockbay 0.10.49 → 0.10.52

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 (74) hide show
  1. package/README.md +60 -60
  2. package/bin/flockbay-mcp.mjs +62 -62
  3. package/bin/flockbay.mjs +95 -95
  4. package/dist/{index-r3VTdgFI.cjs → index-IntvnrmZ.cjs} +71 -14
  5. package/dist/{index-D8AeVbvv.mjs → index-wkeSi0iC.mjs} +70 -13
  6. package/dist/index.cjs +2 -2
  7. package/dist/index.mjs +2 -2
  8. package/dist/lib.cjs +1 -1
  9. package/dist/lib.mjs +1 -1
  10. package/dist/{migratePlugin-CEvGPul8.mjs → migratePlugin-C71hAoc5.mjs} +12 -2
  11. package/dist/{migratePlugin-wFK-k3Wk.cjs → migratePlugin-D1XjjWFq.cjs} +12 -2
  12. package/dist/{runCodex-DuyuYqoB.cjs → runCodex-B7i_ICoQ.cjs} +34 -23
  13. package/dist/{runCodex-ZfUyhHF6.mjs → runCodex-BSx-hCu6.mjs} +34 -23
  14. package/dist/{runGemini-DBU1mMdp.mjs → runGemini-Dwyh7_WI.mjs} +2 -2
  15. package/dist/{runGemini-D7j5Y5TF.cjs → runGemini-gBKR3rdL.cjs} +2 -2
  16. package/dist/{types-DNr0xwSy.cjs → types-5z8UADAi.cjs} +224 -15
  17. package/dist/{types-BRJuZQj_.mjs → types-Bgzap7qR.mjs} +224 -16
  18. package/package.json +131 -131
  19. package/scripts/claude_local_launcher.cjs +72 -72
  20. package/scripts/claude_remote_launcher.cjs +15 -15
  21. package/scripts/claude_version_utils.cjs +445 -445
  22. package/scripts/ripgrep_launcher.cjs +32 -32
  23. package/scripts/run-with-root-env.mjs +37 -32
  24. package/scripts/session_hook_forwarder.cjs +49 -49
  25. package/scripts/test-codex-abort-history.mjs +77 -77
  26. package/scripts/unpack-tools.cjs +244 -244
  27. package/tools/licenses/difftastic-LICENSE +21 -21
  28. package/tools/licenses/ripgrep-LICENSE +3 -3
  29. package/tools/unreal-mcp/UPSTREAM_VERSION.md +8 -8
  30. package/tools/unreal-mcp/upstream/Docs/README.md +8 -8
  31. package/tools/unreal-mcp/upstream/Docs/Tools/README.md +6 -6
  32. package/tools/unreal-mcp/upstream/Docs/Tools/actor_tools.md +183 -183
  33. package/tools/unreal-mcp/upstream/Docs/Tools/blueprint_tools.md +268 -268
  34. package/tools/unreal-mcp/upstream/Docs/Tools/editor_tools.md +104 -104
  35. package/tools/unreal-mcp/upstream/Docs/Tools/node_tools.md +274 -274
  36. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Config/FilterPlugin.ini +8 -8
  37. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/FlockbayMCP.uplugin +36 -36
  38. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/FlockbayMCP.Build.cs +82 -82
  39. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp +1364 -1364
  40. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp +1310 -1310
  41. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPCommandSchema.cpp +756 -756
  42. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPCommonUtils.cpp +1177 -1177
  43. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPEditorCommands.cpp +2618 -2618
  44. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPProjectCommands.cpp +71 -71
  45. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/Commands/UnrealMCPUMGCommands.cpp +543 -543
  46. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/MCPServerRunnable.cpp +372 -372
  47. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/UnrealMCPBridge.cpp +457 -457
  48. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Private/UnrealMCPModule.cpp +21 -21
  49. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPBlueprintCommands.h +35 -35
  50. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPBlueprintNodeCommands.h +30 -30
  51. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPCommandSchema.h +41 -41
  52. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPCommonUtils.h +59 -59
  53. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPEditorCommands.h +61 -61
  54. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPProjectCommands.h +20 -20
  55. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/Commands/UnrealMCPUMGCommands.h +82 -82
  56. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/MCPServerRunnable.h +33 -33
  57. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/UnrealMCPBridge.h +64 -64
  58. package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/FlockbayMCP/Source/FlockbayMCP/Public/UnrealMCPModule.h +22 -22
  59. package/tools/unreal-mcp/upstream/Python/README.md +39 -39
  60. package/tools/unreal-mcp/upstream/Python/pyproject.toml +22 -22
  61. package/tools/unreal-mcp/upstream/Python/scripts/actors/test_cube.py +202 -202
  62. package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_blueprints_with_different_components.py +496 -496
  63. package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_cube_blueprint.py +193 -193
  64. package/tools/unreal-mcp/upstream/Python/scripts/node/test_component_reference.py +266 -266
  65. package/tools/unreal-mcp/upstream/Python/scripts/node/test_create_bird_blueprint_with_input_and_camera.py +617 -617
  66. package/tools/unreal-mcp/upstream/Python/scripts/node/test_input_mapping.py +365 -365
  67. package/tools/unreal-mcp/upstream/Python/scripts/node/test_physics_variables.py +389 -389
  68. package/tools/unreal-mcp/upstream/Python/tools/blueprint_tools.py +419 -419
  69. package/tools/unreal-mcp/upstream/Python/tools/editor_tools.py +369 -369
  70. package/tools/unreal-mcp/upstream/Python/tools/node_tools.py +429 -429
  71. package/tools/unreal-mcp/upstream/Python/tools/project_tools.py +63 -63
  72. package/tools/unreal-mcp/upstream/Python/tools/umg_tools.py +332 -332
  73. package/tools/unreal-mcp/upstream/Python/unreal_mcp_server.py +398 -398
  74. package/tools/unreal-mcp/upstream/Python/uv.lock +521 -521
@@ -1,497 +1,497 @@
1
- #!/usr/bin/env python
2
- """
3
- Comprehensive test suite for creating and spawning Blueprints with various component types in Unreal Engine via MCP.
4
-
5
- The script runs three main test scenarios:
6
-
7
- 1. Static Mesh Components Test:
8
- - Creates three different Blueprints with StaticMeshComponents:
9
- * BP_CubeMesh: A basic cube at [0, 0, 100]
10
- * BP_SphereMesh: A sphere at [100, 0, 100]
11
- * BP_CylinderMesh: A cylinder at [0, 100, 100], rotated 90° and stretched (scale: [0.75, 0.75, 2.0])
12
-
13
- 2. Collision Components Test:
14
- - Tests creation of physics collision shapes:
15
- * BP_BoxCollision: Box collision with 'BlockAll' profile
16
- * BP_SphereCollision: Sphere collision with 'OverlapAll' profile and 100 unit radius
17
-
18
- 3. Scene Component Hierarchy Test (Currently Disabled):
19
- - Demonstrates component parenting with:
20
- * Root SceneComponent
21
- * Child cube mesh offset 50 units right
22
- * Child sphere mesh offset 50 units left
23
-
24
- Each test follows a consistent workflow:
25
- 1. Create the Blueprint
26
- 2. Add and configure components
27
- 3. Set necessary properties (meshes, collision profiles, etc.)
28
- 4. Compile the Blueprint
29
- 5. Spawn an instance in the level
30
-
31
- The script provides detailed logging and error handling for each operation,
32
- making it useful both as a test suite and as an example of the MCP Blueprint API usage.
33
- """
34
-
35
- import sys
36
- import os
37
- import time
38
- import socket
39
- import json
40
- import logging
41
- from typing import Dict, Any, Optional, List
42
-
43
- # Add the parent directory to the path so we can import the server module
44
- sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
45
-
46
- # Set up logging
47
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
48
- logger = logging.getLogger("TestComponentCreation")
49
-
50
- def send_command(command: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
51
- """Send a command to the Unreal MCP server and get the response."""
52
- try:
53
- # Connect to Unreal MCP server (fresh connection for each command)
54
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
55
- sock.connect(("127.0.0.1", 55557))
56
-
57
- try:
58
- # Create command object
59
- command_obj = {
60
- "type": command,
61
- "params": params
62
- }
63
-
64
- # Convert to JSON and send
65
- command_json = json.dumps(command_obj)
66
- logger.info(f"Sending command: {command_json}")
67
- sock.sendall(command_json.encode('utf-8'))
68
-
69
- # Receive response
70
- chunks = []
71
- while True:
72
- chunk = sock.recv(4096)
73
- if not chunk:
74
- break
75
- chunks.append(chunk)
76
-
77
- # Try parsing to see if we have a complete response
78
- try:
79
- data = b''.join(chunks)
80
- json.loads(data.decode('utf-8'))
81
- # If we can parse it, we have the complete response
82
- break
83
- except json.JSONDecodeError:
84
- # Not a complete JSON object yet, continue receiving
85
- continue
86
-
87
- # Parse response
88
- data = b''.join(chunks)
89
- response = json.loads(data.decode('utf-8'))
90
- logger.info(f"Received response: {response}")
91
- return response
92
-
93
- finally:
94
- # Always close the socket
95
- sock.close()
96
-
97
- except Exception as e:
98
- logger.error(f"Error sending command: {e}")
99
- return None
100
-
101
- def create_blueprint(name: str, parent_class: str = "Actor") -> bool:
102
- """Create a blueprint with the given name and parent class."""
103
- bp_params = {
104
- "name": name,
105
- "parent_class": parent_class
106
- }
107
-
108
- response = send_command("create_blueprint", bp_params)
109
-
110
- # Check response
111
- if not response or response.get("status") != "success":
112
- logger.error(f"Failed to create blueprint: {response}")
113
- return False
114
-
115
- # Check if blueprint already existed
116
- if response.get("result", {}).get("already_exists"):
117
- logger.info(f"Blueprint '{name}' already exists, reusing it")
118
- else:
119
- logger.info(f"Blueprint '{name}' created successfully!")
120
-
121
- return True
122
-
123
- def add_component(
124
- blueprint_name: str,
125
- component_type: str,
126
- component_name: str,
127
- location: List[float] = [],
128
- rotation: List[float] = [],
129
- scale: List[float] = [],
130
- properties: Dict[str, Any] = {}
131
- ) -> bool:
132
- """Add a component to the specified blueprint."""
133
- component_params: Dict[str, Any] = {
134
- "blueprint_name": blueprint_name,
135
- "component_type": component_type,
136
- "component_name": component_name
137
- }
138
-
139
- # Add optional parameters if provided
140
- if location:
141
- component_params["location"] = location
142
- if rotation:
143
- component_params["rotation"] = rotation
144
- if scale:
145
- component_params["scale"] = scale
146
- if properties:
147
- component_params["component_properties"] = properties
148
-
149
- response = send_command("add_component_to_blueprint", component_params)
150
-
151
- # Check response
152
- if not response or response.get("status") != "success":
153
- logger.error(f"Failed to add {component_type} component '{component_name}': {response}")
154
- return False
155
-
156
- logger.info(f"Component '{component_name}' of type '{component_type}' added successfully!")
157
- return True
158
-
159
- def set_static_mesh(
160
- blueprint_name: str,
161
- component_name: str,
162
- mesh_type: str
163
- ) -> bool:
164
- """Set the static mesh for a component."""
165
- # Convert simple shape name to full asset path if needed
166
- if mesh_type in ["Cube", "Sphere", "Cylinder", "Cone", "Plane"]:
167
- static_mesh = f"/Engine/BasicShapes/{mesh_type}.{mesh_type}"
168
- else:
169
- # Assume it's already a full path
170
- static_mesh = mesh_type
171
-
172
- params = {
173
- "blueprint_name": blueprint_name,
174
- "component_name": component_name,
175
- "static_mesh": static_mesh
176
- }
177
-
178
- response = send_command("set_static_mesh_properties", params)
179
-
180
- # Check response
181
- if not response or response.get("status") != "success":
182
- logger.error(f"Failed to set static mesh '{mesh_type}' for component '{component_name}': {response}")
183
- return False
184
-
185
- logger.info(f"Static mesh '{mesh_type}' set for component '{component_name}' successfully!")
186
- return True
187
-
188
- def compile_blueprint(blueprint_name: str) -> bool:
189
- """Compile the specified blueprint."""
190
- compile_params = {
191
- "blueprint_name": blueprint_name
192
- }
193
-
194
- response = send_command("compile_blueprint", compile_params)
195
-
196
- # Check response
197
- if not response or response.get("status") != "success":
198
- logger.error(f"Failed to compile blueprint '{blueprint_name}': {response}")
199
- return False
200
-
201
- logger.info(f"Blueprint '{blueprint_name}' compiled successfully!")
202
- return True
203
-
204
- def spawn_blueprint_actor(blueprint_name: str, actor_name: str, location: List[float] = []) -> bool:
205
- """Spawn an actor from the specified blueprint."""
206
- spawn_params: Dict[str, Any] = {
207
- "blueprint_name": blueprint_name,
208
- "actor_name": actor_name
209
- }
210
-
211
- # Add location if provided
212
- if location:
213
- spawn_params["location"] = location
214
- else:
215
- spawn_params["location"] = [0.0, 0.0, 100.0] # Default 100 units up
216
-
217
- response = send_command("spawn_blueprint_actor", spawn_params)
218
-
219
- # Check response
220
- if not response or response.get("status") != "success":
221
- logger.error(f"Failed to spawn actor from blueprint '{blueprint_name}': {response}")
222
- return False
223
-
224
- logger.info(f"Actor '{actor_name}' spawned from blueprint '{blueprint_name}' successfully!")
225
- return True
226
-
227
- def test_static_mesh_components():
228
- """Test creating blueprints with different static mesh components."""
229
- logger.info("\n=== Testing Static Mesh Components ===\n")
230
-
231
- # Test cube mesh
232
- logger.info("Testing cube mesh component...")
233
- bp_cube_name = "BP_CubeMesh"
234
- if not create_blueprint(bp_cube_name):
235
- return False
236
-
237
- if not add_component(
238
- blueprint_name=bp_cube_name,
239
- component_type="StaticMeshComponent",
240
- component_name="CubeMeshComponent",
241
- location=[0.0, 0.0, 0.0],
242
- scale=[1.0, 1.0, 1.0],
243
- properties={"bVisible": True}
244
- ):
245
- return False
246
-
247
- if not set_static_mesh(
248
- blueprint_name=bp_cube_name,
249
- component_name="CubeMeshComponent",
250
- mesh_type="Cube"
251
- ):
252
- return False
253
-
254
- if not compile_blueprint(bp_cube_name):
255
- return False
256
-
257
- if not spawn_blueprint_actor(bp_cube_name, "CubeMeshActor", [0.0, 0.0, 100.0]):
258
- return False
259
-
260
- # Test sphere mesh
261
- logger.info("Testing sphere mesh component...")
262
- bp_sphere_name = "BP_SphereMesh"
263
- if not create_blueprint(bp_sphere_name):
264
- return False
265
-
266
- if not add_component(
267
- blueprint_name=bp_sphere_name,
268
- component_type="StaticMeshComponent",
269
- component_name="SphereMeshComponent",
270
- location=[0.0, 0.0, 0.0],
271
- scale=[1.0, 1.0, 1.0],
272
- properties={"bVisible": True}
273
- ):
274
- return False
275
-
276
- if not set_static_mesh(
277
- blueprint_name=bp_sphere_name,
278
- component_name="SphereMeshComponent",
279
- mesh_type="Sphere"
280
- ):
281
- return False
282
-
283
- if not compile_blueprint(bp_sphere_name):
284
- return False
285
-
286
- if not spawn_blueprint_actor(bp_sphere_name, "SphereMeshActor", [100.0, 0.0, 100.0]):
287
- return False
288
-
289
- # Test cylinder mesh
290
- logger.info("Testing cylinder mesh component...")
291
- bp_cylinder_name = "BP_CylinderMesh"
292
- if not create_blueprint(bp_cylinder_name):
293
- return False
294
-
295
- if not add_component(
296
- blueprint_name=bp_cylinder_name,
297
- component_type="StaticMeshComponent",
298
- component_name="CylinderMeshComponent",
299
- location=[0.0, 0.0, 0.0],
300
- rotation=[0.0, 0.0, 90.0], # Rotated 90 degrees
301
- scale=[0.75, 0.75, 2.0], # Stretched in Z
302
- properties={"bVisible": True}
303
- ):
304
- return False
305
-
306
- if not set_static_mesh(
307
- blueprint_name=bp_cylinder_name,
308
- component_name="CylinderMeshComponent",
309
- mesh_type="Cylinder"
310
- ):
311
- return False
312
-
313
- if not compile_blueprint(bp_cylinder_name):
314
- return False
315
-
316
- if not spawn_blueprint_actor(bp_cylinder_name, "CylinderMeshActor", [0.0, 100.0, 100.0]):
317
- return False
318
-
319
- return True
320
-
321
- def test_collision_components():
322
- """Test creating blueprints with different collision components."""
323
- logger.info("\n=== Testing Collision Components ===\n")
324
-
325
- # Test box collision component
326
- logger.info("Testing box collision component...")
327
- bp_box_name = "BP_BoxCollision"
328
- if not create_blueprint(bp_box_name):
329
- return False
330
-
331
- if not add_component(
332
- blueprint_name=bp_box_name,
333
- component_type="BoxComponent",
334
- component_name="BoxCollisionComponent",
335
- location=[0.0, 0.0, 0.0],
336
- scale=[1.0, 1.0, 1.0],
337
- properties={
338
- "bVisible": True,
339
- "CollisionProfileName": "BlockAll"
340
- }
341
- ):
342
- return False
343
-
344
- if not compile_blueprint(bp_box_name):
345
- return False
346
-
347
- if not spawn_blueprint_actor(bp_box_name, "BoxCollisionActor", [0.0, 0.0, 100.0]):
348
- return False
349
-
350
- # Test sphere collision component
351
- logger.info("Testing sphere collision component...")
352
- bp_sphere_name = "BP_SphereCollision"
353
- if not create_blueprint(bp_sphere_name):
354
- return False
355
-
356
- if not add_component(
357
- blueprint_name=bp_sphere_name,
358
- component_type="SphereComponent",
359
- component_name="SphereCollisionComponent",
360
- location=[0.0, 0.0, 0.0],
361
- scale=[1.0, 1.0, 1.0],
362
- properties={
363
- "bVisible": True,
364
- "CollisionProfileName": "OverlapAll",
365
- "SphereRadius": 100.0 # Custom radius
366
- }
367
- ):
368
- return False
369
-
370
- if not compile_blueprint(bp_sphere_name):
371
- return False
372
-
373
- if not spawn_blueprint_actor(bp_sphere_name, "SphereCollisionActor", [100.0, 0.0, 100.0]):
374
- return False
375
-
376
- return True
377
-
378
- def test_scene_component_hierarchy():
379
- """Test creating a blueprint with a hierarchy of scene components."""
380
- logger.info("\n=== Testing Scene Component Hierarchy ===\n")
381
-
382
- # Create a blueprint for the hierarchy test
383
- bp_name = "BP_ComponentHierarchy"
384
- if not create_blueprint(bp_name):
385
- return False
386
-
387
- # Add a root scene component
388
- logger.info("Adding root SceneComponent...")
389
- if not add_component(
390
- blueprint_name=bp_name,
391
- component_type="SceneComponent",
392
- component_name="RootSceneComponent",
393
- location=[0.0, 0.0, 0.0]
394
- ):
395
- return False
396
-
397
- # Compile the blueprint after adding the root component
398
- logger.info("Compiling blueprint after adding root component...")
399
- if not compile_blueprint(bp_name):
400
- return False
401
-
402
- # Add cube mesh as first child
403
- logger.info("Adding cube mesh as first child...")
404
- cube_component_name = "ChildCubeMeshComponent"
405
- if not add_component(
406
- blueprint_name=bp_name,
407
- component_type="StaticMeshComponent",
408
- component_name=cube_component_name,
409
- location=[50.0, 0.0, 0.0]
410
- ):
411
- return False
412
-
413
- # Compile the blueprint after adding the first child
414
- logger.info("Compiling blueprint after adding first child...")
415
- if not compile_blueprint(bp_name):
416
- return False
417
-
418
- # Set mesh type for cube
419
- if not set_static_mesh(
420
- blueprint_name=bp_name,
421
- component_name=cube_component_name,
422
- mesh_type="Cube"
423
- ):
424
- return False
425
-
426
- # Compile the blueprint after setting the mesh
427
- logger.info("Compiling blueprint after setting first mesh...")
428
- if not compile_blueprint(bp_name):
429
- return False
430
-
431
- # Add sphere mesh as second child
432
- logger.info("Adding sphere mesh as second child...")
433
- sphere_component_name = "ChildSphereMeshComponent"
434
- if not add_component(
435
- blueprint_name=bp_name,
436
- component_type="StaticMeshComponent",
437
- component_name=sphere_component_name,
438
- location=[-50.0, 0.0, 0.0]
439
- ):
440
- return False
441
-
442
- # Compile the blueprint after adding the second child
443
- logger.info("Compiling blueprint after adding second child...")
444
- if not compile_blueprint(bp_name):
445
- return False
446
-
447
- # Set mesh type for sphere
448
- if not set_static_mesh(
449
- blueprint_name=bp_name,
450
- component_name=sphere_component_name,
451
- mesh_type="Sphere"
452
- ):
453
- return False
454
-
455
- # Final compilation
456
- logger.info("Final blueprint compilation...")
457
- if not compile_blueprint(bp_name):
458
- return False
459
-
460
- # Spawn an actor from the blueprint
461
- logger.info("Spawning hierarchy actor...")
462
- if not spawn_blueprint_actor(bp_name, "HierarchyTestActor", [0.0, 0.0, 100.0]):
463
- return False
464
-
465
- return True
466
-
467
- def main():
468
- """Main function to test different component creation scenarios."""
469
- try:
470
- # Test different component scenarios
471
- tests = [
472
- test_static_mesh_components,
473
- test_collision_components,
474
- # test_scene_component_hierarchy # TODO: Not working
475
- ]
476
-
477
- success_count = 0
478
-
479
- for test_func in tests:
480
- if test_func():
481
- success_count += 1
482
- logger.info(f"{test_func.__name__} completed successfully!")
483
- else:
484
- logger.error(f"{test_func.__name__} failed!")
485
-
486
- # Add a delay between tests
487
- time.sleep(2)
488
-
489
- # Report overall results
490
- logger.info(f"\n=== Test Results: {success_count}/{len(tests)} tests passed ===\n")
491
-
492
- except Exception as e:
493
- logger.error(f"Error during testing: {e}")
494
- sys.exit(1)
495
-
496
- if __name__ == "__main__":
1
+ #!/usr/bin/env python
2
+ """
3
+ Comprehensive test suite for creating and spawning Blueprints with various component types in Unreal Engine via MCP.
4
+
5
+ The script runs three main test scenarios:
6
+
7
+ 1. Static Mesh Components Test:
8
+ - Creates three different Blueprints with StaticMeshComponents:
9
+ * BP_CubeMesh: A basic cube at [0, 0, 100]
10
+ * BP_SphereMesh: A sphere at [100, 0, 100]
11
+ * BP_CylinderMesh: A cylinder at [0, 100, 100], rotated 90° and stretched (scale: [0.75, 0.75, 2.0])
12
+
13
+ 2. Collision Components Test:
14
+ - Tests creation of physics collision shapes:
15
+ * BP_BoxCollision: Box collision with 'BlockAll' profile
16
+ * BP_SphereCollision: Sphere collision with 'OverlapAll' profile and 100 unit radius
17
+
18
+ 3. Scene Component Hierarchy Test (Currently Disabled):
19
+ - Demonstrates component parenting with:
20
+ * Root SceneComponent
21
+ * Child cube mesh offset 50 units right
22
+ * Child sphere mesh offset 50 units left
23
+
24
+ Each test follows a consistent workflow:
25
+ 1. Create the Blueprint
26
+ 2. Add and configure components
27
+ 3. Set necessary properties (meshes, collision profiles, etc.)
28
+ 4. Compile the Blueprint
29
+ 5. Spawn an instance in the level
30
+
31
+ The script provides detailed logging and error handling for each operation,
32
+ making it useful both as a test suite and as an example of the MCP Blueprint API usage.
33
+ """
34
+
35
+ import sys
36
+ import os
37
+ import time
38
+ import socket
39
+ import json
40
+ import logging
41
+ from typing import Dict, Any, Optional, List
42
+
43
+ # Add the parent directory to the path so we can import the server module
44
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
45
+
46
+ # Set up logging
47
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
48
+ logger = logging.getLogger("TestComponentCreation")
49
+
50
+ def send_command(command: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
51
+ """Send a command to the Unreal MCP server and get the response."""
52
+ try:
53
+ # Connect to Unreal MCP server (fresh connection for each command)
54
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
55
+ sock.connect(("127.0.0.1", 55557))
56
+
57
+ try:
58
+ # Create command object
59
+ command_obj = {
60
+ "type": command,
61
+ "params": params
62
+ }
63
+
64
+ # Convert to JSON and send
65
+ command_json = json.dumps(command_obj)
66
+ logger.info(f"Sending command: {command_json}")
67
+ sock.sendall(command_json.encode('utf-8'))
68
+
69
+ # Receive response
70
+ chunks = []
71
+ while True:
72
+ chunk = sock.recv(4096)
73
+ if not chunk:
74
+ break
75
+ chunks.append(chunk)
76
+
77
+ # Try parsing to see if we have a complete response
78
+ try:
79
+ data = b''.join(chunks)
80
+ json.loads(data.decode('utf-8'))
81
+ # If we can parse it, we have the complete response
82
+ break
83
+ except json.JSONDecodeError:
84
+ # Not a complete JSON object yet, continue receiving
85
+ continue
86
+
87
+ # Parse response
88
+ data = b''.join(chunks)
89
+ response = json.loads(data.decode('utf-8'))
90
+ logger.info(f"Received response: {response}")
91
+ return response
92
+
93
+ finally:
94
+ # Always close the socket
95
+ sock.close()
96
+
97
+ except Exception as e:
98
+ logger.error(f"Error sending command: {e}")
99
+ return None
100
+
101
+ def create_blueprint(name: str, parent_class: str = "Actor") -> bool:
102
+ """Create a blueprint with the given name and parent class."""
103
+ bp_params = {
104
+ "name": name,
105
+ "parent_class": parent_class
106
+ }
107
+
108
+ response = send_command("create_blueprint", bp_params)
109
+
110
+ # Check response
111
+ if not response or response.get("status") != "success":
112
+ logger.error(f"Failed to create blueprint: {response}")
113
+ return False
114
+
115
+ # Check if blueprint already existed
116
+ if response.get("result", {}).get("already_exists"):
117
+ logger.info(f"Blueprint '{name}' already exists, reusing it")
118
+ else:
119
+ logger.info(f"Blueprint '{name}' created successfully!")
120
+
121
+ return True
122
+
123
+ def add_component(
124
+ blueprint_name: str,
125
+ component_type: str,
126
+ component_name: str,
127
+ location: List[float] = [],
128
+ rotation: List[float] = [],
129
+ scale: List[float] = [],
130
+ properties: Dict[str, Any] = {}
131
+ ) -> bool:
132
+ """Add a component to the specified blueprint."""
133
+ component_params: Dict[str, Any] = {
134
+ "blueprint_name": blueprint_name,
135
+ "component_type": component_type,
136
+ "component_name": component_name
137
+ }
138
+
139
+ # Add optional parameters if provided
140
+ if location:
141
+ component_params["location"] = location
142
+ if rotation:
143
+ component_params["rotation"] = rotation
144
+ if scale:
145
+ component_params["scale"] = scale
146
+ if properties:
147
+ component_params["component_properties"] = properties
148
+
149
+ response = send_command("add_component_to_blueprint", component_params)
150
+
151
+ # Check response
152
+ if not response or response.get("status") != "success":
153
+ logger.error(f"Failed to add {component_type} component '{component_name}': {response}")
154
+ return False
155
+
156
+ logger.info(f"Component '{component_name}' of type '{component_type}' added successfully!")
157
+ return True
158
+
159
+ def set_static_mesh(
160
+ blueprint_name: str,
161
+ component_name: str,
162
+ mesh_type: str
163
+ ) -> bool:
164
+ """Set the static mesh for a component."""
165
+ # Convert simple shape name to full asset path if needed
166
+ if mesh_type in ["Cube", "Sphere", "Cylinder", "Cone", "Plane"]:
167
+ static_mesh = f"/Engine/BasicShapes/{mesh_type}.{mesh_type}"
168
+ else:
169
+ # Assume it's already a full path
170
+ static_mesh = mesh_type
171
+
172
+ params = {
173
+ "blueprint_name": blueprint_name,
174
+ "component_name": component_name,
175
+ "static_mesh": static_mesh
176
+ }
177
+
178
+ response = send_command("set_static_mesh_properties", params)
179
+
180
+ # Check response
181
+ if not response or response.get("status") != "success":
182
+ logger.error(f"Failed to set static mesh '{mesh_type}' for component '{component_name}': {response}")
183
+ return False
184
+
185
+ logger.info(f"Static mesh '{mesh_type}' set for component '{component_name}' successfully!")
186
+ return True
187
+
188
+ def compile_blueprint(blueprint_name: str) -> bool:
189
+ """Compile the specified blueprint."""
190
+ compile_params = {
191
+ "blueprint_name": blueprint_name
192
+ }
193
+
194
+ response = send_command("compile_blueprint", compile_params)
195
+
196
+ # Check response
197
+ if not response or response.get("status") != "success":
198
+ logger.error(f"Failed to compile blueprint '{blueprint_name}': {response}")
199
+ return False
200
+
201
+ logger.info(f"Blueprint '{blueprint_name}' compiled successfully!")
202
+ return True
203
+
204
+ def spawn_blueprint_actor(blueprint_name: str, actor_name: str, location: List[float] = []) -> bool:
205
+ """Spawn an actor from the specified blueprint."""
206
+ spawn_params: Dict[str, Any] = {
207
+ "blueprint_name": blueprint_name,
208
+ "actor_name": actor_name
209
+ }
210
+
211
+ # Add location if provided
212
+ if location:
213
+ spawn_params["location"] = location
214
+ else:
215
+ spawn_params["location"] = [0.0, 0.0, 100.0] # Default 100 units up
216
+
217
+ response = send_command("spawn_blueprint_actor", spawn_params)
218
+
219
+ # Check response
220
+ if not response or response.get("status") != "success":
221
+ logger.error(f"Failed to spawn actor from blueprint '{blueprint_name}': {response}")
222
+ return False
223
+
224
+ logger.info(f"Actor '{actor_name}' spawned from blueprint '{blueprint_name}' successfully!")
225
+ return True
226
+
227
+ def test_static_mesh_components():
228
+ """Test creating blueprints with different static mesh components."""
229
+ logger.info("\n=== Testing Static Mesh Components ===\n")
230
+
231
+ # Test cube mesh
232
+ logger.info("Testing cube mesh component...")
233
+ bp_cube_name = "BP_CubeMesh"
234
+ if not create_blueprint(bp_cube_name):
235
+ return False
236
+
237
+ if not add_component(
238
+ blueprint_name=bp_cube_name,
239
+ component_type="StaticMeshComponent",
240
+ component_name="CubeMeshComponent",
241
+ location=[0.0, 0.0, 0.0],
242
+ scale=[1.0, 1.0, 1.0],
243
+ properties={"bVisible": True}
244
+ ):
245
+ return False
246
+
247
+ if not set_static_mesh(
248
+ blueprint_name=bp_cube_name,
249
+ component_name="CubeMeshComponent",
250
+ mesh_type="Cube"
251
+ ):
252
+ return False
253
+
254
+ if not compile_blueprint(bp_cube_name):
255
+ return False
256
+
257
+ if not spawn_blueprint_actor(bp_cube_name, "CubeMeshActor", [0.0, 0.0, 100.0]):
258
+ return False
259
+
260
+ # Test sphere mesh
261
+ logger.info("Testing sphere mesh component...")
262
+ bp_sphere_name = "BP_SphereMesh"
263
+ if not create_blueprint(bp_sphere_name):
264
+ return False
265
+
266
+ if not add_component(
267
+ blueprint_name=bp_sphere_name,
268
+ component_type="StaticMeshComponent",
269
+ component_name="SphereMeshComponent",
270
+ location=[0.0, 0.0, 0.0],
271
+ scale=[1.0, 1.0, 1.0],
272
+ properties={"bVisible": True}
273
+ ):
274
+ return False
275
+
276
+ if not set_static_mesh(
277
+ blueprint_name=bp_sphere_name,
278
+ component_name="SphereMeshComponent",
279
+ mesh_type="Sphere"
280
+ ):
281
+ return False
282
+
283
+ if not compile_blueprint(bp_sphere_name):
284
+ return False
285
+
286
+ if not spawn_blueprint_actor(bp_sphere_name, "SphereMeshActor", [100.0, 0.0, 100.0]):
287
+ return False
288
+
289
+ # Test cylinder mesh
290
+ logger.info("Testing cylinder mesh component...")
291
+ bp_cylinder_name = "BP_CylinderMesh"
292
+ if not create_blueprint(bp_cylinder_name):
293
+ return False
294
+
295
+ if not add_component(
296
+ blueprint_name=bp_cylinder_name,
297
+ component_type="StaticMeshComponent",
298
+ component_name="CylinderMeshComponent",
299
+ location=[0.0, 0.0, 0.0],
300
+ rotation=[0.0, 0.0, 90.0], # Rotated 90 degrees
301
+ scale=[0.75, 0.75, 2.0], # Stretched in Z
302
+ properties={"bVisible": True}
303
+ ):
304
+ return False
305
+
306
+ if not set_static_mesh(
307
+ blueprint_name=bp_cylinder_name,
308
+ component_name="CylinderMeshComponent",
309
+ mesh_type="Cylinder"
310
+ ):
311
+ return False
312
+
313
+ if not compile_blueprint(bp_cylinder_name):
314
+ return False
315
+
316
+ if not spawn_blueprint_actor(bp_cylinder_name, "CylinderMeshActor", [0.0, 100.0, 100.0]):
317
+ return False
318
+
319
+ return True
320
+
321
+ def test_collision_components():
322
+ """Test creating blueprints with different collision components."""
323
+ logger.info("\n=== Testing Collision Components ===\n")
324
+
325
+ # Test box collision component
326
+ logger.info("Testing box collision component...")
327
+ bp_box_name = "BP_BoxCollision"
328
+ if not create_blueprint(bp_box_name):
329
+ return False
330
+
331
+ if not add_component(
332
+ blueprint_name=bp_box_name,
333
+ component_type="BoxComponent",
334
+ component_name="BoxCollisionComponent",
335
+ location=[0.0, 0.0, 0.0],
336
+ scale=[1.0, 1.0, 1.0],
337
+ properties={
338
+ "bVisible": True,
339
+ "CollisionProfileName": "BlockAll"
340
+ }
341
+ ):
342
+ return False
343
+
344
+ if not compile_blueprint(bp_box_name):
345
+ return False
346
+
347
+ if not spawn_blueprint_actor(bp_box_name, "BoxCollisionActor", [0.0, 0.0, 100.0]):
348
+ return False
349
+
350
+ # Test sphere collision component
351
+ logger.info("Testing sphere collision component...")
352
+ bp_sphere_name = "BP_SphereCollision"
353
+ if not create_blueprint(bp_sphere_name):
354
+ return False
355
+
356
+ if not add_component(
357
+ blueprint_name=bp_sphere_name,
358
+ component_type="SphereComponent",
359
+ component_name="SphereCollisionComponent",
360
+ location=[0.0, 0.0, 0.0],
361
+ scale=[1.0, 1.0, 1.0],
362
+ properties={
363
+ "bVisible": True,
364
+ "CollisionProfileName": "OverlapAll",
365
+ "SphereRadius": 100.0 # Custom radius
366
+ }
367
+ ):
368
+ return False
369
+
370
+ if not compile_blueprint(bp_sphere_name):
371
+ return False
372
+
373
+ if not spawn_blueprint_actor(bp_sphere_name, "SphereCollisionActor", [100.0, 0.0, 100.0]):
374
+ return False
375
+
376
+ return True
377
+
378
+ def test_scene_component_hierarchy():
379
+ """Test creating a blueprint with a hierarchy of scene components."""
380
+ logger.info("\n=== Testing Scene Component Hierarchy ===\n")
381
+
382
+ # Create a blueprint for the hierarchy test
383
+ bp_name = "BP_ComponentHierarchy"
384
+ if not create_blueprint(bp_name):
385
+ return False
386
+
387
+ # Add a root scene component
388
+ logger.info("Adding root SceneComponent...")
389
+ if not add_component(
390
+ blueprint_name=bp_name,
391
+ component_type="SceneComponent",
392
+ component_name="RootSceneComponent",
393
+ location=[0.0, 0.0, 0.0]
394
+ ):
395
+ return False
396
+
397
+ # Compile the blueprint after adding the root component
398
+ logger.info("Compiling blueprint after adding root component...")
399
+ if not compile_blueprint(bp_name):
400
+ return False
401
+
402
+ # Add cube mesh as first child
403
+ logger.info("Adding cube mesh as first child...")
404
+ cube_component_name = "ChildCubeMeshComponent"
405
+ if not add_component(
406
+ blueprint_name=bp_name,
407
+ component_type="StaticMeshComponent",
408
+ component_name=cube_component_name,
409
+ location=[50.0, 0.0, 0.0]
410
+ ):
411
+ return False
412
+
413
+ # Compile the blueprint after adding the first child
414
+ logger.info("Compiling blueprint after adding first child...")
415
+ if not compile_blueprint(bp_name):
416
+ return False
417
+
418
+ # Set mesh type for cube
419
+ if not set_static_mesh(
420
+ blueprint_name=bp_name,
421
+ component_name=cube_component_name,
422
+ mesh_type="Cube"
423
+ ):
424
+ return False
425
+
426
+ # Compile the blueprint after setting the mesh
427
+ logger.info("Compiling blueprint after setting first mesh...")
428
+ if not compile_blueprint(bp_name):
429
+ return False
430
+
431
+ # Add sphere mesh as second child
432
+ logger.info("Adding sphere mesh as second child...")
433
+ sphere_component_name = "ChildSphereMeshComponent"
434
+ if not add_component(
435
+ blueprint_name=bp_name,
436
+ component_type="StaticMeshComponent",
437
+ component_name=sphere_component_name,
438
+ location=[-50.0, 0.0, 0.0]
439
+ ):
440
+ return False
441
+
442
+ # Compile the blueprint after adding the second child
443
+ logger.info("Compiling blueprint after adding second child...")
444
+ if not compile_blueprint(bp_name):
445
+ return False
446
+
447
+ # Set mesh type for sphere
448
+ if not set_static_mesh(
449
+ blueprint_name=bp_name,
450
+ component_name=sphere_component_name,
451
+ mesh_type="Sphere"
452
+ ):
453
+ return False
454
+
455
+ # Final compilation
456
+ logger.info("Final blueprint compilation...")
457
+ if not compile_blueprint(bp_name):
458
+ return False
459
+
460
+ # Spawn an actor from the blueprint
461
+ logger.info("Spawning hierarchy actor...")
462
+ if not spawn_blueprint_actor(bp_name, "HierarchyTestActor", [0.0, 0.0, 100.0]):
463
+ return False
464
+
465
+ return True
466
+
467
+ def main():
468
+ """Main function to test different component creation scenarios."""
469
+ try:
470
+ # Test different component scenarios
471
+ tests = [
472
+ test_static_mesh_components,
473
+ test_collision_components,
474
+ # test_scene_component_hierarchy # TODO: Not working
475
+ ]
476
+
477
+ success_count = 0
478
+
479
+ for test_func in tests:
480
+ if test_func():
481
+ success_count += 1
482
+ logger.info(f"{test_func.__name__} completed successfully!")
483
+ else:
484
+ logger.error(f"{test_func.__name__} failed!")
485
+
486
+ # Add a delay between tests
487
+ time.sleep(2)
488
+
489
+ # Report overall results
490
+ logger.info(f"\n=== Test Results: {success_count}/{len(tests)} tests passed ===\n")
491
+
492
+ except Exception as e:
493
+ logger.error(f"Error during testing: {e}")
494
+ sys.exit(1)
495
+
496
+ if __name__ == "__main__":
497
497
  main()