ultimate-unreal-engine-mcp 0.1.0

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 (124) hide show
  1. package/README.md +729 -0
  2. package/dist/build/error-parser.js +51 -0
  3. package/dist/build/fix-suggester.js +84 -0
  4. package/dist/build/ubt-runner.js +146 -0
  5. package/dist/cli.js +13 -0
  6. package/dist/config.js +8 -0
  7. package/dist/docs/data/ue57-api.js +228 -0
  8. package/dist/docs/doc-index.js +110 -0
  9. package/dist/docs/types.js +4 -0
  10. package/dist/generators/class-generator.js +363 -0
  11. package/dist/generators/file-modifier.js +276 -0
  12. package/dist/generators/uht-validator.js +177 -0
  13. package/dist/index.js +89 -0
  14. package/dist/parsers/cpp-class-index.js +230 -0
  15. package/dist/parsers/cpp-parser.js +369 -0
  16. package/dist/parsers/ini-parser.js +216 -0
  17. package/dist/parsers/uproject-parser.js +130 -0
  18. package/dist/plugin-bridge/client.js +217 -0
  19. package/dist/plugin-bridge/protocol.js +6 -0
  20. package/dist/plugin-bridge/retry.js +23 -0
  21. package/dist/setup.js +209 -0
  22. package/dist/tools/ai-systems/index.js +247 -0
  23. package/dist/tools/ai-systems/types.js +4 -0
  24. package/dist/tools/animation/index.js +241 -0
  25. package/dist/tools/animation/types.js +4 -0
  26. package/dist/tools/audio/index.js +204 -0
  27. package/dist/tools/audio/types.js +4 -0
  28. package/dist/tools/blueprint/index.js +495 -0
  29. package/dist/tools/blueprint/types.js +4 -0
  30. package/dist/tools/build/index.js +163 -0
  31. package/dist/tools/chaos/index.js +230 -0
  32. package/dist/tools/chaos/types.js +4 -0
  33. package/dist/tools/collision-physics/index.js +211 -0
  34. package/dist/tools/config/index.js +288 -0
  35. package/dist/tools/cpp/index.js +305 -0
  36. package/dist/tools/docs/index.js +251 -0
  37. package/dist/tools/editor/index.js +242 -0
  38. package/dist/tools/gas/index.js +222 -0
  39. package/dist/tools/gas/types.js +5 -0
  40. package/dist/tools/import-export/index.js +218 -0
  41. package/dist/tools/input/index.js +146 -0
  42. package/dist/tools/known-issues/index.js +88 -0
  43. package/dist/tools/known-issues/middleware.js +55 -0
  44. package/dist/tools/known-issues/store.js +125 -0
  45. package/dist/tools/livelink/index.js +203 -0
  46. package/dist/tools/livelink/types.js +4 -0
  47. package/dist/tools/material/index.js +190 -0
  48. package/dist/tools/motion-design/index.js +251 -0
  49. package/dist/tools/motion-design/types.js +6 -0
  50. package/dist/tools/movie-render/index.js +220 -0
  51. package/dist/tools/networking/index.js +149 -0
  52. package/dist/tools/pcg/index.js +164 -0
  53. package/dist/tools/selection/index.js +180 -0
  54. package/dist/tools/sequencer/index.js +218 -0
  55. package/dist/tools/validation/index.js +183 -0
  56. package/dist/tools/validation/types.js +4 -0
  57. package/dist/tools/viewport/index.js +310 -0
  58. package/dist/tools/worldpartition/index.js +226 -0
  59. package/dist/tools/worldpartition/types.js +4 -0
  60. package/dist/utils/execFileNoThrow.js +40 -0
  61. package/dist/utils/logger.js +27 -0
  62. package/dist/utils/path-guard.js +26 -0
  63. package/package.json +40 -0
  64. package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
  65. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
  66. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
  67. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
  68. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
  69. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
  70. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
  71. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
  72. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
  73. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
  74. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
  75. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
  76. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
  77. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
  78. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
  79. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
  80. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
  81. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
  82. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
  83. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
  84. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
  85. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
  86. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
  87. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
  88. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
  89. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
  90. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
  91. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
  92. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
  93. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
  94. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
  95. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
  96. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
  97. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
  98. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
  99. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
  100. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
  101. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
  102. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
  103. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
  104. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
  105. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
  106. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
  107. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
  108. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
  109. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
  110. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
  111. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
  112. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
  113. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
  114. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
  115. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
  116. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
  117. package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
  118. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
  119. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
  120. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
  121. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
  122. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
  123. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
  124. package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
@@ -0,0 +1,338 @@
1
+ // MCPPieCommands.cpp
2
+ // Implements MCP command handlers for PIE (Play In Editor) session control.
3
+ //
4
+ // pie.start -- start PIE session (validates not already active)
5
+ // pie.stop -- stop PIE session (validates PIE is active)
6
+ // pie.logs -- return buffered log lines captured since PIE started
7
+ // pie.gameState -- return actor positions and active GameMode class during PIE
8
+ //
9
+ // All handlers are guaranteed to run on the game thread (FMCPCommandRouter guarantee via AsyncTask).
10
+ // PIE log capture uses a custom FOutputDevice subclass added to GLog.
11
+ // Log ring-buffer is capped at 10,000 lines (T-12-02: DoS mitigation).
12
+
13
+ #include "MCPPieCommands.h"
14
+
15
+ #include "Editor.h"
16
+ #include "Engine/World.h"
17
+ #include "GameFramework/GameModeBase.h"
18
+ #include "GameFramework/Actor.h"
19
+ #include "EngineUtils.h"
20
+ #include "Misc/OutputDevice.h"
21
+ #include "Misc/OutputDeviceRedirector.h"
22
+ #include "Serialization/JsonSerializer.h"
23
+ #include "Serialization/JsonWriter.h"
24
+ #include "Dom/JsonObject.h"
25
+ #include "Dom/JsonValue.h"
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // PIE log capture — FOutputDevice subclass + ring buffer
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /** Maximum number of log lines retained in the PIE capture buffer (T-12-02). */
32
+ static constexpr int32 GPieLogMaxLines = 10000;
33
+
34
+ /** Ring buffer of log lines captured during PIE. Accessed only on the game thread. */
35
+ static TArray<FString> GPieCapturedLogs;
36
+
37
+ /**
38
+ * Custom output device that appends log messages to GPieCapturedLogs.
39
+ * Added to GLog when PIE starts, removed when PIE stops.
40
+ * The ring buffer is capped at GPieLogMaxLines; oldest entries are dropped first.
41
+ */
42
+ class FMCPPieLogCapture : public FOutputDevice
43
+ {
44
+ public:
45
+ FMCPPieLogCapture()
46
+ {
47
+ // Suppress timestamps — they bloat output and callers can filter by line.
48
+ bSuppressEventTag = false;
49
+ bAutoEmitLineTerminator = false;
50
+ }
51
+
52
+ virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override
53
+ {
54
+ // Format: [Category] Message
55
+ FString Line = FString::Printf(TEXT("[%s] %s"), *Category.ToString(), V);
56
+
57
+ // Enforce ring buffer cap: drop oldest entry when at limit (T-12-02).
58
+ if (GPieCapturedLogs.Num() >= GPieLogMaxLines)
59
+ {
60
+ GPieCapturedLogs.RemoveAt(0, 1, /*bAllowShrinking=*/false);
61
+ }
62
+
63
+ GPieCapturedLogs.Add(MoveTemp(Line));
64
+ }
65
+
66
+ virtual bool CanBeUsedOnMultipleThreads() const override
67
+ {
68
+ // Log messages may come from non-game threads; the array is not
69
+ // thread-safe, but FOutputDeviceRedirector serialises calls under a
70
+ // lock before forwarding to device Serialize(), so this is safe.
71
+ return false;
72
+ }
73
+ };
74
+
75
+ /** Singleton log capture device; null when PIE is not running. */
76
+ static TUniquePtr<FMCPPieLogCapture> GPieLogDevice;
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Internal JSON response helpers
80
+ // ---------------------------------------------------------------------------
81
+
82
+ static FString BuildPieSuccessResponse(const FString& CorrId, TSharedPtr<FJsonObject> Data)
83
+ {
84
+ TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
85
+ Obj->SetBoolField(TEXT("success"), true);
86
+ Obj->SetStringField(TEXT("correlationId"), CorrId);
87
+ if (Data.IsValid())
88
+ {
89
+ Obj->SetObjectField(TEXT("data"), Data);
90
+ }
91
+
92
+ FString Output;
93
+ TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
94
+ FJsonSerializer::Serialize(Obj.ToSharedRef(), Writer);
95
+ return Output;
96
+ }
97
+
98
+ static FString BuildPieErrorResponse(const FString& CorrId, const FString& Error)
99
+ {
100
+ TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
101
+ Obj->SetBoolField(TEXT("success"), false);
102
+ Obj->SetStringField(TEXT("correlationId"), CorrId);
103
+ Obj->SetStringField(TEXT("error"), Error);
104
+
105
+ FString Output;
106
+ TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
107
+ FJsonSerializer::Serialize(Obj.ToSharedRef(), Writer);
108
+ return Output;
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // RegisterPieCommands
113
+ // ---------------------------------------------------------------------------
114
+
115
+ void RegisterPieCommands(FMCPCommandRouter& Router)
116
+ {
117
+ // -----------------------------------------------------------------------
118
+ // pie.start
119
+ // Starts a PIE session.
120
+ // Guard: returns "pie_already_active" if PIE is already running (T-12-01).
121
+ // Clears log buffer, creates and registers the log capture device.
122
+ // -----------------------------------------------------------------------
123
+ Router.RegisterHandler(TEXT("pie.start"), [](TSharedPtr<FJsonObject> Cmd, FMCPResponseSender SendResponse)
124
+ {
125
+ const FString CorrId = Cmd->GetStringField(TEXT("correlationId"));
126
+
127
+ if (!GEditor)
128
+ {
129
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("no_editor")) + TEXT("\n"));
130
+ return;
131
+ }
132
+
133
+ // T-12-01: Prevent double-start.
134
+ if (GEditor->IsPlayingSessionInEditor())
135
+ {
136
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("pie_already_active")) + TEXT("\n"));
137
+ return;
138
+ }
139
+
140
+ // Reset log buffer and start capture.
141
+ GPieCapturedLogs.Empty();
142
+ GPieLogDevice = MakeUnique<FMCPPieLogCapture>();
143
+ GLog->AddOutputDevice(GPieLogDevice.Get());
144
+
145
+ // Start PIE (false = game mode, not simulate-in-editor).
146
+ GEditor->PlayInEditor(GEditor->GetEditorWorldContext().World(), /*bInSimulateInEditor=*/false);
147
+
148
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
149
+ Data->SetBoolField(TEXT("started"), true);
150
+
151
+ SendResponse(BuildPieSuccessResponse(CorrId, Data) + TEXT("\n"));
152
+ });
153
+
154
+ // -----------------------------------------------------------------------
155
+ // pie.stop
156
+ // Stops the active PIE session.
157
+ // Guard: returns "pie_not_active" if PIE is not currently running.
158
+ // Removes and destroys the log capture device.
159
+ // -----------------------------------------------------------------------
160
+ Router.RegisterHandler(TEXT("pie.stop"), [](TSharedPtr<FJsonObject> Cmd, FMCPResponseSender SendResponse)
161
+ {
162
+ const FString CorrId = Cmd->GetStringField(TEXT("correlationId"));
163
+
164
+ if (!GEditor)
165
+ {
166
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("no_editor")) + TEXT("\n"));
167
+ return;
168
+ }
169
+
170
+ if (!GEditor->IsPlayingSessionInEditor())
171
+ {
172
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("pie_not_active")) + TEXT("\n"));
173
+ return;
174
+ }
175
+
176
+ // Detach and destroy the log capture device before ending the session.
177
+ if (GPieLogDevice.IsValid())
178
+ {
179
+ GLog->RemoveOutputDevice(GPieLogDevice.Get());
180
+ GPieLogDevice.Reset();
181
+ }
182
+
183
+ GEditor->RequestEndPlayMap();
184
+
185
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
186
+ Data->SetBoolField(TEXT("stopped"), true);
187
+
188
+ SendResponse(BuildPieSuccessResponse(CorrId, Data) + TEXT("\n"));
189
+ });
190
+
191
+ // -----------------------------------------------------------------------
192
+ // pie.logs
193
+ // Returns buffered log lines captured since the last pie.start.
194
+ // Optional payload fields:
195
+ // category_filter (string) -- substring match, case-insensitive; empty = all
196
+ // max_lines (int32) -- number of most-recent lines to return [1..1000], default 100
197
+ // Response: {lines:[...], total_captured:int, returned:int}
198
+ // T-12-02: max_lines clamped to 1000.
199
+ // -----------------------------------------------------------------------
200
+ Router.RegisterHandler(TEXT("pie.logs"), [](TSharedPtr<FJsonObject> Cmd, FMCPResponseSender SendResponse)
201
+ {
202
+ const FString CorrId = Cmd->GetStringField(TEXT("correlationId"));
203
+
204
+ // Extract optional payload.
205
+ FString CategoryFilter;
206
+ int32 MaxLines = 100;
207
+
208
+ const TSharedPtr<FJsonValue>* PayloadVal = Cmd->Values.Find(TEXT("payload"));
209
+ if (PayloadVal && (*PayloadVal)->Type == EJson::Object)
210
+ {
211
+ TSharedPtr<FJsonObject> Payload = (*PayloadVal)->AsObject();
212
+ Payload->TryGetStringField(TEXT("category_filter"), CategoryFilter);
213
+
214
+ double MaxLinesDouble = 100.0;
215
+ if (Payload->TryGetNumberField(TEXT("max_lines"), MaxLinesDouble))
216
+ {
217
+ MaxLines = static_cast<int32>(MaxLinesDouble);
218
+ }
219
+ }
220
+
221
+ // Clamp max_lines to [1, 1000] (T-12-02 DoS mitigation).
222
+ MaxLines = FMath::Clamp(MaxLines, 1, 1000);
223
+
224
+ const int32 TotalCaptured = GPieCapturedLogs.Num();
225
+
226
+ // Filter by category if requested.
227
+ TArray<FString> Filtered;
228
+ if (CategoryFilter.IsEmpty())
229
+ {
230
+ Filtered = GPieCapturedLogs;
231
+ }
232
+ else
233
+ {
234
+ for (const FString& Line : GPieCapturedLogs)
235
+ {
236
+ if (Line.Contains(CategoryFilter, ESearchCase::IgnoreCase))
237
+ {
238
+ Filtered.Add(Line);
239
+ }
240
+ }
241
+ }
242
+
243
+ // Take the last MaxLines entries (most recent).
244
+ const int32 StartIdx = FMath::Max(0, Filtered.Num() - MaxLines);
245
+ const int32 ReturnedCount = Filtered.Num() - StartIdx;
246
+
247
+ TArray<TSharedPtr<FJsonValue>> LinesArray;
248
+ LinesArray.Reserve(ReturnedCount);
249
+ for (int32 i = StartIdx; i < Filtered.Num(); ++i)
250
+ {
251
+ LinesArray.Add(MakeShared<FJsonValueString>(Filtered[i]));
252
+ }
253
+
254
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
255
+ Data->SetArrayField(TEXT("lines"), LinesArray);
256
+ Data->SetNumberField(TEXT("total_captured"), static_cast<double>(TotalCaptured));
257
+ Data->SetNumberField(TEXT("returned"), static_cast<double>(ReturnedCount));
258
+
259
+ SendResponse(BuildPieSuccessResponse(CorrId, Data) + TEXT("\n"));
260
+ });
261
+
262
+ // -----------------------------------------------------------------------
263
+ // pie.gameState
264
+ // Returns the active GameMode class and up to 200 actor positions from
265
+ // the PIE world (GEditor->PlayWorld).
266
+ // Guard: returns "pie_not_active" if PIE is not running.
267
+ // Response: {game_mode_class:string, actor_count:int, actors:[{label,class,location:{x,y,z}},...]}
268
+ // T-12-03: actor list capped at 200.
269
+ // -----------------------------------------------------------------------
270
+ Router.RegisterHandler(TEXT("pie.gameState"), [](TSharedPtr<FJsonObject> Cmd, FMCPResponseSender SendResponse)
271
+ {
272
+ const FString CorrId = Cmd->GetStringField(TEXT("correlationId"));
273
+
274
+ if (!GEditor)
275
+ {
276
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("no_editor")) + TEXT("\n"));
277
+ return;
278
+ }
279
+
280
+ if (!GEditor->IsPlayingSessionInEditor())
281
+ {
282
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("pie_not_active")) + TEXT("\n"));
283
+ return;
284
+ }
285
+
286
+ UWorld* PieWorld = GEditor->PlayWorld;
287
+ if (!PieWorld)
288
+ {
289
+ SendResponse(BuildPieErrorResponse(CorrId, TEXT("pie_world_unavailable")) + TEXT("\n"));
290
+ return;
291
+ }
292
+
293
+ // Resolve the active GameMode class name.
294
+ AGameModeBase* GM = PieWorld->GetAuthGameMode<AGameModeBase>();
295
+ FString GameModeClass = GM ? GM->GetClass()->GetName() : TEXT("None");
296
+
297
+ // Iterate PIE actors, capped at 200 (T-12-03).
298
+ static constexpr int32 MaxActors = 200;
299
+ TArray<TSharedPtr<FJsonValue>> ActorsArray;
300
+ ActorsArray.Reserve(MaxActors);
301
+
302
+ for (TActorIterator<AActor> It(PieWorld); It && ActorsArray.Num() < MaxActors; ++It)
303
+ {
304
+ AActor* Actor = *It;
305
+ if (!Actor)
306
+ {
307
+ continue;
308
+ }
309
+
310
+ // Skip Unreal internal default actors.
311
+ if (Actor->GetName().StartsWith(TEXT("Default_")))
312
+ {
313
+ continue;
314
+ }
315
+
316
+ const FVector Loc = Actor->GetActorLocation();
317
+
318
+ TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
319
+ LocObj->SetNumberField(TEXT("x"), static_cast<double>(Loc.X));
320
+ LocObj->SetNumberField(TEXT("y"), static_cast<double>(Loc.Y));
321
+ LocObj->SetNumberField(TEXT("z"), static_cast<double>(Loc.Z));
322
+
323
+ TSharedPtr<FJsonObject> ActorObj = MakeShared<FJsonObject>();
324
+ ActorObj->SetStringField(TEXT("label"), Actor->GetActorLabel());
325
+ ActorObj->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
326
+ ActorObj->SetObjectField(TEXT("location"), LocObj);
327
+
328
+ ActorsArray.Add(MakeShared<FJsonValueObject>(ActorObj));
329
+ }
330
+
331
+ TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
332
+ Data->SetStringField(TEXT("game_mode_class"), GameModeClass);
333
+ Data->SetNumberField(TEXT("actor_count"), static_cast<double>(ActorsArray.Num()));
334
+ Data->SetArrayField(TEXT("actors"), ActorsArray);
335
+
336
+ SendResponse(BuildPieSuccessResponse(CorrId, Data) + TEXT("\n"));
337
+ });
338
+ }
@@ -0,0 +1,16 @@
1
+ // MCPPieCommands.h
2
+ // Declares handler registration for PIE (Play In Editor) control.
3
+ // Handles: pie.start, pie.stop, pie.logs, pie.gameState
4
+ //
5
+ // Call RegisterPieCommands(*Router) in MCPBridgeSubsystem::Initialize()
6
+ // BEFORE the TCP server starts accepting connections.
7
+
8
+ #pragma once
9
+
10
+ #include "MCPCommandRouter.h"
11
+
12
+ /**
13
+ * Register all four PIE command handlers into the given router.
14
+ * Must be called on the game thread before connections arrive.
15
+ */
16
+ void RegisterPieCommands(FMCPCommandRouter& Router);