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,1024 @@
1
+ #include "PCGHandlers.h"
2
+ #include "HandlerRegistry.h"
3
+ #include "AssetRegistry/AssetRegistryModule.h"
4
+ #include "AssetToolsModule.h"
5
+ #include "IAssetTools.h"
6
+ #include "EditorScriptingUtilities/Public/EditorAssetLibrary.h"
7
+ #include "UObject/UObjectGlobals.h"
8
+ #include "UObject/TopLevelAssetPath.h"
9
+ #include "Dom/JsonObject.h"
10
+ #include "Dom/JsonValue.h"
11
+ #include "Editor.h"
12
+ #include "Engine/World.h"
13
+ #include "EngineUtils.h"
14
+ #include "PCGGraph.h"
15
+ // PCGGraphInterface.h may not be directly includable in 5.7
16
+ #include "PCGComponent.h"
17
+ #include "PCGNode.h"
18
+ #include "PCGSettings.h"
19
+ #include "PCGPin.h"
20
+ #include "PCGEdge.h"
21
+ #include "PCGVolume.h"
22
+ #include "UObject/Package.h"
23
+ #include "Misc/PackageName.h"
24
+ #include "UObject/SavePackage.h"
25
+ #include "GameFramework/Actor.h"
26
+
27
+ void FPCGHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
28
+ {
29
+ Registry.RegisterHandler(TEXT("list_pcg_graphs"), &ListPCGGraphs);
30
+ Registry.RegisterHandler(TEXT("get_pcg_components"), &GetPCGComponents);
31
+ Registry.RegisterHandler(TEXT("create_pcg_graph"), &CreatePCGGraph);
32
+ Registry.RegisterHandler(TEXT("read_pcg_graph"), &ReadPCGGraph);
33
+ Registry.RegisterHandler(TEXT("add_pcg_node"), &AddPCGNode);
34
+ Registry.RegisterHandler(TEXT("connect_pcg_nodes"), &ConnectPCGNodes);
35
+ Registry.RegisterHandler(TEXT("remove_pcg_node"), &RemovePCGNode);
36
+ Registry.RegisterHandler(TEXT("set_pcg_node_settings"), &SetPCGNodeSettings);
37
+ Registry.RegisterHandler(TEXT("execute_pcg_graph"), &ExecutePCGGraph);
38
+ Registry.RegisterHandler(TEXT("spawn_pcg_volume"), &SpawnPCGVolume);
39
+ Registry.RegisterHandler(TEXT("add_pcg_volume"), &SpawnPCGVolume);
40
+ Registry.RegisterHandler(TEXT("read_pcg_node_settings"), &ReadPCGNodeSettings);
41
+ Registry.RegisterHandler(TEXT("get_pcg_component_details"), &GetPCGComponentDetails);
42
+ }
43
+
44
+ TSharedPtr<FJsonValue> FPCGHandlers::ListPCGGraphs(const TSharedPtr<FJsonObject>& Params)
45
+ {
46
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
47
+
48
+ IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
49
+ TArray<FAssetData> Assets;
50
+ AR.GetAssetsByClass(FTopLevelAssetPath(TEXT("/Script/PCG"), TEXT("PCGGraph")), Assets, true);
51
+
52
+ TArray<TSharedPtr<FJsonValue>> AssetArray;
53
+ for (const FAssetData& Asset : Assets)
54
+ {
55
+ TSharedPtr<FJsonObject> AssetObj = MakeShared<FJsonObject>();
56
+ AssetObj->SetStringField(TEXT("name"), Asset.AssetName.ToString());
57
+ AssetObj->SetStringField(TEXT("path"), Asset.GetObjectPathString());
58
+ AssetArray.Add(MakeShared<FJsonValueObject>(AssetObj));
59
+ }
60
+ Result->SetArrayField(TEXT("graphs"), AssetArray);
61
+ Result->SetNumberField(TEXT("count"), AssetArray.Num());
62
+ Result->SetBoolField(TEXT("success"), true);
63
+ return MakeShared<FJsonValueObject>(Result);
64
+ }
65
+
66
+ TSharedPtr<FJsonValue> FPCGHandlers::GetPCGComponents(const TSharedPtr<FJsonObject>& Params)
67
+ {
68
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
69
+
70
+ UWorld* World = GEditor->GetEditorWorldContext().World();
71
+ if (!World)
72
+ {
73
+ Result->SetStringField(TEXT("error"), TEXT("No editor world available"));
74
+ Result->SetBoolField(TEXT("success"), false);
75
+ return MakeShared<FJsonValueObject>(Result);
76
+ }
77
+
78
+ TArray<TSharedPtr<FJsonValue>> CompArray;
79
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
80
+ {
81
+ AActor* Actor = *ActorIt;
82
+ if (!Actor) continue;
83
+
84
+ TArray<UPCGComponent*> PCGComps;
85
+ Actor->GetComponents<UPCGComponent>(PCGComps);
86
+ for (UPCGComponent* PCGComp : PCGComps)
87
+ {
88
+ if (!PCGComp) continue;
89
+ TSharedPtr<FJsonObject> CompObj = MakeShared<FJsonObject>();
90
+ CompObj->SetStringField(TEXT("actorName"), Actor->GetActorLabel());
91
+ CompObj->SetStringField(TEXT("actorClass"), Actor->GetClass()->GetName());
92
+ CompObj->SetStringField(TEXT("componentName"), PCGComp->GetName());
93
+ if (PCGComp->GetGraph())
94
+ {
95
+ CompObj->SetStringField(TEXT("graphName"), PCGComp->GetGraph()->GetName());
96
+ }
97
+ CompArray.Add(MakeShared<FJsonValueObject>(CompObj));
98
+ }
99
+ }
100
+
101
+ Result->SetArrayField(TEXT("components"), CompArray);
102
+ Result->SetNumberField(TEXT("count"), CompArray.Num());
103
+ Result->SetBoolField(TEXT("success"), true);
104
+ return MakeShared<FJsonValueObject>(Result);
105
+ }
106
+
107
+ TSharedPtr<FJsonValue> FPCGHandlers::CreatePCGGraph(const TSharedPtr<FJsonObject>& Params)
108
+ {
109
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
110
+
111
+ FString Name;
112
+ if (!Params->TryGetStringField(TEXT("name"), Name))
113
+ {
114
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'name' parameter"));
115
+ Result->SetBoolField(TEXT("success"), false);
116
+ return MakeShared<FJsonValueObject>(Result);
117
+ }
118
+
119
+ FString PackagePath = TEXT("/Game/PCG");
120
+ Params->TryGetStringField(TEXT("packagePath"), PackagePath);
121
+
122
+ FString FullAssetPath = PackagePath + TEXT("/") + Name;
123
+ UEditorAssetLibrary::DeleteAsset(FullAssetPath);
124
+
125
+ FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
126
+ IAssetTools& AssetTools = AssetToolsModule.Get();
127
+
128
+ UObject* NewAsset = AssetTools.CreateAsset(Name, PackagePath, UPCGGraph::StaticClass(), nullptr);
129
+ if (!NewAsset)
130
+ {
131
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create PCGGraph"));
132
+ Result->SetBoolField(TEXT("success"), false);
133
+ return MakeShared<FJsonValueObject>(Result);
134
+ }
135
+
136
+ UEditorAssetLibrary::SaveAsset(NewAsset->GetPathName());
137
+
138
+ Result->SetStringField(TEXT("path"), NewAsset->GetPathName());
139
+ Result->SetStringField(TEXT("name"), Name);
140
+ Result->SetBoolField(TEXT("success"), true);
141
+ return MakeShared<FJsonValueObject>(Result);
142
+ }
143
+
144
+ TSharedPtr<FJsonValue> FPCGHandlers::ReadPCGGraph(const TSharedPtr<FJsonObject>& Params)
145
+ {
146
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
147
+
148
+ FString AssetPath;
149
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
150
+ {
151
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
152
+ Result->SetBoolField(TEXT("success"), false);
153
+ return MakeShared<FJsonValueObject>(Result);
154
+ }
155
+
156
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *AssetPath);
157
+ if (!Graph)
158
+ {
159
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCGGraph not found: %s"), *AssetPath));
160
+ Result->SetBoolField(TEXT("success"), false);
161
+ return MakeShared<FJsonValueObject>(Result);
162
+ }
163
+
164
+ Result->SetStringField(TEXT("name"), Graph->GetName());
165
+ Result->SetStringField(TEXT("path"), AssetPath);
166
+
167
+ const auto& Nodes = Graph->GetNodes();
168
+ TArray<TSharedPtr<FJsonValue>> NodeArray;
169
+ for (const UPCGNode* Node : Nodes)
170
+ {
171
+ if (!Node) continue;
172
+ TSharedPtr<FJsonObject> NodeObj = MakeShared<FJsonObject>();
173
+ NodeObj->SetStringField(TEXT("name"), Node->GetName());
174
+ NodeObj->SetStringField(TEXT("title"), Node->GetNodeTitle(EPCGNodeTitleType::ListView).ToString());
175
+ NodeArray.Add(MakeShared<FJsonValueObject>(NodeObj));
176
+ }
177
+
178
+ Result->SetArrayField(TEXT("nodes"), NodeArray);
179
+ Result->SetNumberField(TEXT("nodeCount"), NodeArray.Num());
180
+ Result->SetBoolField(TEXT("success"), true);
181
+ return MakeShared<FJsonValueObject>(Result);
182
+ }
183
+
184
+ TSharedPtr<FJsonValue> FPCGHandlers::AddPCGNode(const TSharedPtr<FJsonObject>& Params)
185
+ {
186
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
187
+
188
+ FString AssetPath;
189
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
190
+ {
191
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
192
+ Result->SetBoolField(TEXT("success"), false);
193
+ return MakeShared<FJsonValueObject>(Result);
194
+ }
195
+
196
+ FString NodeType;
197
+ if (!Params->TryGetStringField(TEXT("nodeType"), NodeType))
198
+ {
199
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeType' parameter"));
200
+ Result->SetBoolField(TEXT("success"), false);
201
+ return MakeShared<FJsonValueObject>(Result);
202
+ }
203
+
204
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *AssetPath);
205
+ if (!Graph)
206
+ {
207
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCGGraph not found: %s"), *AssetPath));
208
+ Result->SetBoolField(TEXT("success"), false);
209
+ return MakeShared<FJsonValueObject>(Result);
210
+ }
211
+
212
+ // Find the settings class by name
213
+ UClass* SettingsClass = FindObject<UClass>(nullptr, *NodeType);
214
+ if (!SettingsClass)
215
+ {
216
+ // Try with /Script/PCG prefix
217
+ SettingsClass = FindObject<UClass>(nullptr, *(TEXT("/Script/PCG.") + NodeType));
218
+ }
219
+ if (!SettingsClass)
220
+ {
221
+ // Try with U prefix stripped
222
+ FString CleanName = NodeType;
223
+ if (!CleanName.StartsWith(TEXT("U")))
224
+ {
225
+ SettingsClass = FindObject<UClass>(nullptr, *(TEXT("/Script/PCG.U") + CleanName));
226
+ }
227
+ }
228
+ if (!SettingsClass || !SettingsClass->IsChildOf(UPCGSettings::StaticClass()))
229
+ {
230
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCG settings class not found or invalid: %s"), *NodeType));
231
+ Result->SetBoolField(TEXT("success"), false);
232
+ return MakeShared<FJsonValueObject>(Result);
233
+ }
234
+
235
+ // Create default settings object
236
+ UPCGSettings* DefaultSettings = NewObject<UPCGSettings>(GetTransientPackage(), SettingsClass);
237
+ if (!DefaultSettings)
238
+ {
239
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create PCG settings instance"));
240
+ Result->SetBoolField(TEXT("success"), false);
241
+ return MakeShared<FJsonValueObject>(Result);
242
+ }
243
+
244
+ // Add node to graph
245
+ UPCGNode* NewNode = Graph->AddNode(DefaultSettings);
246
+ if (!NewNode)
247
+ {
248
+ Result->SetStringField(TEXT("error"), TEXT("Failed to add node to PCG graph"));
249
+ Result->SetBoolField(TEXT("success"), false);
250
+ return MakeShared<FJsonValueObject>(Result);
251
+ }
252
+
253
+ // Set position if provided
254
+ double PosX = 0, PosY = 0;
255
+ if (Params->TryGetNumberField(TEXT("posX"), PosX) || Params->TryGetNumberField(TEXT("posY"), PosY))
256
+ {
257
+ NewNode->PositionX = (int32)PosX;
258
+ NewNode->PositionY = (int32)PosY;
259
+ }
260
+
261
+ // Save the graph asset
262
+ UEditorAssetLibrary::SaveAsset(AssetPath);
263
+
264
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
265
+ Result->SetStringField(TEXT("nodeName"), NewNode->GetName());
266
+ Result->SetStringField(TEXT("nodeType"), NodeType);
267
+ Result->SetStringField(TEXT("nodeTitle"), NewNode->GetNodeTitle(EPCGNodeTitleType::ListView).ToString());
268
+ Result->SetBoolField(TEXT("success"), true);
269
+ return MakeShared<FJsonValueObject>(Result);
270
+ }
271
+
272
+ TSharedPtr<FJsonValue> FPCGHandlers::ConnectPCGNodes(const TSharedPtr<FJsonObject>& Params)
273
+ {
274
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
275
+
276
+ FString AssetPath;
277
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
278
+ {
279
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
280
+ Result->SetBoolField(TEXT("success"), false);
281
+ return MakeShared<FJsonValueObject>(Result);
282
+ }
283
+
284
+ FString SourceNodeName;
285
+ if (!Params->TryGetStringField(TEXT("sourceNodeName"), SourceNodeName) && !Params->TryGetStringField(TEXT("sourceNode"), SourceNodeName))
286
+ {
287
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'sourceNodeName' or 'sourceNode' parameter"));
288
+ Result->SetBoolField(TEXT("success"), false);
289
+ return MakeShared<FJsonValueObject>(Result);
290
+ }
291
+
292
+ FString TargetNodeName;
293
+ if (!Params->TryGetStringField(TEXT("targetNodeName"), TargetNodeName) && !Params->TryGetStringField(TEXT("targetNode"), TargetNodeName))
294
+ {
295
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'targetNodeName' or 'targetNode' parameter"));
296
+ Result->SetBoolField(TEXT("success"), false);
297
+ return MakeShared<FJsonValueObject>(Result);
298
+ }
299
+
300
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *AssetPath);
301
+ if (!Graph)
302
+ {
303
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCGGraph not found: %s"), *AssetPath));
304
+ Result->SetBoolField(TEXT("success"), false);
305
+ return MakeShared<FJsonValueObject>(Result);
306
+ }
307
+
308
+ // Find source and target nodes
309
+ UPCGNode* SourceNode = nullptr;
310
+ UPCGNode* TargetNode = nullptr;
311
+ const auto& Nodes = Graph->GetNodes();
312
+ for (UPCGNode* Node : Nodes)
313
+ {
314
+ if (!Node) continue;
315
+ if (Node->GetName() == SourceNodeName)
316
+ {
317
+ SourceNode = Node;
318
+ }
319
+ if (Node->GetName() == TargetNodeName)
320
+ {
321
+ TargetNode = Node;
322
+ }
323
+ }
324
+
325
+ // Also check the input and output nodes
326
+ if (!SourceNode && Graph->GetInputNode() && Graph->GetInputNode()->GetName() == SourceNodeName)
327
+ {
328
+ SourceNode = Graph->GetInputNode();
329
+ }
330
+ if (!TargetNode && Graph->GetOutputNode() && Graph->GetOutputNode()->GetName() == TargetNodeName)
331
+ {
332
+ TargetNode = Graph->GetOutputNode();
333
+ }
334
+
335
+ if (!SourceNode)
336
+ {
337
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Source node not found: %s"), *SourceNodeName));
338
+ Result->SetBoolField(TEXT("success"), false);
339
+ return MakeShared<FJsonValueObject>(Result);
340
+ }
341
+ if (!TargetNode)
342
+ {
343
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Target node not found: %s"), *TargetNodeName));
344
+ Result->SetBoolField(TEXT("success"), false);
345
+ return MakeShared<FJsonValueObject>(Result);
346
+ }
347
+
348
+ // Get pin labels if specified, otherwise use the first available pins
349
+ FString SourcePinLabel;
350
+ if (!Params->TryGetStringField(TEXT("sourcePinLabel"), SourcePinLabel))
351
+ {
352
+ Params->TryGetStringField(TEXT("sourcePin"), SourcePinLabel);
353
+ }
354
+ FString TargetPinLabel;
355
+ if (!Params->TryGetStringField(TEXT("targetPinLabel"), TargetPinLabel))
356
+ {
357
+ Params->TryGetStringField(TEXT("targetPin"), TargetPinLabel);
358
+ }
359
+
360
+ // UE 5.7: Pin and edge APIs refactored; use Graph->AddEdge() with node+label
361
+ // Resolve the pin labels to use for the connection
362
+ FName ResolvedSourcePinLabel = NAME_None;
363
+ FName ResolvedTargetPinLabel = NAME_None;
364
+
365
+ if (SourcePinLabel.IsEmpty())
366
+ {
367
+ // Use the first output pin's label
368
+ const TArray<TObjectPtr<UPCGPin>>& OutPins = SourceNode->GetOutputPins();
369
+ if (OutPins.Num() > 0 && OutPins[0])
370
+ {
371
+ ResolvedSourcePinLabel = OutPins[0]->Properties.Label;
372
+ }
373
+ }
374
+ else
375
+ {
376
+ ResolvedSourcePinLabel = FName(*SourcePinLabel);
377
+ }
378
+
379
+ if (TargetPinLabel.IsEmpty())
380
+ {
381
+ // Use the first input pin's label
382
+ const TArray<TObjectPtr<UPCGPin>>& InPins = TargetNode->GetInputPins();
383
+ if (InPins.Num() > 0 && InPins[0])
384
+ {
385
+ ResolvedTargetPinLabel = InPins[0]->Properties.Label;
386
+ }
387
+ }
388
+ else
389
+ {
390
+ ResolvedTargetPinLabel = FName(*TargetPinLabel);
391
+ }
392
+
393
+ if (ResolvedSourcePinLabel == NAME_None)
394
+ {
395
+ Result->SetStringField(TEXT("error"), TEXT("No suitable output pin found on source node"));
396
+ Result->SetBoolField(TEXT("success"), false);
397
+ return MakeShared<FJsonValueObject>(Result);
398
+ }
399
+ if (ResolvedTargetPinLabel == NAME_None)
400
+ {
401
+ Result->SetStringField(TEXT("error"), TEXT("No suitable input pin found on target node"));
402
+ Result->SetBoolField(TEXT("success"), false);
403
+ return MakeShared<FJsonValueObject>(Result);
404
+ }
405
+
406
+ UPCGNode* ResultNode = Graph->AddEdge(SourceNode, ResolvedSourcePinLabel, TargetNode, ResolvedTargetPinLabel);
407
+ if (!ResultNode)
408
+ {
409
+ Result->SetStringField(TEXT("error"), TEXT("Failed to connect pins - connection may already exist or be incompatible"));
410
+ Result->SetBoolField(TEXT("success"), false);
411
+ return MakeShared<FJsonValueObject>(Result);
412
+ }
413
+
414
+ // Save the graph asset
415
+ UEditorAssetLibrary::SaveAsset(AssetPath);
416
+
417
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
418
+ Result->SetStringField(TEXT("sourceNodeName"), SourceNodeName);
419
+ Result->SetStringField(TEXT("targetNodeName"), TargetNodeName);
420
+ Result->SetStringField(TEXT("sourcePinLabel"), ResolvedSourcePinLabel.ToString());
421
+ Result->SetStringField(TEXT("targetPinLabel"), ResolvedTargetPinLabel.ToString());
422
+ Result->SetBoolField(TEXT("success"), true);
423
+ return MakeShared<FJsonValueObject>(Result);
424
+ }
425
+
426
+ TSharedPtr<FJsonValue> FPCGHandlers::RemovePCGNode(const TSharedPtr<FJsonObject>& Params)
427
+ {
428
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
429
+
430
+ FString AssetPath;
431
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
432
+ {
433
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
434
+ Result->SetBoolField(TEXT("success"), false);
435
+ return MakeShared<FJsonValueObject>(Result);
436
+ }
437
+
438
+ FString NodeName;
439
+ if (!Params->TryGetStringField(TEXT("nodeName"), NodeName))
440
+ {
441
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeName' parameter"));
442
+ Result->SetBoolField(TEXT("success"), false);
443
+ return MakeShared<FJsonValueObject>(Result);
444
+ }
445
+
446
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *AssetPath);
447
+ if (!Graph)
448
+ {
449
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCGGraph not found: %s"), *AssetPath));
450
+ Result->SetBoolField(TEXT("success"), false);
451
+ return MakeShared<FJsonValueObject>(Result);
452
+ }
453
+
454
+ // Find the node by name
455
+ UPCGNode* FoundNode = nullptr;
456
+ const auto& Nodes = Graph->GetNodes();
457
+ for (UPCGNode* Node : Nodes)
458
+ {
459
+ if (Node && Node->GetName() == NodeName)
460
+ {
461
+ FoundNode = Node;
462
+ break;
463
+ }
464
+ }
465
+
466
+ if (!FoundNode)
467
+ {
468
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Node not found: %s"), *NodeName));
469
+ Result->SetBoolField(TEXT("success"), false);
470
+ return MakeShared<FJsonValueObject>(Result);
471
+ }
472
+
473
+ // Remove the node from the graph
474
+ Graph->RemoveNode(FoundNode);
475
+
476
+ // Save the graph asset
477
+ UEditorAssetLibrary::SaveAsset(AssetPath);
478
+
479
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
480
+ Result->SetStringField(TEXT("removedNodeName"), NodeName);
481
+ Result->SetBoolField(TEXT("success"), true);
482
+ return MakeShared<FJsonValueObject>(Result);
483
+ }
484
+
485
+ TSharedPtr<FJsonValue> FPCGHandlers::SetPCGNodeSettings(const TSharedPtr<FJsonObject>& Params)
486
+ {
487
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
488
+
489
+ FString AssetPath;
490
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
491
+ {
492
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
493
+ Result->SetBoolField(TEXT("success"), false);
494
+ return MakeShared<FJsonValueObject>(Result);
495
+ }
496
+
497
+ FString NodeName;
498
+ if (!Params->TryGetStringField(TEXT("nodeName"), NodeName))
499
+ {
500
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeName' parameter"));
501
+ Result->SetBoolField(TEXT("success"), false);
502
+ return MakeShared<FJsonValueObject>(Result);
503
+ }
504
+
505
+ // Accept either a 'settings' object (key-value pairs) or individual 'propertyName'/'propertyValue'
506
+ const TSharedPtr<FJsonObject>* SettingsObj = nullptr;
507
+ FString PropertyName;
508
+ FString PropertyValue;
509
+ bool bUseSettingsObject = Params->TryGetObjectField(TEXT("settings"), SettingsObj) && SettingsObj && (*SettingsObj).IsValid();
510
+ bool bUseSingleProperty = !bUseSettingsObject && Params->TryGetStringField(TEXT("propertyName"), PropertyName);
511
+
512
+ if (!bUseSettingsObject && !bUseSingleProperty)
513
+ {
514
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'settings' object or 'propertyName'/'propertyValue' parameters"));
515
+ Result->SetBoolField(TEXT("success"), false);
516
+ return MakeShared<FJsonValueObject>(Result);
517
+ }
518
+
519
+ if (bUseSingleProperty && !Params->TryGetStringField(TEXT("propertyValue"), PropertyValue))
520
+ {
521
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'propertyValue' parameter"));
522
+ Result->SetBoolField(TEXT("success"), false);
523
+ return MakeShared<FJsonValueObject>(Result);
524
+ }
525
+
526
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *AssetPath);
527
+ if (!Graph)
528
+ {
529
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCGGraph not found: %s"), *AssetPath));
530
+ Result->SetBoolField(TEXT("success"), false);
531
+ return MakeShared<FJsonValueObject>(Result);
532
+ }
533
+
534
+ // Find the node by name
535
+ UPCGNode* FoundNode = nullptr;
536
+ const auto& Nodes = Graph->GetNodes();
537
+ for (UPCGNode* Node : Nodes)
538
+ {
539
+ if (Node && Node->GetName() == NodeName)
540
+ {
541
+ FoundNode = Node;
542
+ break;
543
+ }
544
+ }
545
+
546
+ if (!FoundNode)
547
+ {
548
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Node not found: %s"), *NodeName));
549
+ Result->SetBoolField(TEXT("success"), false);
550
+ return MakeShared<FJsonValueObject>(Result);
551
+ }
552
+
553
+ // Get the settings object from the node
554
+ UPCGSettings* Settings = const_cast<UPCGSettings*>(FoundNode->GetSettings());
555
+ if (!Settings)
556
+ {
557
+ Result->SetStringField(TEXT("error"), TEXT("Node has no settings object"));
558
+ Result->SetBoolField(TEXT("success"), false);
559
+ return MakeShared<FJsonValueObject>(Result);
560
+ }
561
+
562
+ // Build a list of property name/value pairs to set
563
+ TArray<TPair<FString, FString>> PropertiesToSet;
564
+ if (bUseSettingsObject)
565
+ {
566
+ for (const auto& Pair : (*SettingsObj)->Values)
567
+ {
568
+ FString ValStr;
569
+ if (Pair.Value->TryGetString(ValStr))
570
+ {
571
+ PropertiesToSet.Add(TPair<FString, FString>(Pair.Key, ValStr));
572
+ }
573
+ else if (Pair.Value->Type == EJson::Number)
574
+ {
575
+ ValStr = FString::SanitizeFloat(Pair.Value->AsNumber());
576
+ PropertiesToSet.Add(TPair<FString, FString>(Pair.Key, ValStr));
577
+ }
578
+ else if (Pair.Value->Type == EJson::Boolean)
579
+ {
580
+ ValStr = Pair.Value->AsBool() ? TEXT("true") : TEXT("false");
581
+ PropertiesToSet.Add(TPair<FString, FString>(Pair.Key, ValStr));
582
+ }
583
+ else
584
+ {
585
+ // For complex types, serialize to string
586
+ ValStr = Pair.Value->AsString();
587
+ PropertiesToSet.Add(TPair<FString, FString>(Pair.Key, ValStr));
588
+ }
589
+ }
590
+ }
591
+ else
592
+ {
593
+ PropertiesToSet.Add(TPair<FString, FString>(PropertyName, PropertyValue));
594
+ }
595
+
596
+ TSharedPtr<FJsonObject> SetResults = MakeShared<FJsonObject>();
597
+ TArray<FString> Errors;
598
+
599
+ for (const auto& Prop : PropertiesToSet)
600
+ {
601
+ FProperty* Property = Settings->GetClass()->FindPropertyByName(FName(*Prop.Key));
602
+ if (!Property)
603
+ {
604
+ Errors.Add(FString::Printf(TEXT("Property '%s' not found on settings"), *Prop.Key));
605
+ continue;
606
+ }
607
+
608
+ void* PropertyAddr = Property->ContainerPtrToValuePtr<void>(Settings);
609
+ const TCHAR* ImportResult = Property->ImportText_Direct(*Prop.Value, PropertyAddr, Settings, PPF_None);
610
+ if (ImportResult == nullptr)
611
+ {
612
+ Errors.Add(FString::Printf(TEXT("Failed to set property '%s' to value '%s'"), *Prop.Key, *Prop.Value));
613
+ }
614
+ else
615
+ {
616
+ SetResults->SetStringField(Prop.Key, Prop.Value);
617
+ }
618
+ }
619
+
620
+ // Save the graph asset
621
+ UEditorAssetLibrary::SaveAsset(AssetPath);
622
+
623
+ Result->SetStringField(TEXT("assetPath"), AssetPath);
624
+ Result->SetStringField(TEXT("nodeName"), NodeName);
625
+ Result->SetObjectField(TEXT("setProperties"), SetResults);
626
+ if (Errors.Num() > 0)
627
+ {
628
+ TArray<TSharedPtr<FJsonValue>> ErrorArray;
629
+ for (const FString& Err : Errors)
630
+ {
631
+ ErrorArray.Add(MakeShared<FJsonValueString>(Err));
632
+ }
633
+ Result->SetArrayField(TEXT("errors"), ErrorArray);
634
+ }
635
+ Result->SetBoolField(TEXT("success"), Errors.Num() == 0);
636
+ return MakeShared<FJsonValueObject>(Result);
637
+ }
638
+
639
+ TSharedPtr<FJsonValue> FPCGHandlers::ExecutePCGGraph(const TSharedPtr<FJsonObject>& Params)
640
+ {
641
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
642
+
643
+ FString ActorLabel;
644
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
645
+ {
646
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
647
+ Result->SetBoolField(TEXT("success"), false);
648
+ return MakeShared<FJsonValueObject>(Result);
649
+ }
650
+
651
+ UWorld* World = GEditor->GetEditorWorldContext().World();
652
+ if (!World)
653
+ {
654
+ Result->SetStringField(TEXT("error"), TEXT("No editor world available"));
655
+ Result->SetBoolField(TEXT("success"), false);
656
+ return MakeShared<FJsonValueObject>(Result);
657
+ }
658
+
659
+ // Find actor by label
660
+ AActor* FoundActor = nullptr;
661
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
662
+ {
663
+ AActor* Actor = *ActorIt;
664
+ if (Actor && Actor->GetActorLabel() == ActorLabel)
665
+ {
666
+ FoundActor = Actor;
667
+ break;
668
+ }
669
+ }
670
+
671
+ if (!FoundActor)
672
+ {
673
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found with label: %s"), *ActorLabel));
674
+ Result->SetBoolField(TEXT("success"), false);
675
+ return MakeShared<FJsonValueObject>(Result);
676
+ }
677
+
678
+ // Get PCG component from the actor
679
+ UPCGComponent* PCGComp = FoundActor->FindComponentByClass<UPCGComponent>();
680
+ if (!PCGComp)
681
+ {
682
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("No PCGComponent found on actor: %s"), *ActorLabel));
683
+ Result->SetBoolField(TEXT("success"), false);
684
+ return MakeShared<FJsonValueObject>(Result);
685
+ }
686
+
687
+ // Set seed if provided
688
+ double Seed = 0;
689
+ if (Params->TryGetNumberField(TEXT("seed"), Seed))
690
+ {
691
+ PCGComp->Seed = (int32)Seed;
692
+ }
693
+
694
+ // Trigger generation
695
+ PCGComp->Generate();
696
+
697
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
698
+ Result->SetStringField(TEXT("componentName"), PCGComp->GetName());
699
+ if (PCGComp->GetGraph())
700
+ {
701
+ Result->SetStringField(TEXT("graphName"), PCGComp->GetGraph()->GetName());
702
+ }
703
+ Result->SetNumberField(TEXT("seed"), PCGComp->Seed);
704
+ Result->SetBoolField(TEXT("success"), true);
705
+ return MakeShared<FJsonValueObject>(Result);
706
+ }
707
+
708
+ TSharedPtr<FJsonValue> FPCGHandlers::SpawnPCGVolume(const TSharedPtr<FJsonObject>& Params)
709
+ {
710
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
711
+
712
+ UWorld* World = GEditor->GetEditorWorldContext().World();
713
+ if (!World)
714
+ {
715
+ Result->SetStringField(TEXT("error"), TEXT("No editor world available"));
716
+ Result->SetBoolField(TEXT("success"), false);
717
+ return MakeShared<FJsonValueObject>(Result);
718
+ }
719
+
720
+ // Parse location
721
+ FVector Location = FVector::ZeroVector;
722
+ double X = 0, Y = 0, Z = 0;
723
+ Params->TryGetNumberField(TEXT("x"), X);
724
+ Params->TryGetNumberField(TEXT("y"), Y);
725
+ Params->TryGetNumberField(TEXT("z"), Z);
726
+ Location = FVector(X, Y, Z);
727
+
728
+ // Parse bounds extent
729
+ FVector Extent = FVector(500.0, 500.0, 500.0);
730
+ double ExtentX = 500, ExtentY = 500, ExtentZ = 500;
731
+ if (Params->TryGetNumberField(TEXT("extentX"), ExtentX) ||
732
+ Params->TryGetNumberField(TEXT("extentY"), ExtentY) ||
733
+ Params->TryGetNumberField(TEXT("extentZ"), ExtentZ))
734
+ {
735
+ Extent = FVector(ExtentX, ExtentY, ExtentZ);
736
+ }
737
+
738
+ // Spawn PCG Volume actor
739
+ FTransform SpawnTransform(FRotator::ZeroRotator, Location);
740
+ APCGVolume* PCGVolumeActor = World->SpawnActor<APCGVolume>(APCGVolume::StaticClass(), SpawnTransform);
741
+ if (!PCGVolumeActor)
742
+ {
743
+ Result->SetStringField(TEXT("error"), TEXT("Failed to spawn PCGVolume actor"));
744
+ Result->SetBoolField(TEXT("success"), false);
745
+ return MakeShared<FJsonValueObject>(Result);
746
+ }
747
+
748
+ // Set the extent/scale of the volume
749
+ PCGVolumeActor->SetActorScale3D(Extent / 100.0);
750
+
751
+ // Set label if provided
752
+ FString Label;
753
+ if (Params->TryGetStringField(TEXT("label"), Label))
754
+ {
755
+ PCGVolumeActor->SetActorLabel(Label);
756
+ }
757
+
758
+ // Set graph reference if provided
759
+ FString GraphPath;
760
+ if (Params->TryGetStringField(TEXT("graphPath"), GraphPath))
761
+ {
762
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *GraphPath);
763
+ if (Graph)
764
+ {
765
+ UPCGComponent* PCGComp = PCGVolumeActor->FindComponentByClass<UPCGComponent>();
766
+ if (PCGComp)
767
+ {
768
+ PCGComp->SetGraph(Graph);
769
+ Result->SetStringField(TEXT("graphPath"), GraphPath);
770
+ Result->SetStringField(TEXT("graphName"), Graph->GetName());
771
+ }
772
+ }
773
+ else
774
+ {
775
+ Result->SetStringField(TEXT("warning"), FString::Printf(TEXT("PCGGraph not found: %s - volume spawned without graph"), *GraphPath));
776
+ }
777
+ }
778
+
779
+ Result->SetStringField(TEXT("actorName"), PCGVolumeActor->GetActorLabel());
780
+ Result->SetStringField(TEXT("actorClass"), PCGVolumeActor->GetClass()->GetName());
781
+
782
+ TSharedPtr<FJsonObject> LocationObj = MakeShared<FJsonObject>();
783
+ LocationObj->SetNumberField(TEXT("x"), Location.X);
784
+ LocationObj->SetNumberField(TEXT("y"), Location.Y);
785
+ LocationObj->SetNumberField(TEXT("z"), Location.Z);
786
+ Result->SetObjectField(TEXT("location"), LocationObj);
787
+
788
+ TSharedPtr<FJsonObject> ExtentObj = MakeShared<FJsonObject>();
789
+ ExtentObj->SetNumberField(TEXT("x"), Extent.X);
790
+ ExtentObj->SetNumberField(TEXT("y"), Extent.Y);
791
+ ExtentObj->SetNumberField(TEXT("z"), Extent.Z);
792
+ Result->SetObjectField(TEXT("extent"), ExtentObj);
793
+
794
+ Result->SetBoolField(TEXT("success"), true);
795
+ return MakeShared<FJsonValueObject>(Result);
796
+ }
797
+
798
+ TSharedPtr<FJsonValue> FPCGHandlers::ReadPCGNodeSettings(const TSharedPtr<FJsonObject>& Params)
799
+ {
800
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
801
+
802
+ FString AssetPath;
803
+ if (!Params->TryGetStringField(TEXT("assetPath"), AssetPath) && !Params->TryGetStringField(TEXT("path"), AssetPath))
804
+ {
805
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'path' or 'assetPath' parameter"));
806
+ Result->SetBoolField(TEXT("success"), false);
807
+ return MakeShared<FJsonValueObject>(Result);
808
+ }
809
+
810
+ FString NodeName;
811
+ if (!Params->TryGetStringField(TEXT("nodeName"), NodeName))
812
+ {
813
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'nodeName' parameter"));
814
+ Result->SetBoolField(TEXT("success"), false);
815
+ return MakeShared<FJsonValueObject>(Result);
816
+ }
817
+
818
+ UPCGGraph* Graph = LoadObject<UPCGGraph>(nullptr, *AssetPath);
819
+ if (!Graph)
820
+ {
821
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("PCGGraph not found: %s"), *AssetPath));
822
+ Result->SetBoolField(TEXT("success"), false);
823
+ return MakeShared<FJsonValueObject>(Result);
824
+ }
825
+
826
+ // Find the node by name
827
+ UPCGNode* FoundNode = nullptr;
828
+ const auto& Nodes = Graph->GetNodes();
829
+ for (UPCGNode* Node : Nodes)
830
+ {
831
+ if (Node && Node->GetName() == NodeName)
832
+ {
833
+ FoundNode = Node;
834
+ break;
835
+ }
836
+ }
837
+
838
+ // Also check input/output nodes
839
+ if (!FoundNode && Graph->GetInputNode() && Graph->GetInputNode()->GetName() == NodeName)
840
+ {
841
+ FoundNode = Graph->GetInputNode();
842
+ }
843
+ if (!FoundNode && Graph->GetOutputNode() && Graph->GetOutputNode()->GetName() == NodeName)
844
+ {
845
+ FoundNode = Graph->GetOutputNode();
846
+ }
847
+
848
+ if (!FoundNode)
849
+ {
850
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Node not found: %s"), *NodeName));
851
+ Result->SetBoolField(TEXT("success"), false);
852
+ return MakeShared<FJsonValueObject>(Result);
853
+ }
854
+
855
+ Result->SetStringField(TEXT("nodeName"), FoundNode->GetName());
856
+ Result->SetStringField(TEXT("nodeTitle"), FoundNode->GetNodeTitle(EPCGNodeTitleType::ListView).ToString());
857
+
858
+ // Read the settings object properties
859
+ const UPCGSettings* Settings = FoundNode->GetSettings();
860
+ if (!Settings)
861
+ {
862
+ Result->SetStringField(TEXT("note"), TEXT("Node has no settings object"));
863
+ Result->SetBoolField(TEXT("success"), true);
864
+ return MakeShared<FJsonValueObject>(Result);
865
+ }
866
+
867
+ Result->SetStringField(TEXT("settingsClass"), Settings->GetClass()->GetName());
868
+
869
+ // Enumerate all editable properties on the settings
870
+ TSharedPtr<FJsonObject> PropertiesObj = MakeShared<FJsonObject>();
871
+ for (TFieldIterator<FProperty> PropIt(Settings->GetClass()); PropIt; ++PropIt)
872
+ {
873
+ FProperty* Property = *PropIt;
874
+ if (!Property) continue;
875
+
876
+ // Only include properties that are editable and visible
877
+ if (!Property->HasAnyPropertyFlags(CPF_Edit)) continue;
878
+
879
+ FString PropertyName = Property->GetName();
880
+ FString PropertyValue;
881
+ const void* PropertyAddr = Property->ContainerPtrToValuePtr<void>(Settings);
882
+ Property->ExportTextItem_Direct(PropertyValue, PropertyAddr, nullptr, nullptr, PPF_None);
883
+
884
+ TSharedPtr<FJsonObject> PropObj = MakeShared<FJsonObject>();
885
+ PropObj->SetStringField(TEXT("value"), PropertyValue);
886
+ PropObj->SetStringField(TEXT("type"), Property->GetCPPType());
887
+ PropertiesObj->SetObjectField(PropertyName, PropObj);
888
+ }
889
+
890
+ Result->SetObjectField(TEXT("settings"), PropertiesObj);
891
+
892
+ // List input/output pins
893
+ TArray<TSharedPtr<FJsonValue>> InputPinsArray;
894
+ const TArray<TObjectPtr<UPCGPin>>& InPins = FoundNode->GetInputPins();
895
+ for (const TObjectPtr<UPCGPin>& Pin : InPins)
896
+ {
897
+ if (!Pin) continue;
898
+ TSharedPtr<FJsonObject> PinObj = MakeShared<FJsonObject>();
899
+ PinObj->SetStringField(TEXT("label"), Pin->Properties.Label.ToString());
900
+ InputPinsArray.Add(MakeShared<FJsonValueObject>(PinObj));
901
+ }
902
+ Result->SetArrayField(TEXT("inputPins"), InputPinsArray);
903
+
904
+ TArray<TSharedPtr<FJsonValue>> OutputPinsArray;
905
+ const TArray<TObjectPtr<UPCGPin>>& OutPins = FoundNode->GetOutputPins();
906
+ for (const TObjectPtr<UPCGPin>& Pin : OutPins)
907
+ {
908
+ if (!Pin) continue;
909
+ TSharedPtr<FJsonObject> PinObj = MakeShared<FJsonObject>();
910
+ PinObj->SetStringField(TEXT("label"), Pin->Properties.Label.ToString());
911
+ OutputPinsArray.Add(MakeShared<FJsonValueObject>(PinObj));
912
+ }
913
+ Result->SetArrayField(TEXT("outputPins"), OutputPinsArray);
914
+
915
+ Result->SetBoolField(TEXT("success"), true);
916
+ return MakeShared<FJsonValueObject>(Result);
917
+ }
918
+
919
+ TSharedPtr<FJsonValue> FPCGHandlers::GetPCGComponentDetails(const TSharedPtr<FJsonObject>& Params)
920
+ {
921
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
922
+
923
+ FString ActorLabel;
924
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
925
+ {
926
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
927
+ Result->SetBoolField(TEXT("success"), false);
928
+ return MakeShared<FJsonValueObject>(Result);
929
+ }
930
+
931
+ UWorld* World = GEditor->GetEditorWorldContext().World();
932
+ if (!World)
933
+ {
934
+ Result->SetStringField(TEXT("error"), TEXT("No editor world available"));
935
+ Result->SetBoolField(TEXT("success"), false);
936
+ return MakeShared<FJsonValueObject>(Result);
937
+ }
938
+
939
+ // Find actor by label
940
+ AActor* FoundActor = nullptr;
941
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
942
+ {
943
+ AActor* Actor = *ActorIt;
944
+ if (Actor && Actor->GetActorLabel() == ActorLabel)
945
+ {
946
+ FoundActor = Actor;
947
+ break;
948
+ }
949
+ }
950
+
951
+ if (!FoundActor)
952
+ {
953
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found with label: %s"), *ActorLabel));
954
+ Result->SetBoolField(TEXT("success"), false);
955
+ return MakeShared<FJsonValueObject>(Result);
956
+ }
957
+
958
+ // Get all PCG components on the actor
959
+ TArray<UPCGComponent*> PCGComps;
960
+ FoundActor->GetComponents<UPCGComponent>(PCGComps);
961
+
962
+ if (PCGComps.Num() == 0)
963
+ {
964
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("No PCGComponent found on actor: %s"), *ActorLabel));
965
+ Result->SetBoolField(TEXT("success"), false);
966
+ return MakeShared<FJsonValueObject>(Result);
967
+ }
968
+
969
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
970
+ Result->SetStringField(TEXT("actorClass"), FoundActor->GetClass()->GetName());
971
+
972
+ TArray<TSharedPtr<FJsonValue>> ComponentsArray;
973
+ for (UPCGComponent* PCGComp : PCGComps)
974
+ {
975
+ if (!PCGComp) continue;
976
+
977
+ TSharedPtr<FJsonObject> CompObj = MakeShared<FJsonObject>();
978
+ CompObj->SetStringField(TEXT("componentName"), PCGComp->GetName());
979
+ CompObj->SetNumberField(TEXT("seed"), PCGComp->Seed);
980
+ CompObj->SetBoolField(TEXT("activated"), PCGComp->bActivated);
981
+
982
+ // Generation trigger
983
+ FString GenTriggerStr;
984
+ switch (PCGComp->GenerationTrigger)
985
+ {
986
+ case EPCGComponentGenerationTrigger::GenerateOnLoad:
987
+ GenTriggerStr = TEXT("GenerateOnLoad");
988
+ break;
989
+ case EPCGComponentGenerationTrigger::GenerateOnDemand:
990
+ GenTriggerStr = TEXT("GenerateOnDemand");
991
+ break;
992
+ default:
993
+ GenTriggerStr = TEXT("Unknown");
994
+ break;
995
+ }
996
+ CompObj->SetStringField(TEXT("generationTrigger"), GenTriggerStr);
997
+
998
+ // Graph details
999
+ UPCGGraph* Graph = const_cast<UPCGGraph*>(PCGComp->GetGraph());
1000
+ if (Graph)
1001
+ {
1002
+ TSharedPtr<FJsonObject> GraphObj = MakeShared<FJsonObject>();
1003
+ GraphObj->SetStringField(TEXT("name"), Graph->GetName());
1004
+ GraphObj->SetStringField(TEXT("path"), Graph->GetPathName());
1005
+ GraphObj->SetNumberField(TEXT("nodeCount"), Graph->GetNodes().Num());
1006
+ CompObj->SetObjectField(TEXT("graph"), GraphObj);
1007
+ }
1008
+
1009
+ // Location info
1010
+ FVector CompLocation = PCGComp->GetOwner() ? PCGComp->GetOwner()->GetActorLocation() : FVector::ZeroVector;
1011
+ TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
1012
+ LocObj->SetNumberField(TEXT("x"), CompLocation.X);
1013
+ LocObj->SetNumberField(TEXT("y"), CompLocation.Y);
1014
+ LocObj->SetNumberField(TEXT("z"), CompLocation.Z);
1015
+ CompObj->SetObjectField(TEXT("location"), LocObj);
1016
+
1017
+ ComponentsArray.Add(MakeShared<FJsonValueObject>(CompObj));
1018
+ }
1019
+
1020
+ Result->SetArrayField(TEXT("components"), ComponentsArray);
1021
+ Result->SetNumberField(TEXT("componentCount"), ComponentsArray.Num());
1022
+ Result->SetBoolField(TEXT("success"), true);
1023
+ return MakeShared<FJsonValueObject>(Result);
1024
+ }