diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/AnimBlueprint_Create.h b/Plugins/UEWingman/Deprecated/AnimBlueprint_Create.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/AnimBlueprint_Create.h rename to Plugins/UEWingman/Deprecated/AnimBlueprint_Create.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/AnimBlueprint_SetBlendSpaceSamples.h b/Plugins/UEWingman/Deprecated/AnimBlueprint_SetBlendSpaceSamples.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/AnimBlueprint_SetBlendSpaceSamples.h rename to Plugins/UEWingman/Deprecated/AnimBlueprint_SetBlendSpaceSamples.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/BlendSpace_Create.h b/Plugins/UEWingman/Deprecated/BlendSpace_Create.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/BlendSpace_Create.h rename to Plugins/UEWingman/Deprecated/BlendSpace_Create.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Blueprint_Diff.h b/Plugins/UEWingman/Deprecated/Blueprint_Diff.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Blueprint_Diff.h rename to Plugins/UEWingman/Deprecated/Blueprint_Diff.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Class_Search.h b/Plugins/UEWingman/Deprecated/Class_Search.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Class_Search.h rename to Plugins/UEWingman/Deprecated/Class_Search.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Class_ShowProperties.h b/Plugins/UEWingman/Deprecated/Class_ShowProperties.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Class_ShowProperties.h rename to Plugins/UEWingman/Deprecated/Class_ShowProperties.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Enum_Create.h b/Plugins/UEWingman/Deprecated/Enum_Create.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Enum_Create.h rename to Plugins/UEWingman/Deprecated/Enum_Create.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Enum_Modify.h b/Plugins/UEWingman/Deprecated/Enum_Modify.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Enum_Modify.h rename to Plugins/UEWingman/Deprecated/Enum_Modify.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/MaterialFunction_Create.h b/Plugins/UEWingman/Deprecated/MaterialFunction_Create.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/MaterialFunction_Create.h rename to Plugins/UEWingman/Deprecated/MaterialFunction_Create.h diff --git a/Plugins/UEWingman/Deprecated/OldSearchNodeSpawners.cpp b/Plugins/UEWingman/Deprecated/OldSearchNodeSpawners.cpp new file mode 100644 index 00000000..8aebfa47 --- /dev/null +++ b/Plugins/UEWingman/Deprecated/OldSearchNodeSpawners.cpp @@ -0,0 +1,26 @@ +// This code was extracted from git commit 0e79b02^ (the commit before it was +// replaced with GetGraphContextActions). Original file was: +// Plugins/BlueprintMCP/Source/BlueprintMCP/Private/MCPUtils.cpp +// +// It uses FBlueprintActionDatabase to search for spawnable node types, +// which is the correct approach for Blueprint (K2) graphs. + +#include "BlueprintActionDatabase.h" +#include "BlueprintNodeSpawner.h" + +FString MCPUtils::NodeSpawnerFullName(UBlueprintNodeSpawner* Spawner) +{ + const FBlueprintActionUiSpec& UiSpec = Spawner->PrimeDefaultUiSpec(); + FString Category = UiSpec.Category.ToString(); + FString MenuName = UiSpec.MenuName.ToString(); + if (Category.IsEmpty()) + { + return MenuName; + } + return Category + TEXT("|") + MenuName; +} + +TArray MCPUtils::SearchNodeSpawners(const FString& Query, int32 MaxResults, bool ExactMatch, UEdGraph* GraphFilter) +{ + return Result; +} diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_AddState.h b/Plugins/UEWingman/Deprecated/StateMachine_AddState.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_AddState.h rename to Plugins/UEWingman/Deprecated/StateMachine_AddState.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_AddTransition.h b/Plugins/UEWingman/Deprecated/StateMachine_AddTransition.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_AddTransition.h rename to Plugins/UEWingman/Deprecated/StateMachine_AddTransition.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_RemoveState.h b/Plugins/UEWingman/Deprecated/StateMachine_RemoveState.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_RemoveState.h rename to Plugins/UEWingman/Deprecated/StateMachine_RemoveState.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_SetAnimation.h b/Plugins/UEWingman/Deprecated/StateMachine_SetAnimation.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_SetAnimation.h rename to Plugins/UEWingman/Deprecated/StateMachine_SetAnimation.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_SetBlendSpace.h b/Plugins/UEWingman/Deprecated/StateMachine_SetBlendSpace.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_SetBlendSpace.h rename to Plugins/UEWingman/Deprecated/StateMachine_SetBlendSpace.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_SetTransitionRule.h b/Plugins/UEWingman/Deprecated/StateMachine_SetTransitionRule.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/StateMachine_SetTransitionRule.h rename to Plugins/UEWingman/Deprecated/StateMachine_SetTransitionRule.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Struct_Create.h b/Plugins/UEWingman/Deprecated/Struct_Create.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Struct_Create.h rename to Plugins/UEWingman/Deprecated/Struct_Create.h diff --git a/Plugins/UEWingman/Source/UEWingman/HalfBaked/Struct_Modify.h b/Plugins/UEWingman/Deprecated/Struct_Modify.h similarity index 100% rename from Plugins/UEWingman/Source/UEWingman/HalfBaked/Struct_Modify.h rename to Plugins/UEWingman/Deprecated/Struct_Modify.h diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Create.h b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Create.h index 84ada828..267d20c8 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Create.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Create.h @@ -6,6 +6,7 @@ #include "WingFetcher.h" #include "WingJson.h" #include "WingUtils.h" +#include "WingGraphActions.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphSchema.h" @@ -66,26 +67,25 @@ public: continue; // Find the action by exact full name - TArray> Matches = WingUtils::SearchGraphActions(TargetGraph, Entry.ActionName, 2, /*ExactMatch=*/true); - if (Matches.Num() == 0) + FWingGraphActions GA(TargetGraph, Entry.ActionName, 2, /*ExactMatch=*/true); + if (GA.Count() == 0) { UWingServer::Printf(TEXT("ERROR: No action found matching '%s'. Use GraphNodeSearchTypes to find available actions.\n"), *Entry.ActionName); continue; } - if (Matches.Num() > 1) + if (GA.Count() > 1) { UWingServer::Printf(TEXT("ERROR: Ambiguous: %d actions match '%s'.\n"), - Matches.Num(), *Entry.ActionName); + GA.Count(), *Entry.ActionName); continue; } // Perform the action - FVector2D Location(Entry.PosX, Entry.PosY); - UEdGraphNode* NewNode = Matches[0]->PerformAction(TargetGraph, nullptr, Location, /*bSelectNewNode=*/false); + UEdGraphNode* NewNode = GA.Execute(0, Entry.PosX, Entry.PosY); if (!NewNode) { - UWingServer::Printf(TEXT("ERROR: PerformAction returned null for '%s'.\n"), *Entry.ActionName); + UWingServer::Printf(TEXT("ERROR: Execute returned null for '%s'.\n"), *Entry.ActionName); continue; } diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SearchTypes.h b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SearchTypes.h index cf20fd1c..7a8862a8 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SearchTypes.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SearchTypes.h @@ -5,6 +5,7 @@ #include "WingHandler.h" #include "WingFetcher.h" #include "WingUtils.h" +#include "WingGraphActions.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphSchema.h" #include "GraphNode_SearchTypes.generated.h" @@ -44,18 +45,18 @@ public: UEdGraph* TargetGraph = F.Walk(Graph).Cast(); if (!TargetGraph) return; - TArray> Actions = WingUtils::SearchGraphActions(TargetGraph, Query, ClampedMax, /*ExactMatch=*/false); + FWingGraphActions GA(TargetGraph, Query, ClampedMax, /*ExactMatch=*/false); - for (const TSharedPtr& Action : Actions) + for (int32 i = 0; i < GA.Count(); i++) { - UWingServer::Printf(TEXT("%s\n"), *WingUtils::ActionFullName(Action)); + UWingServer::Printf(TEXT("%s\n"), *GA.GetFullName(i)); } - if (Actions.Num() == 0) + if (GA.Count() == 0) { UWingServer::Print(TEXT("No matching node types found.\n")); } - else if (Actions.Num() >= ClampedMax) + else if (GA.Count() >= ClampedMax) { UWingServer::Printf(TEXT("WARNING: Reached limit of %d results. Refine your query or increase MaxResults.\n"), ClampedMax); } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingGraphActions.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingGraphActions.cpp new file mode 100644 index 00000000..1925d16b --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingGraphActions.cpp @@ -0,0 +1,138 @@ +#include "WingGraphActions.h" +#include "EdGraph/EdGraphSchema.h" +#include "BlueprintActionDatabase.h" +#include "BlueprintNodeSpawner.h" +#include "EdGraphSchema_K2.h" +#include "Kismet2/BlueprintEditorUtils.h" + +FString FWingGraphActions::GetFullName(const FEdGraphSchemaAction& Action) +{ + FString Category = Action.GetCategory().ToString(); + FString MenuName = Action.GetMenuDescription().ToString(); + return Category + TEXT("|") + MenuName; +} + +FString FWingGraphActions::GetFullName(const UBlueprintNodeSpawner* Spawner) +{ + const FBlueprintActionUiSpec& UiSpec = Spawner->PrimeDefaultUiSpec(); + FString Category = UiSpec.Category.ToString(); + FString MenuName = UiSpec.MenuName.ToString(); + return Category + TEXT("|") + MenuName; +} + +bool FWingGraphActions::IsMatch(const FEdGraphSchemaAction& Action, const FString &QueryLower, bool Exact) +{ + FString FullName = GetFullName(Action).ToLower(); + if (FullName.Len() < 3) return false; + if (FullName == QueryLower) return true; + if (Exact) return false; + if (FullName.Contains(QueryLower)) return true; + FString Keywords = Action.GetKeywords().ToString(); + if (Keywords.ToLower().Contains(QueryLower)) return true; + return false; +} + +bool FWingGraphActions::IsMatch(const UBlueprintNodeSpawner* Spawner, const FString &QueryLower, bool Exact) +{ + FString FullName = GetFullName(Spawner).ToLower(); + if (FullName.Len() < 3) return false; + if (FullName == QueryLower) return true; + if (Exact) return false; + if (FullName.Contains(QueryLower)) return true; + FString Keywords = Spawner->PrimeDefaultUiSpec().Keywords.ToString(); + if (Keywords.ToLower().Contains(QueryLower)) return true; + return false; +} + +UEdGraphNode* FWingGraphActions::Execute(FEdGraphSchemaAction& Action, int32 X, int32 Y) +{ + FVector2D Location(X, Y); + UEdGraphNode* NewNode = Action.PerformAction(Graph, nullptr, Location, /*bSelectNewNode=*/false); + return NewNode; +} + +UEdGraphNode* FWingGraphActions::Execute(UBlueprintNodeSpawner* Spawner, int32 X, int32 Y) +{ + FVector2D Location(X, Y); + IBlueprintNodeBinder::FBindingSet Bindings; + return Spawner->Invoke(Graph, Bindings, Location); +} + +void FWingGraphActions::CollectActions(UEdGraph* GraphP, const FString& Query, int32 MaxResults, bool ExactMatch) +{ + Graph = GraphP; + FString QueryLower = Query.ToLower(); + FGraphContextMenuBuilder ContextMenuBuilder(Graph); + Graph->GetSchema()->GetGraphContextActions(ContextMenuBuilder); + + for (int32 i = 0; i < ContextMenuBuilder.GetNumActions(); i++) + { + if ((MaxResults > 0) && (Actions.Num() >= MaxResults)) break; + TSharedPtr Action = ContextMenuBuilder.GetSchemaAction(i); + if (IsMatch(*Action, QueryLower, ExactMatch)) Actions.Add(Action); + } +} + +void FWingGraphActions::CollectSpawners(UEdGraph* GraphP, const FString& Query, int32 MaxResults, bool ExactMatch) +{ + Graph = GraphP; + FString QueryLower = Query.ToLower(); + + for (const auto& Pair : FBlueprintActionDatabase::Get().GetAllActions()) + { + for (UBlueprintNodeSpawner* Spawner : Pair.Value) + { + if ((MaxResults > 0) && (Spawners.Num() >= MaxResults)) break; + + if (!Spawner) continue; + + // Filter by graph compatibility if a graph was provided + if (Spawner->NodeClass) + { + UEdGraphNode* NodeCDO = CastChecked(Spawner->NodeClass->ClassDefaultObject); + if (!NodeCDO->IsCompatibleWithGraph(Graph)) + { + continue; + } + } + + if (IsMatch(Spawner, QueryLower, ExactMatch)) Spawners.Add(Spawner); + } + } +} + +FString FWingGraphActions::GetFullName(int N) +{ + if (N < Actions.Num()) + { + return GetFullName(*Actions[N]); + } + else + { + return GetFullName(Spawners[N - Actions.Num()]); + } +} + +UEdGraphNode* FWingGraphActions::Execute(int32 N, int32 PosX, int32 PosY) +{ + if (N < Actions.Num()) + { + return Execute(*Actions[N], PosX, PosY); + } + else + { + return Execute(Spawners[N - Actions.Num()], PosX, PosY); + } +} + +FWingGraphActions::FWingGraphActions(UEdGraph *Graph, const FString& Query, int32 MaxResults, bool ExactMatch) +{ + if (Cast(Graph->GetSchema())) + { + CollectSpawners(Graph, Query, MaxResults, ExactMatch); + } + else + { + CollectActions(Graph, Query, MaxResults, ExactMatch); + } +} diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp index 980bae88..1a88d31b 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp @@ -471,55 +471,6 @@ UAnimStateTransitionNode* WingUtils::FindTransition(UAnimationStateMachineGraph* return nullptr; } -// ============================================================ -// Graph actions (node spawning) -// ============================================================ - -FString WingUtils::ActionFullName(const TSharedPtr& Action) -{ - FString Category = Action->GetCategory().ToString(); - FString MenuName = Action->GetMenuDescription().ToString(); - if (Category.IsEmpty()) - return MenuName; - return Category + TEXT("|") + MenuName; -} - -TArray> WingUtils::SearchGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults, bool ExactMatch) -{ - FString QueryLower = Query.ToLower(); - TArray> Result; - - FGraphContextMenuBuilder ContextMenuBuilder(Graph); - Graph->GetSchema()->GetGraphContextActions(ContextMenuBuilder); - - for (int32 i = 0; i < ContextMenuBuilder.GetNumActions(); i++) - { - TSharedPtr Action = ContextMenuBuilder.GetSchemaAction(i); - if (!Action.IsValid()) continue; - - FString FullName = ActionFullName(Action); - if (FullName.IsEmpty()) continue; - - if (ExactMatch) - { - if (FullName.ToLower() != QueryLower) - continue; - } - else - { - FString Keywords = Action->GetKeywords().ToString(); - if (!FullName.ToLower().Contains(QueryLower) && !Keywords.ToLower().Contains(QueryLower)) - continue; - } - - Result.Add(Action); - if ((MaxResults > 0) && (Result.Num() >= MaxResults)) - break; - } - - return Result; -} - // ============================================================ // Support for locating UE Wingman Handlers // ============================================================ diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingGraphActions.h b/Plugins/UEWingman/Source/UEWingman/Public/WingGraphActions.h new file mode 100644 index 00000000..4deaaf54 --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingGraphActions.h @@ -0,0 +1,51 @@ +#pragma once + +#include "CoreMinimal.h" +#include "EdGraph/EdGraph.h" + +class UBlueprintNodeSpawner; +struct FEdGraphSchemaAction; + +// Holds a collection of graph actions, populated from either the +// BlueprintActionDatabase (for K2 graphs) or GetGraphContextActions +// (for everything else). +struct FWingGraphActions +{ +public: + // Constructor populates the list of actions. + FWingGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults = 0, bool ExactMatch=false); + + // Get the number of results. + int Count() const { return Spawners.Num() + Actions.Num(); } + + // Get the name of the nth result. + FString GetFullName(int32 N); + + // Execute the nth result, which should create a graph node. + // If it can't, it will print an error and return nullptr. + UEdGraphNode *Execute(int32 N, int32 PosX, int32 PosY); + +private: + // The Graph that we're generating Actions for. + UEdGraph *Graph; + + // One of these two will be populated, depending on graph type. + TArray> Actions; + TArray Spawners; + + // Get the full name of an action. + FString GetFullName(const FEdGraphSchemaAction& Action); + FString GetFullName(const UBlueprintNodeSpawner* Spawner); + + // Compare an action against a query. + bool IsMatch(const FEdGraphSchemaAction& Action, const FString &QueryLower, bool Exact); + bool IsMatch(const UBlueprintNodeSpawner* Spawner, const FString &QueryLower, bool Exact); + + // Execute an action + UEdGraphNode* Execute(FEdGraphSchemaAction& Action, int32 X, int32 Y); + UEdGraphNode* Execute(UBlueprintNodeSpawner* Spawner, int32 X, int32 Y); + + // Routines that collect actions and spawners. + void CollectActions(UEdGraph* GraphP, const FString& Query, int32 MaxResults, bool ExactMatch); + void CollectSpawners(UEdGraph* GraphP, const FString& Query, int32 MaxResults, bool ExactMatch); +}; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h b/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h index aa267f9d..117efdb0 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingUtils.h @@ -230,10 +230,6 @@ public: static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName); static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName); - // ----- Graph actions (node spawning) ----- - static FString ActionFullName(const TSharedPtr& Action); - static TArray> SearchGraphActions(UEdGraph* Graph, const FString& Query, int32 MaxResults = 0, bool ExactMatch = false); - // ----- Text formatting ----- static FString WrapText(const FString& Text, int32 ColLimit, const FString& Prefix);