More progress on variables

This commit is contained in:
2026-03-31 03:50:06 -04:00
parent 9720fa9f8d
commit 93ce030fc0
13 changed files with 588 additions and 578 deletions

Binary file not shown.

View File

@@ -12,7 +12,6 @@
#include "Kismet2/KismetEditorUtilities.h" #include "Kismet2/KismetEditorUtilities.h"
#include "BlueprintVariable_Create.generated.h" #include "BlueprintVariable_Create.generated.h"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -23,15 +22,16 @@ class UWing_BlueprintVariable_Create : public UObject, public IWingHandler
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(meta=(Description="Blueprint name or package path")) UPROPERTY(meta=(Description="Blueprint path"))
FString Blueprint; FString Blueprint;
UPROPERTY(meta=(Description="Variable declarations, one per line. Format: type name (flags) = default")) UPROPERTY(meta=(Description="Variable declarations"))
FString Variables; FString Variables;
virtual FString GetDescription() const override virtual FString GetDescription() const override
{ {
return TEXT("Add new member variables to a Blueprint. Format: 'type name (flags) = default', one per line."); return TEXT("Add new member variables to a Blueprint. "
"Format: 'type name (flags) = default', one per line.");
} }
virtual void Handle() override virtual void Handle() override
@@ -41,31 +41,10 @@ public:
if (!BP) return; if (!BP) return;
// Parse the variable declarations. // Parse the variable declarations.
WingVariables Vars; WingVariables Vars(BP);
if (!Vars.ParseString(Variables)) return; if (!Vars.BlueprintVariables.ParseString(Variables)) return;
if (Vars.IsEmpty()) { UWingServer::Print(TEXT("ERROR: No variables specified.\n")); return; } if (!Vars.Check()) return;
if (!Vars.Create()) return;
// Check for name collisions against existing variables, components, and the like.
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateNames(Names, Vars.GetVariables(), TEXT("variable or component"))) return;
// 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;
}
}
// Update everything.
if (!Vars.AssociateBlueprintVariables(BP)) return;
Vars.UpdateVariableFlags();
FKismetEditorUtilities::CompileBlueprint(BP);
if (!Vars.UpdateVariableDefaults()) return;
UWingServer::Printf(TEXT("Success.\n")); UWingServer::Printf(TEXT("Success.\n"));
} }
}; };

View File

@@ -32,9 +32,8 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
WingVariables Vars; WingVariables Vars(BP);
Vars.LoadBlueprintVariables(BP); Vars.Load();
if (Vars.IsEmpty()) { UWingServer::Print(TEXT("No variables.\n")); return; } Vars.Print(UWingServer::GetPrintBuffer());
Vars.PrintAll(UWingServer::GetPrintBuffer(), true, TEXT("Blueprint Variables:"));
} }
}; };

View File

@@ -39,20 +39,10 @@ public:
if (!BP) return; if (!BP) return;
// Parse the variable declarations. // Parse the variable declarations.
WingVariables Vars; WingVariables Vars(BP);
if (!Vars.ParseString(Variables)) return; if (!Vars.BlueprintVariables.ParseString(Variables)) return;
if (Vars.IsEmpty()) { UWingServer::Print(TEXT("ERROR: No variables specified.\n")); return; } if (!Vars.Check()) return;
if (!Vars.CheckSanity(WingVariables::Cat::BlueprintVariables)) return; if (!Vars.Modify()) return;
// Associate with existing blueprint variables.
if (!Vars.AssociateBlueprintVariables(BP)) return;
// Update types and flags, compile, then update defaults.
Vars.UpdateVariableTypes();
Vars.UpdateVariableFlags();
FKismetEditorUtilities::CompileBlueprint(BP);
if (!Vars.UpdateVariableDefaults()) return;
UWingServer::Printf(TEXT("Success.\n")); UWingServer::Printf(TEXT("Success.\n"));
} }
}; };

View File

@@ -84,9 +84,9 @@ public:
} }
// Variables (new format) // Variables (new format)
WingVariables BlueprintVars; WingVariables BlueprintVars(BP);
BlueprintVars.LoadBlueprintVariables(BP); BlueprintVars.Load();
BlueprintVars.PrintAll(UWingServer::GetPrintBuffer(), true, TEXT("Blueprint Variables 2:")); BlueprintVars.Print(UWingServer::GetPrintBuffer());
// Components // Components
TArray<UWingComponentReference*> Components3 = UWingComponentReference::GetAll(BP); TArray<UWingComponentReference*> Components3 = UWingComponentReference::GetAll(BP);

View File

@@ -0,0 +1,57 @@
#pragma once
#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "K2Node_EditablePinBase.h"
#include "K2Node_FunctionEntry.h"
#include "GraphVariables_Create.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Create : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a function or macro graph (e.g. '/Game/MyBP,graph:MyFunction')"))
FString Graph;
UPROPERTY(meta=(Optional, Description="Inputs to the graph"))
FString InputVariables;
UPROPERTY(meta=(Optional, Description="Outputs to the graph"))
FString OutputVariables;
UPROPERTY(meta=(Optional, Description="Locals to the graph"))
FString LocalVariables;
virtual FString GetDescription() const override
{
return TEXT("Add new inputs, outputs, and local variables to a graph. "
"Format: 'type name (flags) = default', one per line");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
WingVariables Vars(G);
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -33,8 +33,8 @@ public:
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return; if (!G) return;
WingGraphVariables Vars; WingVariables Vars(G);
Vars.LoadGraph(G); Vars.Load();
Vars.PrintAll(UWingServer::GetPrintBuffer(), true); Vars.Print(UWingServer::GetPrintBuffer());
} }
}; };

View File

@@ -21,13 +21,13 @@ public:
UPROPERTY(meta=(Description="Path to a graph")) UPROPERTY(meta=(Description="Path to a graph"))
FString Graph; FString Graph;
UPROPERTY(meta=(Optional, Description="Argument declarations, one per line.")) UPROPERTY(meta=(Optional, Description="Inputs to the graph"))
FString Arguments; FString InputVariables;
UPROPERTY(meta=(Optional, Description="Return Value declarations, one per line.")) UPROPERTY(meta=(Optional, Description="Outputs to the graph"))
FString ReturnValues; FString OutputVariables;
UPROPERTY(meta=(Optional, Description="Local Variable declarations, one per line.")) UPROPERTY(meta=(Optional, Description="Locals to the graph"))
FString LocalVariables; FString LocalVariables;
virtual FString GetDescription() const override virtual FString GetDescription() const override
@@ -41,14 +41,12 @@ public:
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return; if (!G) return;
WingGraphVariables Vars; WingVariables Vars(G);
if (!Vars.ParseStrings(Arguments, ReturnValues, LocalVariables)) return; if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.CheckSanity(G)) return; if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
if (!Vars.AssociateGraph(G)) return; if (!Vars.LocalVariables.ParseString(LocalVariables)) return;
Vars.UpdateVariableTypes(); if (!Vars.Check()) return;
Vars.UpdateVariableFlags(); if (!Vars.Modify()) return;
Vars.UpdateVariableDefaults();
UWingServer::Printf(TEXT("Success.\n")); UWingServer::Printf(TEXT("Success.\n"));
} }
}; };

View File

@@ -289,9 +289,9 @@ void WingGraphExport::EmitMaterialProperties(UEdGraphNode* Node, FStringBuilderB
void WingGraphExport::EmitLocalVariables() void WingGraphExport::EmitLocalVariables()
{ {
WingGraphVariables Vars; WingVariables Vars(Graph);
Vars.LoadGraph(Graph); Vars.Load();
Vars.PrintAll(Output, false); Vars.Print(Output);
} }
void WingGraphExport::EmitGraph() void WingGraphExport::EmitGraph()

View File

@@ -476,6 +476,12 @@ TArray<UBlueprint*> WingUtils::GetAncestorBlueprints(UBlueprint *BP, bool Oldest
return Blueprints; return Blueprints;
} }
UObject *WingUtils::GetGeneratedCDO(UBlueprint *BP)
{
if (BP->GeneratedClass == nullptr) return nullptr;
return BP->GeneratedClass->GetDefaultObject();
}
// ============================================================ // ============================================================
// Material helpers // Material helpers
// ============================================================ // ============================================================

View File

@@ -10,6 +10,7 @@
#include "K2Node_Tunnel.h" #include "K2Node_Tunnel.h"
#include "K2Node_EditablePinBase.h" #include "K2Node_EditablePinBase.h"
#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
// Flag names used for blueprint variables. // Flag names used for blueprint variables.
static const FName Flag_InstanceEditable(TEXT("InstanceEditable")); static const FName Flag_InstanceEditable(TEXT("InstanceEditable"));
@@ -22,197 +23,58 @@ static const FName Flag_ExposeToCinematics(TEXT("ExposeToCinematics"));
static TSet<FName> Flags_None = { }; static TSet<FName> Flags_None = { };
static TSet<FName> Flags_BlueprintVariables = { Flag_InstanceEditable, Flag_BlueprintReadOnly, Flag_ExposeOnSpawn, Flag_Private, Flag_ExposeToCinematics }; static TSet<FName> Flags_BlueprintVariables = { Flag_InstanceEditable, Flag_BlueprintReadOnly, Flag_ExposeOnSpawn, Flag_Private, Flag_ExposeToCinematics };
const TSet<FName> &WingVariables::GetRelevantFlagSet(WingVariables::Cat C)
void WingVariableList::Print(FStringBuilderBase &Out)
{ {
switch (C) if (Variables.IsEmpty()) return;
Out.Append(ListName);
Out.AppendChar(':');
Out.AppendChar('\n');
for (const Var& V : Variables)
{ {
case Cat::BlueprintVariables: return Flags_BlueprintVariables; FString TypeStr = UWingTypes::TypeToText(V.Type);
case Cat::LocalVariables: return Flags_None; FString NameStr = WingUtils::ExternalizeID(V.Name);
case Cat::FunctionArguments: return Flags_None;
case Cat::FunctionReturnValues: return Flags_None; // Build flags string.
case Cat::MacroEntry: return Flags_None; FString FlagsStr;
case Cat::MacroExit: return Flags_None; if (V.Flags.Num() > 0)
{
TArray<FName> Sorted = V.Flags.Array();
Sorted.Sort(FNameLexicalLess());
for (const FName& Flag : Sorted)
{
if (!FlagsStr.IsEmpty()) FlagsStr += TEXT(", ");
FlagsStr += Flag.ToString();
} }
} }
void WingVariables::Var::AddFlagIfRelevant(FName Flag, WingVariables::Cat C) // Print: type name (flags) = defaultvalue
{ Out.Appendf(TEXT(" %s %s"), *TypeStr, *NameStr);
if (GetRelevantFlagSet(C).Contains(Flag)) Flags.Add(Flag); if (!FlagsStr.IsEmpty())
Out.Appendf(TEXT(" (%s)"), *FlagsStr);
if (!V.DefaultValue.IsEmpty())
Out.Appendf(TEXT(" = %s"), *V.DefaultValue);
Out.Append(TEXT("\n"));
}
} }
void WingVariables::Clear() void WingVariableList::ClearLinks()
{ {
Variables.Empty(); for (Var &V : Variables)
Blueprint = nullptr;
FuncEntry = nullptr;
PinBase = nullptr;
}
void WingVariables::ClearAssociation()
{
for (Var V : Variables)
{ {
V.BPVar = nullptr; V.BPVar = nullptr;
V.Pin = nullptr; V.Pin = nullptr;
} }
Blueprint = nullptr;
FuncEntry = nullptr;
PinBase = nullptr;
} }
void WingVariables::LoadBlueprintVariables(UBlueprint *BP) bool WingVariableList::CheckSanity(const TSet<FName> &GoodFlags, bool Allow)
{ {
Clear(); if ((!Allow) && (!Variables.IsEmpty()))
for (FBPVariableDescription& Desc : BP->NewVariables)
{ {
// Skip event dispatchers. UWingServer::Printf(TEXT("In this context, %s must be empty."), ListName);
if (Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue;
// Parse the bulk of the flags.
Var V = ParseVariableDescription(Desc, Cat::BlueprintVariables);
// 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();
V.DefaultSpecified = true;
}
}
Variables.Add(MoveTemp(V));
}
}
void WingVariables::LoadLocalVariables(UK2Node_EditablePinBase *Node)
{
Clear();
if (UK2Node_FunctionEntry *Func = Cast<UK2Node_FunctionEntry>(Node))
{
for (FBPVariableDescription& Desc : Func->LocalVariables)
{
Var V = ParseVariableDescription(Desc, Cat::LocalVariables);
V.DefaultValue = Desc.DefaultValue;
V.DefaultSpecified = true;
Variables.Add(MoveTemp(V));
}
}
}
void WingVariables::LoadEditablePinBase(UK2Node_EditablePinBase* Node)
{
Clear();
if (Node != nullptr)
{
for (const TSharedPtr<FUserPinInfo>& PinInfo : Node->UserDefinedPins)
{
Var V;
V.Name = PinInfo->PinName;
V.Type = PinInfo->PinType;
V.DefaultValue = PinInfo->PinDefaultValue;
V.DefaultSpecified = true;
Variables.Add(MoveTemp(V));
}
}
}
bool WingVariables::AssociateBlueprintVariables(UBlueprint *BP)
{
ClearAssociation();
Blueprint = BP;
for (Var &V : Variables)
{
FBPVariableDescription *Desc = WingUtils::FindOneWithInternalID(V.Name, BP->NewVariables, TEXT("non-inherited variable"));
if (Desc == nullptr) return false;
V.BPVar = Desc;
}
return true;
}
bool WingVariables::AssociateLocalVariables(UK2Node_EditablePinBase *Node)
{
ClearAssociation();
if (UK2Node_FunctionEntry *Func = Cast<UK2Node_FunctionEntry>(Node))
{
FuncEntry = Func;
for (Var &V : Variables)
{
FBPVariableDescription *Desc = WingUtils::FindOneWithInternalID(V.Name, Func->LocalVariables, TEXT("local variable"));
if (Desc == nullptr) return false;
V.BPVar = Desc;
}
return true;
}
else
{
if (Variables.IsEmpty()) { return true; }
UWingServer::Printf(TEXT("Graph can't have local variables, not a function graph: %s\n"),
*WingUtils::FormatName(Node->GetGraph()));
return false; return false;
} }
}
bool WingVariables::AssociateEditablePinBase(UK2Node_EditablePinBase *Node)
{
ClearAssociation();
if (Node != nullptr)
{
PinBase = Node;
for (Var &V : Variables)
{
TSharedPtr<FUserPinInfo> *Found = WingUtils::FindOneWithInternalID(V.Name, Node->UserDefinedPins, TEXT("pin"));
if (!Found) return false;
V.Pin = *Found;
}
}
else
{
if (Variables.IsEmpty()) return true;
UWingServer::Printf(TEXT("Cannot modify variables, graph node does not exist yet"));
return false;
}
return true;
}
void WingVariables::UpdateVariableTypes()
{
if (Blueprint) return UpdateBlueprintVariableTypes();
if (FuncEntry) return UpdateLocalVariableTypes();
if (PinBase) return UpdateEditablePinBaseTypes();
}
void WingVariables::UpdateVariableFlags()
{
if (Blueprint) return UpdateBlueprintVariableFlags();
if (FuncEntry) return UpdateLocalVariableFlags();
if (PinBase) return UpdateEditablePinBaseFlags();
}
bool WingVariables::UpdateVariableDefaults()
{
if (Blueprint) return UpdateBlueprintVariableDefaults();
if (FuncEntry) return UpdateLocalVariableDefaults();
if (PinBase) return UpdateEditablePinBaseDefaults();
return true;
}
bool WingVariables::CheckEmpty(Cat Category)
{
if (!Variables.IsEmpty())
{
UWingServer::Printf(TEXT("This kind of graph is not allowed to have %s"),
*StaticEnum<EWingVariableCategory>()->GetNameStringByValue(int(Category)));
return false;
}
return true;
}
bool WingVariables::CheckSanity(Cat Category)
{
const TSet<FName> &Relevant = GetRelevantFlagSet(Category);
for (const Var &Variable : Variables) for (const Var &Variable : Variables)
{ {
FString VarName = WingUtils::ExternalizeID(Variable.Name); FString VarName = WingUtils::ExternalizeID(Variable.Name);
@@ -229,10 +91,10 @@ bool WingVariables::CheckSanity(Cat Category)
} }
for (FName Flag : Variable.Flags) for (FName Flag : Variable.Flags)
{ {
if (!Relevant.Contains(Flag)) if (!GoodFlags.Contains(Flag))
{ {
UWingServer::Printf(TEXT("Flag %s is not valid here. Valid flags are:"), *Flag.ToString()); 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()); for (FName Rel : GoodFlags) UWingServer::Printf(TEXT(" %s\n"), *Rel.ToString());
return false; return false;
} }
} }
@@ -240,28 +102,51 @@ bool WingVariables::CheckSanity(Cat Category)
return true; return true;
} }
bool WingVariables::ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out) bool WingVariableList::ParseString(const FString &Input)
{ {
Tok.Advance(); // Step over open-paren Variables.Empty();
TArray<FString> Lines;
Input.ParseIntoArrayLines(Lines);
for (const FString& Line : Lines)
{
WingTokenizer Tok(Line);
if (Tok.NextType() == 0) continue;
Var V;
V.DefaultSpecified = false;
if (!ParseOneVariable(Tok, V)) return false;
Variables.Add(MoveTemp(V));
}
return true;
}
bool WingVariableList::ParseNamesString(const FString &Input)
{
Variables.Empty();
WingTokenizer Tok(Input);
while (Tok.TokenIs(Tok.Identifier)) while (Tok.TokenIs(Tok.Identifier))
{ {
Out.Add(Tok.NextName()); FName Name = Tok.NextName();
Var V;
V.Name = Name;
Variables.Add(V);
V.DefaultSpecified = false;
Tok.Advance(); Tok.Advance();
// Commas are optional.
if (Tok.TokenIs(',')) Tok.Advance(); if (Tok.TokenIs(',')) Tok.Advance();
} }
if (!Tok.TokenIs(')')) if (!Tok.TokenIs(0))
{ {
Tok.SaveCursor(NAME_None); Tok.SaveCursor(NAME_None);
UWingServer::Printf(TEXT("ERROR: flag list contains invalid token '%s'\n"), UWingServer::Printf(TEXT("Unexpected token %s in variable list"),
*FString(Tok.GetRange(NAME_None, 1))); *FString(Tok.GetRange(NAME_None, 1)));
return false; return false;
} }
Tok.Advance(); // Step over close-paren
return true; return true;
} }
bool WingVariables::ParseOneVariable(WingTokenizer &Tok, Var &V) bool WingVariableList::ParseOneVariable(WingTokenizer &Tok, Var &V)
{ {
// Parse type. // Parse type.
UWingTypes::Requirements Req; UWingTypes::Requirements Req;
@@ -306,99 +191,236 @@ bool WingVariables::ParseOneVariable(WingTokenizer &Tok, Var &V)
return true; return true;
} }
bool WingVariables::ParseString(const FString &Input) bool WingVariableList::ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out)
{ {
Variables.Empty(); Tok.Advance(); // Step over open-paren
while (Tok.TokenIs(Tok.Identifier))
TArray<FString> Lines;
Input.ParseIntoArrayLines(Lines);
for (const FString& Line : Lines)
{ {
WingTokenizer Tok(Line); Out.Add(Tok.NextName());
if (Tok.NextType() == 0) continue; Tok.Advance();
Var V; // Commas are optional.
V.DefaultSpecified = false; if (Tok.TokenIs(',')) Tok.Advance();
if (!ParseOneVariable(Tok, V)) return false;
Variables.Add(MoveTemp(V));
} }
if (!Tok.TokenIs(')'))
{
Tok.SaveCursor(NAME_None);
UWingServer::Printf(TEXT("ERROR: flag list contains invalid token '%s'\n"),
*FString(Tok.GetRange(NAME_None, 1)));
return false;
}
Tok.Advance(); // Step over close-paren
return true; return true;
} }
WingVariables::Var WingVariables::ParseVariableDescription(const FBPVariableDescription &Desc, WingVariables::Cat Category) void WingVariables::Empty()
{
BlueprintVariables.Empty();
LocalVariables.Empty();
InputVariables.Empty();
OutputVariables.Empty();
}
void WingVariables::ClearLinks()
{
BlueprintVariables.ClearLinks();
LocalVariables.ClearLinks();
InputVariables.ClearLinks();
OutputVariables.ClearLinks();
}
void WingVariables::Print(FStringBuilderBase &Out)
{
BlueprintVariables.Print(Out);
LocalVariables.Print(Out);
InputVariables.Print(Out);
OutputVariables.Print(Out);
}
void WingVariables::Load()
{
Empty();
if (Blueprint != nullptr) return LoadBlueprint();
if (Graph != nullptr) return LoadGraph();
}
void WingVariables::LoadBlueprint()
{
UObject *CDO = WingUtils::GetGeneratedCDO(Blueprint);
for (FBPVariableDescription& Desc : Blueprint->NewVariables)
{
// Skip event dispatchers.
if (Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue;
// Load up the variable.
Var V = LoadBlueprintVariableDescription(Desc, CDO);
BlueprintVariables.Variables.Add(MoveTemp(V));
}
}
void WingVariables::LoadGraph()
{
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
if (EntryNode.IsValid()) LoadEditablePinBase(EntryNode.Get(), InputVariables);
if (ResultNode.IsValid()) LoadEditablePinBase(ResultNode.Get(), OutputVariables);
LoadLocalVariables(EntryNode.Get());
}
void WingVariables::LoadLocalVariables(UK2Node_EditablePinBase *Node)
{
UK2Node_FunctionEntry *Func = Cast<UK2Node_FunctionEntry>(Node);
if (Func == nullptr) return;
for (FBPVariableDescription& Desc : Func->LocalVariables)
{
Var V = LoadLocalVariableDescription(Desc);
LocalVariables.Variables.Add(MoveTemp(V));
}
}
WingVariables::Var WingVariables::LoadBlueprintVariableDescription(FBPVariableDescription &Desc, UObject *CDO)
{ {
Var Result; Var Result;
Result.Name = Desc.VarName; Result.Name = Desc.VarName;
Result.Type = Desc.VarType; Result.Type = Desc.VarType;
if (!(Desc.PropertyFlags & CPF_DisableEditOnInstance)) if (!(Desc.PropertyFlags & CPF_DisableEditOnInstance))
Result.AddFlagIfRelevant(Flag_InstanceEditable, Category); Result.Flags.Add(Flag_InstanceEditable);
if (Desc.PropertyFlags & CPF_BlueprintReadOnly) if (Desc.PropertyFlags & CPF_BlueprintReadOnly)
Result.AddFlagIfRelevant(Flag_BlueprintReadOnly, Category); Result.Flags.Add(Flag_BlueprintReadOnly);
if (Desc.PropertyFlags & CPF_Interp) if (Desc.PropertyFlags & CPF_Interp)
Result.AddFlagIfRelevant(Flag_ExposeToCinematics, Category); Result.Flags.Add(Flag_ExposeToCinematics);
if (Desc.HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn)) if (Desc.HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn))
Result.AddFlagIfRelevant(Flag_ExposeOnSpawn, Category); Result.Flags.Add(Flag_ExposeOnSpawn);
if (Desc.HasMetaData(FBlueprintMetadata::MD_Private)) if (Desc.HasMetaData(FBlueprintMetadata::MD_Private))
Result.AddFlagIfRelevant(Flag_Private, Category); Result.Flags.Add(Flag_Private);
// Read default value from CDO if available.
if (CDO)
{
FProperty* Prop = CDO->GetClass()->FindPropertyByName(Desc.VarName);
if (Prop)
{
Result.DefaultValue = FWingProperty(Prop, CDO).GetText();
Result.DefaultSpecified = true;
}
}
return Result; return Result;
} }
void WingVariables::PrintAll(FStringBuilderBase &Out, bool Always, const TCHAR *Header) WingVariables::Var WingVariables::LoadLocalVariableDescription(FBPVariableDescription &Desc)
{ {
if (Header != nullptr) Var Result;
{ Result.Name = Desc.VarName;
if (Always || (!Variables.IsEmpty())) Result.Type = Desc.VarType;
{ Result.DefaultValue = Desc.DefaultValue;
Out.Append(Header); Result.DefaultSpecified = true;
Out.AppendChar('\n'); return Result;
} }
}
for (const Var& V : Variables)
{
FString TypeStr = UWingTypes::TypeToText(V.Type);
FString NameStr = WingUtils::ExternalizeID(V.Name);
// Build flags string. void WingVariables::LoadEditablePinBase(UK2Node_EditablePinBase* Node, WingVariableList &List)
FString FlagsStr;
if (V.Flags.Num() > 0)
{ {
TArray<FName> Sorted = V.Flags.Array(); if (Node == nullptr) return;
Sorted.Sort(FNameLexicalLess()); for (const TSharedPtr<FUserPinInfo>& PinInfo : Node->UserDefinedPins)
for (const FName& Flag : Sorted)
{ {
if (!FlagsStr.IsEmpty()) FlagsStr += TEXT(", "); Var V;
FlagsStr += Flag.ToString(); V.Name = PinInfo->PinName;
V.Type = PinInfo->PinType;
V.DefaultValue = PinInfo->PinDefaultValue;
V.DefaultSpecified = true;
List.Variables.Add(MoveTemp(V));
} }
} }
// Print: type name (flags) = defaultvalue bool WingVariables::Check()
Out.Appendf(TEXT(" %s %s"), *TypeStr, *NameStr); {
if (!FlagsStr.IsEmpty()) if (Blueprint) return CheckBlueprint();
Out.Appendf(TEXT(" (%s)"), *FlagsStr); if (Graph) return CheckGraph();
if (!V.DefaultValue.IsEmpty()) check(false);
Out.Appendf(TEXT(" = %s"), *V.DefaultValue); return false;
Out.Append(TEXT("\n"));
}
} }
void WingVariables::UpdateBlueprintVariableTypes() bool WingVariables::CheckBlueprint()
{ {
for (const Var &Input : Variables) bool OK = true;
{ if (!BlueprintVariables.CheckSanity(Flags_BlueprintVariables, true)) OK = false;
Input.BPVar->VarType = Input.Type; if (!LocalVariables.CheckSanity(Flags_None, false)) OK = false;
} if (!InputVariables.CheckSanity(Flags_None, false)) OK = false;
if (!OutputVariables.CheckSanity(Flags_None, false)) OK = false;
return OK;
} }
void WingVariables::UpdateBlueprintVariableFlags() bool WingVariables::CheckGraph()
{ {
for (const Var &Input : Variables) EGraphType T = Graph->GetSchema()->GetGraphType(Graph);
bool AllowLocal = (T == EGraphType::GT_Function);
bool AllowInput = (T == EGraphType::GT_Function) || (T == EGraphType::GT_Macro);
bool AllowOutput = (T == EGraphType::GT_Function) || (T == EGraphType::GT_Macro);
if ((!AllowLocal) && (!AllowInput) && (!AllowOutput))
{
UWingServer::Printf(TEXT("This graph has no editable variables."));
return false;
}
bool OK = true;
if (!BlueprintVariables.CheckSanity(Flags_None, false)) OK = false;
if (!LocalVariables.CheckSanity(Flags_None, AllowLocal)) OK = false;
if (!InputVariables.CheckSanity(Flags_None, AllowInput)) OK = false;
if (!OutputVariables.CheckSanity(Flags_None, AllowOutput)) OK = false;
return OK;
}
bool WingVariables::Modify()
{
if (Blueprint) return ModifyBlueprint();
if (Graph) return ModifyGraph();
check(false);
return false;
}
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)
{
V.BPVar->VarType = V.Type;
ModifyBlueprintVariableFlags(V);
if (V.DefaultSpecified) AnyDefaults = true;
}
// Next step not needed if no defaults specified.
if (!AnyDefaults) return true;
FKismetEditorUtilities::CompileBlueprint(Blueprint);
UObject *CDO = WingUtils::GetGeneratedCDO(Blueprint);
if (!CDO)
{
UWingServer::Printf(TEXT("Cannot store variable defaults, blueprint didn't compile"));
return false;
}
if (!ModifyDefaultsAsProperties(BlueprintVariables, CDO)) return false;
return true;
}
void WingVariables::ModifyBlueprintVariableFlags(Var &Input)
{ {
FBPVariableDescription &Out = *Input.BPVar; FBPVariableDescription &Out = *Input.BPVar;
@@ -428,165 +450,128 @@ void WingVariables::UpdateBlueprintVariableFlags()
else else
Out.RemoveMetaData(FBlueprintMetadata::MD_Private); Out.RemoveMetaData(FBlueprintMetadata::MD_Private);
} }
}
bool WingVariables::UpdateBlueprintVariableDefaults() bool WingVariables::ModifyDefaultsAsProperties(WingVariableList &List, UObject *CDO)
{ {
UObject *CDO = nullptr; for (Var &Input : List.Variables)
if (Blueprint->GeneratedClass) CDO = Blueprint->GeneratedClass->GetDefaultObject();
if (CDO == nullptr)
{
UWingServer::Printf(TEXT("Blueprint is not compiled, cannot update variable defaults."));
return false;
}
int OK = true;
for (const Var &Input : Variables)
{ {
if (Input.DefaultSpecified) if (Input.DefaultSpecified)
{ {
FProperty* Prop = Blueprint->GeneratedClass->FindPropertyByName(Input.Name); FProperty* Prop = CDO->GetClass()->FindPropertyByName(Input.Name);
if (!Prop) if (!Prop)
{ {
UWingServer::Printf(TEXT("Variable exists in blueprint, but not the generated class, which is weird: %s."), UWingServer::Printf(TEXT("Variable exists in blueprint, but not the generated class, which is weird: %s."),
*WingTokenizer::ExternalizeID(Input.Name)); *WingTokenizer::ExternalizeID(Input.Name));
return false; return false;
} }
if (!FWingProperty(Prop, CDO).SetText(Input.DefaultValue)) OK = false; if (!FWingProperty(Prop, CDO).SetText(Input.DefaultValue)) return false;
}
}
return OK;
}
void WingVariables::UpdateLocalVariableTypes()
{
for (const Var &Input : Variables)
{
Input.BPVar->VarType = Input.Type;
}
}
void WingVariables::UpdateLocalVariableFlags()
{
for (const Var &Input : Variables)
{
FBPVariableDescription &Out = *Input.BPVar;
// Currently, no supported flags for local variables.
}
}
bool WingVariables::UpdateLocalVariableDefaults()
{
for (const Var &Input : Variables)
{
if (Input.DefaultSpecified)
{
Input.BPVar->DefaultValue = Input.DefaultValue;
} }
} }
return true; return true;
} }
void WingVariables::UpdateEditablePinBaseTypes() bool WingVariables::ModifyGraph()
{ {
for (const Var &Input : Variables) if (!CheckGraph()) return false;
Input.Pin->PinType = Input.Type; ClearLinks();
PinBase->ReconstructNode();
UK2Node_EditablePinBase *InputNode, *OutputNode;
UK2Node_FunctionEntry *LocalNode;
if (!GetGraphNodes(InputNode, OutputNode, LocalNode)) return false;
for (Var &V : LocalVariables.Variables)
{
FBPVariableDescription *Desc =
WingUtils::FindOneWithInternalID(V.Name, LocalNode->LocalVariables, TEXT("local variable"));
if (Desc == nullptr) return false;
Desc->VarType = V.Type;
if (V.DefaultSpecified) Desc->DefaultValue = V.DefaultValue;
}
for (Var &V : InputVariables.Variables)
{
TSharedPtr<FUserPinInfo> *Found =
WingUtils::FindOneWithInternalID(V.Name, InputNode->UserDefinedPins, TEXT("input variable"));
if (!Found) return false;
(*Found)->PinType = V.Type;
if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue;
}
for (Var &V : OutputVariables.Variables)
{
TSharedPtr<FUserPinInfo> *Found =
WingUtils::FindOneWithInternalID(V.Name, OutputNode->UserDefinedPins, TEXT("output variable"));
if (!Found) return false;
(*Found)->PinType = V.Type;
if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue;
} }
void WingVariables::UpdateEditablePinBaseFlags() if (InputNode) InputNode->ReconstructNode();
{ if (OutputNode) OutputNode->ReconstructNode();
// Currently no flags apply to editable pin base pins.
}
bool WingVariables::UpdateEditablePinBaseDefaults()
{
for (const Var &Input : Variables)
{
if (Input.DefaultSpecified)
Input.Pin->PinDefaultValue = Input.DefaultValue;
}
PinBase->ReconstructNode();
return true; return true;
} }
void WingGraphVariables::LoadGraph(const UEdGraph *Graph) bool WingVariables::GetGraphNodes(
UK2Node_EditablePinBase *&InputNode,
UK2Node_EditablePinBase *&OutputNode,
UK2Node_FunctionEntry *&LocalNode)
{ {
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode; // In theory, none of these errors should trigger, because
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode; // we call CheckSanity before calling this function. But
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode); // you never know.
Arguments.LoadEditablePinBase(EntryNode.Get()); InputNode = nullptr;
ReturnValues.LoadEditablePinBase(ResultNode.Get()); OutputNode = nullptr;
LocalVariables.LoadLocalVariables(EntryNode.Get()); LocalNode = nullptr;
}
bool WingGraphVariables::ParseStrings(const FString &A, const FString &R, const FString &L) TWeakObjectPtr<UK2Node_EditablePinBase> Inputs, Outputs;
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, Inputs, Outputs);
if (!Inputs.IsValid())
{ {
if (!Arguments.ParseString(A)) return false; UWingServer::Printf(TEXT("ERROR: no function entry node for graph."));
if (!ReturnValues.ParseString(R)) return false; return false;
if (!LocalVariables.ParseString(L)) return false;
return true;
} }
if (!Outputs.IsValid() && (!OutputVariables.Variables.IsEmpty()))
bool WingGraphVariables::AssociateGraph(const UEdGraph *Graph)
{ {
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode; Outputs = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(Inputs.Get());
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode; if (!Outputs.IsValid())
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
bool OK = true;
if (!Arguments.AssociateEditablePinBase(EntryNode.Get())) OK = false;
if (!ReturnValues.AssociateEditablePinBase(ResultNode.Get())) OK = false;
if (!LocalVariables.AssociateLocalVariables(EntryNode.Get())) OK = false;
return OK;
}
void WingGraphVariables::PrintAll(FStringBuilderBase &Out, bool Always)
{ {
Arguments.PrintAll(Out, Always, TEXT("Arguments:")); UWingServer::Printf(TEXT("ERROR: couldn't create result node for graph."));
ReturnValues.PrintAll(Out, Always, TEXT("ReturnValues:"));
LocalVariables.PrintAll(Out, Always, TEXT("LocalVariables:"));
}
void WingGraphVariables::UpdateVariableTypes()
{
Arguments.UpdateVariableTypes();
ReturnValues.UpdateVariableTypes();
LocalVariables.UpdateVariableTypes();
}
void WingGraphVariables::UpdateVariableFlags()
{
Arguments.UpdateVariableFlags();
ReturnValues.UpdateVariableFlags();
LocalVariables.UpdateVariableFlags();
}
bool WingGraphVariables::UpdateVariableDefaults()
{
return Arguments.UpdateVariableDefaults() &&
ReturnValues.UpdateVariableDefaults() &&
LocalVariables.UpdateVariableDefaults();
}
bool WingGraphVariables::CheckSanity(const UEdGraph *Graph)
{
EGraphType Type = Graph->GetSchema()->GetGraphType(Graph);
if (Type == EGraphType::GT_Function)
{
return Arguments.CheckSanity(Cat::FunctionArguments) &&
ReturnValues.CheckSanity(Cat::FunctionReturnValues) &&
LocalVariables.CheckSanity(Cat::LocalVariables);
}
else if (Type == EGraphType::GT_Macro)
{
return Arguments.CheckSanity(Cat::MacroEntry) &&
ReturnValues.CheckSanity(Cat::MacroExit) &&
LocalVariables.CheckEmpty(Cat::LocalVariables);
}
else
{
UWingServer::Printf(
TEXT("Graphs of this type may not have arguments, return values, or local variables."));
return false; return false;
} }
} }
UK2Node_FunctionEntry *Locals = Cast<UK2Node_FunctionEntry>(Inputs.Get());
if (!Locals && (!LocalVariables.Variables.IsEmpty()))
{
UWingServer::Printf(TEXT("ERROR: function doesn't have a proper entry node for local variables"));
return false;
}
InputNode = Inputs.Get();
OutputNode = Outputs.Get();
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<FName> Names;
// FBlueprintEditorUtils::GetClassVariableList(BP, Names);
// if (!WingUtils::FindNoDuplicateNames(Names, Vars.GetVariables(), TEXT("variable or component"))) return;
// // Check for duplicate names against existing pins and locals.
// TSet<FName> Names;
// WingUtils::FindNoDuplicateNames(Names, EntryNode->UserDefinedPins, TEXT("local variables"));
// WingUtils::FindNoDuplicateNames(Names, ResultNode->UserDefinedPins, TEXT("local variables"));
// if (UK2Node_FunctionEntry* FuncEntry = Cast<UK2Node_FunctionEntry>(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;

View File

@@ -240,6 +240,7 @@ public:
static TArray<UEdGraphNode*> AllNodes(UBlueprint* BP); static TArray<UEdGraphNode*> AllNodes(UBlueprint* BP);
static TArray<UEdGraphNode*> AllNodes(UEdGraph *Graph); static TArray<UEdGraphNode*> AllNodes(UEdGraph *Graph);
static TArray<UBlueprint*> GetAncestorBlueprints(UBlueprint *BP, bool OldestFirst = false); static TArray<UBlueprint*> GetAncestorBlueprints(UBlueprint *BP, bool OldestFirst = false);
static UObject *GetGeneratedCDO(UBlueprint *BP);
// ----- Material helpers ----- // ----- Material helpers -----
static void EnsureMaterialGraph(UMaterial* Material); static void EnsureMaterialGraph(UMaterial* Material);

View File

@@ -7,23 +7,12 @@
struct WingTokenizer; struct WingTokenizer;
class UK2Node_EditablePinBase; class UK2Node_EditablePinBase;
class UK2Node_FunctionEntry; class UK2Node_FunctionEntry;
struct FUserPinInfo;
UENUM()
enum class EWingVariableCategory : uint8
{
BlueprintVariables,
LocalVariables,
FunctionArguments,
FunctionReturnValues,
MacroEntry,
MacroExit
};
class WingVariables class WingVariableList
{ {
public: public:
using Cat = EWingVariableCategory;
struct Var struct Var
{ {
// Internal name. // Internal name.
@@ -36,7 +25,7 @@ public:
FString DefaultValue; FString DefaultValue;
// When parsing a string, true if default was specified. // When parsing a string, true if default was specified.
bool DefaultSpecified = true; bool DefaultSpecified = false;
// Boolean flags. // Boolean flags.
TSet<FName> Flags; TSet<FName> Flags;
@@ -48,109 +37,115 @@ public:
// This is only populated if these variables are associated // This is only populated if these variables are associated
// with an editable pin base. // with an editable pin base.
TSharedPtr<FUserPinInfo> Pin = nullptr; TSharedPtr<FUserPinInfo> Pin = nullptr;
// Add the flag if it's relevant to category C.
void AddFlagIfRelevant(FName Flag, Cat C);
}; };
private:
// A list of all the variables.
TArray<Var> Variables;
// A pointer to a blueprint.
UBlueprint *Blueprint = nullptr;
// A pointer to a Function entry node.
UK2Node_FunctionEntry *FuncEntry = nullptr;
// A pointer to an editable pin base.
UK2Node_EditablePinBase* PinBase = nullptr;
public: public:
// Get the variables. // The actual list of variables.
const TArray<Var> GetVariables() { return Variables; } TArray<Var> Variables;
// Returns the set of flags that are supported by this variable category. // The list has a name, which is used for generating good messages.
static const TSet<FName> &GetRelevantFlagSet(Cat C); const TCHAR *ListName;
// Check if the variable list is empty. // Constructor.
bool IsEmpty() const { return Variables.IsEmpty(); } WingVariableList(const TCHAR *MyListName) : ListName(MyListName) {}
// Print all the variables to a string builder. // Return true if the variables are empty.
void PrintAll(FStringBuilderBase &Out, bool Always, const TCHAR *Header); void Empty() { Variables.Empty(); }
// Clear everything // Print the variables to a string builder.
void Clear(); void Print(FStringBuilderBase &Out);
// Clear any association. // Clear the BPVar and Pin fields.
void ClearAssociation(); void ClearLinks();
// Check the sanity. // Check the sanity of the vars in the array. If allow
bool CheckSanity(Cat C); // is false, then no variables are allowed in the array.
bool CheckSanity(const TSet<FName> &GoodFlags, bool Allow);
// Check that the variables are empty. // Parse variables from a string.
bool CheckEmpty(Cat C);
// Parse variables from a string. One variable per line.
// Format: type name (flag1, flag2) = defaultvalue
// Returns false on parse error.
bool ParseString(const FString &Input); bool ParseString(const FString &Input);
// Make a best effort to load all available existing state. // Parse variable names only from a string.
// This does not associate the variables. If you want to manipulate bool ParseNamesString(const FString &Input);
// the variables, you'll have to associate them.
void LoadBlueprintVariables(UBlueprint *BP);
void LoadLocalVariables(UK2Node_EditablePinBase* Node);
void LoadEditablePinBase(UK2Node_EditablePinBase* Node);
// Associate every variable in the list with an existing
// variable in a blueprint or graph.
bool AssociateBlueprintVariables(UBlueprint *BP);
bool AssociateLocalVariables(UK2Node_EditablePinBase* Node);
bool AssociateEditablePinBase(UK2Node_EditablePinBase* Node);
// Copy data from these variables into their associated blueprint variables.
void UpdateVariableTypes();
void UpdateVariableFlags();
bool UpdateVariableDefaults();
private: private:
static Var ParseVariableDescription(const FBPVariableDescription &V, Cat C); bool ParseOneVariable(WingTokenizer &Tok, Var &V);
static bool ParseOneVariable(WingTokenizer &Tok, Var &Out); bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out);
static bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out);
void UpdateBlueprintVariableTypes();
void UpdateBlueprintVariableFlags();
bool UpdateBlueprintVariableDefaults();
void UpdateLocalVariableTypes();
void UpdateLocalVariableFlags();
bool UpdateLocalVariableDefaults();
void UpdateEditablePinBaseTypes();
void UpdateEditablePinBaseFlags();
bool UpdateEditablePinBaseDefaults();
}; };
struct WingGraphVariables class WingVariables
{ {
WingVariables Arguments; public:
WingVariables ReturnValues; using Var = WingVariableList::Var;
WingVariables LocalVariables;
WingGraphVariables() {} // The backing store. Only one of these should be set.
using Cat = WingVariables::Cat; UBlueprint *Blueprint = nullptr;
UEdGraph *Graph = nullptr;
void LoadGraph(const UEdGraph *Graph); // The Workspace. At any given time, these may or may not contain
bool AssociateGraph(const UEdGraph *Graph); // the same data as the backing store.
bool ParseStrings(const FString &A, const FString &R, const FString &L);
void PrintAll(FStringBuilderBase &Out, bool Always); WingVariableList BlueprintVariables{TEXT("Blueprint Variables")};
void UpdateVariableTypes(); WingVariableList LocalVariables{TEXT("Local Variables")};
void UpdateVariableFlags(); WingVariableList InputVariables{TEXT("Input Variables")};
bool UpdateVariableDefaults(); WingVariableList OutputVariables{TEXT("Output Variables")};
bool CheckSanity(const UEdGraph *Graph);
// Constructors. Just initialize the pointers to the backing store.
WingVariables(UBlueprint *BP) : Blueprint(BP) {}
WingVariables(UEdGraph *G) : Graph(G) {}
// Clear the workspace. Doesn't affect the backing store.
void Empty();
// Clear the BPVar and Pin fields.
void ClearLinks();
// Print the contents of the workspace.
void Print(FStringBuilderBase &Out);
// Load: clear the workspace, then
// copy everything from the backing store into the workspace.
void Load();
// Check: make sure the contents of the workspace makes sense
// given the type of backing store.
bool Check();
// Use the variables in the workspace to modify the backing store.
bool Modify();
// Create every variable in the workspace in the backing store.
bool Create() { return true; }
private: private:
static Cat GraphEntryCategory(const UEdGraph *Graph); void LoadBlueprint();
static Cat GraphExitCategory(const UEdGraph *Graph); void LoadGraph();
void LoadLocalVariables(UK2Node_EditablePinBase *Node);
Var LoadBlueprintVariableDescription(FBPVariableDescription &Desc, UObject *CDO);
Var LoadLocalVariableDescription(FBPVariableDescription &Desc);
void LoadEditablePinBase(UK2Node_EditablePinBase *Node, WingVariableList &List);
bool CheckBlueprint();
bool CheckGraph();
bool ModifyBlueprint();
void ModifyBlueprintVariableFlags(Var &Input);
bool ModifyDefaultsAsProperties(WingVariableList &List, UObject *CDO);
bool ModifyGraph();
bool GetGraphNodes(
UK2Node_EditablePinBase *&InputNode,
UK2Node_EditablePinBase *&OutputNode,
UK2Node_FunctionEntry *&LocalNode);
}; };