flockbay 0.10.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/bin/flockbay-mcp.mjs +56 -0
- package/bin/flockbay.mjs +78 -0
- package/dist/codex/flockbayMcpStdioBridge.cjs +383 -0
- package/dist/codex/flockbayMcpStdioBridge.d.cts +2 -0
- package/dist/codex/flockbayMcpStdioBridge.d.mts +2 -0
- package/dist/codex/flockbayMcpStdioBridge.mjs +381 -0
- package/dist/flockbayScreenshotGate-DJX3Is5d.mjs +136 -0
- package/dist/flockbayScreenshotGate-DkxU24cR.cjs +138 -0
- package/dist/index--o4BPz5o.cjs +10311 -0
- package/dist/index-CUp3juDS.mjs +10268 -0
- package/dist/index.cjs +43 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +40 -0
- package/dist/lib.cjs +33 -0
- package/dist/lib.d.cts +957 -0
- package/dist/lib.d.mts +957 -0
- package/dist/lib.mjs +23 -0
- package/dist/runCodex-D3eT-TvB.cjs +3449 -0
- package/dist/runCodex-o6PCbHQ7.mjs +3446 -0
- package/dist/runGemini-Bt0oEj_g.mjs +3183 -0
- package/dist/runGemini-CBxZp6I7.cjs +3185 -0
- package/dist/types-C-jnUdn_.cjs +4498 -0
- package/dist/types-DGd6ea2Z.mjs +4450 -0
- package/kits/kit.open_world/kit.json +59 -0
- package/package.json +130 -0
- package/scripts/claude_local_launcher.cjs +73 -0
- package/scripts/claude_remote_launcher.cjs +16 -0
- package/scripts/claude_version_utils.cjs +391 -0
- package/scripts/ripgrep_launcher.cjs +33 -0
- package/scripts/session_hook_forwarder.cjs +49 -0
- package/scripts/test-codex-abort-history.mjs +77 -0
- package/scripts/unpack-tools.cjs +222 -0
- package/tools/licenses/difftastic-LICENSE +21 -0
- package/tools/licenses/ripgrep-LICENSE +3 -0
- package/tools/unreal-mcp/UPSTREAM_VERSION.md +8 -0
- package/tools/unreal-mcp/upstream/Docs/README.md +8 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/README.md +7 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/actor_tools.md +184 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/blueprint_tools.md +268 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/editor_tools.md +104 -0
- package/tools/unreal-mcp/upstream/Docs/Tools/node_tools.md +274 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Config/FilterPlugin.ini +8 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp +1160 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp +924 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp +709 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +896 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPProjectCommands.cpp +72 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/Commands/UnrealMCPUMGCommands.cpp +544 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/MCPServerRunnable.cpp +321 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/UnrealMCPBridge.cpp +419 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Private/UnrealMCPModule.cpp +21 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintCommands.h +34 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPBlueprintNodeCommands.h +27 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPCommonUtils.h +59 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPEditorCommands.h +40 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPProjectCommands.h +20 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/Commands/UnrealMCPUMGCommands.h +82 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/MCPServerRunnable.h +34 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/UnrealMCPBridge.h +64 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/Public/UnrealMCPModule.h +22 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/Source/UnrealMCP/UnrealMCP.Build.cs +78 -0
- package/tools/unreal-mcp/upstream/MCPGameProject/Plugins/UnrealMCP/UnrealMCP.uplugin +36 -0
- package/tools/unreal-mcp/upstream/Python/README.md +40 -0
- package/tools/unreal-mcp/upstream/Python/pyproject.toml +22 -0
- package/tools/unreal-mcp/upstream/Python/scripts/actors/test_cube.py +203 -0
- package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_blueprints_with_different_components.py +497 -0
- package/tools/unreal-mcp/upstream/Python/scripts/blueprints/test_create_and_spawn_cube_blueprint.py +194 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_component_reference.py +267 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_create_bird_blueprint_with_input_and_camera.py +618 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_input_mapping.py +366 -0
- package/tools/unreal-mcp/upstream/Python/scripts/node/test_physics_variables.py +390 -0
- package/tools/unreal-mcp/upstream/Python/tools/blueprint_tools.py +420 -0
- package/tools/unreal-mcp/upstream/Python/tools/editor_tools.py +369 -0
- package/tools/unreal-mcp/upstream/Python/tools/node_tools.py +430 -0
- package/tools/unreal-mcp/upstream/Python/tools/project_tools.py +64 -0
- package/tools/unreal-mcp/upstream/Python/tools/umg_tools.py +333 -0
- package/tools/unreal-mcp/upstream/Python/unreal_mcp_server.py +398 -0
- package/tools/unreal-mcp/upstream/Python/uv.lock +521 -0
|
@@ -0,0 +1,924 @@
|
|
|
1
|
+
#include "Commands/UnrealMCPBlueprintNodeCommands.h"
|
|
2
|
+
#include "Commands/UnrealMCPCommonUtils.h"
|
|
3
|
+
#include "Engine/Blueprint.h"
|
|
4
|
+
#include "Engine/BlueprintGeneratedClass.h"
|
|
5
|
+
#include "EdGraph/EdGraph.h"
|
|
6
|
+
#include "EdGraph/EdGraphNode.h"
|
|
7
|
+
#include "EdGraph/EdGraphPin.h"
|
|
8
|
+
#include "K2Node_Event.h"
|
|
9
|
+
#include "K2Node_CallFunction.h"
|
|
10
|
+
#include "K2Node_VariableGet.h"
|
|
11
|
+
#include "K2Node_InputAction.h"
|
|
12
|
+
#include "K2Node_Self.h"
|
|
13
|
+
#include "Kismet2/BlueprintEditorUtils.h"
|
|
14
|
+
#include "Kismet2/KismetEditorUtilities.h"
|
|
15
|
+
#include "GameFramework/InputSettings.h"
|
|
16
|
+
#include "Camera/CameraActor.h"
|
|
17
|
+
#include "Kismet/GameplayStatics.h"
|
|
18
|
+
#include "EdGraphSchema_K2.h"
|
|
19
|
+
|
|
20
|
+
// Declare the log category
|
|
21
|
+
DEFINE_LOG_CATEGORY_STATIC(LogUnrealMCP, Log, All);
|
|
22
|
+
|
|
23
|
+
FUnrealMCPBlueprintNodeCommands::FUnrealMCPBlueprintNodeCommands()
|
|
24
|
+
{
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleCommand(const FString& CommandType, const TSharedPtr<FJsonObject>& Params)
|
|
28
|
+
{
|
|
29
|
+
if (CommandType == TEXT("connect_blueprint_nodes"))
|
|
30
|
+
{
|
|
31
|
+
return HandleConnectBlueprintNodes(Params);
|
|
32
|
+
}
|
|
33
|
+
else if (CommandType == TEXT("add_blueprint_get_self_component_reference"))
|
|
34
|
+
{
|
|
35
|
+
return HandleAddBlueprintGetSelfComponentReference(Params);
|
|
36
|
+
}
|
|
37
|
+
else if (CommandType == TEXT("add_blueprint_event_node"))
|
|
38
|
+
{
|
|
39
|
+
return HandleAddBlueprintEvent(Params);
|
|
40
|
+
}
|
|
41
|
+
else if (CommandType == TEXT("add_blueprint_function_node"))
|
|
42
|
+
{
|
|
43
|
+
return HandleAddBlueprintFunctionCall(Params);
|
|
44
|
+
}
|
|
45
|
+
else if (CommandType == TEXT("add_blueprint_variable"))
|
|
46
|
+
{
|
|
47
|
+
return HandleAddBlueprintVariable(Params);
|
|
48
|
+
}
|
|
49
|
+
else if (CommandType == TEXT("add_blueprint_input_action_node"))
|
|
50
|
+
{
|
|
51
|
+
return HandleAddBlueprintInputActionNode(Params);
|
|
52
|
+
}
|
|
53
|
+
else if (CommandType == TEXT("add_blueprint_self_reference"))
|
|
54
|
+
{
|
|
55
|
+
return HandleAddBlueprintSelfReference(Params);
|
|
56
|
+
}
|
|
57
|
+
else if (CommandType == TEXT("find_blueprint_nodes"))
|
|
58
|
+
{
|
|
59
|
+
return HandleFindBlueprintNodes(Params);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Unknown blueprint node command: %s"), *CommandType));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleConnectBlueprintNodes(const TSharedPtr<FJsonObject>& Params)
|
|
66
|
+
{
|
|
67
|
+
// Get required parameters
|
|
68
|
+
FString BlueprintName;
|
|
69
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
70
|
+
{
|
|
71
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
FString SourceNodeId;
|
|
75
|
+
if (!Params->TryGetStringField(TEXT("source_node_id"), SourceNodeId))
|
|
76
|
+
{
|
|
77
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'source_node_id' parameter"));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
FString TargetNodeId;
|
|
81
|
+
if (!Params->TryGetStringField(TEXT("target_node_id"), TargetNodeId))
|
|
82
|
+
{
|
|
83
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'target_node_id' parameter"));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
FString SourcePinName;
|
|
87
|
+
if (!Params->TryGetStringField(TEXT("source_pin"), SourcePinName))
|
|
88
|
+
{
|
|
89
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'source_pin' parameter"));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
FString TargetPinName;
|
|
93
|
+
if (!Params->TryGetStringField(TEXT("target_pin"), TargetPinName))
|
|
94
|
+
{
|
|
95
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'target_pin' parameter"));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Find the blueprint
|
|
99
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
100
|
+
if (!Blueprint)
|
|
101
|
+
{
|
|
102
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Get the event graph
|
|
106
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
107
|
+
if (!EventGraph)
|
|
108
|
+
{
|
|
109
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Find the nodes
|
|
113
|
+
UEdGraphNode* SourceNode = nullptr;
|
|
114
|
+
UEdGraphNode* TargetNode = nullptr;
|
|
115
|
+
for (UEdGraphNode* Node : EventGraph->Nodes)
|
|
116
|
+
{
|
|
117
|
+
if (Node->NodeGuid.ToString() == SourceNodeId)
|
|
118
|
+
{
|
|
119
|
+
SourceNode = Node;
|
|
120
|
+
}
|
|
121
|
+
else if (Node->NodeGuid.ToString() == TargetNodeId)
|
|
122
|
+
{
|
|
123
|
+
TargetNode = Node;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!SourceNode || !TargetNode)
|
|
128
|
+
{
|
|
129
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Source or target node not found"));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Connect the nodes
|
|
133
|
+
if (FUnrealMCPCommonUtils::ConnectGraphNodes(EventGraph, SourceNode, SourcePinName, TargetNode, TargetPinName))
|
|
134
|
+
{
|
|
135
|
+
// Mark the blueprint as modified
|
|
136
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
137
|
+
|
|
138
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
139
|
+
ResultObj->SetStringField(TEXT("source_node_id"), SourceNodeId);
|
|
140
|
+
ResultObj->SetStringField(TEXT("target_node_id"), TargetNodeId);
|
|
141
|
+
return ResultObj;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to connect nodes"));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintGetSelfComponentReference(const TSharedPtr<FJsonObject>& Params)
|
|
148
|
+
{
|
|
149
|
+
// Get required parameters
|
|
150
|
+
FString BlueprintName;
|
|
151
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
152
|
+
{
|
|
153
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
FString ComponentName;
|
|
157
|
+
if (!Params->TryGetStringField(TEXT("component_name"), ComponentName))
|
|
158
|
+
{
|
|
159
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'component_name' parameter"));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Get position parameters (optional)
|
|
163
|
+
FVector2D NodePosition(0.0f, 0.0f);
|
|
164
|
+
if (Params->HasField(TEXT("node_position")))
|
|
165
|
+
{
|
|
166
|
+
NodePosition = FUnrealMCPCommonUtils::GetVector2DFromJson(Params, TEXT("node_position"));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Find the blueprint
|
|
170
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
171
|
+
if (!Blueprint)
|
|
172
|
+
{
|
|
173
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Get the event graph
|
|
177
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
178
|
+
if (!EventGraph)
|
|
179
|
+
{
|
|
180
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// We'll skip component verification since the GetAllNodes API may have changed in UE5.5
|
|
184
|
+
|
|
185
|
+
// Create the variable get node directly
|
|
186
|
+
UK2Node_VariableGet* GetComponentNode = NewObject<UK2Node_VariableGet>(EventGraph);
|
|
187
|
+
if (!GetComponentNode)
|
|
188
|
+
{
|
|
189
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create get component node"));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Set up the variable reference properly for UE5.5
|
|
193
|
+
FMemberReference& VarRef = GetComponentNode->VariableReference;
|
|
194
|
+
VarRef.SetSelfMember(FName(*ComponentName));
|
|
195
|
+
|
|
196
|
+
// Set node position
|
|
197
|
+
GetComponentNode->NodePosX = NodePosition.X;
|
|
198
|
+
GetComponentNode->NodePosY = NodePosition.Y;
|
|
199
|
+
|
|
200
|
+
// Add to graph
|
|
201
|
+
EventGraph->AddNode(GetComponentNode);
|
|
202
|
+
GetComponentNode->CreateNewGuid();
|
|
203
|
+
GetComponentNode->PostPlacedNewNode();
|
|
204
|
+
GetComponentNode->AllocateDefaultPins();
|
|
205
|
+
|
|
206
|
+
// Explicitly reconstruct node for UE5.5
|
|
207
|
+
GetComponentNode->ReconstructNode();
|
|
208
|
+
|
|
209
|
+
// Mark the blueprint as modified
|
|
210
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
211
|
+
|
|
212
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
213
|
+
ResultObj->SetStringField(TEXT("node_id"), GetComponentNode->NodeGuid.ToString());
|
|
214
|
+
return ResultObj;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintEvent(const TSharedPtr<FJsonObject>& Params)
|
|
218
|
+
{
|
|
219
|
+
// Get required parameters
|
|
220
|
+
FString BlueprintName;
|
|
221
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
222
|
+
{
|
|
223
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
FString EventName;
|
|
227
|
+
if (!Params->TryGetStringField(TEXT("event_name"), EventName))
|
|
228
|
+
{
|
|
229
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'event_name' parameter"));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Get position parameters (optional)
|
|
233
|
+
FVector2D NodePosition(0.0f, 0.0f);
|
|
234
|
+
if (Params->HasField(TEXT("node_position")))
|
|
235
|
+
{
|
|
236
|
+
NodePosition = FUnrealMCPCommonUtils::GetVector2DFromJson(Params, TEXT("node_position"));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Find the blueprint
|
|
240
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
241
|
+
if (!Blueprint)
|
|
242
|
+
{
|
|
243
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Get the event graph
|
|
247
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
248
|
+
if (!EventGraph)
|
|
249
|
+
{
|
|
250
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Create the event node
|
|
254
|
+
UK2Node_Event* EventNode = FUnrealMCPCommonUtils::CreateEventNode(EventGraph, EventName, NodePosition);
|
|
255
|
+
if (!EventNode)
|
|
256
|
+
{
|
|
257
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create event node"));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Mark the blueprint as modified
|
|
261
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
262
|
+
|
|
263
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
264
|
+
ResultObj->SetStringField(TEXT("node_id"), EventNode->NodeGuid.ToString());
|
|
265
|
+
return ResultObj;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunctionCall(const TSharedPtr<FJsonObject>& Params)
|
|
269
|
+
{
|
|
270
|
+
// Get required parameters
|
|
271
|
+
FString BlueprintName;
|
|
272
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
273
|
+
{
|
|
274
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
FString FunctionName;
|
|
278
|
+
if (!Params->TryGetStringField(TEXT("function_name"), FunctionName))
|
|
279
|
+
{
|
|
280
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'function_name' parameter"));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Get position parameters (optional)
|
|
284
|
+
FVector2D NodePosition(0.0f, 0.0f);
|
|
285
|
+
if (Params->HasField(TEXT("node_position")))
|
|
286
|
+
{
|
|
287
|
+
NodePosition = FUnrealMCPCommonUtils::GetVector2DFromJson(Params, TEXT("node_position"));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Check for target parameter (optional)
|
|
291
|
+
FString Target;
|
|
292
|
+
Params->TryGetStringField(TEXT("target"), Target);
|
|
293
|
+
|
|
294
|
+
// Find the blueprint
|
|
295
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
296
|
+
if (!Blueprint)
|
|
297
|
+
{
|
|
298
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Get the event graph
|
|
302
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
303
|
+
if (!EventGraph)
|
|
304
|
+
{
|
|
305
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Find the function
|
|
309
|
+
UFunction* Function = nullptr;
|
|
310
|
+
UK2Node_CallFunction* FunctionNode = nullptr;
|
|
311
|
+
|
|
312
|
+
// Add extensive logging for debugging
|
|
313
|
+
UE_LOG(LogTemp, Display, TEXT("Looking for function '%s' in target '%s'"),
|
|
314
|
+
*FunctionName, Target.IsEmpty() ? TEXT("Blueprint") : *Target);
|
|
315
|
+
|
|
316
|
+
// Check if we have a target class specified
|
|
317
|
+
if (!Target.IsEmpty())
|
|
318
|
+
{
|
|
319
|
+
// Try to find the target class
|
|
320
|
+
UClass* TargetClass = nullptr;
|
|
321
|
+
|
|
322
|
+
// First try without a prefix
|
|
323
|
+
TargetClass = FindObject<UClass>(ANY_PACKAGE, *Target);
|
|
324
|
+
UE_LOG(LogTemp, Display, TEXT("Tried to find class '%s': %s"),
|
|
325
|
+
*Target, TargetClass ? TEXT("Found") : TEXT("Not found"));
|
|
326
|
+
|
|
327
|
+
// If not found, try with U prefix (common convention for UE classes)
|
|
328
|
+
if (!TargetClass && !Target.StartsWith(TEXT("U")))
|
|
329
|
+
{
|
|
330
|
+
FString TargetWithPrefix = FString(TEXT("U")) + Target;
|
|
331
|
+
TargetClass = FindObject<UClass>(ANY_PACKAGE, *TargetWithPrefix);
|
|
332
|
+
UE_LOG(LogTemp, Display, TEXT("Tried to find class '%s': %s"),
|
|
333
|
+
*TargetWithPrefix, TargetClass ? TEXT("Found") : TEXT("Not found"));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// If still not found, try with common component names
|
|
337
|
+
if (!TargetClass)
|
|
338
|
+
{
|
|
339
|
+
// Try some common component class names
|
|
340
|
+
TArray<FString> PossibleClassNames;
|
|
341
|
+
PossibleClassNames.Add(FString(TEXT("U")) + Target + TEXT("Component"));
|
|
342
|
+
PossibleClassNames.Add(Target + TEXT("Component"));
|
|
343
|
+
|
|
344
|
+
for (const FString& ClassName : PossibleClassNames)
|
|
345
|
+
{
|
|
346
|
+
TargetClass = FindObject<UClass>(ANY_PACKAGE, *ClassName);
|
|
347
|
+
if (TargetClass)
|
|
348
|
+
{
|
|
349
|
+
UE_LOG(LogTemp, Display, TEXT("Found class using alternative name '%s'"), *ClassName);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Special case handling for common classes like UGameplayStatics
|
|
356
|
+
if (!TargetClass && Target == TEXT("UGameplayStatics"))
|
|
357
|
+
{
|
|
358
|
+
// For UGameplayStatics, use a direct reference to known class
|
|
359
|
+
TargetClass = FindObject<UClass>(ANY_PACKAGE, TEXT("UGameplayStatics"));
|
|
360
|
+
if (!TargetClass)
|
|
361
|
+
{
|
|
362
|
+
// Try loading it from its known package
|
|
363
|
+
TargetClass = LoadObject<UClass>(nullptr, TEXT("/Script/Engine.GameplayStatics"));
|
|
364
|
+
UE_LOG(LogTemp, Display, TEXT("Explicitly loading GameplayStatics: %s"),
|
|
365
|
+
TargetClass ? TEXT("Success") : TEXT("Failed"));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// If we found a target class, look for the function there
|
|
370
|
+
if (TargetClass)
|
|
371
|
+
{
|
|
372
|
+
UE_LOG(LogTemp, Display, TEXT("Looking for function '%s' in class '%s'"),
|
|
373
|
+
*FunctionName, *TargetClass->GetName());
|
|
374
|
+
|
|
375
|
+
// First try exact name
|
|
376
|
+
Function = TargetClass->FindFunctionByName(*FunctionName);
|
|
377
|
+
|
|
378
|
+
// If not found, try class hierarchy
|
|
379
|
+
UClass* CurrentClass = TargetClass;
|
|
380
|
+
while (!Function && CurrentClass)
|
|
381
|
+
{
|
|
382
|
+
UE_LOG(LogTemp, Display, TEXT("Searching in class: %s"), *CurrentClass->GetName());
|
|
383
|
+
|
|
384
|
+
// Try exact match
|
|
385
|
+
Function = CurrentClass->FindFunctionByName(*FunctionName);
|
|
386
|
+
|
|
387
|
+
// Try case-insensitive match
|
|
388
|
+
if (!Function)
|
|
389
|
+
{
|
|
390
|
+
for (TFieldIterator<UFunction> FuncIt(CurrentClass); FuncIt; ++FuncIt)
|
|
391
|
+
{
|
|
392
|
+
UFunction* AvailableFunc = *FuncIt;
|
|
393
|
+
UE_LOG(LogTemp, Display, TEXT(" - Available function: %s"), *AvailableFunc->GetName());
|
|
394
|
+
|
|
395
|
+
if (AvailableFunc->GetName().Equals(FunctionName, ESearchCase::IgnoreCase))
|
|
396
|
+
{
|
|
397
|
+
UE_LOG(LogTemp, Display, TEXT(" - Found case-insensitive match: %s"), *AvailableFunc->GetName());
|
|
398
|
+
Function = AvailableFunc;
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Move to parent class
|
|
405
|
+
CurrentClass = CurrentClass->GetSuperClass();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Special handling for known functions
|
|
409
|
+
if (!Function)
|
|
410
|
+
{
|
|
411
|
+
if (TargetClass->GetName() == TEXT("GameplayStatics") &&
|
|
412
|
+
(FunctionName == TEXT("GetActorOfClass") || FunctionName.Equals(TEXT("GetActorOfClass"), ESearchCase::IgnoreCase)))
|
|
413
|
+
{
|
|
414
|
+
UE_LOG(LogTemp, Display, TEXT("Using special case handling for GameplayStatics::GetActorOfClass"));
|
|
415
|
+
|
|
416
|
+
// Create the function node directly
|
|
417
|
+
FunctionNode = NewObject<UK2Node_CallFunction>(EventGraph);
|
|
418
|
+
if (FunctionNode)
|
|
419
|
+
{
|
|
420
|
+
// Direct setup for known function
|
|
421
|
+
FunctionNode->FunctionReference.SetExternalMember(
|
|
422
|
+
FName(TEXT("GetActorOfClass")),
|
|
423
|
+
TargetClass
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
FunctionNode->NodePosX = NodePosition.X;
|
|
427
|
+
FunctionNode->NodePosY = NodePosition.Y;
|
|
428
|
+
EventGraph->AddNode(FunctionNode);
|
|
429
|
+
FunctionNode->CreateNewGuid();
|
|
430
|
+
FunctionNode->PostPlacedNewNode();
|
|
431
|
+
FunctionNode->AllocateDefaultPins();
|
|
432
|
+
|
|
433
|
+
UE_LOG(LogTemp, Display, TEXT("Created GetActorOfClass node directly"));
|
|
434
|
+
|
|
435
|
+
// List all pins
|
|
436
|
+
for (UEdGraphPin* Pin : FunctionNode->Pins)
|
|
437
|
+
{
|
|
438
|
+
UE_LOG(LogTemp, Display, TEXT(" - Pin: %s, Direction: %d, Category: %s"),
|
|
439
|
+
*Pin->PinName.ToString(), (int32)Pin->Direction, *Pin->PinType.PinCategory.ToString());
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// If we still haven't found the function, try in the blueprint's class
|
|
448
|
+
if (!Function && !FunctionNode)
|
|
449
|
+
{
|
|
450
|
+
UE_LOG(LogTemp, Display, TEXT("Trying to find function in blueprint class"));
|
|
451
|
+
Function = Blueprint->GeneratedClass->FindFunctionByName(*FunctionName);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Create the function call node if we found the function
|
|
455
|
+
if (Function && !FunctionNode)
|
|
456
|
+
{
|
|
457
|
+
FunctionNode = FUnrealMCPCommonUtils::CreateFunctionCallNode(EventGraph, Function, NodePosition);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (!FunctionNode)
|
|
461
|
+
{
|
|
462
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Function not found: %s in target %s"), *FunctionName, Target.IsEmpty() ? TEXT("Blueprint") : *Target));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Set parameters if provided
|
|
466
|
+
if (Params->HasField(TEXT("params")))
|
|
467
|
+
{
|
|
468
|
+
const TSharedPtr<FJsonObject>* ParamsObj;
|
|
469
|
+
if (Params->TryGetObjectField(TEXT("params"), ParamsObj))
|
|
470
|
+
{
|
|
471
|
+
// Process parameters
|
|
472
|
+
for (const TPair<FString, TSharedPtr<FJsonValue>>& Param : (*ParamsObj)->Values)
|
|
473
|
+
{
|
|
474
|
+
const FString& ParamName = Param.Key;
|
|
475
|
+
const TSharedPtr<FJsonValue>& ParamValue = Param.Value;
|
|
476
|
+
|
|
477
|
+
// Find the parameter pin
|
|
478
|
+
UEdGraphPin* ParamPin = FUnrealMCPCommonUtils::FindPin(FunctionNode, ParamName, EGPD_Input);
|
|
479
|
+
if (ParamPin)
|
|
480
|
+
{
|
|
481
|
+
UE_LOG(LogTemp, Display, TEXT("Found parameter pin '%s' of category '%s'"),
|
|
482
|
+
*ParamName, *ParamPin->PinType.PinCategory.ToString());
|
|
483
|
+
UE_LOG(LogTemp, Display, TEXT(" Current default value: '%s'"), *ParamPin->DefaultValue);
|
|
484
|
+
if (ParamPin->PinType.PinSubCategoryObject.IsValid())
|
|
485
|
+
{
|
|
486
|
+
UE_LOG(LogTemp, Display, TEXT(" Pin subcategory: '%s'"),
|
|
487
|
+
*ParamPin->PinType.PinSubCategoryObject->GetName());
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Set parameter based on type
|
|
491
|
+
if (ParamValue->Type == EJson::String)
|
|
492
|
+
{
|
|
493
|
+
FString StringVal = ParamValue->AsString();
|
|
494
|
+
UE_LOG(LogTemp, Display, TEXT(" Setting string parameter '%s' to: '%s'"),
|
|
495
|
+
*ParamName, *StringVal);
|
|
496
|
+
|
|
497
|
+
// Handle class reference parameters (e.g., ActorClass in GetActorOfClass)
|
|
498
|
+
if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class)
|
|
499
|
+
{
|
|
500
|
+
// For class references, we require the exact class name with proper prefix
|
|
501
|
+
// - Actor classes must start with 'A' (e.g., ACameraActor)
|
|
502
|
+
// - Non-actor classes must start with 'U' (e.g., UObject)
|
|
503
|
+
const FString& ClassName = StringVal;
|
|
504
|
+
|
|
505
|
+
// TODO: This likely won't work in UE5.5+, so don't rely on it.
|
|
506
|
+
UClass* Class = FindObject<UClass>(ANY_PACKAGE, *ClassName);
|
|
507
|
+
|
|
508
|
+
if (!Class)
|
|
509
|
+
{
|
|
510
|
+
Class = LoadObject<UClass>(nullptr, *ClassName);
|
|
511
|
+
UE_LOG(LogUnrealMCP, Display, TEXT("FindObject<UClass> failed. Assuming soft path path: %s"), *ClassName);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// If not found, try with Engine module path
|
|
515
|
+
if (!Class)
|
|
516
|
+
{
|
|
517
|
+
FString EngineClassName = FString::Printf(TEXT("/Script/Engine.%s"), *ClassName);
|
|
518
|
+
Class = LoadObject<UClass>(nullptr, *EngineClassName);
|
|
519
|
+
UE_LOG(LogUnrealMCP, Display, TEXT("Trying Engine module path: %s"), *EngineClassName);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (!Class)
|
|
523
|
+
{
|
|
524
|
+
UE_LOG(LogUnrealMCP, Error, TEXT("Failed to find class '%s'. Make sure to use the exact class name with proper prefix (A for actors, U for non-actors)"), *ClassName);
|
|
525
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Failed to find class '%s'"), *ClassName));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const UEdGraphSchema_K2* K2Schema = Cast<const UEdGraphSchema_K2>(EventGraph->GetSchema());
|
|
529
|
+
if (!K2Schema)
|
|
530
|
+
{
|
|
531
|
+
UE_LOG(LogUnrealMCP, Error, TEXT("Failed to get K2Schema"));
|
|
532
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get K2Schema"));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
K2Schema->TrySetDefaultObject(*ParamPin, Class);
|
|
536
|
+
if (ParamPin->DefaultObject != Class)
|
|
537
|
+
{
|
|
538
|
+
UE_LOG(LogUnrealMCP, Error, TEXT("Failed to set class reference for pin '%s' to '%s'"), *ParamPin->PinName.ToString(), *ClassName);
|
|
539
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Failed to set class reference for pin '%s'"), *ParamPin->PinName.ToString()));
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
UE_LOG(LogUnrealMCP, Log, TEXT("Successfully set class reference for pin '%s' to '%s'"), *ParamPin->PinName.ToString(), *ClassName);
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Int)
|
|
546
|
+
{
|
|
547
|
+
// Ensure we're using an integer value (no decimal)
|
|
548
|
+
int32 IntValue = FMath::RoundToInt(ParamValue->AsNumber());
|
|
549
|
+
ParamPin->DefaultValue = FString::FromInt(IntValue);
|
|
550
|
+
UE_LOG(LogTemp, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"),
|
|
551
|
+
*ParamName, IntValue, *ParamPin->DefaultValue);
|
|
552
|
+
}
|
|
553
|
+
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
|
|
554
|
+
{
|
|
555
|
+
// For other numeric types
|
|
556
|
+
float FloatValue = ParamValue->AsNumber();
|
|
557
|
+
ParamPin->DefaultValue = FString::SanitizeFloat(FloatValue);
|
|
558
|
+
UE_LOG(LogTemp, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"),
|
|
559
|
+
*ParamName, FloatValue, *ParamPin->DefaultValue);
|
|
560
|
+
}
|
|
561
|
+
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Boolean)
|
|
562
|
+
{
|
|
563
|
+
bool BoolValue = ParamValue->AsBool();
|
|
564
|
+
ParamPin->DefaultValue = BoolValue ? TEXT("true") : TEXT("false");
|
|
565
|
+
UE_LOG(LogTemp, Display, TEXT(" Set boolean parameter '%s' to: %s"),
|
|
566
|
+
*ParamName, *ParamPin->DefaultValue);
|
|
567
|
+
}
|
|
568
|
+
else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct && ParamPin->PinType.PinSubCategoryObject == TBaseStructure<FVector>::Get())
|
|
569
|
+
{
|
|
570
|
+
// Handle array parameters - like Vector parameters
|
|
571
|
+
const TArray<TSharedPtr<FJsonValue>>* ArrayValue;
|
|
572
|
+
if (ParamValue->TryGetArray(ArrayValue))
|
|
573
|
+
{
|
|
574
|
+
// Check if this could be a vector (array of 3 numbers)
|
|
575
|
+
if (ArrayValue->Num() == 3)
|
|
576
|
+
{
|
|
577
|
+
// Create a proper vector string: (X=0.0,Y=0.0,Z=1000.0)
|
|
578
|
+
float X = (*ArrayValue)[0]->AsNumber();
|
|
579
|
+
float Y = (*ArrayValue)[1]->AsNumber();
|
|
580
|
+
float Z = (*ArrayValue)[2]->AsNumber();
|
|
581
|
+
|
|
582
|
+
FString VectorString = FString::Printf(TEXT("(X=%f,Y=%f,Z=%f)"), X, Y, Z);
|
|
583
|
+
ParamPin->DefaultValue = VectorString;
|
|
584
|
+
|
|
585
|
+
UE_LOG(LogTemp, Display, TEXT(" Set vector parameter '%s' to: %s"),
|
|
586
|
+
*ParamName, *VectorString);
|
|
587
|
+
UE_LOG(LogTemp, Display, TEXT(" Final pin value: '%s'"),
|
|
588
|
+
*ParamPin->DefaultValue);
|
|
589
|
+
}
|
|
590
|
+
else
|
|
591
|
+
{
|
|
592
|
+
UE_LOG(LogTemp, Warning, TEXT("Array parameter type not fully supported yet"));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
else if (ParamValue->Type == EJson::Number)
|
|
598
|
+
{
|
|
599
|
+
// Handle integer vs float parameters correctly
|
|
600
|
+
if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Int)
|
|
601
|
+
{
|
|
602
|
+
// Ensure we're using an integer value (no decimal)
|
|
603
|
+
int32 IntValue = FMath::RoundToInt(ParamValue->AsNumber());
|
|
604
|
+
ParamPin->DefaultValue = FString::FromInt(IntValue);
|
|
605
|
+
UE_LOG(LogTemp, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"),
|
|
606
|
+
*ParamName, IntValue, *ParamPin->DefaultValue);
|
|
607
|
+
}
|
|
608
|
+
else
|
|
609
|
+
{
|
|
610
|
+
// For other numeric types
|
|
611
|
+
float FloatValue = ParamValue->AsNumber();
|
|
612
|
+
ParamPin->DefaultValue = FString::SanitizeFloat(FloatValue);
|
|
613
|
+
UE_LOG(LogTemp, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"),
|
|
614
|
+
*ParamName, FloatValue, *ParamPin->DefaultValue);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
else if (ParamValue->Type == EJson::Boolean)
|
|
618
|
+
{
|
|
619
|
+
bool BoolValue = ParamValue->AsBool();
|
|
620
|
+
ParamPin->DefaultValue = BoolValue ? TEXT("true") : TEXT("false");
|
|
621
|
+
UE_LOG(LogTemp, Display, TEXT(" Set boolean parameter '%s' to: %s"),
|
|
622
|
+
*ParamName, *ParamPin->DefaultValue);
|
|
623
|
+
}
|
|
624
|
+
else if (ParamValue->Type == EJson::Array)
|
|
625
|
+
{
|
|
626
|
+
UE_LOG(LogTemp, Display, TEXT(" Processing array parameter '%s'"), *ParamName);
|
|
627
|
+
// Handle array parameters - like Vector parameters
|
|
628
|
+
const TArray<TSharedPtr<FJsonValue>>* ArrayValue;
|
|
629
|
+
if (ParamValue->TryGetArray(ArrayValue))
|
|
630
|
+
{
|
|
631
|
+
// Check if this could be a vector (array of 3 numbers)
|
|
632
|
+
if (ArrayValue->Num() == 3 &&
|
|
633
|
+
(ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct) &&
|
|
634
|
+
(ParamPin->PinType.PinSubCategoryObject == TBaseStructure<FVector>::Get()))
|
|
635
|
+
{
|
|
636
|
+
// Create a proper vector string: (X=0.0,Y=0.0,Z=1000.0)
|
|
637
|
+
float X = (*ArrayValue)[0]->AsNumber();
|
|
638
|
+
float Y = (*ArrayValue)[1]->AsNumber();
|
|
639
|
+
float Z = (*ArrayValue)[2]->AsNumber();
|
|
640
|
+
|
|
641
|
+
FString VectorString = FString::Printf(TEXT("(X=%f,Y=%f,Z=%f)"), X, Y, Z);
|
|
642
|
+
ParamPin->DefaultValue = VectorString;
|
|
643
|
+
|
|
644
|
+
UE_LOG(LogTemp, Display, TEXT(" Set vector parameter '%s' to: %s"),
|
|
645
|
+
*ParamName, *VectorString);
|
|
646
|
+
UE_LOG(LogTemp, Display, TEXT(" Final pin value: '%s'"),
|
|
647
|
+
*ParamPin->DefaultValue);
|
|
648
|
+
}
|
|
649
|
+
else
|
|
650
|
+
{
|
|
651
|
+
UE_LOG(LogTemp, Warning, TEXT("Array parameter type not fully supported yet"));
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
// Add handling for other types as needed
|
|
656
|
+
}
|
|
657
|
+
else
|
|
658
|
+
{
|
|
659
|
+
UE_LOG(LogTemp, Warning, TEXT("Parameter pin '%s' not found"), *ParamName);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Mark the blueprint as modified
|
|
666
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
667
|
+
|
|
668
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
669
|
+
ResultObj->SetStringField(TEXT("node_id"), FunctionNode->NodeGuid.ToString());
|
|
670
|
+
return ResultObj;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintVariable(const TSharedPtr<FJsonObject>& Params)
|
|
674
|
+
{
|
|
675
|
+
// Get required parameters
|
|
676
|
+
FString BlueprintName;
|
|
677
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
678
|
+
{
|
|
679
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
FString VariableName;
|
|
683
|
+
if (!Params->TryGetStringField(TEXT("variable_name"), VariableName))
|
|
684
|
+
{
|
|
685
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'variable_name' parameter"));
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
FString VariableType;
|
|
689
|
+
if (!Params->TryGetStringField(TEXT("variable_type"), VariableType))
|
|
690
|
+
{
|
|
691
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'variable_type' parameter"));
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Get optional parameters
|
|
695
|
+
bool IsExposed = false;
|
|
696
|
+
if (Params->HasField(TEXT("is_exposed")))
|
|
697
|
+
{
|
|
698
|
+
IsExposed = Params->GetBoolField(TEXT("is_exposed"));
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Find the blueprint
|
|
702
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
703
|
+
if (!Blueprint)
|
|
704
|
+
{
|
|
705
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Create variable based on type
|
|
709
|
+
FEdGraphPinType PinType;
|
|
710
|
+
|
|
711
|
+
// Set up pin type based on variable_type string
|
|
712
|
+
if (VariableType == TEXT("Boolean"))
|
|
713
|
+
{
|
|
714
|
+
PinType.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
715
|
+
}
|
|
716
|
+
else if (VariableType == TEXT("Integer") || VariableType == TEXT("Int"))
|
|
717
|
+
{
|
|
718
|
+
PinType.PinCategory = UEdGraphSchema_K2::PC_Int;
|
|
719
|
+
}
|
|
720
|
+
else if (VariableType == TEXT("Float"))
|
|
721
|
+
{
|
|
722
|
+
PinType.PinCategory = UEdGraphSchema_K2::PC_Float;
|
|
723
|
+
}
|
|
724
|
+
else if (VariableType == TEXT("String"))
|
|
725
|
+
{
|
|
726
|
+
PinType.PinCategory = UEdGraphSchema_K2::PC_String;
|
|
727
|
+
}
|
|
728
|
+
else if (VariableType == TEXT("Vector"))
|
|
729
|
+
{
|
|
730
|
+
PinType.PinCategory = UEdGraphSchema_K2::PC_Struct;
|
|
731
|
+
PinType.PinSubCategoryObject = TBaseStructure<FVector>::Get();
|
|
732
|
+
}
|
|
733
|
+
else
|
|
734
|
+
{
|
|
735
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Unsupported variable type: %s"), *VariableType));
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Create the variable
|
|
739
|
+
FBlueprintEditorUtils::AddMemberVariable(Blueprint, FName(*VariableName), PinType);
|
|
740
|
+
|
|
741
|
+
// Set variable properties
|
|
742
|
+
FBPVariableDescription* NewVar = nullptr;
|
|
743
|
+
for (FBPVariableDescription& Variable : Blueprint->NewVariables)
|
|
744
|
+
{
|
|
745
|
+
if (Variable.VarName == FName(*VariableName))
|
|
746
|
+
{
|
|
747
|
+
NewVar = &Variable;
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (NewVar)
|
|
753
|
+
{
|
|
754
|
+
// Set exposure in editor
|
|
755
|
+
if (IsExposed)
|
|
756
|
+
{
|
|
757
|
+
NewVar->PropertyFlags |= CPF_Edit;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Mark the blueprint as modified
|
|
762
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
763
|
+
|
|
764
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
765
|
+
ResultObj->SetStringField(TEXT("variable_name"), VariableName);
|
|
766
|
+
ResultObj->SetStringField(TEXT("variable_type"), VariableType);
|
|
767
|
+
return ResultObj;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintInputActionNode(const TSharedPtr<FJsonObject>& Params)
|
|
771
|
+
{
|
|
772
|
+
// Get required parameters
|
|
773
|
+
FString BlueprintName;
|
|
774
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
775
|
+
{
|
|
776
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
FString ActionName;
|
|
780
|
+
if (!Params->TryGetStringField(TEXT("action_name"), ActionName))
|
|
781
|
+
{
|
|
782
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'action_name' parameter"));
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Get position parameters (optional)
|
|
786
|
+
FVector2D NodePosition(0.0f, 0.0f);
|
|
787
|
+
if (Params->HasField(TEXT("node_position")))
|
|
788
|
+
{
|
|
789
|
+
NodePosition = FUnrealMCPCommonUtils::GetVector2DFromJson(Params, TEXT("node_position"));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Find the blueprint
|
|
793
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
794
|
+
if (!Blueprint)
|
|
795
|
+
{
|
|
796
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Get the event graph
|
|
800
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
801
|
+
if (!EventGraph)
|
|
802
|
+
{
|
|
803
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Create the input action node
|
|
807
|
+
UK2Node_InputAction* InputActionNode = FUnrealMCPCommonUtils::CreateInputActionNode(EventGraph, ActionName, NodePosition);
|
|
808
|
+
if (!InputActionNode)
|
|
809
|
+
{
|
|
810
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create input action node"));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Mark the blueprint as modified
|
|
814
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
815
|
+
|
|
816
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
817
|
+
ResultObj->SetStringField(TEXT("node_id"), InputActionNode->NodeGuid.ToString());
|
|
818
|
+
return ResultObj;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintSelfReference(const TSharedPtr<FJsonObject>& Params)
|
|
822
|
+
{
|
|
823
|
+
// Get required parameters
|
|
824
|
+
FString BlueprintName;
|
|
825
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
826
|
+
{
|
|
827
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Get position parameters (optional)
|
|
831
|
+
FVector2D NodePosition(0.0f, 0.0f);
|
|
832
|
+
if (Params->HasField(TEXT("node_position")))
|
|
833
|
+
{
|
|
834
|
+
NodePosition = FUnrealMCPCommonUtils::GetVector2DFromJson(Params, TEXT("node_position"));
|
|
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
|
+
// Get the event graph
|
|
845
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
846
|
+
if (!EventGraph)
|
|
847
|
+
{
|
|
848
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Create the self node
|
|
852
|
+
UK2Node_Self* SelfNode = FUnrealMCPCommonUtils::CreateSelfReferenceNode(EventGraph, NodePosition);
|
|
853
|
+
if (!SelfNode)
|
|
854
|
+
{
|
|
855
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to create self node"));
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Mark the blueprint as modified
|
|
859
|
+
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
|
860
|
+
|
|
861
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
862
|
+
ResultObj->SetStringField(TEXT("node_id"), SelfNode->NodeGuid.ToString());
|
|
863
|
+
return ResultObj;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
TSharedPtr<FJsonObject> FUnrealMCPBlueprintNodeCommands::HandleFindBlueprintNodes(const TSharedPtr<FJsonObject>& Params)
|
|
867
|
+
{
|
|
868
|
+
// Get required parameters
|
|
869
|
+
FString BlueprintName;
|
|
870
|
+
if (!Params->TryGetStringField(TEXT("blueprint_name"), BlueprintName))
|
|
871
|
+
{
|
|
872
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'blueprint_name' parameter"));
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
FString NodeType;
|
|
876
|
+
if (!Params->TryGetStringField(TEXT("node_type"), NodeType))
|
|
877
|
+
{
|
|
878
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'node_type' parameter"));
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// Find the blueprint
|
|
882
|
+
UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName);
|
|
883
|
+
if (!Blueprint)
|
|
884
|
+
{
|
|
885
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName));
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Get the event graph
|
|
889
|
+
UEdGraph* EventGraph = FUnrealMCPCommonUtils::FindOrCreateEventGraph(Blueprint);
|
|
890
|
+
if (!EventGraph)
|
|
891
|
+
{
|
|
892
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Failed to get event graph"));
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// Create a JSON array for the node GUIDs
|
|
896
|
+
TArray<TSharedPtr<FJsonValue>> NodeGuidArray;
|
|
897
|
+
|
|
898
|
+
// Filter nodes by the exact requested type
|
|
899
|
+
if (NodeType == TEXT("Event"))
|
|
900
|
+
{
|
|
901
|
+
FString EventName;
|
|
902
|
+
if (!Params->TryGetStringField(TEXT("event_name"), EventName))
|
|
903
|
+
{
|
|
904
|
+
return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'event_name' parameter for Event node search"));
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Look for nodes with exact event name (e.g., ReceiveBeginPlay)
|
|
908
|
+
for (UEdGraphNode* Node : EventGraph->Nodes)
|
|
909
|
+
{
|
|
910
|
+
UK2Node_Event* EventNode = Cast<UK2Node_Event>(Node);
|
|
911
|
+
if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName))
|
|
912
|
+
{
|
|
913
|
+
UE_LOG(LogTemp, Display, TEXT("Found event node with name %s: %s"), *EventName, *EventNode->NodeGuid.ToString());
|
|
914
|
+
NodeGuidArray.Add(MakeShared<FJsonValueString>(EventNode->NodeGuid.ToString()));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
// Add other node types as needed (InputAction, etc.)
|
|
919
|
+
|
|
920
|
+
TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
|
|
921
|
+
ResultObj->SetArrayField(TEXT("node_guids"), NodeGuidArray);
|
|
922
|
+
|
|
923
|
+
return ResultObj;
|
|
924
|
+
}
|