Code is all tore up

This commit is contained in:
2026-03-30 20:10:23 -04:00
parent 386455b1d0
commit e982fec1a4
19 changed files with 876 additions and 179 deletions

Binary file not shown.

View File

@@ -11,6 +11,7 @@
#include "Engine/SimpleConstructionScript.h"
#include "Engine/SCS_Node.h"
#include "Components/ActorComponent.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "ActorComponent_Add.generated.h"
@@ -51,8 +52,9 @@ public:
// Check that the proposed name is valid
FName InternalID = WingUtils::CheckProposedName(Component);
if (InternalID.IsNone()) return;
TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP);
if (!WingUtils::FindNoneWithInternalID(InternalID, AllComponents, TEXT("Component"))) return;
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"))) return;
// Resolve the component class by name
UWingTypes::Requirements Req;
@@ -65,6 +67,7 @@ public:
if (!UWingComponentReference::CheckValidComponentClass(ComponentClass)) return;
// Find the specified parent component
TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP);
UWingComponentReference* ParentComp = WingUtils::FindOneWithExternalID(Parent, AllComponents, TEXT("Component"));
if (!ParentComp) return;

View File

@@ -4,13 +4,12 @@
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingBlueprintVar.h"
#include "WingUtils.h"
#include "WingTypes.h"
#include "WingVariables.h"
#include "Engine/Blueprint.h"
#include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "BlueprintVariable_Create.generated.h"
@@ -27,15 +26,12 @@ public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name of the new variable"))
FString Name;
UPROPERTY(meta=(Optional, Description="Variable configuration: VarType, Category, DefaultValue, InstanceEditable, BlueprintReadOnly, ExposeOnSpawn, Private, ExposeToCinematics, etc."))
FWingJsonObject Config;
UPROPERTY(meta=(Description="Variable declarations, one per line. Format: type name (flags) = default"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Add a new member variable to a Blueprint. Pass Config to set type, category, flags, etc.");
return TEXT("Add new member variables to a Blueprint. Format: 'type name (flags) = default', one per line.");
}
virtual void Handle() override
@@ -44,32 +40,33 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check validity of the proposed name
FName InternalID = WingUtils::CheckProposedName(Name);
if (InternalID.IsNone()) return;
if (!WingUtils::FindNoneWithInternalID(InternalID, BP->NewVariables, TEXT("Variable"))) return;
// Parse the variable declarations.
WingVariables Parsed;
if (!WingVariables::ParseString(Variables, Parsed)) return;
if (Parsed.IsEmpty()) { UWingServer::Print(TEXT("ERROR: No variables specified.\n")); return; }
if (!Parsed.CheckSanity(WingVariables::Cat::Blueprint)) return;
// Add the variable with a default type
FEdGraphPinType DefaultType;
DefaultType.PinCategory = UEdGraphSchema_K2::PC_Int;
if (!FBlueprintEditorUtils::AddMemberVariable(BP, InternalID, DefaultType))
// Check for name collisions against existing variables, components, and the like.
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateNames(Names, Parsed.GetVariables(), TEXT("variable or component"))) return;
// Create the variables.
for (const WingVariables::Var& V : Parsed.GetVariables())
{
UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s' to %s\n"), *Name, *WingUtils::FormatName(BP));
return;
}
// Find the newly created variable description
FWingBlueprintVar Editor(BP, Name);
if (Editor.NotFound()) return;
// Apply config if provided
if (Config.Json && Config.Json->Values.Num() > 0)
{
if (!Editor.ApplyJson(Config.Json.Get()))
if (!FBlueprintEditorUtils::AddMemberVariable(BP, V.Name, V.Type))
{
UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s'\n"),
*WingUtils::ExternalizeID(V.Name));
return;
}
}
UWingServer::Printf(TEXT("Created variable %s (%s) in %s\n"),
*Name, *UWingTypes::TypeToText(Editor.Desc->VarType), *WingUtils::FormatName(BP));
// Update everything.
if (!Parsed.AssociateBlueprintNewVariables(BP)) return;
Parsed.UpdateVariableFlags();
FKismetEditorUtilities::CompileBlueprint(BP);
if (!Parsed.UpdateVariableDefaults()) return;
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -4,10 +4,7 @@
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingUtils.h"
#include "WingBlueprintVar.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "WingVariables.h"
#include "BlueprintVariable_Dump.generated.h"
@@ -24,12 +21,9 @@ public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name of the variable to inspect"))
FString Variable;
virtual FString GetDescription() const override
{
return TEXT("Show all editable properties of a Blueprint variable.");
return TEXT("List all member variables of a Blueprint.");
}
virtual void Handle() override
@@ -38,10 +32,8 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
FWingBlueprintVar Editor(BP, Variable);
if (Editor.NotFound()) return;
UWingServer::Printf(TEXT("Variable %s in %s:\n"), *Variable, *WingUtils::FormatName(BP));
Editor.Dump();
WingVariables Vars = WingVariables::ParseBlueprintVariables(BP);
if (Vars.IsEmpty()) { UWingServer::Print(TEXT("No variables.\n")); return; }
Vars.PrintAll();
}
};

View File

@@ -4,12 +4,10 @@
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingBlueprintVar.h"
#include "WingUtils.h"
#include "WingTypes.h"
#include "WingVariables.h"
#include "Engine/Blueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "BlueprintVariable_Modify.generated.h"
@@ -26,15 +24,12 @@ public:
UPROPERTY(meta=(Description="Blueprint name or package path"))
FString Blueprint;
UPROPERTY(meta=(Description="Name of the variable to modify"))
FString Variable;
UPROPERTY(meta=(Description="Properties to change: VarType, Category, DefaultValue, InstanceEditable, BlueprintReadOnly, ExposeOnSpawn, Private, ExposeToCinematics, etc."))
FWingJsonObject Properties;
UPROPERTY(meta=(Description="Variable declarations, one per line. Format: type name (flags) = default"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Modify properties of an existing Blueprint variable.");
return TEXT("Modify existing Blueprint variables. Format: 'type name (flags) = default', one per line.");
}
virtual void Handle() override
@@ -43,19 +38,21 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
FWingBlueprintVar Editor(BP, Variable);
if (Editor.NotFound()) return;
// Parse the variable declarations.
WingVariables Parsed;
if (!WingVariables::ParseString(Variables, Parsed)) return;
if (Parsed.IsEmpty()) { UWingServer::Print(TEXT("ERROR: No variables specified.\n")); return; }
if (!Parsed.CheckSanity(WingVariables::Cat::Blueprint)) return;
if (!Properties.Json || Properties.Json->Values.Num() == 0)
{
UWingServer::Print(TEXT("ERROR: No properties specified\n"));
return;
}
// Associate with existing blueprint variables.
if (!Parsed.AssociateBlueprintNewVariables(BP)) return;
if (!Editor.ApplyJson(Properties.Json.Get()))
return;
// Update types and flags, compile, then update defaults.
Parsed.UpdateVariableTypes();
Parsed.UpdateVariableFlags();
FKismetEditorUtilities::CompileBlueprint(BP);
if (!Parsed.UpdateVariableDefaults()) return;
UWingServer::Printf(TEXT("Modified variable %s (%s) in %s\n"),
*Variable, *UWingTypes::TypeToText(Editor.Desc->VarType), *WingUtils::FormatName(BP));
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -0,0 +1,59 @@
#pragma once
#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "GraphVariables_Dump.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Dump : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a function graph (e.g. '/Game/MyBP,graph:MyFunction')"))
FString Graph;
virtual FString GetDescription() const override
{
return TEXT("List all arguments, return values, and local variables of a function graph.");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
FStringBuilderBase &Output = UWingServer::GetPrintBuffer();
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(G, EntryNode, ResultNode);
WingVariables Arguments = WingVariables::ParseEditablePinBase(EntryNode.Get());
WingVariables ReturnValues = WingVariables::ParseEditablePinBase(EntryNode.Get());
WingVariables Locals = WingVariables::ParseFunctionLocalVariables(EntryNode.Get());
if (!Arguments.GetVariables().IsEmpty())
{
Output.Appendf(TEXT("Arguments:\n"));
Arguments.PrintAll(Output, 4);
}
if (!ReturnValues.GetVariables().IsEmpty())
{
Output.Appendf(TEXT("ReturnValues:\n"));
ReturnValues.PrintAll(Output, 4);
}
if (!Locals.GetVariables().IsEmpty())
{
Output.Appendf(TEXT("LocalVariables:\n"));
Locals.PrintAll(Output, 4);
}
}
};

View File

@@ -0,0 +1,50 @@
#pragma once
#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingServer.h"
#include "WingFetcher.h"
#include "WingVariables.h"
#include "GraphVariables_Modify.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_GraphVariables_Modify : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Path to a function graph (e.g. '/Game/MyBP,graph:MyFunction')"))
FString Graph;
UPROPERTY(meta=(Description="Variable declarations, one per line. Format: type name = default"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Modify existing local variables of a function graph.");
}
virtual void Handle() override
{
WingFetcher F;
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
WingVariables Parsed;
if (!WingVariables::ParseString(Variables, Parsed)) return;
if (Parsed.IsEmpty()) { UWingServer::Print(TEXT("ERROR: No variables specified.\n")); return; }
if (!Parsed.CheckSanity(WingVariables::Cat::FunctionLocal)) return;
if (!Parsed.AssociateFunctionLocalVariables(G)) return;
Parsed.UpdateVariableTypes();
Parsed.UpdateVariableFlags();
Parsed.UpdateVariableDefaults();
UWingServer::Printf(TEXT("Success.\n"));
}
};

View File

@@ -0,0 +1,36 @@
#pragma once
#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingServer.h"
#include "WingVariables.h"
#include "Test_ParseVariables.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_Test_ParseVariables : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Variable declarations, one per line. Format: type name (flags) = default"))
FString Variables;
virtual FString GetDescription() const override
{
return TEXT("Test: parse variable declarations and print them back out.");
}
virtual void Handle() override
{
WingVariables Parsed;
if (!WingVariables::ParseString(Variables, Parsed)) return;
UWingServer::Print(TEXT("Parsed variables:\n"));
Parsed.PrintAll(1);
}
};

View File

@@ -9,6 +9,7 @@
#include "WidgetBlueprint.h"
#include "Blueprint/WidgetTree.h"
#include "Blueprint/UserWidget.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Components/PanelWidget.h"
#include "Widget_Create.generated.h"
@@ -72,8 +73,11 @@ public:
// Check that the name is unique among existing widgets.
TArray<UWidget*> AllWidgets;
Tree->GetAllWidgets(AllWidgets);
if (!WingUtils::FindNoneWithInternalID(InternalID, AllWidgets, TEXT("Widget"))) return;
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateNames(Names, AllWidgets, TEXT("widget or variable"))) return;
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("widget or variable"))) return;
// If a parent is specified, find it and verify it's a panel.
UPanelWidget* ParentPanel = nullptr;
if (!Parent.IsEmpty())

View File

@@ -141,6 +141,14 @@ void UWingComponentReference::AddChildNode(UBlueprint *BP, USCS_Node *NewNode, F
bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingComponentReference *Parent, FName Name)
{
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (Names.Contains(Name))
{
UWingServer::Printf(TEXT("There is already a variable or component named %s in %s.\n"),
*WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class));
return false;
}
FoundComponent ExistingComponent = FindComponent(BP, Name);
if (!CheckNoSuchComponent(ExistingComponent)) return false;
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);

View File

@@ -13,6 +13,7 @@
#include "WingFunctionArgs.h"
#include "WingVariables.h"
#include "MaterialGraph/MaterialGraphNode.h"
#include "Kismet2/BlueprintEditorUtils.h"
WingGraphExport::WingGraphExport(UEdGraph* InGraph)
: Graph(InGraph)
@@ -288,18 +289,26 @@ void WingGraphExport::EmitMaterialProperties(UEdGraphNode* Node, FStringBuilderB
void WingGraphExport::EmitLocalVariables()
{
for (UEdGraphNode* Node : Graph->Nodes)
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
WingVariables Arguments = WingVariables::ParseEditablePinBase(EntryNode.Get());
WingVariables ReturnValues = WingVariables::ParseEditablePinBase(EntryNode.Get());
WingVariables Locals = WingVariables::ParseFunctionLocalVariables(EntryNode.Get());
if (!Arguments.GetVariables().IsEmpty())
{
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(Node);
if (!EntryNode) continue;
WingVariables Locals = WingVariables::ParseFunctionLocalVariables(EntryNode);
if (!Locals.IsEmpty())
{
Output.Appendf(TEXT("Function Local Variables:\n"));
Locals.PrintAll(Output, 4);
}
break;
Output.Appendf(TEXT("Arguments:\n"));
Arguments.PrintAll(Output, 4);
}
if (!ReturnValues.GetVariables().IsEmpty())
{
Output.Appendf(TEXT("ReturnValues:\n"));
ReturnValues.PrintAll(Output, 4);
}
if (!Locals.GetVariables().IsEmpty())
{
Output.Appendf(TEXT("LocalVariables:\n"));
Locals.PrintAll(Output, 4);
}
}

View File

@@ -44,19 +44,21 @@ WingCharacterClasses::WingCharacterClasses()
WingCharacterClasses WingCharacterClasses::TheSet;
void WingTokenizer::Add(TCHAR Type, FName Name)
void WingTokenizer::Add(TCHAR Type, FName Name, FStringView Before, FStringView After)
{
Token T;
T.Type = Type;
T.Name = Name;
T.Source = FStringView(Before.GetData(), After.GetData() - Before.GetData());
Tokens.Add(T);
}
void WingTokenizer::Add(TCHAR Type, FStringView Rest)
void WingTokenizer::Add(TCHAR Type, FStringView Rest, FStringView Before, FStringView After)
{
Token T;
T.Type = Type;
T.Rest = Rest;
T.Source = FStringView(Before.GetData(), After.GetData() - Before.GetData());
Tokens.Add(T);
}
@@ -125,6 +127,16 @@ TCHAR WingTokenizer::TokenizeEscapeSequence(FStringView &Rest, FString &Error)
return Result;
}
FStringView WingTokenizer::TokenizeAssetName(FStringView &Rest, FString &Error)
{
if (!Error.IsEmpty()) return FStringView();
int i = 0;
while ((i < Rest.Len()) && (Rest[i] != ',')) i++;
FStringView Result = Rest.SubStr(0, i);
Rest = Rest.RightChop(i);
return Result;
}
FName WingTokenizer::TokenizeIdentifier(FStringView &Rest, FString &Error)
{
if (!Error.IsEmpty()) return FName();
@@ -185,21 +197,31 @@ WingTokenizer::WingTokenizer(const FString& In)
Rest = Rest.RightChop(1);
continue;
}
FStringView Before = Rest;
if (Ch == '=')
{
Add(RestOfLine, Rest.RightChop(1));
FStringView Body = Rest.RightChop(1);
Rest = Rest.RightChop(Rest.Len());
Add(RestOfLine, Body, Before, Rest);
break;
}
if (Ch == '/')
{
FStringView Asset = TokenizeAssetName(Rest, Error);
Add(AssetName, Asset, Before, Rest);
continue;
}
if ((Ch == '.') || (Ch == '&'))
{
Add(Identifier, TokenizeIdentifier(Rest, Error));
FName Id = TokenizeIdentifier(Rest, Error);
Add(Identifier, Id, Before, Rest);
continue;
}
Cat Category = WingCharacterClasses::GetCat(Ch);
if (Category == Cat::Punctuation)
{
Add(Ch, FString());
Rest = Rest.RightChop(1);
Add(Ch, FString(), Before, Rest);
continue;
}
if (Category == Cat::Control)
@@ -207,17 +229,38 @@ WingTokenizer::WingTokenizer(const FString& In)
Error = "Control characters in input, not allowed";
break;
}
Add(Identifier, TokenizeIdentifier(Rest, Error));
FName Id = TokenizeIdentifier(Rest, Error);
Add(Identifier, Id, Before, Rest);
continue;
}
if (!Error.IsEmpty()) Tokens.Empty();
// Two sentinels means we can safely do lookahead 2 without risk.
Add(0, FName());
Add(0, FName());
Rest = Rest.LeftChop(Rest.Len());
Add(0, FName(), Rest, Rest);
Add(0, FName(), Rest, Rest);
Next = Tokens.GetData();
}
void WingTokenizer::SaveCursor(FName Name)
{
int Cursor = Next - Tokens.GetData();
for (auto &Pair : SavedCursor) if (Pair.Key == Name) { Pair.Value = Cursor; return; }
SavedCursor.Emplace(Name, Cursor);
}
FStringView WingTokenizer::GetRange(FName SavePos, int Extra) const
{
int Lo = 0;
for (auto &Pair : SavedCursor) if (Pair.Key == SavePos) Lo = Pair.Value;
int Hi = (Next - Tokens.GetData()) + Extra;
Hi = FMath::Clamp(Hi, Lo, Tokens.Num());
if (Lo >= Hi) return FStringView();
const TCHAR* Start = Tokens[Lo].Source.GetData();
const TCHAR* End = Tokens[Hi - 1].Source.GetData() + Tokens[Hi - 1].Source.Len();
return FStringView(Start, End - Start);
}
void WingTokenizer::PrintEverything(FStringBuilderBase &Out) const
{
if (!Error.IsEmpty())
@@ -242,7 +285,7 @@ void WingTokenizer::PrintEverything(FStringBuilderBase &Out) const
}
}
}
if (T.Type == RestOfLine)
if ((T.Type == RestOfLine) || (T.Type == AssetName))
{
Out.Appendf(TEXT("[%s]"), *FString(T.Rest));
}

View File

@@ -431,21 +431,16 @@ static const FName NAME_TypeSoft(TEXT("Soft"));
static const FName NAME_TypeClass(TEXT("Class"));
static const FName NAME_TypeSoftClass(TEXT("SoftClass"));
static const FName NAME_StartOfType("Start-of-Type");
void UWingTypes::PrintParseError(WingTokenizer& Tok, const TCHAR* Message)
{
UWingServer::Printf(TEXT("ERROR parsing type '%s' — %s\n"), *Tok.GetInput(), Message);
FString TypeText(Tok.GetRange(NAME_StartOfType, 1));
UWingServer::Printf(TEXT("ERROR parsing type '%s' — %s\n"), *TypeText, Message);
UWingServer::SuggestManual(WingManual::Section::Types);
}
bool UWingTypes::ParseEOF(WingTokenizer& Tok)
{
if (Tok.NextType() != 0)
{
PrintParseError(Tok, TEXT("extra tokens at end of input"));
return false;
}
return true;
}
bool UWingTypes::ParseChar(WingTokenizer& Tok, TCHAR c)
{
@@ -547,7 +542,6 @@ bool UWingTypes::ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType)
{
if (!ParseMaybeWrapped(Tok, OutType)) return false;
}
if (!ParseEOF(Tok)) return false;
return true;
}
@@ -611,8 +605,12 @@ bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGr
// ---------------------------------------------------------------------------
bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require)
bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF)
{
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
check(Types);
Tok.SaveCursor(NAME_StartOfType);
if (!Require.BlueprintType.IsSet() ||
!Require.Blueprintable.IsSet() ||
!Require.AllowContainer.IsSet())
@@ -621,19 +619,23 @@ bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, co
return false;
}
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
check(Types);
WingTokenizer Tok(Text);
OutPinType = FEdGraphPinType();
if (!Types->ParseType(Tok, OutPinType))
{
OutPinType = FEdGraphPinType(); return false;
}
if (CheckEOF && (Tok.NextType() != 0))
{
PrintParseError(Tok, TEXT("Extra tokens at end of Type name"));
OutPinType = FEdGraphPinType(); return false;
}
if (!Require.AllowContainer.GetValue())
{
if (OutPinType.IsContainer())
{
FString Text(Tok.GetRange(NAME_StartOfType, 0));
UWingServer::Printf(TEXT("ERROR: Type '%s' is a container, not allowed here\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false;
@@ -665,6 +667,7 @@ bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, co
{
if (!IsBlueprintType(OutPinType))
{
FString Text(Tok.GetRange(NAME_StartOfType, 0));
UWingServer::Printf(TEXT("ERROR: Not a blueprint type: %s\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false;
@@ -675,6 +678,7 @@ bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, co
{
if (!IsBlueprintable(OutPinType))
{
FString Text(Tok.GetRange(NAME_StartOfType, 0));
UWingServer::Printf(TEXT("ERROR: Not a blueprintable type: %s\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false;
@@ -684,6 +688,12 @@ bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, co
return true;
}
bool UWingTypes::TextToType(const FString &Text, FEdGraphPinType& OutPinType, const Requirements &Require)
{
WingTokenizer Tok(Text);
return TextToType(Tok, OutPinType, Require, true);
}
UClass* UWingTypes::TextToOneObjectType(const FString& Text, const Requirements &Require)
{
FEdGraphPinType PinType;

View File

@@ -2,8 +2,13 @@
#include "WingServer.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "WingProperty.h"
#include "WingTokenizer.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_FunctionEntry.h"
#include "K2Node_FunctionResult.h"
#include "K2Node_Tunnel.h"
#include "K2Node_EditablePinBase.h"
#include "Kismet2/BlueprintEditorUtils.h"
// Flag names used for blueprint variables.
@@ -33,30 +38,167 @@ 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)
WingVariables WingVariables::ParseBlueprintVariables(UBlueprint *BP)
{
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);
WingVariables Result;
Result.Category = Cat::Blueprint;
Result.Blueprint = BP;
for (FBPVariableDescription& Desc : BP->NewVariables)
{
// Skip event dispatchers.
if (Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue;
if (Desc.PropertyFlags & CPF_Interp)
Result.AddFlagIfRelevant(Flag_ExposeToCinematics, Category);
// Parse the bulk of the flags.
Var V = ParseVariableDescription(Desc, Cat::Blueprint);
V.BPVar = &Desc;
if (Desc.HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn))
Result.AddFlagIfRelevant(Flag_ExposeOnSpawn, Category);
if (Desc.HasMetaData(FBlueprintMetadata::MD_Private))
Result.AddFlagIfRelevant(Flag_Private, Category);
// 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(UK2Node_EditablePinBase *Node)
{
WingVariables Result;
if (UK2Node_FunctionEntry *Func = Cast<UK2Node_FunctionEntry>(Node))
{
Result.Category = Cat::FunctionLocal;
Result.PinBase = Node;
for (FBPVariableDescription& Desc : Func->LocalVariables)
{
Var V = ParseVariableDescription(Desc, Cat::FunctionLocal);
V.DefaultValue = Desc.DefaultValue;
V.BPVar = &Desc;
Result.Variables.Add(MoveTemp(V));
}
}
else
{
Result.Category = Cat::Empty;
}
return Result;
}
WingVariables WingVariables::ParseEditablePinBase(UK2Node_EditablePinBase* Node)
{
WingVariables Result;
if (Node != nullptr)
{
Result.Category = Cat::EditablePinBase;
Result.PinBase = Node;
for (const TSharedPtr<FUserPinInfo>& PinInfo : Node->UserDefinedPins)
{
Var V;
V.Name = PinInfo->PinName;
V.Type = PinInfo->PinType;
V.DefaultValue = PinInfo->PinDefaultValue;
V.Pin = PinInfo;
Result.Variables.Add(MoveTemp(V));
}
}
else
{
Result.Category = Cat::Empty;
}
return Result;
}
bool WingVariables::AssociateBlueprintNewVariables(UBlueprint *BP)
{
Category = Cat::Blueprint;
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;
}
Blueprint = BP;
return true;
}
bool WingVariables::AssociateFunctionLocalVariables(UK2Node_EditablePinBase *Node)
{
Category = Cat::FunctionLocal;
if (UK2Node_FunctionEntry *Func = Cast<UK2Node_FunctionEntry>(Node))
{
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
{
UWingServer::Printf(TEXT("Graph can't have local variables, not a function graph: %s\n"),
*WingUtils::FormatName(Node->GetGraph()));
return false;
}
}
bool WingVariables::AssociateEditablePinBase(UK2Node_EditablePinBase *InPinBase)
{
if (!InPinBase || !InPinBase->IsEditable())
{
UWingServer::Print(TEXT("ERROR: Node does not have editable pins.\n"));
return false;
}
Category = Cat::EditablePinBase;
PinBase = InPinBase;
for (Var &V : Variables)
{
TSharedPtr<FUserPinInfo> *Found = WingUtils::FindOneWithInternalID(V.Name, InPinBase->UserDefinedPins, TEXT("pin"));
if (!Found) return false;
V.Pin = *Found;
}
return true;
}
void WingVariables::UpdateVariableTypes()
{
switch (Category)
{
case Cat::Blueprint: UpdateBlueprintVariableTypes(); break;
case Cat::FunctionLocal: UpdateLocalVariableTypes(); break;
case Cat::EditablePinBase: UpdateEditablePinBaseTypes(); break;
default: return;
}
}
void WingVariables::UpdateVariableFlags()
{
switch (Category)
{
case Cat::Blueprint: UpdateBlueprintVariableFlags(); break;
case Cat::FunctionLocal: UpdateLocalVariableFlags(); break;
case Cat::EditablePinBase: UpdateEditablePinBaseFlags(); break;
default: return;
}
}
bool WingVariables::UpdateVariableDefaults()
{
switch (Category)
{
case Cat::Blueprint: return UpdateBlueprintVariableDefaults(); break;
case Cat::FunctionLocal: return UpdateLocalVariableDefaults(); break;
case Cat::EditablePinBase: return UpdateEditablePinBaseDefaults(); break;
default: return true;
}
}
bool WingVariables::CheckSanity(Cat Category)
{
const TSet<FName> &Relevant = GetRelevantFlagSet(Category);
@@ -94,42 +236,113 @@ bool WingVariables::CheckSanity(Cat Category)
}
WingVariables WingVariables::ParseBlueprintVariables(const UBlueprint *BP)
bool WingVariables::ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out)
{
WingVariables Result;
for (const FBPVariableDescription& Desc : BP->NewVariables)
Tok.Advance(); // Step over open-paren
while (Tok.TokenIs(Tok.Identifier))
{
// 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));
Out.Add(Tok.NextName());
Tok.Advance();
// Commas are optional.
if (Tok.TokenIs(',')) Tok.Advance();
}
return Result;
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;
}
WingVariables WingVariables::ParseFunctionLocalVariables(const UK2Node_FunctionEntry *Func)
bool WingVariables::ParseOneVariable(WingTokenizer &Tok, Var &V)
{
WingVariables Result;
if (!Func) return Result;
// Parse type.
UWingTypes::Requirements Req;
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(Tok, V.Type, Req, false))
return false;
for (const FBPVariableDescription& Desc : Func->LocalVariables)
// Parse name.
if (Tok.NextType() != Tok.Identifier)
{
Var V = ParseVariableDescription(Desc, Cat::FunctionLocal);
V.DefaultValue = Desc.DefaultValue;
Result.Variables.Add(MoveTemp(V));
UWingServer::Print(TEXT("ERROR: Expected variable name after type\n"));
return false;
}
V.Name = Tok.NextName();
Tok.Advance();
// Parse optional flags: (flag1, flag2, ...)
if (Tok.TokenIs('('))
{
if (!ParseVariableFlags(Tok, V.Flags)) return false;
}
// Parse optional default value: = rest-of-line
if (Tok.NextType() == Tok.RestOfLine)
{
V.DefaultSpecified = true;
V.DefaultValue = FString(Tok.NextRest().TrimStartAndEnd());
Tok.Advance();
}
// Should be at end of line.
if (Tok.NextType() != 0)
{
Tok.SaveCursor(NAME_None);
UWingServer::Printf(TEXT("ERROR: Unexpected token after variable declaration: '%s'\n"),
*FString(Tok.GetRange(NAME_None, 1)));
return false;
}
return true;
}
bool WingVariables::ParseString(const FString &Input)
{
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;
}
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;
}
@@ -170,3 +383,154 @@ void WingVariables::PrintAll(int32 Indent)
{
PrintAll(UWingServer::GetPrintBuffer(), Indent);
}
void WingVariables::UpdateBlueprintVariableTypes()
{
for (const Var &Input : Variables)
{
Input.BPVar->VarType = Input.Type;
}
}
void WingVariables::UpdateBlueprintVariableFlags()
{
for (const Var &Input : Variables)
{
FBPVariableDescription &Out = *Input.BPVar;
// InstanceEditable: absence means CPF_DisableEditOnInstance is set
if (Input.Flags.Contains(Flag_InstanceEditable))
Out.PropertyFlags &= ~CPF_DisableEditOnInstance;
else
Out.PropertyFlags |= CPF_DisableEditOnInstance;
if (Input.Flags.Contains(Flag_BlueprintReadOnly))
Out.PropertyFlags |= CPF_BlueprintReadOnly;
else
Out.PropertyFlags &= ~CPF_BlueprintReadOnly;
if (Input.Flags.Contains(Flag_ExposeToCinematics))
Out.PropertyFlags |= CPF_Interp;
else
Out.PropertyFlags &= ~CPF_Interp;
if (Input.Flags.Contains(Flag_ExposeOnSpawn))
Out.SetMetaData(FBlueprintMetadata::MD_ExposeOnSpawn, TEXT("true"));
else
Out.RemoveMetaData(FBlueprintMetadata::MD_ExposeOnSpawn);
if (Input.Flags.Contains(Flag_Private))
Out.SetMetaData(FBlueprintMetadata::MD_Private, TEXT("true"));
else
Out.RemoveMetaData(FBlueprintMetadata::MD_Private);
}
}
bool WingVariables::UpdateBlueprintVariableDefaults()
{
UObject *CDO = nullptr;
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)
{
FProperty* Prop = Blueprint->GeneratedClass->FindPropertyByName(Input.Name);
if (!Prop)
{
UWingServer::Printf(TEXT("Variable exists in blueprint, but not the generated class, which is weird: %s."),
*WingTokenizer::ExternalizeID(Input.Name));
return false;
}
if (!FWingProperty(Prop, CDO).SetText(Input.DefaultValue)) OK = 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;
}
void WingVariables::UpdateEditablePinBaseTypes()
{
for (const Var &Input : Variables)
Input.Pin->PinType = Input.Type;
PinBase->ReconstructNode();
}
void WingVariables::UpdateEditablePinBaseFlags()
{
// 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;
}
void WingGraphVariables::ParseGraph(const UEdGraph *Graph)
{
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, EntryNode, ResultNode);
Arguments = WingVariables::ParseEditablePinBase(EntryNode.Get());
ReturnValues = WingVariables::ParseEditablePinBase(ResultNode.Get());
LocalVariables = WingVariables::ParseLocalVariables(EntryNode.Get());
}
bool WingGraphVariables::ParseAll(const FString &A, const FString &R, const FString &L)
{
if (!Arguments.ParseString(A)) return false;
if (!ReturnValues.ParseString(R)) return false;
if (!LocalVariables.ParseString(L)) return false;
return true;
}
bool WingGraphVariables::AssociateAll(const UEdGraph *Graph)
{
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode;
TWeakObjectPtr<UK2Node_EditablePinBase> ResultNode;
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;
}

View File

@@ -103,13 +103,15 @@ struct WingTokenizer
//
//////////////////////////////////////////////////////////////////////////
const TCHAR Identifier = 'i';
const TCHAR RestOfLine = 'r';
static const TCHAR Identifier = 'i';
static const TCHAR AssetName = 'a';
static const TCHAR RestOfLine = 'r';
// Get the next token.
TCHAR NextType() const { return Next[0].Type; }
FName NextName() const { return Next[0].Name; }
FStringView NextRest() const { return Next[0].Rest; }
FStringView NextSource() const { return Next[0].Source; }
// Check the next token.
bool TokenIs(TCHAR Type) const { return Next[0].Type == Type; }
@@ -122,9 +124,13 @@ struct WingTokenizer
// Advance the cursor. Don't move past the two sentinels.
void Advance() { int I = Next-Tokens.GetData(); if (I + 2 < Tokens.Num()) Next++; }
// Get the original input string.
const FString& GetInput() const { return Input; }
// Save the current cursor position, giving the save a name.
void SaveCursor(FName Name);
// Get the input text from a saved cursor position to the current
// cursor, optionally including additional tokens.
FStringView GetRange(FName SavePos, int32 Extra = 0) const;
// Tokenize a line of input. The tokens are stored in
// the token array, and the cursor is positioned on the first
@@ -176,8 +182,9 @@ private:
struct Token
{
FName Name; // Only if it's an identifier token.
FStringView Rest; // Only if it's a rest-of-line token.
FStringView Rest; // Only if it's a rest-of-line token or Asset.
TCHAR Type = 0; // A punctuation mark, or Identifier, or RestOfLine
FStringView Source; // The span of input that produced this token.
};
// The string that we tokenized.
@@ -192,6 +199,9 @@ private:
// The cursor which advances along the tokens. Never moves past sentinels.
Token *Next;
// A table of saved cursor positions.
TArray<TPair<FName, int>, TInlineAllocator<3>> SavedCursor;
//////////////////////////////////////////////////////////////////////////
//
// Internal Implementation Functions.
@@ -200,15 +210,22 @@ private:
using Cat = WingCharacterClasses::Cat;
// Add a token to the token array.
void Add(TCHAR Type, FName InternalID);
void Add(TCHAR Type, FStringView Rest);
// Add a token to the token array. Before/After are the Rest
// string view before and after tokenization; the difference
// is stored as the Source span.
void Add(TCHAR Type, FName InternalID, FStringView Before, FStringView After);
void Add(TCHAR Type, FStringView Rest, FStringView Before, FStringView After);
// Convert numbers to TCHAR. If there's an error, set the error
// message and return zero.
static TCHAR FromHex(FStringView Digits, FString &Error);
static TCHAR FromDecimal(FStringView Digits, FString &Error);
// Tokenize an asset name. Attempt to consume an asset name from rest,
// and return the asset name as a string view. On error,
// sets the error message and returns the empty string view.
static FStringView TokenizeAssetName(FStringView &Rest, FString &Error);
// Tokenize an escape sequence. Attempts to consume a valid escape
// sequence from rest, and return the character indicated. On error,
// sets the error message and returns zero.

View File

@@ -118,7 +118,6 @@ private:
// ---------------------------------------------------------------------------
static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message);
static bool ParseEOF(WingTokenizer& Tok);
static bool ParseChar(WingTokenizer& Tok, TCHAR c);
bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType);
bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType);
@@ -144,6 +143,11 @@ public:
};
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. If CheckEOF is true,
// makes sure that the input is *just* a type and nothing else after it.
static bool TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF);
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false.
static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require);

View File

@@ -14,6 +14,7 @@
#include "K2Node_EditablePinBase.h"
#include "Components/Widget.h"
#include "WingActorComponent.h"
#include "WingVariables.h"
struct FEdGraphSchemaAction;
class UAnimationStateMachineGraph;
@@ -60,8 +61,10 @@ public:
static FName GetFName(const FProperty *Prop) { return Prop->GetFName(); }
static FName GetFName(const FWingProperty &Prop);
static FName GetFName(const FUserPinInfo &Pin) { return Pin.PinName; }
static FName GetFName(const TSharedPtr<FUserPinInfo> &Pin) { return Pin->PinName; }
static FName GetFName(const UWingComponentReference *Ref) { return Ref->VariableName; }
static FName GetFName(const UWidget *Widget) { return Widget->GetFName(); }
static FName GetFName(const WingVariables::Var &Var) { return Var.Name; }
////////////////////////////////////////////////////////
//
@@ -132,6 +135,21 @@ public:
return FindNoneWithInternalID(InternalID, Array, Kind);
}
static bool FindNoDuplicateName(TSet<FName> &Collection, FName InternalID, const TCHAR *Kind)
{
if (Collection.Contains(InternalID))
{ CheckExactlyOneNamed(2, Kind, InternalID); return false; }
return true;
}
template<typename ArrayType>
static bool FindNoDuplicateNames(TSet<FName> &Collection, ArrayType &&Array, const TCHAR *Kind)
{
for (auto &Elt : Array)
if (!FindNoDuplicateName(Collection, GetFName(Elt), Kind)) return false;
return true;
}
////////////////////////////////////////////////////////
//
// Name Formatting

View File

@@ -1,20 +1,23 @@
#pragma once
#include "CoreMinimal.h"
#include "WingProperty.h"
#include "EdGraph/EdGraphPin.h"
#include "Engine/Blueprint.h"
#include "K2Node_EditablePinBase.h"
class UK2Node_FunctionEntry;
struct WingTokenizer;
class WingVariables
{
public:
enum class Cat
{
Empty,
Blueprint,
FunctionLocal
LocalVariable,
EditablePinBase,
};
private:
struct Var
{
// Internal name.
@@ -26,18 +29,41 @@ private:
// Default Value.
FString DefaultValue;
// When parsing a string, true if default was specified.
bool DefaultSpecified = true;
// Boolean flags.
TSet<FName> Flags;
// This is only populated if these variables are associated
// with blueprint variables or local variables.
FBPVariableDescription *BPVar = nullptr;
// This is only populated if these variables are associated
// with an editable pin base.
TSharedPtr<FUserPinInfo> Pin = nullptr;
// Add the flag if it's relevant to category C.
void AddFlagIfRelevant(FName Flag, Cat C);
};
private:
// The category of these variables.
Cat Category;
// A list of all the variables.
TArray<Var> Variables;
TArray<Var> Variables;
// A pointer to a blueprint.
UBlueprint *Blueprint = nullptr;
// A pointer to an editable pin base.
UK2Node_EditablePinBase* PinBase = nullptr;
public:
// Get the variables.
const TArray<Var> GetVariables() { return Variables; }
// Returns the set of flags that are supported by this variable category.
static const TSet<FName> &GetRelevantFlagSet(Cat C);
@@ -53,11 +79,52 @@ public:
// Make sure that this variable list makes sense for the given context.
bool CheckSanity(Cat Category);
// 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);
// Parse variables from a given source.
static WingVariables ParseBlueprintVariables(const UBlueprint *BP);
static WingVariables ParseFunctionLocalVariables(const UK2Node_FunctionEntry *F);
static WingVariables ParseBlueprintVariables(UBlueprint *BP);
static WingVariables ParseLocalVariables(UK2Node_EditablePinBase* Node);
static WingVariables ParseEditablePinBase(UK2Node_EditablePinBase* Node);
// Associate every variable in the list with an existing blueprint variable.
bool AssociateBlueprintNewVariables(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:
static Var ParseVariableDescription(const FBPVariableDescription &V, Cat C);
static bool ParseOneVariable(WingTokenizer &Tok, Var &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
{
WingVariables Arguments;
WingVariables ReturnValues;
WingVariables LocalVariables;
WingGraphVariables() {}
void ParseGraph(const UEdGraph *Graph);
bool ParseStrings(const FString &A, const FString &R, const FString &L);
bool AssociateGraph(const UEdGraph *Graph);
};

View File

@@ -20,19 +20,38 @@ def main():
print("Usage: ue-wingman.py ShowCommands [key=value ...]")
sys.exit(1)
msg = {"command": args[0]}
for arg in args[1:]:
key, _, value = arg.partition("=")
if value.lower() == "true":
value = True
elif value.lower() == "false":
value = False
else:
no_args_commands = {"ShowCommands", "UserManual"}
if len(args) == 1 and args[0] not in no_args_commands:
# No extra arguments: read JSON object from stdin.
# Accumulate lines and try to parse after each one.
decoder = json.JSONDecoder()
raw = ""
msg = None
for line in sys.stdin:
raw += line
try:
value = int(value)
except ValueError:
pass
msg[key] = value
msg, _ = decoder.raw_decode(raw.lstrip())
break
except json.JSONDecodeError:
continue
if msg is None:
print("Could not parse a complete JSON object from stdin")
sys.exit(1)
msg["command"] = args[0]
else:
msg = {"command": args[0]}
for arg in args[1:]:
key, _, value = arg.partition("=")
if value.lower() == "true":
value = True
elif value.lower() == "false":
value = False
else:
try:
value = int(value)
except ValueError:
pass
msg[key] = value
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(TIMEOUT)