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
|
@@ -1,323 +1,323 @@
|
|
|
1
|
-
// Split from AssetHandlers.cpp. All functions below are still members of
|
|
2
|
-
// FAssetHandlers - this file is a translation-unit partition, not a new class.
|
|
3
|
-
// Handler registration stays in AssetHandlers.cpp::RegisterHandlers.
|
|
4
|
-
//
|
|
5
|
-
// #735 - UserDefinedStruct authoring. UUserDefinedStruct has no scripting entry
|
|
6
|
-
// point for creating, listing, renaming, retyping, or removing members; the
|
|
7
|
-
// Struct editor does that through FStructureEditorUtils (editor-internal). The
|
|
8
|
-
// key requirement is that renaming a field preserve the member GUID so existing
|
|
9
|
-
// Blueprint pins and DataTable rows keyed off that field survive the rename -
|
|
10
|
-
// FStructureEditorUtils::RenameVariable does exactly that (it only rewrites the
|
|
11
|
-
// friendly name, not the GUID). These handlers wrap it so struct-backed systems
|
|
12
|
-
// (DataTables, Blueprint variables) can be authored end to end from the bridge.
|
|
13
|
-
|
|
14
|
-
#include "AssetHandlers.h"
|
|
15
|
-
#include "BlueprintHandlers.h"
|
|
16
|
-
#include "HandlerRegistry.h"
|
|
17
|
-
#include "HandlerUtils.h"
|
|
18
|
-
#include "StructUtils/UserDefinedStruct.h"
|
|
19
|
-
#include "Kismet2/StructureEditorUtils.h"
|
|
20
|
-
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
|
|
21
|
-
#include "AssetRegistry/AssetRegistryModule.h"
|
|
22
|
-
#include "EditorAssetLibrary.h"
|
|
23
|
-
#include "Dom/JsonObject.h"
|
|
24
|
-
#include "Dom/JsonValue.h"
|
|
25
|
-
|
|
26
|
-
// Human-readable type label for one member (e.g. "int", "Vector (struct)",
|
|
27
|
-
// "Actor (object)", "int[]"). Best-effort; agents mainly key off name/guid.
|
|
28
|
-
static FString StructFieldTypeLabel(const FStructVariableDescription& Desc)
|
|
29
|
-
{
|
|
30
|
-
FString Base = Desc.Category.ToString();
|
|
31
|
-
if (!Desc.SubCategoryObject.IsNull())
|
|
32
|
-
{
|
|
33
|
-
Base = FString::Printf(TEXT("%s (%s)"), *Desc.SubCategoryObject.GetAssetName(), *Base);
|
|
34
|
-
}
|
|
35
|
-
switch (Desc.ContainerType)
|
|
36
|
-
{
|
|
37
|
-
case EPinContainerType::Array: return Base + TEXT("[]");
|
|
38
|
-
case EPinContainerType::Set: return TEXT("Set<") + Base + TEXT(">");
|
|
39
|
-
case EPinContainerType::Map: return TEXT("Map<") + Base + TEXT(">");
|
|
40
|
-
default: return Base;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Serialize one member to JSON: index, internal VarName, friendly (display) name,
|
|
45
|
-
// GUID string, type label.
|
|
46
|
-
static TSharedPtr<FJsonObject> StructFieldToJson(const FStructVariableDescription& Desc, int32 Index)
|
|
47
|
-
{
|
|
48
|
-
TSharedPtr<FJsonObject> V = MakeShared<FJsonObject>();
|
|
49
|
-
V->SetNumberField(TEXT("index"), Index);
|
|
50
|
-
V->SetStringField(TEXT("name"), Desc.VarName.ToString());
|
|
51
|
-
V->SetStringField(TEXT("friendlyName"), Desc.FriendlyName);
|
|
52
|
-
V->SetStringField(TEXT("guid"), Desc.VarGuid.ToString());
|
|
53
|
-
V->SetStringField(TEXT("type"), StructFieldTypeLabel(Desc));
|
|
54
|
-
return V;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Resolve a member's GUID from an explicit "fieldGuid" param, or by matching a
|
|
58
|
-
// "fieldName" param against either the friendly (display) name or the internal
|
|
59
|
-
// VarName. Returns false (invalid Guid) if unresolved.
|
|
60
|
-
static bool ResolveStructFieldGuid(const TSharedPtr<FJsonObject>& Params, UUserDefinedStruct* Struct, FGuid& OutGuid)
|
|
61
|
-
{
|
|
62
|
-
const FString GuidStr = OptionalString(Params, TEXT("fieldGuid"));
|
|
63
|
-
if (!GuidStr.IsEmpty())
|
|
64
|
-
{
|
|
65
|
-
return FGuid::Parse(GuidStr, OutGuid);
|
|
66
|
-
}
|
|
67
|
-
const FString FieldName = OptionalString(Params, TEXT("fieldName"));
|
|
68
|
-
if (FieldName.IsEmpty()) return false;
|
|
69
|
-
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
70
|
-
{
|
|
71
|
-
if (Desc.FriendlyName == FieldName || Desc.VarName.ToString() == FieldName)
|
|
72
|
-
{
|
|
73
|
-
OutGuid = Desc.VarGuid;
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
TSharedPtr<FJsonValue> FAssetHandlers::CreateUserDefinedStruct(const TSharedPtr<FJsonObject>& Params)
|
|
81
|
-
{
|
|
82
|
-
FString Name;
|
|
83
|
-
if (auto Err = RequireString(Params, TEXT("name"), Name)) return Err;
|
|
84
|
-
const FString PackagePath = OptionalString(Params, TEXT("packagePath"), TEXT("/Game"));
|
|
85
|
-
const FString OnConflict = OptionalString(Params, TEXT("onConflict"), TEXT("skip"));
|
|
86
|
-
|
|
87
|
-
if (auto Existing = MCPCheckAssetExists(PackagePath, Name, OnConflict, TEXT("UserDefinedStruct")))
|
|
88
|
-
{
|
|
89
|
-
return Existing;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
UPackage* Package = CreatePackage(*(PackagePath + TEXT("/") + Name));
|
|
93
|
-
if (!Package) return MCPError(FString::Printf(TEXT("Failed to create package for %s"), *Name));
|
|
94
|
-
|
|
95
|
-
UUserDefinedStruct* Struct = FStructureEditorUtils::CreateUserDefinedStruct(Package, FName(*Name), RF_Public | RF_Standalone);
|
|
96
|
-
if (!Struct) return MCPError(TEXT("Failed to create UserDefinedStruct"));
|
|
97
|
-
|
|
98
|
-
// CreateUserDefinedStruct seeds one default member. Capture its GUID so we can
|
|
99
|
-
// strip it once the caller's own fields are in (a struct must never hit zero
|
|
100
|
-
// members, so we add first and remove the seed last).
|
|
101
|
-
TArray<FGuid> SeedGuids;
|
|
102
|
-
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
103
|
-
{
|
|
104
|
-
SeedGuids.Add(Desc.VarGuid);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const TArray<TSharedPtr<FJsonValue>>* Fields = nullptr;
|
|
108
|
-
int32 AddedCount = 0;
|
|
109
|
-
if (Params->TryGetArrayField(TEXT("fields"), Fields) && Fields)
|
|
110
|
-
{
|
|
111
|
-
for (const TSharedPtr<FJsonValue>& Entry : *Fields)
|
|
112
|
-
{
|
|
113
|
-
const TSharedPtr<FJsonObject>* Obj = nullptr;
|
|
114
|
-
if (!Entry->TryGetObject(Obj) || !Obj) continue;
|
|
115
|
-
const FString FieldName = OptionalString(*Obj, TEXT("name"));
|
|
116
|
-
const FString FieldType = OptionalString(*Obj, TEXT("type"), TEXT("bool"));
|
|
117
|
-
if (FieldName.IsEmpty()) continue;
|
|
118
|
-
|
|
119
|
-
// Snapshot GUIDs so we can identify the member AddVariable appends.
|
|
120
|
-
TSet<FGuid> Before;
|
|
121
|
-
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct)) Before.Add(D.VarGuid);
|
|
122
|
-
|
|
123
|
-
if (!FStructureEditorUtils::AddVariable(Struct, FBlueprintHandlers::MakePinType(FieldType))) continue;
|
|
124
|
-
|
|
125
|
-
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct))
|
|
126
|
-
{
|
|
127
|
-
if (!Before.Contains(D.VarGuid))
|
|
128
|
-
{
|
|
129
|
-
FStructureEditorUtils::RenameVariable(Struct, D.VarGuid, FieldName);
|
|
130
|
-
++AddedCount;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Only drop the seed member if the caller supplied replacements; otherwise the
|
|
138
|
-
// struct keeps the default so it never has zero members.
|
|
139
|
-
if (AddedCount > 0)
|
|
140
|
-
{
|
|
141
|
-
for (const FGuid& Seed : SeedGuids)
|
|
142
|
-
{
|
|
143
|
-
FStructureEditorUtils::RemoveVariable(Struct, Seed);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
FAssetRegistryModule::AssetCreated(Struct);
|
|
148
|
-
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
149
|
-
|
|
150
|
-
TSharedPtr<FJsonObject> Res = MCPSuccess();
|
|
151
|
-
MCPSetCreated(Res);
|
|
152
|
-
Res->SetStringField(TEXT("path"), Struct->GetPathName());
|
|
153
|
-
Res->SetStringField(TEXT("name"), Name);
|
|
154
|
-
TArray<TSharedPtr<FJsonValue>> FieldList;
|
|
155
|
-
int32 Idx = 0;
|
|
156
|
-
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
157
|
-
{
|
|
158
|
-
FieldList.Add(MakeShared<FJsonValueObject>(StructFieldToJson(Desc, Idx++)));
|
|
159
|
-
}
|
|
160
|
-
Res->SetArrayField(TEXT("fields"), FieldList);
|
|
161
|
-
Res->SetNumberField(TEXT("count"), FieldList.Num());
|
|
162
|
-
MCPSetDeleteAssetRollback(Res, Struct->GetPathName());
|
|
163
|
-
return MCPResult(Res);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
TSharedPtr<FJsonValue> FAssetHandlers::ListStructFields(const TSharedPtr<FJsonObject>& Params)
|
|
167
|
-
{
|
|
168
|
-
FString AssetPath;
|
|
169
|
-
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
170
|
-
|
|
171
|
-
UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(UEditorAssetLibrary::LoadAsset(AssetPath));
|
|
172
|
-
if (!Struct) return MCPError(FString::Printf(TEXT("UserDefinedStruct not found (native structs are not editable): %s"), *AssetPath));
|
|
173
|
-
|
|
174
|
-
TSharedPtr<FJsonObject> Res = MCPSuccess();
|
|
175
|
-
Res->SetStringField(TEXT("path"), AssetPath);
|
|
176
|
-
Res->SetStringField(TEXT("name"), Struct->GetName());
|
|
177
|
-
|
|
178
|
-
TArray<TSharedPtr<FJsonValue>> FieldList;
|
|
179
|
-
int32 Idx = 0;
|
|
180
|
-
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
181
|
-
{
|
|
182
|
-
FieldList.Add(MakeShared<FJsonValueObject>(StructFieldToJson(Desc, Idx++)));
|
|
183
|
-
}
|
|
184
|
-
Res->SetArrayField(TEXT("fields"), FieldList);
|
|
185
|
-
Res->SetNumberField(TEXT("count"), FieldList.Num());
|
|
186
|
-
return MCPResult(Res);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
TSharedPtr<FJsonValue> FAssetHandlers::EditUserDefinedStruct(const TSharedPtr<FJsonObject>& Params)
|
|
190
|
-
{
|
|
191
|
-
FString AssetPath;
|
|
192
|
-
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
193
|
-
FString Op;
|
|
194
|
-
if (auto Err = RequireString(Params, TEXT("op"), Op)) return Err;
|
|
195
|
-
|
|
196
|
-
UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(UEditorAssetLibrary::LoadAsset(AssetPath));
|
|
197
|
-
if (!Struct) return MCPError(FString::Printf(TEXT("UserDefinedStruct not found (native structs are not editable): %s"), *AssetPath));
|
|
198
|
-
|
|
199
|
-
TSharedPtr<FJsonObject> Res = MCPSuccess();
|
|
200
|
-
Res->SetStringField(TEXT("path"), AssetPath);
|
|
201
|
-
Res->SetStringField(TEXT("op"), Op);
|
|
202
|
-
|
|
203
|
-
if (Op == TEXT("add_field"))
|
|
204
|
-
{
|
|
205
|
-
const FString FieldType = OptionalString(Params, TEXT("type"), TEXT("bool"));
|
|
206
|
-
FString FieldName = OptionalString(Params, TEXT("fieldName"));
|
|
207
|
-
if (FieldName.IsEmpty()) FieldName = OptionalString(Params, TEXT("newDisplayName"));
|
|
208
|
-
|
|
209
|
-
TSet<FGuid> Before;
|
|
210
|
-
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct)) Before.Add(D.VarGuid);
|
|
211
|
-
|
|
212
|
-
if (!FStructureEditorUtils::AddVariable(Struct, FBlueprintHandlers::MakePinType(FieldType)))
|
|
213
|
-
{
|
|
214
|
-
return MCPError(FString::Printf(TEXT("Failed to add field of type '%s'"), *FieldType));
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
FGuid NewGuid;
|
|
218
|
-
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct))
|
|
219
|
-
{
|
|
220
|
-
if (!Before.Contains(D.VarGuid)) { NewGuid = D.VarGuid; break; }
|
|
221
|
-
}
|
|
222
|
-
if (NewGuid.IsValid() && !FieldName.IsEmpty())
|
|
223
|
-
{
|
|
224
|
-
FStructureEditorUtils::RenameVariable(Struct, NewGuid, FieldName);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
228
|
-
MCPSetUpdated(Res);
|
|
229
|
-
if (const FStructVariableDescription* Desc = FStructureEditorUtils::GetVarDescByGuid(Struct, NewGuid))
|
|
230
|
-
{
|
|
231
|
-
Res->SetObjectField(TEXT("field"), StructFieldToJson(*Desc, INDEX_NONE));
|
|
232
|
-
}
|
|
233
|
-
Res->SetNumberField(TEXT("count"), FStructureEditorUtils::GetVarDesc(Struct).Num());
|
|
234
|
-
|
|
235
|
-
TSharedPtr<FJsonObject> RbPayload = MakeShared<FJsonObject>();
|
|
236
|
-
RbPayload->SetStringField(TEXT("assetPath"), AssetPath);
|
|
237
|
-
RbPayload->SetStringField(TEXT("op"), TEXT("remove_field"));
|
|
238
|
-
RbPayload->SetStringField(TEXT("fieldGuid"), NewGuid.ToString());
|
|
239
|
-
MCPSetRollback(Res, TEXT("edit_user_defined_struct"), RbPayload);
|
|
240
|
-
return MCPResult(Res);
|
|
241
|
-
}
|
|
242
|
-
else if (Op == TEXT("rename_field"))
|
|
243
|
-
{
|
|
244
|
-
FString NewDisplayName;
|
|
245
|
-
if (auto Err = RequireString(Params, TEXT("newDisplayName"), NewDisplayName)) return Err;
|
|
246
|
-
FGuid Guid;
|
|
247
|
-
if (!ResolveStructFieldGuid(Params, Struct, Guid))
|
|
248
|
-
return MCPError(TEXT("Could not resolve field (pass 'fieldGuid', or 'fieldName' matching a friendly or internal name)"));
|
|
249
|
-
|
|
250
|
-
const FStructVariableDescription* Before = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid);
|
|
251
|
-
const FString PrevName = Before ? Before->FriendlyName : FString();
|
|
252
|
-
|
|
253
|
-
// RenameVariable rewrites only the friendly name; the member GUID is
|
|
254
|
-
// preserved, so Blueprint pins and DataTable rows keyed off it survive.
|
|
255
|
-
if (!FStructureEditorUtils::RenameVariable(Struct, Guid, NewDisplayName))
|
|
256
|
-
{
|
|
257
|
-
return MCPError(FString::Printf(TEXT("Rename to '%s' failed (name invalid or duplicate)"), *NewDisplayName));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
261
|
-
MCPSetUpdated(Res);
|
|
262
|
-
if (const FStructVariableDescription* After = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid))
|
|
263
|
-
{
|
|
264
|
-
Res->SetObjectField(TEXT("field"), StructFieldToJson(*After, INDEX_NONE));
|
|
265
|
-
}
|
|
266
|
-
Res->SetStringField(TEXT("previousDisplayName"), PrevName);
|
|
267
|
-
|
|
268
|
-
TSharedPtr<FJsonObject> RbPayload = MakeShared<FJsonObject>();
|
|
269
|
-
RbPayload->SetStringField(TEXT("assetPath"), AssetPath);
|
|
270
|
-
RbPayload->SetStringField(TEXT("op"), TEXT("rename_field"));
|
|
271
|
-
RbPayload->SetStringField(TEXT("fieldGuid"), Guid.ToString());
|
|
272
|
-
RbPayload->SetStringField(TEXT("newDisplayName"), PrevName);
|
|
273
|
-
MCPSetRollback(Res, TEXT("edit_user_defined_struct"), RbPayload);
|
|
274
|
-
return MCPResult(Res);
|
|
275
|
-
}
|
|
276
|
-
else if (Op == TEXT("set_field_type"))
|
|
277
|
-
{
|
|
278
|
-
FString FieldType;
|
|
279
|
-
if (auto Err = RequireString(Params, TEXT("type"), FieldType)) return Err;
|
|
280
|
-
FGuid Guid;
|
|
281
|
-
if (!ResolveStructFieldGuid(Params, Struct, Guid))
|
|
282
|
-
return MCPError(TEXT("Could not resolve field (pass 'fieldGuid', or 'fieldName' matching a friendly or internal name)"));
|
|
283
|
-
|
|
284
|
-
if (!FStructureEditorUtils::ChangeVariableType(Struct, Guid, FBlueprintHandlers::MakePinType(FieldType)))
|
|
285
|
-
{
|
|
286
|
-
return MCPError(FString::Printf(TEXT("Failed to change field type to '%s'"), *FieldType));
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
290
|
-
MCPSetUpdated(Res);
|
|
291
|
-
if (const FStructVariableDescription* After = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid))
|
|
292
|
-
{
|
|
293
|
-
Res->SetObjectField(TEXT("field"), StructFieldToJson(*After, INDEX_NONE));
|
|
294
|
-
}
|
|
295
|
-
return MCPResult(Res);
|
|
296
|
-
}
|
|
297
|
-
else if (Op == TEXT("remove_field"))
|
|
298
|
-
{
|
|
299
|
-
FGuid Guid;
|
|
300
|
-
if (!ResolveStructFieldGuid(Params, Struct, Guid))
|
|
301
|
-
return MCPError(TEXT("Could not resolve field (pass 'fieldGuid', or 'fieldName' matching a friendly or internal name)"));
|
|
302
|
-
if (FStructureEditorUtils::GetVarDesc(Struct).Num() <= 1)
|
|
303
|
-
return MCPError(TEXT("Cannot remove the last field; a UserDefinedStruct must keep at least one member"));
|
|
304
|
-
|
|
305
|
-
TSharedPtr<FJsonObject> Removed;
|
|
306
|
-
if (const FStructVariableDescription* Desc = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid))
|
|
307
|
-
{
|
|
308
|
-
Removed = StructFieldToJson(*Desc, INDEX_NONE);
|
|
309
|
-
}
|
|
310
|
-
if (!FStructureEditorUtils::RemoveVariable(Struct, Guid))
|
|
311
|
-
{
|
|
312
|
-
return MCPError(TEXT("RemoveVariable failed"));
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
316
|
-
MCPSetUpdated(Res);
|
|
317
|
-
if (Removed.IsValid()) Res->SetObjectField(TEXT("removed"), Removed);
|
|
318
|
-
Res->SetNumberField(TEXT("count"), FStructureEditorUtils::GetVarDesc(Struct).Num());
|
|
319
|
-
return MCPResult(Res);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return MCPError(FString::Printf(TEXT("Unknown op '%s' (expected add_field|rename_field|set_field_type|remove_field)"), *Op));
|
|
323
|
-
}
|
|
1
|
+
// Split from AssetHandlers.cpp. All functions below are still members of
|
|
2
|
+
// FAssetHandlers - this file is a translation-unit partition, not a new class.
|
|
3
|
+
// Handler registration stays in AssetHandlers.cpp::RegisterHandlers.
|
|
4
|
+
//
|
|
5
|
+
// #735 - UserDefinedStruct authoring. UUserDefinedStruct has no scripting entry
|
|
6
|
+
// point for creating, listing, renaming, retyping, or removing members; the
|
|
7
|
+
// Struct editor does that through FStructureEditorUtils (editor-internal). The
|
|
8
|
+
// key requirement is that renaming a field preserve the member GUID so existing
|
|
9
|
+
// Blueprint pins and DataTable rows keyed off that field survive the rename -
|
|
10
|
+
// FStructureEditorUtils::RenameVariable does exactly that (it only rewrites the
|
|
11
|
+
// friendly name, not the GUID). These handlers wrap it so struct-backed systems
|
|
12
|
+
// (DataTables, Blueprint variables) can be authored end to end from the bridge.
|
|
13
|
+
|
|
14
|
+
#include "AssetHandlers.h"
|
|
15
|
+
#include "BlueprintHandlers.h"
|
|
16
|
+
#include "HandlerRegistry.h"
|
|
17
|
+
#include "HandlerUtils.h"
|
|
18
|
+
#include "StructUtils/UserDefinedStruct.h"
|
|
19
|
+
#include "Kismet2/StructureEditorUtils.h"
|
|
20
|
+
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
|
|
21
|
+
#include "AssetRegistry/AssetRegistryModule.h"
|
|
22
|
+
#include "EditorAssetLibrary.h"
|
|
23
|
+
#include "Dom/JsonObject.h"
|
|
24
|
+
#include "Dom/JsonValue.h"
|
|
25
|
+
|
|
26
|
+
// Human-readable type label for one member (e.g. "int", "Vector (struct)",
|
|
27
|
+
// "Actor (object)", "int[]"). Best-effort; agents mainly key off name/guid.
|
|
28
|
+
static FString StructFieldTypeLabel(const FStructVariableDescription& Desc)
|
|
29
|
+
{
|
|
30
|
+
FString Base = Desc.Category.ToString();
|
|
31
|
+
if (!Desc.SubCategoryObject.IsNull())
|
|
32
|
+
{
|
|
33
|
+
Base = FString::Printf(TEXT("%s (%s)"), *Desc.SubCategoryObject.GetAssetName(), *Base);
|
|
34
|
+
}
|
|
35
|
+
switch (Desc.ContainerType)
|
|
36
|
+
{
|
|
37
|
+
case EPinContainerType::Array: return Base + TEXT("[]");
|
|
38
|
+
case EPinContainerType::Set: return TEXT("Set<") + Base + TEXT(">");
|
|
39
|
+
case EPinContainerType::Map: return TEXT("Map<") + Base + TEXT(">");
|
|
40
|
+
default: return Base;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Serialize one member to JSON: index, internal VarName, friendly (display) name,
|
|
45
|
+
// GUID string, type label.
|
|
46
|
+
static TSharedPtr<FJsonObject> StructFieldToJson(const FStructVariableDescription& Desc, int32 Index)
|
|
47
|
+
{
|
|
48
|
+
TSharedPtr<FJsonObject> V = MakeShared<FJsonObject>();
|
|
49
|
+
V->SetNumberField(TEXT("index"), Index);
|
|
50
|
+
V->SetStringField(TEXT("name"), Desc.VarName.ToString());
|
|
51
|
+
V->SetStringField(TEXT("friendlyName"), Desc.FriendlyName);
|
|
52
|
+
V->SetStringField(TEXT("guid"), Desc.VarGuid.ToString());
|
|
53
|
+
V->SetStringField(TEXT("type"), StructFieldTypeLabel(Desc));
|
|
54
|
+
return V;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Resolve a member's GUID from an explicit "fieldGuid" param, or by matching a
|
|
58
|
+
// "fieldName" param against either the friendly (display) name or the internal
|
|
59
|
+
// VarName. Returns false (invalid Guid) if unresolved.
|
|
60
|
+
static bool ResolveStructFieldGuid(const TSharedPtr<FJsonObject>& Params, UUserDefinedStruct* Struct, FGuid& OutGuid)
|
|
61
|
+
{
|
|
62
|
+
const FString GuidStr = OptionalString(Params, TEXT("fieldGuid"));
|
|
63
|
+
if (!GuidStr.IsEmpty())
|
|
64
|
+
{
|
|
65
|
+
return FGuid::Parse(GuidStr, OutGuid);
|
|
66
|
+
}
|
|
67
|
+
const FString FieldName = OptionalString(Params, TEXT("fieldName"));
|
|
68
|
+
if (FieldName.IsEmpty()) return false;
|
|
69
|
+
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
70
|
+
{
|
|
71
|
+
if (Desc.FriendlyName == FieldName || Desc.VarName.ToString() == FieldName)
|
|
72
|
+
{
|
|
73
|
+
OutGuid = Desc.VarGuid;
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
TSharedPtr<FJsonValue> FAssetHandlers::CreateUserDefinedStruct(const TSharedPtr<FJsonObject>& Params)
|
|
81
|
+
{
|
|
82
|
+
FString Name;
|
|
83
|
+
if (auto Err = RequireString(Params, TEXT("name"), Name)) return Err;
|
|
84
|
+
const FString PackagePath = OptionalString(Params, TEXT("packagePath"), TEXT("/Game"));
|
|
85
|
+
const FString OnConflict = OptionalString(Params, TEXT("onConflict"), TEXT("skip"));
|
|
86
|
+
|
|
87
|
+
if (auto Existing = MCPCheckAssetExists(PackagePath, Name, OnConflict, TEXT("UserDefinedStruct")))
|
|
88
|
+
{
|
|
89
|
+
return Existing;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
UPackage* Package = CreatePackage(*(PackagePath + TEXT("/") + Name));
|
|
93
|
+
if (!Package) return MCPError(FString::Printf(TEXT("Failed to create package for %s"), *Name));
|
|
94
|
+
|
|
95
|
+
UUserDefinedStruct* Struct = FStructureEditorUtils::CreateUserDefinedStruct(Package, FName(*Name), RF_Public | RF_Standalone);
|
|
96
|
+
if (!Struct) return MCPError(TEXT("Failed to create UserDefinedStruct"));
|
|
97
|
+
|
|
98
|
+
// CreateUserDefinedStruct seeds one default member. Capture its GUID so we can
|
|
99
|
+
// strip it once the caller's own fields are in (a struct must never hit zero
|
|
100
|
+
// members, so we add first and remove the seed last).
|
|
101
|
+
TArray<FGuid> SeedGuids;
|
|
102
|
+
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
103
|
+
{
|
|
104
|
+
SeedGuids.Add(Desc.VarGuid);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const TArray<TSharedPtr<FJsonValue>>* Fields = nullptr;
|
|
108
|
+
int32 AddedCount = 0;
|
|
109
|
+
if (Params->TryGetArrayField(TEXT("fields"), Fields) && Fields)
|
|
110
|
+
{
|
|
111
|
+
for (const TSharedPtr<FJsonValue>& Entry : *Fields)
|
|
112
|
+
{
|
|
113
|
+
const TSharedPtr<FJsonObject>* Obj = nullptr;
|
|
114
|
+
if (!Entry->TryGetObject(Obj) || !Obj) continue;
|
|
115
|
+
const FString FieldName = OptionalString(*Obj, TEXT("name"));
|
|
116
|
+
const FString FieldType = OptionalString(*Obj, TEXT("type"), TEXT("bool"));
|
|
117
|
+
if (FieldName.IsEmpty()) continue;
|
|
118
|
+
|
|
119
|
+
// Snapshot GUIDs so we can identify the member AddVariable appends.
|
|
120
|
+
TSet<FGuid> Before;
|
|
121
|
+
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct)) Before.Add(D.VarGuid);
|
|
122
|
+
|
|
123
|
+
if (!FStructureEditorUtils::AddVariable(Struct, FBlueprintHandlers::MakePinType(FieldType))) continue;
|
|
124
|
+
|
|
125
|
+
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct))
|
|
126
|
+
{
|
|
127
|
+
if (!Before.Contains(D.VarGuid))
|
|
128
|
+
{
|
|
129
|
+
FStructureEditorUtils::RenameVariable(Struct, D.VarGuid, FieldName);
|
|
130
|
+
++AddedCount;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Only drop the seed member if the caller supplied replacements; otherwise the
|
|
138
|
+
// struct keeps the default so it never has zero members.
|
|
139
|
+
if (AddedCount > 0)
|
|
140
|
+
{
|
|
141
|
+
for (const FGuid& Seed : SeedGuids)
|
|
142
|
+
{
|
|
143
|
+
FStructureEditorUtils::RemoveVariable(Struct, Seed);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
FAssetRegistryModule::AssetCreated(Struct);
|
|
148
|
+
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
149
|
+
|
|
150
|
+
TSharedPtr<FJsonObject> Res = MCPSuccess();
|
|
151
|
+
MCPSetCreated(Res);
|
|
152
|
+
Res->SetStringField(TEXT("path"), Struct->GetPathName());
|
|
153
|
+
Res->SetStringField(TEXT("name"), Name);
|
|
154
|
+
TArray<TSharedPtr<FJsonValue>> FieldList;
|
|
155
|
+
int32 Idx = 0;
|
|
156
|
+
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
157
|
+
{
|
|
158
|
+
FieldList.Add(MakeShared<FJsonValueObject>(StructFieldToJson(Desc, Idx++)));
|
|
159
|
+
}
|
|
160
|
+
Res->SetArrayField(TEXT("fields"), FieldList);
|
|
161
|
+
Res->SetNumberField(TEXT("count"), FieldList.Num());
|
|
162
|
+
MCPSetDeleteAssetRollback(Res, Struct->GetPathName());
|
|
163
|
+
return MCPResult(Res);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
TSharedPtr<FJsonValue> FAssetHandlers::ListStructFields(const TSharedPtr<FJsonObject>& Params)
|
|
167
|
+
{
|
|
168
|
+
FString AssetPath;
|
|
169
|
+
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
170
|
+
|
|
171
|
+
UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(UEditorAssetLibrary::LoadAsset(AssetPath));
|
|
172
|
+
if (!Struct) return MCPError(FString::Printf(TEXT("UserDefinedStruct not found (native structs are not editable): %s"), *AssetPath));
|
|
173
|
+
|
|
174
|
+
TSharedPtr<FJsonObject> Res = MCPSuccess();
|
|
175
|
+
Res->SetStringField(TEXT("path"), AssetPath);
|
|
176
|
+
Res->SetStringField(TEXT("name"), Struct->GetName());
|
|
177
|
+
|
|
178
|
+
TArray<TSharedPtr<FJsonValue>> FieldList;
|
|
179
|
+
int32 Idx = 0;
|
|
180
|
+
for (const FStructVariableDescription& Desc : FStructureEditorUtils::GetVarDesc(Struct))
|
|
181
|
+
{
|
|
182
|
+
FieldList.Add(MakeShared<FJsonValueObject>(StructFieldToJson(Desc, Idx++)));
|
|
183
|
+
}
|
|
184
|
+
Res->SetArrayField(TEXT("fields"), FieldList);
|
|
185
|
+
Res->SetNumberField(TEXT("count"), FieldList.Num());
|
|
186
|
+
return MCPResult(Res);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
TSharedPtr<FJsonValue> FAssetHandlers::EditUserDefinedStruct(const TSharedPtr<FJsonObject>& Params)
|
|
190
|
+
{
|
|
191
|
+
FString AssetPath;
|
|
192
|
+
if (auto Err = RequireStringAlt(Params, TEXT("assetPath"), TEXT("path"), AssetPath)) return Err;
|
|
193
|
+
FString Op;
|
|
194
|
+
if (auto Err = RequireString(Params, TEXT("op"), Op)) return Err;
|
|
195
|
+
|
|
196
|
+
UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(UEditorAssetLibrary::LoadAsset(AssetPath));
|
|
197
|
+
if (!Struct) return MCPError(FString::Printf(TEXT("UserDefinedStruct not found (native structs are not editable): %s"), *AssetPath));
|
|
198
|
+
|
|
199
|
+
TSharedPtr<FJsonObject> Res = MCPSuccess();
|
|
200
|
+
Res->SetStringField(TEXT("path"), AssetPath);
|
|
201
|
+
Res->SetStringField(TEXT("op"), Op);
|
|
202
|
+
|
|
203
|
+
if (Op == TEXT("add_field"))
|
|
204
|
+
{
|
|
205
|
+
const FString FieldType = OptionalString(Params, TEXT("type"), TEXT("bool"));
|
|
206
|
+
FString FieldName = OptionalString(Params, TEXT("fieldName"));
|
|
207
|
+
if (FieldName.IsEmpty()) FieldName = OptionalString(Params, TEXT("newDisplayName"));
|
|
208
|
+
|
|
209
|
+
TSet<FGuid> Before;
|
|
210
|
+
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct)) Before.Add(D.VarGuid);
|
|
211
|
+
|
|
212
|
+
if (!FStructureEditorUtils::AddVariable(Struct, FBlueprintHandlers::MakePinType(FieldType)))
|
|
213
|
+
{
|
|
214
|
+
return MCPError(FString::Printf(TEXT("Failed to add field of type '%s'"), *FieldType));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
FGuid NewGuid;
|
|
218
|
+
for (const FStructVariableDescription& D : FStructureEditorUtils::GetVarDesc(Struct))
|
|
219
|
+
{
|
|
220
|
+
if (!Before.Contains(D.VarGuid)) { NewGuid = D.VarGuid; break; }
|
|
221
|
+
}
|
|
222
|
+
if (NewGuid.IsValid() && !FieldName.IsEmpty())
|
|
223
|
+
{
|
|
224
|
+
FStructureEditorUtils::RenameVariable(Struct, NewGuid, FieldName);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
228
|
+
MCPSetUpdated(Res);
|
|
229
|
+
if (const FStructVariableDescription* Desc = FStructureEditorUtils::GetVarDescByGuid(Struct, NewGuid))
|
|
230
|
+
{
|
|
231
|
+
Res->SetObjectField(TEXT("field"), StructFieldToJson(*Desc, INDEX_NONE));
|
|
232
|
+
}
|
|
233
|
+
Res->SetNumberField(TEXT("count"), FStructureEditorUtils::GetVarDesc(Struct).Num());
|
|
234
|
+
|
|
235
|
+
TSharedPtr<FJsonObject> RbPayload = MakeShared<FJsonObject>();
|
|
236
|
+
RbPayload->SetStringField(TEXT("assetPath"), AssetPath);
|
|
237
|
+
RbPayload->SetStringField(TEXT("op"), TEXT("remove_field"));
|
|
238
|
+
RbPayload->SetStringField(TEXT("fieldGuid"), NewGuid.ToString());
|
|
239
|
+
MCPSetRollback(Res, TEXT("edit_user_defined_struct"), RbPayload);
|
|
240
|
+
return MCPResult(Res);
|
|
241
|
+
}
|
|
242
|
+
else if (Op == TEXT("rename_field"))
|
|
243
|
+
{
|
|
244
|
+
FString NewDisplayName;
|
|
245
|
+
if (auto Err = RequireString(Params, TEXT("newDisplayName"), NewDisplayName)) return Err;
|
|
246
|
+
FGuid Guid;
|
|
247
|
+
if (!ResolveStructFieldGuid(Params, Struct, Guid))
|
|
248
|
+
return MCPError(TEXT("Could not resolve field (pass 'fieldGuid', or 'fieldName' matching a friendly or internal name)"));
|
|
249
|
+
|
|
250
|
+
const FStructVariableDescription* Before = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid);
|
|
251
|
+
const FString PrevName = Before ? Before->FriendlyName : FString();
|
|
252
|
+
|
|
253
|
+
// RenameVariable rewrites only the friendly name; the member GUID is
|
|
254
|
+
// preserved, so Blueprint pins and DataTable rows keyed off it survive.
|
|
255
|
+
if (!FStructureEditorUtils::RenameVariable(Struct, Guid, NewDisplayName))
|
|
256
|
+
{
|
|
257
|
+
return MCPError(FString::Printf(TEXT("Rename to '%s' failed (name invalid or duplicate)"), *NewDisplayName));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
261
|
+
MCPSetUpdated(Res);
|
|
262
|
+
if (const FStructVariableDescription* After = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid))
|
|
263
|
+
{
|
|
264
|
+
Res->SetObjectField(TEXT("field"), StructFieldToJson(*After, INDEX_NONE));
|
|
265
|
+
}
|
|
266
|
+
Res->SetStringField(TEXT("previousDisplayName"), PrevName);
|
|
267
|
+
|
|
268
|
+
TSharedPtr<FJsonObject> RbPayload = MakeShared<FJsonObject>();
|
|
269
|
+
RbPayload->SetStringField(TEXT("assetPath"), AssetPath);
|
|
270
|
+
RbPayload->SetStringField(TEXT("op"), TEXT("rename_field"));
|
|
271
|
+
RbPayload->SetStringField(TEXT("fieldGuid"), Guid.ToString());
|
|
272
|
+
RbPayload->SetStringField(TEXT("newDisplayName"), PrevName);
|
|
273
|
+
MCPSetRollback(Res, TEXT("edit_user_defined_struct"), RbPayload);
|
|
274
|
+
return MCPResult(Res);
|
|
275
|
+
}
|
|
276
|
+
else if (Op == TEXT("set_field_type"))
|
|
277
|
+
{
|
|
278
|
+
FString FieldType;
|
|
279
|
+
if (auto Err = RequireString(Params, TEXT("type"), FieldType)) return Err;
|
|
280
|
+
FGuid Guid;
|
|
281
|
+
if (!ResolveStructFieldGuid(Params, Struct, Guid))
|
|
282
|
+
return MCPError(TEXT("Could not resolve field (pass 'fieldGuid', or 'fieldName' matching a friendly or internal name)"));
|
|
283
|
+
|
|
284
|
+
if (!FStructureEditorUtils::ChangeVariableType(Struct, Guid, FBlueprintHandlers::MakePinType(FieldType)))
|
|
285
|
+
{
|
|
286
|
+
return MCPError(FString::Printf(TEXT("Failed to change field type to '%s'"), *FieldType));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
290
|
+
MCPSetUpdated(Res);
|
|
291
|
+
if (const FStructVariableDescription* After = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid))
|
|
292
|
+
{
|
|
293
|
+
Res->SetObjectField(TEXT("field"), StructFieldToJson(*After, INDEX_NONE));
|
|
294
|
+
}
|
|
295
|
+
return MCPResult(Res);
|
|
296
|
+
}
|
|
297
|
+
else if (Op == TEXT("remove_field"))
|
|
298
|
+
{
|
|
299
|
+
FGuid Guid;
|
|
300
|
+
if (!ResolveStructFieldGuid(Params, Struct, Guid))
|
|
301
|
+
return MCPError(TEXT("Could not resolve field (pass 'fieldGuid', or 'fieldName' matching a friendly or internal name)"));
|
|
302
|
+
if (FStructureEditorUtils::GetVarDesc(Struct).Num() <= 1)
|
|
303
|
+
return MCPError(TEXT("Cannot remove the last field; a UserDefinedStruct must keep at least one member"));
|
|
304
|
+
|
|
305
|
+
TSharedPtr<FJsonObject> Removed;
|
|
306
|
+
if (const FStructVariableDescription* Desc = FStructureEditorUtils::GetVarDescByGuid(Struct, Guid))
|
|
307
|
+
{
|
|
308
|
+
Removed = StructFieldToJson(*Desc, INDEX_NONE);
|
|
309
|
+
}
|
|
310
|
+
if (!FStructureEditorUtils::RemoveVariable(Struct, Guid))
|
|
311
|
+
{
|
|
312
|
+
return MCPError(TEXT("RemoveVariable failed"));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
UEditorAssetLibrary::SaveLoadedAsset(Struct);
|
|
316
|
+
MCPSetUpdated(Res);
|
|
317
|
+
if (Removed.IsValid()) Res->SetObjectField(TEXT("removed"), Removed);
|
|
318
|
+
Res->SetNumberField(TEXT("count"), FStructureEditorUtils::GetVarDesc(Struct).Num());
|
|
319
|
+
return MCPResult(Res);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return MCPError(FString::Printf(TEXT("Unknown op '%s' (expected add_field|rename_field|set_field_type|remove_field)"), *Op));
|
|
323
|
+
}
|