Lots of work on WingTypes
This commit is contained in:
@@ -69,7 +69,11 @@ bool WingFunctionArgs::ParseArgs(const FString& Args, TArray<FParsedArg>& 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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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<UClass>(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<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 ChooseShortName(BP->GeneratedClass);
|
||||
return FString();
|
||||
return NewShortName(Path, UEdGraphSchema_K2::PC_Enum, nullptr, Cast<UUserDefinedEnum>(Obj) != nullptr);
|
||||
}
|
||||
|
||||
if (!Cast<UScriptStruct>(Obj) && !Cast<UClass>(Obj) && !Cast<UEnum>(Obj))
|
||||
return FString();
|
||||
if (Cast<UScriptStruct>(Obj))
|
||||
{
|
||||
return NewShortName(Path, UEdGraphSchema_K2::PC_Struct, nullptr, Cast<UUserDefinedStruct>(Obj) != nullptr);
|
||||
}
|
||||
|
||||
FString ProposedName = GetProposedName(Obj);
|
||||
return ChooseShortName(ProposedName, Obj->GetPathName());
|
||||
if (const UClass* Class = Cast<UClass>(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<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)
|
||||
@@ -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<UWingTypes>();
|
||||
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<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
|
||||
// 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<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
|
||||
{
|
||||
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<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;
|
||||
}
|
||||
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<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>();
|
||||
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<UClass>(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<UClass>(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<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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user