ue-mcp 1.2.0 → 1.2.1
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/instructions.js +130 -130
- package/dist/manifest-signature.d.ts +1 -0
- package/dist/manifest-signature.js +53 -0
- package/dist/manifest-signature.js.map +1 -0
- package/dist/pie/schema.d.ts +791 -0
- package/dist/pie/schema.js +151 -0
- package/dist/pie/schema.js.map +1 -0
- package/dist/plugin-cli.js +299 -299
- package/dist/tool-counts.json +2 -2
- package/dist/watch-cli.js.map +1 -0
- package/package.json +6 -3
- 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.cpp +10 -32
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -21
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +3 -18
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +1 -19
- 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/HandlerUtils.h +30 -0
- 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/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
|
+
}
|
|
@@ -75,32 +75,10 @@
|
|
|
75
75
|
#include "AI/Navigation/NavCollisionBase.h"
|
|
76
76
|
|
|
77
77
|
// ─── Protected mount guardrail ──────────────────────────────────────────
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// UEditorAssetLibrary::DeleteAsset will happily destroy files under
|
|
81
|
-
// <engineRoot>/Engine/Content/ if not stopped - verified the hard way.
|
|
82
|
-
// Apply this check to every handler that deletes, moves, or renames an
|
|
83
|
-
// asset. Plugin content roots (mounted under /<PluginName>/) are NOT
|
|
84
|
-
// protected here; per-project plugin content is expected to be writable.
|
|
78
|
+
// The rule itself is MCPIsProtectedAssetPath in HandlerUtils.h, shared by
|
|
79
|
+
// every asset translation unit so the write paths cannot drift apart.
|
|
85
80
|
namespace
|
|
86
81
|
{
|
|
87
|
-
bool IsProtectedAssetPath(const FString& Path)
|
|
88
|
-
{
|
|
89
|
-
FString P = Path;
|
|
90
|
-
P.TrimStartAndEndInline();
|
|
91
|
-
if (P.IsEmpty()) return false;
|
|
92
|
-
// Tolerate leading whitespace and the surface form (no leading slash).
|
|
93
|
-
if (!P.StartsWith(TEXT("/"))) P = TEXT("/") + P;
|
|
94
|
-
const FString L = P.ToLower();
|
|
95
|
-
if (L.StartsWith(TEXT("/engine/"))) return true;
|
|
96
|
-
if (L.StartsWith(TEXT("/script/"))) return true;
|
|
97
|
-
if (L.StartsWith(TEXT("/memory/"))) return true;
|
|
98
|
-
if (L.StartsWith(TEXT("/temp/"))) return true;
|
|
99
|
-
// Verse runtime objects surface as /Script/CoreUObject.* etc.
|
|
100
|
-
if (L.Contains(TEXT("/script/"))) return true;
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
82
|
TSharedPtr<FJsonValue> MakeProtectedPathError(const FString& Path)
|
|
105
83
|
{
|
|
106
84
|
return MCPError(FString::Printf(
|
|
@@ -1488,8 +1466,8 @@ TSharedPtr<FJsonValue> FAssetHandlers::RenameAsset(const TSharedPtr<FJsonObject>
|
|
|
1488
1466
|
return MCPError(TEXT("Missing 'sourcePath'+'destinationPath' or 'assetPath'+'newName'"));
|
|
1489
1467
|
}
|
|
1490
1468
|
|
|
1491
|
-
if (
|
|
1492
|
-
if (
|
|
1469
|
+
if (MCPIsProtectedAssetPath(SourcePath)) return MakeProtectedPathError(SourcePath);
|
|
1470
|
+
if (MCPIsProtectedAssetPath(DestPath)) return MakeProtectedPathError(DestPath);
|
|
1493
1471
|
|
|
1494
1472
|
// Idempotency: if already at destination, no-op.
|
|
1495
1473
|
if (SourcePath == DestPath)
|
|
@@ -1697,7 +1675,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAsset(const TSharedPtr<FJsonObject>
|
|
|
1697
1675
|
FString AssetPath;
|
|
1698
1676
|
if (auto Err = RequireStringAlt(Params, TEXT("path"), TEXT("assetPath"), AssetPath)) return Err;
|
|
1699
1677
|
|
|
1700
|
-
if (
|
|
1678
|
+
if (MCPIsProtectedAssetPath(AssetPath)) return MakeProtectedPathError(AssetPath);
|
|
1701
1679
|
|
|
1702
1680
|
const bool bForce = OptionalBool(Params, TEXT("force"), false);
|
|
1703
1681
|
|
|
@@ -1763,7 +1741,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteAssetBatch(const TSharedPtr<FJsonOb
|
|
|
1763
1741
|
TSharedPtr<FJsonObject> Entry = MakeShared<FJsonObject>();
|
|
1764
1742
|
Entry->SetStringField(TEXT("path"), Path);
|
|
1765
1743
|
|
|
1766
|
-
if (
|
|
1744
|
+
if (MCPIsProtectedAssetPath(Path))
|
|
1767
1745
|
{
|
|
1768
1746
|
Entry->SetStringField(TEXT("status"), TEXT("protected"));
|
|
1769
1747
|
Entry->SetStringField(TEXT("reason"), TEXT("Engine/Script/Memory/Temp mounts are read-only via the bridge"));
|
|
@@ -1892,7 +1870,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkRename(const TSharedPtr<FJsonObject>&
|
|
|
1892
1870
|
continue;
|
|
1893
1871
|
}
|
|
1894
1872
|
|
|
1895
|
-
if (
|
|
1873
|
+
if (MCPIsProtectedAssetPath(SourcePath) || MCPIsProtectedAssetPath(NewPackagePath))
|
|
1896
1874
|
{
|
|
1897
1875
|
Record->SetStringField(TEXT("status"), TEXT("protected"));
|
|
1898
1876
|
Record->SetStringField(TEXT("reason"), TEXT("Engine/Script/Memory/Temp mounts are read-only via the bridge"));
|
|
@@ -2555,8 +2533,8 @@ TSharedPtr<FJsonValue> FAssetHandlers::MoveFolder(const TSharedPtr<FJsonObject>&
|
|
|
2555
2533
|
SourcePath.RemoveFromEnd(TEXT("/"));
|
|
2556
2534
|
DestinationPath.RemoveFromEnd(TEXT("/"));
|
|
2557
2535
|
|
|
2558
|
-
if (
|
|
2559
|
-
if (
|
|
2536
|
+
if (MCPIsProtectedAssetPath(SourcePath)) return MakeProtectedPathError(SourcePath);
|
|
2537
|
+
if (MCPIsProtectedAssetPath(DestinationPath)) return MakeProtectedPathError(DestinationPath);
|
|
2560
2538
|
|
|
2561
2539
|
// Scan source path to discover all assets
|
|
2562
2540
|
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
|
@@ -2738,7 +2716,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::DeleteFolder(const TSharedPtr<FJsonObject
|
|
|
2738
2716
|
continue;
|
|
2739
2717
|
}
|
|
2740
2718
|
|
|
2741
|
-
if (
|
|
2719
|
+
if (MCPIsProtectedAssetPath(Norm))
|
|
2742
2720
|
{
|
|
2743
2721
|
Entry->SetStringField(TEXT("status"), TEXT("failed"));
|
|
2744
2722
|
Entry->SetStringField(TEXT("reason"), TEXT("protected_path"));
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp
CHANGED
|
@@ -64,26 +64,6 @@ namespace
|
|
|
64
64
|
bool PassedPreflight() const { return Error.IsEmpty(); }
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
// Mirrors IsProtectedAssetPath in AssetHandlers.cpp, which is file-local to
|
|
68
|
-
// that translation unit. Keep the two rule sets identical: a write path that
|
|
69
|
-
// enforces a weaker rule than its neighbours is how engine content gets
|
|
70
|
-
// mutated through the bridge.
|
|
71
|
-
bool IsProtectedBulkAssetPath(const FString& Path)
|
|
72
|
-
{
|
|
73
|
-
FString Normalized = Path;
|
|
74
|
-
Normalized.TrimStartAndEndInline();
|
|
75
|
-
if (Normalized.IsEmpty()) return false;
|
|
76
|
-
if (!Normalized.StartsWith(TEXT("/"))) Normalized = TEXT("/") + Normalized;
|
|
77
|
-
const FString Lower = Normalized.ToLower();
|
|
78
|
-
if (Lower.StartsWith(TEXT("/engine/"))) return true;
|
|
79
|
-
if (Lower.StartsWith(TEXT("/script/"))) return true;
|
|
80
|
-
if (Lower.StartsWith(TEXT("/memory/"))) return true;
|
|
81
|
-
if (Lower.StartsWith(TEXT("/temp/"))) return true;
|
|
82
|
-
// Verse runtime objects surface as /Script/CoreUObject.* etc.
|
|
83
|
-
if (Lower.Contains(TEXT("/script/"))) return true;
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
67
|
TSharedPtr<FJsonObject> MakePropertyReadback(
|
|
88
68
|
const FPreparedPropertyWrite& Prepared,
|
|
89
69
|
const TSharedPtr<FJsonValue>& ActualValue,
|
|
@@ -198,7 +178,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkSetAssetProperties(const TSharedPtr<F
|
|
|
198
178
|
}
|
|
199
179
|
PreparedAsset.AssetPath = AssetPath;
|
|
200
180
|
|
|
201
|
-
if (
|
|
181
|
+
if (MCPIsProtectedAssetPath(AssetPath))
|
|
202
182
|
{
|
|
203
183
|
RejectItem(BulkStatusProtected, FString::Printf(
|
|
204
184
|
TEXT("Refusing to mutate protected mount: %s. Engine, /Script/, /Memory/, /Temp/ are read-only via the bridge."),
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp
CHANGED
|
@@ -50,21 +50,6 @@ struct FPreparedUpsertItem
|
|
|
50
50
|
TArray<FPreparedProperty> Properties;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
bool IsProtectedAssetPath(const FString& Path)
|
|
54
|
-
{
|
|
55
|
-
FString Normalized = Path;
|
|
56
|
-
Normalized.TrimStartAndEndInline();
|
|
57
|
-
if (!Normalized.StartsWith(TEXT("/")))
|
|
58
|
-
{
|
|
59
|
-
Normalized = TEXT("/") + Normalized;
|
|
60
|
-
}
|
|
61
|
-
const FString Lower = Normalized.ToLower();
|
|
62
|
-
return Lower.StartsWith(TEXT("/engine/"))
|
|
63
|
-
|| Lower.StartsWith(TEXT("/script/"))
|
|
64
|
-
|| Lower.StartsWith(TEXT("/memory/"))
|
|
65
|
-
|| Lower.StartsWith(TEXT("/temp/"));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
53
|
UClass* ResolveDataAssetClass(const FString& ClassName)
|
|
69
54
|
{
|
|
70
55
|
UClass* DataClass = nullptr;
|
|
@@ -144,7 +129,7 @@ bool ParseAssetIdentity(
|
|
|
144
129
|
}
|
|
145
130
|
|
|
146
131
|
const FString LongPackageName = OutItem.PackagePath + TEXT("/") + OutItem.Name;
|
|
147
|
-
if (
|
|
132
|
+
if (MCPIsProtectedAssetPath(LongPackageName))
|
|
148
133
|
{
|
|
149
134
|
OutError = FString::Printf(TEXT("Refusing to mutate protected package '%s'"), *LongPackageName);
|
|
150
135
|
return false;
|
|
@@ -357,7 +342,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkRestoreDataAssets(const TSharedPtr<FJ
|
|
|
357
342
|
FString AssetPath;
|
|
358
343
|
const TSharedPtr<FJsonObject>* Properties = nullptr;
|
|
359
344
|
if (!(*Item)->TryGetStringField(TEXT("assetPath"), AssetPath)
|
|
360
|
-
||
|
|
345
|
+
|| MCPIsProtectedAssetPath(AssetPath)
|
|
361
346
|
|| !(*Item)->TryGetObjectField(TEXT("properties"), Properties)
|
|
362
347
|
|| !Properties
|
|
363
348
|
|| !Properties->IsValid())
|
|
@@ -414,7 +399,7 @@ TSharedPtr<FJsonValue> FAssetHandlers::BulkRestoreDataAssets(const TSharedPtr<FJ
|
|
|
414
399
|
FString AssetPath;
|
|
415
400
|
if (!(*CreatedAssetPaths)[PathIndex].IsValid()
|
|
416
401
|
|| !(*CreatedAssetPaths)[PathIndex]->TryGetString(AssetPath)
|
|
417
|
-
||
|
|
402
|
+
|| MCPIsProtectedAssetPath(AssetPath))
|
|
418
403
|
{
|
|
419
404
|
Errors.Add(MakeShared<FJsonValueString>(FString::Printf(TEXT("createdAssetPaths[%d] is invalid"), PathIndex)));
|
|
420
405
|
continue;
|