ue-mcp 0.4.0-alpha

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 (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -0
  3. package/dist/bridge.d.ts +25 -0
  4. package/dist/bridge.js +124 -0
  5. package/dist/bridge.js.map +1 -0
  6. package/dist/deployer.d.ts +18 -0
  7. package/dist/deployer.js +175 -0
  8. package/dist/deployer.js.map +1 -0
  9. package/dist/editor-control.d.ts +15 -0
  10. package/dist/editor-control.js +181 -0
  11. package/dist/editor-control.js.map +1 -0
  12. package/dist/github-app.d.ts +4 -0
  13. package/dist/github-app.js +94 -0
  14. package/dist/github-app.js.map +1 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +125 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/init.d.ts +2 -0
  19. package/dist/init.js +290 -0
  20. package/dist/init.js.map +1 -0
  21. package/dist/instructions.d.ts +1 -0
  22. package/dist/instructions.js +150 -0
  23. package/dist/instructions.js.map +1 -0
  24. package/dist/project.d.ts +32 -0
  25. package/dist/project.js +175 -0
  26. package/dist/project.js.map +1 -0
  27. package/dist/tools/animation.d.ts +2 -0
  28. package/dist/tools/animation.js +62 -0
  29. package/dist/tools/animation.js.map +1 -0
  30. package/dist/tools/asset.d.ts +2 -0
  31. package/dist/tools/asset.js +128 -0
  32. package/dist/tools/asset.js.map +1 -0
  33. package/dist/tools/audio.d.ts +2 -0
  34. package/dist/tools/audio.js +24 -0
  35. package/dist/tools/audio.js.map +1 -0
  36. package/dist/tools/blueprint.d.ts +2 -0
  37. package/dist/tools/blueprint.js +64 -0
  38. package/dist/tools/blueprint.js.map +1 -0
  39. package/dist/tools/demo.d.ts +2 -0
  40. package/dist/tools/demo.js +10 -0
  41. package/dist/tools/demo.js.map +1 -0
  42. package/dist/tools/editor.d.ts +2 -0
  43. package/dist/tools/editor.js +133 -0
  44. package/dist/tools/editor.js.map +1 -0
  45. package/dist/tools/feedback.d.ts +2 -0
  46. package/dist/tools/feedback.js +44 -0
  47. package/dist/tools/feedback.js.map +1 -0
  48. package/dist/tools/foliage.d.ts +2 -0
  49. package/dist/tools/foliage.js +29 -0
  50. package/dist/tools/foliage.js.map +1 -0
  51. package/dist/tools/gameplay.d.ts +2 -0
  52. package/dist/tools/gameplay.js +101 -0
  53. package/dist/tools/gameplay.js.map +1 -0
  54. package/dist/tools/gas.d.ts +2 -0
  55. package/dist/tools/gas.js +43 -0
  56. package/dist/tools/gas.js.map +1 -0
  57. package/dist/tools/landscape.d.ts +2 -0
  58. package/dist/tools/landscape.js +32 -0
  59. package/dist/tools/landscape.js.map +1 -0
  60. package/dist/tools/level.d.ts +2 -0
  61. package/dist/tools/level.js +66 -0
  62. package/dist/tools/level.js.map +1 -0
  63. package/dist/tools/material.d.ts +2 -0
  64. package/dist/tools/material.js +59 -0
  65. package/dist/tools/material.js.map +1 -0
  66. package/dist/tools/networking.d.ts +2 -0
  67. package/dist/tools/networking.js +42 -0
  68. package/dist/tools/networking.js.map +1 -0
  69. package/dist/tools/niagara.d.ts +2 -0
  70. package/dist/tools/niagara.js +42 -0
  71. package/dist/tools/niagara.js.map +1 -0
  72. package/dist/tools/pcg.d.ts +2 -0
  73. package/dist/tools/pcg.js +39 -0
  74. package/dist/tools/pcg.js.map +1 -0
  75. package/dist/tools/project.d.ts +2 -0
  76. package/dist/tools/project.js +282 -0
  77. package/dist/tools/project.js.map +1 -0
  78. package/dist/tools/reflection.d.ts +2 -0
  79. package/dist/tools/reflection.js +26 -0
  80. package/dist/tools/reflection.js.map +1 -0
  81. package/dist/tools/widget.d.ts +2 -0
  82. package/dist/tools/widget.js +34 -0
  83. package/dist/tools/widget.js.map +1 -0
  84. package/dist/types.d.ts +21 -0
  85. package/dist/types.js +38 -0
  86. package/dist/types.js.map +1 -0
  87. package/package.json +76 -0
  88. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +746 -0
  89. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.h +81 -0
  90. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +49 -0
  91. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +37 -0
  92. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.cpp +75 -0
  93. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.h +50 -0
  94. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +1418 -0
  95. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +43 -0
  96. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +1773 -0
  97. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +48 -0
  98. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers.cpp +289 -0
  99. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers.h +18 -0
  100. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +1982 -0
  101. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +44 -0
  102. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +1217 -0
  103. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.h +71 -0
  104. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DialogHandlers.cpp +465 -0
  105. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DialogHandlers.h +49 -0
  106. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +1676 -0
  107. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +53 -0
  108. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +876 -0
  109. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +22 -0
  110. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +2222 -0
  111. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +54 -0
  112. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +783 -0
  113. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +24 -0
  114. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +898 -0
  115. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +24 -0
  116. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +1270 -0
  117. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +34 -0
  118. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +2190 -0
  119. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +53 -0
  120. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.cpp +554 -0
  121. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.h +29 -0
  122. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +601 -0
  123. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.h +25 -0
  124. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +1024 -0
  125. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.h +25 -0
  126. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +370 -0
  127. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.h +19 -0
  128. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ReflectionHandlers.cpp +499 -0
  129. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ReflectionHandlers.h +27 -0
  130. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +426 -0
  131. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +19 -0
  132. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +303 -0
  133. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.h +18 -0
  134. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +985 -0
  135. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +27 -0
  136. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/JsonSerializer.cpp +181 -0
  137. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/JsonSerializer.h +42 -0
  138. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +39 -0
  139. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/UE_MCP_BridgeModule.h +14 -0
  140. package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
  141. package/plugin/ue_mcp_bridge/UE_MCP_Bridge.uplugin +55 -0
@@ -0,0 +1,1982 @@
1
+ #include "BlueprintHandlers.h"
2
+ #include "HandlerRegistry.h"
3
+ #include "Kismet2/BlueprintEditorUtils.h"
4
+ #include "Kismet2/KismetEditorUtilities.h"
5
+ #include "Engine/Blueprint.h"
6
+ #include "EdGraph/EdGraphPin.h"
7
+ #include "EdGraphSchema_K2.h"
8
+ #include "K2Node.h"
9
+ #include "SubobjectDataSubsystem.h"
10
+ #include "SubobjectDataHandle.h"
11
+ #include "SubobjectData.h"
12
+ #include "SubobjectDataBlueprintFunctionLibrary.h"
13
+ #include "Editor.h"
14
+ #include "Editor/EditorEngine.h"
15
+ #include "UObject/UObjectGlobals.h"
16
+ #include "UObject/UnrealType.h"
17
+ #include "UObject/Package.h"
18
+ #include "Misc/PackageName.h"
19
+ #include "UObject/SavePackage.h"
20
+ #include "Internationalization/Text.h"
21
+ #include "UObject/TopLevelAssetPath.h"
22
+ #include "AssetToolsModule.h"
23
+ #include "IAssetTools.h"
24
+ #include "Factories/BlueprintFactory.h"
25
+ // BlueprintGraphDefinitions.h removed - not available in UE 5.7
26
+ #include "EdGraph/EdGraph.h"
27
+ #include "K2Node_CallFunction.h"
28
+ #include "K2Node_Event.h"
29
+ #include "K2Node_FunctionEntry.h"
30
+ #include "K2Node_IfThenElse.h"
31
+ #include "K2Node_MacroInstance.h"
32
+ #include "K2Node_VariableGet.h"
33
+ #include "K2Node_VariableSet.h"
34
+ #include "K2Node_CustomEvent.h"
35
+ #include "Dom/JsonObject.h"
36
+ #include "Dom/JsonValue.h"
37
+ #include "Misc/MessageDialog.h"
38
+ #include "Kismet/GameplayStatics.h"
39
+ #include "Kismet/KismetSystemLibrary.h"
40
+ #include "Kismet/KismetMathLibrary.h"
41
+ #include "Kismet/KismetStringLibrary.h"
42
+ #include "Kismet/KismetArrayLibrary.h"
43
+
44
+ void FBlueprintHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
45
+ {
46
+ Registry.RegisterHandler(TEXT("create_blueprint"), &CreateBlueprint);
47
+ Registry.RegisterHandler(TEXT("read_blueprint"), &ReadBlueprint);
48
+ Registry.RegisterHandler(TEXT("add_variable"), &AddVariable);
49
+ Registry.RegisterHandler(TEXT("add_component"), &AddComponent);
50
+ Registry.RegisterHandler(TEXT("add_blueprint_interface"), &AddBlueprintInterface);
51
+ Registry.RegisterHandler(TEXT("compile_blueprint"), &CompileBlueprint);
52
+ Registry.RegisterHandler(TEXT("search_node_types"), &SearchNodeTypes);
53
+ Registry.RegisterHandler(TEXT("list_node_types"), &ListNodeTypes);
54
+ Registry.RegisterHandler(TEXT("list_blueprint_variables"), &ListBlueprintVariables);
55
+ Registry.RegisterHandler(TEXT("set_variable_properties"), &SetVariableProperties);
56
+ Registry.RegisterHandler(TEXT("create_function"), &CreateFunction);
57
+ Registry.RegisterHandler(TEXT("list_blueprint_functions"), &ListBlueprintFunctions);
58
+ Registry.RegisterHandler(TEXT("add_node"), &AddNode);
59
+ Registry.RegisterHandler(TEXT("read_blueprint_graph"), &ReadBlueprintGraph);
60
+ Registry.RegisterHandler(TEXT("add_event_dispatcher"), &AddEventDispatcher);
61
+ Registry.RegisterHandler(TEXT("rename_function"), &RenameFunction);
62
+ Registry.RegisterHandler(TEXT("delete_function"), &DeleteFunction);
63
+ Registry.RegisterHandler(TEXT("create_blueprint_interface"), &CreateBlueprintInterface);
64
+ Registry.RegisterHandler(TEXT("list_node_types_detailed"), &ListNodeTypesDetailed);
65
+ Registry.RegisterHandler(TEXT("search_callable_functions"), &SearchCallableFunctions);
66
+ Registry.RegisterHandler(TEXT("connect_pins"), &ConnectPins);
67
+ Registry.RegisterHandler(TEXT("delete_node"), &DeleteNode);
68
+ Registry.RegisterHandler(TEXT("set_node_property"), &SetNodeProperty);
69
+ }
70
+
71
+ UBlueprint* FBlueprintHandlers::LoadBlueprint(const FString& AssetPath)
72
+ {
73
+ return LoadObject<UBlueprint>(nullptr, *AssetPath);
74
+ }
75
+
76
+ FEdGraphPinType FBlueprintHandlers::MakePinType(const FString& TypeStr)
77
+ {
78
+ FEdGraphPinType PinType;
79
+ PinType.PinCategory = NAME_None;
80
+ PinType.PinSubCategory = NAME_None;
81
+
82
+ FString LowerType = TypeStr.ToLower();
83
+
84
+ // Map type strings to pin categories
85
+ if (LowerType == TEXT("bool") || LowerType == TEXT("boolean"))
86
+ {
87
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Boolean;
88
+ }
89
+ else if (LowerType == TEXT("int") || LowerType == TEXT("integer") || LowerType == TEXT("int32"))
90
+ {
91
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Int;
92
+ }
93
+ else if (LowerType == TEXT("int64"))
94
+ {
95
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Int64;
96
+ }
97
+ else if (LowerType == TEXT("float") || LowerType == TEXT("double") || LowerType == TEXT("real"))
98
+ {
99
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Real;
100
+ PinType.PinSubCategory = UEdGraphSchema_K2::PC_Double;
101
+ }
102
+ else if (LowerType == TEXT("string") || LowerType == TEXT("str"))
103
+ {
104
+ PinType.PinCategory = UEdGraphSchema_K2::PC_String;
105
+ }
106
+ else if (LowerType == TEXT("name"))
107
+ {
108
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Name;
109
+ }
110
+ else if (LowerType == TEXT("text"))
111
+ {
112
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Text;
113
+ }
114
+ else if (LowerType == TEXT("object"))
115
+ {
116
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Object;
117
+ }
118
+ else if (LowerType == TEXT("class"))
119
+ {
120
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Class;
121
+ }
122
+ else
123
+ {
124
+ // Default to real/float
125
+ PinType.PinCategory = UEdGraphSchema_K2::PC_Real;
126
+ PinType.PinSubCategory = UEdGraphSchema_K2::PC_Double;
127
+ }
128
+
129
+ return PinType;
130
+ }
131
+
132
+ UEdGraph* FBlueprintHandlers::FindGraph(UBlueprint* Blueprint, const FString& GraphName)
133
+ {
134
+ if (!Blueprint) return nullptr;
135
+
136
+ for (UEdGraph* Graph : Blueprint->UbergraphPages)
137
+ {
138
+ if (Graph && Graph->GetName() == GraphName)
139
+ {
140
+ return Graph;
141
+ }
142
+ }
143
+ for (UEdGraph* Graph : Blueprint->FunctionGraphs)
144
+ {
145
+ if (Graph && Graph->GetName() == GraphName)
146
+ {
147
+ return Graph;
148
+ }
149
+ }
150
+ return nullptr;
151
+ }
152
+
153
+ UEdGraphNode* FBlueprintHandlers::FindNodeByGuidOrName(UEdGraph* Graph, const FString& NodeId)
154
+ {
155
+ if (!Graph) return nullptr;
156
+
157
+ // Try to parse as GUID first
158
+ FGuid SearchGuid;
159
+ if (FGuid::Parse(NodeId, SearchGuid))
160
+ {
161
+ for (UEdGraphNode* Node : Graph->Nodes)
162
+ {
163
+ if (Node && Node->NodeGuid == SearchGuid)
164
+ {
165
+ return Node;
166
+ }
167
+ }
168
+ }
169
+
170
+ // Fallback: search by name/title
171
+ for (UEdGraphNode* Node : Graph->Nodes)
172
+ {
173
+ if (!Node) continue;
174
+ if (Node->GetName() == NodeId)
175
+ {
176
+ return Node;
177
+ }
178
+ if (Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString() == NodeId)
179
+ {
180
+ return Node;
181
+ }
182
+ }
183
+
184
+ return nullptr;
185
+ }
186
+
187
+ TSharedPtr<FJsonValue> FBlueprintHandlers::CreateBlueprint(const TSharedPtr<FJsonObject>& Params)
188
+ {
189
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
190
+
191
+ FString AssetPath;
192
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
193
+ {
194
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
195
+ Result->SetBoolField(TEXT("success"), false);
196
+ return MakeShared<FJsonValueObject>(Result);
197
+ }
198
+
199
+ FString ParentClassName = TEXT("Actor");
200
+ Params->TryGetStringField(TEXT("parentClass"), ParentClassName);
201
+
202
+ // Find parent class
203
+ UClass* ParentClass = nullptr;
204
+ ParentClass = FindObject<UClass>(nullptr, *ParentClassName);
205
+ if (!ParentClass)
206
+ {
207
+ ParentClass = FindObject<UClass>(nullptr, *(TEXT("A") + ParentClassName));
208
+ }
209
+ if (!ParentClass)
210
+ {
211
+ ParentClass = FindObject<UClass>(nullptr, *(TEXT("U") + ParentClassName));
212
+ }
213
+
214
+ if (!ParentClass)
215
+ {
216
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Parent class not found: %s"), *ParentClassName));
217
+ Result->SetBoolField(TEXT("success"), false);
218
+ return MakeShared<FJsonValueObject>(Result);
219
+ }
220
+
221
+ // Create blueprint
222
+ FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
223
+ IAssetTools& AssetTools = AssetToolsModule.Get();
224
+
225
+ FString PackageName;
226
+ FString AssetName;
227
+ AssetPath.Split(TEXT("/"), &PackageName, &AssetName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
228
+ PackageName = PackageName.LeftChop(1); // Remove trailing /
229
+
230
+ UBlueprintFactory* BlueprintFactory = NewObject<UBlueprintFactory>();
231
+ UBlueprint* NewBlueprint = Cast<UBlueprint>(AssetTools.CreateAsset(AssetName, PackageName, UBlueprint::StaticClass(), BlueprintFactory));
232
+ if (!NewBlueprint)
233
+ {
234
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create Blueprint"));
235
+ Result->SetBoolField(TEXT("success"), false);
236
+ return MakeShared<FJsonValueObject>(Result);
237
+ }
238
+
239
+ NewBlueprint->ParentClass = ParentClass;
240
+ FKismetEditorUtilities::CompileBlueprint(NewBlueprint);
241
+
242
+ Result->SetStringField(TEXT("path"), AssetPath);
243
+ Result->SetStringField(TEXT("className"), NewBlueprint->GetName());
244
+ Result->SetBoolField(TEXT("success"), true);
245
+
246
+ return MakeShared<FJsonValueObject>(Result);
247
+ }
248
+
249
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ReadBlueprint(const TSharedPtr<FJsonObject>& Params)
250
+ {
251
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
252
+
253
+ FString AssetPath;
254
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
255
+ {
256
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
257
+ Result->SetBoolField(TEXT("success"), false);
258
+ return MakeShared<FJsonValueObject>(Result);
259
+ }
260
+
261
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
262
+ if (!Blueprint)
263
+ {
264
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
265
+ Result->SetBoolField(TEXT("success"), false);
266
+ return MakeShared<FJsonValueObject>(Result);
267
+ }
268
+
269
+ Result->SetStringField(TEXT("path"), AssetPath);
270
+ Result->SetStringField(TEXT("className"), Blueprint->GetName());
271
+ if (Blueprint->ParentClass)
272
+ {
273
+ Result->SetStringField(TEXT("parentClass"), Blueprint->ParentClass->GetName());
274
+ }
275
+ Result->SetBoolField(TEXT("success"), true);
276
+
277
+ return MakeShared<FJsonValueObject>(Result);
278
+ }
279
+
280
+ TSharedPtr<FJsonValue> FBlueprintHandlers::AddVariable(const TSharedPtr<FJsonObject>& Params)
281
+ {
282
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
283
+
284
+ FString AssetPath;
285
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
286
+ {
287
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
288
+ Result->SetBoolField(TEXT("success"), false);
289
+ return MakeShared<FJsonValueObject>(Result);
290
+ }
291
+
292
+ FString VarName;
293
+ if (!Params->TryGetStringField(TEXT("name"), VarName))
294
+ {
295
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'name' parameter"));
296
+ Result->SetBoolField(TEXT("success"), false);
297
+ return MakeShared<FJsonValueObject>(Result);
298
+ }
299
+
300
+ FString VarType = TEXT("Float");
301
+ Params->TryGetStringField(TEXT("type"), VarType);
302
+
303
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
304
+ if (!Blueprint)
305
+ {
306
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
307
+ Result->SetBoolField(TEXT("success"), false);
308
+ return MakeShared<FJsonValueObject>(Result);
309
+ }
310
+
311
+ // Create pin type
312
+ FEdGraphPinType PinType = MakePinType(VarType);
313
+
314
+ // Use FBlueprintEditorUtils to add variable
315
+ FName VarNameFName(*VarName);
316
+ bool bSuccess = FBlueprintEditorUtils::AddMemberVariable(Blueprint, VarNameFName, PinType);
317
+
318
+ if (bSuccess)
319
+ {
320
+ // Compile and save
321
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
322
+ // Save asset
323
+ UPackage* Package = Blueprint->GetOutermost();
324
+ if (Package)
325
+ {
326
+ Package->MarkPackageDirty();
327
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
328
+ FSavePackageArgs SaveArgs;
329
+ SaveArgs.TopLevelFlags = RF_Standalone;
330
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
331
+ }
332
+
333
+ Result->SetStringField(TEXT("path"), AssetPath);
334
+ Result->SetStringField(TEXT("variableName"), VarName);
335
+ Result->SetStringField(TEXT("variableType"), VarType);
336
+ Result->SetBoolField(TEXT("success"), true);
337
+ }
338
+ else
339
+ {
340
+ Result->SetStringField(TEXT("error"), TEXT("Failed to add variable - FBlueprintEditorUtils::AddMemberVariable returned false"));
341
+ Result->SetBoolField(TEXT("success"), false);
342
+ }
343
+
344
+ return MakeShared<FJsonValueObject>(Result);
345
+ }
346
+
347
+ TSharedPtr<FJsonValue> FBlueprintHandlers::AddComponent(const TSharedPtr<FJsonObject>& Params)
348
+ {
349
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
350
+
351
+ FString AssetPath;
352
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
353
+ {
354
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
355
+ Result->SetBoolField(TEXT("success"), false);
356
+ return MakeShared<FJsonValueObject>(Result);
357
+ }
358
+
359
+ FString ComponentClass;
360
+ if (!Params->TryGetStringField(TEXT("componentClass"), ComponentClass))
361
+ {
362
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'componentClass' parameter"));
363
+ Result->SetBoolField(TEXT("success"), false);
364
+ return MakeShared<FJsonValueObject>(Result);
365
+ }
366
+
367
+ FString ComponentName = ComponentClass;
368
+ Params->TryGetStringField(TEXT("componentName"), ComponentName);
369
+
370
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
371
+ if (!Blueprint)
372
+ {
373
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
374
+ Result->SetBoolField(TEXT("success"), false);
375
+ return MakeShared<FJsonValueObject>(Result);
376
+ }
377
+
378
+ // Find component class
379
+ UClass* CompClass = FindObject<UClass>(nullptr, *ComponentClass);
380
+ if (!CompClass)
381
+ {
382
+ CompClass = FindObject<UClass>(nullptr, *(TEXT("U") + ComponentClass));
383
+ }
384
+
385
+ if (!CompClass)
386
+ {
387
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Component class not found: %s"), *ComponentClass));
388
+ Result->SetBoolField(TEXT("success"), false);
389
+ return MakeShared<FJsonValueObject>(Result);
390
+ }
391
+
392
+ // Try using SubobjectDataSubsystem (UE5 method)
393
+ bool bSuccess = false;
394
+ if (USubobjectDataSubsystem* Subsystem = GEngine->GetEngineSubsystem<USubobjectDataSubsystem>())
395
+ {
396
+ // Get blueprint handles using K2 function
397
+ TArray<FSubobjectDataHandle> Handles;
398
+ Subsystem->K2_GatherSubobjectDataForBlueprint(Blueprint, Handles);
399
+ if (Handles.Num() > 0)
400
+ {
401
+ FSubobjectDataHandle RootHandle = Handles[0];
402
+
403
+ FAddNewSubobjectParams AddParams;
404
+ AddParams.ParentHandle = RootHandle;
405
+ AddParams.NewClass = CompClass;
406
+ AddParams.BlueprintContext = Blueprint;
407
+
408
+ FText FailReason;
409
+ FSubobjectDataHandle NewHandle = Subsystem->AddNewSubobject(AddParams, FailReason);
410
+ if (NewHandle.IsValid())
411
+ {
412
+ // Rename if needed
413
+ if (ComponentName != ComponentClass)
414
+ {
415
+ Subsystem->RenameSubobject(NewHandle, FText::FromString(ComponentName));
416
+ }
417
+ bSuccess = true;
418
+ }
419
+ }
420
+ }
421
+
422
+ if (bSuccess)
423
+ {
424
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
425
+ // Save asset
426
+ UPackage* Package = Blueprint->GetOutermost();
427
+ if (Package)
428
+ {
429
+ Package->MarkPackageDirty();
430
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
431
+ FSavePackageArgs SaveArgs;
432
+ SaveArgs.TopLevelFlags = RF_Standalone;
433
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
434
+ }
435
+
436
+ Result->SetStringField(TEXT("path"), AssetPath);
437
+ Result->SetStringField(TEXT("componentClass"), ComponentClass);
438
+ Result->SetStringField(TEXT("componentName"), ComponentName);
439
+ Result->SetBoolField(TEXT("success"), true);
440
+ }
441
+ else
442
+ {
443
+ Result->SetStringField(TEXT("error"), TEXT("Failed to add component via SubobjectDataSubsystem"));
444
+ Result->SetBoolField(TEXT("success"), false);
445
+ }
446
+
447
+ return MakeShared<FJsonValueObject>(Result);
448
+ }
449
+
450
+ TSharedPtr<FJsonValue> FBlueprintHandlers::AddBlueprintInterface(const TSharedPtr<FJsonObject>& Params)
451
+ {
452
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
453
+
454
+ FString BlueprintPath;
455
+ if (!Params->TryGetStringField(TEXT("blueprintPath"), BlueprintPath))
456
+ {
457
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'blueprintPath' parameter"));
458
+ Result->SetBoolField(TEXT("success"), false);
459
+ return MakeShared<FJsonValueObject>(Result);
460
+ }
461
+
462
+ FString InterfacePathStr;
463
+ if (!Params->TryGetStringField(TEXT("interfacePath"), InterfacePathStr))
464
+ {
465
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'interfacePath' parameter"));
466
+ Result->SetBoolField(TEXT("success"), false);
467
+ return MakeShared<FJsonValueObject>(Result);
468
+ }
469
+
470
+ UBlueprint* Blueprint = LoadBlueprint(BlueprintPath);
471
+ if (!Blueprint)
472
+ {
473
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintPath));
474
+ Result->SetBoolField(TEXT("success"), false);
475
+ return MakeShared<FJsonValueObject>(Result);
476
+ }
477
+
478
+ UClass* InterfaceClass = LoadObject<UClass>(nullptr, *InterfacePathStr);
479
+ if (!InterfaceClass)
480
+ {
481
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Interface not found: %s"), *InterfacePathStr));
482
+ Result->SetBoolField(TEXT("success"), false);
483
+ return MakeShared<FJsonValueObject>(Result);
484
+ }
485
+
486
+ // Use FBlueprintEditorUtils to add interface
487
+ FTopLevelAssetPath InterfaceAssetPath(InterfaceClass->GetPathName());
488
+ FBlueprintEditorUtils::ImplementNewInterface(Blueprint, InterfaceAssetPath);
489
+ bool bSuccess = true;
490
+
491
+ if (bSuccess)
492
+ {
493
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
494
+ // Save asset
495
+ UPackage* Package = Blueprint->GetOutermost();
496
+ if (Package)
497
+ {
498
+ Package->MarkPackageDirty();
499
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
500
+ FSavePackageArgs SaveArgs;
501
+ SaveArgs.TopLevelFlags = RF_Standalone;
502
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
503
+ }
504
+
505
+ Result->SetStringField(TEXT("blueprintPath"), BlueprintPath);
506
+ Result->SetStringField(TEXT("interfacePath"), InterfacePathStr);
507
+ Result->SetBoolField(TEXT("success"), true);
508
+ }
509
+ else
510
+ {
511
+ Result->SetStringField(TEXT("error"), TEXT("Failed to add interface - FBlueprintEditorUtils::AddInterfaceToBlueprint returned false"));
512
+ Result->SetBoolField(TEXT("success"), false);
513
+ }
514
+
515
+ return MakeShared<FJsonValueObject>(Result);
516
+ }
517
+
518
+ TSharedPtr<FJsonValue> FBlueprintHandlers::CompileBlueprint(const TSharedPtr<FJsonObject>& Params)
519
+ {
520
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
521
+
522
+ FString AssetPath;
523
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
524
+ {
525
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
526
+ Result->SetBoolField(TEXT("success"), false);
527
+ return MakeShared<FJsonValueObject>(Result);
528
+ }
529
+
530
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
531
+ if (!Blueprint)
532
+ {
533
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
534
+ Result->SetBoolField(TEXT("success"), false);
535
+ return MakeShared<FJsonValueObject>(Result);
536
+ }
537
+
538
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
539
+ Result->SetStringField(TEXT("path"), AssetPath);
540
+ Result->SetBoolField(TEXT("success"), true);
541
+
542
+ return MakeShared<FJsonValueObject>(Result);
543
+ }
544
+
545
+ TSharedPtr<FJsonValue> FBlueprintHandlers::SearchNodeTypes(const TSharedPtr<FJsonObject>& Params)
546
+ {
547
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
548
+
549
+ FString Query;
550
+ if (!Params->TryGetStringField(TEXT("query"), Query))
551
+ {
552
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'query' parameter"));
553
+ Result->SetBoolField(TEXT("success"), false);
554
+ return MakeShared<FJsonValueObject>(Result);
555
+ }
556
+
557
+ TArray<TSharedPtr<FJsonValue>> MatchingTypes;
558
+ FString LowerQuery = Query.ToLower();
559
+
560
+ // Search UFunction names across common engine classes
561
+ TArray<UClass*> ClassesToSearch;
562
+ ClassesToSearch.Add(AActor::StaticClass());
563
+ ClassesToSearch.Add(UGameplayStatics::StaticClass());
564
+ ClassesToSearch.Add(UKismetSystemLibrary::StaticClass());
565
+ ClassesToSearch.Add(UKismetMathLibrary::StaticClass());
566
+ ClassesToSearch.Add(UKismetStringLibrary::StaticClass());
567
+
568
+ for (UClass* SearchClass : ClassesToSearch)
569
+ {
570
+ if (!SearchClass) continue;
571
+ for (TFieldIterator<UFunction> FuncIt(SearchClass); FuncIt; ++FuncIt)
572
+ {
573
+ UFunction* Func = *FuncIt;
574
+ if (!Func) continue;
575
+
576
+ FString FuncName = Func->GetName();
577
+ if (FuncName.ToLower().Contains(LowerQuery))
578
+ {
579
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
580
+ Entry->SetStringField(TEXT("name"), FuncName);
581
+ Entry->SetStringField(TEXT("class"), SearchClass->GetName());
582
+ Entry->SetStringField(TEXT("fullPath"), Func->GetPathName());
583
+ MatchingTypes.Add(MakeShared<FJsonValueObject>(Entry));
584
+ }
585
+ }
586
+ }
587
+
588
+ Result->SetArrayField(TEXT("results"), MatchingTypes);
589
+ Result->SetNumberField(TEXT("count"), MatchingTypes.Num());
590
+ Result->SetBoolField(TEXT("success"), true);
591
+
592
+ return MakeShared<FJsonValueObject>(Result);
593
+ }
594
+
595
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ListNodeTypes(const TSharedPtr<FJsonObject>& Params)
596
+ {
597
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
598
+
599
+ FString Category = TEXT("Utilities");
600
+ Params->TryGetStringField(TEXT("category"), Category);
601
+
602
+ TArray<TSharedPtr<FJsonValue>> NodeTypes;
603
+ FString LowerCategory = Category.ToLower();
604
+
605
+ // Map categories to relevant classes and function sets
606
+ TArray<UClass*> ClassesToSearch;
607
+
608
+ if (LowerCategory == TEXT("utilities"))
609
+ {
610
+ ClassesToSearch.Add(UKismetSystemLibrary::StaticClass());
611
+ }
612
+ else if (LowerCategory == TEXT("math"))
613
+ {
614
+ ClassesToSearch.Add(UKismetMathLibrary::StaticClass());
615
+ }
616
+ else if (LowerCategory == TEXT("string"))
617
+ {
618
+ ClassesToSearch.Add(UKismetStringLibrary::StaticClass());
619
+ }
620
+ else if (LowerCategory == TEXT("gameplay"))
621
+ {
622
+ ClassesToSearch.Add(UGameplayStatics::StaticClass());
623
+ }
624
+ else if (LowerCategory == TEXT("actor"))
625
+ {
626
+ ClassesToSearch.Add(AActor::StaticClass());
627
+ }
628
+ else
629
+ {
630
+ // Default: search all common classes
631
+ ClassesToSearch.Add(UKismetSystemLibrary::StaticClass());
632
+ ClassesToSearch.Add(UKismetMathLibrary::StaticClass());
633
+ ClassesToSearch.Add(UGameplayStatics::StaticClass());
634
+ }
635
+
636
+ for (UClass* SearchClass : ClassesToSearch)
637
+ {
638
+ if (!SearchClass) continue;
639
+ for (TFieldIterator<UFunction> FuncIt(SearchClass); FuncIt; ++FuncIt)
640
+ {
641
+ UFunction* Func = *FuncIt;
642
+ if (!Func) continue;
643
+ if (!Func->HasAnyFunctionFlags(FUNC_BlueprintCallable | FUNC_BlueprintPure)) continue;
644
+
645
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
646
+ Entry->SetStringField(TEXT("name"), Func->GetName());
647
+ Entry->SetStringField(TEXT("class"), SearchClass->GetName());
648
+ NodeTypes.Add(MakeShared<FJsonValueObject>(Entry));
649
+ }
650
+ }
651
+
652
+ Result->SetStringField(TEXT("category"), Category);
653
+ Result->SetArrayField(TEXT("nodeTypes"), NodeTypes);
654
+ Result->SetNumberField(TEXT("count"), NodeTypes.Num());
655
+ Result->SetBoolField(TEXT("success"), true);
656
+
657
+ return MakeShared<FJsonValueObject>(Result);
658
+ }
659
+
660
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ListBlueprintVariables(const TSharedPtr<FJsonObject>& Params)
661
+ {
662
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
663
+
664
+ FString AssetPath;
665
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
666
+ {
667
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
668
+ Result->SetBoolField(TEXT("success"), false);
669
+ return MakeShared<FJsonValueObject>(Result);
670
+ }
671
+
672
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
673
+ if (!Blueprint)
674
+ {
675
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
676
+ Result->SetBoolField(TEXT("success"), false);
677
+ return MakeShared<FJsonValueObject>(Result);
678
+ }
679
+
680
+ TArray<TSharedPtr<FJsonValue>> Variables;
681
+ for (const FBPVariableDescription& Var : Blueprint->NewVariables)
682
+ {
683
+ TSharedPtr<FJsonObject> VarObj = MakeShared<FJsonObject>();
684
+ VarObj->SetStringField(TEXT("name"), Var.VarName.ToString());
685
+ VarObj->SetStringField(TEXT("type"), Var.VarType.PinCategory.ToString());
686
+ VarObj->SetStringField(TEXT("guid"), Var.VarGuid.ToString());
687
+
688
+ // Check metadata
689
+ if (Var.HasMetaData(FBlueprintMetadata::MD_Private))
690
+ {
691
+ VarObj->SetBoolField(TEXT("private"), true);
692
+ }
693
+ if (Var.HasMetaData(FBlueprintMetadata::MD_FunctionCategory))
694
+ {
695
+ VarObj->SetStringField(TEXT("category"), Var.GetMetaData(FBlueprintMetadata::MD_FunctionCategory));
696
+ }
697
+ if (Var.HasMetaData(FBlueprintMetadata::MD_Tooltip))
698
+ {
699
+ VarObj->SetStringField(TEXT("tooltip"), Var.GetMetaData(FBlueprintMetadata::MD_Tooltip));
700
+ }
701
+
702
+ VarObj->SetBoolField(TEXT("instanceEditable"),
703
+ !Var.HasMetaData(FBlueprintMetadata::MD_Private) && Var.PropertyFlags & CPF_Edit);
704
+
705
+ Variables.Add(MakeShared<FJsonValueObject>(VarObj));
706
+ }
707
+
708
+ Result->SetStringField(TEXT("path"), AssetPath);
709
+ Result->SetArrayField(TEXT("variables"), Variables);
710
+ Result->SetNumberField(TEXT("count"), Variables.Num());
711
+ Result->SetBoolField(TEXT("success"), true);
712
+
713
+ return MakeShared<FJsonValueObject>(Result);
714
+ }
715
+
716
+ TSharedPtr<FJsonValue> FBlueprintHandlers::SetVariableProperties(const TSharedPtr<FJsonObject>& Params)
717
+ {
718
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
719
+
720
+ FString AssetPath;
721
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
722
+ {
723
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
724
+ Result->SetBoolField(TEXT("success"), false);
725
+ return MakeShared<FJsonValueObject>(Result);
726
+ }
727
+
728
+ FString VarName;
729
+ if (!Params->TryGetStringField(TEXT("name"), VarName))
730
+ {
731
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'name' parameter"));
732
+ Result->SetBoolField(TEXT("success"), false);
733
+ return MakeShared<FJsonValueObject>(Result);
734
+ }
735
+
736
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
737
+ if (!Blueprint)
738
+ {
739
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
740
+ Result->SetBoolField(TEXT("success"), false);
741
+ return MakeShared<FJsonValueObject>(Result);
742
+ }
743
+
744
+ // Find the variable
745
+ FBPVariableDescription* FoundVar = nullptr;
746
+ for (FBPVariableDescription& Var : Blueprint->NewVariables)
747
+ {
748
+ if (Var.VarName.ToString() == VarName)
749
+ {
750
+ FoundVar = &Var;
751
+ break;
752
+ }
753
+ }
754
+
755
+ if (!FoundVar)
756
+ {
757
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Variable not found: %s"), *VarName));
758
+ Result->SetBoolField(TEXT("success"), false);
759
+ return MakeShared<FJsonValueObject>(Result);
760
+ }
761
+
762
+ // Set instance editable
763
+ bool bInstanceEditable = false;
764
+ if (Params->TryGetBoolField(TEXT("instanceEditable"), bInstanceEditable))
765
+ {
766
+ if (bInstanceEditable)
767
+ {
768
+ FoundVar->PropertyFlags |= CPF_Edit;
769
+ FoundVar->RemoveMetaData(FBlueprintMetadata::MD_Private);
770
+ }
771
+ else
772
+ {
773
+ FoundVar->PropertyFlags &= ~CPF_Edit;
774
+ }
775
+ }
776
+
777
+ // Set category
778
+ FString CategoryStr;
779
+ if (Params->TryGetStringField(TEXT("category"), CategoryStr))
780
+ {
781
+ FoundVar->SetMetaData(FBlueprintMetadata::MD_FunctionCategory, *CategoryStr);
782
+ }
783
+
784
+ // Set tooltip
785
+ FString TooltipStr;
786
+ if (Params->TryGetStringField(TEXT("tooltip"), TooltipStr))
787
+ {
788
+ FoundVar->SetMetaData(FBlueprintMetadata::MD_Tooltip, *TooltipStr);
789
+ }
790
+
791
+ // Compile and save
792
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
793
+ UPackage* Package = Blueprint->GetOutermost();
794
+ if (Package)
795
+ {
796
+ Package->MarkPackageDirty();
797
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
798
+ FSavePackageArgs SaveArgs;
799
+ SaveArgs.TopLevelFlags = RF_Standalone;
800
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
801
+ }
802
+
803
+ Result->SetStringField(TEXT("path"), AssetPath);
804
+ Result->SetStringField(TEXT("variableName"), VarName);
805
+ Result->SetBoolField(TEXT("success"), true);
806
+
807
+ return MakeShared<FJsonValueObject>(Result);
808
+ }
809
+
810
+ TSharedPtr<FJsonValue> FBlueprintHandlers::CreateFunction(const TSharedPtr<FJsonObject>& Params)
811
+ {
812
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
813
+
814
+ FString AssetPath;
815
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
816
+ {
817
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
818
+ Result->SetBoolField(TEXT("success"), false);
819
+ return MakeShared<FJsonValueObject>(Result);
820
+ }
821
+
822
+ FString FunctionName;
823
+ if (!Params->TryGetStringField(TEXT("functionName"), FunctionName))
824
+ {
825
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'functionName' parameter"));
826
+ Result->SetBoolField(TEXT("success"), false);
827
+ return MakeShared<FJsonValueObject>(Result);
828
+ }
829
+
830
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
831
+ if (!Blueprint)
832
+ {
833
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
834
+ Result->SetBoolField(TEXT("success"), false);
835
+ return MakeShared<FJsonValueObject>(Result);
836
+ }
837
+
838
+ UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(
839
+ Blueprint,
840
+ FName(*FunctionName),
841
+ UEdGraph::StaticClass(),
842
+ UEdGraphSchema_K2::StaticClass()
843
+ );
844
+ if (!NewGraph)
845
+ {
846
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Failed to create function: %s"), *FunctionName));
847
+ Result->SetBoolField(TEXT("success"), false);
848
+ return MakeShared<FJsonValueObject>(Result);
849
+ }
850
+
851
+ FBlueprintEditorUtils::AddFunctionGraph<UClass>(Blueprint, NewGraph, /*bIsUserCreated=*/true, /*SignatureFromObject=*/nullptr);
852
+
853
+ // Compile and save
854
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
855
+ UPackage* Package = Blueprint->GetOutermost();
856
+ if (Package)
857
+ {
858
+ Package->MarkPackageDirty();
859
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
860
+ FSavePackageArgs SaveArgs;
861
+ SaveArgs.TopLevelFlags = RF_Standalone;
862
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
863
+ }
864
+
865
+ Result->SetStringField(TEXT("path"), AssetPath);
866
+ Result->SetStringField(TEXT("functionName"), FunctionName);
867
+ Result->SetBoolField(TEXT("success"), true);
868
+
869
+ return MakeShared<FJsonValueObject>(Result);
870
+ }
871
+
872
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ListBlueprintFunctions(const TSharedPtr<FJsonObject>& Params)
873
+ {
874
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
875
+
876
+ FString AssetPath;
877
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
878
+ {
879
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
880
+ Result->SetBoolField(TEXT("success"), false);
881
+ return MakeShared<FJsonValueObject>(Result);
882
+ }
883
+
884
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
885
+ if (!Blueprint)
886
+ {
887
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
888
+ Result->SetBoolField(TEXT("success"), false);
889
+ return MakeShared<FJsonValueObject>(Result);
890
+ }
891
+
892
+ TArray<TSharedPtr<FJsonValue>> Functions;
893
+ for (UEdGraph* Graph : Blueprint->FunctionGraphs)
894
+ {
895
+ if (!Graph) continue;
896
+ TSharedPtr<FJsonObject> FuncObj = MakeShared<FJsonObject>();
897
+ FuncObj->SetStringField(TEXT("name"), Graph->GetName());
898
+ FuncObj->SetNumberField(TEXT("nodeCount"), Graph->Nodes.Num());
899
+ Functions.Add(MakeShared<FJsonValueObject>(FuncObj));
900
+ }
901
+
902
+ Result->SetStringField(TEXT("path"), AssetPath);
903
+ Result->SetArrayField(TEXT("functions"), Functions);
904
+ Result->SetNumberField(TEXT("count"), Functions.Num());
905
+ Result->SetBoolField(TEXT("success"), true);
906
+
907
+ return MakeShared<FJsonValueObject>(Result);
908
+ }
909
+
910
+ TSharedPtr<FJsonValue> FBlueprintHandlers::AddNode(const TSharedPtr<FJsonObject>& Params)
911
+ {
912
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
913
+
914
+ FString AssetPath;
915
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
916
+ {
917
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
918
+ Result->SetBoolField(TEXT("success"), false);
919
+ return MakeShared<FJsonValueObject>(Result);
920
+ }
921
+
922
+ FString GraphName = TEXT("EventGraph");
923
+ Params->TryGetStringField(TEXT("graphName"), GraphName);
924
+
925
+ FString NodeClass;
926
+ if (!Params->TryGetStringField(TEXT("nodeClass"), NodeClass))
927
+ {
928
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeClass' parameter"));
929
+ Result->SetBoolField(TEXT("success"), false);
930
+ return MakeShared<FJsonValueObject>(Result);
931
+ }
932
+
933
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
934
+ if (!Blueprint)
935
+ {
936
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
937
+ Result->SetBoolField(TEXT("success"), false);
938
+ return MakeShared<FJsonValueObject>(Result);
939
+ }
940
+
941
+ // Find the target graph
942
+ UEdGraph* TargetGraph = FindGraph(Blueprint, GraphName);
943
+
944
+ if (!TargetGraph)
945
+ {
946
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph not found: %s"), *GraphName));
947
+ Result->SetBoolField(TEXT("success"), false);
948
+ return MakeShared<FJsonValueObject>(Result);
949
+ }
950
+
951
+ // Get optional node params
952
+ const TSharedPtr<FJsonObject>* NodeParams = nullptr;
953
+ Params->TryGetObjectField(TEXT("nodeParams"), NodeParams);
954
+
955
+ UK2Node* NewNode = nullptr;
956
+
957
+ if (NodeClass == TEXT("K2Node_CallFunction") || NodeClass == TEXT("CallFunction"))
958
+ {
959
+ UK2Node_CallFunction* CallNode = NewObject<UK2Node_CallFunction>(TargetGraph);
960
+ if (CallNode)
961
+ {
962
+ // Set function reference from nodeParams
963
+ if (NodeParams)
964
+ {
965
+ FString FunctionName;
966
+ if ((*NodeParams)->TryGetStringField(TEXT("functionName"), FunctionName))
967
+ {
968
+ FString TargetClassName;
969
+ if ((*NodeParams)->TryGetStringField(TEXT("targetClass"), TargetClassName))
970
+ {
971
+ UClass* TargetClass = FindObject<UClass>(nullptr, *TargetClassName);
972
+ if (!TargetClass)
973
+ {
974
+ TargetClass = FindObject<UClass>(nullptr, *(TEXT("U") + TargetClassName));
975
+ }
976
+ if (TargetClass)
977
+ {
978
+ UFunction* Func = TargetClass->FindFunctionByName(FName(*FunctionName));
979
+ if (Func)
980
+ {
981
+ CallNode->SetFromFunction(Func);
982
+ }
983
+ }
984
+ }
985
+ else
986
+ {
987
+ // Try finding function by name on the blueprint's parent class
988
+ if (Blueprint->ParentClass)
989
+ {
990
+ UFunction* Func = Blueprint->ParentClass->FindFunctionByName(FName(*FunctionName));
991
+ if (Func)
992
+ {
993
+ CallNode->SetFromFunction(Func);
994
+ }
995
+ }
996
+ }
997
+ }
998
+ }
999
+
1000
+ TargetGraph->AddNode(CallNode, false, false);
1001
+ CallNode->CreateNewGuid();
1002
+ CallNode->PostPlacedNewNode();
1003
+ CallNode->AllocateDefaultPins();
1004
+ NewNode = CallNode;
1005
+ }
1006
+ }
1007
+ else if (NodeClass == TEXT("K2Node_Event") || NodeClass == TEXT("Event"))
1008
+ {
1009
+ UK2Node_Event* EventNode = NewObject<UK2Node_Event>(TargetGraph);
1010
+ if (EventNode)
1011
+ {
1012
+ if (NodeParams)
1013
+ {
1014
+ FString EventName;
1015
+ if ((*NodeParams)->TryGetStringField(TEXT("eventName"), EventName))
1016
+ {
1017
+ EventNode->CustomFunctionName = FName(*EventName);
1018
+ }
1019
+ }
1020
+
1021
+ TargetGraph->AddNode(EventNode, false, false);
1022
+ EventNode->CreateNewGuid();
1023
+ EventNode->PostPlacedNewNode();
1024
+ EventNode->AllocateDefaultPins();
1025
+ NewNode = EventNode;
1026
+ }
1027
+ }
1028
+ else
1029
+ {
1030
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Unsupported node class: %s. Supported: K2Node_CallFunction, K2Node_Event"), *NodeClass));
1031
+ Result->SetBoolField(TEXT("success"), false);
1032
+ return MakeShared<FJsonValueObject>(Result);
1033
+ }
1034
+
1035
+ if (!NewNode)
1036
+ {
1037
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create node"));
1038
+ Result->SetBoolField(TEXT("success"), false);
1039
+ return MakeShared<FJsonValueObject>(Result);
1040
+ }
1041
+
1042
+ // Compile and save
1043
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1044
+ UPackage* Package = Blueprint->GetOutermost();
1045
+ if (Package)
1046
+ {
1047
+ Package->MarkPackageDirty();
1048
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1049
+ FSavePackageArgs SaveArgs;
1050
+ SaveArgs.TopLevelFlags = RF_Standalone;
1051
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1052
+ }
1053
+
1054
+ Result->SetStringField(TEXT("path"), AssetPath);
1055
+ Result->SetStringField(TEXT("graphName"), GraphName);
1056
+ Result->SetStringField(TEXT("nodeClass"), NodeClass);
1057
+ Result->SetStringField(TEXT("nodeId"), NewNode->NodeGuid.ToString());
1058
+ Result->SetBoolField(TEXT("success"), true);
1059
+
1060
+ return MakeShared<FJsonValueObject>(Result);
1061
+ }
1062
+
1063
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ReadBlueprintGraph(const TSharedPtr<FJsonObject>& Params)
1064
+ {
1065
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1066
+
1067
+ FString AssetPath;
1068
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1069
+ {
1070
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1071
+ Result->SetBoolField(TEXT("success"), false);
1072
+ return MakeShared<FJsonValueObject>(Result);
1073
+ }
1074
+
1075
+ FString GraphName = TEXT("EventGraph");
1076
+ Params->TryGetStringField(TEXT("graphName"), GraphName);
1077
+
1078
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1079
+ if (!Blueprint)
1080
+ {
1081
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1082
+ Result->SetBoolField(TEXT("success"), false);
1083
+ return MakeShared<FJsonValueObject>(Result);
1084
+ }
1085
+
1086
+ // Find the graph in UbergraphPages and FunctionGraphs
1087
+ UEdGraph* TargetGraph = FindGraph(Blueprint, GraphName);
1088
+
1089
+ if (!TargetGraph)
1090
+ {
1091
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph not found: %s"), *GraphName));
1092
+ Result->SetBoolField(TEXT("success"), false);
1093
+ return MakeShared<FJsonValueObject>(Result);
1094
+ }
1095
+
1096
+ TArray<TSharedPtr<FJsonValue>> Nodes;
1097
+ for (UEdGraphNode* Node : TargetGraph->Nodes)
1098
+ {
1099
+ if (!Node) continue;
1100
+
1101
+ TSharedPtr<FJsonObject> NodeObj = MakeShared<FJsonObject>();
1102
+ NodeObj->SetStringField(TEXT("id"), Node->NodeGuid.ToString());
1103
+ NodeObj->SetStringField(TEXT("class"), Node->GetClass()->GetName());
1104
+ NodeObj->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString());
1105
+ NodeObj->SetNumberField(TEXT("posX"), Node->NodePosX);
1106
+ NodeObj->SetNumberField(TEXT("posY"), Node->NodePosY);
1107
+ NodeObj->SetStringField(TEXT("comment"), Node->NodeComment);
1108
+
1109
+ // List pins
1110
+ TArray<TSharedPtr<FJsonValue>> Pins;
1111
+ for (UEdGraphPin* Pin : Node->Pins)
1112
+ {
1113
+ if (!Pin) continue;
1114
+ TSharedPtr<FJsonObject> PinObj = MakeShared<FJsonObject>();
1115
+ PinObj->SetStringField(TEXT("name"), Pin->PinName.ToString());
1116
+ PinObj->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString());
1117
+ PinObj->SetStringField(TEXT("direction"), Pin->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output"));
1118
+ PinObj->SetStringField(TEXT("defaultValue"), Pin->DefaultValue);
1119
+ PinObj->SetBoolField(TEXT("connected"), Pin->LinkedTo.Num() > 0);
1120
+ Pins.Add(MakeShared<FJsonValueObject>(PinObj));
1121
+ }
1122
+ NodeObj->SetArrayField(TEXT("pins"), Pins);
1123
+
1124
+ Nodes.Add(MakeShared<FJsonValueObject>(NodeObj));
1125
+ }
1126
+
1127
+ Result->SetStringField(TEXT("path"), AssetPath);
1128
+ Result->SetStringField(TEXT("graphName"), GraphName);
1129
+ Result->SetArrayField(TEXT("nodes"), Nodes);
1130
+ Result->SetNumberField(TEXT("nodeCount"), Nodes.Num());
1131
+ Result->SetBoolField(TEXT("success"), true);
1132
+
1133
+ return MakeShared<FJsonValueObject>(Result);
1134
+ }
1135
+
1136
+ TSharedPtr<FJsonValue> FBlueprintHandlers::AddEventDispatcher(const TSharedPtr<FJsonObject>& Params)
1137
+ {
1138
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1139
+
1140
+ FString BlueprintPath;
1141
+ if (!Params->TryGetStringField(TEXT("blueprintPath"), BlueprintPath))
1142
+ {
1143
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'blueprintPath' parameter"));
1144
+ Result->SetBoolField(TEXT("success"), false);
1145
+ return MakeShared<FJsonValueObject>(Result);
1146
+ }
1147
+
1148
+ FString DispatcherName;
1149
+ if (!Params->TryGetStringField(TEXT("name"), DispatcherName))
1150
+ {
1151
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'name' parameter"));
1152
+ Result->SetBoolField(TEXT("success"), false);
1153
+ return MakeShared<FJsonValueObject>(Result);
1154
+ }
1155
+
1156
+ UBlueprint* Blueprint = LoadBlueprint(BlueprintPath);
1157
+ if (!Blueprint)
1158
+ {
1159
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintPath));
1160
+ Result->SetBoolField(TEXT("success"), false);
1161
+ return MakeShared<FJsonValueObject>(Result);
1162
+ }
1163
+
1164
+ // Add multicast delegate variable
1165
+ FEdGraphPinType PinType;
1166
+ PinType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate;
1167
+
1168
+ FName DispatcherFName(*DispatcherName);
1169
+ bool bSuccess = FBlueprintEditorUtils::AddMemberVariable(Blueprint, DispatcherFName, PinType);
1170
+
1171
+ if (bSuccess)
1172
+ {
1173
+ // Compile and save
1174
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1175
+ UPackage* Package = Blueprint->GetOutermost();
1176
+ if (Package)
1177
+ {
1178
+ Package->MarkPackageDirty();
1179
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1180
+ FSavePackageArgs SaveArgs;
1181
+ SaveArgs.TopLevelFlags = RF_Standalone;
1182
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1183
+ }
1184
+
1185
+ Result->SetStringField(TEXT("blueprintPath"), BlueprintPath);
1186
+ Result->SetStringField(TEXT("name"), DispatcherName);
1187
+ Result->SetBoolField(TEXT("success"), true);
1188
+ }
1189
+ else
1190
+ {
1191
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Failed to add event dispatcher: %s"), *DispatcherName));
1192
+ Result->SetBoolField(TEXT("success"), false);
1193
+ }
1194
+
1195
+ return MakeShared<FJsonValueObject>(Result);
1196
+ }
1197
+
1198
+ TSharedPtr<FJsonValue> FBlueprintHandlers::RenameFunction(const TSharedPtr<FJsonObject>& Params)
1199
+ {
1200
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1201
+
1202
+ FString AssetPath;
1203
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1204
+ {
1205
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1206
+ Result->SetBoolField(TEXT("success"), false);
1207
+ return MakeShared<FJsonValueObject>(Result);
1208
+ }
1209
+
1210
+ FString OldName;
1211
+ if (!Params->TryGetStringField(TEXT("oldName"), OldName))
1212
+ {
1213
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'oldName' parameter"));
1214
+ Result->SetBoolField(TEXT("success"), false);
1215
+ return MakeShared<FJsonValueObject>(Result);
1216
+ }
1217
+
1218
+ FString NewName;
1219
+ if (!Params->TryGetStringField(TEXT("newName"), NewName))
1220
+ {
1221
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'newName' parameter"));
1222
+ Result->SetBoolField(TEXT("success"), false);
1223
+ return MakeShared<FJsonValueObject>(Result);
1224
+ }
1225
+
1226
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1227
+ if (!Blueprint)
1228
+ {
1229
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1230
+ Result->SetBoolField(TEXT("success"), false);
1231
+ return MakeShared<FJsonValueObject>(Result);
1232
+ }
1233
+
1234
+ // Find the function graph
1235
+ UEdGraph* FoundGraph = nullptr;
1236
+ for (UEdGraph* Graph : Blueprint->FunctionGraphs)
1237
+ {
1238
+ if (Graph && Graph->GetName() == OldName)
1239
+ {
1240
+ FoundGraph = Graph;
1241
+ break;
1242
+ }
1243
+ }
1244
+
1245
+ if (!FoundGraph)
1246
+ {
1247
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Function not found: %s"), *OldName));
1248
+ Result->SetBoolField(TEXT("success"), false);
1249
+ return MakeShared<FJsonValueObject>(Result);
1250
+ }
1251
+
1252
+ FBlueprintEditorUtils::RenameGraph(FoundGraph, NewName);
1253
+
1254
+ // Compile and save
1255
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1256
+ UPackage* Package = Blueprint->GetOutermost();
1257
+ if (Package)
1258
+ {
1259
+ Package->MarkPackageDirty();
1260
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1261
+ FSavePackageArgs SaveArgs;
1262
+ SaveArgs.TopLevelFlags = RF_Standalone;
1263
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1264
+ }
1265
+
1266
+ Result->SetStringField(TEXT("path"), AssetPath);
1267
+ Result->SetStringField(TEXT("oldName"), OldName);
1268
+ Result->SetStringField(TEXT("newName"), NewName);
1269
+ Result->SetBoolField(TEXT("success"), true);
1270
+
1271
+ return MakeShared<FJsonValueObject>(Result);
1272
+ }
1273
+
1274
+ TSharedPtr<FJsonValue> FBlueprintHandlers::DeleteFunction(const TSharedPtr<FJsonObject>& Params)
1275
+ {
1276
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1277
+
1278
+ FString AssetPath;
1279
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1280
+ {
1281
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1282
+ Result->SetBoolField(TEXT("success"), false);
1283
+ return MakeShared<FJsonValueObject>(Result);
1284
+ }
1285
+
1286
+ FString FunctionName;
1287
+ if (!Params->TryGetStringField(TEXT("functionName"), FunctionName))
1288
+ {
1289
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'functionName' parameter"));
1290
+ Result->SetBoolField(TEXT("success"), false);
1291
+ return MakeShared<FJsonValueObject>(Result);
1292
+ }
1293
+
1294
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1295
+ if (!Blueprint)
1296
+ {
1297
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1298
+ Result->SetBoolField(TEXT("success"), false);
1299
+ return MakeShared<FJsonValueObject>(Result);
1300
+ }
1301
+
1302
+ // Find the function graph
1303
+ UEdGraph* FoundGraph = nullptr;
1304
+ for (UEdGraph* Graph : Blueprint->FunctionGraphs)
1305
+ {
1306
+ if (Graph && Graph->GetName() == FunctionName)
1307
+ {
1308
+ FoundGraph = Graph;
1309
+ break;
1310
+ }
1311
+ }
1312
+
1313
+ if (!FoundGraph)
1314
+ {
1315
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Function not found: %s"), *FunctionName));
1316
+ Result->SetBoolField(TEXT("success"), false);
1317
+ return MakeShared<FJsonValueObject>(Result);
1318
+ }
1319
+
1320
+ FBlueprintEditorUtils::RemoveGraph(Blueprint, FoundGraph);
1321
+
1322
+ // Compile and save
1323
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1324
+ UPackage* Package = Blueprint->GetOutermost();
1325
+ if (Package)
1326
+ {
1327
+ Package->MarkPackageDirty();
1328
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1329
+ FSavePackageArgs SaveArgs;
1330
+ SaveArgs.TopLevelFlags = RF_Standalone;
1331
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1332
+ }
1333
+
1334
+ Result->SetStringField(TEXT("path"), AssetPath);
1335
+ Result->SetStringField(TEXT("functionName"), FunctionName);
1336
+ Result->SetBoolField(TEXT("success"), true);
1337
+
1338
+ return MakeShared<FJsonValueObject>(Result);
1339
+ }
1340
+
1341
+ TSharedPtr<FJsonValue> FBlueprintHandlers::CreateBlueprintInterface(const TSharedPtr<FJsonObject>& Params)
1342
+ {
1343
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1344
+
1345
+ FString AssetPath;
1346
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1347
+ {
1348
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1349
+ Result->SetBoolField(TEXT("success"), false);
1350
+ return MakeShared<FJsonValueObject>(Result);
1351
+ }
1352
+
1353
+ // Create a Blueprint Interface asset
1354
+ FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
1355
+ IAssetTools& AssetTools = AssetToolsModule.Get();
1356
+
1357
+ FString PackageName;
1358
+ FString AssetName;
1359
+ AssetPath.Split(TEXT("/"), &PackageName, &AssetName, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
1360
+ PackageName = PackageName.LeftChop(1); // Remove trailing /
1361
+
1362
+ UBlueprintFactory* BlueprintFactory = NewObject<UBlueprintFactory>();
1363
+ BlueprintFactory->BlueprintType = BPTYPE_Interface;
1364
+ BlueprintFactory->ParentClass = UInterface::StaticClass();
1365
+
1366
+ UBlueprint* NewInterface = Cast<UBlueprint>(AssetTools.CreateAsset(AssetName, PackageName, UBlueprint::StaticClass(), BlueprintFactory));
1367
+ if (!NewInterface)
1368
+ {
1369
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create Blueprint Interface"));
1370
+ Result->SetBoolField(TEXT("success"), false);
1371
+ return MakeShared<FJsonValueObject>(Result);
1372
+ }
1373
+
1374
+ FKismetEditorUtilities::CompileBlueprint(NewInterface);
1375
+
1376
+ Result->SetStringField(TEXT("path"), AssetPath);
1377
+ Result->SetStringField(TEXT("name"), NewInterface->GetName());
1378
+ Result->SetBoolField(TEXT("success"), true);
1379
+
1380
+ return MakeShared<FJsonValueObject>(Result);
1381
+ }
1382
+
1383
+ // ============================================================================
1384
+ // NEW HANDLERS
1385
+ // ============================================================================
1386
+
1387
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ListNodeTypesDetailed(const TSharedPtr<FJsonObject>& Params)
1388
+ {
1389
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1390
+
1391
+ // Static catalog of common K2Node types with descriptions and categories
1392
+ struct FNodeTypeEntry
1393
+ {
1394
+ const TCHAR* Name;
1395
+ const TCHAR* Category;
1396
+ const TCHAR* Description;
1397
+ const TCHAR* ClassName;
1398
+ };
1399
+
1400
+ static const FNodeTypeEntry CommonNodes[] =
1401
+ {
1402
+ // Flow Control
1403
+ { TEXT("Branch"), TEXT("Flow Control"), TEXT("If/else conditional branch"), TEXT("K2Node_IfThenElse") },
1404
+ { TEXT("Sequence"), TEXT("Flow Control"), TEXT("Execute multiple outputs in order"), TEXT("K2Node_ExecutionSequence") },
1405
+ { TEXT("DoOnce"), TEXT("Flow Control"), TEXT("Execute only the first time"), TEXT("K2Node_DoOnceMultiInput") },
1406
+ { TEXT("FlipFlop"), TEXT("Flow Control"), TEXT("Alternates between two outputs"), TEXT("K2Node_FlipFlop") },
1407
+ { TEXT("Gate"), TEXT("Flow Control"), TEXT("Open/close execution gate"), TEXT("K2Node_Gate") },
1408
+ { TEXT("ForEachLoop"), TEXT("Flow Control"), TEXT("Loop over array elements"), TEXT("K2Node_ForEachElementInEnum") },
1409
+ { TEXT("WhileLoop"), TEXT("Flow Control"), TEXT("Loop while condition is true"), TEXT("K2Node_WhileLoop") },
1410
+ { TEXT("Select"), TEXT("Flow Control"), TEXT("Select output based on index"), TEXT("K2Node_Select") },
1411
+ { TEXT("Switch"), TEXT("Flow Control"), TEXT("Switch on value (int, string, enum, name)"), TEXT("K2Node_Switch") },
1412
+ { TEXT("Delay"), TEXT("Flow Control"), TEXT("Wait for specified time before continuing"), TEXT("K2Node_Delay") },
1413
+
1414
+ // Events
1415
+ { TEXT("EventBeginPlay"), TEXT("Events"), TEXT("Called when play begins"), TEXT("K2Node_Event") },
1416
+ { TEXT("EventTick"), TEXT("Events"), TEXT("Called every frame"), TEXT("K2Node_Event") },
1417
+ { TEXT("EventActorBeginOverlap"), TEXT("Events"), TEXT("Called when an actor overlaps"), TEXT("K2Node_Event") },
1418
+ { TEXT("EventHit"), TEXT("Events"), TEXT("Called when actor is hit"), TEXT("K2Node_Event") },
1419
+ { TEXT("EventAnyDamage"), TEXT("Events"), TEXT("Called when any damage is received"), TEXT("K2Node_Event") },
1420
+ { TEXT("CustomEvent"), TEXT("Events"), TEXT("User-defined custom event"), TEXT("K2Node_CustomEvent") },
1421
+ { TEXT("EventDispatcher"), TEXT("Events"), TEXT("Multicast delegate event dispatcher"), TEXT("K2Node_CreateDelegate") },
1422
+ { TEXT("InputAction"), TEXT("Events"), TEXT("Respond to input action"), TEXT("K2Node_InputAction") },
1423
+ { TEXT("InputKey"), TEXT("Events"), TEXT("Respond to key press/release"), TEXT("K2Node_InputKey") },
1424
+
1425
+ // Functions
1426
+ { TEXT("CallFunction"), TEXT("Functions"), TEXT("Call a function by name"), TEXT("K2Node_CallFunction") },
1427
+ { TEXT("PrintString"), TEXT("Functions"), TEXT("Print text to screen/log"), TEXT("K2Node_CallFunction") },
1428
+ { TEXT("SpawnActor"), TEXT("Functions"), TEXT("Spawn an actor from class"), TEXT("K2Node_SpawnActorFromClass") },
1429
+ { TEXT("DestroyActor"), TEXT("Functions"), TEXT("Destroy an actor"), TEXT("K2Node_CallFunction") },
1430
+ { TEXT("GetAllActorsOfClass"), TEXT("Functions"), TEXT("Get all actors of a specific class"), TEXT("K2Node_CallFunction") },
1431
+ { TEXT("SetTimer"), TEXT("Functions"), TEXT("Set a timer by function name or event"), TEXT("K2Node_CallFunction") },
1432
+ { TEXT("ClearTimer"), TEXT("Functions"), TEXT("Clear/invalidate a timer"), TEXT("K2Node_CallFunction") },
1433
+
1434
+ // Variables
1435
+ { TEXT("VariableGet"), TEXT("Variables"), TEXT("Get the value of a variable"), TEXT("K2Node_VariableGet") },
1436
+ { TEXT("VariableSet"), TEXT("Variables"), TEXT("Set the value of a variable"), TEXT("K2Node_VariableSet") },
1437
+ { TEXT("MakeArray"), TEXT("Variables"), TEXT("Construct an array from elements"), TEXT("K2Node_MakeArray") },
1438
+ { TEXT("MakeStruct"), TEXT("Variables"), TEXT("Construct a struct from members"), TEXT("K2Node_MakeStruct") },
1439
+ { TEXT("BreakStruct"), TEXT("Variables"), TEXT("Break a struct into its members"), TEXT("K2Node_BreakStruct") },
1440
+
1441
+ // Math
1442
+ { TEXT("Add"), TEXT("Math"), TEXT("Add two values (int, float, vector)"), TEXT("K2Node_CallFunction") },
1443
+ { TEXT("Subtract"), TEXT("Math"), TEXT("Subtract two values"), TEXT("K2Node_CallFunction") },
1444
+ { TEXT("Multiply"), TEXT("Math"), TEXT("Multiply two values"), TEXT("K2Node_CallFunction") },
1445
+ { TEXT("Divide"), TEXT("Math"), TEXT("Divide two values"), TEXT("K2Node_CallFunction") },
1446
+ { TEXT("RandomFloat"), TEXT("Math"), TEXT("Generate random float in range"), TEXT("K2Node_CallFunction") },
1447
+ { TEXT("Clamp"), TEXT("Math"), TEXT("Clamp value between min and max"), TEXT("K2Node_CallFunction") },
1448
+ { TEXT("Lerp"), TEXT("Math"), TEXT("Linear interpolation"), TEXT("K2Node_CallFunction") },
1449
+ { TEXT("VectorLength"), TEXT("Math"), TEXT("Get length of a vector"), TEXT("K2Node_CallFunction") },
1450
+ { TEXT("Normalize"), TEXT("Math"), TEXT("Normalize a vector"), TEXT("K2Node_CallFunction") },
1451
+
1452
+ // Casting & Type
1453
+ { TEXT("Cast"), TEXT("Casting"), TEXT("Cast to a specific class"), TEXT("K2Node_DynamicCast") },
1454
+ { TEXT("IsValid"), TEXT("Casting"), TEXT("Check if object reference is valid"), TEXT("K2Node_CallFunction") },
1455
+ { TEXT("ClassIsChildOf"), TEXT("Casting"), TEXT("Check class inheritance"), TEXT("K2Node_CallFunction") },
1456
+
1457
+ // String
1458
+ { TEXT("Format"), TEXT("String"), TEXT("Format text with arguments"), TEXT("K2Node_FormatText") },
1459
+ { TEXT("Append"), TEXT("String"), TEXT("Concatenate strings"), TEXT("K2Node_CallFunction") },
1460
+ { TEXT("Contains"), TEXT("String"), TEXT("Check if string contains substring"), TEXT("K2Node_CallFunction") },
1461
+
1462
+ // Utility
1463
+ { TEXT("CreateWidget"), TEXT("Utility"), TEXT("Create a UMG widget instance"), TEXT("K2Node_CreateWidget") },
1464
+ { TEXT("Macro"), TEXT("Utility"), TEXT("Instance of a macro graph"), TEXT("K2Node_MacroInstance") },
1465
+ { TEXT("Comment"), TEXT("Utility"), TEXT("Comment box for organizing graphs"), TEXT("EdGraphNode_Comment") },
1466
+ { TEXT("Reroute"), TEXT("Utility"), TEXT("Reroute node for cleaner wiring"), TEXT("K2Node_Knot") },
1467
+ };
1468
+
1469
+ FString FilterCategory;
1470
+ Params->TryGetStringField(TEXT("category"), FilterCategory);
1471
+ FString LowerFilter = FilterCategory.ToLower();
1472
+
1473
+ TArray<TSharedPtr<FJsonValue>> NodeTypesArray;
1474
+ for (const FNodeTypeEntry& Entry : CommonNodes)
1475
+ {
1476
+ if (!LowerFilter.IsEmpty())
1477
+ {
1478
+ FString EntryCat = FString(Entry.Category).ToLower();
1479
+ if (!EntryCat.Contains(LowerFilter))
1480
+ {
1481
+ continue;
1482
+ }
1483
+ }
1484
+
1485
+ TSharedPtr<FJsonObject> NodeObj = MakeShared<FJsonObject>();
1486
+ NodeObj->SetStringField(TEXT("name"), Entry.Name);
1487
+ NodeObj->SetStringField(TEXT("category"), Entry.Category);
1488
+ NodeObj->SetStringField(TEXT("description"), Entry.Description);
1489
+ NodeObj->SetStringField(TEXT("className"), Entry.ClassName);
1490
+ NodeTypesArray.Add(MakeShared<FJsonValueObject>(NodeObj));
1491
+ }
1492
+
1493
+ Result->SetArrayField(TEXT("nodeTypes"), NodeTypesArray);
1494
+ Result->SetNumberField(TEXT("count"), NodeTypesArray.Num());
1495
+ Result->SetBoolField(TEXT("success"), true);
1496
+
1497
+ return MakeShared<FJsonValueObject>(Result);
1498
+ }
1499
+
1500
+ TSharedPtr<FJsonValue> FBlueprintHandlers::SearchCallableFunctions(const TSharedPtr<FJsonObject>& Params)
1501
+ {
1502
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1503
+
1504
+ FString Query;
1505
+ if (!Params->TryGetStringField(TEXT("query"), Query))
1506
+ {
1507
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'query' parameter"));
1508
+ Result->SetBoolField(TEXT("success"), false);
1509
+ return MakeShared<FJsonValueObject>(Result);
1510
+ }
1511
+
1512
+ int32 MaxResults = 50;
1513
+ Params->TryGetNumberField(TEXT("maxResults"), MaxResults);
1514
+
1515
+ FString LowerQuery = Query.ToLower();
1516
+
1517
+ // Build the list of library classes to search
1518
+ TArray<UClass*> LibraryClasses;
1519
+ LibraryClasses.Add(UKismetSystemLibrary::StaticClass());
1520
+ LibraryClasses.Add(UGameplayStatics::StaticClass());
1521
+ LibraryClasses.Add(UKismetMathLibrary::StaticClass());
1522
+ LibraryClasses.Add(UKismetStringLibrary::StaticClass());
1523
+ LibraryClasses.Add(UKismetArrayLibrary::StaticClass());
1524
+ LibraryClasses.Add(AActor::StaticClass());
1525
+ LibraryClasses.Add(APawn::StaticClass());
1526
+ LibraryClasses.Add(APlayerController::StaticClass());
1527
+
1528
+ // Optionally filter by a specific class
1529
+ FString TargetClassName;
1530
+ if (Params->TryGetStringField(TEXT("targetClass"), TargetClassName))
1531
+ {
1532
+ UClass* TargetClass = FindObject<UClass>(nullptr, *TargetClassName);
1533
+ if (!TargetClass)
1534
+ {
1535
+ TargetClass = FindObject<UClass>(nullptr, *(TEXT("U") + TargetClassName));
1536
+ }
1537
+ if (!TargetClass)
1538
+ {
1539
+ TargetClass = FindObject<UClass>(nullptr, *(TEXT("A") + TargetClassName));
1540
+ }
1541
+ if (TargetClass)
1542
+ {
1543
+ LibraryClasses.Empty();
1544
+ LibraryClasses.Add(TargetClass);
1545
+ }
1546
+ }
1547
+
1548
+ TArray<TSharedPtr<FJsonValue>> ResultsArray;
1549
+
1550
+ for (UClass* SearchClass : LibraryClasses)
1551
+ {
1552
+ if (!SearchClass) continue;
1553
+
1554
+ for (TFieldIterator<UFunction> FuncIt(SearchClass); FuncIt; ++FuncIt)
1555
+ {
1556
+ UFunction* Func = *FuncIt;
1557
+ if (!Func) continue;
1558
+
1559
+ // Only include blueprint-callable functions
1560
+ if (!Func->HasAnyFunctionFlags(FUNC_BlueprintCallable | FUNC_BlueprintPure)) continue;
1561
+
1562
+ FString FuncName = Func->GetName();
1563
+ FString LowerFuncName = FuncName.ToLower();
1564
+
1565
+ if (!LowerFuncName.Contains(LowerQuery)) continue;
1566
+
1567
+ TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
1568
+ Entry->SetStringField(TEXT("name"), FuncName);
1569
+ Entry->SetStringField(TEXT("class"), SearchClass->GetName());
1570
+ Entry->SetStringField(TEXT("fullPath"), Func->GetPathName());
1571
+
1572
+ // Pure vs impure
1573
+ Entry->SetBoolField(TEXT("isPure"), Func->HasAnyFunctionFlags(FUNC_BlueprintPure));
1574
+ Entry->SetBoolField(TEXT("isStatic"), Func->HasAnyFunctionFlags(FUNC_Static));
1575
+
1576
+ // Collect parameters info
1577
+ TArray<TSharedPtr<FJsonValue>> ParamsArray;
1578
+ FString ReturnType;
1579
+ for (TFieldIterator<FProperty> PropIt(Func); PropIt; ++PropIt)
1580
+ {
1581
+ FProperty* Prop = *PropIt;
1582
+ if (!Prop) continue;
1583
+
1584
+ if (Prop->HasAnyPropertyFlags(CPF_ReturnParm))
1585
+ {
1586
+ ReturnType = Prop->GetCPPType();
1587
+ }
1588
+ else if (Prop->HasAnyPropertyFlags(CPF_Parm))
1589
+ {
1590
+ TSharedPtr<FJsonObject> ParamObj = MakeShared<FJsonObject>();
1591
+ ParamObj->SetStringField(TEXT("name"), Prop->GetName());
1592
+ ParamObj->SetStringField(TEXT("type"), Prop->GetCPPType());
1593
+ ParamObj->SetBoolField(TEXT("isOutput"), Prop->HasAnyPropertyFlags(CPF_OutParm));
1594
+ ParamsArray.Add(MakeShared<FJsonValueObject>(ParamObj));
1595
+ }
1596
+ }
1597
+ Entry->SetArrayField(TEXT("parameters"), ParamsArray);
1598
+ if (!ReturnType.IsEmpty())
1599
+ {
1600
+ Entry->SetStringField(TEXT("returnType"), ReturnType);
1601
+ }
1602
+
1603
+ // Tooltip from metadata
1604
+ FString Tooltip = Func->GetMetaData(TEXT("ToolTip"));
1605
+ if (!Tooltip.IsEmpty())
1606
+ {
1607
+ Entry->SetStringField(TEXT("tooltip"), Tooltip);
1608
+ }
1609
+
1610
+ ResultsArray.Add(MakeShared<FJsonValueObject>(Entry));
1611
+
1612
+ if (ResultsArray.Num() >= MaxResults)
1613
+ {
1614
+ break;
1615
+ }
1616
+ }
1617
+
1618
+ if (ResultsArray.Num() >= MaxResults)
1619
+ {
1620
+ break;
1621
+ }
1622
+ }
1623
+
1624
+ Result->SetArrayField(TEXT("results"), ResultsArray);
1625
+ Result->SetNumberField(TEXT("count"), ResultsArray.Num());
1626
+ Result->SetStringField(TEXT("query"), Query);
1627
+ Result->SetBoolField(TEXT("success"), true);
1628
+
1629
+ return MakeShared<FJsonValueObject>(Result);
1630
+ }
1631
+
1632
+ TSharedPtr<FJsonValue> FBlueprintHandlers::ConnectPins(const TSharedPtr<FJsonObject>& Params)
1633
+ {
1634
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1635
+
1636
+ FString AssetPath;
1637
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1638
+ {
1639
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1640
+ Result->SetBoolField(TEXT("success"), false);
1641
+ return MakeShared<FJsonValueObject>(Result);
1642
+ }
1643
+
1644
+ FString GraphName = TEXT("EventGraph");
1645
+ Params->TryGetStringField(TEXT("graphName"), GraphName);
1646
+
1647
+ FString SourceNodeId;
1648
+ if (!Params->TryGetStringField(TEXT("sourceNodeId"), SourceNodeId) && !Params->TryGetStringField(TEXT("sourceNode"), SourceNodeId))
1649
+ {
1650
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'sourceNode' parameter"));
1651
+ Result->SetBoolField(TEXT("success"), false);
1652
+ return MakeShared<FJsonValueObject>(Result);
1653
+ }
1654
+
1655
+ FString SourcePinName;
1656
+ if (!Params->TryGetStringField(TEXT("sourcePinName"), SourcePinName) && !Params->TryGetStringField(TEXT("sourcePin"), SourcePinName))
1657
+ {
1658
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'sourcePin' parameter"));
1659
+ Result->SetBoolField(TEXT("success"), false);
1660
+ return MakeShared<FJsonValueObject>(Result);
1661
+ }
1662
+
1663
+ FString TargetNodeId;
1664
+ if (!Params->TryGetStringField(TEXT("targetNodeId"), TargetNodeId) && !Params->TryGetStringField(TEXT("targetNode"), TargetNodeId))
1665
+ {
1666
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'targetNode' parameter"));
1667
+ Result->SetBoolField(TEXT("success"), false);
1668
+ return MakeShared<FJsonValueObject>(Result);
1669
+ }
1670
+
1671
+ FString TargetPinName;
1672
+ if (!Params->TryGetStringField(TEXT("targetPinName"), TargetPinName) && !Params->TryGetStringField(TEXT("targetPin"), TargetPinName))
1673
+ {
1674
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'targetPin' parameter"));
1675
+ Result->SetBoolField(TEXT("success"), false);
1676
+ return MakeShared<FJsonValueObject>(Result);
1677
+ }
1678
+
1679
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1680
+ if (!Blueprint)
1681
+ {
1682
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1683
+ Result->SetBoolField(TEXT("success"), false);
1684
+ return MakeShared<FJsonValueObject>(Result);
1685
+ }
1686
+
1687
+ UEdGraph* TargetGraph = FindGraph(Blueprint, GraphName);
1688
+ if (!TargetGraph)
1689
+ {
1690
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph not found: %s"), *GraphName));
1691
+ Result->SetBoolField(TEXT("success"), false);
1692
+ return MakeShared<FJsonValueObject>(Result);
1693
+ }
1694
+
1695
+ // Find source node
1696
+ UEdGraphNode* SourceNode = FindNodeByGuidOrName(TargetGraph, SourceNodeId);
1697
+ if (!SourceNode)
1698
+ {
1699
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Source node not found: %s"), *SourceNodeId));
1700
+ Result->SetBoolField(TEXT("success"), false);
1701
+ return MakeShared<FJsonValueObject>(Result);
1702
+ }
1703
+
1704
+ // Find target node
1705
+ UEdGraphNode* TargetNode = FindNodeByGuidOrName(TargetGraph, TargetNodeId);
1706
+ if (!TargetNode)
1707
+ {
1708
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Target node not found: %s"), *TargetNodeId));
1709
+ Result->SetBoolField(TEXT("success"), false);
1710
+ return MakeShared<FJsonValueObject>(Result);
1711
+ }
1712
+
1713
+ // Find source pin
1714
+ UEdGraphPin* SourcePin = nullptr;
1715
+ for (UEdGraphPin* Pin : SourceNode->Pins)
1716
+ {
1717
+ if (Pin && Pin->PinName.ToString() == SourcePinName)
1718
+ {
1719
+ SourcePin = Pin;
1720
+ break;
1721
+ }
1722
+ }
1723
+ if (!SourcePin)
1724
+ {
1725
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Source pin not found: '%s' on node '%s'"), *SourcePinName, *SourceNodeId));
1726
+ Result->SetBoolField(TEXT("success"), false);
1727
+ return MakeShared<FJsonValueObject>(Result);
1728
+ }
1729
+
1730
+ // Find target pin
1731
+ UEdGraphPin* TargetPin = nullptr;
1732
+ for (UEdGraphPin* Pin : TargetNode->Pins)
1733
+ {
1734
+ if (Pin && Pin->PinName.ToString() == TargetPinName)
1735
+ {
1736
+ TargetPin = Pin;
1737
+ break;
1738
+ }
1739
+ }
1740
+ if (!TargetPin)
1741
+ {
1742
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Target pin not found: '%s' on node '%s'"), *TargetPinName, *TargetNodeId));
1743
+ Result->SetBoolField(TEXT("success"), false);
1744
+ return MakeShared<FJsonValueObject>(Result);
1745
+ }
1746
+
1747
+ // Try to create connection
1748
+ const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
1749
+ bool bConnected = Schema->TryCreateConnection(SourcePin, TargetPin);
1750
+
1751
+ if (bConnected)
1752
+ {
1753
+ // Compile and save
1754
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1755
+ UPackage* Package = Blueprint->GetOutermost();
1756
+ if (Package)
1757
+ {
1758
+ Package->MarkPackageDirty();
1759
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1760
+ FSavePackageArgs SaveArgs;
1761
+ SaveArgs.TopLevelFlags = RF_Standalone;
1762
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1763
+ }
1764
+
1765
+ Result->SetStringField(TEXT("path"), AssetPath);
1766
+ Result->SetStringField(TEXT("graphName"), GraphName);
1767
+ Result->SetStringField(TEXT("sourceNodeId"), SourceNodeId);
1768
+ Result->SetStringField(TEXT("sourcePinName"), SourcePinName);
1769
+ Result->SetStringField(TEXT("targetNodeId"), TargetNodeId);
1770
+ Result->SetStringField(TEXT("targetPinName"), TargetPinName);
1771
+ Result->SetBoolField(TEXT("success"), true);
1772
+ }
1773
+ else
1774
+ {
1775
+ // Get more info about why it failed
1776
+ FString ErrorMsg = TEXT("TryCreateConnection failed. Pins may be incompatible.");
1777
+ FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin);
1778
+ if (!Response.Message.IsEmpty())
1779
+ {
1780
+ ErrorMsg = FString::Printf(TEXT("Connection failed: %s"), *Response.Message.ToString());
1781
+ }
1782
+ Result->SetStringField(TEXT("error"), ErrorMsg);
1783
+ Result->SetBoolField(TEXT("success"), false);
1784
+ }
1785
+
1786
+ return MakeShared<FJsonValueObject>(Result);
1787
+ }
1788
+
1789
+ TSharedPtr<FJsonValue> FBlueprintHandlers::DeleteNode(const TSharedPtr<FJsonObject>& Params)
1790
+ {
1791
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1792
+
1793
+ FString AssetPath;
1794
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1795
+ {
1796
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1797
+ Result->SetBoolField(TEXT("success"), false);
1798
+ return MakeShared<FJsonValueObject>(Result);
1799
+ }
1800
+
1801
+ FString GraphName = TEXT("EventGraph");
1802
+ Params->TryGetStringField(TEXT("graphName"), GraphName);
1803
+
1804
+ FString NodeId;
1805
+ if (!Params->TryGetStringField(TEXT("nodeId"), NodeId) && !Params->TryGetStringField(TEXT("nodeName"), NodeId))
1806
+ {
1807
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeId' or 'nodeName' parameter"));
1808
+ Result->SetBoolField(TEXT("success"), false);
1809
+ return MakeShared<FJsonValueObject>(Result);
1810
+ }
1811
+
1812
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1813
+ if (!Blueprint)
1814
+ {
1815
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1816
+ Result->SetBoolField(TEXT("success"), false);
1817
+ return MakeShared<FJsonValueObject>(Result);
1818
+ }
1819
+
1820
+ UEdGraph* TargetGraph = FindGraph(Blueprint, GraphName);
1821
+ if (!TargetGraph)
1822
+ {
1823
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph not found: %s"), *GraphName));
1824
+ Result->SetBoolField(TEXT("success"), false);
1825
+ return MakeShared<FJsonValueObject>(Result);
1826
+ }
1827
+
1828
+ // Find the node
1829
+ UEdGraphNode* NodeToDelete = FindNodeByGuidOrName(TargetGraph, NodeId);
1830
+ if (!NodeToDelete)
1831
+ {
1832
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Node not found: %s"), *NodeId));
1833
+ Result->SetBoolField(TEXT("success"), false);
1834
+ return MakeShared<FJsonValueObject>(Result);
1835
+ }
1836
+
1837
+ // Break all pin links before removing
1838
+ NodeToDelete->BreakAllNodeLinks();
1839
+
1840
+ // Remove node from graph
1841
+ TargetGraph->RemoveNode(NodeToDelete);
1842
+
1843
+ // Compile and save
1844
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1845
+ UPackage* Package = Blueprint->GetOutermost();
1846
+ if (Package)
1847
+ {
1848
+ Package->MarkPackageDirty();
1849
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1850
+ FSavePackageArgs SaveArgs;
1851
+ SaveArgs.TopLevelFlags = RF_Standalone;
1852
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1853
+ }
1854
+
1855
+ Result->SetStringField(TEXT("path"), AssetPath);
1856
+ Result->SetStringField(TEXT("graphName"), GraphName);
1857
+ Result->SetStringField(TEXT("nodeId"), NodeId);
1858
+ Result->SetBoolField(TEXT("success"), true);
1859
+
1860
+ return MakeShared<FJsonValueObject>(Result);
1861
+ }
1862
+
1863
+ TSharedPtr<FJsonValue> FBlueprintHandlers::SetNodeProperty(const TSharedPtr<FJsonObject>& Params)
1864
+ {
1865
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1866
+
1867
+ FString AssetPath;
1868
+ if (!Params->TryGetStringField(TEXT("path"), AssetPath) && !Params->TryGetStringField(TEXT("assetPath"), AssetPath))
1869
+ {
1870
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
1871
+ Result->SetBoolField(TEXT("success"), false);
1872
+ return MakeShared<FJsonValueObject>(Result);
1873
+ }
1874
+
1875
+ FString GraphName = TEXT("EventGraph");
1876
+ Params->TryGetStringField(TEXT("graphName"), GraphName);
1877
+
1878
+ FString NodeId;
1879
+ if (!Params->TryGetStringField(TEXT("nodeId"), NodeId) && !Params->TryGetStringField(TEXT("nodeName"), NodeId))
1880
+ {
1881
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeId' or 'nodeName' parameter"));
1882
+ Result->SetBoolField(TEXT("success"), false);
1883
+ return MakeShared<FJsonValueObject>(Result);
1884
+ }
1885
+
1886
+ FString PinName;
1887
+ if (!Params->TryGetStringField(TEXT("pinName"), PinName) && !Params->TryGetStringField(TEXT("propertyName"), PinName))
1888
+ {
1889
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'pinName' or 'propertyName' parameter"));
1890
+ Result->SetBoolField(TEXT("success"), false);
1891
+ return MakeShared<FJsonValueObject>(Result);
1892
+ }
1893
+
1894
+ FString DefaultValue;
1895
+ if (!Params->TryGetStringField(TEXT("defaultValue"), DefaultValue) && !Params->TryGetStringField(TEXT("value"), DefaultValue))
1896
+ {
1897
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'defaultValue' or 'value' parameter"));
1898
+ Result->SetBoolField(TEXT("success"), false);
1899
+ return MakeShared<FJsonValueObject>(Result);
1900
+ }
1901
+
1902
+ UBlueprint* Blueprint = LoadBlueprint(AssetPath);
1903
+ if (!Blueprint)
1904
+ {
1905
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Blueprint not found: %s"), *AssetPath));
1906
+ Result->SetBoolField(TEXT("success"), false);
1907
+ return MakeShared<FJsonValueObject>(Result);
1908
+ }
1909
+
1910
+ UEdGraph* TargetGraph = FindGraph(Blueprint, GraphName);
1911
+ if (!TargetGraph)
1912
+ {
1913
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Graph not found: %s"), *GraphName));
1914
+ Result->SetBoolField(TEXT("success"), false);
1915
+ return MakeShared<FJsonValueObject>(Result);
1916
+ }
1917
+
1918
+ // Find the node
1919
+ UEdGraphNode* TargetNode = FindNodeByGuidOrName(TargetGraph, NodeId);
1920
+ if (!TargetNode)
1921
+ {
1922
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Node not found: %s"), *NodeId));
1923
+ Result->SetBoolField(TEXT("success"), false);
1924
+ return MakeShared<FJsonValueObject>(Result);
1925
+ }
1926
+
1927
+ // Find the pin on the node
1928
+ UEdGraphPin* TargetPin = nullptr;
1929
+ for (UEdGraphPin* Pin : TargetNode->Pins)
1930
+ {
1931
+ if (Pin && Pin->PinName.ToString() == PinName)
1932
+ {
1933
+ TargetPin = Pin;
1934
+ break;
1935
+ }
1936
+ }
1937
+
1938
+ if (!TargetPin)
1939
+ {
1940
+ // List available pins for better error message
1941
+ TArray<FString> PinNames;
1942
+ for (UEdGraphPin* Pin : TargetNode->Pins)
1943
+ {
1944
+ if (Pin)
1945
+ {
1946
+ PinNames.Add(Pin->PinName.ToString());
1947
+ }
1948
+ }
1949
+ FString AvailablePins = FString::Join(PinNames, TEXT(", "));
1950
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Pin not found: '%s'. Available pins: [%s]"), *PinName, *AvailablePins));
1951
+ Result->SetBoolField(TEXT("success"), false);
1952
+ return MakeShared<FJsonValueObject>(Result);
1953
+ }
1954
+
1955
+ // Set the default value using the schema
1956
+ const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
1957
+ Schema->TrySetDefaultValue(*TargetPin, DefaultValue);
1958
+
1959
+ // Notify the node that a pin default changed
1960
+ TargetNode->PinDefaultValueChanged(TargetPin);
1961
+
1962
+ // Compile and save
1963
+ FKismetEditorUtilities::CompileBlueprint(Blueprint);
1964
+ UPackage* Package = Blueprint->GetOutermost();
1965
+ if (Package)
1966
+ {
1967
+ Package->MarkPackageDirty();
1968
+ FString PackageFileName = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
1969
+ FSavePackageArgs SaveArgs;
1970
+ SaveArgs.TopLevelFlags = RF_Standalone;
1971
+ UPackage::SavePackage(Package, nullptr, *PackageFileName, SaveArgs);
1972
+ }
1973
+
1974
+ Result->SetStringField(TEXT("path"), AssetPath);
1975
+ Result->SetStringField(TEXT("graphName"), GraphName);
1976
+ Result->SetStringField(TEXT("nodeId"), NodeId);
1977
+ Result->SetStringField(TEXT("pinName"), PinName);
1978
+ Result->SetStringField(TEXT("defaultValue"), DefaultValue);
1979
+ Result->SetBoolField(TEXT("success"), true);
1980
+
1981
+ return MakeShared<FJsonValueObject>(Result);
1982
+ }