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

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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();
}