Lots of work on WingTypes

This commit is contained in:
2026-03-25 02:32:26 -04:00
parent 03e61cd9a0
commit 6325b15cb7
10 changed files with 629 additions and 321 deletions

View File

@@ -63,7 +63,12 @@ public:
if (InternalName.IsEmpty()) return; if (InternalName.IsEmpty()) return;
// Resolve the component class by name // Resolve the component class by name
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class); UWingTypes::Requirements Req;
Req.BlueprintType = false;
Req.Blueprintable = false;
Req.AllowContainer = false;
Req.IsChildOf = UActorComponent::StaticClass();
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class, Req);
if (!ComponentClass) return; if (!ComponentClass) return;
// Find the specified parent component // Find the specified parent component

View File

@@ -51,7 +51,11 @@ public:
if (!Type.IsEmpty()) if (!Type.IsEmpty())
{ {
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type); UWingTypes::Requirements Req;
Req.BlueprintType = false;
Req.Blueprintable = false;
Req.AllowContainer = false;
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type, Req);
if (!TypeClass) return; if (!TypeClass) return;
Filter.ClassPaths.Add(TypeClass->GetClassPathName()); Filter.ClassPaths.Add(TypeClass->GetClassPathName());
} }

View File

@@ -39,7 +39,11 @@ public:
if (!BP) return; if (!BP) return;
// Resolve the interface class // Resolve the interface class
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface); UWingTypes::Requirements Req;
Req.BlueprintType = false;
Req.Blueprintable = false;
Req.AllowContainer = false;
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface, Req);
if (!InterfaceClass) return; if (!InterfaceClass) return;
// Check for duplicates // Check for duplicates

View File

@@ -43,15 +43,19 @@ public:
if (!Maker.Ok()) return; if (!Maker.Ok()) return;
// Resolve parent class based on blueprint type // Resolve parent class based on blueprint type
UWingTypes::Requirements Req;
Req.BlueprintType = false;
Req.Blueprintable = true;
Req.AllowContainer = false;
UClass* ParentClassObj = nullptr; UClass* ParentClassObj = nullptr;
switch (BlueprintType) switch (BlueprintType)
{ {
case BPTYPE_Normal: case BPTYPE_Normal:
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass); ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req);
if (!ParentClassObj) return; if (!ParentClassObj) return;
break; break;
case BPTYPE_MacroLibrary: case BPTYPE_MacroLibrary:
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass); ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req);
if (!ParentClassObj) return; if (!ParentClassObj) return;
break; break;
case BPTYPE_Interface: case BPTYPE_Interface:

View File

@@ -41,7 +41,11 @@ public:
if (!BP) return; if (!BP) return;
// Find the new parent class by short type name // Find the new parent class by short type name
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent); UWingTypes::Requirements Req;
Req.BlueprintType = false;
Req.Blueprintable = true;
Req.AllowContainer = false;
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent, Req);
if (!NewParentClassObj) return; if (!NewParentClassObj) return;
// Validate reparent // Validate reparent

View File

@@ -4,13 +4,6 @@
#include "WingServer.h" #include "WingServer.h"
#include "WingHandler.h" #include "WingHandler.h"
#include "WingTypes.h" #include "WingTypes.h"
#include "WingUtils.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "UObject/UObjectIterator.h"
#include "StructUtils/UserDefinedStruct.h"
#include "Engine/UserDefinedEnum.h"
#include "Engine/Blueprint.h"
#include "TypeName_Search.generated.h" #include "TypeName_Search.generated.h"
@@ -30,73 +23,46 @@ public:
UPROPERTY(meta=(Optional, Description="Maximum number of results")) UPROPERTY(meta=(Optional, Description="Maximum number of results"))
int32 Limit = 100; int32 Limit = 100;
UPROPERTY(meta=(Optional, Description="If true, include all types, not just BlueprintType/Blueprintable ones"))
bool Exhaustive = false;
virtual FString GetDescription() const override virtual FString GetDescription() const override
{ {
return TEXT("Search for type names usable in pin type specifications. " return TEXT("Search for type names usable in pin type specifications. "
"Returns short names that can be used with commands like Blueprint_ChangeVariableType."); "Returns short names that can be used with commands like Blueprint_ChangeVariableType.");
} }
void TryMatchObject(TSet<UObject*> &Matches, UObject *Obj)
{
if (!Obj) return;
FString Name = Obj->GetName();
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) return;
Matches.Add(Obj);
}
void TryMatchObjects(TSet<UObject*> &Matches, UClass *Class)
{
ForEachObjectOfClass(Class, [&](UObject *Obj){
if (Matches.Num() == Limit) return;
TryMatchObject(Matches, Obj);
}, true);
}
void TryMatchAssets(TSet<UObject*> &Matches, UClass *Class)
{
IAssetRegistry& Registry = *IAssetRegistry::Get();
TArray<FAssetData> AssetResults;
Registry.GetAssetsByClass(Class->GetClassPathName(), AssetResults, true);
for (const FAssetData& Data : AssetResults)
{
if (Matches.Num() == Limit) return;
FString Name = Data.AssetName.ToString();
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) continue;
UObject *Obj = Data.GetAsset();
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
Obj = BP->GeneratedClass;
TryMatchObject(Matches, Obj);
}
}
virtual void Handle() override virtual void Handle() override
{ {
TSet<UObject*> Matches; const TMap<FString, UWingTypes::Info>& AllTypes = UWingTypes::GetAllTypes();
TryMatchObjects(Matches, UScriptStruct::StaticClass());
TryMatchObjects(Matches, UClass::StaticClass());
TryMatchObjects(Matches, UEnum::StaticClass());
TryMatchAssets(Matches, UBlueprint::StaticClass());
TryMatchAssets(Matches, UUserDefinedStruct::StaticClass());
TryMatchAssets(Matches, UUserDefinedEnum::StaticClass());
TArray<FString> Results; TArray<FString> Results;
for (const UObject *Obj : Matches) for (const auto& Pair : AllTypes)
{ {
const TCHAR *Kind = TEXT("Unknown"); if (Results.Num() >= Limit) break;
if (Cast<UEnum>(Obj))
Kind = TEXT("Enum"); const UWingTypes::Info& Info = Pair.Value;
else if (Cast<UScriptStruct>(Obj)) if (!Info.Short.Contains(Query, ESearchCase::IgnoreCase)) continue;
Kind = TEXT("Struct"); if (!Exhaustive && !UWingTypes::IsBlueprintType(Info) && !UWingTypes::IsBlueprintable(Info)) continue;
else if (const UClass* Class = Cast<UClass>(Obj))
Kind = Class->IsChildOf(UInterface::StaticClass()) ? TEXT("Interface") : TEXT("Class"); FString Line = FString::Printf(TEXT("%s %s %s"),
Results.Add(FString::Printf(TEXT("%s %s\n"), Kind, *UWingTypes::TypeToText(Obj))); *Info.PinCategory.ToString(), *Info.Short, *Info.PinSubCategoryObject);
if (Info.NativeParent)
Line += FString::Printf(TEXT(" Nat=%s"), *Info.NativeParent->GetName());
if (!Info.PinSubCategory.IsNone())
Line += FString::Printf(TEXT(" PSC=%s"), *Info.PinSubCategory.ToString());
if (Info.IsUserDefined)
Line += TEXT(" (UserDefined)");
Line += TEXT("\n");
Results.Add(MoveTemp(Line));
} }
Results.Sort(); Results.Sort();
for (const auto &Result : Results) for (const auto& Result : Results)
{ {
UWingServer::Print(Result); UWingServer::Print(Result);
} }
if (Results.Num() == Limit) if (Results.Num() >= Limit)
{ {
UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n")); UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n"));
} }

View File

@@ -69,7 +69,11 @@ bool WingFunctionArgs::ParseArgs(const FString& Args, TArray<FParsedArg>& OutArg
} }
FParsedArg Arg; FParsedArg Arg;
if (!UWingTypes::TextToType(TypeStr, Arg.PinType)) return false; UWingTypes::Requirements Req;
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(TypeStr, Arg.PinType, Req)) return false;
Arg.PinName = FName(*NameStr); Arg.PinName = FName(*NameStr);
OutArgs.Add(MoveTemp(Arg)); OutArgs.Add(MoveTemp(Arg));
} }

View File

@@ -62,7 +62,11 @@ bool FWingProperty::SetText(const FString &Value)
if (IsPinTypeProperty(Prop)) if (IsPinTypeProperty(Prop))
{ {
FEdGraphPinType PinType; FEdGraphPinType PinType;
if (!UWingTypes::TextToType(Value, PinType)) return false; UWingTypes::Requirements Req;
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(Value, PinType, Req)) return false;
Prop->SetValue_InContainer(Container, &PinType); Prop->SetValue_InContainer(Container, &PinType);
return true; return true;
} }

View File

@@ -8,46 +8,182 @@
#include "Engine/UserDefinedEnum.h" #include "Engine/UserDefinedEnum.h"
#include "UObject/UObjectIterator.h" #include "UObject/UObjectIterator.h"
#include "AssetRegistry/AssetRegistryModule.h" #include "AssetRegistry/AssetRegistryModule.h"
#include "Blueprint/BlueprintSupport.h"
#include "Kismet2/KismetEditorUtilities.h"
// ---------------------------------------------------------------------------
// Simple Accessors
// ---------------------------------------------------------------------------
const TMap<FString, UWingTypes::Info>& UWingTypes::GetAllTypes()
{
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
check(Types);
return Types->ShortToInfo;
}
// ---------------------------------------------------------------------------
// Simple Utility Functions.
// ---------------------------------------------------------------------------
bool UWingTypes::IsBlueprintType(const UObject *Obj)
{
if (const UEnum* Enum = Cast<UEnum>(Obj))
return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Enum);
if (const UScriptStruct* Struct = Cast<UScriptStruct>(Obj))
return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct);
if (const UClass* Class = Cast<UClass>(Obj))
return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Class);
return false;
}
bool UWingTypes::IsBlueprintType(const FEdGraphPinType &Type)
{
if ((Type.PinCategory == UEdGraphSchema_K2::PC_Delegate) ||
(Type.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) ||
(Type.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_Delegate) ||
(Type.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_MCDelegate))
return false;
if ((Type.PinSubCategoryObject != nullptr) &&
!IsBlueprintType(Type.PinSubCategoryObject.Get()))
return false;
if ((Type.PinValueType.TerminalSubCategoryObject != nullptr) &&
!IsBlueprintType(Type.PinValueType.TerminalSubCategoryObject.Get()))
return false;
return true;
}
bool UWingTypes::IsChildOf(const FEdGraphPinType &Type, UClass *Parent)
{
if (Type.IsContainer()) return false;
if ((Type.PinCategory != UEdGraphSchema_K2::PC_Object) &&
(Type.PinCategory != UEdGraphSchema_K2::PC_Interface))
return false;
UClass *Class = Cast<UClass>(Type.PinSubCategoryObject.Get());
if (!Class) return false;
return Class->IsChildOf(Parent);
}
bool UWingTypes::IsBlueprintable(const FEdGraphPinType &Type)
{
if (Type.IsContainer()) return false;
if ((Type.PinCategory != UEdGraphSchema_K2::PC_Object) &&
(Type.PinCategory != UEdGraphSchema_K2::PC_Interface))
return false;
UClass *Class = Cast<UClass>(Type.PinSubCategoryObject.Get());
if (!Class) return false;
return FKismetEditorUtilities::CanCreateBlueprintOfClass(Class);
}
bool UWingTypes::IsBlueprintType(const Info &TypeInfo)
{
// Blocked types (delegates).
if (TypeInfo.PinCategory.IsNone()) return false;
// User-defined types are always valid blueprint variable types.
if (TypeInfo.IsUserDefined) return true;
// Primitives have no PinSubCategoryObject.
if (TypeInfo.PinSubCategoryObject.IsEmpty()) return true;
// Load and check.
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo.PinSubCategoryObject);
if (!Obj) return false;
return IsBlueprintType(Obj);
}
bool UWingTypes::IsBlueprintable(const Info &TypeInfo)
{
// Only classes can be blueprintable.
if (TypeInfo.PinCategory != UEdGraphSchema_K2::PC_Object &&
TypeInfo.PinCategory != UEdGraphSchema_K2::PC_Interface)
return false;
// User-defined classes are already blueprints, so yes.
if (TypeInfo.IsUserDefined) return true;
// Load and check.
UClass* Class = LoadObject<UClass>(nullptr, *TypeInfo.PinSubCategoryObject);
if (!Class) return false;
return FKismetEditorUtilities::CanCreateBlueprintOfClass(Class);
}
const UClass *UWingTypes::FindNativeParent(const UClass *Obj)
{
const UClass* Native = Obj;
while (Native && Native->ClassGeneratedBy != nullptr)
Native = Native->GetSuperClass();
return Native;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Choose Short Name // Choose Short Name
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
FString UWingTypes::GetProposedName(const UObject *Obj) void UWingTypes::ReserveShortName(FName Name, FName PinCategory, FName PinSubCategory)
{ {
FString Name = Obj->GetName(); FString NameStr = Name.ToString();
if (Name.EndsWith(TEXT("_C"))) Info Dummy;
{ Dummy.Short = NameStr;
if (const UClass* Class = Cast<UClass>(Obj)) Dummy.PinCategory = PinCategory;
{ Dummy.PinSubCategory = PinSubCategory;
if (Class->ClassGeneratedBy != nullptr) Dummy.PinSubCategoryObject.Empty();
Name.LeftChopInline(2); Dummy.NativeParent = nullptr;
} Dummy.IsUserDefined = false;
} ShortToInfo.Add(NameStr.ToLower(), MoveTemp(Dummy));
return WingUtils::SanitizeName(Name);
} }
void UWingTypes::ReserveShortName(FName Name) void UWingTypes::ReserveShortName(FName Name)
{ {
FString NameStr = Name.ToString(); ReserveShortName(Name, Name, FName());
ShortToPath.Add(NameStr.ToLower(), FString(TEXT("PRIMITIVE")));
} }
FString UWingTypes::ChooseShortName(const FString &Proposal, const FString &FullObjectPath) FString UWingTypes::GetShortName(const FString &Path)
{ {
// You must call this with an object path, not an asset path. FString *Result = PathToShort.Find(Path);
check(FullObjectPath.Contains(TEXT("."))); if (Result) return *Result;
return FString();
}
// Look to see if we've already assigned a short name to this path. FString UWingTypes::NewShortName(const FString &Path, FName PinCategory, const UClass *NativeParent, bool IsUserDefined)
FString *OldShort = PathToShort.Find(FullObjectPath); {
if (OldShort != nullptr) return *OldShort; // Verify that the path is not already associated.
check(!PathToShort.Find(Path));
// Derive the name proposal from the path.
int32 DotIndex;
check(Path.FindLastChar('.', DotIndex));
FString Proposal = Path.Mid(DotIndex + 1);
if ((PinCategory == UEdGraphSchema_K2::PC_Object || PinCategory == UEdGraphSchema_K2::PC_Interface)
&& IsUserDefined && Proposal.EndsWith(TEXT("_C")))
Proposal.LeftChopInline(2);
Proposal = WingUtils::SanitizeName(Proposal);
check(!Proposal.IsEmpty());
// Construct the Info struct.
Info TypeInfo;
TypeInfo.PinCategory = PinCategory;
TypeInfo.PinSubCategoryObject = Path;
TypeInfo.NativeParent = NativeParent;
TypeInfo.IsUserDefined = IsUserDefined;
// Check if the proposed name is available. // Check if the proposed name is available.
FString Lower = Proposal.ToLower(); FString Lower = Proposal.ToLower();
if (!ShortToPath.Contains(Lower)) if (!ShortToInfo.Contains(Lower))
{ {
ShortToPath.Add(Lower, FullObjectPath); TypeInfo.Short = Proposal;
PathToShort.Add(FullObjectPath, Proposal); PathToShort.Add(Path, Proposal);
ShortToInfo.Add(Lower, TypeInfo);
return Proposal; return Proposal;
} }
@@ -56,11 +192,12 @@ FString UWingTypes::ChooseShortName(const FString &Proposal, const FString &Full
for (int32 i = 2; ; ++i) for (int32 i = 2; ; ++i)
{ {
FString NumberedLower = FString::Printf(TEXT("%s%d"), *Lower, i); FString NumberedLower = FString::Printf(TEXT("%s%d"), *Lower, i);
if (!ShortToPath.Contains(NumberedLower)) if (!ShortToInfo.Contains(NumberedLower))
{ {
FString NumberedProposal = FString::Printf(TEXT("%s_%d"), *Proposal, i); FString NumberedProposal = FString::Printf(TEXT("%s_%d"), *Proposal, i);
ShortToPath.Add(NumberedLower, FullObjectPath); TypeInfo.Short = NumberedProposal;
PathToShort.Add(FullObjectPath, NumberedProposal); PathToShort.Add(Path, NumberedProposal);
ShortToInfo.Add(NumberedLower, TypeInfo);
return NumberedProposal; return NumberedProposal;
} }
} }
@@ -70,17 +207,88 @@ FString UWingTypes::ChooseShortName(const UObject* Obj)
{ {
// If it's a blueprint, register its generated class instead. // If it's a blueprint, register its generated class instead.
if (const UBlueprint* BP = Cast<UBlueprint>(Obj)) if (const UBlueprint* BP = Cast<UBlueprint>(Obj))
Obj = BP->GeneratedClass;
// Don't register null pointer.
if (Obj == nullptr) return FString();
FString Path = Obj->GetPathName();
FString Existing = GetShortName(Path);
if (!Existing.IsEmpty()) return Existing;
if (Cast<UEnum>(Obj))
{ {
if (BP->GeneratedClass) return NewShortName(Path, UEdGraphSchema_K2::PC_Enum, nullptr, Cast<UUserDefinedEnum>(Obj) != nullptr);
return ChooseShortName(BP->GeneratedClass);
return FString();
} }
if (!Cast<UScriptStruct>(Obj) && !Cast<UClass>(Obj) && !Cast<UEnum>(Obj)) if (Cast<UScriptStruct>(Obj))
return FString(); {
return NewShortName(Path, UEdGraphSchema_K2::PC_Struct, nullptr, Cast<UUserDefinedStruct>(Obj) != nullptr);
}
FString ProposedName = GetProposedName(Obj); if (const UClass* Class = Cast<UClass>(Obj))
return ChooseShortName(ProposedName, Obj->GetPathName()); {
bool IsInterface = Class->IsChildOf(UInterface::StaticClass());
bool IsUserDefined = (Class->ClassGeneratedBy != nullptr);
FName PinCategory = IsInterface ? UEdGraphSchema_K2::PC_Interface : UEdGraphSchema_K2::PC_Object;
return NewShortName(Path, PinCategory, FindNativeParent(Class), IsUserDefined);
}
return FString();
}
void UWingTypes::ChooseShortName(const FAssetData &Data)
{
FString AssetName = Data.AssetName.ToString();
FString PackageName = Data.PackageName.ToString();
FTopLevelAssetPath ClassPath = Data.AssetClassPath;
// Handle structs.
if (ClassPath == UUserDefinedStruct::StaticClass()->GetClassPathName())
{
FString Path = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
if (!GetShortName(Path).IsEmpty()) return;
NewShortName(Path, UEdGraphSchema_K2::PC_Struct, nullptr, true);
return;
}
// Handle enums.
if (ClassPath == UUserDefinedEnum::StaticClass()->GetClassPathName())
{
FString Path = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
if (!GetShortName(Path).IsEmpty()) return;
NewShortName(Path, UEdGraphSchema_K2::PC_Enum, nullptr, true);
return;
}
// Handle Blueprints. Get the generated class path.
FString Path;
if (!Data.GetTagValue(FBlueprintTags::GeneratedClassPath, Path)) return;
Path = FPackageName::ExportTextPathToObjectPath(Path);
if (!GetShortName(Path).IsEmpty()) return;
// Get the Native Parent.
FString NativeParentPath;
if (!Data.GetTagValue(FBlueprintTags::NativeParentClassPath, NativeParentPath)) return;
NativeParentPath = FPackageName::ExportTextPathToObjectPath(NativeParentPath);
UClass *NativeParent = FindObject<UClass>(nullptr, *NativeParentPath);
if (NativeParent == nullptr) return;
// Get the blueprint type.
FString BPType;
if (!Data.GetTagValue(FBlueprintTags::BlueprintType, BPType)) return;
if (BPType == TEXT("BPTYPE_Interface"))
{
check(NativeParent->IsChildOf(UInterface::StaticClass()));
NewShortName(Path, UEdGraphSchema_K2::PC_Interface, NativeParent, true);
return;
}
else if (BPType == TEXT("BPTYPE_Normal") || BPType == TEXT("BPTYPE_Const"))
{
NewShortName(Path, UEdGraphSchema_K2::PC_Object, NativeParent, true);
return;
}
} }
void UWingTypes::ChooseShortNames(UPackage* Package) void UWingTypes::ChooseShortNames(UPackage* Package)
@@ -93,31 +301,11 @@ void UWingTypes::ChooseShortNames(UPackage* Package)
}, false); }, false);
} }
void UWingTypes::ChooseShortName(const FAssetData &Data)
{
FString AssetName = Data.AssetName.ToString();
FString PackageName = Data.PackageName.ToString();
FTopLevelAssetPath ClassPath = Data.AssetClassPath;
if (ClassPath == UBlueprint::StaticClass()->GetClassPathName())
{
// Blueprint: the generated class is AssetName_C
FString ObjectPath = FString::Printf(TEXT("%s.%s_C"), *PackageName, *AssetName);
ChooseShortName(WingUtils::SanitizeName(AssetName), ObjectPath);
}
else if (ClassPath == UUserDefinedStruct::StaticClass()->GetClassPathName() ||
ClassPath == UUserDefinedEnum::StaticClass()->GetClassPathName())
{
FString ObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
ChooseShortName(WingUtils::SanitizeName(AssetName), ObjectPath);
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// TypeToText // TypeToText
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
FString UWingTypes::TypeToTextInner(FName Category, FName SubCategory, UObject* SubCategoryObject) FString UWingTypes::TypeToText(FName Category, FName SubCategory, UObject* SubCategoryObject)
{ {
if ((Category == UEdGraphSchema_K2::PC_Delegate) || if ((Category == UEdGraphSchema_K2::PC_Delegate) ||
(Category == UEdGraphSchema_K2::PC_MCDelegate) || (Category == UEdGraphSchema_K2::PC_MCDelegate) ||
@@ -177,7 +365,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType)
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>(); UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
if (!Types) return FString(); if (!Types) return FString();
FString Inner = Types->TypeToTextInner(PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get()); FString Inner = Types->TypeToText(PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get());
if (Inner.IsEmpty()) if (Inner.IsEmpty())
return FString(); return FString();
@@ -187,7 +375,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType)
return FString::Printf(TEXT("Set<%s>"), *Inner); return FString::Printf(TEXT("Set<%s>"), *Inner);
if (PinType.IsMap()) if (PinType.IsMap())
{ {
FString ValueInner = Types->TypeToTextInner( FString ValueInner = Types->TypeToText(
PinType.PinValueType.TerminalCategory, PinType.PinValueType.TerminalCategory,
PinType.PinValueType.TerminalSubCategory, PinType.PinValueType.TerminalSubCategory,
PinType.PinValueType.TerminalSubCategoryObject.Get()); PinType.PinValueType.TerminalSubCategoryObject.Get());
@@ -229,66 +417,7 @@ FString UWingTypes::TypeToTextOrDie(const UObject* Obj)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Subsystem lifecycle // Tokenizer, Parser, and Resolve Short Name
// ---------------------------------------------------------------------------
void UWingTypes::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
// Collect all packages and sort by name for stable short-name assignment.
TArray<UPackage*> Packages;
for (TObjectIterator<UPackage> It; It; ++It)
Packages.Add(*It);
Packages.Sort([](const UPackage& A, const UPackage& B) { return A.GetName() < B.GetName(); });
// Reserve the short names of the primitives.
ReserveShortName(UEdGraphSchema_K2::PC_Delegate);
ReserveShortName(UEdGraphSchema_K2::PC_MCDelegate);
ReserveShortName(UEdGraphSchema_K2::PC_Boolean);
ReserveShortName(UEdGraphSchema_K2::PC_Int);
ReserveShortName(UEdGraphSchema_K2::PC_Int64);
ReserveShortName(UEdGraphSchema_K2::PC_Float);
ReserveShortName(UEdGraphSchema_K2::PC_Double);
ReserveShortName(UEdGraphSchema_K2::PC_Byte);
ReserveShortName(UEdGraphSchema_K2::PC_Name);
ReserveShortName(UEdGraphSchema_K2::PC_String);
ReserveShortName(UEdGraphSchema_K2::PC_Text);
// Scan priority packages first, then everything else in sorted order.
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/CoreUObject")));
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Engine")));
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Integration")));
// Choose short names for everything else.
for (UPackage* Pkg : Packages)
ChooseShortNames(Pkg);
// Scan the asset registry for unloaded assets.
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
TArray<FAssetData> AllAssets;
AssetRegistry.GetAllAssets(AllAssets, true);
for (const FAssetData& Data : AllAssets)
ChooseShortName(Data);
// Register for future asset discoveries.
AssetRegistry.OnAssetAdded().AddUObject(this, &UWingTypes::ChooseShortName);
UE_LOG(LogTemp, Display, TEXT("WingTypes: Registered %d types"), ShortToPath.Num());
}
void UWingTypes::Deinitialize()
{
if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry")))
{
IAssetRegistry& AssetRegistry = FModuleManager::GetModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
AssetRegistry.OnAssetAdded().RemoveAll(this);
}
Super::Deinitialize();
}
// ---------------------------------------------------------------------------
// Tokenizer
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
void UWingTypes::Tokenize(const FString& Input) void UWingTypes::Tokenize(const FString& Input)
@@ -325,69 +454,6 @@ void UWingTypes::Tokenize(const FString& Input)
} }
} }
// ---------------------------------------------------------------------------
// Short Name Resolution
// ---------------------------------------------------------------------------
bool UWingTypes::ResolveShortName(const FString &Name, FEdGraphPinType &OutType)
{
FString *Path = ShortToPath.Find(Name.ToLower());
if (!Path)
{
Error = FString::Printf(TEXT("Unrecognized type '%s'"), *Name);
return false;
}
// Primitives (int, float, etc.) are registered with the path "PRIMITIVE".
if (*Path == TEXT("PRIMITIVE"))
{
OutType.PinCategory = FName(*Name);
if ((OutType.PinCategory == UEdGraphSchema_K2::PC_Double) ||
(OutType.PinCategory == UEdGraphSchema_K2::PC_Float))
{
OutType.PinSubCategory = OutType.PinCategory;
OutType.PinCategory = UEdGraphSchema_K2::PC_Real;
}
return true;
}
// Load the object.
UObject* Obj = LoadObject<UObject>(nullptr, **Path);
if (!Obj)
{
Error = FString::Printf(TEXT("Failed to load type '%s' at path '%s'"), *Name, **Path);
return false;
}
// The short name registry only contains UClass, UScriptStruct, and UEnum.
checkf(Cast<UClass>(Obj) || Cast<UScriptStruct>(Obj) || Cast<UEnum>(Obj),
TEXT("Short name '%s' resolved to unexpected type '%s'"), *Name, *Obj->GetClass()->GetName());
// Determine the category from the object type.
if (Cast<UScriptStruct>(Obj))
{
OutType.PinCategory = UEdGraphSchema_K2::PC_Struct;
}
else if (UClass* Class = Cast<UClass>(Obj))
{
if (Class->IsChildOf(UInterface::StaticClass()))
OutType.PinCategory = UEdGraphSchema_K2::PC_Interface;
else
OutType.PinCategory = UEdGraphSchema_K2::PC_Object;
}
else
{
OutType.PinCategory = UEdGraphSchema_K2::PC_Byte;
}
OutType.PinSubCategoryObject = Obj;
return true;
}
// ---------------------------------------------------------------------------
// Parsing Types
// ---------------------------------------------------------------------------
bool UWingTypes::TokenIs(const TCHAR* Text) const bool UWingTypes::TokenIs(const TCHAR* Text) const
{ {
if (Cursor >= Tokens.Num()) return false; if (Cursor >= Tokens.Num()) return false;
@@ -452,9 +518,9 @@ bool UWingTypes::ParseWrapped(FName Wrapper, FEdGraphPinType& OutType)
Cursor++; Cursor++;
if (!ParseChar('<')) return false; if (!ParseChar('<')) return false;
if (!ParsePlainIdentifier(OutType)) return false; if (!ParsePlainIdentifier(OutType)) return false;
if (!Cast<UClass>(OutType.PinSubCategoryObject)) if (OutType.PinCategory != UEdGraphSchema_K2::PC_Object)
{ {
Error = FString::Printf(TEXT("%s is not a Class"), *OutType.PinSubCategoryObject->GetName()); Error = FString::Printf(TEXT("%s is not an object type"), *OutType.PinSubCategoryObject->GetName());
return false; return false;
} }
if (!ParseChar('>')) return false; if (!ParseChar('>')) return false;
@@ -528,43 +594,154 @@ bool UWingTypes::ParseType(FEdGraphPinType& OutType)
return true; return true;
} }
FString UWingTypes::TryTextToType(const FString& Text, FEdGraphPinType& OutPinType) bool UWingTypes::ResolveShortName(const FString &Name, FEdGraphPinType &OutType)
{ {
Info* TypeInfo = ShortToInfo.Find(Name.ToLower());
if (!TypeInfo)
{
Error = FString::Printf(TEXT("Unrecognized type '%s'"), *Name);
return false;
}
// Handle primitives.
if (TypeInfo->PinSubCategoryObject.IsEmpty())
{
OutType.PinCategory = TypeInfo->PinCategory;
OutType.PinSubCategory = TypeInfo->PinSubCategory;
return true;
}
// Load the PinSubCategoryObject.
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo->PinSubCategoryObject);
if (!Obj)
{
Error = FString::Printf(TEXT("Failed to load type '%s' at path '%s'"), *Name, *TypeInfo->PinSubCategoryObject);
return false;
}
// Sanity check the result.
if (UClass *Class = Cast<UClass>(Obj))
{
if (Class->IsChildOf(UInterface::StaticClass()))
{
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Interface);
}
else
{
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Object);
}
}
else if (Cast<UScriptStruct>(Obj))
{
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Struct);
}
else if (Cast<UEnum>(Obj))
{
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Enum);
}
else
{
UE_LOG(LogTemp, Fatal, TEXT("PinSubCategoryObject '%s' doesn't belong in type registry"), *Obj->GetClass()->GetName());
}
OutType.PinCategory = TypeInfo->PinCategory;
OutType.PinSubCategoryObject = Obj;
return true;
}
// ---------------------------------------------------------------------------
// Text to Type
// ---------------------------------------------------------------------------
bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require)
{
if (!Require.BlueprintType.IsSet() ||
!Require.Blueprintable.IsSet() ||
!Require.AllowContainer.IsSet())
{
UWingServer::Printf(TEXT("ERROR: TextToType called with underspecified Requirements list.\n"));
return false;
}
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>(); UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
check(Types); check(Types);
Types->Error.Empty(); Types->Error.Empty();
Types->Tokenize(Text); Types->Tokenize(Text);
OutPinType = FEdGraphPinType(); OutPinType = FEdGraphPinType();
if (!Types->ParseType(OutPinType)) return Types->Error; if (!Types->ParseType(OutPinType))
return FString(); {
UWingServer::Printf(TEXT("%s\n"), *Types->Error);
OutPinType = FEdGraphPinType(); return false;
}
if (!Require.AllowContainer.GetValue())
{
if (OutPinType.IsContainer())
{
UWingServer::Printf(TEXT("ERROR: Type %s is a container, not allowed here\n"), *Text);
OutPinType = FEdGraphPinType(); return false;
}
}
if (!Require.PinCategory.IsNone())
{
if (OutPinType.PinCategory != Require.PinCategory)
{
UWingServer::Printf(TEXT("ERROR: Need a type which is an %s, got a %s instead."),
*Require.PinCategory.ToString(), *OutPinType.PinCategory.ToString());
OutPinType = FEdGraphPinType(); return false;
}
}
if (Require.IsChildOf)
{
if (!IsChildOf(OutPinType, Require.IsChildOf))
{
UWingServer::Printf(TEXT("ERROR: Type must derive from %s\n"), *WingUtils::FormatName(Require.IsChildOf));
OutPinType = FEdGraphPinType(); return false;
}
}
if (Require.BlueprintType.GetValue())
{
if (!IsBlueprintType(OutPinType))
{
UWingServer::Printf(TEXT("ERROR: Not a blueprint type: %s\n"), *Text);
OutPinType = FEdGraphPinType(); return false;
}
}
if (Require.Blueprintable.GetValue())
{
if (!IsBlueprintable(OutPinType))
{
UWingServer::Printf(TEXT("ERROR: Not a blueprintable type: %s\n"), *Text);
OutPinType = FEdGraphPinType(); return false;
}
}
return true;
} }
bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType) UClass* UWingTypes::TextToOneObjectType(const FString& Text, const Requirements &Require)
{
FString Error = TryTextToType(Text, OutPinType);
if (Error.IsEmpty()) return true;
UWingServer::Print(Error);
return false;
}
UClass* UWingTypes::TextToOneObjectType(const FString& Text)
{ {
FEdGraphPinType PinType; FEdGraphPinType PinType;
if (!TextToType(Text, PinType)) return nullptr; if (!TextToType(Text, PinType, Require)) return nullptr;
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get()); UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) || if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) ||
(PinType.IsContainer())) (PinType.IsContainer()))
{ {
UWingServer::Printf(TEXT("ERROR: '%s' is not a plain object class\n"), *Text); UWingServer::Printf(TEXT("ERROR: '%s' is not an object class\n"), *Text);
return nullptr; return nullptr;
} }
return Class; return Class;
} }
UClass* UWingTypes::TextToOneInterfaceType(const FString& Text) UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require)
{ {
FEdGraphPinType PinType; FEdGraphPinType PinType;
if (!TextToType(Text, PinType)) return nullptr; if (!TextToType(Text, PinType, Require)) return nullptr;
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get()); UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) || if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) ||
(PinType.IsContainer())) (PinType.IsContainer()))
@@ -575,3 +752,60 @@ UClass* UWingTypes::TextToOneInterfaceType(const FString& Text)
return Class; return Class;
} }
// ---------------------------------------------------------------------------
// Subsystem lifecycle
// ---------------------------------------------------------------------------
void UWingTypes::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
// Collect all packages and sort by name for stable short-name assignment.
TArray<UPackage*> Packages;
for (TObjectIterator<UPackage> It; It; ++It)
Packages.Add(*It);
Packages.Sort([](const UPackage& A, const UPackage& B) { return A.GetName() < B.GetName(); });
// Reserve the short names of the primitives.
ReserveShortName(UEdGraphSchema_K2::PC_Delegate);
ReserveShortName(UEdGraphSchema_K2::PC_MCDelegate);
ReserveShortName(UEdGraphSchema_K2::PC_Boolean);
ReserveShortName(UEdGraphSchema_K2::PC_Int);
ReserveShortName(UEdGraphSchema_K2::PC_Int64);
ReserveShortName(UEdGraphSchema_K2::PC_Float, UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Float);
ReserveShortName(UEdGraphSchema_K2::PC_Double, UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Double);
ReserveShortName(UEdGraphSchema_K2::PC_Byte);
ReserveShortName(UEdGraphSchema_K2::PC_Name);
ReserveShortName(UEdGraphSchema_K2::PC_String);
ReserveShortName(UEdGraphSchema_K2::PC_Text);
// Scan priority packages first, then everything else in sorted order.
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/CoreUObject")));
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Engine")));
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Integration")));
// Choose short names for everything else.
for (UPackage* Pkg : Packages)
ChooseShortNames(Pkg);
// Scan the asset registry for unloaded assets.
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
TArray<FAssetData> AllAssets;
AssetRegistry.GetAllAssets(AllAssets, true);
for (const FAssetData& Data : AllAssets)
ChooseShortName(Data);
// Register for future asset discoveries.
AssetRegistry.OnAssetAdded().AddUObject(this, &UWingTypes::ChooseShortName);
}
void UWingTypes::Deinitialize()
{
if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry")))
{
IAssetRegistry& AssetRegistry = FModuleManager::GetModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
AssetRegistry.OnAssetAdded().RemoveAll(this);
}
Super::Deinitialize();
}

View File

@@ -15,78 +15,106 @@ class UWingTypes : public UEditorSubsystem
GENERATED_BODY() GENERATED_BODY()
public: public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override; // ---------------------------------------------------------------------------
virtual void Deinitialize() override; // Simple Accessors
// ---------------------------------------------------------------------------
// Convert a pin type to a type string. Returns empty string // This struct is used to record the information about a type
// on failure. struct Info
{
FString Short;
FName PinCategory;
FName PinSubCategory;
FString PinSubCategoryObject;
// The following are only set for interfaces and Objects.
const UClass *NativeParent = nullptr;
bool IsUserDefined = false;
};
// Get the full short-name-to-info map.
static const TMap<FString, Info>& GetAllTypes();
public:
// ---------------------------------------------------------------------------
// Simple Utility Functions.
// ---------------------------------------------------------------------------
// True if the UClass, UStruct, or UEnum is a BlueprintType
static bool IsBlueprintType(const UObject *Obj);
// True if all parts of the pintype are blueprinttypes.
static bool IsBlueprintType(const FEdGraphPinType &Type);
// True if the pintype is a blueprintable interface or object.
static bool IsBlueprintable(const FEdGraphPinType &Type);
// True if the pintype is a child of the specified uclass.
static bool IsChildOf(const FEdGraphPinType &Type, UClass *Class);
// True if the info struct represents a BlueprintType
static bool IsBlueprintType(const Info &TypeInfo);
// True if the info struct represents a Blueprintable type.
static bool IsBlueprintable(const Info &TypeInfo);
// Get the native parent of any uclass.
const UClass *FindNativeParent(const UClass *Class);
private:
// ---------------------------------------------------------------------------
// Choose Short Name
// ---------------------------------------------------------------------------
// Reserve the short name for a primitive type.
void ReserveShortName(FName Name, FName PinCategory, FName PinSubCategory);
void ReserveShortName(FName Name);
// Get the short name if it already exists.
FString GetShortName(const FString &Path);
// Core version: choose a short name for a path that doesn't already
// have a short name. Records all the associated information.
FString NewShortName(const FString &Path, FName PinCategory, const UClass *NativeParent, bool IsUserDefined);
// Choose a short name for an already-loaded UObject.
FString ChooseShortName(const UObject* Obj);
// Choose a short name for an FAssetData.
void ChooseShortName(const FAssetData &Data);
// Choose short names for every type in the package.
void ChooseShortNames(UPackage* Package);
private:
// ---------------------------------------------------------------------------
// TypeToText
// ---------------------------------------------------------------------------
// Return a type string for a primitive or wrapped type (No Containers)
FString TypeToText(FName Category, FName SubCategory, UObject* SubCategoryObject);
public:
// Convert a pin type to a type string.
// Returns empty string on failure.
static FString TypeToText(const FEdGraphPinType& PinType); static FString TypeToText(const FEdGraphPinType& PinType);
// Convert a Property to a type string.
// Returns empty string on failure.
static FString TypeToText(const FProperty *Property); static FString TypeToText(const FProperty *Property);
// Get the type name for a UClass, UScriptStruct, or UEnum. // Get the type name for a UClass, UScriptStruct, or UEnum.
// Returns empty string if the object is not one of those types. // Returns empty string on failure.
static FString TypeToText(const UObject* Obj); static FString TypeToText(const UObject* Obj);
// Get the type name for a UClass, UScriptStruct, or UEnum. // Get the type name for a UClass, UScriptStruct, or UEnum.
// Check fail if the result is empty. // Check fail if the result is empty.
static FString TypeToTextOrDie(const UObject* Obj); static FString TypeToTextOrDie(const UObject* Obj);
// Try to parse a type. If there's a problem, returns an error
// message. If all goes well, returns empty string.
static FString TryTextToType(const FString& Text, FEdGraphPinType& OutPinType);
// Try to parse a type. If there's a problem, prints an error
// message and returns false.
static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType);
// Parse a type string and verify it's a single object class (PC_Object,
// no container, no wrapper). Returns nullptr and prints error on failure.
static UClass* TextToOneObjectType(const FString& Text);
// Parse a type string and verify it's a single interface class (PC_Interface,
// no container, no wrapper). Returns nullptr and prints error on failure.
static UClass* TextToOneInterfaceType(const FString& Text);
private: private:
FString TypeToTextInner(FName Category, FName SubCategory, UObject* SubCategoryObject); // ---------------------------------------------------------------------------
// Tokenizer, Parser, and ResolveShortName
// ---------------------------------------------------------------------------
bool ResolveShortName(const FString &Name, FEdGraphPinType &OutType);
// Get the object's name, not including the _C generated
// by the blueprint compiler for generated classes.
static FString GetProposedName(const UObject *Obj);
// Reserve the short name for a primitive type.
// The value stored in the map is just "PRIMITIVE".
void ReserveShortName(FName Name);
// Choose a short name for the type at the specified
// full object path. Never reuses a short name, so every
// type will have a unique name, at least as long as the
// editor is up. The path must be the complete path of
// an object, including the .FOO_C at the end. The name
// chosen will either be the proposed name, or some
// small variation of that.
FString ChooseShortName(const FString &Proposed, const FString &FullObjectPath);
// Chooses a short name for the specified type. If the
// object is not a class, struct, interface, or enum, returns
// null string.
FString ChooseShortName(const UObject* Obj);
// Choose short names for every type in the package.
void ChooseShortNames(UPackage* Package);
// Choose a name for the asset's primary object, without
// loading the object.
void ChooseShortName(const FAssetData &Data);
// Short name registry: bidirectional mapping between short names and full paths.
TMap<FString, FString> ShortToPath; // e.g. "vector" -> "/Script/CoreUObject.Vector"
TMap<FString, FString> PathToShort; // e.g. "/Script/CoreUObject.Vector" -> "Vector"
// Tokenizer and parser (used by TextToType)
void Tokenize(const FString& Input); void Tokenize(const FString& Input);
bool TokenIs(const TCHAR* Text) const; bool TokenIs(const TCHAR* Text) const;
bool TokenIs(const TCHAR* Text, TCHAR ch) const; bool TokenIs(const TCHAR* Text, TCHAR ch) const;
@@ -102,6 +130,57 @@ private:
bool ParseMap(FEdGraphPinType& OutType); bool ParseMap(FEdGraphPinType& OutType);
bool ParseType(FEdGraphPinType& OutType); bool ParseType(FEdGraphPinType& OutType);
bool ResolveShortName(const FString &Name, FEdGraphPinType &OutType);
public:
// ---------------------------------------------------------------------------
// TextToType
// ---------------------------------------------------------------------------
struct Requirements
{
TOptional<bool> BlueprintType;
TOptional<bool> Blueprintable;
TOptional<bool> AllowContainer;
UClass *IsChildOf = nullptr;
FName PinCategory = FName();
};
// 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);
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. Requires that the type
// is an object type (even if that's not specified in the requirements struct).
static UClass* TextToOneObjectType(const FString& Text, const Requirements &Require);
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. Requires that the type
// is an interface type (even if that's not specified in the requirements struct).
static UClass* TextToOneInterfaceType(const FString& Text, const Requirements &Require);
private:
// ---------------------------------------------------------------------------
// Subsystem lifecycle
// ---------------------------------------------------------------------------
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
private:
// ---------------------------------------------------------------------------
// State
// ---------------------------------------------------------------------------
// Given a short name, get all the information about that type.
TMap<FString, Info> ShortToInfo;
// Given a class path, return the short name, e.g. "/Script/CoreUObject.Vector" -> "Vector"
TMap<FString, FString> PathToShort;
// These fields are only used during the parsing of a type.
TArray<FString> Tokens; TArray<FString> Tokens;
int32 Cursor = 0; int32 Cursor = 0;
FString Error; FString Error;