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.
- package/README.md +729 -0
- package/dist/build/error-parser.js +51 -0
- package/dist/build/fix-suggester.js +84 -0
- package/dist/build/ubt-runner.js +146 -0
- package/dist/cli.js +13 -0
- package/dist/config.js +8 -0
- package/dist/docs/data/ue57-api.js +228 -0
- package/dist/docs/doc-index.js +110 -0
- package/dist/docs/types.js +4 -0
- package/dist/generators/class-generator.js +363 -0
- package/dist/generators/file-modifier.js +276 -0
- package/dist/generators/uht-validator.js +177 -0
- package/dist/index.js +89 -0
- package/dist/parsers/cpp-class-index.js +230 -0
- package/dist/parsers/cpp-parser.js +369 -0
- package/dist/parsers/ini-parser.js +216 -0
- package/dist/parsers/uproject-parser.js +130 -0
- package/dist/plugin-bridge/client.js +217 -0
- package/dist/plugin-bridge/protocol.js +6 -0
- package/dist/plugin-bridge/retry.js +23 -0
- package/dist/setup.js +209 -0
- package/dist/tools/ai-systems/index.js +247 -0
- package/dist/tools/ai-systems/types.js +4 -0
- package/dist/tools/animation/index.js +241 -0
- package/dist/tools/animation/types.js +4 -0
- package/dist/tools/audio/index.js +204 -0
- package/dist/tools/audio/types.js +4 -0
- package/dist/tools/blueprint/index.js +495 -0
- package/dist/tools/blueprint/types.js +4 -0
- package/dist/tools/build/index.js +163 -0
- package/dist/tools/chaos/index.js +230 -0
- package/dist/tools/chaos/types.js +4 -0
- package/dist/tools/collision-physics/index.js +211 -0
- package/dist/tools/config/index.js +288 -0
- package/dist/tools/cpp/index.js +305 -0
- package/dist/tools/docs/index.js +251 -0
- package/dist/tools/editor/index.js +242 -0
- package/dist/tools/gas/index.js +222 -0
- package/dist/tools/gas/types.js +5 -0
- package/dist/tools/import-export/index.js +218 -0
- package/dist/tools/input/index.js +146 -0
- package/dist/tools/known-issues/index.js +88 -0
- package/dist/tools/known-issues/middleware.js +55 -0
- package/dist/tools/known-issues/store.js +125 -0
- package/dist/tools/livelink/index.js +203 -0
- package/dist/tools/livelink/types.js +4 -0
- package/dist/tools/material/index.js +190 -0
- package/dist/tools/motion-design/index.js +251 -0
- package/dist/tools/motion-design/types.js +6 -0
- package/dist/tools/movie-render/index.js +220 -0
- package/dist/tools/networking/index.js +149 -0
- package/dist/tools/pcg/index.js +164 -0
- package/dist/tools/selection/index.js +180 -0
- package/dist/tools/sequencer/index.js +218 -0
- package/dist/tools/validation/index.js +183 -0
- package/dist/tools/validation/types.js +4 -0
- package/dist/tools/viewport/index.js +310 -0
- package/dist/tools/worldpartition/index.js +226 -0
- package/dist/tools/worldpartition/types.js +4 -0
- package/dist/utils/execFileNoThrow.js +40 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/path-guard.js +26 -0
- package/package.json +40 -0
- package/unreal-plugin/MCPBridge/MCPBridge.uplugin +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/MCPBridgeEditor.Build.cs +68 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.cpp +919 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAICommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.cpp +415 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPActorCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.cpp +653 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAnimationCommands.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.cpp +290 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAssetCommands.h +17 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.cpp +624 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPAudioCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.cpp +616 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintHandlers.h +25 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.cpp +744 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBlueprintWriteHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeEditor.cpp +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.cpp +149 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPBridgeSubsystem.h +38 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.cpp +771 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPChaosCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.cpp +749 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPCollisionPhysicsCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.cpp +172 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPEditorStateCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.cpp +715 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPGASCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.cpp +679 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPImportExportCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.cpp +381 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPInputHandlers.h +24 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.cpp +504 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPLiveLinkCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.cpp +511 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMaterialCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.cpp +1110 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMotionDesignCommands.h +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.cpp +590 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPMovieRenderCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.cpp +482 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPNetworkingCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.cpp +338 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPPieCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.cpp +677 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSelectionCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.cpp +721 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPSequencerCommands.h +16 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.cpp +368 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPValidationCommands.h +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.cpp +1208 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPViewportCommands.h +29 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.cpp +822 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Private/MCPWorldPartitionCommands.h +23 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeEditor/Public/MCPBridgeEditor.h +14 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/MCPBridgeRuntime.Build.cs +28 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPBridgeRuntime.cpp +22 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPCommandRouter.cpp +118 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Private/MCPTcpServer.cpp +196 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPBridgeRuntime.h +15 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPCommandRouter.h +55 -0
- package/unreal-plugin/MCPBridge/Source/MCPBridgeRuntime/Public/MCPTcpServer.h +59 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
// MCPBlueprintHandlers.cpp
|
|
2
|
+
// Implements blueprint.read, blueprint.graph, and blueprint.list command handlers.
|
|
3
|
+
// All handlers are registered on the FMCPCommandRouter and run on the game thread
|
|
4
|
+
// (enforced by FMCPCommandRouter::Dispatch via AsyncTask).
|
|
5
|
+
//
|
|
6
|
+
// Response format matches the ping handler pattern in MCPCommandRouter.cpp:
|
|
7
|
+
// { success: bool, correlationId: string, data: {...} } + "\n"
|
|
8
|
+
|
|
9
|
+
#include "MCPBlueprintHandlers.h"
|
|
10
|
+
|
|
11
|
+
#include "MCPCommandRouter.h"
|
|
12
|
+
|
|
13
|
+
// Blueprint APIs
|
|
14
|
+
#include "Engine/Blueprint.h"
|
|
15
|
+
#include "BlueprintEditorUtils.h"
|
|
16
|
+
#include "EdGraph/EdGraph.h"
|
|
17
|
+
#include "EdGraph/EdGraphNode.h"
|
|
18
|
+
#include "EdGraph/EdGraphPin.h"
|
|
19
|
+
#include "Engine/SimpleConstructionScript.h"
|
|
20
|
+
#include "Engine/SCS_Node.h"
|
|
21
|
+
|
|
22
|
+
// K2Node APIs — required for blueprint.cppUsage graph scanning
|
|
23
|
+
#include "K2Node_CallFunction.h"
|
|
24
|
+
#include "K2Node_VariableGet.h"
|
|
25
|
+
#include "K2Node_VariableSet.h"
|
|
26
|
+
|
|
27
|
+
// Asset Registry APIs
|
|
28
|
+
#include "AssetRegistry/AssetRegistryModule.h"
|
|
29
|
+
#include "AssetRegistry/IAssetRegistry.h"
|
|
30
|
+
#include "AssetRegistry/AssetData.h"
|
|
31
|
+
#include "Blueprint/BlueprintTags.h"
|
|
32
|
+
|
|
33
|
+
// JSON APIs
|
|
34
|
+
#include "Dom/JsonObject.h"
|
|
35
|
+
#include "Serialization/JsonSerializer.h"
|
|
36
|
+
#include "Serialization/JsonWriter.h"
|
|
37
|
+
|
|
38
|
+
void RegisterBlueprintHandlers(FMCPCommandRouter& Router)
|
|
39
|
+
{
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Shared response helpers captured by value in each handler lambda.
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
auto SendSuccess = [](FMCPResponseSender SendResponse,
|
|
45
|
+
const FString& CorrelationId,
|
|
46
|
+
TSharedPtr<FJsonObject> Data)
|
|
47
|
+
{
|
|
48
|
+
TSharedPtr<FJsonObject> Response = MakeShared<FJsonObject>();
|
|
49
|
+
Response->SetBoolField(TEXT("success"), true);
|
|
50
|
+
if (!CorrelationId.IsEmpty())
|
|
51
|
+
{
|
|
52
|
+
Response->SetStringField(TEXT("correlationId"), CorrelationId);
|
|
53
|
+
}
|
|
54
|
+
Response->SetObjectField(TEXT("data"), Data);
|
|
55
|
+
|
|
56
|
+
FString Output;
|
|
57
|
+
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
|
|
58
|
+
FJsonSerializer::Serialize(Response.ToSharedRef(), Writer);
|
|
59
|
+
Output += TEXT("\n");
|
|
60
|
+
SendResponse(Output);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
auto SendError = [](FMCPResponseSender SendResponse,
|
|
64
|
+
const FString& CorrelationId,
|
|
65
|
+
const FString& ErrorCode)
|
|
66
|
+
{
|
|
67
|
+
TSharedPtr<FJsonObject> Response = MakeShared<FJsonObject>();
|
|
68
|
+
Response->SetBoolField(TEXT("success"), false);
|
|
69
|
+
if (!CorrelationId.IsEmpty())
|
|
70
|
+
{
|
|
71
|
+
Response->SetStringField(TEXT("correlationId"), CorrelationId);
|
|
72
|
+
}
|
|
73
|
+
Response->SetStringField(TEXT("error"), ErrorCode);
|
|
74
|
+
|
|
75
|
+
FString Output;
|
|
76
|
+
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
|
|
77
|
+
FJsonSerializer::Serialize(Response.ToSharedRef(), Writer);
|
|
78
|
+
Output += TEXT("\n");
|
|
79
|
+
SendResponse(Output);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// blueprint.read
|
|
84
|
+
// Input payload: { "asset_path": "/Game/Blueprints/BP_MyActor" }
|
|
85
|
+
// Returns: { assetPath, parentClass, variables[], components[] }
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
Router.RegisterHandler(TEXT("blueprint.read"),
|
|
88
|
+
[SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
|
|
89
|
+
{
|
|
90
|
+
FString CorrelationId;
|
|
91
|
+
Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
|
|
92
|
+
|
|
93
|
+
// Extract asset_path from payload
|
|
94
|
+
const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
|
|
95
|
+
FString AssetPath;
|
|
96
|
+
if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj) ||
|
|
97
|
+
!(*PayloadObj)->TryGetStringField(TEXT("asset_path"), AssetPath) ||
|
|
98
|
+
AssetPath.IsEmpty())
|
|
99
|
+
{
|
|
100
|
+
SendError(SendResponse, CorrelationId, TEXT("missing_asset_path"));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Load the Blueprint asset via UE asset system (never parses .uasset binary directly)
|
|
105
|
+
UBlueprint* BP = LoadObject<UBlueprint>(nullptr, *AssetPath);
|
|
106
|
+
if (!BP)
|
|
107
|
+
{
|
|
108
|
+
UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] blueprint.read: Blueprint not found at path '%s'"), *AssetPath);
|
|
109
|
+
SendError(SendResponse, CorrelationId, TEXT("blueprint_not_found"));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
|
|
114
|
+
Data->SetStringField(TEXT("assetPath"), AssetPath);
|
|
115
|
+
Data->SetStringField(TEXT("parentClass"),
|
|
116
|
+
BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None"));
|
|
117
|
+
|
|
118
|
+
// Variables: iterate BP->NewVariables (member variables defined in the Blueprint)
|
|
119
|
+
TArray<TSharedPtr<FJsonValue>> VarArray;
|
|
120
|
+
for (const FBPVariableDescription& Var : BP->NewVariables)
|
|
121
|
+
{
|
|
122
|
+
TSharedPtr<FJsonObject> VarObj = MakeShared<FJsonObject>();
|
|
123
|
+
VarObj->SetStringField(TEXT("name"), Var.VarName.ToString());
|
|
124
|
+
VarObj->SetStringField(TEXT("type"), Var.VarType.PinCategory.ToString());
|
|
125
|
+
VarArray.Add(MakeShared<FJsonValueObject>(VarObj));
|
|
126
|
+
}
|
|
127
|
+
Data->SetArrayField(TEXT("variables"), VarArray);
|
|
128
|
+
|
|
129
|
+
// Components: iterate SimpleConstructionScript nodes (actor component tree)
|
|
130
|
+
TArray<TSharedPtr<FJsonValue>> CompArray;
|
|
131
|
+
if (BP->SimpleConstructionScript)
|
|
132
|
+
{
|
|
133
|
+
for (USCS_Node* Node : BP->SimpleConstructionScript->GetAllNodes())
|
|
134
|
+
{
|
|
135
|
+
if (!Node || !Node->ComponentTemplate)
|
|
136
|
+
{
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
TSharedPtr<FJsonObject> CompObj = MakeShared<FJsonObject>();
|
|
140
|
+
CompObj->SetStringField(TEXT("name"), Node->GetVariableName().ToString());
|
|
141
|
+
CompObj->SetStringField(TEXT("class"), Node->ComponentTemplate->GetClass()->GetName());
|
|
142
|
+
CompArray.Add(MakeShared<FJsonValueObject>(CompObj));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
Data->SetArrayField(TEXT("components"), CompArray);
|
|
146
|
+
|
|
147
|
+
SendSuccess(SendResponse, CorrelationId, Data);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// blueprint.graph
|
|
152
|
+
// Input payload: { "asset_path": "/Game/Blueprints/BP_MyActor", "graph_name": "EventGraph" }
|
|
153
|
+
// graph_name is optional -- defaults to "EventGraph"
|
|
154
|
+
// Returns: { assetPath, graphName, nodes[], connections[] }
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
Router.RegisterHandler(TEXT("blueprint.graph"),
|
|
157
|
+
[SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
|
|
158
|
+
{
|
|
159
|
+
FString CorrelationId;
|
|
160
|
+
Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
|
|
161
|
+
|
|
162
|
+
// Extract asset_path and optional graph_name from payload
|
|
163
|
+
const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
|
|
164
|
+
FString AssetPath;
|
|
165
|
+
if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj) ||
|
|
166
|
+
!(*PayloadObj)->TryGetStringField(TEXT("asset_path"), AssetPath) ||
|
|
167
|
+
AssetPath.IsEmpty())
|
|
168
|
+
{
|
|
169
|
+
SendError(SendResponse, CorrelationId, TEXT("missing_asset_path"));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
FString GraphName = TEXT("EventGraph");
|
|
174
|
+
(*PayloadObj)->TryGetStringField(TEXT("graph_name"), GraphName);
|
|
175
|
+
|
|
176
|
+
// Load the Blueprint asset
|
|
177
|
+
UBlueprint* BP = LoadObject<UBlueprint>(nullptr, *AssetPath);
|
|
178
|
+
if (!BP)
|
|
179
|
+
{
|
|
180
|
+
UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] blueprint.graph: Blueprint not found at path '%s'"), *AssetPath);
|
|
181
|
+
SendError(SendResponse, CorrelationId, TEXT("blueprint_not_found"));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Search UbergraphPages (event graphs) then FunctionGraphs for the named graph
|
|
186
|
+
UEdGraph* TargetGraph = nullptr;
|
|
187
|
+
for (UEdGraph* Graph : BP->UbergraphPages)
|
|
188
|
+
{
|
|
189
|
+
if (Graph && Graph->GetName() == GraphName)
|
|
190
|
+
{
|
|
191
|
+
TargetGraph = Graph;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (!TargetGraph)
|
|
196
|
+
{
|
|
197
|
+
for (UEdGraph* Graph : BP->FunctionGraphs)
|
|
198
|
+
{
|
|
199
|
+
if (Graph && Graph->GetName() == GraphName)
|
|
200
|
+
{
|
|
201
|
+
TargetGraph = Graph;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Default to first ubergraph if "EventGraph" was requested and not found by name
|
|
207
|
+
if (!TargetGraph && GraphName == TEXT("EventGraph") && BP->UbergraphPages.Num() > 0)
|
|
208
|
+
{
|
|
209
|
+
TargetGraph = BP->UbergraphPages[0];
|
|
210
|
+
}
|
|
211
|
+
if (!TargetGraph)
|
|
212
|
+
{
|
|
213
|
+
UE_LOG(LogTemp, Warning, TEXT("[MCPBridge] blueprint.graph: Graph '%s' not found in '%s'"),
|
|
214
|
+
*GraphName, *AssetPath);
|
|
215
|
+
SendError(SendResponse, CorrelationId, TEXT("graph_not_found"));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Serialize nodes with their pins
|
|
220
|
+
TArray<TSharedPtr<FJsonValue>> NodeArray;
|
|
221
|
+
for (UEdGraphNode* Node : TargetGraph->Nodes)
|
|
222
|
+
{
|
|
223
|
+
if (!Node)
|
|
224
|
+
{
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
TSharedPtr<FJsonObject> NodeObj = MakeShared<FJsonObject>();
|
|
229
|
+
NodeObj->SetStringField(TEXT("nodeGuid"), Node->NodeGuid.ToString());
|
|
230
|
+
NodeObj->SetStringField(TEXT("type"), Node->GetClass()->GetName());
|
|
231
|
+
NodeObj->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString());
|
|
232
|
+
NodeObj->SetNumberField(TEXT("posX"), Node->NodePosX);
|
|
233
|
+
NodeObj->SetNumberField(TEXT("posY"), Node->NodePosY);
|
|
234
|
+
|
|
235
|
+
// Serialize pins
|
|
236
|
+
TArray<TSharedPtr<FJsonValue>> PinArray;
|
|
237
|
+
for (UEdGraphPin* Pin : Node->Pins)
|
|
238
|
+
{
|
|
239
|
+
if (!Pin)
|
|
240
|
+
{
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
TSharedPtr<FJsonObject> PinObj = MakeShared<FJsonObject>();
|
|
244
|
+
PinObj->SetStringField(TEXT("pinName"), Pin->PinName.ToString());
|
|
245
|
+
PinObj->SetStringField(TEXT("direction"),
|
|
246
|
+
Pin->Direction == EGPD_Input ? TEXT("input") : TEXT("output"));
|
|
247
|
+
PinObj->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString());
|
|
248
|
+
PinObj->SetStringField(TEXT("defaultValue"), Pin->DefaultValue);
|
|
249
|
+
PinArray.Add(MakeShared<FJsonValueObject>(PinObj));
|
|
250
|
+
}
|
|
251
|
+
NodeObj->SetArrayField(TEXT("pins"), PinArray);
|
|
252
|
+
NodeArray.Add(MakeShared<FJsonValueObject>(NodeObj));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Serialize connections: walk output pins and their LinkedTo list
|
|
256
|
+
TArray<TSharedPtr<FJsonValue>> ConnArray;
|
|
257
|
+
for (UEdGraphNode* Node : TargetGraph->Nodes)
|
|
258
|
+
{
|
|
259
|
+
if (!Node)
|
|
260
|
+
{
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
for (UEdGraphPin* Pin : Node->Pins)
|
|
264
|
+
{
|
|
265
|
+
if (!Pin || Pin->Direction != EGPD_Output)
|
|
266
|
+
{
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
|
|
270
|
+
{
|
|
271
|
+
if (!LinkedPin || !LinkedPin->GetOwningNode())
|
|
272
|
+
{
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
TSharedPtr<FJsonObject> Conn = MakeShared<FJsonObject>();
|
|
276
|
+
Conn->SetStringField(TEXT("fromNodeGuid"), Node->NodeGuid.ToString());
|
|
277
|
+
Conn->SetStringField(TEXT("fromPinName"), Pin->PinName.ToString());
|
|
278
|
+
Conn->SetStringField(TEXT("toNodeGuid"), LinkedPin->GetOwningNode()->NodeGuid.ToString());
|
|
279
|
+
Conn->SetStringField(TEXT("toPinName"), LinkedPin->PinName.ToString());
|
|
280
|
+
ConnArray.Add(MakeShared<FJsonValueObject>(Conn));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
|
|
286
|
+
Data->SetStringField(TEXT("assetPath"), AssetPath);
|
|
287
|
+
Data->SetStringField(TEXT("graphName"), TargetGraph->GetName());
|
|
288
|
+
Data->SetArrayField(TEXT("nodes"), NodeArray);
|
|
289
|
+
Data->SetArrayField(TEXT("connections"), ConnArray);
|
|
290
|
+
|
|
291
|
+
SendSuccess(SendResponse, CorrelationId, Data);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
// blueprint.list
|
|
296
|
+
// Input payload: { "path_prefix": "/Game/Blueprints/" } (path_prefix optional)
|
|
297
|
+
// Returns: { blueprints[], count }
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
Router.RegisterHandler(TEXT("blueprint.list"),
|
|
300
|
+
[SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
|
|
301
|
+
{
|
|
302
|
+
FString CorrelationId;
|
|
303
|
+
Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
|
|
304
|
+
|
|
305
|
+
// Extract optional path_prefix from payload
|
|
306
|
+
FString PathPrefix;
|
|
307
|
+
const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
|
|
308
|
+
if (Command->TryGetObjectField(TEXT("payload"), PayloadObj))
|
|
309
|
+
{
|
|
310
|
+
(*PayloadObj)->TryGetStringField(TEXT("path_prefix"), PathPrefix);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Query the Asset Registry for all Blueprint assets
|
|
314
|
+
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
|
|
315
|
+
TEXT("AssetRegistry")).Get();
|
|
316
|
+
|
|
317
|
+
FARFilter Filter;
|
|
318
|
+
Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
|
|
319
|
+
Filter.bRecursivePaths = true;
|
|
320
|
+
if (!PathPrefix.IsEmpty())
|
|
321
|
+
{
|
|
322
|
+
Filter.PackagePaths.Add(FName(*PathPrefix));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
TArray<FAssetData> Assets;
|
|
326
|
+
AR.GetAssets(Filter, Assets);
|
|
327
|
+
|
|
328
|
+
TArray<TSharedPtr<FJsonValue>> AssetArray;
|
|
329
|
+
for (const FAssetData& Asset : Assets)
|
|
330
|
+
{
|
|
331
|
+
TSharedPtr<FJsonObject> AssetObj = MakeShared<FJsonObject>();
|
|
332
|
+
AssetObj->SetStringField(TEXT("assetPath"), Asset.GetSoftObjectPath().ToString());
|
|
333
|
+
AssetObj->SetStringField(TEXT("packagePath"), Asset.PackagePath.ToString());
|
|
334
|
+
|
|
335
|
+
// ParentClass is stored as a tag on Blueprint assets in the asset registry
|
|
336
|
+
FString ParentClass;
|
|
337
|
+
Asset.GetTagValue(FBlueprintTags::ParentClassPath, ParentClass);
|
|
338
|
+
AssetObj->SetStringField(TEXT("parentClass"), ParentClass);
|
|
339
|
+
|
|
340
|
+
AssetArray.Add(MakeShared<FJsonValueObject>(AssetObj));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
|
|
344
|
+
Data->SetArrayField(TEXT("blueprints"), AssetArray);
|
|
345
|
+
Data->SetNumberField(TEXT("count"), AssetArray.Num());
|
|
346
|
+
|
|
347
|
+
SendSuccess(SendResponse, CorrelationId, Data);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
void RegisterBlueprintBridgeHandlers(FMCPCommandRouter& Router)
|
|
352
|
+
{
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
// Shared response helpers — same pattern as RegisterBlueprintHandlers above.
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
|
|
357
|
+
auto SendSuccess = [](FMCPResponseSender SendResponse,
|
|
358
|
+
const FString& CorrelationId,
|
|
359
|
+
TSharedPtr<FJsonObject> Data)
|
|
360
|
+
{
|
|
361
|
+
TSharedPtr<FJsonObject> Response = MakeShared<FJsonObject>();
|
|
362
|
+
Response->SetBoolField(TEXT("success"), true);
|
|
363
|
+
if (!CorrelationId.IsEmpty())
|
|
364
|
+
{
|
|
365
|
+
Response->SetStringField(TEXT("correlationId"), CorrelationId);
|
|
366
|
+
}
|
|
367
|
+
Response->SetObjectField(TEXT("data"), Data);
|
|
368
|
+
|
|
369
|
+
FString Output;
|
|
370
|
+
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
|
|
371
|
+
FJsonSerializer::Serialize(Response.ToSharedRef(), Writer);
|
|
372
|
+
Output += TEXT("\n");
|
|
373
|
+
SendResponse(Output);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
auto SendError = [](FMCPResponseSender SendResponse,
|
|
377
|
+
const FString& CorrelationId,
|
|
378
|
+
const FString& ErrorCode)
|
|
379
|
+
{
|
|
380
|
+
TSharedPtr<FJsonObject> Response = MakeShared<FJsonObject>();
|
|
381
|
+
Response->SetBoolField(TEXT("success"), false);
|
|
382
|
+
if (!CorrelationId.IsEmpty())
|
|
383
|
+
{
|
|
384
|
+
Response->SetStringField(TEXT("correlationId"), CorrelationId);
|
|
385
|
+
}
|
|
386
|
+
Response->SetStringField(TEXT("error"), ErrorCode);
|
|
387
|
+
|
|
388
|
+
FString Output;
|
|
389
|
+
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
|
|
390
|
+
FJsonSerializer::Serialize(Response.ToSharedRef(), Writer);
|
|
391
|
+
Output += TEXT("\n");
|
|
392
|
+
SendResponse(Output);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// ---------------------------------------------------------------------------
|
|
396
|
+
// blueprint.subclasses
|
|
397
|
+
// Input payload: { "class_name": "AMyActor" }
|
|
398
|
+
// Returns: { className, subclasses[] } where each entry has { assetPath, packagePath, parentClassTag }
|
|
399
|
+
//
|
|
400
|
+
// Uses asset registry tag filtering only — does NOT call LoadObject to avoid loading
|
|
401
|
+
// potentially thousands of Blueprint assets for a class hierarchy query.
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
Router.RegisterHandler(TEXT("blueprint.subclasses"),
|
|
404
|
+
[SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
|
|
405
|
+
{
|
|
406
|
+
FString CorrelationId;
|
|
407
|
+
Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
|
|
408
|
+
|
|
409
|
+
// Extract class_name from payload (T-11-01: validate non-empty before query)
|
|
410
|
+
const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
|
|
411
|
+
FString ClassName;
|
|
412
|
+
if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj) ||
|
|
413
|
+
!(*PayloadObj)->TryGetStringField(TEXT("class_name"), ClassName) ||
|
|
414
|
+
ClassName.IsEmpty())
|
|
415
|
+
{
|
|
416
|
+
SendError(SendResponse, CorrelationId, TEXT("missing_class_name"));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Query the Asset Registry for all Blueprint assets (no path prefix — search entire project)
|
|
421
|
+
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
|
|
422
|
+
TEXT("AssetRegistry")).Get();
|
|
423
|
+
|
|
424
|
+
FARFilter Filter;
|
|
425
|
+
Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
|
|
426
|
+
Filter.bRecursivePaths = true;
|
|
427
|
+
|
|
428
|
+
TArray<FAssetData> Assets;
|
|
429
|
+
AR.GetAssets(Filter, Assets);
|
|
430
|
+
|
|
431
|
+
// Filter by ParentClassPath tag — match if the tag value contains the requested class_name.
|
|
432
|
+
// The tag value is typically "/Script/MyGame.AMyActor" so a Contains check is appropriate.
|
|
433
|
+
TArray<TSharedPtr<FJsonValue>> SubclassArray;
|
|
434
|
+
for (const FAssetData& Asset : Assets)
|
|
435
|
+
{
|
|
436
|
+
FString ParentClassTag;
|
|
437
|
+
Asset.GetTagValue(FBlueprintTags::ParentClassPath, ParentClassTag);
|
|
438
|
+
|
|
439
|
+
if (ParentClassTag.IsEmpty())
|
|
440
|
+
{
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Case-sensitive contains — class names are case-sensitive in C++/UE
|
|
445
|
+
if (ParentClassTag.Contains(ClassName))
|
|
446
|
+
{
|
|
447
|
+
TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
|
|
448
|
+
Entry->SetStringField(TEXT("assetPath"), Asset.GetSoftObjectPath().ToString());
|
|
449
|
+
Entry->SetStringField(TEXT("packagePath"), Asset.PackagePath.ToString());
|
|
450
|
+
Entry->SetStringField(TEXT("parentClassTag"), ParentClassTag);
|
|
451
|
+
SubclassArray.Add(MakeShared<FJsonValueObject>(Entry));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
|
|
456
|
+
Data->SetStringField(TEXT("className"), ClassName);
|
|
457
|
+
Data->SetArrayField(TEXT("subclasses"), SubclassArray);
|
|
458
|
+
|
|
459
|
+
SendSuccess(SendResponse, CorrelationId, Data);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// ---------------------------------------------------------------------------
|
|
463
|
+
// blueprint.cppUsage
|
|
464
|
+
// Input payload: { "class_name": "AMyActor", "member_name": "Attack" }
|
|
465
|
+
// Returns: { className, memberName, usages[], count }
|
|
466
|
+
// Each usage: { assetPath, graphName, nodeType, nodeTitle }
|
|
467
|
+
//
|
|
468
|
+
// Scans UK2Node_CallFunction, UK2Node_VariableGet, and UK2Node_VariableSet nodes
|
|
469
|
+
// across all Blueprint graphs (UbergraphPages + FunctionGraphs).
|
|
470
|
+
// LoadObject is called per asset — this is intentional and expected to be slow on
|
|
471
|
+
// large projects (T-11-02: accepted DoS risk — editor-only, not a production path).
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
Router.RegisterHandler(TEXT("blueprint.cppUsage"),
|
|
474
|
+
[SendSuccess, SendError](TSharedPtr<FJsonObject> Command, FMCPResponseSender SendResponse)
|
|
475
|
+
{
|
|
476
|
+
FString CorrelationId;
|
|
477
|
+
Command->TryGetStringField(TEXT("correlationId"), CorrelationId);
|
|
478
|
+
|
|
479
|
+
// Extract class_name and member_name from payload
|
|
480
|
+
const TSharedPtr<FJsonObject>* PayloadObj = nullptr;
|
|
481
|
+
FString ClassName;
|
|
482
|
+
FString MemberName;
|
|
483
|
+
if (!Command->TryGetObjectField(TEXT("payload"), PayloadObj))
|
|
484
|
+
{
|
|
485
|
+
SendError(SendResponse, CorrelationId, TEXT("missing_parameters"));
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
(*PayloadObj)->TryGetStringField(TEXT("class_name"), ClassName);
|
|
489
|
+
(*PayloadObj)->TryGetStringField(TEXT("member_name"), MemberName);
|
|
490
|
+
|
|
491
|
+
if (ClassName.IsEmpty() || MemberName.IsEmpty())
|
|
492
|
+
{
|
|
493
|
+
SendError(SendResponse, CorrelationId, TEXT("missing_parameters"));
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Query the Asset Registry for all Blueprint assets
|
|
498
|
+
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
|
|
499
|
+
TEXT("AssetRegistry")).Get();
|
|
500
|
+
|
|
501
|
+
FARFilter Filter;
|
|
502
|
+
Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
|
|
503
|
+
Filter.bRecursivePaths = true;
|
|
504
|
+
|
|
505
|
+
TArray<FAssetData> Assets;
|
|
506
|
+
AR.GetAssets(Filter, Assets);
|
|
507
|
+
|
|
508
|
+
// Collect usage records — deduplicated by assetPath+graphName+nodeType+nodeTitle
|
|
509
|
+
TSet<FString> SeenKeys;
|
|
510
|
+
TArray<TSharedPtr<FJsonValue>> UsageArray;
|
|
511
|
+
|
|
512
|
+
for (const FAssetData& AssetData : Assets)
|
|
513
|
+
{
|
|
514
|
+
const FString AssetPath = AssetData.GetSoftObjectPath().ToString();
|
|
515
|
+
|
|
516
|
+
// Load the Blueprint to access its graphs
|
|
517
|
+
UBlueprint* BP = LoadObject<UBlueprint>(nullptr, *AssetPath);
|
|
518
|
+
if (!BP)
|
|
519
|
+
{
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Build a combined list of graphs to scan: event graphs + function graphs
|
|
524
|
+
TArray<UEdGraph*> AllGraphs;
|
|
525
|
+
AllGraphs.Append(BP->UbergraphPages);
|
|
526
|
+
AllGraphs.Append(BP->FunctionGraphs);
|
|
527
|
+
|
|
528
|
+
for (UEdGraph* Graph : AllGraphs)
|
|
529
|
+
{
|
|
530
|
+
if (!Graph)
|
|
531
|
+
{
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const FString GraphName = Graph->GetName();
|
|
536
|
+
|
|
537
|
+
for (UEdGraphNode* Node : Graph->Nodes)
|
|
538
|
+
{
|
|
539
|
+
if (!Node)
|
|
540
|
+
{
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
bool bMatched = false;
|
|
545
|
+
|
|
546
|
+
// Check UK2Node_CallFunction: match member_name AND owning class contains class_name
|
|
547
|
+
if (UK2Node_CallFunction* CallNode = Cast<UK2Node_CallFunction>(Node))
|
|
548
|
+
{
|
|
549
|
+
if (CallNode->FunctionReference.GetMemberName().ToString() == MemberName)
|
|
550
|
+
{
|
|
551
|
+
// Check if the function's parent class name contains class_name
|
|
552
|
+
UClass* MemberParent = CallNode->FunctionReference.GetMemberParentClass();
|
|
553
|
+
const bool bClassMatch = MemberParent
|
|
554
|
+
? MemberParent->GetName().Contains(ClassName)
|
|
555
|
+
: false;
|
|
556
|
+
|
|
557
|
+
// Also accept if the blueprint's parent class tag contains class_name
|
|
558
|
+
FString ParentClassTag;
|
|
559
|
+
AssetData.GetTagValue(FBlueprintTags::ParentClassPath, ParentClassTag);
|
|
560
|
+
const bool bTagMatch = ParentClassTag.Contains(ClassName);
|
|
561
|
+
|
|
562
|
+
if (bClassMatch || bTagMatch)
|
|
563
|
+
{
|
|
564
|
+
bMatched = true;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
// Check UK2Node_VariableGet: match member_name only (variable names are unique within scope)
|
|
569
|
+
else if (UK2Node_VariableGet* GetNode = Cast<UK2Node_VariableGet>(Node))
|
|
570
|
+
{
|
|
571
|
+
if (GetNode->VariableReference.GetMemberName().ToString() == MemberName)
|
|
572
|
+
{
|
|
573
|
+
bMatched = true;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
// Check UK2Node_VariableSet: match member_name only
|
|
577
|
+
else if (UK2Node_VariableSet* SetNode = Cast<UK2Node_VariableSet>(Node))
|
|
578
|
+
{
|
|
579
|
+
if (SetNode->VariableReference.GetMemberName().ToString() == MemberName)
|
|
580
|
+
{
|
|
581
|
+
bMatched = true;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (bMatched)
|
|
586
|
+
{
|
|
587
|
+
const FString NodeType = Node->GetClass()->GetName();
|
|
588
|
+
const FString NodeTitle = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString();
|
|
589
|
+
|
|
590
|
+
// Deduplicate by composite key
|
|
591
|
+
const FString DedupeKey = AssetPath + TEXT("|") + GraphName + TEXT("|") + NodeType + TEXT("|") + NodeTitle;
|
|
592
|
+
if (!SeenKeys.Contains(DedupeKey))
|
|
593
|
+
{
|
|
594
|
+
SeenKeys.Add(DedupeKey);
|
|
595
|
+
|
|
596
|
+
TSharedPtr<FJsonObject> Usage = MakeShared<FJsonObject>();
|
|
597
|
+
Usage->SetStringField(TEXT("assetPath"), AssetPath);
|
|
598
|
+
Usage->SetStringField(TEXT("graphName"), GraphName);
|
|
599
|
+
Usage->SetStringField(TEXT("nodeType"), NodeType);
|
|
600
|
+
Usage->SetStringField(TEXT("nodeTitle"), NodeTitle);
|
|
601
|
+
UsageArray.Add(MakeShared<FJsonValueObject>(Usage));
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
TSharedPtr<FJsonObject> Data = MakeShared<FJsonObject>();
|
|
609
|
+
Data->SetStringField(TEXT("className"), ClassName);
|
|
610
|
+
Data->SetStringField(TEXT("memberName"), MemberName);
|
|
611
|
+
Data->SetArrayField(TEXT("usages"), UsageArray);
|
|
612
|
+
Data->SetNumberField(TEXT("count"), UsageArray.Num());
|
|
613
|
+
|
|
614
|
+
SendSuccess(SendResponse, CorrelationId, Data);
|
|
615
|
+
});
|
|
616
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// MCPBlueprintHandlers.h
|
|
2
|
+
// Declares the Blueprint command handler registration functions.
|
|
3
|
+
// Call RegisterBlueprintHandlers() and RegisterBlueprintBridgeHandlers()
|
|
4
|
+
// from UMCPBridgeSubsystem::Initialize() after the router is created.
|
|
5
|
+
|
|
6
|
+
#pragma once
|
|
7
|
+
|
|
8
|
+
#include "CoreMinimal.h"
|
|
9
|
+
|
|
10
|
+
class FMCPCommandRouter;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Register blueprint.read, blueprint.graph, and blueprint.list handlers
|
|
14
|
+
* on the given router. All handlers run on the game thread (enforced by
|
|
15
|
+
* FMCPCommandRouter::Dispatch via AsyncTask).
|
|
16
|
+
*/
|
|
17
|
+
void RegisterBlueprintHandlers(FMCPCommandRouter& Router);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register Blueprint-C++ bridge handlers on the given router:
|
|
21
|
+
* blueprint.subclasses -- find all Blueprint assets that inherit from a given C++ class
|
|
22
|
+
* blueprint.cppUsage -- scan Blueprint graphs for nodes referencing a specific C++ member
|
|
23
|
+
* All handlers run on the game thread (enforced by FMCPCommandRouter::Dispatch via AsyncTask).
|
|
24
|
+
*/
|
|
25
|
+
void RegisterBlueprintBridgeHandlers(FMCPCommandRouter& Router);
|