diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintVariable_Remove.h b/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintVariable_Remove.h new file mode 100644 index 00000000..f3778b30 --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintVariable_Remove.h @@ -0,0 +1,43 @@ +#pragma once + +#include "CoreMinimal.h" +#include "WingServer.h" +#include "WingHandler.h" +#include "WingFetcher.h" +#include "WingVariables.h" +#include "BlueprintVariable_Remove.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UWing_BlueprintVariable_Remove : public UObject, public IWingHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Blueprint path")) + FString Blueprint; + + UPROPERTY(meta=(Description="Variable names to remove, comma-separated")) + FString Variables; + + virtual FString GetDescription() const override + { + return TEXT("Remove member variables from a Blueprint."); + } + + virtual void Handle() override + { + WingFetcher F; + UBlueprint* BP = F.Walk(Blueprint).Cast(); + if (!BP) return; + + WingVariables Vars(BP); + if (!Vars.BlueprintVariables.ParseNamesString(Variables)) return; + if (!Vars.Remove()) return; + UWingServer::Printf(TEXT("Success.\n")); + } +}; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphVariables_Remove.h b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphVariables_Remove.h new file mode 100644 index 00000000..10b01154 --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphVariables_Remove.h @@ -0,0 +1,51 @@ +#pragma once + +#include "CoreMinimal.h" +#include "WingServer.h" +#include "WingHandler.h" +#include "WingFetcher.h" +#include "WingVariables.h" +#include "GraphVariables_Remove.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UWing_GraphVariables_Remove : public UObject, public IWingHandler +{ + GENERATED_BODY() + +public: + UPROPERTY(meta=(Description="Path to a function or macro graph")) + FString Graph; + + UPROPERTY(meta=(Optional, Description="Input variable names to remove, comma-separated")) + FString InputVariables; + + UPROPERTY(meta=(Optional, Description="Output variable names to remove, comma-separated")) + FString OutputVariables; + + UPROPERTY(meta=(Optional, Description="Local variable names to remove, comma-separated")) + FString LocalVariables; + + virtual FString GetDescription() const override + { + return TEXT("Remove inputs, outputs, and local variables from a graph."); + } + + virtual void Handle() override + { + WingFetcher F; + UEdGraph* G = F.Walk(Graph).Cast(); + if (!G) return; + + WingVariables Vars(G); + if (!Vars.InputVariables.ParseNamesString(InputVariables)) return; + if (!Vars.OutputVariables.ParseNamesString(OutputVariables)) return; + if (!Vars.LocalVariables.ParseNamesString(LocalVariables)) return; + if (!Vars.Remove()) return; + UWingServer::Printf(TEXT("Success.\n")); + } +}; diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp index e92dea1d..7e25ca75 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp @@ -386,37 +386,25 @@ bool WingVariables::Modify() bool WingVariables::ModifyBlueprint() { if (!CheckBlueprint()) return false; - ClearLinks(); - - // Link our variables to blueprint variable descriptions. - for (Var &V : BlueprintVariables.Variables) - { - V.BPVar = WingUtils::FindOneWithInternalID(V.Name, Blueprint->NewVariables, TEXT("non-inherited variable")); - if (V.BPVar == nullptr) return false; - } - - // Update the type and the flags. - bool AnyDefaults = false; - for (Var &V : BlueprintVariables.Variables) + if (LinkBlueprintVariables()) return false; + for (Var &V : BlueprintVariables.Variables) { V.BPVar->VarType = V.Type; ModifyBlueprintVariableFlags(V); - if (V.DefaultSpecified) AnyDefaults = true; } + if (!ModifyBlueprintDefaults()) return false; + return true; +} - // Next step not needed if no defaults specified. - if (!AnyDefaults) return true; - - FKismetEditorUtilities::CompileBlueprint(Blueprint); - - UObject *CDO = WingUtils::GetGeneratedCDO(Blueprint); - if (!CDO) +bool WingVariables::LinkBlueprintVariables() +{ + ClearLinks(); + for (Var &V : BlueprintVariables.Variables) { - UWingServer::Printf(TEXT("Cannot store variable defaults, blueprint didn't compile")); - return false; + V.BPVar = WingUtils::FindOneWithInternalID( + V.Name, Blueprint->NewVariables, TEXT("non-inherited variable")); + if (V.BPVar == nullptr) return false; } - if (!ModifyDefaultsAsProperties(BlueprintVariables, CDO)) return false; - return true; } @@ -451,9 +439,22 @@ void WingVariables::ModifyBlueprintVariableFlags(Var &Input) Out.RemoveMetaData(FBlueprintMetadata::MD_Private); } -bool WingVariables::ModifyDefaultsAsProperties(WingVariableList &List, UObject *CDO) +bool WingVariables::ModifyBlueprintDefaults() { - for (Var &Input : List.Variables) + bool AnySpecified = false; + for (Var &Input : BlueprintVariables.Variables) + if (Input.DefaultSpecified) AnySpecified = true; + if (!AnySpecified) return true; + + FKismetEditorUtilities::CompileBlueprint(Blueprint); + UObject *CDO = WingUtils::GetGeneratedCDO(Blueprint); + if (!CDO) + { + UWingServer::Printf(TEXT("Blueprint didn't compile, cannot store default values")); + return false; + } + + for (Var &Input : BlueprintVariables.Variables) { if (Input.DefaultSpecified) { @@ -548,30 +549,169 @@ bool WingVariables::GetGraphNodes( LocalNode = Locals; return true; } - // // Create the variables. - // for (const WingVariables::Var& V : Vars.GetVariables()) - // { - // if (!FBlueprintEditorUtils::AddMemberVariable(BP, V.Name, V.Type)) - // { - // UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s'\n"), - // *WingUtils::ExternalizeID(V.Name)); - // return; - // } - // } - // // Check for name collisions against existing variables, components, and the like. - // TSet Names; - // FBlueprintEditorUtils::GetClassVariableList(BP, Names); - // if (!WingUtils::FindNoDuplicateNames(Names, Vars.GetVariables(), TEXT("variable or component"))) return; +bool WingVariables::Create() +{ + if (Blueprint) return CreateBlueprint(); + if (Graph) return CreateGraph(); + check(false); + return false; +} +bool WingVariables::CreateBlueprint() +{ + if (!CheckBlueprint()) return false; + ClearLinks(); - // // Check for duplicate names against existing pins and locals. - // TSet Names; - // WingUtils::FindNoDuplicateNames(Names, EntryNode->UserDefinedPins, TEXT("local variables")); - // WingUtils::FindNoDuplicateNames(Names, ResultNode->UserDefinedPins, TEXT("local variables")); - // if (UK2Node_FunctionEntry* FuncEntry = Cast(EntryNode.Get())) - // WingUtils::FindNoDuplicateNames(Names, FuncEntry->LocalVariables, TEXT("local variables")); - // if (!WingUtils::FindNoDuplicateNames(Names, Vars.Arguments.GetVariables(), TEXT("local variables"))) return; - // if (!WingUtils::FindNoDuplicateNames(Names, Vars.ReturnValues.GetVariables(), TEXT("local variables"))) return; - // if (!WingUtils::FindNoDuplicateNames(Names, Vars.LocalVariables.GetVariables(), TEXT("local variables"))) return; + // Check for name collisions against existing variables, components, and the like. + TSet Names; + FBlueprintEditorUtils::GetClassVariableList(Blueprint, Names); + if (!WingUtils::FindNoDuplicateNames(Names, BlueprintVariables.Variables, TEXT("variable or component"))) return false; + // Create the variables. + for (const WingVariables::Var& V : BlueprintVariables.Variables) + { + if (!FBlueprintEditorUtils::AddMemberVariable(Blueprint, V.Name, V.Type)) + { + UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s'\n"), + *WingUtils::ExternalizeID(V.Name)); + return false; + } + } + + if (LinkBlueprintVariables()) return false; + for (Var &V : BlueprintVariables.Variables) ModifyBlueprintVariableFlags(V); + if (!ModifyBlueprintDefaults()) return false; + return false; +} + +bool WingVariables::CreateGraph() +{ + if (!CheckGraph()) return false; + ClearLinks(); + + UK2Node_EditablePinBase *InputNode, *OutputNode; + UK2Node_FunctionEntry *LocalNode; + if (!GetGraphNodes(InputNode, OutputNode, LocalNode)) return false; + + // Check for name collisions against existing local variables. + const TCHAR *ctx = TEXT("local, function input, and function output variables"); + TSet Names; + if (InputNode && !WingUtils::FindNoDuplicateNames( + Names, InputNode->UserDefinedPins, ctx)) return false; + if (OutputNode && !WingUtils::FindNoDuplicateNames( + Names, OutputNode->UserDefinedPins, ctx)) return false; + if (LocalNode && !WingUtils::FindNoDuplicateNames( + Names, LocalNode->LocalVariables, ctx)) return false; + if (!WingUtils::FindNoDuplicateNames( + Names, InputVariables.Variables, ctx)) return false; + if (!WingUtils::FindNoDuplicateNames( + Names, OutputVariables.Variables, ctx)) return false; + if (!WingUtils::FindNoDuplicateNames( + Names, LocalVariables.Variables, ctx)) return false; + + // Create input pins on the input node. + for (const Var& V : InputVariables.Variables) + AddUserPinInfo(V, EGPD_Output, InputNode); + + // Create output pins on the output node. + for (const Var& V : OutputVariables.Variables) + AddUserPinInfo(V, EGPD_Input, OutputNode); + + // Create local variables via the proper API. + UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(Graph); + for (const Var& V : LocalVariables.Variables) + { + if (!FBlueprintEditorUtils::AddLocalVariable(BP, Graph, V.Name, V.Type, V.DefaultValue)) + { + UWingServer::Printf(TEXT("ERROR: Failed to create local variable '%s'\n"), + *WingUtils::ExternalizeID(V.Name)); + return false; + } + } + + if (InputNode) InputNode->ReconstructNode(); + if (OutputNode) OutputNode->ReconstructNode(); + return true; +} + +void WingVariables::AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node) +{ + TSharedPtr Result = MakeShareable( new FUserPinInfo() ); + Result->PinName = V.Name; + Result->PinType = V.Type; + Result->PinDefaultValue = V.DefaultValue; + Result->DesiredPinDirection = Dir; + Node->UserDefinedPins.Add(Result); +} + +bool WingVariables::Remove() +{ + if (Blueprint) return RemoveBlueprint(); + if (Graph) return RemoveGraph(); + check(false); + return false; +} + +bool WingVariables::RemoveBlueprint() +{ + // Verify that all named variables exist. + TArray Names; + for (const Var& V : BlueprintVariables.Variables) + { + FBPVariableDescription* Found = + WingUtils::FindOneWithInternalID(V.Name, Blueprint->NewVariables, TEXT("non-inherited variable")); + if (!Found) return false; + Names.Add(V.Name); + } + + // Remove them. + FBlueprintEditorUtils::BulkRemoveMemberVariables(Blueprint, Names); + return true; +} + +bool WingVariables::RemoveGraph() +{ + UK2Node_EditablePinBase *InputNode, *OutputNode; + UK2Node_FunctionEntry *LocalNode; + if (!GetGraphNodes(InputNode, OutputNode, LocalNode)) return false; + + // Verify that all named variables exist before removing anything. + for (const Var& V : InputVariables.Variables) + { + TSharedPtr* Found = + WingUtils::FindOneWithInternalID(V.Name, InputNode->UserDefinedPins, TEXT("input variable")); + if (!Found) return false; + } + for (const Var& V : OutputVariables.Variables) + { + TSharedPtr* Found = + WingUtils::FindOneWithInternalID(V.Name, OutputNode->UserDefinedPins, TEXT("output variable")); + if (!Found) return false; + } + for (const Var& V : LocalVariables.Variables) + { + FBPVariableDescription* Found = + WingUtils::FindOneWithInternalID(V.Name, LocalNode->LocalVariables, TEXT("local variable")); + if (!Found) return false; + } + + // Remove input and output pins. + for (const Var& V : InputVariables.Variables) + InputNode->RemoveUserDefinedPinByName(V.Name); + for (const Var& V : OutputVariables.Variables) + OutputNode->RemoveUserDefinedPinByName(V.Name); + + // Remove local variables. + UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(Graph); + for (const Var& V : LocalVariables.Variables) + { + LocalNode->LocalVariables.RemoveAll( + [&](const FBPVariableDescription& Desc) { return Desc.VarName == V.Name; }); + FBlueprintEditorUtils::RemoveVariableNodes(BP, V.Name, true, Graph); + } + + if (InputNode) InputNode->ReconstructNode(); + if (OutputNode) OutputNode->ReconstructNode(); + return true; +} diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h b/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h index a4078cdd..90303f4e 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h @@ -124,7 +124,11 @@ public: // Create every variable in the workspace in the backing store. - bool Create() { return true; } + bool Create(); + + // Remove every variable mentioned in the workspace from the backing store. + + bool Remove(); private: void LoadBlueprint(); @@ -138,14 +142,24 @@ private: bool CheckGraph(); bool ModifyBlueprint(); + bool LinkBlueprintVariables(); void ModifyBlueprintVariableFlags(Var &Input); - bool ModifyDefaultsAsProperties(WingVariableList &List, UObject *CDO); + bool ModifyBlueprintDefaults(); bool ModifyGraph(); + bool CreateBlueprint(); + + bool CreateGraph(); + + bool RemoveBlueprint(); + bool RemoveGraph(); + bool GetGraphNodes( UK2Node_EditablePinBase *&InputNode, UK2Node_EditablePinBase *&OutputNode, UK2Node_FunctionEntry *&LocalNode); + + void AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node); };