Code is all tore up
This commit is contained in:
BIN
Content/Testing/BP_Test.uasset
LFS
BIN
Content/Testing/BP_Test.uasset
LFS
Binary file not shown.
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
if (!FBlueprintEditorUtils::AddMemberVariable(BP, V.Name, V.Type))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s'\n"),
|
||||
*WingUtils::ExternalizeID(V.Name));
|
||||
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()))
|
||||
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"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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"));
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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,7 +73,10 @@ 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
Output.Appendf(TEXT("Arguments:\n"));
|
||||
Arguments.PrintAll(Output, 4);
|
||||
}
|
||||
break;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
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_DisableEditOnInstance))
|
||||
Result.AddFlagIfRelevant(Flag_InstanceEditable, Category);
|
||||
// Parse the bulk of the flags.
|
||||
Var V = ParseVariableDescription(Desc, Cat::Blueprint);
|
||||
V.BPVar = &Desc;
|
||||
|
||||
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);
|
||||
// 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)
|
||||
Out.Add(Tok.NextName());
|
||||
Tok.Advance();
|
||||
// Commas are optional.
|
||||
if (Tok.TokenIs(',')) Tok.Advance();
|
||||
}
|
||||
if (!Tok.TokenIs(')'))
|
||||
{
|
||||
UObject* CDO = BP->GeneratedClass->GetDefaultObject();
|
||||
FProperty* Prop = BP->GeneratedClass->FindPropertyByName(Desc.VarName);
|
||||
if (CDO && Prop)
|
||||
V.DefaultValue = FWingProperty(Prop, CDO).GetText();
|
||||
Tok.SaveCursor(NAME_None);
|
||||
UWingServer::Printf(TEXT("ERROR: flag list contains invalid token '%s'\n"),
|
||||
*FString(Tok.GetRange(NAME_None, 1)));
|
||||
return false;
|
||||
}
|
||||
|
||||
Result.Variables.Add(MoveTemp(V));
|
||||
}
|
||||
return Result;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
@@ -123,8 +125,12 @@ 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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,25 @@ def main():
|
||||
print("Usage: ue-wingman.py ShowCommands [key=value ...]")
|
||||
sys.exit(1)
|
||||
|
||||
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:
|
||||
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("=")
|
||||
|
||||
Reference in New Issue
Block a user