From 8d9ce48daee2e6dd056333c9ecbabd0ec32fcbc1 Mon Sep 17 00:00:00 2001 From: jyelon Date: Sun, 15 Mar 2026 22:24:10 -0400 Subject: [PATCH] BlueprintVariable handlers --- Content/Tangibles/TAN_Character.uasset | 4 +- Content/Testing/BP_VarTest2.uasset | 3 + .../Blueprint_SetVariableMetadata.h | 190 ------------------ .../HalfBaked/Blueprint_AddVariable.h | 93 --------- .../HalfBaked/Blueprint_ChangeVariableType.h | 147 -------------- .../HalfBaked/Blueprint_RemoveVariable.h | 73 ------- .../Handlers/BlueprintVariable_Create.h | 79 ++++++++ .../Handlers/BlueprintVariable_Delete.h | 49 +++++ .../Handlers/BlueprintVariable_Dump.h | 47 +++++ .../Handlers/BlueprintVariable_Modify.h | 62 ++++++ .../BlueprintMCP/Private/BPVarEditor.cpp | 110 ++++++++++ .../Source/BlueprintMCP/Private/MCPJson.cpp | 104 +++++----- .../BlueprintMCP/Private/MCPProperty.cpp | 37 +++- .../Source/BlueprintMCP/Public/BPVarEditor.h | 53 +++++ .../Source/BlueprintMCP/Public/MCPJson.h | 4 +- .../Source/BlueprintMCP/Public/MCPProperty.h | 4 +- 16 files changed, 490 insertions(+), 569 deletions(-) create mode 100644 Content/Testing/BP_VarTest2.uasset delete mode 100644 Plugins/BlueprintMCP/Deprecated/Blueprint_SetVariableMetadata.h delete mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_AddVariable.h delete mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_ChangeVariableType.h delete mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_RemoveVariable.h create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Create.h create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Delete.h create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Dump.h create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Modify.h create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BPVarEditor.cpp create mode 100644 Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BPVarEditor.h diff --git a/Content/Tangibles/TAN_Character.uasset b/Content/Tangibles/TAN_Character.uasset index 1420fe51..83ec658d 100644 --- a/Content/Tangibles/TAN_Character.uasset +++ b/Content/Tangibles/TAN_Character.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45e83f34d7dd667c2baee9497235404cadf505821373862d355f791ad4181241 -size 375635 +oid sha256:29b2e6afcce8a0d56e35b5fb1e3c9bcf91598766d5ed765561b8f45311972cca +size 372409 diff --git a/Content/Testing/BP_VarTest2.uasset b/Content/Testing/BP_VarTest2.uasset new file mode 100644 index 00000000..d89a665d --- /dev/null +++ b/Content/Testing/BP_VarTest2.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af0f525d40c30779f7f64dd24204f92b5145e9d24e9e0957284e646080004e80 +size 46477 diff --git a/Plugins/BlueprintMCP/Deprecated/Blueprint_SetVariableMetadata.h b/Plugins/BlueprintMCP/Deprecated/Blueprint_SetVariableMetadata.h deleted file mode 100644 index 6cfaa263..00000000 --- a/Plugins/BlueprintMCP/Deprecated/Blueprint_SetVariableMetadata.h +++ /dev/null @@ -1,190 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "Engine/Blueprint.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Blueprint_SetVariableMetadata.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Blueprint_SetVariableMetadata : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Blueprint name or package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="Name of the variable to modify")) - FString Variable; - - UPROPERTY(meta=(Optional, Description="Category to assign the variable to")) - FString Category; - - UPROPERTY(meta=(Optional, Description="Tooltip text for the variable")) - FString Tooltip; - - UPROPERTY(meta=(Optional, Description="Replication mode: none, replicated, or repNotify")) - FString Replication; - - UPROPERTY(meta=(Optional, Description="If true, expose this variable on spawn")) - bool ExposeOnSpawn = false; - - UPROPERTY(meta=(Optional, Description="If true, mark the variable as private")) - bool IsPrivate = false; - - UPROPERTY(meta=(Optional, Description="Editability mode: editAnywhere, editDefaultsOnly, editInstanceOnly, or none")) - FString Editability; - - virtual FString GetDescription() const override - { - return TEXT("Set variable metadata properties such as category, tooltip, " - "replication, editability, and visibility flags."); - } - - virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override - { - MCPFetcher F(Result); - UBlueprint* BP = F.Walk(Blueprint).Cast(); - if (!BP) return; - - // Find the variable using Identifies for consistent name matching - FBPVariableDescription* VarDesc = nullptr; - for (FBPVariableDescription& Var : BP->NewVariables) - { - if (MCPUtils::FormatName(Var) == Variable || - Var.VarName.ToString().Equals(Variable, ESearchCase::IgnoreCase)) - { - VarDesc = &Var; - break; - } - } - - if (!VarDesc) - { - Result.Appendf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"), - *Variable, *MCPUtils::FormatName(BP)); - for (const FBPVariableDescription& Var : BP->NewVariables) - { - Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); - } - return; - } - - FName VarFName = VarDesc->VarName; - int32 ChangeCount = 0; - F.PreEdit(); - - // Category - if (Json->HasField(TEXT("category"))) - { - VarDesc->Category = FText::FromString(Category); - FBlueprintEditorUtils::SetBlueprintVariableCategory(BP, VarFName, nullptr, FText::FromString(Category)); - Result.Appendf(TEXT("Set category to '%s'.\n"), *Category); - ChangeCount++; - } - - // Tooltip - if (Json->HasField(TEXT("tooltip"))) - { - FBlueprintEditorUtils::SetBlueprintVariableMetaData(BP, VarFName, nullptr, TEXT("tooltip"), Tooltip); - Result.Appendf(TEXT("Set tooltip to '%s'.\n"), *Tooltip); - ChangeCount++; - } - - // Replication - if (Json->HasField(TEXT("replication"))) - { - if (Replication == TEXT("none")) - { - VarDesc->PropertyFlags &= ~CPF_Net; - VarDesc->PropertyFlags &= ~CPF_RepNotify; - VarDesc->RepNotifyFunc = NAME_None; - } - else if (Replication == TEXT("replicated")) - { - VarDesc->PropertyFlags |= CPF_Net; - VarDesc->PropertyFlags &= ~CPF_RepNotify; - VarDesc->RepNotifyFunc = NAME_None; - } - else if (Replication == TEXT("repNotify")) - { - VarDesc->PropertyFlags |= CPF_Net | CPF_RepNotify; - VarDesc->RepNotifyFunc = FName(*FString::Printf(TEXT("OnRep_%s"), *Variable)); - } - else - { - Result.Appendf(TEXT("ERROR: Invalid replication value '%s'. Valid: none, replicated, repNotify\n"), *Replication); - return; - } - Result.Appendf(TEXT("Set replication to '%s'.\n"), *Replication); - ChangeCount++; - } - - // ExposeOnSpawn - if (Json->HasField(TEXT("exposeOnSpawn"))) - { - if (ExposeOnSpawn) - VarDesc->PropertyFlags |= CPF_ExposeOnSpawn; - else - VarDesc->PropertyFlags &= ~CPF_ExposeOnSpawn; - Result.Appendf(TEXT("Set exposeOnSpawn to %s.\n"), ExposeOnSpawn ? TEXT("true") : TEXT("false")); - ChangeCount++; - } - - // isPrivate - if (Json->HasField(TEXT("isPrivate"))) - { - FBlueprintEditorUtils::SetBlueprintVariableMetaData(BP, VarFName, nullptr, - TEXT("BlueprintPrivate"), IsPrivate ? TEXT("true") : TEXT("false")); - Result.Appendf(TEXT("Set isPrivate to %s.\n"), IsPrivate ? TEXT("true") : TEXT("false")); - ChangeCount++; - } - - // Editability - if (Json->HasField(TEXT("editability"))) - { - VarDesc->PropertyFlags &= ~(CPF_Edit | CPF_DisableEditOnInstance | CPF_DisableEditOnTemplate); - - if (Editability == TEXT("editAnywhere")) - { - VarDesc->PropertyFlags |= CPF_Edit; - } - else if (Editability == TEXT("editDefaultsOnly")) - { - VarDesc->PropertyFlags |= CPF_Edit | CPF_DisableEditOnInstance; - } - else if (Editability == TEXT("editInstanceOnly")) - { - VarDesc->PropertyFlags |= CPF_Edit | CPF_DisableEditOnTemplate; - } - else if (Editability != TEXT("none")) - { - Result.Appendf(TEXT("ERROR: Invalid editability value '%s'. Valid: editAnywhere, editDefaultsOnly, editInstanceOnly, none\n"), *Editability); - return; - } - Result.Appendf(TEXT("Set editability to '%s'.\n"), *Editability); - ChangeCount++; - } - - if (ChangeCount == 0) - { - Result.Append(TEXT("ERROR: No metadata fields specified. Provide at least one of: category, tooltip, replication, exposeOnSpawn, isPrivate, editability\n")); - return; - } - - F.PostEdit(); - bool bSaved = MCPUtils::SaveBlueprintPackage(BP); - - Result.Appendf(TEXT("Updated %d field(s) on %s in %s.%s\n"), - ChangeCount, *VarFName.ToString(), *MCPUtils::FormatName(BP), - bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); - } -}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_AddVariable.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_AddVariable.h deleted file mode 100644 index ecd0015f..00000000 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_AddVariable.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPHandler.h" -#include "MCPFetcher.h" -#include "MCPTypes.h" -#include "MCPUtils.h" -#include "MCPServer.h" -#include "Engine/Blueprint.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Blueprint_AddVariable.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Blueprint_AddVariable : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Blueprint name or package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="Name of the new variable")) - FString VariableName; - - UPROPERTY(meta=(Description="Type of the new variable")) - FString VariableType; - - UPROPERTY(meta=(Optional, Description="Category to assign the variable to")) - FString Category; - - UPROPERTY(meta=(Optional, Description="If true, make the variable an array")) - bool IsArray = false; - - UPROPERTY(meta=(Optional, Description="Default value for the variable")) - FString DefaultValue; - - virtual FString GetDescription() const override - { - return TEXT("Add a new member variable to a Blueprint."); - } - - virtual void Handle() override - { - MCPFetcher F; - UBlueprint* BP = F.Asset(Blueprint).Cast(); - if (!BP) return; - - // Check for duplicate variable name - FName VarFName(*VariableName); - for (const FBPVariableDescription& Var : BP->NewVariables) - { - if (Var.VarName == VarFName) - { - UMCPServer::Printf(TEXT("ERROR: Variable '%s' already exists in %s\n"), *VariableName, *MCPUtils::FormatName(BP)); - return; - } - } - - // Resolve the type - FEdGraphPinType PinType; - if (!UMCPTypes::TextToType(VariableType, PinType)) - return; - - if (IsArray) - PinType.ContainerType = EPinContainerType::Array; - - // Add the variable - if (!FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, PinType, DefaultValue)) - { - UMCPServer::Printf(TEXT("ERROR: Failed to add variable '%s' to %s\n"), *VariableName, *MCPUtils::FormatName(BP)); - return; - } - - if (!Category.IsEmpty()) - FBlueprintEditorUtils::SetBlueprintVariableCategory(BP, VarFName, nullptr, FText::FromString(Category)); - - bool bSaved = MCPUtils::SaveBlueprintPackage(BP); - - UMCPServer::Printf(TEXT("Added %s %s to %s\n"), - *VariableType, *VariableName, *MCPUtils::FormatName(BP)); - if (IsArray) - UMCPServer::Print(TEXT("Container: Array\n")); - if (!Category.IsEmpty()) - UMCPServer::Printf(TEXT("Category: %s\n"), *Category); - if (!bSaved) - UMCPServer::Print(TEXT("Warning: package save failed\n")); - } -}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_ChangeVariableType.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_ChangeVariableType.h deleted file mode 100644 index 42aece2a..00000000 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_ChangeVariableType.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPServer.h" -#include "MCPTypes.h" -#include "MCPHandler.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "Engine/Blueprint.h" -#include "EdGraph/EdGraphPin.h" -#include "K2Node_VariableGet.h" -#include "K2Node_VariableSet.h" -#include "Blueprint_ChangeVariableType.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Blueprint_ChangeVariableType : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Blueprint name or package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="Name of the variable to change")) - FString Variable; - - UPROPERTY(meta=(Description="New type name for the variable")) - FString NewType; - - UPROPERTY(meta=(Optional, Description="Type category: object, softobject, class, softclass, interface, struct, enum")) - FString TypeCategory; - - UPROPERTY(meta=(Optional, Description="If true, analyze the change without applying it")) - bool DryRun = false; - - virtual FString GetDescription() const override - { - return TEXT("Change the type of a Blueprint member variable. " - "Supports dry-run mode to preview affected nodes before committing."); - } - - virtual void Handle() override - { - MCPFetcher F; - UBlueprint* BP = F.Walk(Blueprint).Cast(); - if (!BP) return; - - // Find the variable - FBPVariableDescription* Found = nullptr; - for (FBPVariableDescription& Var : BP->NewVariables) - { - if (MCPUtils::FormatName(Var) == Variable || - Var.VarName.ToString().Equals(Variable, ESearchCase::IgnoreCase)) - { - Found = &Var; - break; - } - } - if (!Found) - { - UMCPServer::Printf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"), - *Variable, *MCPUtils::FormatName(BP)); - for (const FBPVariableDescription& Var : BP->NewVariables) - UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); - return; - } - - // Build the new pin type using shared resolver - FEdGraphPinType NewPinType; - FString ResolveInput = NewType; - - // If typeCategory is an object reference variant, use colon syntax for the resolver - if (TypeCategory == TEXT("object") || TypeCategory == TEXT("softobject") || - TypeCategory == TEXT("class") || TypeCategory == TEXT("softclass") || - TypeCategory == TEXT("interface")) - { - ResolveInput = TypeCategory + TEXT(":") + NewType; - } - - if (!UMCPTypes::TextToType(ResolveInput, NewPinType)) - return; - - // List affected nodes (get/set nodes for this variable) - FName VarFName = Found->VarName; - auto AppendAffectedNodes = [&](const auto& NodeArray, const TCHAR* NodeType) - { - for (auto* VarNode : NodeArray) - { - if (VarNode->GetVarName() != VarFName) continue; - UMCPServer::Printf(TEXT(" %s %s in %s\n"), NodeType, - *MCPUtils::FormatName(static_cast(VarNode)), - *MCPUtils::FormatName(VarNode->GetGraph())); - for (UEdGraphPin* Pin : VarNode->Pins) - { - if (!Pin || Pin->LinkedTo.Num() == 0) continue; - if (NodeType[0] == 'G' && Pin->Direction != EGPD_Output) continue; // Get nodes: only output pins - UMCPServer::Printf(TEXT(" %s connected to %d pin(s)\n"), - *MCPUtils::FormatName(Pin), Pin->LinkedTo.Num()); - } - } - }; - - auto GetNodes = MCPUtils::AllNodes(BP); - auto SetNodes = MCPUtils::AllNodes(BP); - - bool bHasAffected = false; - for (auto* VG : GetNodes) if (VG->GetVarName() == VarFName) { bHasAffected = true; break; } - if (!bHasAffected) - for (auto* VS : SetNodes) if (VS->GetVarName() == VarFName) { bHasAffected = true; break; } - - if (DryRun) - { - UMCPServer::Printf(TEXT("Dry run: would change %s from %s to %s\n"), - *MCPUtils::FormatName(*Found), - *UMCPTypes::TypeToText(Found->VarType), - *UMCPTypes::TypeToText(NewPinType)); - if (bHasAffected) - { - UMCPServer::Print(TEXT("Affected nodes:\n")); - AppendAffectedNodes(GetNodes, TEXT("Get")); - AppendAffectedNodes(SetNodes, TEXT("Set")); - } - return; - } - - // Apply the type change - Found->VarType = NewPinType; - - bool bSaved = MCPUtils::SaveBlueprintPackage(BP); - UMCPServer::Printf(TEXT("Changed %s to %s.%s\n"), - *MCPUtils::FormatName(*Found), - *UMCPTypes::TypeToText(NewPinType), - bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); - - if (bHasAffected) - { - UMCPServer::Print(TEXT("Affected nodes:\n")); - AppendAffectedNodes(GetNodes, TEXT("Get")); - AppendAffectedNodes(SetNodes, TEXT("Set")); - } - } -}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_RemoveVariable.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_RemoveVariable.h deleted file mode 100644 index 0d7df465..00000000 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/HalfBaked/Blueprint_RemoveVariable.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "MCPServer.h" -#include "MCPHandler.h" -#include "MCPFetcher.h" -#include "MCPUtils.h" -#include "Engine/Blueprint.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Blueprint_RemoveVariable.generated.h" - - -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- -// --------------------------------------------------------------------------- - -UCLASS() -class UMCP_Blueprint_RemoveVariable : public UObject, public IMCPHandler -{ - GENERATED_BODY() - -public: - UPROPERTY(meta=(Description="Blueprint name or package path")) - FString Blueprint; - - UPROPERTY(meta=(Description="Name of the variable to remove")) - FString VariableName; - - virtual FString GetDescription() const override - { - return TEXT("Remove a member variable from a Blueprint."); - } - - virtual void Handle() override - { - MCPFetcher F; - UBlueprint* BP = F.Walk(Blueprint).Cast(); - if (!BP) return; - - // Find the variable using Identifies for consistent name matching - const FBPVariableDescription* Found = nullptr; - for (const FBPVariableDescription& Var : BP->NewVariables) - { - if (MCPUtils::FormatName(Var) == VariableName || - Var.VarName.ToString().Equals(VariableName, ESearchCase::IgnoreCase)) - { - Found = &Var; - break; - } - } - - if (!Found) - { - UMCPServer::Printf(TEXT("ERROR: Variable '%s' not found in %s.\nAvailable variables:\n"), - *VariableName, *MCPUtils::FormatName(BP)); - for (const FBPVariableDescription& Var : BP->NewVariables) - { - UMCPServer::Printf(TEXT(" %s\n"), *MCPUtils::FormatName(Var)); - } - return; - } - - FName VarFName = Found->VarName; - - // RemoveMemberVariable also cleans up Get/Set nodes - FBlueprintEditorUtils::RemoveMemberVariable(BP, VarFName); - - bool bSaved = MCPUtils::SaveBlueprintPackage(BP); - UMCPServer::Printf(TEXT("Removed variable %s from %s.%s\n"), - *VarFName.ToString(), *MCPUtils::FormatName(BP), - bSaved ? TEXT("") : TEXT(" WARNING: save failed.")); - } -}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Create.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Create.h new file mode 100644 index 00000000..b97026ae --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Create.h @@ -0,0 +1,79 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPServer.h" +#include "MCPHandler.h" +#include "MCPFetcher.h" +#include "MCPJson.h" +#include "MCPProperty.h" +#include "BPVarEditor.h" +#include "MCPUtils.h" +#include "MCPTypes.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "BlueprintVariable_Create.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UMCP_BlueprintVariable_Create : public UObject, public IMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Name of the new variable")) + FString Name; + + UPROPERTY(meta=(Optional, Description="Variable configuration: VarType, Category, DefaultValue, InstanceEditable, BlueprintReadOnly, ExposeOnSpawn, Private, ExposeToCinematics, etc.")) + FMCPJsonObject Config; + + virtual FString GetDescription() const override + { + return TEXT("Add a new member variable to a Blueprint. Pass Config to set type, category, flags, etc."); + } + + virtual void Handle() override + { + MCPFetcher F; + UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast(); + if (!BP) return; + + // Check for duplicate variable name + FName VarFName(*Name); + if (FBlueprintEditorUtils::FindNewVariableIndex(BP, VarFName) != INDEX_NONE) + { + UMCPServer::Printf(TEXT("ERROR: Variable '%s' already exists in %s\n"), *Name, *MCPUtils::FormatName(BP)); + return; + } + + // Add the variable with a default type + FEdGraphPinType DefaultType; + DefaultType.PinCategory = UEdGraphSchema_K2::PC_Int; + if (!FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, DefaultType)) + { + UMCPServer::Printf(TEXT("ERROR: Failed to add variable '%s' to %s\n"), *Name, *MCPUtils::FormatName(BP)); + return; + } + + // Find the newly created variable description + FBPVarEditor Editor(BP, Name); + if (Editor.NotFound()) return; + + // Apply config if provided + if (Config.Json && Config.Json->Values.Num() > 0) + { + if (!Editor.LoadJson(Config.Json.Get())) + return; + } + + UMCPServer::Printf(TEXT("Created variable %s (%s) in %s\n"), + *Name, *UMCPTypes::TypeToText(Editor.Desc->VarType), *MCPUtils::FormatName(BP)); + } +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Delete.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Delete.h new file mode 100644 index 00000000..c2249243 --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Delete.h @@ -0,0 +1,49 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPServer.h" +#include "MCPHandler.h" +#include "MCPFetcher.h" +#include "MCPUtils.h" +#include "BPVarEditor.h" +#include "Engine/Blueprint.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "BlueprintVariable_Delete.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UMCP_BlueprintVariable_Delete : public UObject, public IMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Name of the variable to delete")) + FString Variable; + + virtual FString GetDescription() const override + { + return TEXT("Remove a member variable from a Blueprint."); + } + + virtual void Handle() override + { + MCPFetcher F; + UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast(); + if (!BP) return; + + FBPVarEditor Editor(BP, Variable); + if (Editor.NotFound()) return; + + FBlueprintEditorUtils::RemoveMemberVariable(BP, Editor.Desc->VarName); + + UMCPServer::Printf(TEXT("Removed variable %s from %s\n"), + *Variable, *MCPUtils::FormatName(BP)); + } +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Dump.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Dump.h new file mode 100644 index 00000000..3306b1d1 --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Dump.h @@ -0,0 +1,47 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPServer.h" +#include "MCPHandler.h" +#include "MCPFetcher.h" +#include "MCPUtils.h" +#include "BPVarEditor.h" +#include "Engine/Blueprint.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "BlueprintVariable_Dump.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UMCP_BlueprintVariable_Dump : public UObject, public IMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Name of the variable to inspect")) + FString Variable; + + virtual FString GetDescription() const override + { + return TEXT("Show all editable properties of a Blueprint variable."); + } + + virtual void Handle() override + { + MCPFetcher F; + UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast(); + if (!BP) return; + + FBPVarEditor Editor(BP, Variable); + if (Editor.NotFound()) return; + + UMCPServer::Printf(TEXT("Variable %s in %s:\n"), *Variable, *MCPUtils::FormatName(BP)); + Editor.Dump(); + } +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Modify.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Modify.h new file mode 100644 index 00000000..e6744135 --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Handlers/BlueprintVariable_Modify.h @@ -0,0 +1,62 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPServer.h" +#include "MCPHandler.h" +#include "MCPFetcher.h" +#include "MCPJson.h" +#include "MCPProperty.h" +#include "BPVarEditor.h" +#include "MCPUtils.h" +#include "MCPTypes.h" +#include "Engine/Blueprint.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "BlueprintVariable_Modify.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UMCP_BlueprintVariable_Modify : public UObject, public IMCPHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint name or package path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Name of the variable to modify")) + FString Variable; + + UPROPERTY(meta=(Description="Properties to change: VarType, Category, DefaultValue, InstanceEditable, BlueprintReadOnly, ExposeOnSpawn, Private, ExposeToCinematics, etc.")) + FMCPJsonObject Properties; + + virtual FString GetDescription() const override + { + return TEXT("Modify properties of an existing Blueprint variable."); + } + + virtual void Handle() override + { + MCPFetcher F; + UBlueprint* BP = F.Walk(Blueprint).ToBlueprint().Cast(); + if (!BP) return; + + FBPVarEditor Editor(BP, Variable); + if (Editor.NotFound()) return; + + if (!Properties.Json || Properties.Json->Values.Num() == 0) + { + UMCPServer::Print(TEXT("ERROR: No properties specified\n")); + return; + } + + if (!Editor.LoadJson(Properties.Json.Get())) + return; + + UMCPServer::Printf(TEXT("Modified variable %s (%s) in %s\n"), + *Variable, *UMCPTypes::TypeToText(Editor.Desc->VarType), *MCPUtils::FormatName(BP)); + } +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BPVarEditor.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BPVarEditor.cpp new file mode 100644 index 00000000..8db79383 --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/BPVarEditor.cpp @@ -0,0 +1,110 @@ +#include "BPVarEditor.h" +#include "MCPJson.h" +#include "MCPServer.h" +#include "MCPTypes.h" +#include "MCPUtils.h" +#include "EdGraphSchema_K2.h" +#include "Kismet2/BlueprintEditorUtils.h" + +FBPVarEditor::FBPVarEditor(UBlueprint* BP, const FString& VarName) +{ + FName VarFName(*VarName); + int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BP, VarFName); + if (VarIndex == INDEX_NONE) + { + UMCPServer::Printf(TEXT("ERROR: Variable '%s' not found in %s\n"), *VarName, *MCPUtils::FormatName(BP)); + return; + } + Desc = &BP->NewVariables[VarIndex]; +} + +void FBPVarEditor::Dump() +{ + Load(); + TArray Props = MergedProperties(); + for (MCPProperty& P : Props) + { + UMCPServer::Printf(TEXT(" %s %s = %s\n"), + *UMCPTypes::TypeToText(P.Prop), + *MCPUtils::FormatName(P.Prop), + *P.GetText()); + } +} + +bool FBPVarEditor::LoadJson(const FJsonObject* Json) +{ + Load(); + TArray Props = MergedProperties(); + if (!MCPJson::PopulateFromJson(Props, Json, true)) + return false; + Save(); + return true; +} + +void FBPVarEditor::Load() +{ + InstanceEditable = !(Desc->PropertyFlags & CPF_DisableEditOnInstance); + BlueprintReadOnly = (Desc->PropertyFlags & CPF_BlueprintReadOnly) != 0; + ExposeToCinematics = (Desc->PropertyFlags & CPF_Interp) != 0; + ExposeOnSpawn = Desc->HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn); + Private = Desc->HasMetaData(FBlueprintMetadata::MD_Private); + + FString Tooltip; + if (Desc->HasMetaData(TEXT("tooltip"))) + Description = Desc->GetMetaData(TEXT("tooltip")); + else + Description.Empty(); +} + +void FBPVarEditor::Save() const +{ + // CPF flags + if (InstanceEditable) + Desc->PropertyFlags &= ~CPF_DisableEditOnInstance; + else + Desc->PropertyFlags |= CPF_DisableEditOnInstance; + + if (BlueprintReadOnly) + Desc->PropertyFlags |= CPF_BlueprintReadOnly; + else + Desc->PropertyFlags &= ~CPF_BlueprintReadOnly; + + if (ExposeToCinematics) + Desc->PropertyFlags |= CPF_Interp; + else + Desc->PropertyFlags &= ~CPF_Interp; + + // Metadata flags + if (ExposeOnSpawn) + Desc->SetMetaData(FBlueprintMetadata::MD_ExposeOnSpawn, TEXT("true")); + else + Desc->RemoveMetaData(FBlueprintMetadata::MD_ExposeOnSpawn); + + if (Private) + Desc->SetMetaData(FBlueprintMetadata::MD_Private, TEXT("true")); + else + Desc->RemoveMetaData(FBlueprintMetadata::MD_Private); + + // Description/tooltip + if (!Description.IsEmpty()) + Desc->SetMetaData(TEXT("tooltip"), Description); + else + Desc->RemoveMetaData(TEXT("tooltip")); +} + +TArray FBPVarEditor::MergedProperties() +{ + TArray Props = MCPProperty::GetAll( + FBPVariableDescription::StaticStruct(), Desc, CPF_Edit); + + MCPProperty::Remove(Props, TEXT("PropertyFlags")); + MCPProperty::Remove(Props, TEXT("MetaDataArray")); + MCPProperty::Remove(Props, TEXT("VarName")); + MCPProperty::Remove(Props, TEXT("VarGuid")); + MCPProperty::Remove(Props, TEXT("DefaultValue")); + + Props.Append(MCPProperty::GetAll( + FBPVarEditor::StaticStruct(), this, (EPropertyFlags)0)); + + return Props; +} diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPJson.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPJson.cpp index 223ddc19..73095690 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPJson.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPJson.cpp @@ -6,46 +6,10 @@ #include "Dom/JsonValue.h" - -bool MCPJson::PopulateFromJson( - UStruct* StructType, void* Container, const FJsonObject* Json) +bool MCPJson::PopulateFromJson(MCPProperty& P, const FJsonObject* Json, bool AllOptional) { - bool Ok = true; - - // Build a set of known property names (as JSON keys) for the unknown-field check. - TSet KnownKeys; - TArray Properties; - - for (TFieldIterator It(StructType, EFieldIterationFlags::None); It; ++It) - { - FProperty* Prop = *It; - Properties.Add(Prop); - KnownKeys.Add(Prop->GetName()); - } - - // Check for unknown fields in the JSON - for (const auto& KV : Json->Values) - { - if (!KnownKeys.Contains(KV.Key)) - { - UMCPServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key); - Ok = false; - } - } - - // Populate each property from JSON - for (FProperty* Property : Properties) - { - if (!PopulateFromJson(Property, Container, Json)) Ok = false; - } - return Ok; -} - -bool MCPJson::PopulateFromJson( - FProperty* Property, void* Container, const FJsonObject* Json) -{ - FString JsonKey = Property->GetName(); - bool bOptional = Property->HasMetaData(TEXT("Optional")); + FString JsonKey = P.Prop->GetName(); + bool bOptional = AllOptional || P.Prop->HasMetaData(TEXT("Optional")); if (!Json->HasField(JsonKey)) { @@ -57,10 +21,10 @@ bool MCPJson::PopulateFromJson( return true; } - void* ValuePtr = Property->ContainerPtrToValuePtr(Container); + void* ValuePtr = P.Prop->ContainerPtrToValuePtr(P.Container); // Special handling for FMCPJsonObject and FMCPJsonArray - if (FStructProperty* StructProp = CastField(Property)) + if (FStructProperty* StructProp = CastField(P.Prop)) { if (StructProp->Struct == FMCPJsonObject::StaticStruct()) { @@ -91,42 +55,70 @@ bool MCPJson::PopulateFromJson( if (JsonValue->Type == EJson::Number) { double D = JsonValue->AsNumber(); - if (FIntProperty* IntProp = CastField(Property)) + if (FIntProperty* IntProp = CastField(P.Prop)) { IntProp->SetPropertyValue(ValuePtr, (int32)D); return true; } - if (FFloatProperty* FloatProp = CastField(Property)) + if (FFloatProperty* FloatProp = CastField(P.Prop)) { FloatProp->SetPropertyValue(ValuePtr, (float)D); return true; } - if (FDoubleProperty* DoubleProp = CastField(Property)) + if (FDoubleProperty* DoubleProp = CastField(P.Prop)) { DoubleProp->SetPropertyValue(ValuePtr, D); return true; } - if (FByteProperty* ByteProp = CastField(Property)) + if (FByteProperty* ByteProp = CastField(P.Prop)) { ByteProp->SetPropertyValue(ValuePtr, (uint8)D); return true; } - UMCPServer::Printf(TEXT("ERROR: '%s' received a number but expects %s\n"), *JsonKey, *Property->GetCPPType()); + UMCPServer::Printf(TEXT("ERROR: '%s' received a number but expects %s\n"), *JsonKey, *P.Prop->GetCPPType()); return false; } if (JsonValue->Type == EJson::Boolean) { - if (FBoolProperty* BoolProp = CastField(Property)) + if (FBoolProperty* BoolProp = CastField(P.Prop)) { BoolProp->SetPropertyValue(ValuePtr, JsonValue->AsBool()); return true; } - UMCPServer::Printf(TEXT("ERROR: '%s' received a boolean but expects %s\n"), *JsonKey, *Property->GetCPPType()); + UMCPServer::Printf(TEXT("ERROR: '%s' received a boolean but expects %s\n"), *JsonKey, *P.Prop->GetCPPType()); return false; } if (JsonValue->Type == EJson::String) { - FString ValueStr = JsonValue->AsString(); - const TCHAR* Result = Property->ImportText_Direct(*ValueStr, ValuePtr, nullptr, PPF_None); - if (!Result) - { - UMCPServer::Printf(TEXT("ERROR: Could not parse '%s' for parameter '%s'\n"), *ValueStr, *JsonKey); - return false; - } - return true; + return P.SetText(JsonValue->AsString()); } UMCPServer::Printf(TEXT("ERROR: '%s' must be a string, number, or boolean\n"), *JsonKey); return false; } +bool MCPJson::PopulateFromJson( + TArray& Props, const FJsonObject* Json, bool AllOptional) +{ + bool Ok = true; + + // Build a set of known property names for the unknown-field check. + TSet KnownKeys; + for (const MCPProperty& P : Props) + KnownKeys.Add(P.Prop->GetName()); + + // Check for unknown fields in the JSON + for (const auto& KV : Json->Values) + { + if (!KnownKeys.Contains(KV.Key)) + { + UMCPServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key); + Ok = false; + } + } + + // Populate each property from JSON + for (MCPProperty& P : Props) + { + if (!PopulateFromJson(P, Json, AllOptional)) Ok = false; + } + return Ok; +} + +bool MCPJson::PopulateFromJson( + UStruct* StructType, void* Container, const FJsonObject* Json) +{ + TArray Props = MCPProperty::GetAll(StructType, Container, (EPropertyFlags)0); + return PopulateFromJson(Props, Json); +} + bool MCPJson::PopulateFromJson( UStruct* StructType, void* Container, const TSharedPtr& JsonValue) diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPProperty.cpp b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPProperty.cpp index ec39fcbe..a549af4d 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPProperty.cpp +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPProperty.cpp @@ -1,16 +1,27 @@ #include "MCPProperty.h" #include "MCPUtils.h" #include "MCPServer.h" +#include "MCPTypes.h" +#include "Engine/Blueprint.h" #include "Materials/MaterialExpression.h" #include "MaterialGraph/MaterialGraphNode.h" +#include "EdGraph/EdGraphPin.h" + +static bool IsPinTypeProperty(FProperty* Prop) +{ + FStructProperty* StructProp = CastField(Prop); + return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct(); +} MCPProperty::MCPProperty(FProperty* InProp, void* InContainer) : Prop(InProp), Container(InContainer) {} FString MCPProperty::GetText() const { - FString Result; void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); + if (IsPinTypeProperty(Prop)) + return UMCPTypes::TypeToText(*static_cast(ValuePtr)); + FString Result; Prop->ExportTextItem_Direct(Result, ValuePtr, nullptr, nullptr, PPF_None); return Result; } @@ -18,6 +29,8 @@ FString MCPProperty::GetText() const bool MCPProperty::SetText(const FString& Value) { void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); + if (IsPinTypeProperty(Prop)) + return UMCPTypes::TextToType(Value, *static_cast(ValuePtr)); const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, nullptr, PPF_None); if (!ImportResult) { @@ -35,15 +48,20 @@ bool MCPProperty::SetText(const FString& Value) return true; } -void MCPProperty::Collect(UObject *Obj, TArray &Props, EPropertyFlags Flags) +void MCPProperty::Collect(UStruct* StructType, void* Container, TArray &Props, EPropertyFlags Flags) { - for (TFieldIterator It(Obj->GetClass()); It; ++It) + for (TFieldIterator It(StructType); It; ++It) { if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue; - Props.Emplace(*It, Obj); + Props.Emplace(*It, Container); } } +void MCPProperty::Remove(TArray& Props, const FString& Name) +{ + Props.RemoveAll([&](const MCPProperty& P) { return P.Prop->GetName() == Name; }); +} + TArray MCPProperty::GetAll(UObject* Obj, EPropertyFlags Flags) { if (!Obj) return {}; @@ -63,7 +81,7 @@ TArray MCPProperty::GetAll(UObject* Obj, EPropertyFlags Flags) Obj = BP->GeneratedClass->GetDefaultObject(); } - Collect(Obj, Result, Flags); + Collect(Obj->GetClass(), Obj, Result, Flags); // If it's a Material Graph node, also collect properties from // the associated material expression. @@ -72,12 +90,19 @@ TArray MCPProperty::GetAll(UObject* Obj, EPropertyFlags Flags) { if (UMaterialExpression* Expr = MatNode->MaterialExpression) { - Collect(Expr, Result, Flags); + Collect(Expr->GetClass(), Expr, Result, Flags); } } return Result; } +TArray MCPProperty::GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags) +{ + TArray Result; + Collect(StructType, Container, Result, Flags); + return Result; +} + TArray MCPProperty::GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring) { TArray All = GetAll(Obj, Flags); diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BPVarEditor.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BPVarEditor.h new file mode 100644 index 00000000..963043af --- /dev/null +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/BPVarEditor.h @@ -0,0 +1,53 @@ +#pragma once + +#include "CoreMinimal.h" +#include "MCPProperty.h" +#include "Engine/Blueprint.h" +#include "BPVarEditor.generated.h" + +// Editor-friendly view of a blueprint variable's properties. +// Wraps an FBPVariableDescription, exposing commonly-used flags +// and metadata as simple UPROPERTYs that the property system can +// populate from JSON. +USTRUCT() +struct FBPVarEditor +{ + GENERATED_BODY() + + FBPVariableDescription* Desc = nullptr; + + FBPVarEditor() = default; + FBPVarEditor(FBPVariableDescription* InDesc) : Desc(InDesc) {} + FBPVarEditor(UBlueprint* BP, const FString& VarName); + + bool NotFound() const { return Desc == nullptr; } + + UPROPERTY(EditAnywhere, meta=(Optional, Description="Variable description/tooltip")) + FString Description; + + UPROPERTY(EditAnywhere, meta=(Optional, Description="Allow editing on instances")) + bool InstanceEditable = false; + + UPROPERTY(EditAnywhere, meta=(Optional, Description="Read-only in blueprints")) + bool BlueprintReadOnly = false; + + UPROPERTY(EditAnywhere, meta=(Optional, Description="Expose as a pin when spawning")) + bool ExposeOnSpawn = false; + + UPROPERTY(EditAnywhere, meta=(Optional, Description="Private to this blueprint")) + bool Private = false; + + UPROPERTY(EditAnywhere, meta=(Optional, Description="Expose to cinematics/sequencer")) + bool ExposeToCinematics = false; + + // Load from Desc, populate from JSON, save back to Desc. + bool LoadJson(const FJsonObject* Json); + + // Print all properties and their current values. + void Dump(); + +private: + void Load(); + void Save() const; + TArray MergedProperties(); +}; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPJson.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPJson.h index 2b24cfe4..1e2ecb60 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPJson.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPJson.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "MCPHandler.h" +#include "MCPProperty.h" #include "Dom/JsonObject.h" // JSON utility functions used by MCP handlers. @@ -9,7 +10,8 @@ class MCPJson { public: - static bool PopulateFromJson(FProperty* Property, void* Container, const FJsonObject* Json); + static bool PopulateFromJson(MCPProperty& Prop, const FJsonObject* Json, bool AllOptional = false); + static bool PopulateFromJson(TArray& Props, const FJsonObject* Json, bool AllOptional = false); static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr& JsonValue); static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json); }; diff --git a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPProperty.h b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPProperty.h index ddebcad0..550b1a2b 100644 --- a/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPProperty.h +++ b/Plugins/BlueprintMCP/Source/BlueprintMCP/Public/MCPProperty.h @@ -20,11 +20,13 @@ public: explicit operator bool() const { return Prop != nullptr; } FProperty* operator->() const { return Prop; } + static void Remove(TArray& Props, const FString& Name); static TArray GetAll(UObject* Obj, EPropertyFlags Flags); + static TArray GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags); static TArray GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring); static TArray GetAllExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name); static MCPProperty GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name); private: - static void Collect(UObject *Obj, TArray &Props, EPropertyFlags Flags); + static void Collect(UStruct* StructType, void* Container, TArray &Props, EPropertyFlags Flags); };