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.
- package/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/bridge.d.ts +25 -0
- package/dist/bridge.js +124 -0
- package/dist/bridge.js.map +1 -0
- package/dist/deployer.d.ts +18 -0
- package/dist/deployer.js +175 -0
- package/dist/deployer.js.map +1 -0
- package/dist/editor-control.d.ts +15 -0
- package/dist/editor-control.js +181 -0
- package/dist/editor-control.js.map +1 -0
- package/dist/github-app.d.ts +4 -0
- package/dist/github-app.js +94 -0
- package/dist/github-app.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +125 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +290 -0
- package/dist/init.js.map +1 -0
- package/dist/instructions.d.ts +1 -0
- package/dist/instructions.js +150 -0
- package/dist/instructions.js.map +1 -0
- package/dist/project.d.ts +32 -0
- package/dist/project.js +175 -0
- package/dist/project.js.map +1 -0
- package/dist/tools/animation.d.ts +2 -0
- package/dist/tools/animation.js +62 -0
- package/dist/tools/animation.js.map +1 -0
- package/dist/tools/asset.d.ts +2 -0
- package/dist/tools/asset.js +128 -0
- package/dist/tools/asset.js.map +1 -0
- package/dist/tools/audio.d.ts +2 -0
- package/dist/tools/audio.js +24 -0
- package/dist/tools/audio.js.map +1 -0
- package/dist/tools/blueprint.d.ts +2 -0
- package/dist/tools/blueprint.js +64 -0
- package/dist/tools/blueprint.js.map +1 -0
- package/dist/tools/demo.d.ts +2 -0
- package/dist/tools/demo.js +10 -0
- package/dist/tools/demo.js.map +1 -0
- package/dist/tools/editor.d.ts +2 -0
- package/dist/tools/editor.js +133 -0
- package/dist/tools/editor.js.map +1 -0
- package/dist/tools/feedback.d.ts +2 -0
- package/dist/tools/feedback.js +44 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/foliage.d.ts +2 -0
- package/dist/tools/foliage.js +29 -0
- package/dist/tools/foliage.js.map +1 -0
- package/dist/tools/gameplay.d.ts +2 -0
- package/dist/tools/gameplay.js +101 -0
- package/dist/tools/gameplay.js.map +1 -0
- package/dist/tools/gas.d.ts +2 -0
- package/dist/tools/gas.js +43 -0
- package/dist/tools/gas.js.map +1 -0
- package/dist/tools/landscape.d.ts +2 -0
- package/dist/tools/landscape.js +32 -0
- package/dist/tools/landscape.js.map +1 -0
- package/dist/tools/level.d.ts +2 -0
- package/dist/tools/level.js +66 -0
- package/dist/tools/level.js.map +1 -0
- package/dist/tools/material.d.ts +2 -0
- package/dist/tools/material.js +59 -0
- package/dist/tools/material.js.map +1 -0
- package/dist/tools/networking.d.ts +2 -0
- package/dist/tools/networking.js +42 -0
- package/dist/tools/networking.js.map +1 -0
- package/dist/tools/niagara.d.ts +2 -0
- package/dist/tools/niagara.js +42 -0
- package/dist/tools/niagara.js.map +1 -0
- package/dist/tools/pcg.d.ts +2 -0
- package/dist/tools/pcg.js +39 -0
- package/dist/tools/pcg.js.map +1 -0
- package/dist/tools/project.d.ts +2 -0
- package/dist/tools/project.js +282 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/reflection.d.ts +2 -0
- package/dist/tools/reflection.js +26 -0
- package/dist/tools/reflection.js.map +1 -0
- package/dist/tools/widget.d.ts +2 -0
- package/dist/tools/widget.js +34 -0
- package/dist/tools/widget.js.map +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.js +38 -0
- package/dist/types.js.map +1 -0
- package/package.json +76 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +746 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.h +81 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +49 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +37 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.cpp +75 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.h +50 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +1418 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +43 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +1773 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +48 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers.cpp +289 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers.h +18 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +1982 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +44 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +1217 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.h +71 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DialogHandlers.cpp +465 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DialogHandlers.h +49 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +1676 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +53 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +876 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +22 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +2222 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +54 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +783 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +24 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +898 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +24 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +1270 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +34 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +2190 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +53 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.cpp +554 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.h +29 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +601 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.h +25 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +1024 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.h +25 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +370 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.h +19 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ReflectionHandlers.cpp +499 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ReflectionHandlers.h +27 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +426 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +19 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +303 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.h +18 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +985 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +27 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/JsonSerializer.cpp +181 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/JsonSerializer.h +42 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +39 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/UE_MCP_BridgeModule.h +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
- 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
|
+
};
|