More MCP refactoring
This commit is contained in:
@@ -91,7 +91,9 @@ public:
|
||||
*Blueprint, *OldParentName, *NewParentClassObj->GetName());
|
||||
|
||||
// Perform reparent
|
||||
BP->PreEditChange(nullptr);
|
||||
BP->ParentClass = NewParentClassObj;
|
||||
BP->PostEditChange();
|
||||
|
||||
// Refresh all nodes to pick up new parent's functions/variables
|
||||
FBlueprintEditorUtils::RefreshAllNodes(BP);
|
||||
@@ -187,24 +189,7 @@ public:
|
||||
EBlueprintType BlueprintTypeEnum = BPTYPE_Normal;
|
||||
if (!BlueprintType.IsEmpty())
|
||||
{
|
||||
if (BlueprintType == TEXT("Interface"))
|
||||
{
|
||||
BlueprintTypeEnum = BPTYPE_Interface;
|
||||
}
|
||||
else if (BlueprintType == TEXT("FunctionLibrary"))
|
||||
{
|
||||
BlueprintTypeEnum = BPTYPE_FunctionLibrary;
|
||||
}
|
||||
else if (BlueprintType == TEXT("MacroLibrary"))
|
||||
{
|
||||
BlueprintTypeEnum = BPTYPE_MacroLibrary;
|
||||
}
|
||||
else if (BlueprintType != TEXT("Normal"))
|
||||
{
|
||||
return MCPUtils::MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid blueprintType '%s'. Valid values: Normal, Interface, FunctionLibrary, MacroLibrary"),
|
||||
*BlueprintType));
|
||||
}
|
||||
if (!MCPUtils::StringToEnum(BlueprintType, BlueprintTypeEnum, Result, TEXT("BPTYPE_"))) return;
|
||||
}
|
||||
|
||||
// For Interface type, parent must be UInterface
|
||||
|
||||
@@ -161,40 +161,13 @@ public:
|
||||
bool bSaved = MCPUtils::SaveMaterialPackage(MaterialObj);
|
||||
|
||||
|
||||
// Map domain back to string for response
|
||||
auto DomainToString = [](EMaterialDomain InDomain) -> FString
|
||||
{
|
||||
switch (InDomain)
|
||||
{
|
||||
case MD_Surface: return TEXT("Surface");
|
||||
case MD_DeferredDecal: return TEXT("DeferredDecal");
|
||||
case MD_LightFunction: return TEXT("LightFunction");
|
||||
case MD_Volume: return TEXT("Volume");
|
||||
case MD_PostProcess: return TEXT("PostProcess");
|
||||
case MD_UI: return TEXT("UI");
|
||||
default: return TEXT("Surface");
|
||||
}
|
||||
};
|
||||
|
||||
auto BlendModeToString = [](EBlendMode Mode) -> FString
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case BLEND_Opaque: return TEXT("Opaque");
|
||||
case BLEND_Masked: return TEXT("Masked");
|
||||
case BLEND_Translucent: return TEXT("Translucent");
|
||||
case BLEND_Additive: return TEXT("Additive");
|
||||
case BLEND_Modulate: return TEXT("Modulate");
|
||||
default: return TEXT("Opaque");
|
||||
}
|
||||
};
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Created Material '%s' (saved: %s)"),
|
||||
*Name, bSaved ? TEXT("true") : TEXT("false"));
|
||||
|
||||
Result->SetStringField(TEXT("path"), MaterialObj->GetPathName());
|
||||
Result->SetStringField(TEXT("domain"), DomainToString(MaterialObj->MaterialDomain));
|
||||
Result->SetStringField(TEXT("blendMode"), BlendModeToString(MaterialObj->BlendMode));
|
||||
Result->SetStringField(TEXT("domain"), MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_")));
|
||||
Result->SetStringField(TEXT("blendMode"), MCPUtils::EnumToString(MaterialObj->BlendMode, TEXT("BLEND_")));
|
||||
Result->SetBoolField(TEXT("twoSided"), MaterialObj->TwoSided != 0);
|
||||
Result->SetBoolField(TEXT("saved"), bSaved);
|
||||
}
|
||||
@@ -240,72 +213,14 @@ public:
|
||||
FString OldValue;
|
||||
FString NewValue;
|
||||
|
||||
// Helper lambdas for converting enum values to strings
|
||||
auto DomainToString = [](EMaterialDomain Domain) -> FString
|
||||
{
|
||||
switch (Domain)
|
||||
{
|
||||
case MD_Surface: return TEXT("Surface");
|
||||
case MD_DeferredDecal: return TEXT("DeferredDecal");
|
||||
case MD_LightFunction: return TEXT("LightFunction");
|
||||
case MD_Volume: return TEXT("Volume");
|
||||
case MD_PostProcess: return TEXT("PostProcess");
|
||||
case MD_UI: return TEXT("UI");
|
||||
default: return TEXT("Unknown");
|
||||
}
|
||||
};
|
||||
|
||||
auto BlendModeToString = [](EBlendMode Mode) -> FString
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case BLEND_Opaque: return TEXT("Opaque");
|
||||
case BLEND_Masked: return TEXT("Masked");
|
||||
case BLEND_Translucent: return TEXT("Translucent");
|
||||
case BLEND_Additive: return TEXT("Additive");
|
||||
case BLEND_Modulate: return TEXT("Modulate");
|
||||
default: return TEXT("Unknown");
|
||||
}
|
||||
};
|
||||
|
||||
auto ShadingModelToString = [](EMaterialShadingModel Model) -> FString
|
||||
{
|
||||
switch (Model)
|
||||
{
|
||||
case MSM_Unlit: return TEXT("Unlit");
|
||||
case MSM_DefaultLit: return TEXT("DefaultLit");
|
||||
case MSM_Subsurface: return TEXT("Subsurface");
|
||||
case MSM_PreintegratedSkin: return TEXT("PreintegratedSkin");
|
||||
case MSM_ClearCoat: return TEXT("ClearCoat");
|
||||
case MSM_SubsurfaceProfile: return TEXT("SubsurfaceProfile");
|
||||
case MSM_TwoSidedFoliage: return TEXT("TwoSidedFoliage");
|
||||
case MSM_Hair: return TEXT("Hair");
|
||||
case MSM_Cloth: return TEXT("Cloth");
|
||||
case MSM_Eye: return TEXT("Eye");
|
||||
default: return TEXT("DefaultLit");
|
||||
}
|
||||
};
|
||||
|
||||
if (Property == TEXT("domain"))
|
||||
{
|
||||
FString ValueStr = Json->GetStringField(TEXT("value"));
|
||||
OldValue = DomainToString(MaterialObj->MaterialDomain);
|
||||
OldValue = MCPUtils::EnumToString(MaterialObj->MaterialDomain, TEXT("MD_"));
|
||||
|
||||
EMaterialDomain NewDomain = MaterialObj->MaterialDomain;
|
||||
if (ValueStr == TEXT("Surface")) NewDomain = MD_Surface;
|
||||
else if (ValueStr == TEXT("DeferredDecal")) NewDomain = MD_DeferredDecal;
|
||||
else if (ValueStr == TEXT("LightFunction")) NewDomain = MD_LightFunction;
|
||||
else if (ValueStr == TEXT("Volume")) NewDomain = MD_Volume;
|
||||
else if (ValueStr == TEXT("PostProcess")) NewDomain = MD_PostProcess;
|
||||
else if (ValueStr == TEXT("UI")) NewDomain = MD_UI;
|
||||
else
|
||||
{
|
||||
return MCPUtils::MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid domain '%s'. Valid values: Surface, DeferredDecal, LightFunction, Volume, PostProcess, UI"),
|
||||
*ValueStr));
|
||||
}
|
||||
|
||||
NewValue = ValueStr;
|
||||
EMaterialDomain NewDomain;
|
||||
if (!MCPUtils::StringToEnum(ValueStr, NewDomain, Result, TEXT("MD_"))) return;
|
||||
NewValue = MCPUtils::EnumToString(NewDomain, TEXT("MD_"));
|
||||
|
||||
if (!DryRun)
|
||||
{
|
||||
@@ -317,22 +232,11 @@ public:
|
||||
else if (Property == TEXT("blendMode"))
|
||||
{
|
||||
FString ValueStr = Json->GetStringField(TEXT("value"));
|
||||
OldValue = BlendModeToString(MaterialObj->BlendMode);
|
||||
OldValue = MCPUtils::EnumToString(MaterialObj->BlendMode, TEXT("BLEND_"));
|
||||
|
||||
EBlendMode NewBlend = MaterialObj->BlendMode;
|
||||
if (ValueStr == TEXT("Opaque")) NewBlend = BLEND_Opaque;
|
||||
else if (ValueStr == TEXT("Masked")) NewBlend = BLEND_Masked;
|
||||
else if (ValueStr == TEXT("Translucent")) NewBlend = BLEND_Translucent;
|
||||
else if (ValueStr == TEXT("Additive")) NewBlend = BLEND_Additive;
|
||||
else if (ValueStr == TEXT("Modulate")) NewBlend = BLEND_Modulate;
|
||||
else
|
||||
{
|
||||
return MCPUtils::MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid blendMode '%s'. Valid values: Opaque, Masked, Translucent, Additive, Modulate"),
|
||||
*ValueStr));
|
||||
}
|
||||
|
||||
NewValue = ValueStr;
|
||||
EBlendMode NewBlend;
|
||||
if (!MCPUtils::StringToEnum(ValueStr, NewBlend, Result, TEXT("BLEND_"))) return;
|
||||
NewValue = MCPUtils::EnumToString(NewBlend, TEXT("BLEND_"));
|
||||
|
||||
if (!DryRun)
|
||||
{
|
||||
@@ -357,27 +261,11 @@ public:
|
||||
else if (Property == TEXT("shadingModel"))
|
||||
{
|
||||
FString ValueStr = Json->GetStringField(TEXT("value"));
|
||||
OldValue = ShadingModelToString(MaterialObj->GetShadingModels().GetFirstShadingModel());
|
||||
OldValue = MCPUtils::EnumToString(MaterialObj->GetShadingModels().GetFirstShadingModel(), TEXT("MSM_"));
|
||||
|
||||
EMaterialShadingModel NewModel = MSM_DefaultLit;
|
||||
if (ValueStr == TEXT("Unlit")) NewModel = MSM_Unlit;
|
||||
else if (ValueStr == TEXT("DefaultLit")) NewModel = MSM_DefaultLit;
|
||||
else if (ValueStr == TEXT("Subsurface")) NewModel = MSM_Subsurface;
|
||||
else if (ValueStr == TEXT("PreintegratedSkin")) NewModel = MSM_PreintegratedSkin;
|
||||
else if (ValueStr == TEXT("ClearCoat")) NewModel = MSM_ClearCoat;
|
||||
else if (ValueStr == TEXT("SubsurfaceProfile")) NewModel = MSM_SubsurfaceProfile;
|
||||
else if (ValueStr == TEXT("TwoSidedFoliage")) NewModel = MSM_TwoSidedFoliage;
|
||||
else if (ValueStr == TEXT("Hair")) NewModel = MSM_Hair;
|
||||
else if (ValueStr == TEXT("Cloth")) NewModel = MSM_Cloth;
|
||||
else if (ValueStr == TEXT("Eye")) NewModel = MSM_Eye;
|
||||
else
|
||||
{
|
||||
return MCPUtils::MakeErrorJson(Result, FString::Printf(
|
||||
TEXT("Invalid shadingModel '%s'. Valid values: Unlit, DefaultLit, Subsurface, PreintegratedSkin, ClearCoat, SubsurfaceProfile, TwoSidedFoliage, Hair, Cloth, Eye"),
|
||||
*ValueStr));
|
||||
}
|
||||
|
||||
NewValue = ValueStr;
|
||||
EMaterialShadingModel NewModel;
|
||||
if (!MCPUtils::StringToEnum(ValueStr, NewModel, Result, TEXT("MSM_"))) return;
|
||||
NewValue = MCPUtils::EnumToString(NewModel, TEXT("MSM_"));
|
||||
|
||||
if (!DryRun)
|
||||
{
|
||||
|
||||
@@ -112,7 +112,9 @@ public:
|
||||
{
|
||||
if (PinInfo.IsValid() && PinInfo->PinName.ToString().Equals(ParamName, ESearchCase::IgnoreCase))
|
||||
{
|
||||
EntryNode->PreEditChange(nullptr);
|
||||
PinInfo->PinType = NewPinType;
|
||||
EntryNode->PostEditChange();
|
||||
bPinFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -332,6 +332,7 @@ public:
|
||||
|
||||
// Update properties
|
||||
int32 ChangedCount = 0;
|
||||
TransNode->PreEditChange(nullptr);
|
||||
|
||||
if (Json->HasField(TEXT("crossfadeDuration")))
|
||||
{
|
||||
@@ -363,6 +364,7 @@ public:
|
||||
{
|
||||
return MCPUtils::MakeErrorJson(Result, TEXT("No properties to update. Provide at least one of: crossfadeDuration, blendMode, priorityOrder, logicType, bBidirectional"));
|
||||
}
|
||||
TransNode->PostEditChange();
|
||||
|
||||
// Compile and save
|
||||
FKismetEditorUtilities::CompileBlueprint(AnimBP);
|
||||
|
||||
@@ -103,15 +103,7 @@ public:
|
||||
WarningsArr.Add(MakeShared<FJsonValueObject>(Msg));
|
||||
}
|
||||
|
||||
FString StatusStr;
|
||||
switch (BP->Status)
|
||||
{
|
||||
case BS_UpToDate: StatusStr = TEXT("UpToDate"); break;
|
||||
case BS_Dirty: StatusStr = TEXT("Dirty"); break;
|
||||
case BS_Error: StatusStr = TEXT("Error"); break;
|
||||
case BS_Unknown: StatusStr = TEXT("Unknown"); break;
|
||||
default: StatusStr = FString::Printf(TEXT("Status_%d"), (int32)BP->Status); break;
|
||||
}
|
||||
FString StatusStr = MCPUtils::EnumToString((EBlueprintStatus)BP->Status, TEXT("BS_"));
|
||||
|
||||
bool bIsValid = (BP->Status == BS_UpToDate) && (ErrorsArr.Num() == 0);
|
||||
|
||||
@@ -129,17 +121,14 @@ public:
|
||||
virtual void Handle(const FJsonObject* Json, FJsonObject* Result) override
|
||||
{
|
||||
MCPAssets<UBlueprint> Finder;
|
||||
Finder.Scan<UBlueprint>().Scan<UWorld>();
|
||||
Finder.Scan<UBlueprint>().Scan<UWorld>().Errors(Result);
|
||||
if (!Blueprint.IsEmpty())
|
||||
{
|
||||
if (!Finder.Exact(Blueprint).ETwo().Info() || Finder.AllData().IsEmpty())
|
||||
{
|
||||
return MCPUtils::MakeErrorJson(Result, FString::Printf(TEXT("Blueprint '%s' not found."), *Blueprint));
|
||||
}
|
||||
if (!Finder.Exact(Blueprint).ENone().ETwo().Info()) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Finder.Substring(Query).Info();
|
||||
if (!Finder.Substring(Query).Info()) return;
|
||||
}
|
||||
|
||||
const TArray<FAssetData>& MatchingAssets = Finder.AllData();
|
||||
|
||||
@@ -158,6 +158,7 @@ public:
|
||||
}
|
||||
|
||||
// Directly modify the variable type in the description array.
|
||||
BP->PreEditChange(nullptr);
|
||||
for (FBPVariableDescription& Var : BP->NewVariables)
|
||||
{
|
||||
if (Var.VarName == FName(*Variable))
|
||||
@@ -166,6 +167,7 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
BP->PostEditChange();
|
||||
|
||||
// Save
|
||||
bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
|
||||
@@ -563,6 +565,8 @@ public:
|
||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: SetVariableMetadata on '%s.%s' — %d field(s) changed"),
|
||||
*Blueprint, *Variable, Changes.Num());
|
||||
|
||||
BP->PreEditChange(nullptr);
|
||||
BP->PostEditChange();
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
|
||||
bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
|
||||
|
||||
|
||||
@@ -67,7 +67,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct MCPErrorCallback;
|
||||
// ----- Error callback -----
|
||||
|
||||
struct MCPErrorCallback
|
||||
{
|
||||
TFunction<void(const FString&)> Func;
|
||||
|
||||
|
||||
MCPErrorCallback(std::nullptr_t);
|
||||
MCPErrorCallback(FString& OutError);
|
||||
MCPErrorCallback(FJsonObject* Result);
|
||||
|
||||
void SetError(const FString& Msg) const { Func(Msg); }
|
||||
};
|
||||
|
||||
// Stateless utility functions used by MCP handlers and the MCP server.
|
||||
// This is effectively a namespace — all methods are static.
|
||||
@@ -97,6 +109,34 @@ public:
|
||||
static void CopyJsonFields(const FJsonObject* Source, FJsonObject* Dest);
|
||||
static FString UrlDecode(const FString& EncodedString);
|
||||
|
||||
// ----- Enum helpers -----
|
||||
// Convert enum value to string. If Prefix is specified, strip "Prefix_" from the front.
|
||||
template<typename T>
|
||||
static FString EnumToString(T Value, const FString& Prefix = FString())
|
||||
{
|
||||
UEnum* Enum = StaticEnum<T>();
|
||||
FString Full = Enum->GetNameStringByValue((int64)Value);
|
||||
if (!Prefix.IsEmpty() && Full.StartsWith(Prefix))
|
||||
return Full.Mid(Prefix.Len());
|
||||
return Full;
|
||||
}
|
||||
|
||||
// Convert string to enum value. If Prefix is specified, prepend it before lookup.
|
||||
// Returns false and sets error if the string doesn't match any value.
|
||||
template<typename T>
|
||||
static bool StringToEnum(const FString& Str, T& OutValue, MCPErrorCallback Error, const FString& Prefix = FString())
|
||||
{
|
||||
UEnum* Enum = StaticEnum<T>();
|
||||
int64 Value = Enum->GetValueByNameString(Prefix + Str);
|
||||
if (Value == INDEX_NONE)
|
||||
{
|
||||
Error.SetError(FString::Printf(TEXT("Invalid value '%s' for %s"), *Str, *Enum->GetName()));
|
||||
return false;
|
||||
}
|
||||
OutValue = (T)Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----- Blueprint helpers -----
|
||||
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
|
||||
static TArray<UEdGraph*> AllGraphsNamed(UBlueprint* BP, const FString& Name);
|
||||
@@ -155,17 +195,3 @@ public:
|
||||
private:
|
||||
static FString SetPropertyFromJson(void* Container, FProperty* Prop, const FString& FieldName, const FJsonObject* Json);
|
||||
};
|
||||
|
||||
// ----- Error callback -----
|
||||
|
||||
struct MCPErrorCallback
|
||||
{
|
||||
TFunction<void(const FString&)> Func;
|
||||
|
||||
|
||||
MCPErrorCallback(std::nullptr_t);
|
||||
MCPErrorCallback(FString& OutError);
|
||||
MCPErrorCallback(FJsonObject* Result);
|
||||
|
||||
void SetError(const FString& Msg) const { Func(Msg); }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user