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,1364 +1,1364 @@
1
- #include "Commands/UnrealMCPBlueprintCommands.h"
2
- #include "Commands/UnrealMCPCommonUtils.h"
3
- #include "Engine/Blueprint.h"
4
- #include "Engine/BlueprintGeneratedClass.h"
5
- #include "Factories/BlueprintFactory.h"
6
- #include "EdGraphSchema_K2.h"
7
- #include "K2Node_Event.h"
8
- #include "K2Node_VariableGet.h"
9
- #include "K2Node_VariableSet.h"
10
- #include "Components/StaticMeshComponent.h"
11
- #include "Components/BoxComponent.h"
12
- #include "Components/SphereComponent.h"
13
- #include "Kismet2/BlueprintEditorUtils.h"
14
- #include "Kismet2/KismetEditorUtilities.h"
15
- #include "Engine/SimpleConstructionScript.h"
16
- #include "Engine/SCS_Node.h"
17
- #include "UObject/Field.h"
18
- #include "UObject/FieldPath.h"
19
- #include "UObject/UObjectGlobals.h"
20
- #include "EditorAssetLibrary.h"
21
- #include "AssetRegistry/AssetRegistryModule.h"
22
- #include "MessageLogModule.h"
23
- #include "IMessageLogListing.h"
24
- #include "GameFramework/Actor.h"
25
- #include "GameFramework/Pawn.h"
26
-
27
- static UClass* FindMcpClassByName(const TCHAR* ClassName)
28
- {
29
- #if defined(ANY_PACKAGE)
30
- return FindObject<UClass>(ANY_PACKAGE, ClassName);
31
- #else
32
- return FindFirstObject<UClass>(ClassName);
33
- #endif
34
- }
35
-
36
- static UClass* FindMcpClassByName(const FString& ClassName)
37
- {
38
- return FindMcpClassByName(*ClassName);
39
- }
40
-
41
- FUnrealMCPBlueprintCommands::FUnrealMCPBlueprintCommands()
42
- {
43
- }
44
-
45
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCommand(const FString& CommandType, const TSharedPtr<FJsonObject>& Params)
46
- {
47
- if (CommandType == TEXT("create_blueprint"))
48
- {
49
- return HandleCreateBlueprint(Params);
50
- }
51
- else if (CommandType == TEXT("add_component_to_blueprint"))
52
- {
53
- return HandleAddComponentToBlueprint(Params);
54
- }
55
- else if (CommandType == TEXT("set_component_property"))
56
- {
57
- return HandleSetComponentProperty(Params);
58
- }
59
- else if (CommandType == TEXT("set_physics_properties"))
60
- {
61
- return HandleSetPhysicsProperties(Params);
62
- }
63
- else if (CommandType == TEXT("compile_blueprint"))
64
- {
65
- return HandleCompileBlueprint(Params);
66
- }
67
- else if (CommandType == TEXT("compile_blueprints_all"))
68
- {
69
- return HandleCompileBlueprintsAll(Params);
70
- }
71
- else if (CommandType == TEXT("spawn_blueprint_actor"))
72
- {
73
- return HandleSpawnBlueprintActor(Params);
74
- }
75
- else if (CommandType == TEXT("set_blueprint_property"))
76
- {
77
- return HandleSetBlueprintProperty(Params);
78
- }
79
- else if (CommandType == TEXT("set_static_mesh_properties"))
80
- {
81
- return HandleSetStaticMeshProperties(Params);
82
- }
83
- else if (CommandType == TEXT("set_pawn_properties"))
84
- {
85
- return HandleSetPawnProperties(Params);
86
- }
87
-
88
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Unknown blueprint command: %s"), *CommandType));
89
- }
90
-
91
- static bool IsSafeToCompileBlueprints(FString& OutError)
92
- {
93
- if (!GEditor)
94
- {
95
- OutError = TEXT("Editor is not available (GEditor is null).");
96
- return false;
97
- }
98
- if (GEditor->PlayWorld)
99
- {
100
- OutError = TEXT("Cannot compile Blueprints while Play-In-Editor is running. Stop PIE and retry.");
101
- return false;
102
- }
103
- return true;
104
- }
105
-
106
- static FString MessageSeverityToString(EMessageSeverity::Type Severity)
107
- {
108
- switch (Severity)
109
- {
110
- case EMessageSeverity::CriticalError:
111
- case EMessageSeverity::Error:
112
- return TEXT("error");
113
- case EMessageSeverity::Warning:
114
- case EMessageSeverity::PerformanceWarning:
115
- return TEXT("warning");
116
- case EMessageSeverity::Info:
117
- return TEXT("info");
118
- default:
119
- return TEXT("info");
120
- }
121
- }
122
-
123
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCreateBlueprint(const TSharedPtr<FJsonObject>& Params)
124
- {
125
- // Get required parameters
126
- FString BlueprintName;
127
- if (!Params->TryGetStringField(TEXT("name"), BlueprintName) && !Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
128
- {
129
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'name' parameter"));
130
- }
131
-
132
- // Check if blueprint already exists
133
- FString PackagePath = TEXT("/Game/Blueprints/");
134
- Params->TryGetStringField(TEXT("path"), PackagePath);
135
- Params->TryGetStringField(TEXT("blueprint_path"), PackagePath);
136
- Params->TryGetStringField(TEXT("packagePath"), PackagePath);
137
- Params->TryGetStringField(TEXT("package_path"), PackagePath);
138
-
139
- PackagePath = PackagePath.TrimStartAndEnd();
140
- if (PackagePath.IsEmpty()) PackagePath = TEXT("/Game/Blueprints/");
141
- if (!PackagePath.StartsWith(TEXT("/Game/")))
142
- {
143
- // Keep it safe + predictable: only allow /Game paths.
144
- PackagePath = TEXT("/Game/Blueprints/");
145
- }
146
- if (!PackagePath.EndsWith(TEXT("/")))
147
- {
148
- PackagePath += TEXT("/");
149
- }
150
-
151
- FString AssetName = BlueprintName;
152
- if (UEditorAssetLibrary::DoesAssetExist(PackagePath + AssetName))
153
- {
154
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint already exists: %s"), *BlueprintName));
155
- }
156
-
157
- // Create the blueprint factory
158
- UBlueprintFactory* Factory = NewObject<UBlueprintFactory>();
159
-
160
- // Handle parent class
161
- FString ParentClass;
162
- Params->TryGetStringField(TEXT("parent_class"), ParentClass);
163
-
164
- // Default to Actor if no parent class specified
165
- UClass* SelectedParentClass = AActor::StaticClass();
166
-
167
- // Try to find the specified parent class
168
- if (!ParentClass.IsEmpty())
169
- {
170
- FString ClassName = ParentClass;
171
- if (!ClassName.StartsWith(TEXT("A")))
172
- {
173
- ClassName = TEXT("A") + ClassName;
174
- }
175
-
176
- // First try direct StaticClass lookup for common classes
177
- UClass* FoundClass = nullptr;
178
- if (ClassName == TEXT("APawn"))
179
- {
180
- FoundClass = APawn::StaticClass();
181
- }
182
- else if (ClassName == TEXT("AActor"))
183
- {
184
- FoundClass = AActor::StaticClass();
185
- }
186
- else
187
- {
188
- // Try loading the class using LoadClass which is more reliable than FindObject
189
- const FString ClassPath = FString::Printf(TEXT("/Script/Engine.%s"), *ClassName);
190
- FoundClass = LoadClass<AActor>(nullptr, *ClassPath);
191
-
192
- if (!FoundClass)
193
- {
194
- // Try alternate paths if not found
195
- const FString GameClassPath = FString::Printf(TEXT("/Script/Game.%s"), *ClassName);
196
- FoundClass = LoadClass<AActor>(nullptr, *GameClassPath);
197
- }
198
- }
199
-
200
- if (FoundClass)
201
- {
202
- SelectedParentClass = FoundClass;
203
- UE_LOG(LogTemp, Log, TEXT("Successfully set parent class to '%s'"), *ClassName);
204
- }
205
- else
206
- {
207
- UE_LOG(LogTemp, Warning, TEXT("Could not find specified parent class '%s' at paths: /Script/Engine.%s or /Script/Game.%s, defaulting to AActor"),
208
- *ClassName, *ClassName, *ClassName);
209
- }
210
- }
211
-
212
- Factory->ParentClass = SelectedParentClass;
213
-
214
- // Create the blueprint
215
- UPackage* Package = CreatePackage(*(PackagePath + AssetName));
216
- UBlueprint* NewBlueprint = Cast<UBlueprint>(Factory->FactoryCreateNew(UBlueprint::StaticClass(), Package, *AssetName, RF_Standalone | RF_Public, nullptr, GWarn));
217
-
218
- if (NewBlueprint)
219
- {
220
- // Notify the asset registry
221
- FAssetRegistryModule::AssetCreated(NewBlueprint);
222
-
223
- // Mark the package dirty
224
- Package->MarkPackageDirty();
225
-
226
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
227
- ResultObj->SetStringField(TEXT("name"), AssetName);
228
- ResultObj->SetStringField(TEXT("path"), PackagePath + AssetName);
229
- return ResultObj;
230
- }
231
-
232
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create blueprint"));
233
- }
234
-
235
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleAddComponentToBlueprint(const TSharedPtr<FJsonObject>& Params)
236
- {
237
- // Get required parameters
238
- FString BlueprintName;
239
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
240
- {
241
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
242
- }
243
-
244
- FString ComponentType;
245
- if (!Params->TryGetStringField(TEXT("component_type"), ComponentType) && !Params->TryGetStringField(TEXT("type"), ComponentType))
246
- {
247
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_type' parameter"));
248
- }
249
-
250
- FString ComponentName;
251
- if (!Params->TryGetStringField(TEXT("component_name"), ComponentName) && !Params->TryGetStringField(TEXT("name"), ComponentName))
252
- {
253
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
254
- }
255
-
256
- // Find the blueprint
257
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
258
- if (!Blueprint)
259
- {
260
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
261
- }
262
-
263
- // Create the component - dynamically find the component class by name
264
- UClass* ComponentClass = nullptr;
265
-
266
- // Try to find the class with exact name first
267
- ComponentClass = FindMcpClassByName(ComponentType);
268
-
269
- // If not found, try with "Component" suffix
270
- if (!ComponentClass && !ComponentType.EndsWith(TEXT("Component")))
271
- {
272
- FString ComponentTypeWithSuffix = ComponentType + TEXT("Component");
273
- ComponentClass = FindMcpClassByName(ComponentTypeWithSuffix);
274
- }
275
-
276
- // If still not found, try with "U" prefix
277
- if (!ComponentClass && !ComponentType.StartsWith(TEXT("U")))
278
- {
279
- FString ComponentTypeWithPrefix = TEXT("U") + ComponentType;
280
- ComponentClass = FindMcpClassByName(ComponentTypeWithPrefix);
281
-
282
- // Try with both prefix and suffix
283
- if (!ComponentClass && !ComponentType.EndsWith(TEXT("Component")))
284
- {
285
- FString ComponentTypeWithBoth = TEXT("U") + ComponentType + TEXT("Component");
286
- ComponentClass = FindMcpClassByName(ComponentTypeWithBoth);
287
- }
288
- }
289
-
290
- // Verify that the class is a valid component type
291
- if (!ComponentClass || !ComponentClass->IsChildOf(UActorComponent::StaticClass()))
292
- {
293
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Unknown component type: %s"), *ComponentType));
294
- }
295
-
296
- // Add the component to the blueprint
297
- USCS_Node* NewNode = Blueprint->SimpleConstructionScript->CreateNode(ComponentClass, *ComponentName);
298
- if (NewNode)
299
- {
300
- // Set transform if provided
301
- USceneComponent* SceneComponent = Cast<USceneComponent>(NewNode->ComponentTemplate);
302
- if (SceneComponent)
303
- {
304
- if (Params->HasField(TEXT("location")))
305
- {
306
- SceneComponent->SetRelativeLocation(FUnrealMCPCommonUtils::GetVectorFromJson(Params, TEXT("location")));
307
- }
308
- if (Params->HasField(TEXT("rotation")))
309
- {
310
- SceneComponent->SetRelativeRotation(FUnrealMCPCommonUtils::GetRotatorFromJson(Params, TEXT("rotation")));
311
- }
312
- if (Params->HasField(TEXT("scale")))
313
- {
314
- SceneComponent->SetRelativeScale3D(FUnrealMCPCommonUtils::GetVectorFromJson(Params, TEXT("scale")));
315
- }
316
- }
317
-
318
- // Add to root if no parent specified
319
- Blueprint->SimpleConstructionScript->AddNode(NewNode);
320
-
321
- // Compile the blueprint
322
- FKismetEditorUtilities::CompileBlueprint(Blueprint);
323
-
324
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
325
- ResultObj->SetStringField(TEXT("component_name"), ComponentName);
326
- ResultObj->SetStringField(TEXT("component_type"), ComponentType);
327
- return ResultObj;
328
- }
329
-
330
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to add component to blueprint"));
331
- }
332
-
333
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(const TSharedPtr<FJsonObject>& Params)
334
- {
335
- // Get required parameters
336
- FString BlueprintName;
337
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
338
- {
339
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
340
- }
341
-
342
- FString ComponentName;
343
- if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
344
- {
345
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
346
- }
347
-
348
- FString PropertyName;
349
- if (!Params->TryGetStringField(TEXT("property_name"), PropertyName))
350
- {
351
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_name' parameter"));
352
- }
353
-
354
- // Log all input parameters for debugging
355
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Blueprint: %s, Component: %s, Property: %s"),
356
- *BlueprintName, *ComponentName, *PropertyName);
357
-
358
- // Log property_value if available
359
- if (Params->HasField(TEXT("property_value")))
360
- {
361
- TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
362
- FString ValueType;
363
-
364
- switch(JsonValue->Type)
365
- {
366
- case EJson::Boolean: ValueType = FString::Printf(TEXT("Boolean: %s"), JsonValue->AsBool() ? TEXT("true") : TEXT("false")); break;
367
- case EJson::Number: ValueType = FString::Printf(TEXT("Number: %f"), JsonValue->AsNumber()); break;
368
- case EJson::String: ValueType = FString::Printf(TEXT("String: %s"), *JsonValue->AsString()); break;
369
- case EJson::Array: ValueType = TEXT("Array"); break;
370
- case EJson::Object: ValueType = TEXT("Object"); break;
371
- default: ValueType = TEXT("Unknown"); break;
372
- }
373
-
374
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Value Type: %s"), *ValueType);
375
- }
376
- else
377
- {
378
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - No property_value provided"));
379
- }
380
-
381
- // Find the blueprint
382
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
383
- if (!Blueprint)
384
- {
385
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Blueprint not found: %s"), *BlueprintName);
386
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
387
- }
388
- else
389
- {
390
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Blueprint found: %s (Class: %s)"),
391
- *BlueprintName,
392
- Blueprint->GeneratedClass ? *Blueprint->GeneratedClass->GetName() : TEXT("NULL"));
393
- }
394
-
395
- // Find the component
396
- USCS_Node* ComponentNode = nullptr;
397
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Searching for component %s in blueprint nodes"), *ComponentName);
398
-
399
- if (!Blueprint->SimpleConstructionScript)
400
- {
401
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - SimpleConstructionScript is NULL for blueprint %s"), *BlueprintName);
402
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid blueprint construction script"));
403
- }
404
-
405
- for (USCS_Node* Node : Blueprint->SimpleConstructionScript->GetAllNodes())
406
- {
407
- if (Node)
408
- {
409
- UE_LOG(LogTemp, Verbose, TEXT("SetComponentProperty - Found node: %s"), *Node->GetVariableName().ToString());
410
- if (Node->GetVariableName().ToString() == ComponentName)
411
- {
412
- ComponentNode = Node;
413
- break;
414
- }
415
- }
416
- else
417
- {
418
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Found NULL node in blueprint"));
419
- }
420
- }
421
-
422
- if (!ComponentNode)
423
- {
424
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component not found: %s"), *ComponentName);
425
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
426
- }
427
- else
428
- {
429
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Component found: %s (Class: %s)"),
430
- *ComponentName,
431
- ComponentNode->ComponentTemplate ? *ComponentNode->ComponentTemplate->GetClass()->GetName() : TEXT("NULL"));
432
- }
433
-
434
- // Get the component template
435
- UObject* ComponentTemplate = ComponentNode->ComponentTemplate;
436
- if (!ComponentTemplate)
437
- {
438
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component template is NULL for %s"), *ComponentName);
439
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid component template"));
440
- }
441
-
442
- // Check if this is a Spring Arm component and log special debug info
443
- if (ComponentTemplate->GetClass()->GetName().Contains(TEXT("SpringArm")))
444
- {
445
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm component detected! Class: %s"),
446
- *ComponentTemplate->GetClass()->GetPathName());
447
-
448
- // Log all properties of the SpringArm component class
449
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm properties:"));
450
- for (TFieldIterator<FProperty> PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt)
451
- {
452
- FProperty* Prop = *PropIt;
453
- UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
454
- }
455
-
456
- // Special handling for Spring Arm properties
457
- if (Params->HasField(TEXT("property_value")))
458
- {
459
- TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
460
-
461
- // Get the property using the new FField system
462
- FProperty* Property = FindFProperty<FProperty>(ComponentTemplate->GetClass(), *PropertyName);
463
- if (!Property)
464
- {
465
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on SpringArm component"), *PropertyName);
466
- return FUnrealMCPCommonUtils::CreateErrorResponse(
467
- FString::Printf(TEXT("Property %s not found on SpringArm component"), *PropertyName));
468
- }
469
-
470
- // Create a scope guard to ensure property cleanup
471
- struct FScopeGuard
472
- {
473
- UObject* Object;
474
- FScopeGuard(UObject* InObject) : Object(InObject)
475
- {
476
- if (Object)
477
- {
478
- Object->Modify();
479
- }
480
- }
481
- ~FScopeGuard()
482
- {
483
- if (Object)
484
- {
485
- Object->PostEditChange();
486
- }
487
- }
488
- } ScopeGuard(ComponentTemplate);
489
-
490
- bool bSuccess = false;
491
- FString ErrorMessage;
492
-
493
- // Handle specific Spring Arm property types
494
- if (FFloatProperty* FloatProp = CastField<FFloatProperty>(Property))
495
- {
496
- if (JsonValue->Type == EJson::Number)
497
- {
498
- const float Value = JsonValue->AsNumber();
499
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting float property %s to %f"), *PropertyName, Value);
500
- FloatProp->SetPropertyValue_InContainer(ComponentTemplate, Value);
501
- bSuccess = true;
502
- }
503
- }
504
- else if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Property))
505
- {
506
- if (JsonValue->Type == EJson::Boolean)
507
- {
508
- const bool Value = JsonValue->AsBool();
509
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting bool property %s to %d"), *PropertyName, Value);
510
- BoolProp->SetPropertyValue_InContainer(ComponentTemplate, Value);
511
- bSuccess = true;
512
- }
513
- }
514
- else if (FStructProperty* StructProp = CastField<FStructProperty>(Property))
515
- {
516
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Handling struct property %s of type %s"),
517
- *PropertyName, *StructProp->Struct->GetName());
518
-
519
- // Special handling for common Spring Arm struct properties
520
- if (StructProp->Struct == TBaseStructure<FVector>::Get())
521
- {
522
- if (JsonValue->Type == EJson::Array)
523
- {
524
- const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
525
- if (Arr.Num() == 3)
526
- {
527
- FVector Vec(
528
- Arr[0]->AsNumber(),
529
- Arr[1]->AsNumber(),
530
- Arr[2]->AsNumber()
531
- );
532
- void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
533
- StructProp->CopySingleValue(PropertyAddr, &Vec);
534
- bSuccess = true;
535
- }
536
- }
537
- }
538
- else if (StructProp->Struct == TBaseStructure<FRotator>::Get())
539
- {
540
- if (JsonValue->Type == EJson::Array)
541
- {
542
- const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
543
- if (Arr.Num() == 3)
544
- {
545
- FRotator Rot(
546
- Arr[0]->AsNumber(),
547
- Arr[1]->AsNumber(),
548
- Arr[2]->AsNumber()
549
- );
550
- void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
551
- StructProp->CopySingleValue(PropertyAddr, &Rot);
552
- bSuccess = true;
553
- }
554
- }
555
- }
556
- }
557
-
558
- if (bSuccess)
559
- {
560
- // Mark the blueprint as modified
561
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set SpringArm property %s"), *PropertyName);
562
- FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
563
-
564
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
565
- ResultObj->SetStringField(TEXT("component"), ComponentName);
566
- ResultObj->SetStringField(TEXT("property"), PropertyName);
567
- ResultObj->SetBoolField(TEXT("success"), true);
568
- return ResultObj;
569
- }
570
- else
571
- {
572
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set SpringArm property %s"), *PropertyName);
573
- return FUnrealMCPCommonUtils::CreateErrorResponse(
574
- FString::Printf(TEXT("Failed to set SpringArm property %s"), *PropertyName));
575
- }
576
- }
577
- }
578
-
579
- // Regular property handling for non-Spring Arm components continues...
580
-
581
- // Set the property value
582
- if (Params->HasField(TEXT("property_value")))
583
- {
584
- TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
585
-
586
- // Get the property
587
- FProperty* Property = FindFProperty<FProperty>(ComponentTemplate->GetClass(), *PropertyName);
588
- if (!Property)
589
- {
590
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on component %s"),
591
- *PropertyName, *ComponentName);
592
-
593
- // List all available properties for this component
594
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available properties for %s:"), *ComponentName);
595
- for (TFieldIterator<FProperty> PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt)
596
- {
597
- FProperty* Prop = *PropIt;
598
- UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
599
- }
600
-
601
- return FUnrealMCPCommonUtils::CreateErrorResponse(
602
- FString::Printf(TEXT("Property %s not found on component %s"), *PropertyName, *ComponentName));
603
- }
604
- else
605
- {
606
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property found: %s (Type: %s)"),
607
- *PropertyName, *Property->GetCPPType());
608
- }
609
-
610
- bool bSuccess = false;
611
- FString ErrorMessage;
612
-
613
- // Handle different property types
614
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Attempting to set property %s"), *PropertyName);
615
-
616
- // Add try-catch block to catch and log any crashes
617
- try
618
- {
619
- if (FStructProperty* StructProp = CastField<FStructProperty>(Property))
620
- {
621
- // Handle vector properties
622
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is a struct: %s"),
623
- StructProp->Struct ? *StructProp->Struct->GetName() : TEXT("NULL"));
624
-
625
- if (StructProp->Struct == TBaseStructure<FVector>::Get())
626
- {
627
- if (JsonValue->Type == EJson::Array)
628
- {
629
- // Handle array input [x, y, z]
630
- const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
631
- if (Arr.Num() == 3)
632
- {
633
- FVector Vec(
634
- Arr[0]->AsNumber(),
635
- Arr[1]->AsNumber(),
636
- Arr[2]->AsNumber()
637
- );
638
- void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
639
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f)"),
640
- Vec.X, Vec.Y, Vec.Z);
641
- StructProp->CopySingleValue(PropertyAddr, &Vec);
642
- bSuccess = true;
643
- }
644
- else
645
- {
646
- ErrorMessage = FString::Printf(TEXT("Vector property requires 3 values, got %d"), Arr.Num());
647
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
648
- }
649
- }
650
- else if (JsonValue->Type == EJson::Number)
651
- {
652
- // Handle scalar input (sets all components to same value)
653
- float Value = JsonValue->AsNumber();
654
- FVector Vec(Value, Value, Value);
655
- void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
656
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f) from scalar"),
657
- Vec.X, Vec.Y, Vec.Z);
658
- StructProp->CopySingleValue(PropertyAddr, &Vec);
659
- bSuccess = true;
660
- }
661
- else
662
- {
663
- ErrorMessage = TEXT("Vector property requires either a single number or array of 3 numbers");
664
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
665
- }
666
- }
667
- else
668
- {
669
- // Handle other struct properties using default handler
670
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic struct handler for %s"),
671
- *PropertyName);
672
- bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage);
673
- if (!bSuccess)
674
- {
675
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set struct property: %s"), *ErrorMessage);
676
- }
677
- }
678
- }
679
- else if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Property))
680
- {
681
- // Handle enum properties
682
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is an enum"));
683
- if (JsonValue->Type == EJson::String)
684
- {
685
- FString EnumValueName = JsonValue->AsString();
686
- UEnum* Enum = EnumProp->GetEnum();
687
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from string: %s"), *EnumValueName);
688
-
689
- if (Enum)
690
- {
691
- int64 EnumValue = Enum->GetValueByNameString(EnumValueName);
692
-
693
- if (EnumValue != INDEX_NONE)
694
- {
695
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Found enum value: %lld"), EnumValue);
696
- EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(
697
- ComponentTemplate,
698
- EnumValue
699
- );
700
- bSuccess = true;
701
- }
702
- else
703
- {
704
- // List all possible enum values
705
- UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available enum values for %s:"),
706
- *Enum->GetName());
707
- for (int32 i = 0; i < Enum->NumEnums(); i++)
708
- {
709
- UE_LOG(LogTemp, Warning, TEXT(" - %s (%lld)"),
710
- *Enum->GetNameStringByIndex(i),
711
- Enum->GetValueByIndex(i));
712
- }
713
-
714
- ErrorMessage = FString::Printf(TEXT("Invalid enum value '%s' for property %s"),
715
- *EnumValueName, *PropertyName);
716
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
717
- }
718
- }
719
- else
720
- {
721
- ErrorMessage = TEXT("Enum object is NULL");
722
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
723
- }
724
- }
725
- else if (JsonValue->Type == EJson::Number)
726
- {
727
- // Allow setting enum by integer value
728
- int64 EnumValue = JsonValue->AsNumber();
729
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from number: %lld"), EnumValue);
730
- EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(
731
- ComponentTemplate,
732
- EnumValue
733
- );
734
- bSuccess = true;
735
- }
736
- else
737
- {
738
- ErrorMessage = TEXT("Enum property requires either a string name or integer value");
739
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
740
- }
741
- }
742
- else if (FNumericProperty* NumericProp = CastField<FNumericProperty>(Property))
743
- {
744
- // Handle numeric properties
745
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is numeric: IsInteger=%d, IsFloat=%d"),
746
- NumericProp->IsInteger(), NumericProp->IsFloatingPoint());
747
-
748
- if (JsonValue->Type == EJson::Number)
749
- {
750
- double Value = JsonValue->AsNumber();
751
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting numeric value: %f"), Value);
752
-
753
- if (NumericProp->IsInteger())
754
- {
755
- NumericProp->SetIntPropertyValue(ComponentTemplate, (int64)Value);
756
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set integer value: %lld"), (int64)Value);
757
- bSuccess = true;
758
- }
759
- else if (NumericProp->IsFloatingPoint())
760
- {
761
- NumericProp->SetFloatingPointPropertyValue(ComponentTemplate, Value);
762
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set float value: %f"), Value);
763
- bSuccess = true;
764
- }
765
- }
766
- else
767
- {
768
- ErrorMessage = TEXT("Numeric property requires a number value");
769
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
770
- }
771
- }
772
- else
773
- {
774
- // Handle all other property types using default handler
775
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic property handler for %s (Type: %s)"),
776
- *PropertyName, *Property->GetCPPType());
777
- bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage);
778
- if (!bSuccess)
779
- {
780
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property: %s"), *ErrorMessage);
781
- }
782
- }
783
- }
784
- catch (const std::exception& Ex)
785
- {
786
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - EXCEPTION: %s"), ANSI_TO_TCHAR(Ex.what()));
787
- return FUnrealMCPCommonUtils::CreateErrorResponse(
788
- FString::Printf(TEXT("Exception while setting property %s: %s"), *PropertyName, ANSI_TO_TCHAR(Ex.what())));
789
- }
790
- catch (...)
791
- {
792
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - UNKNOWN EXCEPTION occurred while setting property %s"), *PropertyName);
793
- return FUnrealMCPCommonUtils::CreateErrorResponse(
794
- FString::Printf(TEXT("Unknown exception while setting property %s"), *PropertyName));
795
- }
796
-
797
- if (bSuccess)
798
- {
799
- // Mark the blueprint as modified
800
- UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set property %s on component %s"),
801
- *PropertyName, *ComponentName);
802
- FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
803
-
804
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
805
- ResultObj->SetStringField(TEXT("component"), ComponentName);
806
- ResultObj->SetStringField(TEXT("property"), PropertyName);
807
- ResultObj->SetBoolField(TEXT("success"), true);
808
- return ResultObj;
809
- }
810
- else
811
- {
812
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property %s: %s"),
813
- *PropertyName, *ErrorMessage);
814
- return FUnrealMCPCommonUtils::CreateErrorResponse(ErrorMessage);
815
- }
816
- }
817
-
818
- UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Missing 'property_value' parameter"));
819
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_value' parameter"));
820
- }
821
-
822
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetPhysicsProperties(const TSharedPtr<FJsonObject>& Params)
823
- {
824
- // Get required parameters
825
- FString BlueprintName;
826
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
827
- {
828
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
829
- }
830
-
831
- FString ComponentName;
832
- if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
833
- {
834
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
835
- }
836
-
837
- // Find the blueprint
838
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
839
- if (!Blueprint)
840
- {
841
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
842
- }
843
-
844
- // Find the component
845
- USCS_Node* ComponentNode = nullptr;
846
- for (USCS_Node* Node : Blueprint->SimpleConstructionScript->GetAllNodes())
847
- {
848
- if (Node && Node->GetVariableName().ToString() == ComponentName)
849
- {
850
- ComponentNode = Node;
851
- break;
852
- }
853
- }
854
-
855
- if (!ComponentNode)
856
- {
857
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
858
- }
859
-
860
- UPrimitiveComponent* PrimComponent = Cast<UPrimitiveComponent>(ComponentNode->ComponentTemplate);
861
- if (!PrimComponent)
862
- {
863
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Component is not a primitive component"));
864
- }
865
-
866
- // Set physics properties
867
- if (Params->HasField(TEXT("simulate_physics")))
868
- {
869
- PrimComponent->SetSimulatePhysics(Params->GetBoolField(TEXT("simulate_physics")));
870
- }
871
-
872
- if (Params->HasField(TEXT("mass")))
873
- {
874
- float Mass = Params->GetNumberField(TEXT("mass"));
875
- // In UE5.5, use proper overrideMass instead of just scaling
876
- PrimComponent->SetMassOverrideInKg(NAME_None, Mass);
877
- UE_LOG(LogTemp, Display, TEXT("Set mass for component %s to %f kg"), *ComponentName, Mass);
878
- }
879
-
880
- if (Params->HasField(TEXT("linear_damping")))
881
- {
882
- PrimComponent->SetLinearDamping(Params->GetNumberField(TEXT("linear_damping")));
883
- }
884
-
885
- if (Params->HasField(TEXT("angular_damping")))
886
- {
887
- PrimComponent->SetAngularDamping(Params->GetNumberField(TEXT("angular_damping")));
888
- }
889
-
890
- // Mark the blueprint as modified
891
- FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
892
-
893
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
894
- ResultObj->SetStringField(TEXT("component"), ComponentName);
895
- return ResultObj;
896
- }
897
-
898
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCompileBlueprint(const TSharedPtr<FJsonObject>& Params)
899
- {
900
- // Get required parameters
901
- FString BlueprintName;
902
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
903
- {
904
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
905
- }
906
-
907
- // Find the blueprint
908
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
909
- if (!Blueprint)
910
- {
911
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
912
- }
913
-
914
- // Compile the blueprint
915
- FKismetEditorUtilities::CompileBlueprint(Blueprint);
916
-
917
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
918
- ResultObj->SetStringField(TEXT("name"), BlueprintName);
919
- ResultObj->SetBoolField(TEXT("compiled"), true);
920
- return ResultObj;
921
- }
922
-
923
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCompileBlueprintsAll(const TSharedPtr<FJsonObject>& Params)
924
- {
925
- {
926
- FString Err;
927
- if (!IsSafeToCompileBlueprints(Err))
928
- {
929
- return FUnrealMCPCommonUtils::CreateErrorResponse(Err);
930
- }
931
- }
932
-
933
- int32 Limit = 500;
934
- if (Params->HasField(TEXT("limit")))
935
- {
936
- Limit = (int32)Params->GetNumberField(TEXT("limit"));
937
- }
938
- Limit = FMath::Clamp(Limit, 1, 10000);
939
-
940
- bool bIncludeWarnings = true;
941
- if (Params->HasField(TEXT("includeWarnings")))
942
- {
943
- bIncludeWarnings = Params->GetBoolField(TEXT("includeWarnings"));
944
- }
945
-
946
- TArray<FString> RootPaths;
947
- if (Params->HasField(TEXT("paths")))
948
- {
949
- const TArray<TSharedPtr<FJsonValue>> PathsJson = Params->GetArrayField(TEXT("paths"));
950
- for (const TSharedPtr<FJsonValue>& PathVal : PathsJson)
951
- {
952
- if (!PathVal.IsValid() || PathVal->Type != EJson::String) continue;
953
- const FString P = PathVal->AsString().TrimStartAndEnd();
954
- if (!P.IsEmpty())
955
- {
956
- RootPaths.Add(P);
957
- }
958
- }
959
- }
960
- if (RootPaths.Num() == 0)
961
- {
962
- RootPaths.Add(TEXT("/Game"));
963
- }
964
-
965
- FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
966
- IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
967
-
968
- FARFilter Filter;
969
- Filter.bRecursivePaths = true;
970
- #if ENGINE_MAJOR_VERSION >= 5
971
- Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
972
- #else
973
- Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
974
- #endif
975
- for (const FString& RootPath : RootPaths)
976
- {
977
- const FString Normalized = RootPath.StartsWith(TEXT("/")) ? RootPath : FString(TEXT("/")) + RootPath;
978
- Filter.PackagePaths.Add(FName(*Normalized));
979
- }
980
-
981
- TArray<FAssetData> Assets;
982
- AssetRegistry.GetAssets(Filter, Assets);
983
-
984
- int32 TotalBlueprints = 0;
985
- int32 Compiled = 0;
986
- int32 Failed = 0;
987
- int32 ErrorMessages = 0;
988
- int32 WarningMessages = 0;
989
-
990
- FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>(TEXT("MessageLog"));
991
- const TSharedRef<IMessageLogListing> Listing = MessageLogModule.GetLogListing(TEXT("BlueprintLog"));
992
-
993
- TArray<TSharedPtr<FJsonValue>> Results;
994
-
995
- for (const FAssetData& Asset : Assets)
996
- {
997
- if (TotalBlueprints >= Limit) break;
998
-
999
- UBlueprint* Blueprint = Cast<UBlueprint>(Asset.GetAsset());
1000
- if (!Blueprint) continue;
1001
-
1002
- TotalBlueprints++;
1003
-
1004
- Listing->ClearMessages();
1005
- FKismetEditorUtilities::CompileBlueprint(Blueprint);
1006
-
1007
- const TArray<TSharedRef<FTokenizedMessage>>& Messages = Listing->GetFilteredMessages();
1008
- int32 BlueprintErrors = 0;
1009
- int32 BlueprintWarnings = 0;
1010
- TArray<TSharedPtr<FJsonValue>> MessageArray;
1011
-
1012
- for (const TSharedRef<FTokenizedMessage>& Message : Messages)
1013
- {
1014
- const EMessageSeverity::Type Severity = Message->GetSeverity();
1015
- const bool bIsWarning =
1016
- Severity == EMessageSeverity::Warning || Severity == EMessageSeverity::PerformanceWarning;
1017
- const bool bIsError = Severity == EMessageSeverity::Error || Severity == EMessageSeverity::CriticalError;
1018
-
1019
- if (bIsError) BlueprintErrors++;
1020
- if (bIsWarning) BlueprintWarnings++;
1021
-
1022
- if (!bIncludeWarnings && bIsWarning) continue;
1023
-
1024
- TSharedPtr<FJsonObject> Msg = MakeShared<FJsonObject>();
1025
- Msg->SetStringField(TEXT("severity"), MessageSeverityToString(Severity));
1026
- Msg->SetStringField(TEXT("message"), Message->ToText().ToString());
1027
- MessageArray.Add(MakeShared<FJsonValueObject>(Msg));
1028
- }
1029
-
1030
- ErrorMessages += BlueprintErrors;
1031
- WarningMessages += BlueprintWarnings;
1032
-
1033
- const bool bHadErrors = Blueprint->Status == BS_Error;
1034
- if (bHadErrors) Failed++; else Compiled++;
1035
-
1036
- TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
1037
- Entry->SetStringField(TEXT("name"), Blueprint->GetName());
1038
- Entry->SetStringField(TEXT("objectPath"), Asset.GetObjectPathString());
1039
- Entry->SetBoolField(TEXT("ok"), !bHadErrors);
1040
- Entry->SetNumberField(TEXT("errors"), BlueprintErrors);
1041
- Entry->SetNumberField(TEXT("warnings"), BlueprintWarnings);
1042
- Entry->SetArrayField(TEXT("messages"), MessageArray);
1043
- Results.Add(MakeShared<FJsonValueObject>(Entry));
1044
- }
1045
-
1046
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
1047
- ResultObj->SetNumberField(TEXT("found"), Assets.Num());
1048
- ResultObj->SetNumberField(TEXT("compiled"), Compiled);
1049
- ResultObj->SetNumberField(TEXT("failed"), Failed);
1050
- ResultObj->SetNumberField(TEXT("errors"), ErrorMessages);
1051
- ResultObj->SetNumberField(TEXT("warnings"), WarningMessages);
1052
- ResultObj->SetArrayField(TEXT("results"), Results);
1053
- ResultObj->SetBoolField(TEXT("truncated"), TotalBlueprints < Assets.Num() && TotalBlueprints >= Limit);
1054
- return ResultObj;
1055
- }
1056
-
1057
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSpawnBlueprintActor(const TSharedPtr<FJsonObject>& Params)
1058
- {
1059
- // Get required parameters
1060
- FString BlueprintName;
1061
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1062
- {
1063
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1064
- }
1065
-
1066
- FString ActorName;
1067
- if (!Params->TryGetStringField(TEXT("actor_name"), ActorName))
1068
- {
1069
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'actor_name' parameter"));
1070
- }
1071
-
1072
- // Find the blueprint
1073
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1074
- if (!Blueprint)
1075
- {
1076
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1077
- }
1078
-
1079
- // Get transform parameters
1080
- FVector Location(0.0f, 0.0f, 0.0f);
1081
- FRotator Rotation(0.0f, 0.0f, 0.0f);
1082
-
1083
- if (Params->HasField(TEXT("location")))
1084
- {
1085
- Location = FUnrealMCPCommonUtils::GetVectorFromJson(Params, TEXT("location"));
1086
- }
1087
- if (Params->HasField(TEXT("rotation")))
1088
- {
1089
- Rotation = FUnrealMCPCommonUtils::GetRotatorFromJson(Params, TEXT("rotation"));
1090
- }
1091
-
1092
- // Spawn the actor
1093
- UWorld* World = GEditor->GetEditorWorldContext().World();
1094
- if (!World)
1095
- {
1096
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get editor world"));
1097
- }
1098
-
1099
- FTransform SpawnTransform;
1100
- SpawnTransform.SetLocation(Location);
1101
- SpawnTransform.SetRotation(FQuat(Rotation));
1102
-
1103
- AActor* NewActor = World->SpawnActor<AActor>(Blueprint->GeneratedClass, SpawnTransform);
1104
- if (NewActor)
1105
- {
1106
- NewActor->SetActorLabel(*ActorName);
1107
- return FUnrealMCPCommonUtils::ActorToJsonObject(NewActor, true);
1108
- }
1109
-
1110
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to spawn blueprint actor"));
1111
- }
1112
-
1113
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetBlueprintProperty(const TSharedPtr<FJsonObject>& Params)
1114
- {
1115
- // Get required parameters
1116
- FString BlueprintName;
1117
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1118
- {
1119
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1120
- }
1121
-
1122
- FString PropertyName;
1123
- if (!Params->TryGetStringField(TEXT("property_name"), PropertyName))
1124
- {
1125
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_name' parameter"));
1126
- }
1127
-
1128
- // Find the blueprint
1129
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1130
- if (!Blueprint)
1131
- {
1132
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1133
- }
1134
-
1135
- // Get the default object
1136
- UObject* DefaultObject = Blueprint->GeneratedClass->GetDefaultObject();
1137
- if (!DefaultObject)
1138
- {
1139
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get default object"));
1140
- }
1141
-
1142
- // Set the property value
1143
- if (Params->HasField(TEXT("property_value")))
1144
- {
1145
- TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
1146
-
1147
- FString ErrorMessage;
1148
- if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, PropertyName, JsonValue, ErrorMessage))
1149
- {
1150
- // Mark the blueprint as modified
1151
- FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
1152
-
1153
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
1154
- ResultObj->SetStringField(TEXT("property"), PropertyName);
1155
- ResultObj->SetBoolField(TEXT("success"), true);
1156
- return ResultObj;
1157
- }
1158
- else
1159
- {
1160
- return FUnrealMCPCommonUtils::CreateErrorResponse(ErrorMessage);
1161
- }
1162
- }
1163
-
1164
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_value' parameter"));
1165
- }
1166
-
1167
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetStaticMeshProperties(const TSharedPtr<FJsonObject>& Params)
1168
- {
1169
- // Get required parameters
1170
- FString BlueprintName;
1171
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1172
- {
1173
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1174
- }
1175
-
1176
- FString ComponentName;
1177
- if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
1178
- {
1179
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
1180
- }
1181
-
1182
- // Find the blueprint
1183
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1184
- if (!Blueprint)
1185
- {
1186
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1187
- }
1188
-
1189
- // Find the component
1190
- USCS_Node* ComponentNode = nullptr;
1191
- for (USCS_Node* Node : Blueprint->SimpleConstructionScript->GetAllNodes())
1192
- {
1193
- if (Node && Node->GetVariableName().ToString() == ComponentName)
1194
- {
1195
- ComponentNode = Node;
1196
- break;
1197
- }
1198
- }
1199
-
1200
- if (!ComponentNode)
1201
- {
1202
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
1203
- }
1204
-
1205
- UStaticMeshComponent* MeshComponent = Cast<UStaticMeshComponent>(ComponentNode->ComponentTemplate);
1206
- if (!MeshComponent)
1207
- {
1208
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Component is not a static mesh component"));
1209
- }
1210
-
1211
- // Set static mesh properties
1212
- if (Params->HasField(TEXT("static_mesh")))
1213
- {
1214
- FString MeshPath = Params->GetStringField(TEXT("static_mesh"));
1215
- UStaticMesh* Mesh = Cast<UStaticMesh>(UEditorAssetLibrary::LoadAsset(MeshPath));
1216
- if (Mesh)
1217
- {
1218
- MeshComponent->SetStaticMesh(Mesh);
1219
- }
1220
- }
1221
-
1222
- if (Params->HasField(TEXT("material")))
1223
- {
1224
- FString MaterialPath = Params->GetStringField(TEXT("material"));
1225
- UMaterialInterface* Material = Cast<UMaterialInterface>(UEditorAssetLibrary::LoadAsset(MaterialPath));
1226
- if (Material)
1227
- {
1228
- MeshComponent->SetMaterial(0, Material);
1229
- }
1230
- }
1231
-
1232
- // Mark the blueprint as modified
1233
- FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
1234
-
1235
- TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
1236
- ResultObj->SetStringField(TEXT("component"), ComponentName);
1237
- return ResultObj;
1238
- }
1239
-
1240
- TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetPawnProperties(const TSharedPtr<FJsonObject>& Params)
1241
- {
1242
- // Get required parameters
1243
- FString BlueprintName;
1244
- if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1245
- {
1246
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1247
- }
1248
-
1249
- // Find the blueprint
1250
- UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1251
- if (!Blueprint)
1252
- {
1253
- return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1254
- }
1255
-
1256
- // Get the default object
1257
- UObject* DefaultObject = Blueprint->GeneratedClass->GetDefaultObject();
1258
- if (!DefaultObject)
1259
- {
1260
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get default object"));
1261
- }
1262
-
1263
- // Track if any properties were set successfully
1264
- bool bAnyPropertiesSet = false;
1265
- TSharedPtr<FJsonObject> ResultsObj = MakeShared<FJsonObject>();
1266
-
1267
- // Set auto possess player if specified
1268
- if (Params->HasField(TEXT("auto_possess_player")))
1269
- {
1270
- TSharedPtr<FJsonValue> AutoPossessValue = Params->Values.FindRef(TEXT("auto_possess_player"));
1271
-
1272
- FString ErrorMessage;
1273
- if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, TEXT("AutoPossessPlayer"), AutoPossessValue, ErrorMessage))
1274
- {
1275
- bAnyPropertiesSet = true;
1276
- TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1277
- PropResultObj->SetBoolField(TEXT("success"), true);
1278
- ResultsObj->SetObjectField(TEXT("AutoPossessPlayer"), PropResultObj);
1279
- }
1280
- else
1281
- {
1282
- TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1283
- PropResultObj->SetBoolField(TEXT("success"), false);
1284
- PropResultObj->SetStringField(TEXT("error"), ErrorMessage);
1285
- ResultsObj->SetObjectField(TEXT("AutoPossessPlayer"), PropResultObj);
1286
- }
1287
- }
1288
-
1289
- // Set controller rotation properties
1290
- const TCHAR* RotationProps[] = {
1291
- TEXT("bUseControllerRotationYaw"),
1292
- TEXT("bUseControllerRotationPitch"),
1293
- TEXT("bUseControllerRotationRoll")
1294
- };
1295
-
1296
- const TCHAR* ParamNames[] = {
1297
- TEXT("use_controller_rotation_yaw"),
1298
- TEXT("use_controller_rotation_pitch"),
1299
- TEXT("use_controller_rotation_roll")
1300
- };
1301
-
1302
- for (int32 i = 0; i < 3; i++)
1303
- {
1304
- if (Params->HasField(ParamNames[i]))
1305
- {
1306
- TSharedPtr<FJsonValue> Value = Params->Values.FindRef(ParamNames[i]);
1307
-
1308
- FString ErrorMessage;
1309
- if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, RotationProps[i], Value, ErrorMessage))
1310
- {
1311
- bAnyPropertiesSet = true;
1312
- TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1313
- PropResultObj->SetBoolField(TEXT("success"), true);
1314
- ResultsObj->SetObjectField(RotationProps[i], PropResultObj);
1315
- }
1316
- else
1317
- {
1318
- TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1319
- PropResultObj->SetBoolField(TEXT("success"), false);
1320
- PropResultObj->SetStringField(TEXT("error"), ErrorMessage);
1321
- ResultsObj->SetObjectField(RotationProps[i], PropResultObj);
1322
- }
1323
- }
1324
- }
1325
-
1326
- // Set can be damaged property
1327
- if (Params->HasField(TEXT("can_be_damaged")))
1328
- {
1329
- TSharedPtr<FJsonValue> Value = Params->Values.FindRef(TEXT("can_be_damaged"));
1330
-
1331
- FString ErrorMessage;
1332
- if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, TEXT("bCanBeDamaged"), Value, ErrorMessage))
1333
- {
1334
- bAnyPropertiesSet = true;
1335
- TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1336
- PropResultObj->SetBoolField(TEXT("success"), true);
1337
- ResultsObj->SetObjectField(TEXT("bCanBeDamaged"), PropResultObj);
1338
- }
1339
- else
1340
- {
1341
- TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1342
- PropResultObj->SetBoolField(TEXT("success"), false);
1343
- PropResultObj->SetStringField(TEXT("error"), ErrorMessage);
1344
- ResultsObj->SetObjectField(TEXT("bCanBeDamaged"), PropResultObj);
1345
- }
1346
- }
1347
-
1348
- // Mark the blueprint as modified if any properties were set
1349
- if (bAnyPropertiesSet)
1350
- {
1351
- FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
1352
- }
1353
- else if (ResultsObj->Values.Num() == 0)
1354
- {
1355
- // No properties were specified
1356
- return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("No properties specified to set"));
1357
- }
1358
-
1359
- TSharedPtr<FJsonObject> ResponseObj = MakeShared<FJsonObject>();
1360
- ResponseObj->SetStringField(TEXT("blueprint"), BlueprintName);
1361
- ResponseObj->SetBoolField(TEXT("success"), bAnyPropertiesSet);
1362
- ResponseObj->SetObjectField(TEXT("results"), ResultsObj);
1363
- return ResponseObj;
1364
- }
1
+ #include "Commands/UnrealMCPBlueprintCommands.h"
2
+ #include "Commands/UnrealMCPCommonUtils.h"
3
+ #include "Engine/Blueprint.h"
4
+ #include "Engine/BlueprintGeneratedClass.h"
5
+ #include "Factories/BlueprintFactory.h"
6
+ #include "EdGraphSchema_K2.h"
7
+ #include "K2Node_Event.h"
8
+ #include "K2Node_VariableGet.h"
9
+ #include "K2Node_VariableSet.h"
10
+ #include "Components/StaticMeshComponent.h"
11
+ #include "Components/BoxComponent.h"
12
+ #include "Components/SphereComponent.h"
13
+ #include "Kismet2/BlueprintEditorUtils.h"
14
+ #include "Kismet2/KismetEditorUtilities.h"
15
+ #include "Engine/SimpleConstructionScript.h"
16
+ #include "Engine/SCS_Node.h"
17
+ #include "UObject/Field.h"
18
+ #include "UObject/FieldPath.h"
19
+ #include "UObject/UObjectGlobals.h"
20
+ #include "EditorAssetLibrary.h"
21
+ #include "AssetRegistry/AssetRegistryModule.h"
22
+ #include "MessageLogModule.h"
23
+ #include "IMessageLogListing.h"
24
+ #include "GameFramework/Actor.h"
25
+ #include "GameFramework/Pawn.h"
26
+
27
+ static UClass* FindMcpClassByName(const TCHAR* ClassName)
28
+ {
29
+ #if defined(ANY_PACKAGE)
30
+ return FindObject<UClass>(ANY_PACKAGE, ClassName);
31
+ #else
32
+ return FindFirstObject<UClass>(ClassName);
33
+ #endif
34
+ }
35
+
36
+ static UClass* FindMcpClassByName(const FString& ClassName)
37
+ {
38
+ return FindMcpClassByName(*ClassName);
39
+ }
40
+
41
+ FUnrealMCPBlueprintCommands::FUnrealMCPBlueprintCommands()
42
+ {
43
+ }
44
+
45
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCommand(const FString& CommandType, const TSharedPtr<FJsonObject>& Params)
46
+ {
47
+ if (CommandType == TEXT("create_blueprint"))
48
+ {
49
+ return HandleCreateBlueprint(Params);
50
+ }
51
+ else if (CommandType == TEXT("add_component_to_blueprint"))
52
+ {
53
+ return HandleAddComponentToBlueprint(Params);
54
+ }
55
+ else if (CommandType == TEXT("set_component_property"))
56
+ {
57
+ return HandleSetComponentProperty(Params);
58
+ }
59
+ else if (CommandType == TEXT("set_physics_properties"))
60
+ {
61
+ return HandleSetPhysicsProperties(Params);
62
+ }
63
+ else if (CommandType == TEXT("compile_blueprint"))
64
+ {
65
+ return HandleCompileBlueprint(Params);
66
+ }
67
+ else if (CommandType == TEXT("compile_blueprints_all"))
68
+ {
69
+ return HandleCompileBlueprintsAll(Params);
70
+ }
71
+ else if (CommandType == TEXT("spawn_blueprint_actor"))
72
+ {
73
+ return HandleSpawnBlueprintActor(Params);
74
+ }
75
+ else if (CommandType == TEXT("set_blueprint_property"))
76
+ {
77
+ return HandleSetBlueprintProperty(Params);
78
+ }
79
+ else if (CommandType == TEXT("set_static_mesh_properties"))
80
+ {
81
+ return HandleSetStaticMeshProperties(Params);
82
+ }
83
+ else if (CommandType == TEXT("set_pawn_properties"))
84
+ {
85
+ return HandleSetPawnProperties(Params);
86
+ }
87
+
88
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Unknown blueprint command: %s"), *CommandType));
89
+ }
90
+
91
+ static bool IsSafeToCompileBlueprints(FString& OutError)
92
+ {
93
+ if (!GEditor)
94
+ {
95
+ OutError = TEXT("Editor is not available (GEditor is null).");
96
+ return false;
97
+ }
98
+ if (GEditor->PlayWorld)
99
+ {
100
+ OutError = TEXT("Cannot compile Blueprints while Play-In-Editor is running. Stop PIE and retry.");
101
+ return false;
102
+ }
103
+ return true;
104
+ }
105
+
106
+ static FString MessageSeverityToString(EMessageSeverity::Type Severity)
107
+ {
108
+ switch (Severity)
109
+ {
110
+ case EMessageSeverity::CriticalError:
111
+ case EMessageSeverity::Error:
112
+ return TEXT("error");
113
+ case EMessageSeverity::Warning:
114
+ case EMessageSeverity::PerformanceWarning:
115
+ return TEXT("warning");
116
+ case EMessageSeverity::Info:
117
+ return TEXT("info");
118
+ default:
119
+ return TEXT("info");
120
+ }
121
+ }
122
+
123
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCreateBlueprint(const TSharedPtr<FJsonObject>& Params)
124
+ {
125
+ // Get required parameters
126
+ FString BlueprintName;
127
+ if (!Params->TryGetStringField(TEXT("name"), BlueprintName) && !Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
128
+ {
129
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'name' parameter"));
130
+ }
131
+
132
+ // Check if blueprint already exists
133
+ FString PackagePath = TEXT("/Game/Blueprints/");
134
+ Params->TryGetStringField(TEXT("path"), PackagePath);
135
+ Params->TryGetStringField(TEXT("blueprint_path"), PackagePath);
136
+ Params->TryGetStringField(TEXT("packagePath"), PackagePath);
137
+ Params->TryGetStringField(TEXT("package_path"), PackagePath);
138
+
139
+ PackagePath = PackagePath.TrimStartAndEnd();
140
+ if (PackagePath.IsEmpty()) PackagePath = TEXT("/Game/Blueprints/");
141
+ if (!PackagePath.StartsWith(TEXT("/Game/")))
142
+ {
143
+ // Keep it safe + predictable: only allow /Game paths.
144
+ PackagePath = TEXT("/Game/Blueprints/");
145
+ }
146
+ if (!PackagePath.EndsWith(TEXT("/")))
147
+ {
148
+ PackagePath += TEXT("/");
149
+ }
150
+
151
+ FString AssetName = BlueprintName;
152
+ if (UEditorAssetLibrary::DoesAssetExist(PackagePath + AssetName))
153
+ {
154
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint already exists: %s"), *BlueprintName));
155
+ }
156
+
157
+ // Create the blueprint factory
158
+ UBlueprintFactory* Factory = NewObject<UBlueprintFactory>();
159
+
160
+ // Handle parent class
161
+ FString ParentClass;
162
+ Params->TryGetStringField(TEXT("parent_class"), ParentClass);
163
+
164
+ // Default to Actor if no parent class specified
165
+ UClass* SelectedParentClass = AActor::StaticClass();
166
+
167
+ // Try to find the specified parent class
168
+ if (!ParentClass.IsEmpty())
169
+ {
170
+ FString ClassName = ParentClass;
171
+ if (!ClassName.StartsWith(TEXT("A")))
172
+ {
173
+ ClassName = TEXT("A") + ClassName;
174
+ }
175
+
176
+ // First try direct StaticClass lookup for common classes
177
+ UClass* FoundClass = nullptr;
178
+ if (ClassName == TEXT("APawn"))
179
+ {
180
+ FoundClass = APawn::StaticClass();
181
+ }
182
+ else if (ClassName == TEXT("AActor"))
183
+ {
184
+ FoundClass = AActor::StaticClass();
185
+ }
186
+ else
187
+ {
188
+ // Try loading the class using LoadClass which is more reliable than FindObject
189
+ const FString ClassPath = FString::Printf(TEXT("/Script/Engine.%s"), *ClassName);
190
+ FoundClass = LoadClass<AActor>(nullptr, *ClassPath);
191
+
192
+ if (!FoundClass)
193
+ {
194
+ // Try alternate paths if not found
195
+ const FString GameClassPath = FString::Printf(TEXT("/Script/Game.%s"), *ClassName);
196
+ FoundClass = LoadClass<AActor>(nullptr, *GameClassPath);
197
+ }
198
+ }
199
+
200
+ if (FoundClass)
201
+ {
202
+ SelectedParentClass = FoundClass;
203
+ UE_LOG(LogTemp, Log, TEXT("Successfully set parent class to '%s'"), *ClassName);
204
+ }
205
+ else
206
+ {
207
+ UE_LOG(LogTemp, Warning, TEXT("Could not find specified parent class '%s' at paths: /Script/Engine.%s or /Script/Game.%s, defaulting to AActor"),
208
+ *ClassName, *ClassName, *ClassName);
209
+ }
210
+ }
211
+
212
+ Factory->ParentClass = SelectedParentClass;
213
+
214
+ // Create the blueprint
215
+ UPackage* Package = CreatePackage(*(PackagePath + AssetName));
216
+ UBlueprint* NewBlueprint = Cast<UBlueprint>(Factory->FactoryCreateNew(UBlueprint::StaticClass(), Package, *AssetName, RF_Standalone | RF_Public, nullptr, GWarn));
217
+
218
+ if (NewBlueprint)
219
+ {
220
+ // Notify the asset registry
221
+ FAssetRegistryModule::AssetCreated(NewBlueprint);
222
+
223
+ // Mark the package dirty
224
+ Package->MarkPackageDirty();
225
+
226
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
227
+ ResultObj->SetStringField(TEXT("name"), AssetName);
228
+ ResultObj->SetStringField(TEXT("path"), PackagePath + AssetName);
229
+ return ResultObj;
230
+ }
231
+
232
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create blueprint"));
233
+ }
234
+
235
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleAddComponentToBlueprint(const TSharedPtr<FJsonObject>& Params)
236
+ {
237
+ // Get required parameters
238
+ FString BlueprintName;
239
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
240
+ {
241
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
242
+ }
243
+
244
+ FString ComponentType;
245
+ if (!Params->TryGetStringField(TEXT("component_type"), ComponentType) && !Params->TryGetStringField(TEXT("type"), ComponentType))
246
+ {
247
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_type' parameter"));
248
+ }
249
+
250
+ FString ComponentName;
251
+ if (!Params->TryGetStringField(TEXT("component_name"), ComponentName) && !Params->TryGetStringField(TEXT("name"), ComponentName))
252
+ {
253
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
254
+ }
255
+
256
+ // Find the blueprint
257
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
258
+ if (!Blueprint)
259
+ {
260
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
261
+ }
262
+
263
+ // Create the component - dynamically find the component class by name
264
+ UClass* ComponentClass = nullptr;
265
+
266
+ // Try to find the class with exact name first
267
+ ComponentClass = FindMcpClassByName(ComponentType);
268
+
269
+ // If not found, try with "Component" suffix
270
+ if (!ComponentClass && !ComponentType.EndsWith(TEXT("Component")))
271
+ {
272
+ FString ComponentTypeWithSuffix = ComponentType + TEXT("Component");
273
+ ComponentClass = FindMcpClassByName(ComponentTypeWithSuffix);
274
+ }
275
+
276
+ // If still not found, try with "U" prefix
277
+ if (!ComponentClass && !ComponentType.StartsWith(TEXT("U")))
278
+ {
279
+ FString ComponentTypeWithPrefix = TEXT("U") + ComponentType;
280
+ ComponentClass = FindMcpClassByName(ComponentTypeWithPrefix);
281
+
282
+ // Try with both prefix and suffix
283
+ if (!ComponentClass && !ComponentType.EndsWith(TEXT("Component")))
284
+ {
285
+ FString ComponentTypeWithBoth = TEXT("U") + ComponentType + TEXT("Component");
286
+ ComponentClass = FindMcpClassByName(ComponentTypeWithBoth);
287
+ }
288
+ }
289
+
290
+ // Verify that the class is a valid component type
291
+ if (!ComponentClass || !ComponentClass->IsChildOf(UActorComponent::StaticClass()))
292
+ {
293
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Unknown component type: %s"), *ComponentType));
294
+ }
295
+
296
+ // Add the component to the blueprint
297
+ USCS_Node* NewNode = Blueprint->SimpleConstructionScript->CreateNode(ComponentClass, *ComponentName);
298
+ if (NewNode)
299
+ {
300
+ // Set transform if provided
301
+ USceneComponent* SceneComponent = Cast<USceneComponent>(NewNode->ComponentTemplate);
302
+ if (SceneComponent)
303
+ {
304
+ if (Params->HasField(TEXT("location")))
305
+ {
306
+ SceneComponent->SetRelativeLocation(FUnrealMCPCommonUtils::GetVectorFromJson(Params, TEXT("location")));
307
+ }
308
+ if (Params->HasField(TEXT("rotation")))
309
+ {
310
+ SceneComponent->SetRelativeRotation(FUnrealMCPCommonUtils::GetRotatorFromJson(Params, TEXT("rotation")));
311
+ }
312
+ if (Params->HasField(TEXT("scale")))
313
+ {
314
+ SceneComponent->SetRelativeScale3D(FUnrealMCPCommonUtils::GetVectorFromJson(Params, TEXT("scale")));
315
+ }
316
+ }
317
+
318
+ // Add to root if no parent specified
319
+ Blueprint->SimpleConstructionScript->AddNode(NewNode);
320
+
321
+ // Compile the blueprint
322
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
323
+
324
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
325
+ ResultObj->SetStringField(TEXT("component_name"), ComponentName);
326
+ ResultObj->SetStringField(TEXT("component_type"), ComponentType);
327
+ return ResultObj;
328
+ }
329
+
330
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to add component to blueprint"));
331
+ }
332
+
333
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetComponentProperty(const TSharedPtr<FJsonObject>& Params)
334
+ {
335
+ // Get required parameters
336
+ FString BlueprintName;
337
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
338
+ {
339
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
340
+ }
341
+
342
+ FString ComponentName;
343
+ if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
344
+ {
345
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
346
+ }
347
+
348
+ FString PropertyName;
349
+ if (!Params->TryGetStringField(TEXT("property_name"), PropertyName))
350
+ {
351
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_name' parameter"));
352
+ }
353
+
354
+ // Log all input parameters for debugging
355
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Blueprint: %s, Component: %s, Property: %s"),
356
+ *BlueprintName, *ComponentName, *PropertyName);
357
+
358
+ // Log property_value if available
359
+ if (Params->HasField(TEXT("property_value")))
360
+ {
361
+ TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
362
+ FString ValueType;
363
+
364
+ switch(JsonValue->Type)
365
+ {
366
+ case EJson::Boolean: ValueType = FString::Printf(TEXT("Boolean: %s"), JsonValue->AsBool() ? TEXT("true") : TEXT("false")); break;
367
+ case EJson::Number: ValueType = FString::Printf(TEXT("Number: %f"), JsonValue->AsNumber()); break;
368
+ case EJson::String: ValueType = FString::Printf(TEXT("String: %s"), *JsonValue->AsString()); break;
369
+ case EJson::Array: ValueType = TEXT("Array"); break;
370
+ case EJson::Object: ValueType = TEXT("Object"); break;
371
+ default: ValueType = TEXT("Unknown"); break;
372
+ }
373
+
374
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Value Type: %s"), *ValueType);
375
+ }
376
+ else
377
+ {
378
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - No property_value provided"));
379
+ }
380
+
381
+ // Find the blueprint
382
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
383
+ if (!Blueprint)
384
+ {
385
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Blueprint not found: %s"), *BlueprintName);
386
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
387
+ }
388
+ else
389
+ {
390
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Blueprint found: %s (Class: %s)"),
391
+ *BlueprintName,
392
+ Blueprint->GeneratedClass ? *Blueprint->GeneratedClass->GetName() : TEXT("NULL"));
393
+ }
394
+
395
+ // Find the component
396
+ USCS_Node* ComponentNode = nullptr;
397
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Searching for component %s in blueprint nodes"), *ComponentName);
398
+
399
+ if (!Blueprint->SimpleConstructionScript)
400
+ {
401
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - SimpleConstructionScript is NULL for blueprint %s"), *BlueprintName);
402
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid blueprint construction script"));
403
+ }
404
+
405
+ for (USCS_Node* Node : Blueprint->SimpleConstructionScript->GetAllNodes())
406
+ {
407
+ if (Node)
408
+ {
409
+ UE_LOG(LogTemp, Verbose, TEXT("SetComponentProperty - Found node: %s"), *Node->GetVariableName().ToString());
410
+ if (Node->GetVariableName().ToString() == ComponentName)
411
+ {
412
+ ComponentNode = Node;
413
+ break;
414
+ }
415
+ }
416
+ else
417
+ {
418
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Found NULL node in blueprint"));
419
+ }
420
+ }
421
+
422
+ if (!ComponentNode)
423
+ {
424
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component not found: %s"), *ComponentName);
425
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
426
+ }
427
+ else
428
+ {
429
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Component found: %s (Class: %s)"),
430
+ *ComponentName,
431
+ ComponentNode->ComponentTemplate ? *ComponentNode->ComponentTemplate->GetClass()->GetName() : TEXT("NULL"));
432
+ }
433
+
434
+ // Get the component template
435
+ UObject* ComponentTemplate = ComponentNode->ComponentTemplate;
436
+ if (!ComponentTemplate)
437
+ {
438
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component template is NULL for %s"), *ComponentName);
439
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid component template"));
440
+ }
441
+
442
+ // Check if this is a Spring Arm component and log special debug info
443
+ if (ComponentTemplate->GetClass()->GetName().Contains(TEXT("SpringArm")))
444
+ {
445
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm component detected! Class: %s"),
446
+ *ComponentTemplate->GetClass()->GetPathName());
447
+
448
+ // Log all properties of the SpringArm component class
449
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm properties:"));
450
+ for (TFieldIterator<FProperty> PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt)
451
+ {
452
+ FProperty* Prop = *PropIt;
453
+ UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
454
+ }
455
+
456
+ // Special handling for Spring Arm properties
457
+ if (Params->HasField(TEXT("property_value")))
458
+ {
459
+ TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
460
+
461
+ // Get the property using the new FField system
462
+ FProperty* Property = FindFProperty<FProperty>(ComponentTemplate->GetClass(), *PropertyName);
463
+ if (!Property)
464
+ {
465
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on SpringArm component"), *PropertyName);
466
+ return FUnrealMCPCommonUtils::CreateErrorResponse(
467
+ FString::Printf(TEXT("Property %s not found on SpringArm component"), *PropertyName));
468
+ }
469
+
470
+ // Create a scope guard to ensure property cleanup
471
+ struct FScopeGuard
472
+ {
473
+ UObject* Object;
474
+ FScopeGuard(UObject* InObject) : Object(InObject)
475
+ {
476
+ if (Object)
477
+ {
478
+ Object->Modify();
479
+ }
480
+ }
481
+ ~FScopeGuard()
482
+ {
483
+ if (Object)
484
+ {
485
+ Object->PostEditChange();
486
+ }
487
+ }
488
+ } ScopeGuard(ComponentTemplate);
489
+
490
+ bool bSuccess = false;
491
+ FString ErrorMessage;
492
+
493
+ // Handle specific Spring Arm property types
494
+ if (FFloatProperty* FloatProp = CastField<FFloatProperty>(Property))
495
+ {
496
+ if (JsonValue->Type == EJson::Number)
497
+ {
498
+ const float Value = JsonValue->AsNumber();
499
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting float property %s to %f"), *PropertyName, Value);
500
+ FloatProp->SetPropertyValue_InContainer(ComponentTemplate, Value);
501
+ bSuccess = true;
502
+ }
503
+ }
504
+ else if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Property))
505
+ {
506
+ if (JsonValue->Type == EJson::Boolean)
507
+ {
508
+ const bool Value = JsonValue->AsBool();
509
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting bool property %s to %d"), *PropertyName, Value);
510
+ BoolProp->SetPropertyValue_InContainer(ComponentTemplate, Value);
511
+ bSuccess = true;
512
+ }
513
+ }
514
+ else if (FStructProperty* StructProp = CastField<FStructProperty>(Property))
515
+ {
516
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Handling struct property %s of type %s"),
517
+ *PropertyName, *StructProp->Struct->GetName());
518
+
519
+ // Special handling for common Spring Arm struct properties
520
+ if (StructProp->Struct == TBaseStructure<FVector>::Get())
521
+ {
522
+ if (JsonValue->Type == EJson::Array)
523
+ {
524
+ const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
525
+ if (Arr.Num() == 3)
526
+ {
527
+ FVector Vec(
528
+ Arr[0]->AsNumber(),
529
+ Arr[1]->AsNumber(),
530
+ Arr[2]->AsNumber()
531
+ );
532
+ void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
533
+ StructProp->CopySingleValue(PropertyAddr, &Vec);
534
+ bSuccess = true;
535
+ }
536
+ }
537
+ }
538
+ else if (StructProp->Struct == TBaseStructure<FRotator>::Get())
539
+ {
540
+ if (JsonValue->Type == EJson::Array)
541
+ {
542
+ const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
543
+ if (Arr.Num() == 3)
544
+ {
545
+ FRotator Rot(
546
+ Arr[0]->AsNumber(),
547
+ Arr[1]->AsNumber(),
548
+ Arr[2]->AsNumber()
549
+ );
550
+ void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
551
+ StructProp->CopySingleValue(PropertyAddr, &Rot);
552
+ bSuccess = true;
553
+ }
554
+ }
555
+ }
556
+ }
557
+
558
+ if (bSuccess)
559
+ {
560
+ // Mark the blueprint as modified
561
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set SpringArm property %s"), *PropertyName);
562
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
563
+
564
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
565
+ ResultObj->SetStringField(TEXT("component"), ComponentName);
566
+ ResultObj->SetStringField(TEXT("property"), PropertyName);
567
+ ResultObj->SetBoolField(TEXT("success"), true);
568
+ return ResultObj;
569
+ }
570
+ else
571
+ {
572
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set SpringArm property %s"), *PropertyName);
573
+ return FUnrealMCPCommonUtils::CreateErrorResponse(
574
+ FString::Printf(TEXT("Failed to set SpringArm property %s"), *PropertyName));
575
+ }
576
+ }
577
+ }
578
+
579
+ // Regular property handling for non-Spring Arm components continues...
580
+
581
+ // Set the property value
582
+ if (Params->HasField(TEXT("property_value")))
583
+ {
584
+ TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
585
+
586
+ // Get the property
587
+ FProperty* Property = FindFProperty<FProperty>(ComponentTemplate->GetClass(), *PropertyName);
588
+ if (!Property)
589
+ {
590
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on component %s"),
591
+ *PropertyName, *ComponentName);
592
+
593
+ // List all available properties for this component
594
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available properties for %s:"), *ComponentName);
595
+ for (TFieldIterator<FProperty> PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt)
596
+ {
597
+ FProperty* Prop = *PropIt;
598
+ UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType());
599
+ }
600
+
601
+ return FUnrealMCPCommonUtils::CreateErrorResponse(
602
+ FString::Printf(TEXT("Property %s not found on component %s"), *PropertyName, *ComponentName));
603
+ }
604
+ else
605
+ {
606
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property found: %s (Type: %s)"),
607
+ *PropertyName, *Property->GetCPPType());
608
+ }
609
+
610
+ bool bSuccess = false;
611
+ FString ErrorMessage;
612
+
613
+ // Handle different property types
614
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Attempting to set property %s"), *PropertyName);
615
+
616
+ // Add try-catch block to catch and log any crashes
617
+ try
618
+ {
619
+ if (FStructProperty* StructProp = CastField<FStructProperty>(Property))
620
+ {
621
+ // Handle vector properties
622
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is a struct: %s"),
623
+ StructProp->Struct ? *StructProp->Struct->GetName() : TEXT("NULL"));
624
+
625
+ if (StructProp->Struct == TBaseStructure<FVector>::Get())
626
+ {
627
+ if (JsonValue->Type == EJson::Array)
628
+ {
629
+ // Handle array input [x, y, z]
630
+ const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
631
+ if (Arr.Num() == 3)
632
+ {
633
+ FVector Vec(
634
+ Arr[0]->AsNumber(),
635
+ Arr[1]->AsNumber(),
636
+ Arr[2]->AsNumber()
637
+ );
638
+ void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
639
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f)"),
640
+ Vec.X, Vec.Y, Vec.Z);
641
+ StructProp->CopySingleValue(PropertyAddr, &Vec);
642
+ bSuccess = true;
643
+ }
644
+ else
645
+ {
646
+ ErrorMessage = FString::Printf(TEXT("Vector property requires 3 values, got %d"), Arr.Num());
647
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
648
+ }
649
+ }
650
+ else if (JsonValue->Type == EJson::Number)
651
+ {
652
+ // Handle scalar input (sets all components to same value)
653
+ float Value = JsonValue->AsNumber();
654
+ FVector Vec(Value, Value, Value);
655
+ void* PropertyAddr = StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
656
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f) from scalar"),
657
+ Vec.X, Vec.Y, Vec.Z);
658
+ StructProp->CopySingleValue(PropertyAddr, &Vec);
659
+ bSuccess = true;
660
+ }
661
+ else
662
+ {
663
+ ErrorMessage = TEXT("Vector property requires either a single number or array of 3 numbers");
664
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
665
+ }
666
+ }
667
+ else
668
+ {
669
+ // Handle other struct properties using default handler
670
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic struct handler for %s"),
671
+ *PropertyName);
672
+ bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage);
673
+ if (!bSuccess)
674
+ {
675
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set struct property: %s"), *ErrorMessage);
676
+ }
677
+ }
678
+ }
679
+ else if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Property))
680
+ {
681
+ // Handle enum properties
682
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is an enum"));
683
+ if (JsonValue->Type == EJson::String)
684
+ {
685
+ FString EnumValueName = JsonValue->AsString();
686
+ UEnum* Enum = EnumProp->GetEnum();
687
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from string: %s"), *EnumValueName);
688
+
689
+ if (Enum)
690
+ {
691
+ int64 EnumValue = Enum->GetValueByNameString(EnumValueName);
692
+
693
+ if (EnumValue != INDEX_NONE)
694
+ {
695
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Found enum value: %lld"), EnumValue);
696
+ EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(
697
+ ComponentTemplate,
698
+ EnumValue
699
+ );
700
+ bSuccess = true;
701
+ }
702
+ else
703
+ {
704
+ // List all possible enum values
705
+ UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available enum values for %s:"),
706
+ *Enum->GetName());
707
+ for (int32 i = 0; i < Enum->NumEnums(); i++)
708
+ {
709
+ UE_LOG(LogTemp, Warning, TEXT(" - %s (%lld)"),
710
+ *Enum->GetNameStringByIndex(i),
711
+ Enum->GetValueByIndex(i));
712
+ }
713
+
714
+ ErrorMessage = FString::Printf(TEXT("Invalid enum value '%s' for property %s"),
715
+ *EnumValueName, *PropertyName);
716
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
717
+ }
718
+ }
719
+ else
720
+ {
721
+ ErrorMessage = TEXT("Enum object is NULL");
722
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
723
+ }
724
+ }
725
+ else if (JsonValue->Type == EJson::Number)
726
+ {
727
+ // Allow setting enum by integer value
728
+ int64 EnumValue = JsonValue->AsNumber();
729
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from number: %lld"), EnumValue);
730
+ EnumProp->GetUnderlyingProperty()->SetIntPropertyValue(
731
+ ComponentTemplate,
732
+ EnumValue
733
+ );
734
+ bSuccess = true;
735
+ }
736
+ else
737
+ {
738
+ ErrorMessage = TEXT("Enum property requires either a string name or integer value");
739
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
740
+ }
741
+ }
742
+ else if (FNumericProperty* NumericProp = CastField<FNumericProperty>(Property))
743
+ {
744
+ // Handle numeric properties
745
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is numeric: IsInteger=%d, IsFloat=%d"),
746
+ NumericProp->IsInteger(), NumericProp->IsFloatingPoint());
747
+
748
+ if (JsonValue->Type == EJson::Number)
749
+ {
750
+ double Value = JsonValue->AsNumber();
751
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting numeric value: %f"), Value);
752
+
753
+ if (NumericProp->IsInteger())
754
+ {
755
+ NumericProp->SetIntPropertyValue(ComponentTemplate, (int64)Value);
756
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set integer value: %lld"), (int64)Value);
757
+ bSuccess = true;
758
+ }
759
+ else if (NumericProp->IsFloatingPoint())
760
+ {
761
+ NumericProp->SetFloatingPointPropertyValue(ComponentTemplate, Value);
762
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set float value: %f"), Value);
763
+ bSuccess = true;
764
+ }
765
+ }
766
+ else
767
+ {
768
+ ErrorMessage = TEXT("Numeric property requires a number value");
769
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage);
770
+ }
771
+ }
772
+ else
773
+ {
774
+ // Handle all other property types using default handler
775
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic property handler for %s (Type: %s)"),
776
+ *PropertyName, *Property->GetCPPType());
777
+ bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage);
778
+ if (!bSuccess)
779
+ {
780
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property: %s"), *ErrorMessage);
781
+ }
782
+ }
783
+ }
784
+ catch (const std::exception& Ex)
785
+ {
786
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - EXCEPTION: %s"), ANSI_TO_TCHAR(Ex.what()));
787
+ return FUnrealMCPCommonUtils::CreateErrorResponse(
788
+ FString::Printf(TEXT("Exception while setting property %s: %s"), *PropertyName, ANSI_TO_TCHAR(Ex.what())));
789
+ }
790
+ catch (...)
791
+ {
792
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - UNKNOWN EXCEPTION occurred while setting property %s"), *PropertyName);
793
+ return FUnrealMCPCommonUtils::CreateErrorResponse(
794
+ FString::Printf(TEXT("Unknown exception while setting property %s"), *PropertyName));
795
+ }
796
+
797
+ if (bSuccess)
798
+ {
799
+ // Mark the blueprint as modified
800
+ UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set property %s on component %s"),
801
+ *PropertyName, *ComponentName);
802
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
803
+
804
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
805
+ ResultObj->SetStringField(TEXT("component"), ComponentName);
806
+ ResultObj->SetStringField(TEXT("property"), PropertyName);
807
+ ResultObj->SetBoolField(TEXT("success"), true);
808
+ return ResultObj;
809
+ }
810
+ else
811
+ {
812
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property %s: %s"),
813
+ *PropertyName, *ErrorMessage);
814
+ return FUnrealMCPCommonUtils::CreateErrorResponse(ErrorMessage);
815
+ }
816
+ }
817
+
818
+ UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Missing 'property_value' parameter"));
819
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_value' parameter"));
820
+ }
821
+
822
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetPhysicsProperties(const TSharedPtr<FJsonObject>& Params)
823
+ {
824
+ // Get required parameters
825
+ FString BlueprintName;
826
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
827
+ {
828
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
829
+ }
830
+
831
+ FString ComponentName;
832
+ if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
833
+ {
834
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
835
+ }
836
+
837
+ // Find the blueprint
838
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
839
+ if (!Blueprint)
840
+ {
841
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
842
+ }
843
+
844
+ // Find the component
845
+ USCS_Node* ComponentNode = nullptr;
846
+ for (USCS_Node* Node : Blueprint->SimpleConstructionScript->GetAllNodes())
847
+ {
848
+ if (Node && Node->GetVariableName().ToString() == ComponentName)
849
+ {
850
+ ComponentNode = Node;
851
+ break;
852
+ }
853
+ }
854
+
855
+ if (!ComponentNode)
856
+ {
857
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
858
+ }
859
+
860
+ UPrimitiveComponent* PrimComponent = Cast<UPrimitiveComponent>(ComponentNode->ComponentTemplate);
861
+ if (!PrimComponent)
862
+ {
863
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Component is not a primitive component"));
864
+ }
865
+
866
+ // Set physics properties
867
+ if (Params->HasField(TEXT("simulate_physics")))
868
+ {
869
+ PrimComponent->SetSimulatePhysics(Params->GetBoolField(TEXT("simulate_physics")));
870
+ }
871
+
872
+ if (Params->HasField(TEXT("mass")))
873
+ {
874
+ float Mass = Params->GetNumberField(TEXT("mass"));
875
+ // In UE5.5, use proper overrideMass instead of just scaling
876
+ PrimComponent->SetMassOverrideInKg(NAME_None, Mass);
877
+ UE_LOG(LogTemp, Display, TEXT("Set mass for component %s to %f kg"), *ComponentName, Mass);
878
+ }
879
+
880
+ if (Params->HasField(TEXT("linear_damping")))
881
+ {
882
+ PrimComponent->SetLinearDamping(Params->GetNumberField(TEXT("linear_damping")));
883
+ }
884
+
885
+ if (Params->HasField(TEXT("angular_damping")))
886
+ {
887
+ PrimComponent->SetAngularDamping(Params->GetNumberField(TEXT("angular_damping")));
888
+ }
889
+
890
+ // Mark the blueprint as modified
891
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
892
+
893
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
894
+ ResultObj->SetStringField(TEXT("component"), ComponentName);
895
+ return ResultObj;
896
+ }
897
+
898
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCompileBlueprint(const TSharedPtr<FJsonObject>& Params)
899
+ {
900
+ // Get required parameters
901
+ FString BlueprintName;
902
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
903
+ {
904
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
905
+ }
906
+
907
+ // Find the blueprint
908
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
909
+ if (!Blueprint)
910
+ {
911
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
912
+ }
913
+
914
+ // Compile the blueprint
915
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
916
+
917
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
918
+ ResultObj->SetStringField(TEXT("name"), BlueprintName);
919
+ ResultObj->SetBoolField(TEXT("compiled"), true);
920
+ return ResultObj;
921
+ }
922
+
923
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleCompileBlueprintsAll(const TSharedPtr<FJsonObject>& Params)
924
+ {
925
+ {
926
+ FString Err;
927
+ if (!IsSafeToCompileBlueprints(Err))
928
+ {
929
+ return FUnrealMCPCommonUtils::CreateErrorResponse(Err);
930
+ }
931
+ }
932
+
933
+ int32 Limit = 500;
934
+ if (Params->HasField(TEXT("limit")))
935
+ {
936
+ Limit = (int32)Params->GetNumberField(TEXT("limit"));
937
+ }
938
+ Limit = FMath::Clamp(Limit, 1, 10000);
939
+
940
+ bool bIncludeWarnings = true;
941
+ if (Params->HasField(TEXT("includeWarnings")))
942
+ {
943
+ bIncludeWarnings = Params->GetBoolField(TEXT("includeWarnings"));
944
+ }
945
+
946
+ TArray<FString> RootPaths;
947
+ if (Params->HasField(TEXT("paths")))
948
+ {
949
+ const TArray<TSharedPtr<FJsonValue>> PathsJson = Params->GetArrayField(TEXT("paths"));
950
+ for (const TSharedPtr<FJsonValue>& PathVal : PathsJson)
951
+ {
952
+ if (!PathVal.IsValid() || PathVal->Type != EJson::String) continue;
953
+ const FString P = PathVal->AsString().TrimStartAndEnd();
954
+ if (!P.IsEmpty())
955
+ {
956
+ RootPaths.Add(P);
957
+ }
958
+ }
959
+ }
960
+ if (RootPaths.Num() == 0)
961
+ {
962
+ RootPaths.Add(TEXT("/Game"));
963
+ }
964
+
965
+ FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
966
+ IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
967
+
968
+ FARFilter Filter;
969
+ Filter.bRecursivePaths = true;
970
+ #if ENGINE_MAJOR_VERSION >= 5
971
+ Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
972
+ #else
973
+ Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
974
+ #endif
975
+ for (const FString& RootPath : RootPaths)
976
+ {
977
+ const FString Normalized = RootPath.StartsWith(TEXT("/")) ? RootPath : FString(TEXT("/")) + RootPath;
978
+ Filter.PackagePaths.Add(FName(*Normalized));
979
+ }
980
+
981
+ TArray<FAssetData> Assets;
982
+ AssetRegistry.GetAssets(Filter, Assets);
983
+
984
+ int32 TotalBlueprints = 0;
985
+ int32 Compiled = 0;
986
+ int32 Failed = 0;
987
+ int32 ErrorMessages = 0;
988
+ int32 WarningMessages = 0;
989
+
990
+ FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>(TEXT("MessageLog"));
991
+ const TSharedRef<IMessageLogListing> Listing = MessageLogModule.GetLogListing(TEXT("BlueprintLog"));
992
+
993
+ TArray<TSharedPtr<FJsonValue>> Results;
994
+
995
+ for (const FAssetData& Asset : Assets)
996
+ {
997
+ if (TotalBlueprints >= Limit) break;
998
+
999
+ UBlueprint* Blueprint = Cast<UBlueprint>(Asset.GetAsset());
1000
+ if (!Blueprint) continue;
1001
+
1002
+ TotalBlueprints++;
1003
+
1004
+ Listing->ClearMessages();
1005
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1006
+
1007
+ const TArray<TSharedRef<FTokenizedMessage>>& Messages = Listing->GetFilteredMessages();
1008
+ int32 BlueprintErrors = 0;
1009
+ int32 BlueprintWarnings = 0;
1010
+ TArray<TSharedPtr<FJsonValue>> MessageArray;
1011
+
1012
+ for (const TSharedRef<FTokenizedMessage>& Message : Messages)
1013
+ {
1014
+ const EMessageSeverity::Type Severity = Message->GetSeverity();
1015
+ const bool bIsWarning =
1016
+ Severity == EMessageSeverity::Warning || Severity == EMessageSeverity::PerformanceWarning;
1017
+ const bool bIsError = Severity == EMessageSeverity::Error || Severity == EMessageSeverity::CriticalError;
1018
+
1019
+ if (bIsError) BlueprintErrors++;
1020
+ if (bIsWarning) BlueprintWarnings++;
1021
+
1022
+ if (!bIncludeWarnings && bIsWarning) continue;
1023
+
1024
+ TSharedPtr<FJsonObject> Msg = MakeShared<FJsonObject>();
1025
+ Msg->SetStringField(TEXT("severity"), MessageSeverityToString(Severity));
1026
+ Msg->SetStringField(TEXT("message"), Message->ToText().ToString());
1027
+ MessageArray.Add(MakeShared<FJsonValueObject>(Msg));
1028
+ }
1029
+
1030
+ ErrorMessages += BlueprintErrors;
1031
+ WarningMessages += BlueprintWarnings;
1032
+
1033
+ const bool bHadErrors = Blueprint->Status == BS_Error;
1034
+ if (bHadErrors) Failed++; else Compiled++;
1035
+
1036
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
1037
+ Entry->SetStringField(TEXT("name"), Blueprint->GetName());
1038
+ Entry->SetStringField(TEXT("objectPath"), Asset.GetObjectPathString());
1039
+ Entry->SetBoolField(TEXT("ok"), !bHadErrors);
1040
+ Entry->SetNumberField(TEXT("errors"), BlueprintErrors);
1041
+ Entry->SetNumberField(TEXT("warnings"), BlueprintWarnings);
1042
+ Entry->SetArrayField(TEXT("messages"), MessageArray);
1043
+ Results.Add(MakeShared<FJsonValueObject>(Entry));
1044
+ }
1045
+
1046
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
1047
+ ResultObj->SetNumberField(TEXT("found"), Assets.Num());
1048
+ ResultObj->SetNumberField(TEXT("compiled"), Compiled);
1049
+ ResultObj->SetNumberField(TEXT("failed"), Failed);
1050
+ ResultObj->SetNumberField(TEXT("errors"), ErrorMessages);
1051
+ ResultObj->SetNumberField(TEXT("warnings"), WarningMessages);
1052
+ ResultObj->SetArrayField(TEXT("results"), Results);
1053
+ ResultObj->SetBoolField(TEXT("truncated"), TotalBlueprints < Assets.Num() && TotalBlueprints >= Limit);
1054
+ return ResultObj;
1055
+ }
1056
+
1057
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSpawnBlueprintActor(const TSharedPtr<FJsonObject>& Params)
1058
+ {
1059
+ // Get required parameters
1060
+ FString BlueprintName;
1061
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1062
+ {
1063
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1064
+ }
1065
+
1066
+ FString ActorName;
1067
+ if (!Params->TryGetStringField(TEXT("actor_name"), ActorName))
1068
+ {
1069
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'actor_name' parameter"));
1070
+ }
1071
+
1072
+ // Find the blueprint
1073
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1074
+ if (!Blueprint)
1075
+ {
1076
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1077
+ }
1078
+
1079
+ // Get transform parameters
1080
+ FVector Location(0.0f, 0.0f, 0.0f);
1081
+ FRotator Rotation(0.0f, 0.0f, 0.0f);
1082
+
1083
+ if (Params->HasField(TEXT("location")))
1084
+ {
1085
+ Location = FUnrealMCPCommonUtils::GetVectorFromJson(Params, TEXT("location"));
1086
+ }
1087
+ if (Params->HasField(TEXT("rotation")))
1088
+ {
1089
+ Rotation = FUnrealMCPCommonUtils::GetRotatorFromJson(Params, TEXT("rotation"));
1090
+ }
1091
+
1092
+ // Spawn the actor
1093
+ UWorld* World = GEditor->GetEditorWorldContext().World();
1094
+ if (!World)
1095
+ {
1096
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get editor world"));
1097
+ }
1098
+
1099
+ FTransform SpawnTransform;
1100
+ SpawnTransform.SetLocation(Location);
1101
+ SpawnTransform.SetRotation(FQuat(Rotation));
1102
+
1103
+ AActor* NewActor = World->SpawnActor<AActor>(Blueprint->GeneratedClass, SpawnTransform);
1104
+ if (NewActor)
1105
+ {
1106
+ NewActor->SetActorLabel(*ActorName);
1107
+ return FUnrealMCPCommonUtils::ActorToJsonObject(NewActor, true);
1108
+ }
1109
+
1110
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to spawn blueprint actor"));
1111
+ }
1112
+
1113
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetBlueprintProperty(const TSharedPtr<FJsonObject>& Params)
1114
+ {
1115
+ // Get required parameters
1116
+ FString BlueprintName;
1117
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1118
+ {
1119
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1120
+ }
1121
+
1122
+ FString PropertyName;
1123
+ if (!Params->TryGetStringField(TEXT("property_name"), PropertyName))
1124
+ {
1125
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_name' parameter"));
1126
+ }
1127
+
1128
+ // Find the blueprint
1129
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1130
+ if (!Blueprint)
1131
+ {
1132
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1133
+ }
1134
+
1135
+ // Get the default object
1136
+ UObject* DefaultObject = Blueprint->GeneratedClass->GetDefaultObject();
1137
+ if (!DefaultObject)
1138
+ {
1139
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get default object"));
1140
+ }
1141
+
1142
+ // Set the property value
1143
+ if (Params->HasField(TEXT("property_value")))
1144
+ {
1145
+ TSharedPtr<FJsonValue> JsonValue = Params->Values.FindRef(TEXT("property_value"));
1146
+
1147
+ FString ErrorMessage;
1148
+ if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, PropertyName, JsonValue, ErrorMessage))
1149
+ {
1150
+ // Mark the blueprint as modified
1151
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
1152
+
1153
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
1154
+ ResultObj->SetStringField(TEXT("property"), PropertyName);
1155
+ ResultObj->SetBoolField(TEXT("success"), true);
1156
+ return ResultObj;
1157
+ }
1158
+ else
1159
+ {
1160
+ return FUnrealMCPCommonUtils::CreateErrorResponse(ErrorMessage);
1161
+ }
1162
+ }
1163
+
1164
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_value' parameter"));
1165
+ }
1166
+
1167
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetStaticMeshProperties(const TSharedPtr<FJsonObject>& Params)
1168
+ {
1169
+ // Get required parameters
1170
+ FString BlueprintName;
1171
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1172
+ {
1173
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1174
+ }
1175
+
1176
+ FString ComponentName;
1177
+ if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
1178
+ {
1179
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
1180
+ }
1181
+
1182
+ // Find the blueprint
1183
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1184
+ if (!Blueprint)
1185
+ {
1186
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1187
+ }
1188
+
1189
+ // Find the component
1190
+ USCS_Node* ComponentNode = nullptr;
1191
+ for (USCS_Node* Node : Blueprint->SimpleConstructionScript->GetAllNodes())
1192
+ {
1193
+ if (Node && Node->GetVariableName().ToString() == ComponentName)
1194
+ {
1195
+ ComponentNode = Node;
1196
+ break;
1197
+ }
1198
+ }
1199
+
1200
+ if (!ComponentNode)
1201
+ {
1202
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName));
1203
+ }
1204
+
1205
+ UStaticMeshComponent* MeshComponent = Cast<UStaticMeshComponent>(ComponentNode->ComponentTemplate);
1206
+ if (!MeshComponent)
1207
+ {
1208
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Component is not a static mesh component"));
1209
+ }
1210
+
1211
+ // Set static mesh properties
1212
+ if (Params->HasField(TEXT("static_mesh")))
1213
+ {
1214
+ FString MeshPath = Params->GetStringField(TEXT("static_mesh"));
1215
+ UStaticMesh* Mesh = Cast<UStaticMesh>(UEditorAssetLibrary::LoadAsset(MeshPath));
1216
+ if (Mesh)
1217
+ {
1218
+ MeshComponent->SetStaticMesh(Mesh);
1219
+ }
1220
+ }
1221
+
1222
+ if (Params->HasField(TEXT("material")))
1223
+ {
1224
+ FString MaterialPath = Params->GetStringField(TEXT("material"));
1225
+ UMaterialInterface* Material = Cast<UMaterialInterface>(UEditorAssetLibrary::LoadAsset(MaterialPath));
1226
+ if (Material)
1227
+ {
1228
+ MeshComponent->SetMaterial(0, Material);
1229
+ }
1230
+ }
1231
+
1232
+ // Mark the blueprint as modified
1233
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
1234
+
1235
+ TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
1236
+ ResultObj->SetStringField(TEXT("component"), ComponentName);
1237
+ return ResultObj;
1238
+ }
1239
+
1240
+ TSharedPtr<FJsonObject> FUnrealMCPBlueprintCommands::HandleSetPawnProperties(const TSharedPtr<FJsonObject>& Params)
1241
+ {
1242
+ // Get required parameters
1243
+ FString BlueprintName;
1244
+ if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
1245
+ {
1246
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
1247
+ }
1248
+
1249
+ // Find the blueprint
1250
+ UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
1251
+ if (!Blueprint)
1252
+ {
1253
+ return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
1254
+ }
1255
+
1256
+ // Get the default object
1257
+ UObject* DefaultObject = Blueprint->GeneratedClass->GetDefaultObject();
1258
+ if (!DefaultObject)
1259
+ {
1260
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get default object"));
1261
+ }
1262
+
1263
+ // Track if any properties were set successfully
1264
+ bool bAnyPropertiesSet = false;
1265
+ TSharedPtr<FJsonObject> ResultsObj = MakeShared<FJsonObject>();
1266
+
1267
+ // Set auto possess player if specified
1268
+ if (Params->HasField(TEXT("auto_possess_player")))
1269
+ {
1270
+ TSharedPtr<FJsonValue> AutoPossessValue = Params->Values.FindRef(TEXT("auto_possess_player"));
1271
+
1272
+ FString ErrorMessage;
1273
+ if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, TEXT("AutoPossessPlayer"), AutoPossessValue, ErrorMessage))
1274
+ {
1275
+ bAnyPropertiesSet = true;
1276
+ TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1277
+ PropResultObj->SetBoolField(TEXT("success"), true);
1278
+ ResultsObj->SetObjectField(TEXT("AutoPossessPlayer"), PropResultObj);
1279
+ }
1280
+ else
1281
+ {
1282
+ TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1283
+ PropResultObj->SetBoolField(TEXT("success"), false);
1284
+ PropResultObj->SetStringField(TEXT("error"), ErrorMessage);
1285
+ ResultsObj->SetObjectField(TEXT("AutoPossessPlayer"), PropResultObj);
1286
+ }
1287
+ }
1288
+
1289
+ // Set controller rotation properties
1290
+ const TCHAR* RotationProps[] = {
1291
+ TEXT("bUseControllerRotationYaw"),
1292
+ TEXT("bUseControllerRotationPitch"),
1293
+ TEXT("bUseControllerRotationRoll")
1294
+ };
1295
+
1296
+ const TCHAR* ParamNames[] = {
1297
+ TEXT("use_controller_rotation_yaw"),
1298
+ TEXT("use_controller_rotation_pitch"),
1299
+ TEXT("use_controller_rotation_roll")
1300
+ };
1301
+
1302
+ for (int32 i = 0; i < 3; i++)
1303
+ {
1304
+ if (Params->HasField(ParamNames[i]))
1305
+ {
1306
+ TSharedPtr<FJsonValue> Value = Params->Values.FindRef(ParamNames[i]);
1307
+
1308
+ FString ErrorMessage;
1309
+ if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, RotationProps[i], Value, ErrorMessage))
1310
+ {
1311
+ bAnyPropertiesSet = true;
1312
+ TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1313
+ PropResultObj->SetBoolField(TEXT("success"), true);
1314
+ ResultsObj->SetObjectField(RotationProps[i], PropResultObj);
1315
+ }
1316
+ else
1317
+ {
1318
+ TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1319
+ PropResultObj->SetBoolField(TEXT("success"), false);
1320
+ PropResultObj->SetStringField(TEXT("error"), ErrorMessage);
1321
+ ResultsObj->SetObjectField(RotationProps[i], PropResultObj);
1322
+ }
1323
+ }
1324
+ }
1325
+
1326
+ // Set can be damaged property
1327
+ if (Params->HasField(TEXT("can_be_damaged")))
1328
+ {
1329
+ TSharedPtr<FJsonValue> Value = Params->Values.FindRef(TEXT("can_be_damaged"));
1330
+
1331
+ FString ErrorMessage;
1332
+ if (FUnrealMCPCommonUtils::SetObjectProperty(DefaultObject, TEXT("bCanBeDamaged"), Value, ErrorMessage))
1333
+ {
1334
+ bAnyPropertiesSet = true;
1335
+ TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1336
+ PropResultObj->SetBoolField(TEXT("success"), true);
1337
+ ResultsObj->SetObjectField(TEXT("bCanBeDamaged"), PropResultObj);
1338
+ }
1339
+ else
1340
+ {
1341
+ TSharedPtr<FJsonObject> PropResultObj = MakeShared<FJsonObject>();
1342
+ PropResultObj->SetBoolField(TEXT("success"), false);
1343
+ PropResultObj->SetStringField(TEXT("error"), ErrorMessage);
1344
+ ResultsObj->SetObjectField(TEXT("bCanBeDamaged"), PropResultObj);
1345
+ }
1346
+ }
1347
+
1348
+ // Mark the blueprint as modified if any properties were set
1349
+ if (bAnyPropertiesSet)
1350
+ {
1351
+ FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
1352
+ }
1353
+ else if (ResultsObj->Values.Num() == 0)
1354
+ {
1355
+ // No properties were specified
1356
+ return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("No properties specified to set"));
1357
+ }
1358
+
1359
+ TSharedPtr<FJsonObject> ResponseObj = MakeShared<FJsonObject>();
1360
+ ResponseObj->SetStringField(TEXT("blueprint"), BlueprintName);
1361
+ ResponseObj->SetBoolField(TEXT("success"), bAnyPropertiesSet);
1362
+ ResponseObj->SetObjectField(TEXT("results"), ResultsObj);
1363
+ return ResponseObj;
1364
+ }