ue-mcp 1.2.1 → 1.2.2
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/dist/context-cli.js +16 -16
- package/dist/engine-observer.js +68 -68
- package/dist/flow/guard.d.ts +26 -37
- package/dist/flow/guard.js +40 -25
- package/dist/flow/guard.js.map +1 -1
- package/dist/flow/guarded-bridge.d.ts +0 -10
- package/dist/flow/guarded-bridge.js +18 -23
- package/dist/flow/guarded-bridge.js.map +1 -1
- package/dist/flow/schema.d.ts +276 -16
- package/dist/flow/task-guards.d.ts +1 -1
- package/dist/flow/task-guards.js +44 -68
- package/dist/flow/task-guards.js.map +1 -1
- package/dist/instructions.js +130 -130
- package/dist/plugin-cli.js +299 -299
- package/dist/tool-counts.json +2 -2
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/EngineStatusHooks.cpp +76 -76
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/EngineStatusHooks.h +27 -27
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.cpp +128 -128
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerRegistry.h +63 -63
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +230 -230
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +323 -323
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +20 -20
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ChooserHandlers_Nested.cpp +537 -537
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.h +77 -77
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +353 -353
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +18 -18
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_InputRuntime.cpp +246 -246
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +597 -597
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +51 -51
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +878 -878
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +449 -449
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Function.cpp +275 -275
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Graph.cpp +551 -551
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.cpp +531 -531
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NetworkingHandlers.h +28 -28
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +677 -677
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +822 -822
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +22 -22
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +218 -218
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.h +17 -17
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/VolumeHelpers_Internal.cpp +46 -46
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/VolumeHelpers_Internal.h +23 -23
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/MCPExternalRegistry.cpp +83 -83
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/UE_MCP_BridgeModule.h +14 -14
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/UE_MCP_BridgeStatusModule.cpp +37 -37
- package/dist/manifest-signature.d.ts +0 -1
- package/dist/manifest-signature.js +0 -53
- package/dist/manifest-signature.js.map +0 -1
- package/dist/pie/schema.d.ts +0 -791
- package/dist/pie/schema.js +0 -151
- package/dist/pie/schema.js.map +0 -1
- package/dist/watch-cli.js.map +0 -1
- /package/plugin/ue_mcp_bridge/{ue_mcp_bridge.uplugin → UE_MCP_Bridge.uplugin} +0 -0
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp
CHANGED
|
@@ -1,230 +1,230 @@
|
|
|
1
|
-
// Control Rig RigVM graph inspection (#774).
|
|
2
|
-
//
|
|
3
|
-
// list_control_rig_variables reported each graph as just a name, class and node
|
|
4
|
-
// count, which is not enough to verify solver wiring - you cannot tell what a
|
|
5
|
-
// rig actually computes from a node count. Reading the RigVM models, their
|
|
6
|
-
// nodes, pins and links required dropping to Python.
|
|
7
|
-
//
|
|
8
|
-
// Translation-unit partition of FAnimationHandlers; registration lives in
|
|
9
|
-
// AnimationHandlers.cpp.
|
|
10
|
-
|
|
11
|
-
#include "AnimationHandlers.h"
|
|
12
|
-
|
|
13
|
-
#include "HandlerUtils.h"
|
|
14
|
-
|
|
15
|
-
#include "EditorAssetLibrary.h"
|
|
16
|
-
#include "Engine/Blueprint.h"
|
|
17
|
-
#include "UObject/UObjectGlobals.h"
|
|
18
|
-
#include "UObject/UObjectHash.h"
|
|
19
|
-
|
|
20
|
-
#include "RigVMModel/RigVMGraph.h"
|
|
21
|
-
#include "RigVMModel/RigVMLink.h"
|
|
22
|
-
#include "RigVMModel/RigVMNode.h"
|
|
23
|
-
#include "RigVMModel/RigVMPin.h"
|
|
24
|
-
|
|
25
|
-
namespace
|
|
26
|
-
{
|
|
27
|
-
FString PinDirectionName(ERigVMPinDirection Direction)
|
|
28
|
-
{
|
|
29
|
-
switch (Direction)
|
|
30
|
-
{
|
|
31
|
-
case ERigVMPinDirection::Input: return TEXT("Input");
|
|
32
|
-
case ERigVMPinDirection::Output: return TEXT("Output");
|
|
33
|
-
case ERigVMPinDirection::IO: return TEXT("IO");
|
|
34
|
-
case ERigVMPinDirection::Visible: return TEXT("Visible");
|
|
35
|
-
case ERigVMPinDirection::Hidden: return TEXT("Hidden");
|
|
36
|
-
default: return TEXT("Invalid");
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
TSharedPtr<FJsonObject> PinToJson(URigVMPin* Pin, bool bIncludeDefaults, int32 Depth)
|
|
41
|
-
{
|
|
42
|
-
TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
|
|
43
|
-
Obj->SetStringField(TEXT("name"), Pin->GetName());
|
|
44
|
-
Obj->SetStringField(TEXT("pinPath"), Pin->GetPinPath());
|
|
45
|
-
Obj->SetStringField(TEXT("cppType"), Pin->GetCPPType());
|
|
46
|
-
Obj->SetStringField(TEXT("direction"), PinDirectionName(Pin->GetDirection()));
|
|
47
|
-
Obj->SetBoolField(TEXT("isExecute"), Pin->IsExecuteContext());
|
|
48
|
-
if (bIncludeDefaults)
|
|
49
|
-
{
|
|
50
|
-
const FString Default = Pin->GetDefaultValue();
|
|
51
|
-
if (!Default.IsEmpty()) Obj->SetStringField(TEXT("defaultValue"), Default);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Struct pins expand into sub-pins; that nesting IS the wiring for
|
|
55
|
-
// things like a transform's individual channels, so keep it, bounded.
|
|
56
|
-
const TArray<URigVMPin*>& SubPins = Pin->GetSubPins();
|
|
57
|
-
if (SubPins.Num() > 0 && Depth >= 3)
|
|
58
|
-
{
|
|
59
|
-
// Say so rather than returning a partial pin tree that reads complete.
|
|
60
|
-
Obj->SetBoolField(TEXT("subPinsTruncated"), true);
|
|
61
|
-
}
|
|
62
|
-
if (SubPins.Num() > 0 && Depth < 3)
|
|
63
|
-
{
|
|
64
|
-
TArray<TSharedPtr<FJsonValue>> SubArray;
|
|
65
|
-
for (URigVMPin* Sub : SubPins)
|
|
66
|
-
{
|
|
67
|
-
if (Sub) SubArray.Add(MakeShared<FJsonValueObject>(PinToJson(Sub, bIncludeDefaults, Depth + 1)));
|
|
68
|
-
}
|
|
69
|
-
Obj->SetArrayField(TEXT("subPins"), SubArray);
|
|
70
|
-
}
|
|
71
|
-
return Obj;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
TSharedPtr<FJsonValue> FAnimationHandlers::ReadControlRigGraph(const TSharedPtr<FJsonObject>& Params)
|
|
76
|
-
{
|
|
77
|
-
FString AssetPath;
|
|
78
|
-
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
79
|
-
|
|
80
|
-
UObject* LoadedAsset = UEditorAssetLibrary::LoadAsset(AssetPath);
|
|
81
|
-
UBlueprint* Blueprint = Cast<UBlueprint>(LoadedAsset);
|
|
82
|
-
if (!Blueprint)
|
|
83
|
-
{
|
|
84
|
-
return MCPError(FString::Printf(TEXT("Failed to load Blueprint at '%s'"), *AssetPath));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const FString GraphFilter = OptionalString(Params, TEXT("graphName"));
|
|
88
|
-
const bool bIncludePins = OptionalBool(Params, TEXT("includePins"), true);
|
|
89
|
-
const bool bIncludeDefaults = OptionalBool(Params, TEXT("includeDefaults"), true);
|
|
90
|
-
const bool bIncludeLinks = OptionalBool(Params, TEXT("includeLinks"), true);
|
|
91
|
-
const int32 NodeLimit = FMath::Max(1, OptionalInt(Params, TEXT("limit"), 200));
|
|
92
|
-
|
|
93
|
-
// ControlRigBlueprint was removed in 5.7 and the client-host interface
|
|
94
|
-
// moved, so reach the models by walking the blueprint's own subobjects for
|
|
95
|
-
// URigVMGraph. That is version-tolerant and needs no editor-only host API.
|
|
96
|
-
TArray<UObject*> Subobjects;
|
|
97
|
-
#if UE_MCP_HAS_5_8_API
|
|
98
|
-
GetObjectsWithOuter(Blueprint, Subobjects, EGetObjectsFlags::IncludeNestedObjects);
|
|
99
|
-
#else
|
|
100
|
-
GetObjectsWithOuter(Blueprint, Subobjects, /*bIncludeNestedObjects=*/true);
|
|
101
|
-
#endif
|
|
102
|
-
|
|
103
|
-
TArray<URigVMGraph*> Models;
|
|
104
|
-
for (UObject* Object : Subobjects)
|
|
105
|
-
{
|
|
106
|
-
if (URigVMGraph* Graph = Cast<URigVMGraph>(Object))
|
|
107
|
-
{
|
|
108
|
-
// Nested graphs (function library entries, collapsed nodes) are
|
|
109
|
-
// reachable from their own outer; keep them all but de-duplicate.
|
|
110
|
-
Models.AddUnique(Graph);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (Models.Num() == 0)
|
|
115
|
-
{
|
|
116
|
-
return MCPError(FString::Printf(
|
|
117
|
-
TEXT("'%s' has no RigVM models. Is this a Control Rig / RigVM asset? (loaded class: %s)"),
|
|
118
|
-
*AssetPath, *Blueprint->GetClass()->GetName()));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
TArray<TSharedPtr<FJsonValue>> GraphsArray;
|
|
122
|
-
int32 TotalNodes = 0;
|
|
123
|
-
int32 TotalLinks = 0;
|
|
124
|
-
bool bTruncatedAny = false;
|
|
125
|
-
|
|
126
|
-
for (URigVMGraph* Graph : Models)
|
|
127
|
-
{
|
|
128
|
-
const FString GraphName = Graph->GetName();
|
|
129
|
-
if (!GraphFilter.IsEmpty() && !GraphName.Contains(GraphFilter, ESearchCase::IgnoreCase))
|
|
130
|
-
{
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
TSharedPtr<FJsonObject> GraphObj = MakeShared<FJsonObject>();
|
|
135
|
-
GraphObj->SetStringField(TEXT("name"), GraphName);
|
|
136
|
-
GraphObj->SetStringField(TEXT("path"), Graph->GetPathName());
|
|
137
|
-
|
|
138
|
-
const TArray<URigVMNode*>& Nodes = Graph->GetNodes();
|
|
139
|
-
GraphObj->SetNumberField(TEXT("nodeCount"), Nodes.Num());
|
|
140
|
-
TotalNodes += Nodes.Num();
|
|
141
|
-
|
|
142
|
-
TArray<TSharedPtr<FJsonValue>> NodesArray;
|
|
143
|
-
bool bGraphTruncated = false;
|
|
144
|
-
for (URigVMNode* Node : Nodes)
|
|
145
|
-
{
|
|
146
|
-
if (!Node) continue;
|
|
147
|
-
if (NodesArray.Num() >= NodeLimit) { bTruncatedAny = true; bGraphTruncated = true; break; }
|
|
148
|
-
|
|
149
|
-
TSharedPtr<FJsonObject> NodeObj = MakeShared<FJsonObject>();
|
|
150
|
-
NodeObj->SetStringField(TEXT("name"), Node->GetName());
|
|
151
|
-
NodeObj->SetStringField(TEXT("nodePath"), Node->GetNodePath());
|
|
152
|
-
NodeObj->SetStringField(TEXT("class"), Node->GetClass()->GetName());
|
|
153
|
-
|
|
154
|
-
if (bIncludePins)
|
|
155
|
-
{
|
|
156
|
-
TArray<TSharedPtr<FJsonValue>> PinsArray;
|
|
157
|
-
for (URigVMPin* Pin : Node->GetPins())
|
|
158
|
-
{
|
|
159
|
-
if (Pin) PinsArray.Add(MakeShared<FJsonValueObject>(PinToJson(Pin, bIncludeDefaults, 0)));
|
|
160
|
-
}
|
|
161
|
-
NodeObj->SetArrayField(TEXT("pins"), PinsArray);
|
|
162
|
-
}
|
|
163
|
-
NodesArray.Add(MakeShared<FJsonValueObject>(NodeObj));
|
|
164
|
-
}
|
|
165
|
-
GraphObj->SetArrayField(TEXT("nodes"), NodesArray);
|
|
166
|
-
// Set at the break site: deriving it from counts false-positives on a
|
|
167
|
-
// graph that merely contains a null node.
|
|
168
|
-
GraphObj->SetBoolField(TEXT("nodesTruncated"), bGraphTruncated);
|
|
169
|
-
|
|
170
|
-
if (bIncludeLinks)
|
|
171
|
-
{
|
|
172
|
-
const TArray<URigVMLink*>& Links = Graph->GetLinks();
|
|
173
|
-
TotalLinks += Links.Num();
|
|
174
|
-
TArray<TSharedPtr<FJsonValue>> LinksArray;
|
|
175
|
-
for (URigVMLink* Link : Links)
|
|
176
|
-
{
|
|
177
|
-
if (!Link) continue;
|
|
178
|
-
URigVMPin* Source = Link->GetSourcePin();
|
|
179
|
-
URigVMPin* Target = Link->GetTargetPin();
|
|
180
|
-
if (!Source || !Target) continue;
|
|
181
|
-
TSharedPtr<FJsonObject> LinkObj = MakeShared<FJsonObject>();
|
|
182
|
-
LinkObj->SetStringField(TEXT("source"), Source->GetPinPath(/*bUseNodePath=*/true));
|
|
183
|
-
LinkObj->SetStringField(TEXT("target"), Target->GetPinPath(/*bUseNodePath=*/true));
|
|
184
|
-
LinksArray.Add(MakeShared<FJsonValueObject>(LinkObj));
|
|
185
|
-
}
|
|
186
|
-
GraphObj->SetArrayField(TEXT("links"), LinksArray);
|
|
187
|
-
GraphObj->SetNumberField(TEXT("linkCount"), LinksArray.Num());
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
GraphsArray.Add(MakeShared<FJsonValueObject>(GraphObj));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Member variables with their full metadata, not just a name and category.
|
|
194
|
-
TArray<TSharedPtr<FJsonValue>> VariablesArray;
|
|
195
|
-
for (const FBPVariableDescription& Var : Blueprint->NewVariables)
|
|
196
|
-
{
|
|
197
|
-
TSharedPtr<FJsonObject> VarObj = MakeShared<FJsonObject>();
|
|
198
|
-
VarObj->SetStringField(TEXT("name"), Var.VarName.ToString());
|
|
199
|
-
VarObj->SetStringField(TEXT("type"), Var.VarType.PinCategory.ToString());
|
|
200
|
-
VarObj->SetStringField(TEXT("subType"), Var.VarType.PinSubCategory.ToString());
|
|
201
|
-
if (Var.VarType.PinSubCategoryObject.IsValid())
|
|
202
|
-
{
|
|
203
|
-
VarObj->SetStringField(TEXT("subTypeObject"), Var.VarType.PinSubCategoryObject->GetPathName());
|
|
204
|
-
}
|
|
205
|
-
VarObj->SetBoolField(TEXT("isArray"), Var.VarType.IsArray());
|
|
206
|
-
VarObj->SetStringField(TEXT("defaultValue"), Var.DefaultValue);
|
|
207
|
-
VarObj->SetBoolField(TEXT("isPublic"), (Var.PropertyFlags & CPF_BlueprintVisible) != 0);
|
|
208
|
-
VarObj->SetBoolField(TEXT("readOnly"), (Var.PropertyFlags & CPF_BlueprintReadOnly) != 0);
|
|
209
|
-
VariablesArray.Add(MakeShared<FJsonValueObject>(VarObj));
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
auto Result = MCPSuccess();
|
|
213
|
-
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
|
214
|
-
Result->SetStringField(TEXT("name"), Blueprint->GetName());
|
|
215
|
-
if (Blueprint->ParentClass)
|
|
216
|
-
{
|
|
217
|
-
Result->SetStringField(TEXT("parentClass"), Blueprint->ParentClass->GetName());
|
|
218
|
-
}
|
|
219
|
-
Result->SetArrayField(TEXT("models"), GraphsArray);
|
|
220
|
-
Result->SetNumberField(TEXT("modelCount"), GraphsArray.Num());
|
|
221
|
-
Result->SetNumberField(TEXT("totalNodes"), TotalNodes);
|
|
222
|
-
Result->SetNumberField(TEXT("totalLinks"), TotalLinks);
|
|
223
|
-
Result->SetArrayField(TEXT("variables"), VariablesArray);
|
|
224
|
-
Result->SetNumberField(TEXT("variableCount"), VariablesArray.Num());
|
|
225
|
-
if (bTruncatedAny)
|
|
226
|
-
{
|
|
227
|
-
Result->SetStringField(TEXT("note"), TEXT("Some graphs hit the node limit; raise 'limit' or narrow with 'graphName'."));
|
|
228
|
-
}
|
|
229
|
-
return MCPResult(Result);
|
|
230
|
-
}
|
|
1
|
+
// Control Rig RigVM graph inspection (#774).
|
|
2
|
+
//
|
|
3
|
+
// list_control_rig_variables reported each graph as just a name, class and node
|
|
4
|
+
// count, which is not enough to verify solver wiring - you cannot tell what a
|
|
5
|
+
// rig actually computes from a node count. Reading the RigVM models, their
|
|
6
|
+
// nodes, pins and links required dropping to Python.
|
|
7
|
+
//
|
|
8
|
+
// Translation-unit partition of FAnimationHandlers; registration lives in
|
|
9
|
+
// AnimationHandlers.cpp.
|
|
10
|
+
|
|
11
|
+
#include "AnimationHandlers.h"
|
|
12
|
+
|
|
13
|
+
#include "HandlerUtils.h"
|
|
14
|
+
|
|
15
|
+
#include "EditorAssetLibrary.h"
|
|
16
|
+
#include "Engine/Blueprint.h"
|
|
17
|
+
#include "UObject/UObjectGlobals.h"
|
|
18
|
+
#include "UObject/UObjectHash.h"
|
|
19
|
+
|
|
20
|
+
#include "RigVMModel/RigVMGraph.h"
|
|
21
|
+
#include "RigVMModel/RigVMLink.h"
|
|
22
|
+
#include "RigVMModel/RigVMNode.h"
|
|
23
|
+
#include "RigVMModel/RigVMPin.h"
|
|
24
|
+
|
|
25
|
+
namespace
|
|
26
|
+
{
|
|
27
|
+
FString PinDirectionName(ERigVMPinDirection Direction)
|
|
28
|
+
{
|
|
29
|
+
switch (Direction)
|
|
30
|
+
{
|
|
31
|
+
case ERigVMPinDirection::Input: return TEXT("Input");
|
|
32
|
+
case ERigVMPinDirection::Output: return TEXT("Output");
|
|
33
|
+
case ERigVMPinDirection::IO: return TEXT("IO");
|
|
34
|
+
case ERigVMPinDirection::Visible: return TEXT("Visible");
|
|
35
|
+
case ERigVMPinDirection::Hidden: return TEXT("Hidden");
|
|
36
|
+
default: return TEXT("Invalid");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
TSharedPtr<FJsonObject> PinToJson(URigVMPin* Pin, bool bIncludeDefaults, int32 Depth)
|
|
41
|
+
{
|
|
42
|
+
TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
|
|
43
|
+
Obj->SetStringField(TEXT("name"), Pin->GetName());
|
|
44
|
+
Obj->SetStringField(TEXT("pinPath"), Pin->GetPinPath());
|
|
45
|
+
Obj->SetStringField(TEXT("cppType"), Pin->GetCPPType());
|
|
46
|
+
Obj->SetStringField(TEXT("direction"), PinDirectionName(Pin->GetDirection()));
|
|
47
|
+
Obj->SetBoolField(TEXT("isExecute"), Pin->IsExecuteContext());
|
|
48
|
+
if (bIncludeDefaults)
|
|
49
|
+
{
|
|
50
|
+
const FString Default = Pin->GetDefaultValue();
|
|
51
|
+
if (!Default.IsEmpty()) Obj->SetStringField(TEXT("defaultValue"), Default);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Struct pins expand into sub-pins; that nesting IS the wiring for
|
|
55
|
+
// things like a transform's individual channels, so keep it, bounded.
|
|
56
|
+
const TArray<URigVMPin*>& SubPins = Pin->GetSubPins();
|
|
57
|
+
if (SubPins.Num() > 0 && Depth >= 3)
|
|
58
|
+
{
|
|
59
|
+
// Say so rather than returning a partial pin tree that reads complete.
|
|
60
|
+
Obj->SetBoolField(TEXT("subPinsTruncated"), true);
|
|
61
|
+
}
|
|
62
|
+
if (SubPins.Num() > 0 && Depth < 3)
|
|
63
|
+
{
|
|
64
|
+
TArray<TSharedPtr<FJsonValue>> SubArray;
|
|
65
|
+
for (URigVMPin* Sub : SubPins)
|
|
66
|
+
{
|
|
67
|
+
if (Sub) SubArray.Add(MakeShared<FJsonValueObject>(PinToJson(Sub, bIncludeDefaults, Depth + 1)));
|
|
68
|
+
}
|
|
69
|
+
Obj->SetArrayField(TEXT("subPins"), SubArray);
|
|
70
|
+
}
|
|
71
|
+
return Obj;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
TSharedPtr<FJsonValue> FAnimationHandlers::ReadControlRigGraph(const TSharedPtr<FJsonObject>& Params)
|
|
76
|
+
{
|
|
77
|
+
FString AssetPath;
|
|
78
|
+
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
79
|
+
|
|
80
|
+
UObject* LoadedAsset = UEditorAssetLibrary::LoadAsset(AssetPath);
|
|
81
|
+
UBlueprint* Blueprint = Cast<UBlueprint>(LoadedAsset);
|
|
82
|
+
if (!Blueprint)
|
|
83
|
+
{
|
|
84
|
+
return MCPError(FString::Printf(TEXT("Failed to load Blueprint at '%s'"), *AssetPath));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const FString GraphFilter = OptionalString(Params, TEXT("graphName"));
|
|
88
|
+
const bool bIncludePins = OptionalBool(Params, TEXT("includePins"), true);
|
|
89
|
+
const bool bIncludeDefaults = OptionalBool(Params, TEXT("includeDefaults"), true);
|
|
90
|
+
const bool bIncludeLinks = OptionalBool(Params, TEXT("includeLinks"), true);
|
|
91
|
+
const int32 NodeLimit = FMath::Max(1, OptionalInt(Params, TEXT("limit"), 200));
|
|
92
|
+
|
|
93
|
+
// ControlRigBlueprint was removed in 5.7 and the client-host interface
|
|
94
|
+
// moved, so reach the models by walking the blueprint's own subobjects for
|
|
95
|
+
// URigVMGraph. That is version-tolerant and needs no editor-only host API.
|
|
96
|
+
TArray<UObject*> Subobjects;
|
|
97
|
+
#if UE_MCP_HAS_5_8_API
|
|
98
|
+
GetObjectsWithOuter(Blueprint, Subobjects, EGetObjectsFlags::IncludeNestedObjects);
|
|
99
|
+
#else
|
|
100
|
+
GetObjectsWithOuter(Blueprint, Subobjects, /*bIncludeNestedObjects=*/true);
|
|
101
|
+
#endif
|
|
102
|
+
|
|
103
|
+
TArray<URigVMGraph*> Models;
|
|
104
|
+
for (UObject* Object : Subobjects)
|
|
105
|
+
{
|
|
106
|
+
if (URigVMGraph* Graph = Cast<URigVMGraph>(Object))
|
|
107
|
+
{
|
|
108
|
+
// Nested graphs (function library entries, collapsed nodes) are
|
|
109
|
+
// reachable from their own outer; keep them all but de-duplicate.
|
|
110
|
+
Models.AddUnique(Graph);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (Models.Num() == 0)
|
|
115
|
+
{
|
|
116
|
+
return MCPError(FString::Printf(
|
|
117
|
+
TEXT("'%s' has no RigVM models. Is this a Control Rig / RigVM asset? (loaded class: %s)"),
|
|
118
|
+
*AssetPath, *Blueprint->GetClass()->GetName()));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
TArray<TSharedPtr<FJsonValue>> GraphsArray;
|
|
122
|
+
int32 TotalNodes = 0;
|
|
123
|
+
int32 TotalLinks = 0;
|
|
124
|
+
bool bTruncatedAny = false;
|
|
125
|
+
|
|
126
|
+
for (URigVMGraph* Graph : Models)
|
|
127
|
+
{
|
|
128
|
+
const FString GraphName = Graph->GetName();
|
|
129
|
+
if (!GraphFilter.IsEmpty() && !GraphName.Contains(GraphFilter, ESearchCase::IgnoreCase))
|
|
130
|
+
{
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
TSharedPtr<FJsonObject> GraphObj = MakeShared<FJsonObject>();
|
|
135
|
+
GraphObj->SetStringField(TEXT("name"), GraphName);
|
|
136
|
+
GraphObj->SetStringField(TEXT("path"), Graph->GetPathName());
|
|
137
|
+
|
|
138
|
+
const TArray<URigVMNode*>& Nodes = Graph->GetNodes();
|
|
139
|
+
GraphObj->SetNumberField(TEXT("nodeCount"), Nodes.Num());
|
|
140
|
+
TotalNodes += Nodes.Num();
|
|
141
|
+
|
|
142
|
+
TArray<TSharedPtr<FJsonValue>> NodesArray;
|
|
143
|
+
bool bGraphTruncated = false;
|
|
144
|
+
for (URigVMNode* Node : Nodes)
|
|
145
|
+
{
|
|
146
|
+
if (!Node) continue;
|
|
147
|
+
if (NodesArray.Num() >= NodeLimit) { bTruncatedAny = true; bGraphTruncated = true; break; }
|
|
148
|
+
|
|
149
|
+
TSharedPtr<FJsonObject> NodeObj = MakeShared<FJsonObject>();
|
|
150
|
+
NodeObj->SetStringField(TEXT("name"), Node->GetName());
|
|
151
|
+
NodeObj->SetStringField(TEXT("nodePath"), Node->GetNodePath());
|
|
152
|
+
NodeObj->SetStringField(TEXT("class"), Node->GetClass()->GetName());
|
|
153
|
+
|
|
154
|
+
if (bIncludePins)
|
|
155
|
+
{
|
|
156
|
+
TArray<TSharedPtr<FJsonValue>> PinsArray;
|
|
157
|
+
for (URigVMPin* Pin : Node->GetPins())
|
|
158
|
+
{
|
|
159
|
+
if (Pin) PinsArray.Add(MakeShared<FJsonValueObject>(PinToJson(Pin, bIncludeDefaults, 0)));
|
|
160
|
+
}
|
|
161
|
+
NodeObj->SetArrayField(TEXT("pins"), PinsArray);
|
|
162
|
+
}
|
|
163
|
+
NodesArray.Add(MakeShared<FJsonValueObject>(NodeObj));
|
|
164
|
+
}
|
|
165
|
+
GraphObj->SetArrayField(TEXT("nodes"), NodesArray);
|
|
166
|
+
// Set at the break site: deriving it from counts false-positives on a
|
|
167
|
+
// graph that merely contains a null node.
|
|
168
|
+
GraphObj->SetBoolField(TEXT("nodesTruncated"), bGraphTruncated);
|
|
169
|
+
|
|
170
|
+
if (bIncludeLinks)
|
|
171
|
+
{
|
|
172
|
+
const TArray<URigVMLink*>& Links = Graph->GetLinks();
|
|
173
|
+
TotalLinks += Links.Num();
|
|
174
|
+
TArray<TSharedPtr<FJsonValue>> LinksArray;
|
|
175
|
+
for (URigVMLink* Link : Links)
|
|
176
|
+
{
|
|
177
|
+
if (!Link) continue;
|
|
178
|
+
URigVMPin* Source = Link->GetSourcePin();
|
|
179
|
+
URigVMPin* Target = Link->GetTargetPin();
|
|
180
|
+
if (!Source || !Target) continue;
|
|
181
|
+
TSharedPtr<FJsonObject> LinkObj = MakeShared<FJsonObject>();
|
|
182
|
+
LinkObj->SetStringField(TEXT("source"), Source->GetPinPath(/*bUseNodePath=*/true));
|
|
183
|
+
LinkObj->SetStringField(TEXT("target"), Target->GetPinPath(/*bUseNodePath=*/true));
|
|
184
|
+
LinksArray.Add(MakeShared<FJsonValueObject>(LinkObj));
|
|
185
|
+
}
|
|
186
|
+
GraphObj->SetArrayField(TEXT("links"), LinksArray);
|
|
187
|
+
GraphObj->SetNumberField(TEXT("linkCount"), LinksArray.Num());
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
GraphsArray.Add(MakeShared<FJsonValueObject>(GraphObj));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Member variables with their full metadata, not just a name and category.
|
|
194
|
+
TArray<TSharedPtr<FJsonValue>> VariablesArray;
|
|
195
|
+
for (const FBPVariableDescription& Var : Blueprint->NewVariables)
|
|
196
|
+
{
|
|
197
|
+
TSharedPtr<FJsonObject> VarObj = MakeShared<FJsonObject>();
|
|
198
|
+
VarObj->SetStringField(TEXT("name"), Var.VarName.ToString());
|
|
199
|
+
VarObj->SetStringField(TEXT("type"), Var.VarType.PinCategory.ToString());
|
|
200
|
+
VarObj->SetStringField(TEXT("subType"), Var.VarType.PinSubCategory.ToString());
|
|
201
|
+
if (Var.VarType.PinSubCategoryObject.IsValid())
|
|
202
|
+
{
|
|
203
|
+
VarObj->SetStringField(TEXT("subTypeObject"), Var.VarType.PinSubCategoryObject->GetPathName());
|
|
204
|
+
}
|
|
205
|
+
VarObj->SetBoolField(TEXT("isArray"), Var.VarType.IsArray());
|
|
206
|
+
VarObj->SetStringField(TEXT("defaultValue"), Var.DefaultValue);
|
|
207
|
+
VarObj->SetBoolField(TEXT("isPublic"), (Var.PropertyFlags & CPF_BlueprintVisible) != 0);
|
|
208
|
+
VarObj->SetBoolField(TEXT("readOnly"), (Var.PropertyFlags & CPF_BlueprintReadOnly) != 0);
|
|
209
|
+
VariablesArray.Add(MakeShared<FJsonValueObject>(VarObj));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
auto Result = MCPSuccess();
|
|
213
|
+
Result->SetStringField(TEXT("assetPath"), AssetPath);
|
|
214
|
+
Result->SetStringField(TEXT("name"), Blueprint->GetName());
|
|
215
|
+
if (Blueprint->ParentClass)
|
|
216
|
+
{
|
|
217
|
+
Result->SetStringField(TEXT("parentClass"), Blueprint->ParentClass->GetName());
|
|
218
|
+
}
|
|
219
|
+
Result->SetArrayField(TEXT("models"), GraphsArray);
|
|
220
|
+
Result->SetNumberField(TEXT("modelCount"), GraphsArray.Num());
|
|
221
|
+
Result->SetNumberField(TEXT("totalNodes"), TotalNodes);
|
|
222
|
+
Result->SetNumberField(TEXT("totalLinks"), TotalLinks);
|
|
223
|
+
Result->SetArrayField(TEXT("variables"), VariablesArray);
|
|
224
|
+
Result->SetNumberField(TEXT("variableCount"), VariablesArray.Num());
|
|
225
|
+
if (bTruncatedAny)
|
|
226
|
+
{
|
|
227
|
+
Result->SetStringField(TEXT("note"), TEXT("Some graphs hit the node limit; raise 'limit' or narrow with 'graphName'."));
|
|
228
|
+
}
|
|
229
|
+
return MCPResult(Result);
|
|
230
|
+
}
|