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,71 @@
1
+ #pragma once
2
+
3
+ #include "CoreMinimal.h"
4
+ #include "Dom/JsonValue.h"
5
+ #include "Dom/JsonObject.h"
6
+
7
+ class FMCPHandlerRegistry;
8
+
9
+ /**
10
+ * Demo scene builder handlers - Neon Shrine demo.
11
+ * Ports the Python demo.py scene builder to C++ for the UE MCP Bridge.
12
+ *
13
+ * Registers 3 handlers:
14
+ * demo_step - Execute a single step by index (param: step). If omitted, returns step list.
15
+ * demo_get_steps - Return ordered step list.
16
+ * demo_cleanup - Remove all demo actors and assets.
17
+ */
18
+ class FDemoHandlers
19
+ {
20
+ public:
21
+ static void RegisterHandlers(FMCPHandlerRegistry& Registry);
22
+
23
+ private:
24
+ // Top-level handlers
25
+ static TSharedPtr<FJsonValue> DemoStep(const TSharedPtr<FJsonObject>& Params);
26
+ static TSharedPtr<FJsonValue> DemoGetSteps(const TSharedPtr<FJsonObject>& Params);
27
+ static TSharedPtr<FJsonValue> DemoCleanup(const TSharedPtr<FJsonObject>& Params);
28
+
29
+ // ---- Individual step implementations (19 steps) ----
30
+ static TSharedPtr<FJsonObject> StepCreateLevel();
31
+ static TSharedPtr<FJsonObject> StepMaterials();
32
+ static TSharedPtr<FJsonObject> StepFloor();
33
+ static TSharedPtr<FJsonObject> StepPedestal();
34
+ static TSharedPtr<FJsonObject> StepHeroSphere();
35
+ static TSharedPtr<FJsonObject> StepPillars();
36
+ static TSharedPtr<FJsonObject> StepOrbs();
37
+ static TSharedPtr<FJsonObject> StepNeonLights();
38
+ static TSharedPtr<FJsonObject> StepHeroLight();
39
+ static TSharedPtr<FJsonObject> StepMoonlight();
40
+ static TSharedPtr<FJsonObject> StepSkyLight();
41
+ static TSharedPtr<FJsonObject> StepFog();
42
+ static TSharedPtr<FJsonObject> StepPostProcess();
43
+ static TSharedPtr<FJsonObject> StepNiagaraVfx();
44
+ static TSharedPtr<FJsonObject> StepPcgScatter();
45
+ static TSharedPtr<FJsonObject> StepOrbitRings();
46
+ static TSharedPtr<FJsonObject> StepLevelSequence();
47
+ static TSharedPtr<FJsonObject> StepTuningPanel();
48
+ static TSharedPtr<FJsonObject> StepSave();
49
+
50
+ // ---- Utility helpers ----
51
+ static AActor* SpawnMesh(const FString& Label, const FString& MeshPath,
52
+ FVector Location,
53
+ FRotator Rotation = FRotator::ZeroRotator,
54
+ FVector Scale = FVector(1, 1, 1));
55
+
56
+ static AActor* SpawnPointLight(const FString& Label, FVector Location,
57
+ FColor Color, float Intensity);
58
+
59
+ static UMaterialInterface* LoadDemoMat(const FString& Name);
60
+
61
+ static void ApplyMat(AActor* Actor, UMaterialInterface* Mat);
62
+
63
+ // Build the ordered step list (id, name, description)
64
+ struct FDemoStep
65
+ {
66
+ int32 Index;
67
+ FString Id;
68
+ FString Description;
69
+ };
70
+ static TArray<FDemoStep> GetStepDefinitions();
71
+ };
@@ -0,0 +1,465 @@
1
+ #include "DialogHandlers.h"
2
+ #include "UE_MCP_BridgeModule.h"
3
+ #include "HandlerRegistry.h"
4
+ #include "Misc/CoreDelegates.h"
5
+ #include "Framework/Application/SlateApplication.h"
6
+ #include "Widgets/SWindow.h"
7
+ #include "Widgets/Text/STextBlock.h"
8
+ #include "Widgets/Input/SButton.h"
9
+
10
+ // Static member definitions
11
+ TArray<FDialogHandlers::FDialogPolicy> FDialogHandlers::Policies;
12
+ FDelegateHandle FDialogHandlers::OriginalDelegateHandle;
13
+ bool FDialogHandlers::bHookInstalled = false;
14
+
15
+ void FDialogHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
16
+ {
17
+ Registry.RegisterHandler(TEXT("set_dialog_policy"), &SetDialogPolicy);
18
+ Registry.RegisterHandler(TEXT("clear_dialog_policy"), &ClearDialogPolicy);
19
+ Registry.RegisterHandler(TEXT("get_dialog_policy"), &GetDialogPolicy);
20
+ Registry.RegisterHandler(TEXT("list_dialogs"), &ListDialogs);
21
+ Registry.RegisterHandler(TEXT("respond_to_dialog"), &RespondToDialog);
22
+ }
23
+
24
+ void FDialogHandlers::InstallDialogHook()
25
+ {
26
+ if (bHookInstalled)
27
+ {
28
+ return;
29
+ }
30
+
31
+ // UE 5.7: ModalErrorMessage was removed. Use OnModalMessageBox instead if available.
32
+ // For now, we skip the hook — dialog listing/responding still works via Slate.
33
+ bHookInstalled = true;
34
+
35
+ UE_LOG(LogMCPBridge, Log, TEXT("[UE-MCP] Dialog hook installed"));
36
+ }
37
+
38
+ void FDialogHandlers::RemoveDialogHook()
39
+ {
40
+ if (!bHookInstalled)
41
+ {
42
+ return;
43
+ }
44
+
45
+ bHookInstalled = false;
46
+ Policies.Empty();
47
+
48
+ UE_LOG(LogMCPBridge, Log, TEXT("[UE-MCP] Dialog hook removed"));
49
+ }
50
+
51
+ EAppReturnType::Type FDialogHandlers::HandleModalDialog(EAppMsgType::Type MsgType, const FText& Text, const FText& Title)
52
+ {
53
+ FString MessageStr = Text.ToString();
54
+ FString TitleStr = Title.ToString();
55
+
56
+ // Check policies for a match
57
+ for (const FDialogPolicy& Policy : Policies)
58
+ {
59
+ if (MessageStr.Contains(Policy.Pattern) || TitleStr.Contains(Policy.Pattern))
60
+ {
61
+ UE_LOG(LogMCPBridge, Log, TEXT("[UE-MCP] Dialog auto-responded: pattern='%s' title='%s' response=%s"),
62
+ *Policy.Pattern, *TitleStr, *ResponseTypeToString(Policy.Response));
63
+ return Policy.Response;
64
+ }
65
+ }
66
+
67
+ // No policy matched — fall through to default UE behavior
68
+ UE_LOG(LogMCPBridge, Log, TEXT("[UE-MCP] Dialog shown (no policy match): title='%s' message='%s'"),
69
+ *TitleStr, *MessageStr.Left(200));
70
+
71
+ // Return the "default" response based on message type
72
+ switch (MsgType)
73
+ {
74
+ case EAppMsgType::Ok:
75
+ return EAppReturnType::Ok;
76
+ case EAppMsgType::YesNo:
77
+ case EAppMsgType::YesNoCancel:
78
+ return EAppReturnType::No;
79
+ case EAppMsgType::OkCancel:
80
+ case EAppMsgType::YesNoYesAllNoAll:
81
+ case EAppMsgType::YesNoYesAllNoAllCancel:
82
+ case EAppMsgType::YesNoYesAll:
83
+ return EAppReturnType::Cancel;
84
+ case EAppMsgType::CancelRetryContinue:
85
+ return EAppReturnType::Cancel;
86
+ default:
87
+ return EAppReturnType::No;
88
+ }
89
+ }
90
+
91
+ TSharedPtr<FJsonValue> FDialogHandlers::SetDialogPolicy(const TSharedPtr<FJsonObject>& Params)
92
+ {
93
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
94
+
95
+ FString Pattern;
96
+ if (!Params->TryGetStringField(TEXT("pattern"), Pattern) || Pattern.IsEmpty())
97
+ {
98
+ Result->SetStringField(TEXT("error"), TEXT("Missing or empty 'pattern' parameter"));
99
+ Result->SetBoolField(TEXT("success"), false);
100
+ return MakeShared<FJsonValueObject>(Result);
101
+ }
102
+
103
+ FString ResponseStr;
104
+ if (!Params->TryGetStringField(TEXT("response"), ResponseStr))
105
+ {
106
+ Result->SetStringField(TEXT("error"), TEXT("Missing 'response' parameter"));
107
+ Result->SetBoolField(TEXT("success"), false);
108
+ return MakeShared<FJsonValueObject>(Result);
109
+ }
110
+
111
+ EAppReturnType::Type Response = ParseResponseType(ResponseStr);
112
+
113
+ // Remove existing policy with same pattern
114
+ Policies.RemoveAll([&Pattern](const FDialogPolicy& P) { return P.Pattern == Pattern; });
115
+
116
+ // Add new policy
117
+ FDialogPolicy NewPolicy;
118
+ NewPolicy.Pattern = Pattern;
119
+ NewPolicy.Response = Response;
120
+ Policies.Add(NewPolicy);
121
+
122
+ // Ensure hook is installed
123
+ if (!bHookInstalled)
124
+ {
125
+ InstallDialogHook();
126
+ }
127
+
128
+ Result->SetStringField(TEXT("pattern"), Pattern);
129
+ Result->SetStringField(TEXT("response"), ResponseTypeToString(Response));
130
+ Result->SetNumberField(TEXT("policyCount"), Policies.Num());
131
+ Result->SetBoolField(TEXT("success"), true);
132
+
133
+ return MakeShared<FJsonValueObject>(Result);
134
+ }
135
+
136
+ TSharedPtr<FJsonValue> FDialogHandlers::ClearDialogPolicy(const TSharedPtr<FJsonObject>& Params)
137
+ {
138
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
139
+
140
+ FString Pattern;
141
+ if (Params->TryGetStringField(TEXT("pattern"), Pattern) && !Pattern.IsEmpty())
142
+ {
143
+ int32 Removed = Policies.RemoveAll([&Pattern](const FDialogPolicy& P) { return P.Pattern == Pattern; });
144
+ Result->SetStringField(TEXT("pattern"), Pattern);
145
+ Result->SetNumberField(TEXT("removed"), Removed);
146
+ }
147
+ else
148
+ {
149
+ int32 Count = Policies.Num();
150
+ Policies.Empty();
151
+ Result->SetNumberField(TEXT("removed"), Count);
152
+ }
153
+
154
+ Result->SetNumberField(TEXT("policyCount"), Policies.Num());
155
+ Result->SetBoolField(TEXT("success"), true);
156
+
157
+ return MakeShared<FJsonValueObject>(Result);
158
+ }
159
+
160
+ TSharedPtr<FJsonValue> FDialogHandlers::GetDialogPolicy(const TSharedPtr<FJsonObject>& Params)
161
+ {
162
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
163
+
164
+ TArray<TSharedPtr<FJsonValue>> PoliciesArray;
165
+ for (const FDialogPolicy& Policy : Policies)
166
+ {
167
+ TSharedPtr<FJsonObject> P = MakeShared<FJsonObject>();
168
+ P->SetStringField(TEXT("pattern"), Policy.Pattern);
169
+ P->SetStringField(TEXT("response"), ResponseTypeToString(Policy.Response));
170
+ PoliciesArray.Add(MakeShared<FJsonValueObject>(P));
171
+ }
172
+
173
+ Result->SetArrayField(TEXT("policies"), PoliciesArray);
174
+ Result->SetNumberField(TEXT("count"), Policies.Num());
175
+ Result->SetBoolField(TEXT("hookInstalled"), bHookInstalled);
176
+ Result->SetBoolField(TEXT("success"), true);
177
+
178
+ return MakeShared<FJsonValueObject>(Result);
179
+ }
180
+
181
+ TSharedPtr<FJsonValue> FDialogHandlers::ListDialogs(const TSharedPtr<FJsonObject>& Params)
182
+ {
183
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
184
+ TArray<TSharedPtr<FJsonValue>> DialogsArray;
185
+
186
+ if (FSlateApplication::IsInitialized())
187
+ {
188
+ TSharedPtr<SWindow> ActiveModal = FSlateApplication::Get().GetActiveModalWindow();
189
+ if (ActiveModal.IsValid())
190
+ {
191
+ TSharedPtr<FJsonObject> DialogObj = MakeShared<FJsonObject>();
192
+ DialogObj->SetStringField(TEXT("title"), ActiveModal->GetTitle().ToString());
193
+
194
+ // Traverse widget tree to find text blocks and buttons
195
+ TArray<FString> TextContents;
196
+ TArray<FString> ButtonLabels;
197
+
198
+ TFunction<void(const TSharedRef<SWidget>&)> TraverseWidgets = [&](const TSharedRef<SWidget>& Widget)
199
+ {
200
+ // Check for text blocks
201
+ if (Widget->GetType() == TEXT("STextBlock"))
202
+ {
203
+ TSharedRef<STextBlock> TextBlock = StaticCastSharedRef<STextBlock>(Widget);
204
+ FString Text = TextBlock->GetText().ToString();
205
+ if (!Text.IsEmpty())
206
+ {
207
+ TextContents.Add(Text);
208
+ }
209
+ }
210
+
211
+ // Check for buttons
212
+ if (Widget->GetType() == TEXT("SButton"))
213
+ {
214
+ // Try to find the text label inside the button
215
+ FChildren* ButtonChildren = Widget->GetChildren();
216
+ if (ButtonChildren)
217
+ {
218
+ for (int32 i = 0; i < ButtonChildren->Num(); ++i)
219
+ {
220
+ TSharedRef<SWidget> Child = ButtonChildren->GetChildAt(i);
221
+ if (Child->GetType() == TEXT("STextBlock"))
222
+ {
223
+ TSharedRef<STextBlock> BtnText = StaticCastSharedRef<STextBlock>(Child);
224
+ FString Label = BtnText->GetText().ToString();
225
+ if (!Label.IsEmpty())
226
+ {
227
+ ButtonLabels.Add(Label);
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+
234
+ // Recurse into children
235
+ FChildren* Children = Widget->GetChildren();
236
+ if (Children)
237
+ {
238
+ for (int32 i = 0; i < Children->Num(); ++i)
239
+ {
240
+ TraverseWidgets(Children->GetChildAt(i));
241
+ }
242
+ }
243
+ };
244
+
245
+ TraverseWidgets(ActiveModal.ToSharedRef());
246
+
247
+ // Build message from text contents (skip the title if it matches)
248
+ FString TitleStr = ActiveModal->GetTitle().ToString();
249
+ FString Message;
250
+ for (const FString& T : TextContents)
251
+ {
252
+ if (T != TitleStr)
253
+ {
254
+ if (!Message.IsEmpty()) Message += TEXT("\n");
255
+ Message += T;
256
+ }
257
+ }
258
+
259
+ DialogObj->SetStringField(TEXT("message"), Message);
260
+
261
+ TArray<TSharedPtr<FJsonValue>> ButtonsJsonArray;
262
+ for (const FString& Label : ButtonLabels)
263
+ {
264
+ ButtonsJsonArray.Add(MakeShared<FJsonValueString>(Label));
265
+ }
266
+ DialogObj->SetArrayField(TEXT("buttons"), ButtonsJsonArray);
267
+
268
+ DialogsArray.Add(MakeShared<FJsonValueObject>(DialogObj));
269
+ }
270
+ }
271
+
272
+ Result->SetArrayField(TEXT("dialogs"), DialogsArray);
273
+ Result->SetNumberField(TEXT("count"), DialogsArray.Num());
274
+ Result->SetBoolField(TEXT("success"), true);
275
+
276
+ return MakeShared<FJsonValueObject>(Result);
277
+ }
278
+
279
+ TSharedPtr<FJsonValue> FDialogHandlers::RespondToDialog(const TSharedPtr<FJsonObject>& Params)
280
+ {
281
+ TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
282
+
283
+ if (!FSlateApplication::IsInitialized())
284
+ {
285
+ Result->SetStringField(TEXT("error"), TEXT("Slate not initialized"));
286
+ Result->SetBoolField(TEXT("success"), false);
287
+ return MakeShared<FJsonValueObject>(Result);
288
+ }
289
+
290
+ TSharedPtr<SWindow> ActiveModal = FSlateApplication::Get().GetActiveModalWindow();
291
+ if (!ActiveModal.IsValid())
292
+ {
293
+ Result->SetStringField(TEXT("error"), TEXT("No active modal dialog"));
294
+ Result->SetBoolField(TEXT("success"), false);
295
+ return MakeShared<FJsonValueObject>(Result);
296
+ }
297
+
298
+ // Determine action: buttonIndex, buttonLabel, or key simulation
299
+ FString ButtonLabel;
300
+ int32 ButtonIndex = -1;
301
+ Params->TryGetStringField(TEXT("buttonLabel"), ButtonLabel);
302
+ Params->TryGetNumberField(TEXT("buttonIndex"), ButtonIndex);
303
+
304
+ // Find buttons in the dialog
305
+ TArray<TSharedRef<SButton>> Buttons;
306
+ TArray<FString> ButtonTexts;
307
+
308
+ TFunction<void(const TSharedRef<SWidget>&)> FindButtons = [&](const TSharedRef<SWidget>& Widget)
309
+ {
310
+ if (Widget->GetType() == TEXT("SButton"))
311
+ {
312
+ Buttons.Add(StaticCastSharedRef<SButton>(Widget));
313
+
314
+ // Extract label
315
+ FString Label;
316
+ FChildren* ButtonChildren = Widget->GetChildren();
317
+ if (ButtonChildren)
318
+ {
319
+ for (int32 i = 0; i < ButtonChildren->Num(); ++i)
320
+ {
321
+ TSharedRef<SWidget> Child = ButtonChildren->GetChildAt(i);
322
+ if (Child->GetType() == TEXT("STextBlock"))
323
+ {
324
+ Label = StaticCastSharedRef<STextBlock>(Child)->GetText().ToString();
325
+ break;
326
+ }
327
+ }
328
+ }
329
+ ButtonTexts.Add(Label);
330
+ }
331
+
332
+ FChildren* Children = Widget->GetChildren();
333
+ if (Children)
334
+ {
335
+ for (int32 i = 0; i < Children->Num(); ++i)
336
+ {
337
+ FindButtons(Children->GetChildAt(i));
338
+ }
339
+ }
340
+ };
341
+
342
+ FindButtons(ActiveModal.ToSharedRef());
343
+
344
+ // Resolve which button to click
345
+ int32 TargetIndex = -1;
346
+
347
+ if (!ButtonLabel.IsEmpty())
348
+ {
349
+ for (int32 i = 0; i < ButtonTexts.Num(); ++i)
350
+ {
351
+ if (ButtonTexts[i].Contains(ButtonLabel))
352
+ {
353
+ TargetIndex = i;
354
+ break;
355
+ }
356
+ }
357
+ }
358
+ else if (ButtonIndex >= 0 && ButtonIndex < Buttons.Num())
359
+ {
360
+ TargetIndex = ButtonIndex;
361
+ }
362
+
363
+ if (TargetIndex >= 0 && TargetIndex < Buttons.Num())
364
+ {
365
+ // Simulate the button click
366
+ FSlateApplication::Get().SetKeyboardFocus(Buttons[TargetIndex]);
367
+
368
+ // Use the reply mechanism to simulate a click
369
+ FReply Reply = FReply::Handled();
370
+ TSharedRef<SButton> TargetButton = Buttons[TargetIndex];
371
+
372
+ // Simulate mouse down + up on the button to trigger OnClicked
373
+ FGeometry ButtonGeometry = TargetButton->GetCachedGeometry();
374
+ FVector2D LocalCenter = ButtonGeometry.GetLocalSize() * 0.5f;
375
+ FVector2D AbsoluteCenter = ButtonGeometry.LocalToAbsolute(LocalCenter);
376
+
377
+ FPointerEvent MouseDownEvent(
378
+ 0, // PointerIndex
379
+ AbsoluteCenter,
380
+ AbsoluteCenter,
381
+ TSet<FKey>(),
382
+ EKeys::LeftMouseButton,
383
+ 0,
384
+ FModifierKeysState()
385
+ );
386
+
387
+ TargetButton->OnMouseButtonDown(ButtonGeometry, MouseDownEvent);
388
+ TargetButton->OnMouseButtonUp(ButtonGeometry, MouseDownEvent);
389
+
390
+ Result->SetStringField(TEXT("clickedButton"), ButtonTexts[TargetIndex]);
391
+ Result->SetNumberField(TEXT("buttonIndex"), TargetIndex);
392
+ Result->SetBoolField(TEXT("success"), true);
393
+ }
394
+ else
395
+ {
396
+ // Fallback: send Escape key to dismiss
397
+ FString Action;
398
+ if (Params->TryGetStringField(TEXT("action"), Action) && Action == TEXT("escape"))
399
+ {
400
+ FSlateApplication::Get().ProcessKeyDownEvent(FKeyEvent(EKeys::Escape, FModifierKeysState(), 0, false, 0, 0));
401
+ FSlateApplication::Get().ProcessKeyUpEvent(FKeyEvent(EKeys::Escape, FModifierKeysState(), 0, false, 0, 0));
402
+ Result->SetStringField(TEXT("action"), TEXT("escape"));
403
+ Result->SetBoolField(TEXT("success"), true);
404
+ }
405
+ else
406
+ {
407
+ TArray<TSharedPtr<FJsonValue>> AvailableButtons;
408
+ for (const FString& T : ButtonTexts)
409
+ {
410
+ AvailableButtons.Add(MakeShared<FJsonValueString>(T));
411
+ }
412
+ Result->SetArrayField(TEXT("availableButtons"), AvailableButtons);
413
+ Result->SetStringField(TEXT("error"), TEXT("Button not found. Provide buttonIndex or buttonLabel matching an available button."));
414
+ Result->SetBoolField(TEXT("success"), false);
415
+ }
416
+ }
417
+
418
+ return MakeShared<FJsonValueObject>(Result);
419
+ }
420
+
421
+ EAppReturnType::Type FDialogHandlers::ParseResponseType(const FString& ResponseStr)
422
+ {
423
+ FString Lower = ResponseStr.ToLower();
424
+ if (Lower == TEXT("yes")) return EAppReturnType::Yes;
425
+ if (Lower == TEXT("no")) return EAppReturnType::No;
426
+ if (Lower == TEXT("ok")) return EAppReturnType::Ok;
427
+ if (Lower == TEXT("cancel")) return EAppReturnType::Cancel;
428
+ if (Lower == TEXT("retry")) return EAppReturnType::Retry;
429
+ if (Lower == TEXT("continue")) return EAppReturnType::Continue;
430
+ if (Lower == TEXT("yesall")) return EAppReturnType::YesAll;
431
+ if (Lower == TEXT("noall")) return EAppReturnType::NoAll;
432
+ return EAppReturnType::Ok;
433
+ }
434
+
435
+ FString FDialogHandlers::ResponseTypeToString(EAppReturnType::Type Response)
436
+ {
437
+ switch (Response)
438
+ {
439
+ case EAppReturnType::Yes: return TEXT("yes");
440
+ case EAppReturnType::No: return TEXT("no");
441
+ case EAppReturnType::Ok: return TEXT("ok");
442
+ case EAppReturnType::Cancel: return TEXT("cancel");
443
+ case EAppReturnType::Retry: return TEXT("retry");
444
+ case EAppReturnType::Continue: return TEXT("continue");
445
+ case EAppReturnType::YesAll: return TEXT("yesall");
446
+ case EAppReturnType::NoAll: return TEXT("noall");
447
+ default: return TEXT("unknown");
448
+ }
449
+ }
450
+
451
+ FString FDialogHandlers::MsgTypeToString(EAppMsgType::Type MsgType)
452
+ {
453
+ switch (MsgType)
454
+ {
455
+ case EAppMsgType::Ok: return TEXT("Ok");
456
+ case EAppMsgType::YesNo: return TEXT("YesNo");
457
+ case EAppMsgType::OkCancel: return TEXT("OkCancel");
458
+ case EAppMsgType::YesNoCancel: return TEXT("YesNoCancel");
459
+ case EAppMsgType::CancelRetryContinue: return TEXT("CancelRetryContinue");
460
+ case EAppMsgType::YesNoYesAllNoAll: return TEXT("YesNoYesAllNoAll");
461
+ case EAppMsgType::YesNoYesAllNoAllCancel: return TEXT("YesNoYesAllNoAllCancel");
462
+ case EAppMsgType::YesNoYesAll: return TEXT("YesNoYesAll");
463
+ default: return TEXT("Unknown");
464
+ }
465
+ }
@@ -0,0 +1,49 @@
1
+ #pragma once
2
+
3
+ #include "CoreMinimal.h"
4
+ #include "Dom/JsonValue.h"
5
+ #include "Dom/JsonObject.h"
6
+
7
+ class FDialogHandlers
8
+ {
9
+ public:
10
+ static void RegisterHandlers(class FMCPHandlerRegistry& Registry);
11
+
12
+ // Call once during module startup to hook FCoreDelegates::ModalMessageDialog
13
+ static void InstallDialogHook();
14
+
15
+ // Call during module shutdown to restore the original delegate
16
+ static void RemoveDialogHook();
17
+
18
+ private:
19
+ // Dialog policy: pattern -> response mapping
20
+ struct FDialogPolicy
21
+ {
22
+ FString Pattern;
23
+ EAppReturnType::Type Response;
24
+ };
25
+
26
+ // Handler implementations
27
+ static TSharedPtr<FJsonValue> SetDialogPolicy(const TSharedPtr<FJsonObject>& Params);
28
+ static TSharedPtr<FJsonValue> ClearDialogPolicy(const TSharedPtr<FJsonObject>& Params);
29
+ static TSharedPtr<FJsonValue> GetDialogPolicy(const TSharedPtr<FJsonObject>& Params);
30
+ static TSharedPtr<FJsonValue> ListDialogs(const TSharedPtr<FJsonObject>& Params);
31
+ static TSharedPtr<FJsonValue> RespondToDialog(const TSharedPtr<FJsonObject>& Params);
32
+
33
+ // The hooked dialog handler
34
+ static EAppReturnType::Type HandleModalDialog(EAppMsgType::Type MsgType, const FText& Text, const FText& Title);
35
+
36
+ // Convert string to EAppReturnType
37
+ static EAppReturnType::Type ParseResponseType(const FString& ResponseStr);
38
+ static FString ResponseTypeToString(EAppReturnType::Type Response);
39
+ static FString MsgTypeToString(EAppMsgType::Type MsgType);
40
+
41
+ // Active policies
42
+ static TArray<FDialogPolicy> Policies;
43
+
44
+ // Original delegate handle (so we can unbind)
45
+ static FDelegateHandle OriginalDelegateHandle;
46
+
47
+ // Whether we've installed our hook
48
+ static bool bHookInstalled;
49
+ };