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,1270 @@
1
+ #include "LevelHandlers.h"
2
+ #include "HandlerRegistry.h"
3
+ #include "EditorScriptingUtilities/Public/EditorLevelLibrary.h"
4
+ #include "Editor.h"
5
+ #include "Editor/EditorEngine.h"
6
+ #include "Engine/World.h"
7
+ #include "Engine/Engine.h"
8
+ #include "GameFramework/Actor.h"
9
+ #include "UObject/UObjectGlobals.h"
10
+ #include "UObject/UObjectIterator.h"
11
+ #include "EngineUtils.h"
12
+ #include "Editor.h"
13
+ #include "Editor/EditorEngine.h"
14
+ #include "Dom/JsonObject.h"
15
+ #include "Dom/JsonValue.h"
16
+ #include "JsonSerializer.h"
17
+ #include "Engine/PointLight.h"
18
+ #include "Engine/SpotLight.h"
19
+ #include "Engine/DirectionalLight.h"
20
+ #include "Engine/RectLight.h"
21
+ #include "Components/PointLightComponent.h"
22
+ #include "Components/SpotLightComponent.h"
23
+ #include "Components/DirectionalLightComponent.h"
24
+ #include "Components/RectLightComponent.h"
25
+ #include "Components/LightComponent.h"
26
+ #include "Engine/BrushBuilder.h"
27
+ #include "GameFramework/Volume.h"
28
+ #include "Engine/BlockingVolume.h"
29
+ #include "Engine/TriggerVolume.h"
30
+ #include "Selection.h"
31
+ #include "Engine/LevelStreaming.h"
32
+ #include "LevelEditorSubsystem.h"
33
+ #include "EditorLevelUtils.h"
34
+ #include "FileHelpers.h"
35
+
36
+ void FLevelHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
37
+ {
38
+ Registry.RegisterHandler(TEXT("get_world_outliner"), &GetOutliner);
39
+ Registry.RegisterHandler(TEXT("place_actor"), &PlaceActor);
40
+ Registry.RegisterHandler(TEXT("delete_actor"), &DeleteActor);
41
+ Registry.RegisterHandler(TEXT("get_actor_details"), &GetActorDetails);
42
+ Registry.RegisterHandler(TEXT("get_current_level"), &GetCurrentLevel);
43
+ Registry.RegisterHandler(TEXT("list_levels"), &ListLevels);
44
+ Registry.RegisterHandler(TEXT("get_selected_actors"), &GetSelectedActors);
45
+ Registry.RegisterHandler(TEXT("list_volumes"), &ListVolumes);
46
+ Registry.RegisterHandler(TEXT("move_actor"), &MoveActor);
47
+ Registry.RegisterHandler(TEXT("select_actors"), &SelectActors);
48
+ Registry.RegisterHandler(TEXT("spawn_light"), &SpawnLight);
49
+ Registry.RegisterHandler(TEXT("set_light_properties"), &SetLightProperties);
50
+ Registry.RegisterHandler(TEXT("spawn_volume"), &SpawnVolume);
51
+ Registry.RegisterHandler(TEXT("add_component_to_actor"), &AddComponentToActor);
52
+ Registry.RegisterHandler(TEXT("load_level"), &LoadLevel);
53
+ Registry.RegisterHandler(TEXT("save_level"), &SaveLevel);
54
+ Registry.RegisterHandler(TEXT("list_sublevels"), &ListSublevels);
55
+ Registry.RegisterHandler(TEXT("set_component_property"), &SetComponentProperty);
56
+ Registry.RegisterHandler(TEXT("set_volume_properties"), &SetVolumeProperties);
57
+ }
58
+
59
+ TSharedPtr<FJsonValue> FLevelHandlers::GetOutliner(const TSharedPtr<FJsonObject>& Params)
60
+ {
61
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
62
+
63
+ UWorld* World = GEditor->GetEditorWorldContext().World();
64
+ if (!World)
65
+ {
66
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
67
+ Result->SetBoolField(TEXT("success"), false);
68
+ return MakeShared<FJsonValueObject>(Result);
69
+ }
70
+
71
+ FString ClassFilter;
72
+ Params->TryGetStringField(TEXT("classFilter"), ClassFilter);
73
+ FString NameFilter;
74
+ Params->TryGetStringField(TEXT("nameFilter"), NameFilter);
75
+ int32 Limit = 500;
76
+ Params->TryGetNumberField(TEXT("limit"), Limit);
77
+
78
+ TArray<TSharedPtr<FJsonValue>> ActorsArray;
79
+ int32 TotalCount = 0;
80
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
81
+ {
82
+ AActor* Actor = *ActorIt;
83
+ if (!Actor) continue;
84
+ TotalCount++;
85
+
86
+ FString ActorClass = Actor->GetClass()->GetName();
87
+ FString ActorName = Actor->GetName();
88
+ FString ActorLabel = Actor->GetActorLabel();
89
+
90
+ if (!ClassFilter.IsEmpty() && !ActorClass.Contains(ClassFilter))
91
+ {
92
+ continue;
93
+ }
94
+ if (!NameFilter.IsEmpty() && !ActorName.Contains(NameFilter) && !ActorLabel.Contains(NameFilter))
95
+ {
96
+ continue;
97
+ }
98
+ if (ActorsArray.Num() >= Limit) break;
99
+
100
+ TSharedPtr<FJsonObject> ActorObj = MakeShared<FJsonObject>();
101
+ ActorObj->SetStringField(TEXT("name"), ActorName);
102
+ ActorObj->SetStringField(TEXT("label"), ActorLabel);
103
+ ActorObj->SetStringField(TEXT("class"), ActorClass);
104
+ ActorObj->SetStringField(TEXT("path"), Actor->GetPathName());
105
+
106
+ FVector Location = Actor->GetActorLocation();
107
+ TSharedPtr<FJsonObject> LocationObj = MakeShared<FJsonObject>();
108
+ LocationObj->SetNumberField(TEXT("x"), Location.X);
109
+ LocationObj->SetNumberField(TEXT("y"), Location.Y);
110
+ LocationObj->SetNumberField(TEXT("z"), Location.Z);
111
+ ActorObj->SetObjectField(TEXT("location"), LocationObj);
112
+
113
+ FRotator Rotation = Actor->GetActorRotation();
114
+ TSharedPtr<FJsonObject> RotationObj = MakeShared<FJsonObject>();
115
+ RotationObj->SetNumberField(TEXT("pitch"), Rotation.Pitch);
116
+ RotationObj->SetNumberField(TEXT("yaw"), Rotation.Yaw);
117
+ RotationObj->SetNumberField(TEXT("roll"), Rotation.Roll);
118
+ ActorObj->SetObjectField(TEXT("rotation"), RotationObj);
119
+
120
+ ActorsArray.Add(MakeShared<FJsonValueObject>(ActorObj));
121
+ }
122
+
123
+ Result->SetStringField(TEXT("worldName"), World->GetName());
124
+ Result->SetNumberField(TEXT("totalActors"), TotalCount);
125
+ Result->SetNumberField(TEXT("returnedActors"), ActorsArray.Num());
126
+ Result->SetArrayField(TEXT("actors"), ActorsArray);
127
+ Result->SetBoolField(TEXT("success"), true);
128
+
129
+ return MakeShared<FJsonValueObject>(Result);
130
+ }
131
+
132
+ TSharedPtr<FJsonValue> FLevelHandlers::PlaceActor(const TSharedPtr<FJsonObject>& Params)
133
+ {
134
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
135
+
136
+ FString ActorClass;
137
+ if (!Params->TryGetStringField(TEXT("actorClass"), ActorClass))
138
+ {
139
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorClass' parameter"));
140
+ Result->SetBoolField(TEXT("success"), false);
141
+ return MakeShared<FJsonValueObject>(Result);
142
+ }
143
+
144
+ UWorld* World = GEditor->GetEditorWorldContext().World();
145
+ if (!World)
146
+ {
147
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
148
+ Result->SetBoolField(TEXT("success"), false);
149
+ return MakeShared<FJsonValueObject>(Result);
150
+ }
151
+
152
+ // Find actor class
153
+ UClass* Class = FindObject<UClass>(nullptr, *ActorClass);
154
+ if (!Class)
155
+ {
156
+ Class = FindObject<UClass>(nullptr, *(TEXT("A") + ActorClass));
157
+ }
158
+
159
+ if (!Class)
160
+ {
161
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor class not found: %s"), *ActorClass));
162
+ Result->SetBoolField(TEXT("success"), false);
163
+ return MakeShared<FJsonValueObject>(Result);
164
+ }
165
+
166
+ // Get location
167
+ FVector Location = FVector::ZeroVector;
168
+ const TSharedPtr<FJsonObject>* LocationObj = nullptr;
169
+ if (Params->TryGetObjectField(TEXT("location"), LocationObj))
170
+ {
171
+ (*LocationObj)->TryGetNumberField(TEXT("x"), Location.X);
172
+ (*LocationObj)->TryGetNumberField(TEXT("y"), Location.Y);
173
+ (*LocationObj)->TryGetNumberField(TEXT("z"), Location.Z);
174
+ }
175
+
176
+ // Spawn actor
177
+ FTransform SpawnTransform(FRotator::ZeroRotator, Location);
178
+ AActor* NewActor = World->SpawnActor<AActor>(Class, SpawnTransform);
179
+ if (!NewActor)
180
+ {
181
+ Result->SetStringField(TEXT("error"), TEXT("Failed to spawn actor"));
182
+ Result->SetBoolField(TEXT("success"), false);
183
+ return MakeShared<FJsonValueObject>(Result);
184
+ }
185
+
186
+ Result->SetStringField(TEXT("actorLabel"), NewActor->GetActorLabel());
187
+ Result->SetStringField(TEXT("actorClass"), ActorClass);
188
+ Result->SetBoolField(TEXT("success"), true);
189
+
190
+ return MakeShared<FJsonValueObject>(Result);
191
+ }
192
+
193
+ TSharedPtr<FJsonValue> FLevelHandlers::DeleteActor(const TSharedPtr<FJsonObject>& Params)
194
+ {
195
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
196
+
197
+ FString ActorLabel;
198
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
199
+ {
200
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
201
+ Result->SetBoolField(TEXT("success"), false);
202
+ return MakeShared<FJsonValueObject>(Result);
203
+ }
204
+
205
+ UWorld* World = GEditor->GetEditorWorldContext().World();
206
+ if (!World)
207
+ {
208
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
209
+ Result->SetBoolField(TEXT("success"), false);
210
+ return MakeShared<FJsonValueObject>(Result);
211
+ }
212
+
213
+ // Find actor by label
214
+ AActor* ActorToDelete = nullptr;
215
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
216
+ {
217
+ if ((*ActorIt)->GetActorLabel() == ActorLabel)
218
+ {
219
+ ActorToDelete = *ActorIt;
220
+ break;
221
+ }
222
+ }
223
+
224
+ if (!ActorToDelete)
225
+ {
226
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
227
+ Result->SetBoolField(TEXT("success"), false);
228
+ return MakeShared<FJsonValueObject>(Result);
229
+ }
230
+
231
+ World->DestroyActor(ActorToDelete);
232
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
233
+ Result->SetBoolField(TEXT("success"), true);
234
+
235
+ return MakeShared<FJsonValueObject>(Result);
236
+ }
237
+
238
+ TSharedPtr<FJsonValue> FLevelHandlers::GetActorDetails(const TSharedPtr<FJsonObject>& Params)
239
+ {
240
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
241
+
242
+ FString ActorLabel;
243
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
244
+ {
245
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
246
+ Result->SetBoolField(TEXT("success"), false);
247
+ return MakeShared<FJsonValueObject>(Result);
248
+ }
249
+
250
+ UWorld* World = GEditor->GetEditorWorldContext().World();
251
+ if (!World)
252
+ {
253
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
254
+ Result->SetBoolField(TEXT("success"), false);
255
+ return MakeShared<FJsonValueObject>(Result);
256
+ }
257
+
258
+ // Find actor by label
259
+ AActor* Actor = nullptr;
260
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
261
+ {
262
+ if ((*ActorIt)->GetActorLabel() == ActorLabel)
263
+ {
264
+ Actor = *ActorIt;
265
+ break;
266
+ }
267
+ }
268
+
269
+ if (!Actor)
270
+ {
271
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
272
+ Result->SetBoolField(TEXT("success"), false);
273
+ return MakeShared<FJsonValueObject>(Result);
274
+ }
275
+
276
+ Result->SetStringField(TEXT("label"), Actor->GetActorLabel());
277
+ Result->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
278
+ Result->SetStringField(TEXT("path"), Actor->GetPathName());
279
+
280
+ FVector Location = Actor->GetActorLocation();
281
+ TSharedPtr<FJsonObject> LocationObj = MakeShared<FJsonObject>();
282
+ LocationObj->SetNumberField(TEXT("x"), Location.X);
283
+ LocationObj->SetNumberField(TEXT("y"), Location.Y);
284
+ LocationObj->SetNumberField(TEXT("z"), Location.Z);
285
+ Result->SetObjectField(TEXT("location"), LocationObj);
286
+
287
+ Result->SetBoolField(TEXT("success"), true);
288
+
289
+ return MakeShared<FJsonValueObject>(Result);
290
+ }
291
+
292
+ TSharedPtr<FJsonValue> FLevelHandlers::GetCurrentLevel(const TSharedPtr<FJsonObject>& Params)
293
+ {
294
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
295
+
296
+ UWorld* World = GEditor->GetEditorWorldContext().World();
297
+ if (!World)
298
+ {
299
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
300
+ Result->SetBoolField(TEXT("success"), false);
301
+ return MakeShared<FJsonValueObject>(Result);
302
+ }
303
+
304
+ ULevel* CurrentLevel = World->GetCurrentLevel();
305
+ if (!CurrentLevel)
306
+ {
307
+ Result->SetStringField(TEXT("error"), TEXT("No current level"));
308
+ Result->SetBoolField(TEXT("success"), false);
309
+ return MakeShared<FJsonValueObject>(Result);
310
+ }
311
+
312
+ Result->SetStringField(TEXT("levelName"), World->GetName());
313
+ Result->SetStringField(TEXT("levelPath"), World->GetPathName());
314
+ Result->SetBoolField(TEXT("success"), true);
315
+
316
+ return MakeShared<FJsonValueObject>(Result);
317
+ }
318
+
319
+ TSharedPtr<FJsonValue> FLevelHandlers::ListLevels(const TSharedPtr<FJsonObject>& Params)
320
+ {
321
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
322
+
323
+ UWorld* World = GEditor->GetEditorWorldContext().World();
324
+ if (!World)
325
+ {
326
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
327
+ Result->SetBoolField(TEXT("success"), false);
328
+ return MakeShared<FJsonValueObject>(Result);
329
+ }
330
+
331
+ TArray<TSharedPtr<FJsonValue>> LevelsArray;
332
+
333
+ // Add persistent level
334
+ TSharedPtr<FJsonObject> PersistentObj = MakeShared<FJsonObject>();
335
+ PersistentObj->SetStringField(TEXT("name"), World->GetName());
336
+ PersistentObj->SetStringField(TEXT("type"), TEXT("persistent"));
337
+ PersistentObj->SetBoolField(TEXT("isLoaded"), true);
338
+ LevelsArray.Add(MakeShared<FJsonValueObject>(PersistentObj));
339
+
340
+ // Add streaming levels
341
+ const TArray<ULevelStreaming*>& StreamingLevels = World->GetStreamingLevels();
342
+ for (ULevelStreaming* StreamingLevel : StreamingLevels)
343
+ {
344
+ if (!StreamingLevel) continue;
345
+
346
+ TSharedPtr<FJsonObject> LevelObj = MakeShared<FJsonObject>();
347
+ LevelObj->SetStringField(TEXT("name"), StreamingLevel->GetWorldAssetPackageFName().ToString());
348
+ LevelObj->SetStringField(TEXT("type"), TEXT("streaming"));
349
+ LevelObj->SetBoolField(TEXT("isLoaded"), StreamingLevel->IsLevelLoaded());
350
+ LevelObj->SetBoolField(TEXT("isVisible"), StreamingLevel->IsLevelVisible());
351
+ LevelsArray.Add(MakeShared<FJsonValueObject>(LevelObj));
352
+ }
353
+
354
+ Result->SetArrayField(TEXT("levels"), LevelsArray);
355
+ Result->SetNumberField(TEXT("count"), LevelsArray.Num());
356
+ Result->SetBoolField(TEXT("success"), true);
357
+
358
+ return MakeShared<FJsonValueObject>(Result);
359
+ }
360
+
361
+ TSharedPtr<FJsonValue> FLevelHandlers::GetSelectedActors(const TSharedPtr<FJsonObject>& Params)
362
+ {
363
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
364
+
365
+ USelection* Selection = GEditor->GetSelectedActors();
366
+ if (!Selection)
367
+ {
368
+ Result->SetStringField(TEXT("error"), TEXT("Unable to get selection"));
369
+ Result->SetBoolField(TEXT("success"), false);
370
+ return MakeShared<FJsonValueObject>(Result);
371
+ }
372
+
373
+ TArray<TSharedPtr<FJsonValue>> ActorsArray;
374
+ for (int32 i = 0; i < Selection->Num(); i++)
375
+ {
376
+ AActor* Actor = Cast<AActor>(Selection->GetSelectedObject(i));
377
+ if (!Actor) continue;
378
+
379
+ TSharedPtr<FJsonObject> ActorObj = MakeShared<FJsonObject>();
380
+ ActorObj->SetStringField(TEXT("name"), Actor->GetName());
381
+ ActorObj->SetStringField(TEXT("label"), Actor->GetActorLabel());
382
+ ActorObj->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
383
+ ActorObj->SetStringField(TEXT("path"), Actor->GetPathName());
384
+
385
+ FVector Location = Actor->GetActorLocation();
386
+ TSharedPtr<FJsonObject> LocationObj = MakeShared<FJsonObject>();
387
+ LocationObj->SetNumberField(TEXT("x"), Location.X);
388
+ LocationObj->SetNumberField(TEXT("y"), Location.Y);
389
+ LocationObj->SetNumberField(TEXT("z"), Location.Z);
390
+ ActorObj->SetObjectField(TEXT("location"), LocationObj);
391
+
392
+ ActorsArray.Add(MakeShared<FJsonValueObject>(ActorObj));
393
+ }
394
+
395
+ Result->SetArrayField(TEXT("actors"), ActorsArray);
396
+ Result->SetNumberField(TEXT("count"), ActorsArray.Num());
397
+ Result->SetBoolField(TEXT("success"), true);
398
+
399
+ return MakeShared<FJsonValueObject>(Result);
400
+ }
401
+
402
+ TSharedPtr<FJsonValue> FLevelHandlers::ListVolumes(const TSharedPtr<FJsonObject>& Params)
403
+ {
404
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
405
+
406
+ UWorld* World = GEditor->GetEditorWorldContext().World();
407
+ if (!World)
408
+ {
409
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
410
+ Result->SetBoolField(TEXT("success"), false);
411
+ return MakeShared<FJsonValueObject>(Result);
412
+ }
413
+
414
+ FString VolumeType;
415
+ Params->TryGetStringField(TEXT("volumeType"), VolumeType);
416
+
417
+ TArray<TSharedPtr<FJsonValue>> VolumesArray;
418
+ for (TActorIterator<AVolume> ActorIt(World); ActorIt; ++ActorIt)
419
+ {
420
+ AVolume* Volume = *ActorIt;
421
+ if (!Volume) continue;
422
+
423
+ FString ClassName = Volume->GetClass()->GetName();
424
+ if (!VolumeType.IsEmpty() && !ClassName.Contains(VolumeType))
425
+ {
426
+ continue;
427
+ }
428
+
429
+ TSharedPtr<FJsonObject> VolumeObj = MakeShared<FJsonObject>();
430
+ VolumeObj->SetStringField(TEXT("name"), Volume->GetName());
431
+ VolumeObj->SetStringField(TEXT("label"), Volume->GetActorLabel());
432
+ VolumeObj->SetStringField(TEXT("class"), ClassName);
433
+ VolumeObj->SetStringField(TEXT("path"), Volume->GetPathName());
434
+
435
+ FVector Location = Volume->GetActorLocation();
436
+ TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
437
+ LocObj->SetNumberField(TEXT("x"), Location.X);
438
+ LocObj->SetNumberField(TEXT("y"), Location.Y);
439
+ LocObj->SetNumberField(TEXT("z"), Location.Z);
440
+ VolumeObj->SetObjectField(TEXT("location"), LocObj);
441
+
442
+ VolumesArray.Add(MakeShared<FJsonValueObject>(VolumeObj));
443
+ }
444
+
445
+ Result->SetArrayField(TEXT("volumes"), VolumesArray);
446
+ Result->SetNumberField(TEXT("count"), VolumesArray.Num());
447
+ Result->SetBoolField(TEXT("success"), true);
448
+
449
+ return MakeShared<FJsonValueObject>(Result);
450
+ }
451
+
452
+ TSharedPtr<FJsonValue> FLevelHandlers::MoveActor(const TSharedPtr<FJsonObject>& Params)
453
+ {
454
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
455
+
456
+ FString ActorLabel;
457
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
458
+ {
459
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
460
+ Result->SetBoolField(TEXT("success"), false);
461
+ return MakeShared<FJsonValueObject>(Result);
462
+ }
463
+
464
+ UWorld* World = GEditor->GetEditorWorldContext().World();
465
+ if (!World)
466
+ {
467
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
468
+ Result->SetBoolField(TEXT("success"), false);
469
+ return MakeShared<FJsonValueObject>(Result);
470
+ }
471
+
472
+ // Find actor by label
473
+ AActor* Actor = nullptr;
474
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
475
+ {
476
+ if ((*ActorIt)->GetActorLabel() == ActorLabel)
477
+ {
478
+ Actor = *ActorIt;
479
+ break;
480
+ }
481
+ }
482
+
483
+ if (!Actor)
484
+ {
485
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
486
+ Result->SetBoolField(TEXT("success"), false);
487
+ return MakeShared<FJsonValueObject>(Result);
488
+ }
489
+
490
+ // Get location
491
+ const TSharedPtr<FJsonObject>* LocObj = nullptr;
492
+ if (Params->TryGetObjectField(TEXT("location"), LocObj))
493
+ {
494
+ FVector Location = Actor->GetActorLocation();
495
+ (*LocObj)->TryGetNumberField(TEXT("x"), Location.X);
496
+ (*LocObj)->TryGetNumberField(TEXT("y"), Location.Y);
497
+ (*LocObj)->TryGetNumberField(TEXT("z"), Location.Z);
498
+ Actor->SetActorLocation(Location);
499
+ }
500
+
501
+ // Get rotation
502
+ const TSharedPtr<FJsonObject>* RotObj = nullptr;
503
+ if (Params->TryGetObjectField(TEXT("rotation"), RotObj))
504
+ {
505
+ FRotator Rotation = Actor->GetActorRotation();
506
+ (*RotObj)->TryGetNumberField(TEXT("pitch"), Rotation.Pitch);
507
+ (*RotObj)->TryGetNumberField(TEXT("yaw"), Rotation.Yaw);
508
+ (*RotObj)->TryGetNumberField(TEXT("roll"), Rotation.Roll);
509
+ Actor->SetActorRotation(Rotation);
510
+ }
511
+
512
+ // Return new transform
513
+ FVector NewLocation = Actor->GetActorLocation();
514
+ TSharedPtr<FJsonObject> NewLocationObj = MakeShared<FJsonObject>();
515
+ NewLocationObj->SetNumberField(TEXT("x"), NewLocation.X);
516
+ NewLocationObj->SetNumberField(TEXT("y"), NewLocation.Y);
517
+ NewLocationObj->SetNumberField(TEXT("z"), NewLocation.Z);
518
+ Result->SetObjectField(TEXT("location"), NewLocationObj);
519
+
520
+ FRotator NewRotation = Actor->GetActorRotation();
521
+ TSharedPtr<FJsonObject> NewRotationObj = MakeShared<FJsonObject>();
522
+ NewRotationObj->SetNumberField(TEXT("pitch"), NewRotation.Pitch);
523
+ NewRotationObj->SetNumberField(TEXT("yaw"), NewRotation.Yaw);
524
+ NewRotationObj->SetNumberField(TEXT("roll"), NewRotation.Roll);
525
+ Result->SetObjectField(TEXT("rotation"), NewRotationObj);
526
+
527
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
528
+ Result->SetBoolField(TEXT("success"), true);
529
+
530
+ return MakeShared<FJsonValueObject>(Result);
531
+ }
532
+
533
+ TSharedPtr<FJsonValue> FLevelHandlers::SelectActors(const TSharedPtr<FJsonObject>& Params)
534
+ {
535
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
536
+
537
+ const TArray<TSharedPtr<FJsonValue>>* ActorLabelsArray = nullptr;
538
+ if (!Params->TryGetArrayField(TEXT("actorLabels"), ActorLabelsArray))
539
+ {
540
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabels' parameter"));
541
+ Result->SetBoolField(TEXT("success"), false);
542
+ return MakeShared<FJsonValueObject>(Result);
543
+ }
544
+
545
+ UWorld* World = GEditor->GetEditorWorldContext().World();
546
+ if (!World)
547
+ {
548
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
549
+ Result->SetBoolField(TEXT("success"), false);
550
+ return MakeShared<FJsonValueObject>(Result);
551
+ }
552
+
553
+ // Deselect all
554
+ GEditor->SelectNone(true, true, false);
555
+
556
+ TArray<TSharedPtr<FJsonValue>> SelectedArray;
557
+ TArray<TSharedPtr<FJsonValue>> NotFoundArray;
558
+
559
+ for (const TSharedPtr<FJsonValue>& LabelValue : *ActorLabelsArray)
560
+ {
561
+ FString Label = LabelValue->AsString();
562
+ bool bFound = false;
563
+
564
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
565
+ {
566
+ if ((*ActorIt)->GetActorLabel() == Label)
567
+ {
568
+ GEditor->SelectActor(*ActorIt, true, true, true);
569
+ SelectedArray.Add(MakeShared<FJsonValueString>(Label));
570
+ bFound = true;
571
+ break;
572
+ }
573
+ }
574
+
575
+ if (!bFound)
576
+ {
577
+ NotFoundArray.Add(MakeShared<FJsonValueString>(Label));
578
+ }
579
+ }
580
+
581
+ Result->SetArrayField(TEXT("selected"), SelectedArray);
582
+ Result->SetArrayField(TEXT("notFound"), NotFoundArray);
583
+ Result->SetNumberField(TEXT("selectedCount"), SelectedArray.Num());
584
+ Result->SetBoolField(TEXT("success"), true);
585
+
586
+ return MakeShared<FJsonValueObject>(Result);
587
+ }
588
+
589
+ TSharedPtr<FJsonValue> FLevelHandlers::SpawnLight(const TSharedPtr<FJsonObject>& Params)
590
+ {
591
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
592
+
593
+ FString LightType;
594
+ if (!Params->TryGetStringField(TEXT("lightType"), LightType))
595
+ {
596
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'lightType' parameter"));
597
+ Result->SetBoolField(TEXT("success"), false);
598
+ return MakeShared<FJsonValueObject>(Result);
599
+ }
600
+
601
+ UWorld* World = GEditor->GetEditorWorldContext().World();
602
+ if (!World)
603
+ {
604
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
605
+ Result->SetBoolField(TEXT("success"), false);
606
+ return MakeShared<FJsonValueObject>(Result);
607
+ }
608
+
609
+ // Get location
610
+ FVector Location = FVector::ZeroVector;
611
+ const TSharedPtr<FJsonObject>* LocationObj = nullptr;
612
+ if (Params->TryGetObjectField(TEXT("location"), LocationObj))
613
+ {
614
+ (*LocationObj)->TryGetNumberField(TEXT("x"), Location.X);
615
+ (*LocationObj)->TryGetNumberField(TEXT("y"), Location.Y);
616
+ (*LocationObj)->TryGetNumberField(TEXT("z"), Location.Z);
617
+ }
618
+
619
+ double Intensity = 5000.0;
620
+ Params->TryGetNumberField(TEXT("intensity"), Intensity);
621
+
622
+ // Determine light class
623
+ UClass* LightClass = nullptr;
624
+ if (LightType.Equals(TEXT("point"), ESearchCase::IgnoreCase))
625
+ {
626
+ LightClass = APointLight::StaticClass();
627
+ }
628
+ else if (LightType.Equals(TEXT("spot"), ESearchCase::IgnoreCase))
629
+ {
630
+ LightClass = ASpotLight::StaticClass();
631
+ }
632
+ else if (LightType.Equals(TEXT("directional"), ESearchCase::IgnoreCase))
633
+ {
634
+ LightClass = ADirectionalLight::StaticClass();
635
+ }
636
+ else if (LightType.Equals(TEXT("rect"), ESearchCase::IgnoreCase))
637
+ {
638
+ LightClass = ARectLight::StaticClass();
639
+ }
640
+ else
641
+ {
642
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Unknown light type: %s. Use point, spot, directional, or rect."), *LightType));
643
+ Result->SetBoolField(TEXT("success"), false);
644
+ return MakeShared<FJsonValueObject>(Result);
645
+ }
646
+
647
+ FTransform LightTransform(FRotator::ZeroRotator, Location);
648
+ AActor* NewLight = World->SpawnActor<AActor>(LightClass, LightTransform);
649
+ if (!NewLight)
650
+ {
651
+ Result->SetStringField(TEXT("error"), TEXT("Failed to spawn light actor"));
652
+ Result->SetBoolField(TEXT("success"), false);
653
+ return MakeShared<FJsonValueObject>(Result);
654
+ }
655
+
656
+ // Set label if provided
657
+ FString Label;
658
+ if (Params->TryGetStringField(TEXT("label"), Label))
659
+ {
660
+ NewLight->SetActorLabel(Label);
661
+ }
662
+
663
+ // Set intensity on light component
664
+ ULightComponent* LightComponent = NewLight->FindComponentByClass<ULightComponent>();
665
+ if (LightComponent)
666
+ {
667
+ LightComponent->SetIntensity(Intensity);
668
+ }
669
+
670
+ Result->SetStringField(TEXT("actorLabel"), NewLight->GetActorLabel());
671
+ Result->SetStringField(TEXT("actorName"), NewLight->GetName());
672
+ Result->SetStringField(TEXT("lightType"), LightType);
673
+ Result->SetBoolField(TEXT("success"), true);
674
+
675
+ return MakeShared<FJsonValueObject>(Result);
676
+ }
677
+
678
+ TSharedPtr<FJsonValue> FLevelHandlers::SetLightProperties(const TSharedPtr<FJsonObject>& Params)
679
+ {
680
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
681
+
682
+ FString ActorLabel;
683
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
684
+ {
685
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
686
+ Result->SetBoolField(TEXT("success"), false);
687
+ return MakeShared<FJsonValueObject>(Result);
688
+ }
689
+
690
+ UWorld* World = GEditor->GetEditorWorldContext().World();
691
+ if (!World)
692
+ {
693
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
694
+ Result->SetBoolField(TEXT("success"), false);
695
+ return MakeShared<FJsonValueObject>(Result);
696
+ }
697
+
698
+ // Find actor by label
699
+ AActor* Actor = nullptr;
700
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
701
+ {
702
+ if ((*ActorIt)->GetActorLabel() == ActorLabel)
703
+ {
704
+ Actor = *ActorIt;
705
+ break;
706
+ }
707
+ }
708
+
709
+ if (!Actor)
710
+ {
711
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
712
+ Result->SetBoolField(TEXT("success"), false);
713
+ return MakeShared<FJsonValueObject>(Result);
714
+ }
715
+
716
+ ULightComponent* LightComponent = Actor->FindComponentByClass<ULightComponent>();
717
+ if (!LightComponent)
718
+ {
719
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor '%s' does not have a light component"), *ActorLabel));
720
+ Result->SetBoolField(TEXT("success"), false);
721
+ return MakeShared<FJsonValueObject>(Result);
722
+ }
723
+
724
+ // Set intensity if provided
725
+ double Intensity = 0.0;
726
+ if (Params->TryGetNumberField(TEXT("intensity"), Intensity))
727
+ {
728
+ LightComponent->SetIntensity(Intensity);
729
+ }
730
+
731
+ // Set color if provided
732
+ const TSharedPtr<FJsonObject>* ColorObj = nullptr;
733
+ if (Params->TryGetObjectField(TEXT("color"), ColorObj))
734
+ {
735
+ double R = 255.0, G = 255.0, B = 255.0;
736
+ (*ColorObj)->TryGetNumberField(TEXT("r"), R);
737
+ (*ColorObj)->TryGetNumberField(TEXT("g"), G);
738
+ (*ColorObj)->TryGetNumberField(TEXT("b"), B);
739
+ LightComponent->SetLightColor(FLinearColor(R / 255.0f, G / 255.0f, B / 255.0f));
740
+ }
741
+
742
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
743
+ Result->SetNumberField(TEXT("intensity"), LightComponent->Intensity);
744
+
745
+ FLinearColor CurrentColor = LightComponent->GetLightColor();
746
+ TSharedPtr<FJsonObject> ColorResult = MakeShared<FJsonObject>();
747
+ ColorResult->SetNumberField(TEXT("r"), CurrentColor.R * 255.0f);
748
+ ColorResult->SetNumberField(TEXT("g"), CurrentColor.G * 255.0f);
749
+ ColorResult->SetNumberField(TEXT("b"), CurrentColor.B * 255.0f);
750
+ Result->SetObjectField(TEXT("color"), ColorResult);
751
+
752
+ Result->SetBoolField(TEXT("success"), true);
753
+
754
+ return MakeShared<FJsonValueObject>(Result);
755
+ }
756
+
757
+ TSharedPtr<FJsonValue> FLevelHandlers::SpawnVolume(const TSharedPtr<FJsonObject>& Params)
758
+ {
759
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
760
+
761
+ FString VolumeType;
762
+ if (!Params->TryGetStringField(TEXT("volumeType"), VolumeType))
763
+ {
764
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'volumeType' parameter"));
765
+ Result->SetBoolField(TEXT("success"), false);
766
+ return MakeShared<FJsonValueObject>(Result);
767
+ }
768
+
769
+ UWorld* World = GEditor->GetEditorWorldContext().World();
770
+ if (!World)
771
+ {
772
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
773
+ Result->SetBoolField(TEXT("success"), false);
774
+ return MakeShared<FJsonValueObject>(Result);
775
+ }
776
+
777
+ // Get location
778
+ FVector Location = FVector::ZeroVector;
779
+ const TSharedPtr<FJsonObject>* LocationObj = nullptr;
780
+ if (Params->TryGetObjectField(TEXT("location"), LocationObj))
781
+ {
782
+ (*LocationObj)->TryGetNumberField(TEXT("x"), Location.X);
783
+ (*LocationObj)->TryGetNumberField(TEXT("y"), Location.Y);
784
+ (*LocationObj)->TryGetNumberField(TEXT("z"), Location.Z);
785
+ }
786
+
787
+ // Get extent
788
+ FVector Extent = FVector(100.0, 100.0, 100.0);
789
+ const TSharedPtr<FJsonObject>* ExtentObj = nullptr;
790
+ if (Params->TryGetObjectField(TEXT("extent"), ExtentObj))
791
+ {
792
+ (*ExtentObj)->TryGetNumberField(TEXT("x"), Extent.X);
793
+ (*ExtentObj)->TryGetNumberField(TEXT("y"), Extent.Y);
794
+ (*ExtentObj)->TryGetNumberField(TEXT("z"), Extent.Z);
795
+ }
796
+
797
+ // Determine volume class
798
+ UClass* VolumeClass = nullptr;
799
+ if (VolumeType.Equals(TEXT("BlockingVolume"), ESearchCase::IgnoreCase) || VolumeType.Equals(TEXT("blocking"), ESearchCase::IgnoreCase))
800
+ {
801
+ VolumeClass = ABlockingVolume::StaticClass();
802
+ }
803
+ else if (VolumeType.Equals(TEXT("TriggerVolume"), ESearchCase::IgnoreCase) || VolumeType.Equals(TEXT("trigger"), ESearchCase::IgnoreCase))
804
+ {
805
+ VolumeClass = ATriggerVolume::StaticClass();
806
+ }
807
+ else
808
+ {
809
+ // Try to find class by name
810
+ VolumeClass = FindObject<UClass>(nullptr, *VolumeType);
811
+ if (!VolumeClass)
812
+ {
813
+ VolumeClass = FindObject<UClass>(nullptr, *(TEXT("A") + VolumeType));
814
+ }
815
+ }
816
+
817
+ if (!VolumeClass)
818
+ {
819
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Volume class not found: %s"), *VolumeType));
820
+ Result->SetBoolField(TEXT("success"), false);
821
+ return MakeShared<FJsonValueObject>(Result);
822
+ }
823
+
824
+ FTransform VolumeTransform(FRotator::ZeroRotator, Location);
825
+ AActor* NewVolume = World->SpawnActor<AActor>(VolumeClass, VolumeTransform);
826
+ if (!NewVolume)
827
+ {
828
+ Result->SetStringField(TEXT("error"), TEXT("Failed to spawn volume actor"));
829
+ Result->SetBoolField(TEXT("success"), false);
830
+ return MakeShared<FJsonValueObject>(Result);
831
+ }
832
+
833
+ // Set label if provided
834
+ FString Label;
835
+ if (Params->TryGetStringField(TEXT("label"), Label))
836
+ {
837
+ NewVolume->SetActorLabel(Label);
838
+ }
839
+
840
+ // Set scale based on extent
841
+ NewVolume->SetActorScale3D(Extent / 100.0);
842
+
843
+ Result->SetStringField(TEXT("actorLabel"), NewVolume->GetActorLabel());
844
+ Result->SetStringField(TEXT("actorName"), NewVolume->GetName());
845
+ Result->SetStringField(TEXT("volumeType"), VolumeType);
846
+ Result->SetBoolField(TEXT("success"), true);
847
+
848
+ return MakeShared<FJsonValueObject>(Result);
849
+ }
850
+
851
+ TSharedPtr<FJsonValue> FLevelHandlers::AddComponentToActor(const TSharedPtr<FJsonObject>& Params)
852
+ {
853
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
854
+
855
+ FString ActorLabel;
856
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
857
+ {
858
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
859
+ Result->SetBoolField(TEXT("success"), false);
860
+ return MakeShared<FJsonValueObject>(Result);
861
+ }
862
+
863
+ FString ComponentClass;
864
+ if (!Params->TryGetStringField(TEXT("componentClass"), ComponentClass))
865
+ {
866
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'componentClass' parameter"));
867
+ Result->SetBoolField(TEXT("success"), false);
868
+ return MakeShared<FJsonValueObject>(Result);
869
+ }
870
+
871
+ FString ComponentName;
872
+ if (!Params->TryGetStringField(TEXT("componentName"), ComponentName))
873
+ {
874
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'componentName' parameter"));
875
+ Result->SetBoolField(TEXT("success"), false);
876
+ return MakeShared<FJsonValueObject>(Result);
877
+ }
878
+
879
+ UWorld* World = GEditor->GetEditorWorldContext().World();
880
+ if (!World)
881
+ {
882
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
883
+ Result->SetBoolField(TEXT("success"), false);
884
+ return MakeShared<FJsonValueObject>(Result);
885
+ }
886
+
887
+ // Find actor by label
888
+ AActor* Actor = nullptr;
889
+ for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
890
+ {
891
+ if ((*ActorIt)->GetActorLabel() == ActorLabel)
892
+ {
893
+ Actor = *ActorIt;
894
+ break;
895
+ }
896
+ }
897
+
898
+ if (!Actor)
899
+ {
900
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
901
+ Result->SetBoolField(TEXT("success"), false);
902
+ return MakeShared<FJsonValueObject>(Result);
903
+ }
904
+
905
+ // Find component class
906
+ UClass* CompClass = FindObject<UClass>(nullptr, *ComponentClass);
907
+ if (!CompClass)
908
+ {
909
+ CompClass = FindObject<UClass>(nullptr, *(TEXT("U") + ComponentClass));
910
+ }
911
+
912
+ if (!CompClass)
913
+ {
914
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Component class not found: %s"), *ComponentClass));
915
+ Result->SetBoolField(TEXT("success"), false);
916
+ return MakeShared<FJsonValueObject>(Result);
917
+ }
918
+
919
+ if (!CompClass->IsChildOf(UActorComponent::StaticClass()))
920
+ {
921
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Class '%s' is not an ActorComponent"), *ComponentClass));
922
+ Result->SetBoolField(TEXT("success"), false);
923
+ return MakeShared<FJsonValueObject>(Result);
924
+ }
925
+
926
+ FName CompName = FName(*ComponentName);
927
+ UActorComponent* NewComponent = NewObject<UActorComponent>(Actor, CompClass, CompName);
928
+ if (!NewComponent)
929
+ {
930
+ Result->SetStringField(TEXT("error"), TEXT("Failed to create component"));
931
+ Result->SetBoolField(TEXT("success"), false);
932
+ return MakeShared<FJsonValueObject>(Result);
933
+ }
934
+
935
+ // If it's a scene component, attach it to root
936
+ USceneComponent* SceneComp = Cast<USceneComponent>(NewComponent);
937
+ if (SceneComp && Actor->GetRootComponent())
938
+ {
939
+ SceneComp->SetupAttachment(Actor->GetRootComponent());
940
+ }
941
+
942
+ NewComponent->RegisterComponent();
943
+ Actor->AddInstanceComponent(NewComponent);
944
+
945
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
946
+ Result->SetStringField(TEXT("componentName"), ComponentName);
947
+ Result->SetStringField(TEXT("componentClass"), NewComponent->GetClass()->GetName());
948
+ Result->SetBoolField(TEXT("success"), true);
949
+
950
+ return MakeShared<FJsonValueObject>(Result);
951
+ }
952
+
953
+ TSharedPtr<FJsonValue> FLevelHandlers::LoadLevel(const TSharedPtr<FJsonObject>& Params)
954
+ {
955
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
956
+
957
+ FString LevelPath;
958
+ if (!Params->TryGetStringField(TEXT("levelPath"), LevelPath))
959
+ {
960
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'levelPath' parameter (e.g. /Game/Maps/MyMap)"));
961
+ Result->SetBoolField(TEXT("success"), false);
962
+ return MakeShared<FJsonValueObject>(Result);
963
+ }
964
+
965
+ // Use the LevelEditorSubsystem to load the level
966
+ ULevelEditorSubsystem* LevelEditorSubsystem = GEditor->GetEditorSubsystem<ULevelEditorSubsystem>();
967
+ if (!LevelEditorSubsystem)
968
+ {
969
+ Result->SetStringField(TEXT("error"), TEXT("LevelEditorSubsystem not available"));
970
+ Result->SetBoolField(TEXT("success"), false);
971
+ return MakeShared<FJsonValueObject>(Result);
972
+ }
973
+
974
+ bool bSuccess = LevelEditorSubsystem->LoadLevel(LevelPath);
975
+ if (!bSuccess)
976
+ {
977
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Failed to load level: %s"), *LevelPath));
978
+ Result->SetBoolField(TEXT("success"), false);
979
+ return MakeShared<FJsonValueObject>(Result);
980
+ }
981
+
982
+ // Get info about the newly loaded world
983
+ UWorld* World = GEditor->GetEditorWorldContext().World();
984
+ if (World)
985
+ {
986
+ Result->SetStringField(TEXT("worldName"), World->GetName());
987
+ Result->SetStringField(TEXT("worldPath"), World->GetPathName());
988
+ }
989
+
990
+ Result->SetStringField(TEXT("levelPath"), LevelPath);
991
+ Result->SetBoolField(TEXT("success"), true);
992
+
993
+ return MakeShared<FJsonValueObject>(Result);
994
+ }
995
+
996
+ TSharedPtr<FJsonValue> FLevelHandlers::SaveLevel(const TSharedPtr<FJsonObject>& Params)
997
+ {
998
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
999
+
1000
+ UWorld* World = GEditor->GetEditorWorldContext().World();
1001
+ if (!World)
1002
+ {
1003
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
1004
+ Result->SetBoolField(TEXT("success"), false);
1005
+ return MakeShared<FJsonValueObject>(Result);
1006
+ }
1007
+
1008
+ // Use the LevelEditorSubsystem to save the current level
1009
+ ULevelEditorSubsystem* LevelEditorSubsystem = GEditor->GetEditorSubsystem<ULevelEditorSubsystem>();
1010
+ if (!LevelEditorSubsystem)
1011
+ {
1012
+ Result->SetStringField(TEXT("error"), TEXT("LevelEditorSubsystem not available"));
1013
+ Result->SetBoolField(TEXT("success"), false);
1014
+ return MakeShared<FJsonValueObject>(Result);
1015
+ }
1016
+
1017
+ bool bSuccess = LevelEditorSubsystem->SaveCurrentLevel();
1018
+
1019
+ Result->SetStringField(TEXT("levelName"), World->GetName());
1020
+ Result->SetStringField(TEXT("levelPath"), World->GetPathName());
1021
+ Result->SetBoolField(TEXT("success"), bSuccess);
1022
+
1023
+ if (!bSuccess)
1024
+ {
1025
+ Result->SetStringField(TEXT("error"), TEXT("Failed to save current level"));
1026
+ }
1027
+
1028
+ return MakeShared<FJsonValueObject>(Result);
1029
+ }
1030
+
1031
+ TSharedPtr<FJsonValue> FLevelHandlers::ListSublevels(const TSharedPtr<FJsonObject>& Params)
1032
+ {
1033
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1034
+
1035
+ UWorld* World = GEditor->GetEditorWorldContext().World();
1036
+ if (!World)
1037
+ {
1038
+ Result->SetStringField(TEXT("error"), TEXT("Editor world not available"));
1039
+ Result->SetBoolField(TEXT("success"), false);
1040
+ return MakeShared<FJsonValueObject>(Result);
1041
+ }
1042
+
1043
+ TArray<TSharedPtr<FJsonValue>> SublevelsArray;
1044
+
1045
+ // Iterate streaming/sublevels
1046
+ const TArray<ULevelStreaming*>& StreamingLevels = World->GetStreamingLevels();
1047
+ for (ULevelStreaming* StreamingLevel : StreamingLevels)
1048
+ {
1049
+ if (!StreamingLevel) continue;
1050
+
1051
+ TSharedPtr<FJsonObject> LevelObj = MakeShared<FJsonObject>();
1052
+ LevelObj->SetStringField(TEXT("packageName"), StreamingLevel->GetWorldAssetPackageFName().ToString());
1053
+ LevelObj->SetStringField(TEXT("class"), StreamingLevel->GetClass()->GetName());
1054
+ LevelObj->SetBoolField(TEXT("isLoaded"), StreamingLevel->IsLevelLoaded());
1055
+ LevelObj->SetBoolField(TEXT("isVisible"), StreamingLevel->IsLevelVisible());
1056
+ LevelObj->SetBoolField(TEXT("shouldBeLoaded"), StreamingLevel->HasLoadRequestPending() || StreamingLevel->IsLevelLoaded());
1057
+
1058
+ // Get streaming level transform
1059
+ FTransform LevelTransform = StreamingLevel->LevelTransform;
1060
+ TSharedPtr<FJsonObject> TransformObj = MakeShared<FJsonObject>();
1061
+ FVector Location = LevelTransform.GetLocation();
1062
+ TransformObj->SetNumberField(TEXT("x"), Location.X);
1063
+ TransformObj->SetNumberField(TEXT("y"), Location.Y);
1064
+ TransformObj->SetNumberField(TEXT("z"), Location.Z);
1065
+ LevelObj->SetObjectField(TEXT("location"), TransformObj);
1066
+
1067
+ // Actor count if loaded
1068
+ if (StreamingLevel->IsLevelLoaded() && StreamingLevel->GetLoadedLevel())
1069
+ {
1070
+ LevelObj->SetNumberField(TEXT("actorCount"), StreamingLevel->GetLoadedLevel()->Actors.Num());
1071
+ }
1072
+
1073
+ SublevelsArray.Add(MakeShared<FJsonValueObject>(LevelObj));
1074
+ }
1075
+
1076
+ Result->SetStringField(TEXT("persistentLevel"), World->GetName());
1077
+ Result->SetArrayField(TEXT("sublevels"), SublevelsArray);
1078
+ Result->SetNumberField(TEXT("count"), SublevelsArray.Num());
1079
+ Result->SetBoolField(TEXT("success"), true);
1080
+
1081
+ return MakeShared<FJsonValueObject>(Result);
1082
+ }
1083
+
1084
+ TSharedPtr<FJsonValue> FLevelHandlers::SetComponentProperty(const TSharedPtr<FJsonObject>& Params)
1085
+ {
1086
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1087
+
1088
+ FString ActorLabel;
1089
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
1090
+ {
1091
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
1092
+ return MakeShared<FJsonValueObject>(Result);
1093
+ }
1094
+
1095
+ FString ComponentName;
1096
+ Params->TryGetStringField(TEXT("componentName"), ComponentName);
1097
+
1098
+ FString PropertyName;
1099
+ if (!Params->TryGetStringField(TEXT("propertyName"), PropertyName))
1100
+ {
1101
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'propertyName' parameter"));
1102
+ return MakeShared<FJsonValueObject>(Result);
1103
+ }
1104
+
1105
+ UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
1106
+ if (!World)
1107
+ {
1108
+ Result->SetStringField(TEXT("error"), TEXT("No editor world"));
1109
+ return MakeShared<FJsonValueObject>(Result);
1110
+ }
1111
+
1112
+ AActor* TargetActor = nullptr;
1113
+ for (TActorIterator<AActor> It(World); It; ++It)
1114
+ {
1115
+ if (It->GetActorLabel() == ActorLabel)
1116
+ {
1117
+ TargetActor = *It;
1118
+ break;
1119
+ }
1120
+ }
1121
+
1122
+ if (!TargetActor)
1123
+ {
1124
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Actor not found: %s"), *ActorLabel));
1125
+ return MakeShared<FJsonValueObject>(Result);
1126
+ }
1127
+
1128
+ // Find the component
1129
+ UActorComponent* TargetComp = nullptr;
1130
+ if (!ComponentName.IsEmpty())
1131
+ {
1132
+ TArray<UActorComponent*> Components;
1133
+ TargetActor->GetComponents(Components);
1134
+ for (UActorComponent* Comp : Components)
1135
+ {
1136
+ if (Comp->GetName() == ComponentName || Comp->GetClass()->GetName() == ComponentName)
1137
+ {
1138
+ TargetComp = Comp;
1139
+ break;
1140
+ }
1141
+ }
1142
+ }
1143
+ else
1144
+ {
1145
+ // Use root component as default
1146
+ TargetComp = TargetActor->GetRootComponent();
1147
+ }
1148
+
1149
+ if (!TargetComp)
1150
+ {
1151
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Component '%s' not found on actor '%s'"), *ComponentName, *ActorLabel));
1152
+ return MakeShared<FJsonValueObject>(Result);
1153
+ }
1154
+
1155
+ // Set the property
1156
+ FProperty* Prop = TargetComp->GetClass()->FindPropertyByName(*PropertyName);
1157
+ if (!Prop)
1158
+ {
1159
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Property '%s' not found on component"), *PropertyName));
1160
+ return MakeShared<FJsonValueObject>(Result);
1161
+ }
1162
+
1163
+ // Handle different value types from JSON
1164
+ const TSharedPtr<FJsonValue>* ValueField = Params->Values.Find(TEXT("value"));
1165
+ if (!ValueField || !(*ValueField).IsValid())
1166
+ {
1167
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'value' parameter"));
1168
+ return MakeShared<FJsonValueObject>(Result);
1169
+ }
1170
+
1171
+ FString ValueStr;
1172
+ if ((*ValueField)->TryGetString(ValueStr))
1173
+ {
1174
+ Prop->ImportText_Direct(*ValueStr, Prop->ContainerPtrToValuePtr<void>(TargetComp), TargetComp, PPF_None);
1175
+ }
1176
+ else
1177
+ {
1178
+ double NumValue;
1179
+ if ((*ValueField)->TryGetNumber(NumValue))
1180
+ {
1181
+ ValueStr = FString::SanitizeFloat(NumValue);
1182
+ Prop->ImportText_Direct(*ValueStr, Prop->ContainerPtrToValuePtr<void>(TargetComp), TargetComp, PPF_None);
1183
+ }
1184
+ else
1185
+ {
1186
+ bool BoolValue;
1187
+ if ((*ValueField)->TryGetBool(BoolValue))
1188
+ {
1189
+ ValueStr = BoolValue ? TEXT("true") : TEXT("false");
1190
+ Prop->ImportText_Direct(*ValueStr, Prop->ContainerPtrToValuePtr<void>(TargetComp), TargetComp, PPF_None);
1191
+ }
1192
+ }
1193
+ }
1194
+
1195
+ TargetComp->MarkPackageDirty();
1196
+
1197
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
1198
+ Result->SetStringField(TEXT("componentClass"), TargetComp->GetClass()->GetName());
1199
+ Result->SetStringField(TEXT("propertyName"), PropertyName);
1200
+ Result->SetBoolField(TEXT("success"), true);
1201
+ return MakeShared<FJsonValueObject>(Result);
1202
+ }
1203
+
1204
+ TSharedPtr<FJsonValue> FLevelHandlers::SetVolumeProperties(const TSharedPtr<FJsonObject>& Params)
1205
+ {
1206
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
1207
+
1208
+ FString ActorLabel;
1209
+ if (!Params->TryGetStringField(TEXT("actorLabel"), ActorLabel))
1210
+ {
1211
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'actorLabel' parameter"));
1212
+ return MakeShared<FJsonValueObject>(Result);
1213
+ }
1214
+
1215
+ UWorld* World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
1216
+ if (!World)
1217
+ {
1218
+ Result->SetStringField(TEXT("error"), TEXT("No editor world"));
1219
+ return MakeShared<FJsonValueObject>(Result);
1220
+ }
1221
+
1222
+ AActor* TargetActor = nullptr;
1223
+ for (TActorIterator<AActor> It(World); It; ++It)
1224
+ {
1225
+ if (It->GetActorLabel() == ActorLabel || It->GetName() == ActorLabel)
1226
+ {
1227
+ TargetActor = *It;
1228
+ break;
1229
+ }
1230
+ }
1231
+
1232
+ if (!TargetActor)
1233
+ {
1234
+ Result->SetStringField(TEXT("error"), FString::Printf(TEXT("Volume not found: %s"), *ActorLabel));
1235
+ return MakeShared<FJsonValueObject>(Result);
1236
+ }
1237
+
1238
+ TArray<TSharedPtr<FJsonValue>> Changes;
1239
+ for (auto& Pair : Params->Values)
1240
+ {
1241
+ if (Pair.Key == TEXT("actorLabel") || Pair.Key == TEXT("action"))
1242
+ continue;
1243
+
1244
+ FProperty* Prop = TargetActor->GetClass()->FindPropertyByName(*Pair.Key);
1245
+ if (Prop)
1246
+ {
1247
+ FString ValueStr;
1248
+ if (Pair.Value->TryGetString(ValueStr))
1249
+ {
1250
+ Prop->ImportText_Direct(*ValueStr, Prop->ContainerPtrToValuePtr<void>(TargetActor), TargetActor, PPF_None);
1251
+ Changes.Add(MakeShared<FJsonValueString>(Pair.Key));
1252
+ }
1253
+ else
1254
+ {
1255
+ double NumVal;
1256
+ if (Pair.Value->TryGetNumber(NumVal))
1257
+ {
1258
+ ValueStr = FString::SanitizeFloat(NumVal);
1259
+ Prop->ImportText_Direct(*ValueStr, Prop->ContainerPtrToValuePtr<void>(TargetActor), TargetActor, PPF_None);
1260
+ Changes.Add(MakeShared<FJsonValueString>(Pair.Key));
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+
1266
+ Result->SetStringField(TEXT("actorLabel"), ActorLabel);
1267
+ Result->SetArrayField(TEXT("changes"), Changes);
1268
+ Result->SetBoolField(TEXT("success"), true);
1269
+ return MakeShared<FJsonValueObject>(Result);
1270
+ }