From 6325b15cb74496f92b01ed07fa0d2c31b020700a Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 25 Mar 2026 02:32:26 -0400 Subject: [PATCH] Lots of work on WingTypes --- .../UEWingman/Handlers/ActorComponent_Add.h | 7 +- .../Source/UEWingman/Handlers/Asset_Search.h | 6 +- .../Handlers/BlueprintInterface_Add.h | 6 +- .../UEWingman/Handlers/Blueprint_Create.h | 8 +- .../UEWingman/Handlers/Blueprint_Reparent.h | 6 +- .../UEWingman/Handlers/TypeName_Search.h | 82 +-- .../UEWingman/Private/WingFunctionArgs.cpp | 6 +- .../Source/UEWingman/Private/WingProperty.cpp | 6 +- .../Source/UEWingman/Private/WingTypes.cpp | 626 ++++++++++++------ .../Source/UEWingman/Public/WingTypes.h | 197 ++++-- 10 files changed, 629 insertions(+), 321 deletions(-) diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/ActorComponent_Add.h b/Plugins/UEWingman/Source/UEWingman/Handlers/ActorComponent_Add.h index e062f5f3..9c499ff1 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/ActorComponent_Add.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/ActorComponent_Add.h @@ -63,7 +63,12 @@ public: if (InternalName.IsEmpty()) return; // 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; // Find the specified parent component diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Search.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Search.h index dfea0884..4fbeff71 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Search.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Search.h @@ -51,7 +51,11 @@ public: 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; Filter.ClassPaths.Add(TypeClass->GetClassPathName()); } diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintInterface_Add.h b/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintInterface_Add.h index 38c78cdf..66bf87e8 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintInterface_Add.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/BlueprintInterface_Add.h @@ -39,7 +39,11 @@ public: if (!BP) return; // 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; // Check for duplicates diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Create.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Create.h index e2cd257c..f8fae457 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Create.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Create.h @@ -43,15 +43,19 @@ public: if (!Maker.Ok()) return; // Resolve parent class based on blueprint type + UWingTypes::Requirements Req; + Req.BlueprintType = false; + Req.Blueprintable = true; + Req.AllowContainer = false; UClass* ParentClassObj = nullptr; switch (BlueprintType) { case BPTYPE_Normal: - ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass); + ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req); if (!ParentClassObj) return; break; case BPTYPE_MacroLibrary: - ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass); + ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req); if (!ParentClassObj) return; break; case BPTYPE_Interface: diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Reparent.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Reparent.h index 9c5f12c3..7e39e942 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Reparent.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Blueprint_Reparent.h @@ -41,7 +41,11 @@ public: if (!BP) return; // 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; // Validate reparent diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/TypeName_Search.h b/Plugins/UEWingman/Source/UEWingman/Handlers/TypeName_Search.h index 960723f0..4ade5462 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/TypeName_Search.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/TypeName_Search.h @@ -4,13 +4,6 @@ #include "WingServer.h" #include "WingHandler.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" @@ -30,73 +23,46 @@ public: UPROPERTY(meta=(Optional, Description="Maximum number of results")) 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 { return TEXT("Search for type names usable in pin type specifications. " "Returns short names that can be used with commands like Blueprint_ChangeVariableType."); } - void TryMatchObject(TSet &Matches, UObject *Obj) - { - if (!Obj) return; - FString Name = Obj->GetName(); - if (!Name.Contains(Query, ESearchCase::IgnoreCase)) return; - Matches.Add(Obj); - } - - void TryMatchObjects(TSet &Matches, UClass *Class) - { - ForEachObjectOfClass(Class, [&](UObject *Obj){ - if (Matches.Num() == Limit) return; - TryMatchObject(Matches, Obj); - }, true); - } - - void TryMatchAssets(TSet &Matches, UClass *Class) - { - IAssetRegistry& Registry = *IAssetRegistry::Get(); - TArray 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(Obj)) - Obj = BP->GeneratedClass; - TryMatchObject(Matches, Obj); - } - } - virtual void Handle() override { - TSet Matches; - TryMatchObjects(Matches, UScriptStruct::StaticClass()); - TryMatchObjects(Matches, UClass::StaticClass()); - TryMatchObjects(Matches, UEnum::StaticClass()); - TryMatchAssets(Matches, UBlueprint::StaticClass()); - TryMatchAssets(Matches, UUserDefinedStruct::StaticClass()); - TryMatchAssets(Matches, UUserDefinedEnum::StaticClass()); + const TMap& AllTypes = UWingTypes::GetAllTypes(); TArray Results; - for (const UObject *Obj : Matches) + for (const auto& Pair : AllTypes) { - const TCHAR *Kind = TEXT("Unknown"); - if (Cast(Obj)) - Kind = TEXT("Enum"); - else if (Cast(Obj)) - Kind = TEXT("Struct"); - else if (const UClass* Class = Cast(Obj)) - Kind = Class->IsChildOf(UInterface::StaticClass()) ? TEXT("Interface") : TEXT("Class"); - Results.Add(FString::Printf(TEXT("%s %s\n"), Kind, *UWingTypes::TypeToText(Obj))); + if (Results.Num() >= Limit) break; + + const UWingTypes::Info& Info = Pair.Value; + if (!Info.Short.Contains(Query, ESearchCase::IgnoreCase)) continue; + if (!Exhaustive && !UWingTypes::IsBlueprintType(Info) && !UWingTypes::IsBlueprintable(Info)) continue; + + FString Line = FString::Printf(TEXT("%s %s %s"), + *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(); - for (const auto &Result : Results) + for (const auto& Result : Results) { UWingServer::Print(Result); } - if (Results.Num() == Limit) + if (Results.Num() >= Limit) { UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n")); } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingFunctionArgs.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingFunctionArgs.cpp index 41204b1d..5ec51a78 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingFunctionArgs.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingFunctionArgs.cpp @@ -69,7 +69,11 @@ bool WingFunctionArgs::ParseArgs(const FString& Args, TArray& OutArg } 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); OutArgs.Add(MoveTemp(Arg)); } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp index d86faba1..85e012c0 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp @@ -62,7 +62,11 @@ bool FWingProperty::SetText(const FString &Value) if (IsPinTypeProperty(Prop)) { 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); return true; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp index 35c1fba4..41025a01 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp @@ -8,46 +8,182 @@ #include "Engine/UserDefinedEnum.h" #include "UObject/UObjectIterator.h" #include "AssetRegistry/AssetRegistryModule.h" +#include "Blueprint/BlueprintSupport.h" +#include "Kismet2/KismetEditorUtilities.h" + + +// --------------------------------------------------------------------------- +// Simple Accessors +// --------------------------------------------------------------------------- + +const TMap& UWingTypes::GetAllTypes() +{ + UWingTypes* Types = GEditor->GetEditorSubsystem(); + check(Types); + return Types->ShortToInfo; +} + +// --------------------------------------------------------------------------- +// Simple Utility Functions. +// --------------------------------------------------------------------------- + +bool UWingTypes::IsBlueprintType(const UObject *Obj) +{ + if (const UEnum* Enum = Cast(Obj)) + return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Enum); + if (const UScriptStruct* Struct = Cast(Obj)) + return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct); + if (const UClass* Class = Cast(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(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(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(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(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 // --------------------------------------------------------------------------- -FString UWingTypes::GetProposedName(const UObject *Obj) +void UWingTypes::ReserveShortName(FName Name, FName PinCategory, FName PinSubCategory) { - FString Name = Obj->GetName(); - if (Name.EndsWith(TEXT("_C"))) - { - if (const UClass* Class = Cast(Obj)) - { - if (Class->ClassGeneratedBy != nullptr) - Name.LeftChopInline(2); - } - } - return WingUtils::SanitizeName(Name); + FString NameStr = Name.ToString(); + Info Dummy; + Dummy.Short = NameStr; + Dummy.PinCategory = PinCategory; + Dummy.PinSubCategory = PinSubCategory; + Dummy.PinSubCategoryObject.Empty(); + Dummy.NativeParent = nullptr; + Dummy.IsUserDefined = false; + ShortToInfo.Add(NameStr.ToLower(), MoveTemp(Dummy)); } void UWingTypes::ReserveShortName(FName Name) { - FString NameStr = Name.ToString(); - ShortToPath.Add(NameStr.ToLower(), FString(TEXT("PRIMITIVE"))); + ReserveShortName(Name, Name, FName()); } -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. - check(FullObjectPath.Contains(TEXT("."))); + FString *Result = PathToShort.Find(Path); + if (Result) return *Result; + return FString(); +} - // Look to see if we've already assigned a short name to this path. - FString *OldShort = PathToShort.Find(FullObjectPath); - if (OldShort != nullptr) return *OldShort; +FString UWingTypes::NewShortName(const FString &Path, FName PinCategory, const UClass *NativeParent, bool IsUserDefined) +{ + // 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. FString Lower = Proposal.ToLower(); - if (!ShortToPath.Contains(Lower)) + if (!ShortToInfo.Contains(Lower)) { - ShortToPath.Add(Lower, FullObjectPath); - PathToShort.Add(FullObjectPath, Proposal); + TypeInfo.Short = Proposal; + PathToShort.Add(Path, Proposal); + ShortToInfo.Add(Lower, TypeInfo); return Proposal; } @@ -56,11 +192,12 @@ FString UWingTypes::ChooseShortName(const FString &Proposal, const FString &Full for (int32 i = 2; ; ++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); - ShortToPath.Add(NumberedLower, FullObjectPath); - PathToShort.Add(FullObjectPath, NumberedProposal); + TypeInfo.Short = NumberedProposal; + PathToShort.Add(Path, NumberedProposal); + ShortToInfo.Add(NumberedLower, TypeInfo); return NumberedProposal; } } @@ -70,17 +207,88 @@ FString UWingTypes::ChooseShortName(const UObject* Obj) { // If it's a blueprint, register its generated class instead. if (const UBlueprint* BP = Cast(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(Obj)) { - if (BP->GeneratedClass) - return ChooseShortName(BP->GeneratedClass); - return FString(); + return NewShortName(Path, UEdGraphSchema_K2::PC_Enum, nullptr, Cast(Obj) != nullptr); } - if (!Cast(Obj) && !Cast(Obj) && !Cast(Obj)) - return FString(); + if (Cast(Obj)) + { + return NewShortName(Path, UEdGraphSchema_K2::PC_Struct, nullptr, Cast(Obj) != nullptr); + } - FString ProposedName = GetProposedName(Obj); - return ChooseShortName(ProposedName, Obj->GetPathName()); + if (const UClass* Class = Cast(Obj)) + { + 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(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) @@ -93,31 +301,11 @@ void UWingTypes::ChooseShortNames(UPackage* Package) }, 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 // --------------------------------------------------------------------------- -FString UWingTypes::TypeToTextInner(FName Category, FName SubCategory, UObject* SubCategoryObject) +FString UWingTypes::TypeToText(FName Category, FName SubCategory, UObject* SubCategoryObject) { if ((Category == UEdGraphSchema_K2::PC_Delegate) || (Category == UEdGraphSchema_K2::PC_MCDelegate) || @@ -177,7 +365,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType) UWingTypes* Types = GEditor->GetEditorSubsystem(); 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()) return FString(); @@ -187,7 +375,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType) return FString::Printf(TEXT("Set<%s>"), *Inner); if (PinType.IsMap()) { - FString ValueInner = Types->TypeToTextInner( + FString ValueInner = Types->TypeToText( PinType.PinValueType.TerminalCategory, PinType.PinValueType.TerminalSubCategory, PinType.PinValueType.TerminalSubCategoryObject.Get()); @@ -229,66 +417,7 @@ FString UWingTypes::TypeToTextOrDie(const UObject* Obj) } // --------------------------------------------------------------------------- -// Subsystem lifecycle -// --------------------------------------------------------------------------- - -void UWingTypes::Initialize(FSubsystemCollectionBase& Collection) -{ - Super::Initialize(Collection); - - // Collect all packages and sort by name for stable short-name assignment. - TArray Packages; - for (TObjectIterator 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(TEXT("AssetRegistry")).Get(); - TArray 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(TEXT("AssetRegistry")).Get(); - AssetRegistry.OnAssetAdded().RemoveAll(this); - } - Super::Deinitialize(); -} - -// --------------------------------------------------------------------------- -// Tokenizer +// Tokenizer, Parser, and Resolve Short Name // --------------------------------------------------------------------------- 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(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(Obj) || Cast(Obj) || Cast(Obj), - TEXT("Short name '%s' resolved to unexpected type '%s'"), *Name, *Obj->GetClass()->GetName()); - - // Determine the category from the object type. - if (Cast(Obj)) - { - OutType.PinCategory = UEdGraphSchema_K2::PC_Struct; - } - else if (UClass* Class = Cast(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 { if (Cursor >= Tokens.Num()) return false; @@ -452,9 +518,9 @@ bool UWingTypes::ParseWrapped(FName Wrapper, FEdGraphPinType& OutType) Cursor++; if (!ParseChar('<')) return false; if (!ParsePlainIdentifier(OutType)) return false; - if (!Cast(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; } if (!ParseChar('>')) return false; @@ -528,43 +594,154 @@ bool UWingTypes::ParseType(FEdGraphPinType& OutType) 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(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(Obj)) + { + if (Class->IsChildOf(UInterface::StaticClass())) + { + check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Interface); + } + else + { + check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Object); + } + } + else if (Cast(Obj)) + { + check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Struct); + } + else if (Cast(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(); check(Types); Types->Error.Empty(); Types->Tokenize(Text); OutPinType = FEdGraphPinType(); - if (!Types->ParseType(OutPinType)) return Types->Error; - return FString(); + if (!Types->ParseType(OutPinType)) + { + 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) -{ - FString Error = TryTextToType(Text, OutPinType); - if (Error.IsEmpty()) return true; - UWingServer::Print(Error); - return false; -} - -UClass* UWingTypes::TextToOneObjectType(const FString& Text) +UClass* UWingTypes::TextToOneObjectType(const FString& Text, const Requirements &Require) { FEdGraphPinType PinType; - if (!TextToType(Text, PinType)) return nullptr; + if (!TextToType(Text, PinType, Require)) return nullptr; UClass* Class = Cast(PinType.PinSubCategoryObject.Get()); if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) || (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 Class; } -UClass* UWingTypes::TextToOneInterfaceType(const FString& Text) +UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require) { FEdGraphPinType PinType; - if (!TextToType(Text, PinType)) return nullptr; + if (!TextToType(Text, PinType, Require)) return nullptr; UClass* Class = Cast(PinType.PinSubCategoryObject.Get()); if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) || (PinType.IsContainer())) @@ -575,3 +752,60 @@ UClass* UWingTypes::TextToOneInterfaceType(const FString& Text) 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 Packages; + for (TObjectIterator 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(TEXT("AssetRegistry")).Get(); + TArray 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(TEXT("AssetRegistry")).Get(); + AssetRegistry.OnAssetAdded().RemoveAll(this); + } + Super::Deinitialize(); +} + diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingTypes.h b/Plugins/UEWingman/Source/UEWingman/Public/WingTypes.h index 8cc904d6..cd5d4b24 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingTypes.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingTypes.h @@ -15,78 +15,106 @@ class UWingTypes : public UEditorSubsystem GENERATED_BODY() public: - virtual void Initialize(FSubsystemCollectionBase& Collection) override; - virtual void Deinitialize() override; + // --------------------------------------------------------------------------- + // Simple Accessors + // --------------------------------------------------------------------------- - // Convert a pin type to a type string. Returns empty string - // on failure. + // This struct is used to record the information about a type + 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& 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); + + // Convert a Property to a type string. + // Returns empty string on failure. static FString TypeToText(const FProperty *Property); // 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); // Get the type name for a UClass, UScriptStruct, or UEnum. // Check fail if the result is empty. 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: - 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 ShortToPath; // e.g. "vector" -> "/Script/CoreUObject.Vector" - TMap PathToShort; // e.g. "/Script/CoreUObject.Vector" -> "Vector" - - // Tokenizer and parser (used by TextToType) void Tokenize(const FString& Input); bool TokenIs(const TCHAR* Text) const; bool TokenIs(const TCHAR* Text, TCHAR ch) const; @@ -102,6 +130,57 @@ private: bool ParseMap(FEdGraphPinType& OutType); bool ParseType(FEdGraphPinType& OutType); + bool ResolveShortName(const FString &Name, FEdGraphPinType &OutType); + +public: + // --------------------------------------------------------------------------- + // TextToType + // --------------------------------------------------------------------------- + + struct Requirements + { + TOptional BlueprintType; + TOptional Blueprintable; + TOptional 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 ShortToInfo; + + // Given a class path, return the short name, e.g. "/Script/CoreUObject.Vector" -> "Vector" + TMap PathToShort; + + // These fields are only used during the parsing of a type. TArray Tokens; int32 Cursor = 0; FString Error;