Framework for printing abridged manual sections in response to syntactic mistakes
This commit is contained in:
@@ -27,7 +27,7 @@ public:
|
|||||||
FString Graph;
|
FString Graph;
|
||||||
|
|
||||||
UPROPERTY(meta=(Optional, Description="True to include less-significant details"))
|
UPROPERTY(meta=(Optional, Description="True to include less-significant details"))
|
||||||
bool bDetails;
|
bool IncludeDetails;
|
||||||
|
|
||||||
virtual FString GetDescription() const override
|
virtual FString GetDescription() const override
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ public:
|
|||||||
|
|
||||||
WingGraphExport Exporter(G);
|
WingGraphExport Exporter(G);
|
||||||
UWingServer::Print(*Exporter.GetOutput());
|
UWingServer::Print(*Exporter.GetOutput());
|
||||||
if (bDetails)
|
if (IncludeDetails)
|
||||||
{
|
{
|
||||||
UWingServer::Print(Exporter.GetDetails());
|
UWingServer::Print(Exporter.GetDetails());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingTypes.h"
|
#include "WingTypes.h"
|
||||||
#include "WingJson.h"
|
#include "WingJson.h"
|
||||||
#include "WingUtils.h"
|
#include "WingManual.h"
|
||||||
#include "ShowCommands.generated.h"
|
#include "ShowCommands.generated.h"
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
@@ -26,60 +26,37 @@ public:
|
|||||||
return TEXT("List all available commands with their descriptions.");
|
return TEXT("List all available commands with their descriptions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitCommand(UClass* Class)
|
virtual void Handle() override
|
||||||
{
|
|
||||||
if (Verbose)
|
|
||||||
{
|
|
||||||
WingUtils::PrintHandlerHelp(Class);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UWingServer::Print(WingUtils::GetHandlerName(Class));
|
|
||||||
UWingServer::Print(TEXT("("));
|
|
||||||
bool bFirst = true;
|
|
||||||
for (TFieldIterator<FProperty> PropIt(Class, EFieldIterationFlags::None); PropIt; ++PropIt)
|
|
||||||
{
|
|
||||||
if (!bFirst) UWingServer::Print(TEXT(","));
|
|
||||||
bFirst = false;
|
|
||||||
if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?"));
|
|
||||||
UWingServer::Print(PropIt->GetName());
|
|
||||||
}
|
|
||||||
UWingServer::Print(TEXT(")\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitCommandList(bool bHalfBaked)
|
|
||||||
{
|
{
|
||||||
FString QueryLower = Query.ToLower();
|
FString QueryLower = Query.ToLower();
|
||||||
FString PrevGroup;
|
FString PrevGroup;
|
||||||
|
|
||||||
for (UClass* Class : WingUtils::CollectHandlerClasses())
|
for (UClass* Class : WingUtils::CollectHandlerClasses())
|
||||||
{
|
{
|
||||||
bool bIsHalfBaked = Class->GetMetaData(TEXT("ModuleRelativePath")).StartsWith(TEXT("HalfBaked/"));
|
|
||||||
if (bIsHalfBaked != bHalfBaked)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
FString ToolName = WingUtils::GetHandlerName(Class);
|
FString ToolName = WingUtils::GetHandlerName(Class);
|
||||||
if (!ToolName.ToLower().Contains(QueryLower))
|
if (!ToolName.ToLower().Contains(QueryLower))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Blank line between groups
|
// Blank line between groups
|
||||||
FString Group = WingUtils::GetHandlerGroup(Class);
|
if (!Verbose)
|
||||||
if (Group != PrevGroup)
|
|
||||||
{
|
{
|
||||||
if (!PrevGroup.IsEmpty())
|
FString Group = WingUtils::GetHandlerGroup(Class);
|
||||||
UWingServer::Print(TEXT("\n"));
|
if (Group != PrevGroup)
|
||||||
PrevGroup = Group;
|
{
|
||||||
|
if (!PrevGroup.IsEmpty())
|
||||||
|
UWingServer::Print(TEXT("\n"));
|
||||||
|
PrevGroup = Group;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitCommand(Class);
|
if (Verbose)
|
||||||
|
{
|
||||||
|
WingManual::PrintHandlerHelp(Class);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WingManual::PrintHandlerPrototype(Class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Handle() override
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("\n"));
|
|
||||||
EmitCommandList(false);
|
|
||||||
// UWingServer::Print(TEXT("\n--- Half-Baked (may have issues) ---\n\n"));
|
|
||||||
// EmitCommandList(true);
|
|
||||||
UWingServer::Printf(TEXT("\n"));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingServer.h"
|
#include "WingManual.h"
|
||||||
#include "WingFetcher.h"
|
|
||||||
#include "UserManual.generated.h"
|
#include "UserManual.generated.h"
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
@@ -19,78 +18,6 @@ public:
|
|||||||
|
|
||||||
virtual void Handle() override
|
virtual void Handle() override
|
||||||
{
|
{
|
||||||
WingFetcher::PrintPathExplanation();
|
WingManual::PrintManual(WingManual::AllSections(), nullptr, false);
|
||||||
UWingServer::Print(TEXT(
|
|
||||||
"\n TYPES:"
|
|
||||||
"\n"
|
|
||||||
"\n To change variable types, or function prototypes, you will"
|
|
||||||
"\n use our syntax for types. Here are some simple examples:"
|
|
||||||
"\n"
|
|
||||||
"\n boolean, int64, double, string, etc."
|
|
||||||
"\n vector, rotator, hitresult, etc."
|
|
||||||
"\n actor, character, playercontroller, etc."
|
|
||||||
"\n eblendmode, emovementmode, etc."
|
|
||||||
"\n"
|
|
||||||
"\n Notice that it's 'actor', not 'AActor'."
|
|
||||||
"\n You can use the following notations for complex types:"
|
|
||||||
"\n"
|
|
||||||
"\n Soft<abp_manny>, Class<pawn>, SoftClass<pawn>"
|
|
||||||
"\n Array<int>, Set<string>, Map<int,string>"
|
|
||||||
"\n"
|
|
||||||
"\n FUNCTION ARGUMENTS AND RETURN VALUES:"
|
|
||||||
"\n"
|
|
||||||
"\n Function argument lists are expressed as comma-separated"
|
|
||||||
"\n lists of type-name pairs:"
|
|
||||||
"\n"
|
|
||||||
"\n double D,PlayerController P,Array<int> A"
|
|
||||||
"\n"
|
|
||||||
"\n To change the arguments or return values of a function, edit the"
|
|
||||||
"\n entry or exit node of the graph using GraphNode_SetArgs."
|
|
||||||
"\n You can view the arguments using GraphNode_Dump. If a return "
|
|
||||||
"\n node doesn't exist, you may have to create it using GraphNode_Create"
|
|
||||||
"\n before you can set return values. Custom event nodes also have"
|
|
||||||
"\n editable arguments."
|
|
||||||
"\n"
|
|
||||||
"\n IDENTIFIER SANITIZATION:\n"
|
|
||||||
"\n"
|
|
||||||
"\n Identifiers in Unreal can contain spaces and punctuation marks.\n"
|
|
||||||
"\n Those punctuation marks could confuse our parsers. For example,"
|
|
||||||
"\n How would we parse Array<X> if the typename X contained a less-than?"
|
|
||||||
"\n So, we automatically translate these characters on output:"
|
|
||||||
"\n"
|
|
||||||
"\n space -> ·"
|
|
||||||
"\n < -> ◁"
|
|
||||||
"\n > -> ▷"
|
|
||||||
"\n , -> ▾"
|
|
||||||
"\n "
|
|
||||||
"\n We do the reverse translation on input. Therefore, you will always"
|
|
||||||
"\n see sanitized versions of identifiers, and you must always use"
|
|
||||||
"\n sanitized versions of identifiers:"
|
|
||||||
"\n"
|
|
||||||
"\n Correct: /Game/Testing/BP_Test,graph:Get·Cursor·Location"
|
|
||||||
"\n Wrong: /Game/Testing/BP_Test,graph:Get Cursor Location"
|
|
||||||
"\n"
|
|
||||||
"\n ABOUT WHITESPACE:"
|
|
||||||
"\n"
|
|
||||||
"\n Do not put excess whitespace into paths, typenames, or"
|
|
||||||
"\n function prototypes, only use whitespace where it is required"
|
|
||||||
"\n by the syntax."
|
|
||||||
"\n"
|
|
||||||
"\n MATERIAL EDITING:"
|
|
||||||
"\n"
|
|
||||||
"\n We do not expose material expressions directly. Instead, you"
|
|
||||||
"\n will be editing the material graph. However, if you Graph_Dump"
|
|
||||||
"\n a material graph, you will see that the nodes contain mxprop"
|
|
||||||
"\n properties, which actually come from the material expressions."
|
|
||||||
"\n You can edit these using Property_Set on the node."
|
|
||||||
"\n"
|
|
||||||
"\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:"
|
|
||||||
"\n"
|
|
||||||
"\n UserManual: this explanation"
|
|
||||||
"\n ShowCommands: a full list of all the commands"
|
|
||||||
"\n Graph_Dump: a detailed listing of any UEdGraph"
|
|
||||||
"\n Property_Dump: show information on many objects"
|
|
||||||
"\n"
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,38 +18,8 @@
|
|||||||
#include "Blueprint/WidgetTree.h"
|
#include "Blueprint/WidgetTree.h"
|
||||||
#include "Components/Widget.h"
|
#include "Components/Widget.h"
|
||||||
#include "Subsystems/AssetEditorSubsystem.h"
|
#include "Subsystems/AssetEditorSubsystem.h"
|
||||||
|
#include "WingServer.h"
|
||||||
void WingFetcher::PrintPathExplanation()
|
#include "WingManual.h"
|
||||||
{
|
|
||||||
UWingServer::Print(TEXT(
|
|
||||||
"\n PATHS:"
|
|
||||||
"\n"
|
|
||||||
"\n Most commands require you to specify a path. A path starts"
|
|
||||||
"\n with an asset name, followed by steps separated by ,"
|
|
||||||
"\n that navigate into the asset. Some Examples:"
|
|
||||||
"\n"
|
|
||||||
"\n /Game/Widgets/WB_Hotkeys,graph:EventGraph,node:Self03,pin:Result"
|
|
||||||
"\n /Game/Testing/BP_Test,graph:Clear·Action·Grid,node:K2Node_CallFunction_0"
|
|
||||||
"\n"
|
|
||||||
"\n The navigation steps supported are:"
|
|
||||||
"\n"
|
|
||||||
"\n graph — move from a blueprint or material to a graph."
|
|
||||||
"\n node — move from a graph to a graph node"
|
|
||||||
"\n pin — move from a graph node to a pin"
|
|
||||||
"\n component — move from a blueprint to a component"
|
|
||||||
"\n levelblueprint — move from a world to a blueprint"
|
|
||||||
"\n widget — move from a widget blueprint to a widget"
|
|
||||||
"\n"
|
|
||||||
"\n Notice that paths use sanitized identifiers. See the UserManual"
|
|
||||||
"\n for more information on name sanitization."
|
|
||||||
"\n"
|
|
||||||
"\n Steps do not always require a parameter. For example, materials"
|
|
||||||
"\n only have one graph, so you can just say:"
|
|
||||||
"\n"
|
|
||||||
"\n /Game/Materials/MyMaterial,graph"
|
|
||||||
"\n"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
WingFetcher::WalkFunc WingFetcher::GetWalker(const FString& Step)
|
WingFetcher::WalkFunc WingFetcher::GetWalker(const FString& Step)
|
||||||
{
|
{
|
||||||
@@ -94,7 +64,7 @@ void WingFetcher::PathFailed(const TCHAR* Expected)
|
|||||||
UWingServer::Printf(TEXT("ERROR: Path specifies a %s, but expected %s\n"), *Obj->GetClass()->GetName(), Expected);
|
UWingServer::Printf(TEXT("ERROR: Path specifies a %s, but expected %s\n"), *Obj->GetClass()->GetName(), Expected);
|
||||||
else
|
else
|
||||||
UWingServer::Printf(TEXT("ERROR: Path led to a null pointer\n"));
|
UWingServer::Printf(TEXT("ERROR: Path led to a null pointer\n"));
|
||||||
PrintPathExplanation();
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
SetError();
|
SetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +76,7 @@ WingFetcher& WingFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expecte
|
|||||||
UWingServer::Printf(TEXT("ERROR: Input to '%s' is %s, but expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected);
|
UWingServer::Printf(TEXT("ERROR: Input to '%s' is %s, but expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected);
|
||||||
else
|
else
|
||||||
UWingServer::Printf(TEXT("ERROR: Path led to a null pointer\n"));
|
UWingServer::Printf(TEXT("ERROR: Path led to a null pointer\n"));
|
||||||
PrintPathExplanation();
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
SetError();
|
SetError();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -115,11 +85,19 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
|
|||||||
{
|
{
|
||||||
if (bError) return *this;
|
if (bError) return *this;
|
||||||
|
|
||||||
|
if (Path.Contains(TEXT(" ")))
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: Paths may not contain whitespace."));
|
||||||
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
|
UWingServer::SuggestManual(WingManual::Section::IdentifierSanitization);
|
||||||
|
return SetError();
|
||||||
|
}
|
||||||
TArray<FString> Segments;
|
TArray<FString> Segments;
|
||||||
Path.ParseIntoArray(Segments, TEXT(","));
|
Path.ParseIntoArray(Segments, TEXT(","));
|
||||||
if (Segments.Num() == 0)
|
if (Segments.Num() == 0)
|
||||||
{
|
{
|
||||||
UWingServer::Print(TEXT("ERROR: Empty path\n"));
|
UWingServer::Print(TEXT("ERROR: Empty path\n"));
|
||||||
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
return SetError();
|
return SetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +118,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
|
|||||||
if (!Func)
|
if (!Func)
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("ERROR: Unknown path step '%s'\n"), *Key);
|
UWingServer::Printf(TEXT("ERROR: Unknown path step '%s'\n"), *Key);
|
||||||
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
return SetError();
|
return SetError();
|
||||||
}
|
}
|
||||||
(this->*Func)(Value);
|
(this->*Func)(Value);
|
||||||
@@ -155,8 +134,8 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
|
|||||||
|
|
||||||
if (!PackagePath.StartsWith(TEXT("/")))
|
if (!PackagePath.StartsWith(TEXT("/")))
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("ERROR: Asset path must start with '/', got '%s'\n"), *PackagePath);
|
UWingServer::Printf(TEXT("ERROR: Path must start with '/', got '%s'\n"), *PackagePath);
|
||||||
PrintPathExplanation();
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
return SetError();
|
return SetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +203,7 @@ WingFetcher& WingFetcher::Graph(const FString& Value)
|
|||||||
if (!Value.IsEmpty())
|
if (!Value.IsEmpty())
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("ERROR: Materials have only one graph, with a blank name.\n\n"));
|
UWingServer::Printf(TEXT("ERROR: Materials have only one graph, with a blank name.\n\n"));
|
||||||
PrintPathExplanation();
|
UWingServer::SuggestManual(WingManual::Section::Paths);
|
||||||
return SetError();
|
return SetError();
|
||||||
}
|
}
|
||||||
WingUtils::EnsureMaterialGraph(Mat);
|
WingUtils::EnsureMaterialGraph(Mat);
|
||||||
|
|||||||
@@ -341,7 +341,6 @@ void WingGraphExport::EmitComments()
|
|||||||
|
|
||||||
// Emit wrapped, indented body.
|
// Emit wrapped, indented body.
|
||||||
Output.Append(WingUtils::WrapText(CommentNode->NodeComment, 70, TEXT(" - ")));
|
Output.Append(WingUtils::WrapText(CommentNode->NodeComment, 70, TEXT(" - ")));
|
||||||
Output.Append(TEXT("\n"));
|
|
||||||
|
|
||||||
// Find contained nodes.
|
// Find contained nodes.
|
||||||
TArray<FString> ContainedNames;
|
TArray<FString> ContainedNames;
|
||||||
|
|||||||
305
Plugins/UEWingman/Source/UEWingman/Private/WingManual.cpp
Normal file
305
Plugins/UEWingman/Source/UEWingman/Private/WingManual.cpp
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
#include "WingManual.h"
|
||||||
|
#include "WingServer.h"
|
||||||
|
#include "WingHandler.h"
|
||||||
|
#include "WingTypes.h"
|
||||||
|
|
||||||
|
TSet<WingManual::Section> WingManual::AllSections()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
Section::Paths,
|
||||||
|
Section::Types,
|
||||||
|
Section::FunctionArguments,
|
||||||
|
Section::IdentifierSanitization,
|
||||||
|
Section::Whitespace,
|
||||||
|
Section::MaterialEditing,
|
||||||
|
Section::ImportantCommands,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void WingManual::PrintHandlerPrototype(UClass *HandlerClass)
|
||||||
|
{
|
||||||
|
UWingServer::Print(WingUtils::GetHandlerName(HandlerClass));
|
||||||
|
UWingServer::Print(TEXT("("));
|
||||||
|
bool bFirst = true;
|
||||||
|
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
|
||||||
|
{
|
||||||
|
if (!bFirst) UWingServer::Print(TEXT(","));
|
||||||
|
bFirst = false;
|
||||||
|
if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?"));
|
||||||
|
UWingServer::Print(PropIt->GetName());
|
||||||
|
}
|
||||||
|
UWingServer::Print(TEXT(")\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WingManual::PrintHandlerArguments(UClass *HandlerClass)
|
||||||
|
{
|
||||||
|
// parameter details
|
||||||
|
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
|
||||||
|
{
|
||||||
|
FProperty* Prop = *PropIt;
|
||||||
|
FString Name = Prop->GetName();
|
||||||
|
FString Type = UWingTypes::TypeToText(Prop);
|
||||||
|
bool bOptional = Prop->HasMetaData(TEXT("Optional"));
|
||||||
|
const FString& Desc = Prop->GetMetaData(TEXT("Description"));
|
||||||
|
|
||||||
|
if (bOptional)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT(" %s (optional %s)"), *Name, *Type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT(" %s (%s)"), *Name, *Type);
|
||||||
|
}
|
||||||
|
if (!Desc.IsEmpty()) UWingServer::Printf(TEXT(" — %s"), *Desc);
|
||||||
|
UWingServer::Print(TEXT("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WingManual::PrintHandlerDescription(UClass *HandlerClass)
|
||||||
|
{
|
||||||
|
const IWingHandler* Handler = Cast<IWingHandler>(HandlerClass->GetDefaultObject());
|
||||||
|
if (!Handler) return;
|
||||||
|
UWingServer::Print(WingUtils::WrapText(Handler->GetDescription(), 80, TEXT(" // ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WingManual::PrintHandlerHelp(UClass* HandlerClass)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT("\n"));
|
||||||
|
PrintHandlerPrototype(HandlerClass);
|
||||||
|
PrintHandlerArguments(HandlerClass);
|
||||||
|
PrintHandlerDescription(HandlerClass);
|
||||||
|
UWingServer::Print(TEXT("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WingManual::PrintManual(TSet<Section> Sections, UClass *Handler, bool Abridged)
|
||||||
|
{
|
||||||
|
if (Handler == nullptr)
|
||||||
|
{
|
||||||
|
Sections.Remove(Section::HandlerHelp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.IsEmpty()) return;
|
||||||
|
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("\n--- AUTOMATIC DOCUMENTATION ---\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::HandlerHelp))
|
||||||
|
{
|
||||||
|
PrintHandlerHelp(Handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::Paths))
|
||||||
|
{
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n PATHS: Here are some example paths:"
|
||||||
|
"\n /Game/Widgets/WB_Hotkeys,widget:Canvas·122"
|
||||||
|
"\n /Game/Testing/BP_Test,graph:Rescale·Actor,node:K2Node_CallFunction_0,pin:Scale"
|
||||||
|
"\n /Game/Chars/BP_Manny,component:Camera·Boom"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n PATHS:"
|
||||||
|
"\n"
|
||||||
|
"\n Most commands require you to specify a path. A path starts"
|
||||||
|
"\n with an asset name, followed by steps separated by ,"
|
||||||
|
"\n that navigate into the asset. Some Examples:"
|
||||||
|
"\n"
|
||||||
|
"\n /Game/Widgets/WB_Hotkeys,widget:Canvas·122"
|
||||||
|
"\n /Game/Testing/BP_Test,graph:Rescale·Actor,node:K2Node_CallFunction_0,pin:Scale"
|
||||||
|
"\n /Game/Chars/BP_Manny,component:Camera·Boom"
|
||||||
|
"\n"
|
||||||
|
"\n The navigation steps supported are:"
|
||||||
|
"\n"
|
||||||
|
"\n graph — move from a blueprint or material to a graph."
|
||||||
|
"\n node — move from a graph to a graph node"
|
||||||
|
"\n pin — move from a graph node to a pin"
|
||||||
|
"\n component — move from a blueprint to a component"
|
||||||
|
"\n levelblueprint — move from a world to a blueprint"
|
||||||
|
"\n widget — move from a widget blueprint to a widget"
|
||||||
|
"\n"
|
||||||
|
"\n Notice that paths use sanitized identifiers. See the section"
|
||||||
|
"\n on identifier sanitization below for more information."
|
||||||
|
"\n"
|
||||||
|
"\n Steps do not always require a parameter. For example, materials"
|
||||||
|
"\n only have one graph, so you can just say:"
|
||||||
|
"\n"
|
||||||
|
"\n /Game/Materials/MyMaterial,graph"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::Types))
|
||||||
|
{
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n TYPES: Here are some examples of valid types:"
|
||||||
|
"\n Bool, String, Vector, Rotator, HitResult, Actor, Character,"
|
||||||
|
"\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn,"
|
||||||
|
"\n Array<Int>, Set<String>, Map<Int,Actor>"
|
||||||
|
"\n Soft<ABP_Manny>, Class<Pawn>, SoftClass<Pawn>"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n TYPES:"
|
||||||
|
"\n"
|
||||||
|
"\n To change variable types, or to express function prototypes, you will"
|
||||||
|
"\n use our syntax for types. Here are some valid examples:"
|
||||||
|
"\n"
|
||||||
|
"\n Bool, String, Vector, Rotator, HitResult, Actor, Character,"
|
||||||
|
"\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn,"
|
||||||
|
"\n Array<Int>, Set<String>, Map<Int,Actor>"
|
||||||
|
"\n Soft<ABP_Manny>, Class<Pawn>, SoftClass<Pawn>"
|
||||||
|
"\n"
|
||||||
|
"\n Notice that it's 'Actor', not 'AActor'. Type names are not"
|
||||||
|
"\n case-sensitive. When a blueprint like /Game/Testing/BP_Foo"
|
||||||
|
"\n is used as a type, the typename is BP_Foo."
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::FunctionArguments))
|
||||||
|
{
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n FUNCTION ARGUMENTS: Here is an example argument list:"
|
||||||
|
"\n double D,PlayerController P,Array<Int> A"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n FUNCTION ARGUMENTS AND RETURN VALUES:"
|
||||||
|
"\n"
|
||||||
|
"\n Function argument lists are expressed as comma-separated"
|
||||||
|
"\n lists of type-name pairs:"
|
||||||
|
"\n"
|
||||||
|
"\n Double D,PlayerController P,Array<Int> A"
|
||||||
|
"\n"
|
||||||
|
"\n To change the arguments or return values of a function, edit the"
|
||||||
|
"\n entry or exit node of the graph using GraphNode_SetArgs."
|
||||||
|
"\n You can view the arguments using GraphNode_Dump. If a return "
|
||||||
|
"\n node doesn't exist, you may have to create it using GraphNode_Create"
|
||||||
|
"\n before you can set return values. Custom event nodes also have"
|
||||||
|
"\n editable arguments."
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::IdentifierSanitization))
|
||||||
|
{
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n IDENTIFIER SANITIZATION:\n"
|
||||||
|
"\n Identifiers in unreal can contain whitespace and punctuation.\n"
|
||||||
|
"\n We sanitize these characters on output:\n"
|
||||||
|
"\n"
|
||||||
|
"\n space → ·"
|
||||||
|
"\n < → ◁"
|
||||||
|
"\n > → ▷"
|
||||||
|
"\n , → ▾"
|
||||||
|
"\n "
|
||||||
|
"\n We do the reverse translation on input. Therefore, you will always"
|
||||||
|
"\n see sanitized versions of identifiers, and you must always use"
|
||||||
|
"\n sanitized versions of identifiers:"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n IDENTIFIER SANITIZATION:\n"
|
||||||
|
"\n"
|
||||||
|
"\n Identifiers in Unreal can contain spaces and punctuation marks.\n"
|
||||||
|
"\n Those punctuation marks could confuse our parsers. For example,"
|
||||||
|
"\n How would we parse Array<X> if the typename X contained a less-than?"
|
||||||
|
"\n So, we automatically translate these characters on output:"
|
||||||
|
"\n"
|
||||||
|
"\n space → ·"
|
||||||
|
"\n < → ◁"
|
||||||
|
"\n > → ▷"
|
||||||
|
"\n , → ▾"
|
||||||
|
"\n "
|
||||||
|
"\n We do the reverse translation on input. Therefore, you will always"
|
||||||
|
"\n see sanitized versions of identifiers, and you must always use"
|
||||||
|
"\n sanitized versions of identifiers:"
|
||||||
|
"\n"
|
||||||
|
"\n Correct: /Game/Testing/BP_Test,graph:Get·Cursor·Location"
|
||||||
|
"\n Wrong: /Game/Testing/BP_Test,graph:Get Cursor Location"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::Whitespace))
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n ABOUT WHITESPACE:"
|
||||||
|
"\n Do not put excess whitespace into paths, typenames, or"
|
||||||
|
"\n function prototypes, only use whitespace where it is required"
|
||||||
|
"\n by the syntax."
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::MaterialEditing))
|
||||||
|
{
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n MATERIAL EDITING:"
|
||||||
|
"\n We do not expose material expressions directly. Instead, use"
|
||||||
|
"\n Property_Dump and Property_Set on the material graph nodes to"
|
||||||
|
"\n edit material expression properties."
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n MATERIAL EDITING:"
|
||||||
|
"\n"
|
||||||
|
"\n We do not expose material expressions directly. Instead, you"
|
||||||
|
"\n will be editing the material graph. However, if you Graph_Dump"
|
||||||
|
"\n a material graph, you will see that the nodes contain mxprop"
|
||||||
|
"\n properties, which actually come from the material expressions."
|
||||||
|
"\n You can edit these using Property_Set on the node."
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sections.Contains(Section::ImportantCommands))
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT(
|
||||||
|
"\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:"
|
||||||
|
"\n UserManual: this explanation"
|
||||||
|
"\n ShowCommands: a full list of all the commands"
|
||||||
|
"\n Blueprint_Dump: a summary of any blueprint"
|
||||||
|
"\n Graph_Dump: a fairly detailed listing of any Graph"
|
||||||
|
"\n Property_Dump: show information on many objects"
|
||||||
|
"\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Abridged)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("\nUse command 'UserManual' to see the full manual.\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
|
#include "WingManual.h"
|
||||||
#include "WingJson.h"
|
#include "WingJson.h"
|
||||||
#include "WingLogCapture.h"
|
#include "WingLogCapture.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
@@ -263,6 +264,8 @@ FString UWingServer::HandleRequest(const FString& Line)
|
|||||||
LogCapture.CapturedErrors.Empty();
|
LogCapture.CapturedErrors.Empty();
|
||||||
LogCapture.bEnabled = true;
|
LogCapture.bEnabled = true;
|
||||||
HandlerOutput.Reset();
|
HandlerOutput.Reset();
|
||||||
|
SuggestedManualSections.Empty();
|
||||||
|
LastHandlerClass = nullptr;
|
||||||
|
|
||||||
TryCallHandler(Line);
|
TryCallHandler(Line);
|
||||||
|
|
||||||
@@ -273,6 +276,11 @@ FString UWingServer::HandleRequest(const FString& Line)
|
|||||||
UWingServer::Printf(TEXT("UE_LOG: %s\n"), *Msg);
|
UWingServer::Printf(TEXT("UE_LOG: %s\n"), *Msg);
|
||||||
}
|
}
|
||||||
LogCapture.CapturedErrors.Empty();
|
LogCapture.CapturedErrors.Empty();
|
||||||
|
if (!SuggestedManualSections.IsEmpty())
|
||||||
|
{
|
||||||
|
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
|
||||||
|
WingManual::PrintManual(SuggestedManualSections, LastHandlerClass, true);
|
||||||
|
}
|
||||||
FString Result = HandlerOutput.ToString();
|
FString Result = HandlerOutput.ToString();
|
||||||
HandlerOutput.Reset();
|
HandlerOutput.Reset();
|
||||||
for (int32 i = 0; i < Result.Len(); ++i)
|
for (int32 i = 0; i < Result.Len(); ++i)
|
||||||
@@ -299,6 +307,7 @@ void UWingServer::TryCallHandler(const FString &Line)
|
|||||||
if (!Request->TryGetStringField(TEXT("command"), Command))
|
if (!Request->TryGetStringField(TEXT("command"), Command))
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("Request does not contain 'command' parameter"));
|
UWingServer::Printf(TEXT("Request does not contain 'command' parameter"));
|
||||||
|
UWingServer::Printf(TEXT("We recommend sending command='UserManual'."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Request->RemoveField(TEXT("command"));
|
Request->RemoveField(TEXT("command"));
|
||||||
@@ -308,8 +317,10 @@ void UWingServer::TryCallHandler(const FString &Line)
|
|||||||
if (!HandlerClass)
|
if (!HandlerClass)
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("Unknown command: %s"), *Command);
|
UWingServer::Printf(TEXT("Unknown command: %s"), *Command);
|
||||||
|
UWingServer::SuggestManual(WingManual::Section::ImportantCommands);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LastHandlerClass = *HandlerClass;
|
||||||
|
|
||||||
// Make an object of the handler class.
|
// Make an object of the handler class.
|
||||||
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
|
TStrongObjectPtr<UObject> HandlerObj(NewObject<UObject>(GetTransientPackage(), *HandlerClass));
|
||||||
@@ -318,8 +329,7 @@ void UWingServer::TryCallHandler(const FString &Line)
|
|||||||
// Populate the handler object with the request parameters.
|
// Populate the handler object with the request parameters.
|
||||||
if (!WingJson::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
|
if (!WingJson::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("\nUsage:\n\n"));
|
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
|
||||||
WingUtils::PrintHandlerHelp(*HandlerClass);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,16 +302,14 @@ FString WingUtils::FormatNodeTitle(const UEdGraphNode *Node)
|
|||||||
|
|
||||||
FString WingUtils::WrapText(const FString& Text, int32 ColLimit, const FString& Prefix)
|
FString WingUtils::WrapText(const FString& Text, int32 ColLimit, const FString& Prefix)
|
||||||
{
|
{
|
||||||
FString Clean = Text;
|
|
||||||
Clean.ReplaceInline(TEXT("\r\n"), TEXT("\n"));
|
|
||||||
TArray<FString> Words;
|
TArray<FString> Words;
|
||||||
Clean.ParseIntoArrayWS(Words);
|
Text.ParseIntoArrayWS(Words);
|
||||||
|
|
||||||
TStringBuilder<1024> Result;
|
TStringBuilder<1024> Result;
|
||||||
int32 Col = 0;
|
int32 Col = 0;
|
||||||
for (const FString& Word : Words)
|
for (const FString& Word : Words)
|
||||||
{
|
{
|
||||||
if (Col > 0 && Col + 1 + Word.Len() > ColLimit)
|
if ((Col > 0) && (Col + 1 + Word.Len() > ColLimit))
|
||||||
{
|
{
|
||||||
Result.Append(TEXT("\n"));
|
Result.Append(TEXT("\n"));
|
||||||
Col = 0;
|
Col = 0;
|
||||||
@@ -319,16 +317,17 @@ FString WingUtils::WrapText(const FString& Text, int32 ColLimit, const FString&
|
|||||||
if (Col == 0)
|
if (Col == 0)
|
||||||
{
|
{
|
||||||
Result.Append(Prefix);
|
Result.Append(Prefix);
|
||||||
Col = Prefix.Len();
|
Result.Append(Word);
|
||||||
|
Col = Prefix.Len() + Word.Len();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Result.Append(TEXT(" "));
|
Result.Append(TEXT(" "));
|
||||||
Col += 1;
|
Result.Append(Word);
|
||||||
|
Col = 1 + Word.Len();
|
||||||
}
|
}
|
||||||
Result.Append(Word);
|
|
||||||
Col += Word.Len();
|
|
||||||
}
|
}
|
||||||
|
if (Col > 0) Result.Append(TEXT("\n"));
|
||||||
return Result.ToString();
|
return Result.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,48 +615,3 @@ FString WingUtils::GetHandlerGroup(UClass* HandlerClass)
|
|||||||
return Name.Left(UnderscoreIdx);
|
return Name.Left(UnderscoreIdx);
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// PrintHandlerHelp — verbose description of one handler command
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
void WingUtils::PrintHandlerHelp(UClass* HandlerClass)
|
|
||||||
{
|
|
||||||
const IWingHandler* Handler = Cast<IWingHandler>(HandlerClass->GetDefaultObject());
|
|
||||||
if (!Handler) return;
|
|
||||||
|
|
||||||
FString ToolName = GetHandlerName(HandlerClass);
|
|
||||||
|
|
||||||
UWingServer::Print(TEXT("\n"));
|
|
||||||
UWingServer::Print(WrapText(Handler->GetDescription(), 80, TEXT("// ")));
|
|
||||||
UWingServer::Print(TEXT("\n"));
|
|
||||||
|
|
||||||
// Command signature line
|
|
||||||
UWingServer::Print(ToolName);
|
|
||||||
UWingServer::Print(TEXT("("));
|
|
||||||
bool bFirst = true;
|
|
||||||
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
|
|
||||||
{
|
|
||||||
if (!bFirst) UWingServer::Print(TEXT(","));
|
|
||||||
bFirst = false;
|
|
||||||
if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?"));
|
|
||||||
UWingServer::Print(PropIt->GetName());
|
|
||||||
}
|
|
||||||
UWingServer::Print(TEXT(")\n"));
|
|
||||||
|
|
||||||
// parameter details
|
|
||||||
for (TFieldIterator<FProperty> PropIt(HandlerClass, EFieldIterationFlags::None); PropIt; ++PropIt)
|
|
||||||
{
|
|
||||||
FProperty* Prop = *PropIt;
|
|
||||||
FString Name = Prop->GetName();
|
|
||||||
FString Type = UWingTypes::TypeToText(Prop);
|
|
||||||
bool bOptional = Prop->HasMetaData(TEXT("Optional"));
|
|
||||||
const FString& Desc = Prop->GetMetaData(TEXT("Description"));
|
|
||||||
|
|
||||||
UWingServer::Printf(TEXT(" %s %s%s"),
|
|
||||||
*Type, *Name, bOptional ? TEXT(" (optional)") : TEXT(""));
|
|
||||||
if (!Desc.IsEmpty())
|
|
||||||
UWingServer::Printf(TEXT(" — %s"), *Desc);
|
|
||||||
UWingServer::Print(TEXT("\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -36,10 +36,7 @@ struct FWalker;
|
|||||||
|
|
||||||
class WingFetcher
|
class WingFetcher
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Print a general explanation of what paths look like.
|
|
||||||
static void PrintPathExplanation();
|
|
||||||
|
|
||||||
// Walk a path from an asset to an object
|
// Walk a path from an asset to an object
|
||||||
// within that asset. If you call walk a
|
// within that asset. If you call walk a
|
||||||
// second time, it will walk additional steps.
|
// second time, it will walk additional steps.
|
||||||
|
|||||||
25
Plugins/UEWingman/Source/UEWingman/Public/WingManual.h
Normal file
25
Plugins/UEWingman/Source/UEWingman/Public/WingManual.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Containers/Set.h"
|
||||||
|
|
||||||
|
class WingManual
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Section
|
||||||
|
{
|
||||||
|
HandlerHelp,
|
||||||
|
Paths,
|
||||||
|
Types,
|
||||||
|
FunctionArguments,
|
||||||
|
IdentifierSanitization,
|
||||||
|
Whitespace,
|
||||||
|
MaterialEditing,
|
||||||
|
ImportantCommands,
|
||||||
|
};
|
||||||
|
|
||||||
|
static TSet<Section> AllSections();
|
||||||
|
static void PrintHandlerPrototype(UClass *Handler);
|
||||||
|
static void PrintHandlerArguments(UClass *Handler);
|
||||||
|
static void PrintHandlerDescription(UClass *Handler);
|
||||||
|
static void PrintHandlerHelp(UClass *Handler);
|
||||||
|
static void PrintManual(TSet<Section> Sections, UClass *Handler, bool Abridged);
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "WingNotifier.h"
|
#include "WingNotifier.h"
|
||||||
#include "WingLogCapture.h"
|
#include "WingLogCapture.h"
|
||||||
|
#include "WingManual.h"
|
||||||
#include "WingServer.generated.h"
|
#include "WingServer.generated.h"
|
||||||
|
|
||||||
class FSocket;
|
class FSocket;
|
||||||
@@ -59,6 +60,9 @@ public:
|
|||||||
GWingServer->HandlerOutput.Appendf(Fmt, Forward<ArgTypes>(Args)...);
|
GWingServer->HandlerOutput.Appendf(Fmt, Forward<ArgTypes>(Args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Suggest that a manual section be printed after the handler finishes. */
|
||||||
|
static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); }
|
||||||
|
|
||||||
/** Whether the server is currently listening. */
|
/** Whether the server is currently listening. */
|
||||||
bool IsRunning() const { return bRunning; }
|
bool IsRunning() const { return bRunning; }
|
||||||
|
|
||||||
@@ -71,7 +75,9 @@ private:
|
|||||||
// ----- Tool dispatch -----
|
// ----- Tool dispatch -----
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FWingNotifier Notifier;
|
FWingNotifier Notifier;
|
||||||
|
UClass* LastHandlerClass;
|
||||||
TStringBuilder<16384> HandlerOutput;
|
TStringBuilder<16384> HandlerOutput;
|
||||||
|
TSet<WingManual::Section> SuggestedManualSections;
|
||||||
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
|
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TMap<FString, TObjectPtr<UClass>> WingHandlerRegistry; // tool name -> UWingHandler subclass
|
TMap<FString, TObjectPtr<UClass>> WingHandlerRegistry; // tool name -> UWingHandler subclass
|
||||||
|
|||||||
@@ -230,7 +230,6 @@ public:
|
|||||||
static TArray<UClass*> CollectHandlerClasses();
|
static TArray<UClass*> CollectHandlerClasses();
|
||||||
static FString GetHandlerName(UClass* HandlerClass);
|
static FString GetHandlerName(UClass* HandlerClass);
|
||||||
static FString GetHandlerGroup(UClass* HandlerClass);
|
static FString GetHandlerGroup(UClass* HandlerClass);
|
||||||
static void PrintHandlerHelp(UClass* HandlerClass);
|
|
||||||
|
|
||||||
// ----- Reparent validation -----
|
// ----- Reparent validation -----
|
||||||
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
|
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
|
||||||
|
|||||||
Reference in New Issue
Block a user