diff --git a/Content/Testing/M_Test.uasset b/Content/Testing/M_Test.uasset index c782a435..3a7352d4 100644 --- a/Content/Testing/M_Test.uasset +++ b/Content/Testing/M_Test.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ed02979e67523f6aefb439525a4a58d5b4a2984a544a34e99fc6d134b4aeba4 -size 26812 +oid sha256:c69da868cefe9949b5bbb2d3a91491c463616835dd39dbe2e2bdcabdda0088fc +size 13809 diff --git a/Plugins/BlueprintMCP/Deprecated/BlueprintSerialization.cpp b/Plugins/BlueprintMCP/Deprecated/BlueprintSerialization.cpp new file mode 100644 index 00000000..e91023fc --- /dev/null +++ b/Plugins/BlueprintMCP/Deprecated/BlueprintSerialization.cpp @@ -0,0 +1,450 @@ +#include "MCPUtils.h" +#include "Dom/JsonValue.h" +#include "Engine/Blueprint.h" +#include "EdGraph/EdGraph.h" +#include "EdGraph/EdGraphNode.h" +#include "EdGraph/EdGraphPin.h" +#include "K2Node_CallFunction.h" +#include "K2Node_Event.h" +#include "K2Node_CustomEvent.h" +#include "K2Node_FunctionEntry.h" +#include "K2Node_EditablePinBase.h" +#include "K2Node_VariableGet.h" +#include "K2Node_VariableSet.h" +#include "K2Node_MacroInstance.h" +#include "K2Node_DynamicCast.h" +#include "K2Node_CallParentFunction.h" +#include "K2Node_IfThenElse.h" + +// Animation Blueprint support +#include "Animation/AnimBlueprint.h" +#include "AnimGraphNode_StateMachine.h" +#include "AnimGraphNode_AssetPlayerBase.h" +#include "AnimGraphNode_SequencePlayer.h" +#include "AnimGraphNode_BlendSpacePlayer.h" +#include "AnimGraphNode_Base.h" +#include "AnimStateNode.h" +#include "AnimStateTransitionNode.h" +#include "AnimStateConduitNode.h" +#include "AnimStateEntryNode.h" +#include "AnimationStateMachineGraph.h" + +// Material support +#include "Materials/MaterialExpression.h" +#include "Materials/MaterialExpressionScalarParameter.h" +#include "Materials/MaterialExpressionVectorParameter.h" +#include "Materials/MaterialExpressionTextureSampleParameter2D.h" +#include "Materials/MaterialExpressionStaticSwitchParameter.h" +#include "Materials/MaterialExpressionConstant.h" +#include "Materials/MaterialExpressionConstant3Vector.h" +#include "Materials/MaterialExpressionConstant4Vector.h" +#include "Materials/MaterialExpressionTextureSample.h" +#include "Materials/MaterialExpressionTextureCoordinate.h" +#include "Materials/MaterialExpressionComponentMask.h" +#include "Materials/MaterialExpressionCustom.h" +#include "Materials/MaterialExpressionFunctionInput.h" +#include "Materials/MaterialExpressionFunctionOutput.h" +#include "Materials/MaterialExpressionMaterialFunctionCall.h" +#include "MaterialGraph/MaterialGraphNode.h" + +TSharedRef MCPUtils::SerializeBlueprint(UBlueprint* BP) +{ + TSharedRef J = MakeShared(); + J->SetStringField(TEXT("name"), BP->GetName()); + J->SetStringField(TEXT("path"), BP->GetPackage()->GetName()); + J->SetStringField(TEXT("parentClass"), BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None")); + J->SetStringField(TEXT("blueprintType"), + StaticEnum()->GetNameStringByValue((int64)BP->BlueprintType)); + + // Animation Blueprint detection + if (UAnimBlueprint* AnimBP = Cast(BP)) + { + J->SetBoolField(TEXT("isAnimBlueprint"), true); + if (AnimBP->TargetSkeleton) + { + J->SetStringField(TEXT("targetSkeleton"), AnimBP->TargetSkeleton->GetName()); + J->SetStringField(TEXT("targetSkeletonPath"), AnimBP->TargetSkeleton->GetPathName()); + } + } + + // Variables + TArray> Vars; + for (const FBPVariableDescription& V : BP->NewVariables) + { + TSharedRef VJ = MakeShared(); + VJ->SetStringField(TEXT("name"), V.VarName.ToString()); + VJ->SetStringField(TEXT("type"), V.VarType.PinCategory.ToString()); + if (V.VarType.PinSubCategoryObject.IsValid()) + VJ->SetStringField(TEXT("subtype"), V.VarType.PinSubCategoryObject->GetName()); + VJ->SetBoolField(TEXT("isArray"), V.VarType.IsArray()); + VJ->SetBoolField(TEXT("isSet"), V.VarType.IsSet()); + VJ->SetBoolField(TEXT("isMap"), V.VarType.IsMap()); + VJ->SetStringField(TEXT("category"), V.Category.ToString()); + VJ->SetStringField(TEXT("defaultValue"), V.DefaultValue); + Vars.Add(MakeShared(VJ)); + } + J->SetArrayField(TEXT("variables"), Vars); + + // Interfaces + TArray> Ifaces; + for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces) + { + if (I.Interface) + Ifaces.Add(MakeShared(I.Interface->GetName())); + } + J->SetArrayField(TEXT("interfaces"), Ifaces); + + return J; +} + +TSharedPtr MCPUtils::SerializeNode(UEdGraphNode* Node) +{ + TSharedRef NJ = MakeShared(); + NJ->SetStringField(TEXT("id"), Node->NodeGuid.ToString()); + NJ->SetStringField(TEXT("class"), Node->GetClass()->GetName()); + NJ->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString()); + if (!Node->NodeComment.IsEmpty()) + NJ->SetStringField(TEXT("comment"), Node->NodeComment); + NJ->SetNumberField(TEXT("posX"), Node->NodePosX); + NJ->SetNumberField(TEXT("posY"), Node->NodePosY); + + // Material graph node — extract UMaterialExpression data + if (UMaterialGraphNode* MatNode = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("MaterialExpression")); + if (MatNode->MaterialExpression) + { + TSharedPtr ExprJson = SerializeMaterialExpression(MatNode->MaterialExpression); + if (ExprJson.IsValid()) + { + NJ->SetObjectField(TEXT("expression"), ExprJson); + } + } + } + // Animation Blueprint node types + else if (auto* SMNode = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateMachine")); + if (SMNode->EditorStateMachineGraph) + { + NJ->SetStringField(TEXT("stateMachineName"), SMNode->EditorStateMachineGraph->GetName()); + int32 StateCount = 0, TransitionCount = 0; + for (UEdGraphNode* SubNode : SMNode->EditorStateMachineGraph->Nodes) + { + if (Cast(SubNode)) StateCount++; + else if (Cast(SubNode)) TransitionCount++; + } + NJ->SetNumberField(TEXT("stateCount"), StateCount); + NJ->SetNumberField(TEXT("transitionCount"), TransitionCount); + } + } + else if (auto* SeqPlayer = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimSequencePlayer")); + if (UAnimationAsset* Asset = SeqPlayer->GetAnimationAsset()) + { + NJ->SetStringField(TEXT("animationAsset"), Asset->GetName()); + NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName()); + } + } + else if (auto* BSPlayer = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimBlendSpacePlayer")); + if (UAnimationAsset* Asset = BSPlayer->GetAnimationAsset()) + { + NJ->SetStringField(TEXT("blendSpaceAsset"), Asset->GetName()); + NJ->SetStringField(TEXT("blendSpaceAssetPath"), Asset->GetPathName()); + } + } + else if (auto* AssetPlayer = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimAssetPlayer")); + if (UAnimationAsset* Asset = AssetPlayer->GetAnimationAsset()) + { + NJ->SetStringField(TEXT("animationAsset"), Asset->GetName()); + NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName()); + } + } + else if (Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimNode")); + } + else if (auto* StateNode = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimState")); + NJ->SetStringField(TEXT("stateName"), StateNode->GetStateName()); + NJ->SetBoolField(TEXT("bAlwaysResetOnEntry"), StateNode->bAlwaysResetOnEntry); + } + else if (auto* TransNode = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimTransition")); + if (UAnimStateNode* FromState = Cast(TransNode->GetPreviousState())) + { + NJ->SetStringField(TEXT("fromState"), FromState->GetStateName()); + } + if (UAnimStateNode* ToState = Cast(TransNode->GetNextState())) + { + NJ->SetStringField(TEXT("toState"), ToState->GetStateName()); + } + NJ->SetNumberField(TEXT("crossfadeDuration"), TransNode->CrossfadeDuration); + NJ->SetNumberField(TEXT("blendMode"), (int32)TransNode->BlendMode); + NJ->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder); + NJ->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue()); + NJ->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional); + } + else if (Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimConduit")); + } + else if (Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateEntry")); + } + // K2Node specifics — check CallParentFunction before CallFunction (inheritance) + else if (auto* CPF = Cast(Node)) + { + NJ->SetStringField(TEXT("functionName"), CPF->FunctionReference.GetMemberName().ToString()); + if (CPF->FunctionReference.GetMemberParentClass()) + NJ->SetStringField(TEXT("targetClass"), CPF->FunctionReference.GetMemberParentClass()->GetName()); + NJ->SetStringField(TEXT("nodeType"), TEXT("CallParentFunction")); + } + else if (auto* CF = Cast(Node)) + { + NJ->SetStringField(TEXT("functionName"), CF->FunctionReference.GetMemberName().ToString()); + if (CF->FunctionReference.GetMemberParentClass()) + NJ->SetStringField(TEXT("targetClass"), CF->FunctionReference.GetMemberParentClass()->GetName()); + } + else if (auto* FE = Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("FunctionEntry")); + + // Serialize UserDefinedPins (parameter names and types) + TArray> ParamArr; + for (const TSharedPtr& PinInfo : FE->UserDefinedPins) + { + if (!PinInfo.IsValid()) continue; + TSharedRef ParamJ = MakeShared(); + ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString()); + FString ParamType = PinInfo->PinType.PinCategory.ToString(); + ParamJ->SetStringField(TEXT("type"), ParamType); + if (PinInfo->PinType.PinSubCategoryObject.IsValid()) + ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName()); + else if (ParamType == TEXT("None") || ParamType.IsEmpty()) + ParamJ->SetBoolField(TEXT("typeUnknown"), true); + ParamArr.Add(MakeShared(ParamJ)); + } + NJ->SetArrayField(TEXT("parameters"), ParamArr); + } + else if (auto* Ev = Cast(Node)) + { + NJ->SetStringField(TEXT("eventName"), Ev->EventReference.GetMemberName().ToString()); + NJ->SetStringField(TEXT("nodeType"), Ev->bOverrideFunction ? TEXT("OverrideEvent") : TEXT("Event")); + } + else if (auto* CE = Cast(Node)) + { + NJ->SetStringField(TEXT("eventName"), CE->CustomFunctionName.ToString()); + NJ->SetStringField(TEXT("nodeType"), TEXT("CustomEvent")); + + // Serialize UserDefinedPins (parameter names and types) + TArray> ParamArr; + for (const TSharedPtr& PinInfo : CE->UserDefinedPins) + { + if (!PinInfo.IsValid()) continue; + TSharedRef ParamJ = MakeShared(); + ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString()); + FString ParamType = PinInfo->PinType.PinCategory.ToString(); + ParamJ->SetStringField(TEXT("type"), ParamType); + if (PinInfo->PinType.PinSubCategoryObject.IsValid()) + ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName()); + else if (ParamType == TEXT("None") || ParamType.IsEmpty()) + ParamJ->SetBoolField(TEXT("typeUnknown"), true); + ParamArr.Add(MakeShared(ParamJ)); + } + NJ->SetArrayField(TEXT("parameters"), ParamArr); + } + else if (auto* VG = Cast(Node)) + { + NJ->SetStringField(TEXT("variableName"), VG->GetVarName().ToString()); + NJ->SetStringField(TEXT("nodeType"), TEXT("VariableGet")); + } + else if (auto* VS = Cast(Node)) + { + NJ->SetStringField(TEXT("variableName"), VS->GetVarName().ToString()); + NJ->SetStringField(TEXT("nodeType"), TEXT("VariableSet")); + } + else if (auto* MI = Cast(Node)) + { + if (MI->GetMacroGraph()) + NJ->SetStringField(TEXT("macroName"), MI->GetMacroGraph()->GetName()); + NJ->SetStringField(TEXT("nodeType"), TEXT("MacroInstance")); + } + else if (auto* DC = Cast(Node)) + { + if (DC->TargetType) + NJ->SetStringField(TEXT("castTarget"), DC->TargetType->GetName()); + NJ->SetStringField(TEXT("nodeType"), TEXT("DynamicCast")); + } + else if (Cast(Node)) + { + NJ->SetStringField(TEXT("nodeType"), TEXT("Branch")); + } + + // Pins + TArray> Pins; + for (UEdGraphPin* Pin : Node->Pins) + { + if (!Pin || Pin->bHidden) continue; + TSharedPtr PJ = SerializePin(Pin); + if (PJ.IsValid()) + Pins.Add(MakeShared(PJ.ToSharedRef())); + } + NJ->SetArrayField(TEXT("pins"), Pins); + return NJ; +} + +TSharedPtr MCPUtils::SerializePin(UEdGraphPin* Pin) +{ + TSharedRef PJ = MakeShared(); + PJ->SetStringField(TEXT("name"), Pin->PinName.ToString()); + PJ->SetStringField(TEXT("direction"), Pin->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output")); + PJ->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString()); + if (Pin->PinType.PinSubCategoryObject.IsValid()) + PJ->SetStringField(TEXT("subtype"), Pin->PinType.PinSubCategoryObject->GetName()); + if (!Pin->DefaultValue.IsEmpty()) + PJ->SetStringField(TEXT("defaultValue"), Pin->DefaultValue); + + if (Pin->LinkedTo.Num() > 0) + { + TArray> Conns; + for (UEdGraphPin* Linked : Pin->LinkedTo) + { + if (!Linked || !Linked->GetOwningNode()) continue; + TSharedRef CJ = MakeShared(); + CJ->SetStringField(TEXT("nodeId"), Linked->GetOwningNode()->NodeGuid.ToString()); + CJ->SetStringField(TEXT("pinName"), Linked->PinName.ToString()); + Conns.Add(MakeShared(CJ)); + } + PJ->SetArrayField(TEXT("connections"), Conns); + } + return PJ; +} + +TSharedPtr MCPUtils::SerializeMaterialExpression(UMaterialExpression* Expression) +{ + if (!Expression) return nullptr; + + TSharedRef EJ = MakeShared(); + EJ->SetStringField(TEXT("class"), Expression->GetClass()->GetName()); + EJ->SetStringField(TEXT("name"), Expression->GetName()); + EJ->SetStringField(TEXT("description"), Expression->GetDescription()); + EJ->SetNumberField(TEXT("posX"), Expression->MaterialExpressionEditorX); + EJ->SetNumberField(TEXT("posY"), Expression->MaterialExpressionEditorY); + + if (auto* SP = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("ScalarParameter")); + EJ->SetStringField(TEXT("parameterName"), SP->ParameterName.ToString()); + EJ->SetNumberField(TEXT("defaultValue"), SP->DefaultValue); + EJ->SetStringField(TEXT("group"), SP->Group.ToString()); + } + else if (auto* VP = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("VectorParameter")); + EJ->SetStringField(TEXT("parameterName"), VP->ParameterName.ToString()); + TSharedRef DefVal = MakeShared(); + DefVal->SetNumberField(TEXT("r"), VP->DefaultValue.R); + DefVal->SetNumberField(TEXT("g"), VP->DefaultValue.G); + DefVal->SetNumberField(TEXT("b"), VP->DefaultValue.B); + DefVal->SetNumberField(TEXT("a"), VP->DefaultValue.A); + EJ->SetObjectField(TEXT("defaultValue"), DefVal); + EJ->SetStringField(TEXT("group"), VP->Group.ToString()); + } + else if (auto* TP = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSampleParameter2D")); + EJ->SetStringField(TEXT("parameterName"), TP->ParameterName.ToString()); + if (TP->Texture) + EJ->SetStringField(TEXT("texture"), TP->Texture->GetPathName()); + EJ->SetStringField(TEXT("group"), TP->Group.ToString()); + } + else if (auto* SSP = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("StaticSwitchParameter")); + EJ->SetStringField(TEXT("parameterName"), SSP->ParameterName.ToString()); + EJ->SetBoolField(TEXT("defaultValue"), SSP->DefaultValue); + EJ->SetStringField(TEXT("group"), SSP->Group.ToString()); + } + else if (auto* SC = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("Constant")); + EJ->SetNumberField(TEXT("value"), SC->R); + } + else if (auto* C3 = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("Constant3Vector")); + TSharedRef Val = MakeShared(); + Val->SetNumberField(TEXT("r"), C3->Constant.R); + Val->SetNumberField(TEXT("g"), C3->Constant.G); + Val->SetNumberField(TEXT("b"), C3->Constant.B); + EJ->SetObjectField(TEXT("value"), Val); + } + else if (auto* C4 = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("Constant4Vector")); + TSharedRef Val = MakeShared(); + Val->SetNumberField(TEXT("r"), C4->Constant.R); + Val->SetNumberField(TEXT("g"), C4->Constant.G); + Val->SetNumberField(TEXT("b"), C4->Constant.B); + Val->SetNumberField(TEXT("a"), C4->Constant.A); + EJ->SetObjectField(TEXT("value"), Val); + } + else if (auto* TS = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSample")); + if (TS->Texture) + EJ->SetStringField(TEXT("texture"), TS->Texture->GetPathName()); + } + else if (auto* TC = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("TextureCoordinate")); + EJ->SetNumberField(TEXT("coordinateIndex"), TC->CoordinateIndex); + EJ->SetNumberField(TEXT("uTiling"), TC->UTiling); + EJ->SetNumberField(TEXT("vTiling"), TC->VTiling); + } + else if (auto* CM = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("ComponentMask")); + EJ->SetBoolField(TEXT("r"), CM->R != 0); + EJ->SetBoolField(TEXT("g"), CM->G != 0); + EJ->SetBoolField(TEXT("b"), CM->B != 0); + EJ->SetBoolField(TEXT("a"), CM->A != 0); + } + else if (auto* Custom = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("Custom")); + EJ->SetStringField(TEXT("code"), Custom->Code); + EJ->SetStringField(TEXT("outputType"), StaticEnum()->GetNameStringByValue((int64)Custom->OutputType)); + } + else if (auto* FI = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionInput")); + EJ->SetStringField(TEXT("inputName"), FI->InputName.ToString()); + } + else if (auto* FO = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionOutput")); + EJ->SetStringField(TEXT("outputName"), FO->OutputName.ToString()); + } + else if (auto* MFC = Cast(Expression)) + { + EJ->SetStringField(TEXT("expressionType"), TEXT("MaterialFunctionCall")); + if (MFC->MaterialFunction) + EJ->SetStringField(TEXT("functionName"), MFC->MaterialFunction->GetName()); + } + else + { + EJ->SetStringField(TEXT("expressionType"), Expression->GetClass()->GetName()); + } + + return EJ; +} diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h index 144e063e..4f0d5902 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/Graph_Dump.h @@ -37,13 +37,13 @@ public: F.Walk(Path); if (!F.Ok()) return; - if (UEdGraph* Graph = Cast(F.Obj)) + if (UEdGraph* Graph = Cast(F.GetObj())) { EmitGraph(Graph, Result); return; } - if (UBlueprint* BP = Cast(F.Obj)) + if (UBlueprint* BP = Cast(F.GetObj())) { TArray Graphs = MCPUtils::AllGraphs(BP); for (UEdGraph* Graph : Graphs) @@ -54,7 +54,7 @@ public: return; } - if (UMaterial* Mat = Cast(F.Obj)) + if (UMaterial* Mat = Cast(F.GetObj())) { MCPUtils::EnsureMaterialGraph(Mat); if (!Mat->MaterialGraph) @@ -67,7 +67,7 @@ public: } Result.Appendf(TEXT("ERROR: Expected a blueprint, material, or graph, got %s\n"), - *MCPUtils::FormatName(F.Obj->GetClass())); + *MCPUtils::FormatName(F.GetObj()->GetClass())); } private: diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp index 1d95ffe3..55e20545 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPFetcher.cpp @@ -10,6 +10,7 @@ #include "Materials/Material.h" #include "MaterialGraph/MaterialGraph.h" #include "MaterialGraph/MaterialGraphNode.h" +#include "IMaterialEditor.h" #include "Engine/LevelScriptBlueprint.h" #include "Subsystems/AssetEditorSubsystem.h" @@ -40,7 +41,6 @@ const TArray& MCPFetcher::GetWalkerTable() { TEXT("pin"), TEXT("Find a named UEdGraphPin on a node"), &MCPFetcher::Pin }, { TEXT("component"), TEXT("Find a named component in a Blueprint's SCS"), &MCPFetcher::Component }, { TEXT("levelblueprint"), TEXT("Get the level blueprint from a UWorld"), &MCPFetcher::LevelBlueprint }, - // { TEXT("matexp"), TEXT("Get the UMaterialExpression from a UMaterialGraphNode"), &MCPFetcher::MatExp }, }; return Table; } @@ -100,12 +100,28 @@ MCPFetcher& MCPFetcher::Asset(const FString& PackagePath) if (!Editor) return SetError(FString::Printf(TEXT("Could not find editor instance for '%s'"), *PackagePath)); - // If this is a material open in the editor, use the editor's transient copy. + // If this is a material, use the editor's transient copy. if (UMaterial* Mat = ::Cast(Obj)) - SetObj(MCPUtils::ReplaceMaterialWithTransientCopy(Mat)); + { + IMaterialEditor *MatEditor = static_cast(Editor); + SetObj(MatEditor->GetMaterialInterface()->GetBaseMaterial()); + } return *this; } +bool MCPFetcher::CheckAssetIsA(UClass* StaticClass) +{ + if (bError) return false; + if (!OriginalAsset || !OriginalAsset->IsA(StaticClass)) + { + SetError(FString::Printf(TEXT("Asset is %s, expected %s"), + OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"), + *StaticClass->GetName())); + return false; + } + return true; +} + const MCPFetcher::FWalker* MCPFetcher::GetWalker(const FString& Key) { for (const FWalker& W : GetWalkerTable()) @@ -314,18 +330,3 @@ MCPFetcher& MCPFetcher::ToGraph() return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material")); } - -MCPFetcher& MCPFetcher::MatExp(const FString& Value) -{ - if (bError) return *this; - - UMaterialGraphNode* MatNode = ::Cast(Obj); - if (!MatNode) - return TypeMismatch(TEXT("matexp"), TEXT("UMaterialGraphNode")); - - if (!MatNode->MaterialExpression) - return SetError(FString::Printf(TEXT("Node '%s' has no MaterialExpression"), *MCPUtils::FormatName(MatNode))); - - SetObj(MatNode->MaterialExpression); - return *this; -} diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp index a37f67a3..68afcc35 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp @@ -540,296 +540,11 @@ bool MCPUtils::SaveBlueprintPackage(UBlueprint* BP) return bSuccess; } -// ============================================================ -// Serialization -// ============================================================ - -TSharedRef MCPUtils::SerializeBlueprint(UBlueprint* BP) -{ - TSharedRef J = MakeShared(); - J->SetStringField(TEXT("name"), BP->GetName()); - J->SetStringField(TEXT("path"), BP->GetPackage()->GetName()); - J->SetStringField(TEXT("parentClass"), BP->ParentClass ? BP->ParentClass->GetName() : TEXT("None")); - J->SetStringField(TEXT("blueprintType"), - StaticEnum()->GetNameStringByValue((int64)BP->BlueprintType)); - - // Animation Blueprint detection - if (UAnimBlueprint* AnimBP = Cast(BP)) - { - J->SetBoolField(TEXT("isAnimBlueprint"), true); - if (AnimBP->TargetSkeleton) - { - J->SetStringField(TEXT("targetSkeleton"), AnimBP->TargetSkeleton->GetName()); - J->SetStringField(TEXT("targetSkeletonPath"), AnimBP->TargetSkeleton->GetPathName()); - } - } - - // Variables - TArray> Vars; - for (const FBPVariableDescription& V : BP->NewVariables) - { - TSharedRef VJ = MakeShared(); - VJ->SetStringField(TEXT("name"), V.VarName.ToString()); - VJ->SetStringField(TEXT("type"), V.VarType.PinCategory.ToString()); - if (V.VarType.PinSubCategoryObject.IsValid()) - VJ->SetStringField(TEXT("subtype"), V.VarType.PinSubCategoryObject->GetName()); - VJ->SetBoolField(TEXT("isArray"), V.VarType.IsArray()); - VJ->SetBoolField(TEXT("isSet"), V.VarType.IsSet()); - VJ->SetBoolField(TEXT("isMap"), V.VarType.IsMap()); - VJ->SetStringField(TEXT("category"), V.Category.ToString()); - VJ->SetStringField(TEXT("defaultValue"), V.DefaultValue); - Vars.Add(MakeShared(VJ)); - } - J->SetArrayField(TEXT("variables"), Vars); - - // Interfaces - TArray> Ifaces; - for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces) - { - if (I.Interface) - Ifaces.Add(MakeShared(I.Interface->GetName())); - } - J->SetArrayField(TEXT("interfaces"), Ifaces); - - return J; -} - -TSharedPtr MCPUtils::SerializeNode(UEdGraphNode* Node) -{ - TSharedRef NJ = MakeShared(); - NJ->SetStringField(TEXT("id"), Node->NodeGuid.ToString()); - NJ->SetStringField(TEXT("class"), Node->GetClass()->GetName()); - NJ->SetStringField(TEXT("title"), Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString()); - if (!Node->NodeComment.IsEmpty()) - NJ->SetStringField(TEXT("comment"), Node->NodeComment); - NJ->SetNumberField(TEXT("posX"), Node->NodePosX); - NJ->SetNumberField(TEXT("posY"), Node->NodePosY); - - // Material graph node — extract UMaterialExpression data - if (UMaterialGraphNode* MatNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("MaterialExpression")); - if (MatNode->MaterialExpression) - { - TSharedPtr ExprJson = SerializeMaterialExpression(MatNode->MaterialExpression); - if (ExprJson.IsValid()) - { - NJ->SetObjectField(TEXT("expression"), ExprJson); - } - } - } - // Animation Blueprint node types - else if (auto* SMNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateMachine")); - if (SMNode->EditorStateMachineGraph) - { - NJ->SetStringField(TEXT("stateMachineName"), SMNode->EditorStateMachineGraph->GetName()); - int32 StateCount = 0, TransitionCount = 0; - for (UEdGraphNode* SubNode : SMNode->EditorStateMachineGraph->Nodes) - { - if (Cast(SubNode)) StateCount++; - else if (Cast(SubNode)) TransitionCount++; - } - NJ->SetNumberField(TEXT("stateCount"), StateCount); - NJ->SetNumberField(TEXT("transitionCount"), TransitionCount); - } - } - else if (auto* SeqPlayer = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimSequencePlayer")); - if (UAnimationAsset* Asset = SeqPlayer->GetAnimationAsset()) - { - NJ->SetStringField(TEXT("animationAsset"), Asset->GetName()); - NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName()); - } - } - else if (auto* BSPlayer = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimBlendSpacePlayer")); - if (UAnimationAsset* Asset = BSPlayer->GetAnimationAsset()) - { - NJ->SetStringField(TEXT("blendSpaceAsset"), Asset->GetName()); - NJ->SetStringField(TEXT("blendSpaceAssetPath"), Asset->GetPathName()); - } - } - else if (auto* AssetPlayer = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimAssetPlayer")); - if (UAnimationAsset* Asset = AssetPlayer->GetAnimationAsset()) - { - NJ->SetStringField(TEXT("animationAsset"), Asset->GetName()); - NJ->SetStringField(TEXT("animationAssetPath"), Asset->GetPathName()); - } - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimNode")); - } - else if (auto* StateNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimState")); - NJ->SetStringField(TEXT("stateName"), StateNode->GetStateName()); - NJ->SetBoolField(TEXT("bAlwaysResetOnEntry"), StateNode->bAlwaysResetOnEntry); - } - else if (auto* TransNode = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimTransition")); - if (UAnimStateNode* FromState = Cast(TransNode->GetPreviousState())) - { - NJ->SetStringField(TEXT("fromState"), FromState->GetStateName()); - } - if (UAnimStateNode* ToState = Cast(TransNode->GetNextState())) - { - NJ->SetStringField(TEXT("toState"), ToState->GetStateName()); - } - NJ->SetNumberField(TEXT("crossfadeDuration"), TransNode->CrossfadeDuration); - NJ->SetNumberField(TEXT("blendMode"), (int32)TransNode->BlendMode); - NJ->SetNumberField(TEXT("priorityOrder"), TransNode->PriorityOrder); - NJ->SetNumberField(TEXT("logicType"), (int32)TransNode->LogicType.GetValue()); - NJ->SetBoolField(TEXT("bBidirectional"), TransNode->Bidirectional); - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimConduit")); - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("AnimStateEntry")); - } - // K2Node specifics — check CallParentFunction before CallFunction (inheritance) - else if (auto* CPF = Cast(Node)) - { - NJ->SetStringField(TEXT("functionName"), CPF->FunctionReference.GetMemberName().ToString()); - if (CPF->FunctionReference.GetMemberParentClass()) - NJ->SetStringField(TEXT("targetClass"), CPF->FunctionReference.GetMemberParentClass()->GetName()); - NJ->SetStringField(TEXT("nodeType"), TEXT("CallParentFunction")); - } - else if (auto* CF = Cast(Node)) - { - NJ->SetStringField(TEXT("functionName"), CF->FunctionReference.GetMemberName().ToString()); - if (CF->FunctionReference.GetMemberParentClass()) - NJ->SetStringField(TEXT("targetClass"), CF->FunctionReference.GetMemberParentClass()->GetName()); - } - else if (auto* FE = Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("FunctionEntry")); - - // Serialize UserDefinedPins (parameter names and types) - TArray> ParamArr; - for (const TSharedPtr& PinInfo : FE->UserDefinedPins) - { - if (!PinInfo.IsValid()) continue; - TSharedRef ParamJ = MakeShared(); - ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString()); - FString ParamType = PinInfo->PinType.PinCategory.ToString(); - ParamJ->SetStringField(TEXT("type"), ParamType); - if (PinInfo->PinType.PinSubCategoryObject.IsValid()) - ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName()); - else if (ParamType == TEXT("None") || ParamType.IsEmpty()) - ParamJ->SetBoolField(TEXT("typeUnknown"), true); - ParamArr.Add(MakeShared(ParamJ)); - } - NJ->SetArrayField(TEXT("parameters"), ParamArr); - } - else if (auto* Ev = Cast(Node)) - { - NJ->SetStringField(TEXT("eventName"), Ev->EventReference.GetMemberName().ToString()); - NJ->SetStringField(TEXT("nodeType"), Ev->bOverrideFunction ? TEXT("OverrideEvent") : TEXT("Event")); - } - else if (auto* CE = Cast(Node)) - { - NJ->SetStringField(TEXT("eventName"), CE->CustomFunctionName.ToString()); - NJ->SetStringField(TEXT("nodeType"), TEXT("CustomEvent")); - - // Serialize UserDefinedPins (parameter names and types) - TArray> ParamArr; - for (const TSharedPtr& PinInfo : CE->UserDefinedPins) - { - if (!PinInfo.IsValid()) continue; - TSharedRef ParamJ = MakeShared(); - ParamJ->SetStringField(TEXT("name"), PinInfo->PinName.ToString()); - FString ParamType = PinInfo->PinType.PinCategory.ToString(); - ParamJ->SetStringField(TEXT("type"), ParamType); - if (PinInfo->PinType.PinSubCategoryObject.IsValid()) - ParamJ->SetStringField(TEXT("subtype"), PinInfo->PinType.PinSubCategoryObject->GetName()); - else if (ParamType == TEXT("None") || ParamType.IsEmpty()) - ParamJ->SetBoolField(TEXT("typeUnknown"), true); - ParamArr.Add(MakeShared(ParamJ)); - } - NJ->SetArrayField(TEXT("parameters"), ParamArr); - } - else if (auto* VG = Cast(Node)) - { - NJ->SetStringField(TEXT("variableName"), VG->GetVarName().ToString()); - NJ->SetStringField(TEXT("nodeType"), TEXT("VariableGet")); - } - else if (auto* VS = Cast(Node)) - { - NJ->SetStringField(TEXT("variableName"), VS->GetVarName().ToString()); - NJ->SetStringField(TEXT("nodeType"), TEXT("VariableSet")); - } - else if (auto* MI = Cast(Node)) - { - if (MI->GetMacroGraph()) - NJ->SetStringField(TEXT("macroName"), MI->GetMacroGraph()->GetName()); - NJ->SetStringField(TEXT("nodeType"), TEXT("MacroInstance")); - } - else if (auto* DC = Cast(Node)) - { - if (DC->TargetType) - NJ->SetStringField(TEXT("castTarget"), DC->TargetType->GetName()); - NJ->SetStringField(TEXT("nodeType"), TEXT("DynamicCast")); - } - else if (Cast(Node)) - { - NJ->SetStringField(TEXT("nodeType"), TEXT("Branch")); - } - - // Pins - TArray> Pins; - for (UEdGraphPin* Pin : Node->Pins) - { - if (!Pin || Pin->bHidden) continue; - TSharedPtr PJ = SerializePin(Pin); - if (PJ.IsValid()) - Pins.Add(MakeShared(PJ.ToSharedRef())); - } - NJ->SetArrayField(TEXT("pins"), Pins); - return NJ; -} - -TSharedPtr MCPUtils::SerializePin(UEdGraphPin* Pin) -{ - TSharedRef PJ = MakeShared(); - PJ->SetStringField(TEXT("name"), Pin->PinName.ToString()); - PJ->SetStringField(TEXT("direction"), Pin->Direction == EGPD_Input ? TEXT("Input") : TEXT("Output")); - PJ->SetStringField(TEXT("type"), Pin->PinType.PinCategory.ToString()); - if (Pin->PinType.PinSubCategoryObject.IsValid()) - PJ->SetStringField(TEXT("subtype"), Pin->PinType.PinSubCategoryObject->GetName()); - if (!Pin->DefaultValue.IsEmpty()) - PJ->SetStringField(TEXT("defaultValue"), Pin->DefaultValue); - - if (Pin->LinkedTo.Num() > 0) - { - TArray> Conns; - for (UEdGraphPin* Linked : Pin->LinkedTo) - { - if (!Linked || !Linked->GetOwningNode()) continue; - TSharedRef CJ = MakeShared(); - CJ->SetStringField(TEXT("nodeId"), Linked->GetOwningNode()->NodeGuid.ToString()); - CJ->SetStringField(TEXT("pinName"), Linked->PinName.ToString()); - Conns.Add(MakeShared(CJ)); - } - PJ->SetArrayField(TEXT("connections"), Conns); - } - return PJ; -} - // ============================================================ // FindClassByName / ResolveTypeFromString // ============================================================ + UClass* MCPUtils::FindClassByName(const FString& ClassName) { // Exact match first (handles both C++ classes and Blueprint _C classes) @@ -1274,125 +989,6 @@ bool MCPUtils::SaveGenericPackage(UObject* Asset) return bSuccess; } -TSharedPtr MCPUtils::SerializeMaterialExpression(UMaterialExpression* Expression) -{ - if (!Expression) return nullptr; - - TSharedRef EJ = MakeShared(); - EJ->SetStringField(TEXT("class"), Expression->GetClass()->GetName()); - EJ->SetStringField(TEXT("name"), Expression->GetName()); - EJ->SetStringField(TEXT("description"), Expression->GetDescription()); - EJ->SetNumberField(TEXT("posX"), Expression->MaterialExpressionEditorX); - EJ->SetNumberField(TEXT("posY"), Expression->MaterialExpressionEditorY); - - if (auto* SP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("ScalarParameter")); - EJ->SetStringField(TEXT("parameterName"), SP->ParameterName.ToString()); - EJ->SetNumberField(TEXT("defaultValue"), SP->DefaultValue); - EJ->SetStringField(TEXT("group"), SP->Group.ToString()); - } - else if (auto* VP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("VectorParameter")); - EJ->SetStringField(TEXT("parameterName"), VP->ParameterName.ToString()); - TSharedRef DefVal = MakeShared(); - DefVal->SetNumberField(TEXT("r"), VP->DefaultValue.R); - DefVal->SetNumberField(TEXT("g"), VP->DefaultValue.G); - DefVal->SetNumberField(TEXT("b"), VP->DefaultValue.B); - DefVal->SetNumberField(TEXT("a"), VP->DefaultValue.A); - EJ->SetObjectField(TEXT("defaultValue"), DefVal); - EJ->SetStringField(TEXT("group"), VP->Group.ToString()); - } - else if (auto* TP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSampleParameter2D")); - EJ->SetStringField(TEXT("parameterName"), TP->ParameterName.ToString()); - if (TP->Texture) - EJ->SetStringField(TEXT("texture"), TP->Texture->GetPathName()); - EJ->SetStringField(TEXT("group"), TP->Group.ToString()); - } - else if (auto* SSP = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("StaticSwitchParameter")); - EJ->SetStringField(TEXT("parameterName"), SSP->ParameterName.ToString()); - EJ->SetBoolField(TEXT("defaultValue"), SSP->DefaultValue); - EJ->SetStringField(TEXT("group"), SSP->Group.ToString()); - } - else if (auto* SC = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Constant")); - EJ->SetNumberField(TEXT("value"), SC->R); - } - else if (auto* C3 = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Constant3Vector")); - TSharedRef Val = MakeShared(); - Val->SetNumberField(TEXT("r"), C3->Constant.R); - Val->SetNumberField(TEXT("g"), C3->Constant.G); - Val->SetNumberField(TEXT("b"), C3->Constant.B); - EJ->SetObjectField(TEXT("value"), Val); - } - else if (auto* C4 = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Constant4Vector")); - TSharedRef Val = MakeShared(); - Val->SetNumberField(TEXT("r"), C4->Constant.R); - Val->SetNumberField(TEXT("g"), C4->Constant.G); - Val->SetNumberField(TEXT("b"), C4->Constant.B); - Val->SetNumberField(TEXT("a"), C4->Constant.A); - EJ->SetObjectField(TEXT("value"), Val); - } - else if (auto* TS = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("TextureSample")); - if (TS->Texture) - EJ->SetStringField(TEXT("texture"), TS->Texture->GetPathName()); - } - else if (auto* TC = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("TextureCoordinate")); - EJ->SetNumberField(TEXT("coordinateIndex"), TC->CoordinateIndex); - EJ->SetNumberField(TEXT("uTiling"), TC->UTiling); - EJ->SetNumberField(TEXT("vTiling"), TC->VTiling); - } - else if (auto* CM = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("ComponentMask")); - EJ->SetBoolField(TEXT("r"), CM->R != 0); - EJ->SetBoolField(TEXT("g"), CM->G != 0); - EJ->SetBoolField(TEXT("b"), CM->B != 0); - EJ->SetBoolField(TEXT("a"), CM->A != 0); - } - else if (auto* Custom = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("Custom")); - EJ->SetStringField(TEXT("code"), Custom->Code); - EJ->SetStringField(TEXT("outputType"), StaticEnum()->GetNameStringByValue((int64)Custom->OutputType)); - } - else if (auto* FI = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionInput")); - EJ->SetStringField(TEXT("inputName"), FI->InputName.ToString()); - } - else if (auto* FO = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("FunctionOutput")); - EJ->SetStringField(TEXT("outputName"), FO->OutputName.ToString()); - } - else if (auto* MFC = Cast(Expression)) - { - EJ->SetStringField(TEXT("expressionType"), TEXT("MaterialFunctionCall")); - if (MFC->MaterialFunction) - EJ->SetStringField(TEXT("functionName"), MFC->MaterialFunction->GetName()); - } - else - { - EJ->SetStringField(TEXT("expressionType"), Expression->GetClass()->GetName()); - } - - return EJ; -} // ============================================================ // Anim blueprint helpers diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h index d83275e0..6f62924a 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPFetcher.h @@ -28,13 +28,6 @@ class IAssetEditorInstance; class MCPFetcher { public: - bool bError = false; - UObject* Obj = nullptr; - UObject* OriginalAsset = nullptr; - IAssetEditorInstance* Editor = nullptr; - UEdGraphPin* ResultPin = nullptr; - MCPErrorCallback ErrorCB = nullptr; - MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {} MCPFetcher(MCPErrorCallback CB, UObject* O) : Obj(O), ErrorCB(CB) {} @@ -47,7 +40,6 @@ public: MCPFetcher& Pin(const FString& Value); MCPFetcher& Component(const FString& Value); MCPFetcher& LevelBlueprint(const FString& Value); - MCPFetcher& MatExp(const FString& Value); // Parse string and walk multiple steps. MCPFetcher& Walk(const FString& Path); @@ -72,28 +64,17 @@ public: static const TArray& GetWalkerTable(); bool Ok() const { return !bError; } + UObject* GetObj() const { return Obj; } UObject* GetAsset() const { return OriginalAsset; } template T* CastAsset() { - if (bError) return nullptr; - T* Result = ::Cast(OriginalAsset); - if (!Result) - SetError(FString::Printf(TEXT("Asset is %s, expected %s"), - OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"), - *T::StaticClass()->GetName())); - return Result; + if (!CheckAssetIsA(T::StaticClass())) return nullptr; + return ::Cast(OriginalAsset); } template EditorType* CastEditor() { - if (bError) return nullptr; - if (!OriginalAsset || !OriginalAsset->IsA()) - { - SetError(FString::Printf(TEXT("Asset is %s, expected %s"), - OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"), - *AssetType::StaticClass()->GetName())); - return nullptr; - } + if (!CheckAssetIsA(AssetType::StaticClass())) return nullptr; return static_cast(Editor); } const TArray& Visited() const { return Chain; } @@ -110,6 +91,12 @@ public: private: + bool bError = false; + UObject* Obj = nullptr; + UObject* OriginalAsset = nullptr; + IAssetEditorInstance* Editor = nullptr; + UEdGraphPin* ResultPin = nullptr; + MCPErrorCallback ErrorCB = nullptr; TArray Chain; static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); } void SetObj(UObject* InObj) { if (InObj) Chain.AddUnique(InObj); Obj = InObj; ResultPin = nullptr; } @@ -117,6 +104,7 @@ private: MCPFetcher& SetError(const FString& Msg); MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected); const FWalker* GetWalker(const FString& Key); + bool CheckAssetIsA(UClass* StaticClass); }; template<> inline UEdGraphPin* MCPFetcher::Cast() diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h index 1da88129..85230dc8 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPUtils.h @@ -251,12 +251,6 @@ public: static UEdGraphNode* FindNodeByGuid(UBlueprint* BP, const FString& GuidString, UEdGraph** OutGraph = nullptr); static bool SaveBlueprintPackage(UBlueprint* BP); - // ----- Serialization ----- - static TSharedRef SerializeBlueprint(UBlueprint* BP); - static TSharedPtr SerializeNode(UEdGraphNode* Node); - static TSharedPtr SerializePin(UEdGraphPin* Pin); - static TSharedPtr SerializeMaterialExpression(UMaterialExpression* Expression); - // ----- Type resolution ----- static UClass* FindClassByName(const FString& ClassName); static bool ResolveTypeFromString(const FString& TypeName, FEdGraphPinType& OutPinType, MCPErrorCallback Error);