From a18cff3fc9c0e1db00d126300a8a2041143f36ae Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 18 Mar 2026 21:48:56 -0400 Subject: [PATCH] Routines to find exactly one --- .../HalfBaked/BlueprintGraph_Create.h | 5 +- .../HalfBaked/BlueprintGraph_Rename.h | 2 +- .../HalfBaked/Blueprint_AddEventDispatcher.h | 5 +- .../Source/UEWingman/Private/WingFetcher.cpp | 15 +---- .../Source/UEWingman/Private/WingUtils.cpp | 63 ++++++++++++------- .../Source/UEWingman/Public/WingUtils.h | 37 ++++++++++- 6 files changed, 80 insertions(+), 47 deletions(-) diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Create.h b/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Create.h index b86b00f9..f0bc5810 100644 --- a/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Create.h +++ b/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Create.h @@ -51,11 +51,8 @@ public: if (!BP) return; // Check graph name uniqueness - if (!WingUtils::AllGraphsNamed(BP, Graph).IsEmpty()) - { - UWingServer::Printf(TEXT("ERROR: A graph named '%s' already exists in %s\n"), *Graph, *WingUtils::FormatName(BP)); + if (!WingUtils::FindExactlyNoneNamed(Graph, WingUtils::AllGraphs(BP))) return; - } // For custom events, also check for existing custom events with the same name if (GraphType == TEXT("customEvent")) diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Rename.h b/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Rename.h index a2da48fd..706b6b30 100644 --- a/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Rename.h +++ b/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlueprintGraph_Rename.h @@ -64,7 +64,7 @@ public: } // Check for name collision - for (UEdGraph* Existing : WingUtils::AllGraphsNamed(BP, NewName)) + for (UEdGraph* Existing : WingUtils::FindAllNamed(NewName, WingUtils::AllGraphs(BP))) { if (Existing != TargetGraph) { diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Blueprint_AddEventDispatcher.h b/Plugins/UEWingman/Source/UEWingman/HalfBaked/Blueprint_AddEventDispatcher.h index 8aa54c63..15ce4c1d 100644 --- a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Blueprint_AddEventDispatcher.h +++ b/Plugins/UEWingman/Source/UEWingman/HalfBaked/Blueprint_AddEventDispatcher.h @@ -71,11 +71,8 @@ public: } // Check against existing graphs (functions, macros, etc.) - if (!WingUtils::AllGraphsNamed(BP, DispatcherName).IsEmpty()) - { - UWingServer::Printf(TEXT("Error: A graph named '%s' already exists.\n"), *DispatcherName); + if (!WingUtils::FindExactlyNoneNamed(DispatcherName, WingUtils::AllGraphs(BP))) return; - } // Add a member variable with PC_MCDelegate pin type FEdGraphPinType DelegateType; diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp index cf404dbb..18adaa90 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp @@ -186,19 +186,10 @@ WingFetcher& WingFetcher::Graph(const FString& Value) if (!BP) return TypeMismatch(TEXT("graph"), TEXT("Blueprint or Material")); - TArray Matches = WingUtils::AllGraphsNamed(BP, Value); - if (Matches.Num() == 0) - { - UWingServer::Printf(TEXT("ERROR: Graph '%s' not found in %s\n"), *Value, *BP->GetName()); - return SetError(); - } - if (Matches.Num() > 1) - { - UWingServer::Printf(TEXT("ERROR: Ambiguous graph '%s' in %s — %d matches\n"), *Value, *BP->GetName(), Matches.Num()); - return SetError(); - } + UEdGraph* Graph = WingUtils::FindExactlyOneNamed(Value, WingUtils::AllGraphs(BP)); + if (!Graph) return SetError(); - SetObj(Matches[0]); + SetObj(Graph); return *this; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp index 6a80c9e4..3bf8aecf 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp @@ -193,25 +193,10 @@ FString WingUtils::FormatName(const FProperty *Prop) return SanitizeName(Prop->GetName()); } -// ============================================================ -// Identifies -// ============================================================ - -// Most types are handled by the template in WingUtils.h. -// UEdGraphNode also matches by GUID: - -bool WingUtils::Identifies(const FString &Name, const UEdGraphNode* Node) -{ - if (Node->NodeGuid.ToString().Equals(Name, ESearchCase::IgnoreCase)) - return true; - return FormatName(Node).Equals(Name, ESearchCase::IgnoreCase); -} - // ============================================================ // Formatting other things // ============================================================ - FString WingUtils::FormatNodeTitle(const UEdGraphNode *Node) { FString Title = Node->GetNodeTitle(ENodeTitleType::FullTitle).ToString(); @@ -284,6 +269,46 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, c return true; } + +// ============================================================ +// Common Error Reporting +// ============================================================ + +bool WingUtils::CheckExactlyOneNamed(int Count, const FString &Kind, const FString &Name) +{ + if (Count == 0) + { + UWingServer::Printf(TEXT("Could not find a %s named %s.\n"), *Kind, *Name); + return false; + } + if (Count > 1) + { + UWingServer::Printf(TEXT("More than one %s named %s\n"), *Kind, *Name); + return false; + } + return true; +} + +bool WingUtils::CheckExactlyOneNamed(int Count, UClass *Class, const FString &Name) +{ + return CheckExactlyOneNamed(Count, Class->GetName(), Name); +} + +bool WingUtils::CheckExactlyNoneNamed(int Count, const FString &Kind, const FString &Name) +{ + if (Count > 0) + { + UWingServer::Printf(TEXT("A %s named %s already exists."), *Kind, *Name); + return false; + } + return true; +} + +bool WingUtils::CheckExactlyNoneNamed(int Count, UClass *Class, const FString &Name) +{ + return CheckExactlyNoneNamed(Count, Class->GetName(), Name); +} + // ============================================================ // Blueprint helpers // ============================================================ @@ -295,14 +320,6 @@ TArray WingUtils::AllGraphs(UBlueprint* BP) return Graphs; } -TArray WingUtils::AllGraphsNamed(UBlueprint* BP, const FString& Name) -{ - TArray Result; - for (UEdGraph* Graph : AllGraphs(BP)) - if (Identifies(Name, Graph)) - Result.Add(Graph); - return Result; -} TArray WingUtils::AllNodes(UBlueprint* BP) { diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h b/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h index c7ec3c17..24d64074 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h @@ -89,8 +89,33 @@ public: return FormatName(std::forward(Obj)).Equals(Name, ESearchCase::IgnoreCase); } - // UEdGraphNode also matches by GUID. - static bool Identifies(const FString &Name, const UEdGraphNode* Node); + template + static TArray FindAllNamed(const FString &Name, const TArray &Array) + { + TArray Result; + for (T* Elt : Array) if (Identifies(Name, Elt)) Result.Add(Elt); + return Result; + } + + template + T* FindExactlyOneNamed(const FString &Name, const TArray &Array) + { + int Count = 0; + T* Result = nullptr; + for (T* Elt : Array) if (Identifies(Name, Elt)) { Count++; Result = Elt; } + if (!CheckExactlyOneNamed(Count, T::StaticClass(), Name)) return nullptr; + return Result; + } + + template + bool FindExactlyNoneNamed(const FString &Name, const TArray &Array) + { + for (T* Elt: Array) if (Identifies(Name, Elt)) + { + return CheckExactlyNoneNamed(1, T::StaticClass(), Name); + } + return true; + } //////////////////////////////////////////////////////// @@ -117,7 +142,6 @@ public: // ----- Blueprint helpers ----- static TArray AllGraphs(UBlueprint* BP); - static TArray AllGraphsNamed(UBlueprint* BP, const FString& Name); static TArray AllNodes(UBlueprint* BP); template static TArray AllNodes(UBlueprint* BP) { @@ -136,6 +160,7 @@ public: Result.Add(Typed); return Result; } + static bool SaveBlueprintPackage(UBlueprint* BP); static UClass* FindClassByName(const FString& ClassName); @@ -173,6 +198,12 @@ public: static FString GetHandlerGroup(UClass* HandlerClass); static void FormatCommandHelp(UClass* HandlerClass); + // ----- Common Error Reporting ----- + bool CheckExactlyOneNamed(int Count, const FString &Kind, const FString &Name); + bool CheckExactlyOneNamed(int Count, UClass *Class, const FString &Name); + bool CheckExactlyNoneNamed(int Count, const FString &Kind, const FString &Name); + bool CheckExactlyNoneNamed(int Count, UClass *Class, const FString &Name); + private: static void AppendNumericSuffix(FString &Name, int32 N); };