flockbay 0.10.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/bin/flockbay-mcp.mjs +56 -0
- package/bin/flockbay.mjs +78 -0
- package/dist/codex/flockbayMcpStdioBridge.cjs +383 -0
- package/dist/codex/flockbayMcpStdioBridge.d.cts +2 -0
- package/dist/codex/flockbayMcpStdioBridge.d.mts +2 -0
- package/dist/codex/flockbayMcpStdioBridge.mjs +381 -0
- package/dist/flockbayScreenshotGate-DJX3Is5d.mjs +136 -0
- package/dist/flockbayScreenshotGate-DkxU24cR.cjs +138 -0
- package/dist/index--o4BPz5o.cjs +10311 -0
- package/dist/index-CUp3juDS.mjs +10268 -0
- package/dist/index.cjs +43 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +40 -0
- package/dist/lib.cjs +33 -0
- package/dist/lib.d.cts +957 -0
- package/dist/lib.d.mts +957 -0
- package/dist/lib.mjs +23 -0
- package/dist/runCodex-D3eT-TvB.cjs +3449 -0
- package/dist/runCodex-o6PCbHQ7.mjs +3446 -0
- package/dist/runGemini-Bt0oEj_g.mjs +3183 -0
- package/dist/runGemini-CBxZp6I7.cjs +3185 -0
- package/dist/types-C-jnUdn_.cjs +4498 -0
- package/dist/types-DGd6ea2Z.mjs +4450 -0
- package/kits/kit.open_world/kit.json +59 -0
- package/package.json +130 -0
- package/scripts/claude_local_launcher.cjs +73 -0
- package/scripts/claude_remote_launcher.cjs +16 -0
- package/scripts/claude_version_utils.cjs +391 -0
- package/scripts/ripgrep_launcher.cjs +33 -0
- package/scripts/session_hook_forwarder.cjs +49 -0
- package/scripts/test-codex-abort-history.mjs +77 -0
- package/scripts/unpack-tools.cjs +222 -0
- package/tools/licenses/difftastic-LICENSE +21 -0
- package/tools/licenses/ripgrep-LICENSE +3 -0
- package/tools/unreal-mcp/UPSTREAM_VERSION.md +8 -0
- package/tools/unreal-mcp/upstream/Docs/README.md +8 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/README.md +7 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/actor_tools.md +184 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/blueprint_tools.md +268 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/editor_tools.md +104 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/node_tools.md +274 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Config/FilterPlugin.ini +8 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp +1160 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp +924 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp +709 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +896 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPProjectCommands.cpp +72 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPUMGCommands.cpp +544 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/MCPServerRunnable.cpp +321 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/UnrealMCPBridge.cpp +419 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/UnrealMCPModule.cpp +21 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintCommands.h +34 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintNodeCommands.h +27 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPCommonUtils.h +59 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPEditorCommands.h +40 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPProjectCommands.h +20 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPUMGCommands.h +82 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/MCPServerRunnable.h +34 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/UnrealMCPBridge.h +64 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/UnrealMCPModule.h +22 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +78 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/UnrealMCP.uplugin +36 -0
- package/tools/unreal-mcp/upstream/Python/README.md +40 -0
- package/tools/unreal-mcp/upstream/Python/pyproject.toml +22 -0
- package/tools/unreal-mcp/upstream/Python/scripts/actors/test_cube.py +203 -0
- package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_blueprints_with_different_components.py +497 -0
- package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_cube_blueprint.py +194 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_component_reference.py +267 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_create_bird_blueprint_with_input_and_camera.py +618 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_input_mapping.py +366 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_physics_variables.py +390 -0
- package/tools/unreal-mcp/upstream/Python/tools/blueprint_tools.py +420 -0
- package/tools/unreal-mcp/upstream/Python/tools/editor_tools.py +369 -0
- package/tools/unreal-mcp/upstream/Python/tools/node_tools.py +430 -0
- package/tools/unreal-mcp/upstream/Python/tools/project_tools.py +64 -0
- package/tools/unreal-mcp/upstream/Python/tools/umg_tools.py +333 -0
- package/tools/unreal-mcp/upstream/Python/unreal_mcp_server.py +398 -0
- package/tools/unreal-mcp/upstream/Python/uv.lock +521 -0
|
@@ -0,0 +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__":
|
|
497
|
+
main()
|
package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_cube_blueprint.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Test script for creating and manipulating a basic Blueprint in Unreal Engine via MCP.
|
|
4
|
+
|
|
5
|
+
This script demonstrates the core Blueprint creation and manipulation workflow:
|
|
6
|
+
1. Creates a new Blueprint class (TestBP) derived from Actor
|
|
7
|
+
2. Adds a StaticMeshComponent named 'CubeVisual' to the Blueprint
|
|
8
|
+
3. Sets the component's mesh to a basic cube shape (/Engine/BasicShapes/Cube.Cube)
|
|
9
|
+
4. Compiles the Blueprint to apply the changes
|
|
10
|
+
5. Spawns an instance of the Blueprint in the level at [0, 0, 100]
|
|
11
|
+
|
|
12
|
+
The script serves as both a test of the MCP Blueprint manipulation functionality
|
|
13
|
+
and a basic example of how to programmatically create and modify Blueprints
|
|
14
|
+
through the MCP interface.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import os
|
|
19
|
+
import time
|
|
20
|
+
import socket
|
|
21
|
+
import json
|
|
22
|
+
import logging
|
|
23
|
+
from typing import Dict, Any, Optional
|
|
24
|
+
|
|
25
|
+
# Add the parent directory to the path so we can import the server module
|
|
26
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
27
|
+
|
|
28
|
+
# Set up logging
|
|
29
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
30
|
+
logger = logging.getLogger("TestBasicBlueprint")
|
|
31
|
+
|
|
32
|
+
def send_command(sock: socket.socket, command: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
33
|
+
"""Send a command to the Unreal MCP server and get the response."""
|
|
34
|
+
try:
|
|
35
|
+
# Create command object
|
|
36
|
+
command_obj = {
|
|
37
|
+
"type": command,
|
|
38
|
+
"params": params
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Convert to JSON and send
|
|
42
|
+
command_json = json.dumps(command_obj)
|
|
43
|
+
logger.info(f"Sending command: {command_json}")
|
|
44
|
+
sock.sendall(command_json.encode('utf-8'))
|
|
45
|
+
|
|
46
|
+
# Receive response
|
|
47
|
+
chunks = []
|
|
48
|
+
while True:
|
|
49
|
+
chunk = sock.recv(4096)
|
|
50
|
+
if not chunk:
|
|
51
|
+
break
|
|
52
|
+
chunks.append(chunk)
|
|
53
|
+
|
|
54
|
+
# Try parsing to see if we have a complete response
|
|
55
|
+
try:
|
|
56
|
+
data = b''.join(chunks)
|
|
57
|
+
json.loads(data.decode('utf-8'))
|
|
58
|
+
# If we can parse it, we have the complete response
|
|
59
|
+
break
|
|
60
|
+
except json.JSONDecodeError:
|
|
61
|
+
# Not a complete JSON object yet, continue receiving
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
# Parse response
|
|
65
|
+
data = b''.join(chunks)
|
|
66
|
+
response = json.loads(data.decode('utf-8'))
|
|
67
|
+
logger.info(f"Received response: {response}")
|
|
68
|
+
return response
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.error(f"Error sending command: {e}")
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
def main():
|
|
75
|
+
"""Main function to test creating a basic blueprint."""
|
|
76
|
+
try:
|
|
77
|
+
# Connect to Unreal MCP server
|
|
78
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
79
|
+
sock.connect(("127.0.0.1", 55557))
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
# Step 1: Create a blueprint
|
|
83
|
+
bp_params = {
|
|
84
|
+
"name": "TestBP",
|
|
85
|
+
"parent_class": "Actor"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
response = send_command(sock, "create_blueprint", bp_params)
|
|
89
|
+
|
|
90
|
+
# Fixed response check to handle nested structure
|
|
91
|
+
if not response or response.get("status") != "success":
|
|
92
|
+
logger.error(f"Failed to create blueprint: {response}")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
# Check if blueprint already existed
|
|
96
|
+
if response.get("result", {}).get("already_exists"):
|
|
97
|
+
logger.info(f"Blueprint 'TestBP' already exists, reusing it")
|
|
98
|
+
else:
|
|
99
|
+
logger.info("Blueprint created successfully!")
|
|
100
|
+
|
|
101
|
+
# Step 2: Add a static mesh component
|
|
102
|
+
component_params = {
|
|
103
|
+
"blueprint_name": "TestBP",
|
|
104
|
+
"component_type": "StaticMeshComponent",
|
|
105
|
+
"component_name": "CubeVisual",
|
|
106
|
+
"location": [0.0, 0.0, 0.0],
|
|
107
|
+
"rotation": [0.0, 0.0, 0.0],
|
|
108
|
+
"scale": [1.0, 1.0, 1.0]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Close and reopen connection for each command
|
|
112
|
+
sock.close()
|
|
113
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
114
|
+
sock.connect(("127.0.0.1", 55557))
|
|
115
|
+
|
|
116
|
+
response = send_command(sock, "add_component_to_blueprint", component_params)
|
|
117
|
+
|
|
118
|
+
# Fixed response check to handle nested structure
|
|
119
|
+
if not response or response.get("status") != "success":
|
|
120
|
+
logger.error(f"Failed to add component: {response}")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
logger.info("Component added successfully!")
|
|
124
|
+
|
|
125
|
+
# Step 3: Set the static mesh properties
|
|
126
|
+
sock.close()
|
|
127
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
128
|
+
sock.connect(("127.0.0.1", 55557))
|
|
129
|
+
|
|
130
|
+
mesh_params = {
|
|
131
|
+
"blueprint_name": "TestBP",
|
|
132
|
+
"component_name": "CubeVisual",
|
|
133
|
+
"static_mesh": "/Engine/BasicShapes/Cube.Cube"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
response = send_command(sock, "set_static_mesh_properties", mesh_params)
|
|
137
|
+
|
|
138
|
+
# Fixed response check to handle nested structure
|
|
139
|
+
if not response or response.get("status") != "success":
|
|
140
|
+
logger.error(f"Failed to set static mesh properties: {response}")
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
logger.info("Static mesh properties set successfully!")
|
|
144
|
+
|
|
145
|
+
# Step 4: Compile the blueprint
|
|
146
|
+
sock.close()
|
|
147
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
148
|
+
sock.connect(("127.0.0.1", 55557))
|
|
149
|
+
|
|
150
|
+
compile_params = {
|
|
151
|
+
"blueprint_name": "TestBP"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
response = send_command(sock, "compile_blueprint", compile_params)
|
|
155
|
+
|
|
156
|
+
# Fixed response check to handle nested structure
|
|
157
|
+
if not response or response.get("status") != "success":
|
|
158
|
+
logger.error(f"Failed to compile blueprint: {response}")
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
logger.info("Blueprint compiled successfully!")
|
|
162
|
+
|
|
163
|
+
# Step 5: Spawn an instance of the blueprint
|
|
164
|
+
sock.close()
|
|
165
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
166
|
+
sock.connect(("127.0.0.1", 55557))
|
|
167
|
+
|
|
168
|
+
spawn_params = {
|
|
169
|
+
"blueprint_name": "TestBP",
|
|
170
|
+
"actor_name": "TestBPInstance",
|
|
171
|
+
"location": [0.0, 0.0, 100.0], # 100 units up
|
|
172
|
+
"rotation": [0.0, 0.0, 0.0],
|
|
173
|
+
"scale": [1.0, 1.0, 1.0]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
response = send_command(sock, "spawn_blueprint_actor", spawn_params)
|
|
177
|
+
|
|
178
|
+
# Fixed response check to handle nested structure
|
|
179
|
+
if not response or response.get("status") != "success":
|
|
180
|
+
logger.error(f"Failed to spawn blueprint actor: {response}")
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
logger.info("Blueprint actor spawned successfully!")
|
|
184
|
+
|
|
185
|
+
finally:
|
|
186
|
+
# Close the socket
|
|
187
|
+
sock.close()
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
logger.error(f"Error: {e}")
|
|
191
|
+
sys.exit(1)
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
main()
|