From 5aef356199006d7d2650e5692e2854a9cef30cb7 Mon Sep 17 00:00:00 2001 From: jyelon Date: Fri, 27 Mar 2026 19:27:49 -0400 Subject: [PATCH] More work on the new variable list functionality --- Content/Testing/BP_Test.uasset | 4 +- .../UEWingman/Handlers/Blueprint_Dump.h | 9 + .../UEWingman/Private/WingGraphExport.cpp | 11 +- .../UEWingman/Private/WingVariables.cpp | 172 ++++++++++++++++++ .../Source/UEWingman/Public/WingServer.h | 3 + .../Source/UEWingman/Public/WingVariables.h | 64 +++++++ 6 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp create mode 100644 Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h diff --git a/Content/Testing/BP_Test.uasset b/Content/Testing/BP_Test.uasset index b398cc92..28e1bcf9 100644 --- a/Content/Testing/BP_Test.uasset +++ b/Content/Testing/BP_Test.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0e512e7e5cf4e0fe827c2ca881fe84681cfe7006228261cf202da95c0dc93c4 -size 42466 +oid sha256:2ad535d90d8e35c0c89b8762e8d1fe1a73948d4ad7eb2ea79792c0e17d2cc789 +size 44316 diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Dump.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Dump.h index 67064d9b..fa20292d 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Dump.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Dump.h @@ -15,6 +15,7 @@ #include "AnimationGraph.h" #include "AnimationGraphSchema.h" #include "AnimationStateMachineSchema.h" +#include "WingVariables.h" #include "WingWidgets.h" #include "WidgetBlueprint.h" #include "Blueprint/WidgetTree.h" @@ -82,6 +83,14 @@ public: } } + // Variables (new format) + WingVariables BlueprintVars = WingVariables::ParseBlueprintVariables(BP); + if (!BlueprintVars.IsEmpty()) + { + UWingServer::Print(TEXT("\nVariables (new format):\n")); + BlueprintVars.PrintAll(2); + } + // Components TArray Components3 = UWingComponentReference::GetAll(BP); if (!Components3.IsEmpty()) diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingGraphExport.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingGraphExport.cpp index 31b96ded..292c5ef6 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingGraphExport.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingGraphExport.cpp @@ -11,6 +11,7 @@ #include "K2Node_CallFunction.h" #include "K2Node_FunctionEntry.h" #include "WingFunctionArgs.h" +#include "WingVariables.h" #include "MaterialGraph/MaterialGraphNode.h" WingGraphExport::WingGraphExport(UEdGraph* InGraph) @@ -292,13 +293,11 @@ void WingGraphExport::EmitLocalVariables() UK2Node_FunctionEntry* EntryNode = Cast(Node); if (!EntryNode) continue; - for (const FBPVariableDescription& Var : EntryNode->LocalVariables) + WingVariables Locals = WingVariables::ParseFunctionLocalVariables(EntryNode); + if (!Locals.IsEmpty()) { - FString Default = Var.DefaultValue.IsEmpty() ? TEXT("") : Var.DefaultValue; - Output.Appendf(TEXT("local %s %s = %s\n"), - *UWingTypes::TypeToText(Var.VarType), - *WingUtils::FormatName(Var), - *Default); + Output.Appendf(TEXT("Function Local Variables:\n")); + Locals.PrintAll(Output, 4); } break; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp new file mode 100644 index 00000000..cf81f331 --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingVariables.cpp @@ -0,0 +1,172 @@ +#include "WingVariables.h" +#include "WingServer.h" +#include "WingTypes.h" +#include "WingUtils.h" +#include "EdGraphSchema_K2.h" +#include "K2Node_FunctionEntry.h" +#include "Kismet2/BlueprintEditorUtils.h" + +// Flag names used for blueprint variables. +static const FName Flag_InstanceEditable(TEXT("InstanceEditable")); +static const FName Flag_BlueprintReadOnly(TEXT("BlueprintReadOnly")); +static const FName Flag_ExposeOnSpawn(TEXT("ExposeOnSpawn")); +static const FName Flag_Private(TEXT("Private")); +static const FName Flag_ExposeToCinematics(TEXT("ExposeToCinematics")); + + +static TSet Flags_None = { }; +static TSet Flags_BlueprintVariables = { Flag_InstanceEditable, Flag_BlueprintReadOnly, Flag_ExposeOnSpawn, Flag_Private, Flag_ExposeToCinematics }; +static TSet Flags_FunctionLocalVariables = { }; + +const TSet &WingVariables::GetRelevantFlagSet(WingVariables::Cat C) +{ + switch (C) + { + case Cat::Blueprint: return Flags_BlueprintVariables; + case Cat::FunctionLocal: return Flags_FunctionLocalVariables; + default: return Flags_None; + } +} + +void WingVariables::Var::AddFlagIfRelevant(FName Flag, WingVariables::Cat C) +{ + if (GetRelevantFlagSet(C).Contains(Flag)) Flags.Add(Flag); +} + +WingVariables::Var WingVariables::ParseVariableDescription(const FBPVariableDescription &Desc, WingVariables::Cat Category) +{ + Var Result; + Result.Name = Desc.VarName; + Result.Type = Desc.VarType; + + if (!(Desc.PropertyFlags & CPF_DisableEditOnInstance)) + Result.AddFlagIfRelevant(Flag_InstanceEditable, Category); + + if (Desc.PropertyFlags & CPF_BlueprintReadOnly) + Result.AddFlagIfRelevant(Flag_BlueprintReadOnly, Category); + + if (Desc.PropertyFlags & CPF_Interp) + Result.AddFlagIfRelevant(Flag_ExposeToCinematics, Category); + + if (Desc.HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn)) + Result.AddFlagIfRelevant(Flag_ExposeOnSpawn, Category); + + if (Desc.HasMetaData(FBlueprintMetadata::MD_Private)) + Result.AddFlagIfRelevant(Flag_Private, Category); + + return Result; +} + +bool WingVariables::CheckSanity(Cat Category) +{ + const TSet &Relevant = GetRelevantFlagSet(Category); + TSet NamesUsed; + for (const Var &Variable : Variables) + { + FString VarName = WingUtils::SanitizeName(Variable.Name); + FString TypeText = UWingTypes::TypeToText(Variable.Type); + if (NamesUsed.Contains(Variable.Name)) + { + UWingServer::Printf(TEXT("Variable name appears twice: %s\n"), *VarName); + return false; + } + if (TypeText.IsEmpty()) + { + UWingServer::Printf(TEXT("Type of variable %s is not valid for unknown reasons\n"), *VarName); + return false; + } + if (!UWingTypes::IsBlueprintType(Variable.Type)) + { + UWingServer::Printf(TEXT("Type is not a valid BlueprintType: %s %s\n"), *TypeText, *VarName); + return false; + } + for (FName Flag : Variable.Flags) + { + if (!Relevant.Contains(Flag)) + { + UWingServer::Printf(TEXT("Flag %s is not valid here. Valid flags are:"), *Flag.ToString()); + for (FName Rel : Relevant) UWingServer::Printf(TEXT(" %s\n"), *Rel.ToString()); + return false; + } + } + } + return true; +} + + +WingVariables WingVariables::ParseBlueprintVariables(const UBlueprint *BP) +{ + WingVariables Result; + for (const FBPVariableDescription& Desc : BP->NewVariables) + { + // Skip event dispatchers. + if (Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue; + + // Parse the bulk of the flags. + Var V = ParseVariableDescription(Desc, Cat::Blueprint); + + // Read default value from CDO if available. + if (BP->GeneratedClass) + { + UObject* CDO = BP->GeneratedClass->GetDefaultObject(); + FProperty* Prop = BP->GeneratedClass->FindPropertyByName(Desc.VarName); + if (CDO && Prop) + V.DefaultValue = FWingProperty(Prop, CDO).GetText(); + } + + Result.Variables.Add(MoveTemp(V)); + } + return Result; +} + +WingVariables WingVariables::ParseFunctionLocalVariables(const UK2Node_FunctionEntry *Func) +{ + WingVariables Result; + if (!Func) return Result; + + for (const FBPVariableDescription& Desc : Func->LocalVariables) + { + Var V = ParseVariableDescription(Desc, Cat::FunctionLocal); + V.DefaultValue = Desc.DefaultValue; + Result.Variables.Add(MoveTemp(V)); + } + return Result; +} + +void WingVariables::PrintAll(FStringBuilderBase &Out, int32 Indent) +{ + FString Prefix; + for (int32 i = 0; i < Indent; i++) Prefix += TEXT(" "); + + for (const Var& V : Variables) + { + FString TypeStr = UWingTypes::TypeToText(V.Type); + FString NameStr = WingUtils::SanitizeName(V.Name); + + // Build flags string. + FString FlagsStr; + if (V.Flags.Num() > 0) + { + TArray Sorted = V.Flags.Array(); + Sorted.Sort(FNameLexicalLess()); + for (const FName& Flag : Sorted) + { + if (!FlagsStr.IsEmpty()) FlagsStr += TEXT(", "); + FlagsStr += Flag.ToString(); + } + } + + // Print: type name (flags) = defaultvalue + Out.Appendf(TEXT("%s%s %s"), *Prefix, *TypeStr, *NameStr); + if (!FlagsStr.IsEmpty()) + Out.Appendf(TEXT(" (%s)"), *FlagsStr); + if (!V.DefaultValue.IsEmpty()) + Out.Appendf(TEXT(" = %s"), *V.DefaultValue); + Out.Append(TEXT("\n")); + } +} + +void WingVariables::PrintAll(int32 Indent) +{ + PrintAll(UWingServer::GetPrintBuffer(), Indent); +} diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h b/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h index 9ce57f3a..0bc1aa89 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h @@ -60,6 +60,9 @@ public: GWingServer->HandlerOutput.Appendf(Fmt, Forward(Args)...); } + /** Get the handler output buffer directly. */ + static FStringBuilderBase& GetPrintBuffer() { return GWingServer->HandlerOutput; } + /** Suggest that a manual section be printed after the handler finishes. */ static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); } diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h b/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h new file mode 100644 index 00000000..3b129aad --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingVariables.h @@ -0,0 +1,64 @@ +#pragma once + +#include "CoreMinimal.h" +#include "WingProperty.h" +#include "Engine/Blueprint.h" + +class UK2Node_FunctionEntry; + +class WingVariables +{ + enum class Cat + { + Blueprint, + FunctionLocal + }; + +private: + struct Var + { + // Internal name. + FName Name; + + // Type. Must be a BlueprintType. + FEdGraphPinType Type; + + // Default Value. + FString DefaultValue; + + // Boolean flags. + TSet Flags; + + // Add the flag if it's relevant to category C. + void AddFlagIfRelevant(FName Flag, Cat C); + }; + +private: + // A list of all the variables. + TArray Variables; + +public: + // Returns the set of flags that are supported by this variable category. + static const TSet &GetRelevantFlagSet(Cat C); + + + // Check if the variable list is empty. + bool IsEmpty() const { return Variables.IsEmpty(); } + + // Print all the variables to a string builder. + void PrintAll(FStringBuilderBase &Out, int32 Indent = 0); + + // Print all the variables via UWingServer. + void PrintAll(int32 Indent = 0); + + // Make sure that this variable list makes sense for the given context. + bool CheckSanity(Cat Category); + + // Parse variables from a given source. + static WingVariables ParseBlueprintVariables(const UBlueprint *BP); + static WingVariables ParseFunctionLocalVariables(const UK2Node_FunctionEntry *F); + +private: + static Var ParseVariableDescription(const FBPVariableDescription &V, Cat C); +}; +