EM_Task/CoreUObject/Private/UObject/Field.cpp

1173 lines
30 KiB
C++
Raw Permalink Normal View History

2026-02-13 16:18:33 +08:00
// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Field.cpp: Defines FField property system fundamentals
=============================================================================*/
#include "UObject/Field.h"
#include "UObject/Class.h"
#include "HAL/ThreadSafeBool.h"
#include "Misc/ScopeLock.h"
#include "Misc/StringBuilder.h"
#include "Serialization/MemoryWriter.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/OutputDeviceHelper.h"
#include "Misc/FeedbackContext.h"
#include "Misc/OutputDeviceConsole.h"
#include "UObject/ErrorException.h"
#include "Modules/ModuleManager.h"
#include "UObject/UObjectAllocator.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "UObject/Package.h"
#include "UObject/MetaData.h"
#include "Templates/Casts.h"
#include "UObject/DebugSerializationFlags.h"
#include "UObject/PropertyTag.h"
#include "UObject/UnrealType.h"
#include "UObject/UnrealTypePrivate.h"
#include "UObject/Stack.h"
#include "Misc/PackageName.h"
#include "UObject/ObjectResource.h"
#include "UObject/LinkerSave.h"
#include "UObject/Interface.h"
#include "Misc/HotReloadInterface.h"
#include "UObject/LinkerPlaceholderClass.h"
#include "UObject/LinkerPlaceholderFunction.h"
#include "UObject/StructScriptLoader.h"
#include "UObject/PropertyHelper.h"
#include "UObject/CoreRedirects.h"
#include "Serialization/ArchiveScriptReferenceCollector.h"
#include "UObject/FrameworkObjectVersion.h"
#include "UObject/WeakFieldPtr.h"
#include "Templates/SubclassOf.h"
#include "UObject/TextProperty.h"
#include "UObject/EnumProperty.h"
// WARNING: This should always be the last include in any file that needs it (except .generated.h)
#include "UObject/UndefineUPropertyMacros.h"
FFieldClass::FFieldClass(const TCHAR* InCPPName, uint64 InId, uint64 InCastFlags, FFieldClass* InSuperClass, FField* (*ConstructFnPtr)(const FFieldVariant&, const FName&, EObjectFlags))
: Id(InId), CastFlags(InCastFlags), ClassFlags(CLASS_None), SuperClass(InSuperClass), DefaultObject(nullptr), ConstructFn(ConstructFnPtr)
{
check(InCPPName);
// Skip the 'F' prefix for the name
check(InCPPName[0] == 'F');
Name = ++InCPPName;
GetAllFieldClasses().Add(this);
GetNameToFieldClassMap().Add(Name, this);
}
FFieldClass::~FFieldClass()
{
delete DefaultObject;
DefaultObject = nullptr;
}
TArray<FFieldClass*>& FFieldClass::GetAllFieldClasses()
{
static TArray<FFieldClass*> AllClasses;
return AllClasses;
}
TMap<FName, FFieldClass*>& FFieldClass::GetNameToFieldClassMap()
{
static TMap<FName, FFieldClass*> NameToFieldClassMap;
return NameToFieldClassMap;
}
FField* FFieldClass::ConstructDefaultObject()
{
FField* NewDefault = Construct(UClass::StaticClass()->GetOutermost(), *FString::Printf(TEXT("Default__%s"), *GetName()), RF_Transient | RF_ClassDefaultObject);
return NewDefault;
}
FString FFieldClass::GetDescription() const
{
return GetName();
}
FText FFieldClass::GetDisplayNameText() const
{
return FText::FromString(GetName());
}
FArchive& operator<<(FArchive& Ar, FFieldClass*& InOutFieldClass)
{
FName ClassName = InOutFieldClass ? InOutFieldClass->GetFName() : NAME_None;
Ar << ClassName;
if (Ar.IsLoading())
{
if (ClassName != NAME_None)
{
InOutFieldClass = FFieldClass::GetNameToFieldClassMap().FindRef(ClassName);
}
else
{
InOutFieldClass = nullptr;
}
}
return Ar;
}
FFieldVariant FFieldVariant::GetOwnerVariant() const
{
if (bIsUObject)
{
return Container.Object->GetOuter();
}
else
{
return Container.Field->GetOwnerVariant();
}
}
bool FFieldVariant::IsA(const UClass* InClass) const
{
return bIsUObject && Container.Object && Container.Object->IsA(InClass);
}
bool FFieldVariant::IsA(const FFieldClass* InClass) const
{
return !bIsUObject && Container.Field && Container.Field->IsA(InClass);
}
UClass* FFieldVariant::GetOwnerClass() const
{
check(Container.Object);
if (bIsUObject)
{
return CastChecked<UField>(Container.Object)->GetOwnerClass();
}
else
{
return Container.Field->GetOwnerClass();
}
}
FString FFieldVariant::GetFullName() const
{
if (bIsUObject)
{
return Container.Object->GetFullName();
}
else
{
return Container.Field->GetFullName();
}
}
FString FFieldVariant::GetPathName() const
{
if (bIsUObject)
{
return Container.Object->GetPathName();
}
else
{
return Container.Field->GetPathName();
}
}
FString FFieldVariant::GetName() const
{
if (bIsUObject)
{
return Container.Object->GetName();
}
else
{
return Container.Field->GetName();
}
}
FName FFieldVariant::GetFName() const
{
if (bIsUObject)
{
return Container.Object->GetFName();
}
else
{
return Container.Field->GetFName();
}
}
FString FFieldVariant::GetClassName() const
{
check(Container.Object);
if (bIsUObject)
{
return Container.Object->GetClass()->GetName();
}
else
{
return Container.Field->GetClass()->GetName();
}
}
bool FFieldVariant::IsNative() const
{
check(Container.Object);
if (bIsUObject)
{
return Container.Object->IsNative();
}
else
{
return Container.Field->IsNative();
}
}
UPackage* FFieldVariant::GetOutermost() const
{
check(Container.Object);
if (bIsUObject)
{
return Container.Object->GetOutermost();
}
else
{
return Container.Field->GetOutermost();
}
}
bool FFieldVariant::IsValidLowLevel() const
{
check(Container.Object);
if (bIsUObject)
{
return Container.Object->IsValidLowLevel();
}
else
{
return !!Container.Field;
}
}
#if WITH_EDITORONLY_DATA
bool FFieldVariant::HasMetaData(const FName& Key) const
{
check(Container.Object);
if (bIsUObject)
{
return CastChecked<UField>(Container.Object)->HasMetaData(Key);
}
else
{
return Container.Field->HasMetaData(Key);
}
}
#endif // WITH_EDITORONLY_DATA
FArchive& operator<<(FArchive& Ar, FFieldVariant& InOutField)
{
Ar << InOutField.bIsUObject;
if (InOutField.bIsUObject)
{
Ar << InOutField.Container.Object;
}
else
{
TFieldPath<FField> FieldRef(InOutField.Container.Field);
Ar << FieldRef;
if (Ar.IsLoading())
{
InOutField.Container.Field = FieldRef.Get();
}
}
return Ar;
}
/*-----------------------------------------------------------------------------
FField implementation.
-----------------------------------------------------------------------------*/
FField* FField::Construct(const FFieldVariant& InOwner, const FName& InName, EObjectFlags InFlags)
{
// Can't construct an abstract type
return nullptr;
}
FFieldClass* FField::StaticClass()
{
static FFieldClass StaticFieldClass(TEXT("FField"), FField::StaticClassCastFlagsPrivate(), FField::StaticClassCastFlags(), nullptr, &FField::Construct);
return &StaticFieldClass;
}
FField::FField(EInternal InInernal, FFieldClass* InClass)
: ClassPrivate(InClass), Owner((FField*)nullptr), Next(nullptr), FlagsPrivate(RF_NoFlags)
#if WITH_EDITORONLY_DATA
,
MetaDataMap(nullptr)
#endif // WITH_EDITORONLY_DATA
{
}
FField::FField(FFieldVariant InOwner, const FName& InName, EObjectFlags InObjectFlags)
: Owner(InOwner), Next(nullptr), NamePrivate(InName), FlagsPrivate(InObjectFlags)
#if WITH_EDITORONLY_DATA
,
MetaDataMap(nullptr)
#endif // WITH_EDITORONLY_DATA
{
}
FField::~FField()
{
#if WITH_EDITORONLY_DATA
if (MetaDataMap)
{
delete MetaDataMap;
MetaDataMap = nullptr;
}
#endif // WITH_EDITORONLY_DATA
}
#if WITH_EDITORONLY_DATA
FField::FField(UField* InField)
: Next(nullptr), NamePrivate(InField->GetFName()), FlagsPrivate(RF_NoFlags), MetaDataMap(nullptr)
{
check(InField);
if (InField->HasAnyFlags(RF_NeedLoad))
{
// The source UField needs to be loaded, otherwise we'll be copying default property values
InField->GetLinker()->Preload(InField);
}
FlagsPrivate = InField->GetFlags();
// Associate this FField with the UField we're constructing from so that next time something tries to convert it,
// it can already grab the cached new FField
InField->SetAssociatedFField(this);
UObject* OriginalOuter = InField->GetOuter();
if (UProperty* OuterProperty = Cast<UProperty>(OriginalOuter))
{
FField* NewOwnerField = OuterProperty->GetAssociatedFField();
if (!NewOwnerField)
{
NewOwnerField = CreateFromUField(OuterProperty);
OuterProperty->SetAssociatedFField(NewOwnerField);
}
Owner = NewOwnerField;
}
else
{
Owner = OriginalOuter;
}
TMap<FName, FString>* FeldMetaDataMap = UMetaData::GetMapForObject(InField);
if (FeldMetaDataMap && FeldMetaDataMap->Num())
{
MetaDataMap = new TMap<FName, FString>(*FeldMetaDataMap);
}
}
#endif // WITH_EDITORONLY_DATA
UClass* FField::GetOwnerClass() const
{
UField* OwnerUField = GetOwnerUField();
if (OwnerUField)
{
UClass* OwnerClass = Cast<UClass>(OwnerUField);
if (OwnerClass)
{
return OwnerClass;
}
else
{
return OwnerUField->GetOwnerClass();
}
}
else
{
return nullptr;
}
}
UStruct* FField::GetOwnerStruct() const
{
UObject* Obj = GetOwnerUObject();
do
{
if (UStruct* Result = Cast<UStruct>(Obj))
{
return Result;
}
Obj = Obj->GetOuter();
} while (Obj);
return nullptr;
}
UField* FField::GetOwnerUField() const
{
UObject* Obj = GetOwnerUObject();
return CastChecked<UField>(Obj);
}
UPackage* FField::GetOutermost() const
{
UObject* OwnerUObject = GetOwnerUObject();
check(OwnerUObject);
return OwnerUObject->GetOutermost();
}
void FField::Bind()
{
}
void FField::PostLoad()
{
Bind();
}
void FField::Serialize(FArchive& Ar)
{
Ar << NamePrivate;
Ar << (uint32&)FlagsPrivate;
#if WITH_EDITORONLY_DATA
if (!Ar.IsCooking())
{
UPackage* Package = GetOutermost();
if (!Package || !Package->bIsCookedForEditor)
{
bool bHasMetaData = false;
if (Ar.IsLoading())
{
Ar << bHasMetaData;
}
else
{
bHasMetaData = MetaDataMap && MetaDataMap->Num();
Ar << bHasMetaData;
}
if (bHasMetaData)
{
if (!MetaDataMap)
{
MetaDataMap = new TMap<FName, FString>();
}
Ar << *MetaDataMap;
}
}
}
#endif
}
void FField::GetPreloadDependencies(TArray<UObject*>& OutDeps)
{
}
void FField::BeginDestroy()
{
}
void FField::AddReferencedObjects(FReferenceCollector& Collector)
{
UObject* UObjectOwner = GetOwnerUObject();
if (UObjectOwner)
{
Collector.AddReferencedObject(UObjectOwner);
}
}
bool FField::IsRooted() const
{
bool bIsRooted = false;
UObject* OwnerObject = GetOwnerUObject();
while (OwnerObject)
{
if (OwnerObject->IsRooted())
{
bIsRooted = true;
break;
}
else
{
OwnerObject = OwnerObject->GetOuter();
}
}
return bIsRooted;
}
bool FField::IsNative() const
{
UObject* OwnerObject = GetOwnerUObject();
if (OwnerObject)
{
return OwnerObject->IsNative();
}
check(false); // we shouldn't ever get here but if we do it's fine to remove this check
return true;
}
bool FField::IsValidLowLevel() const
{
return this != nullptr;
}
bool FField::IsIn(const UObject* InOwner) const
{
check(InOwner);
UObject* OwnerObject = GetOwnerUObject();
if (OwnerObject)
{
if (OwnerObject == InOwner)
{
return true;
}
else
{
return OwnerObject->IsIn(InOwner);
}
}
return false;
}
bool FField::IsIn(const FField* InOwner) const
{
for (FField* OwnerField = GetOwner<FField>(); OwnerField; OwnerField = OwnerField->GetOwner<FField>())
{
if (OwnerField == InOwner)
{
return true;
}
}
return false;
}
FLinkerLoad* FField::GetLinker() const
{
UObject* OwnerObject = GetOwnerUObject();
return OwnerObject ? OwnerObject->GetLinker() : nullptr;
}
void FField::AddCppProperty(FProperty* Property)
{
UE_LOG(LogClass, Fatal, TEXT("FField::AddCppProperty"));
}
FString FField::GetPathName(const UObject* StopOuter /*= nullptr*/) const
{
TStringBuilder<256> ResultString;
GetPathName(StopOuter, ResultString);
return FString(FStringView(ResultString));
}
void FField::GetPathName(const UObject* StopOuter, FStringBuilderBase& ResultString) const
{
TArray<FName, TInlineAllocator<16>> ParentFields;
for (FFieldVariant TempOwner = Owner; TempOwner.IsValid(); TempOwner = TempOwner.GetOwnerVariant())
{
if (!TempOwner.IsUObject())
{
FField* FieldOwner = TempOwner.ToField();
ParentFields.Add(FieldOwner->GetFName());
}
else
{
UObject* ObjectOwner = TempOwner.ToUObject();
ObjectOwner->GetPathName(StopOuter, ResultString);
ResultString << SUBOBJECT_DELIMITER_CHAR;
break;
}
}
for (int FieldIndex = ParentFields.Num() - 1; FieldIndex >= 0; --FieldIndex)
{
ParentFields[FieldIndex].AppendString(ResultString);
ResultString << TEXT(".");
}
GetFName().AppendString(ResultString);
}
FString FField::GetFullName() const
{
FString FullName = GetClass()->GetName();
FullName += TEXT(" ");
FullName += GetPathName();
return FullName;
}
UObject* FField::GetTypedOwner(UClass* Target) const
{
UObject* Result = nullptr;
for (UObject* NextOuter = GetOwnerUObject(); Result == nullptr && NextOuter != nullptr; NextOuter = NextOuter->GetOuter())
{
if (NextOuter->IsA(Target))
{
Result = NextOuter;
}
}
return Result;
}
FString FField::GetAuthoredName() const
{
UStruct* Struct = GetOwnerStruct();
if (Struct)
{
return Struct->GetAuthoredNameForField(this);
}
return FString();
}
void FField::Rename(const FName& NewName)
{
NamePrivate = NewName;
// @todo: What about FFieldPath now?
}
FField* FField::GetTypedOwner(FFieldClass* Target) const
{
FField* Result = nullptr;
for (FField* NextOuter = GetOwner<FField>(); Result == nullptr && NextOuter != nullptr; NextOuter = NextOuter->GetOwner<FField>())
{
if (NextOuter->IsA(Target))
{
Result = NextOuter;
}
}
return Result;
}
#if WITH_EDITORONLY_DATA
FString FField::GetFullGroupName(bool bStartWithOuter) const
{
if (bStartWithOuter)
{
if (Owner.IsValid())
{
if (Owner.IsUObject())
{
return Owner.ToUObject()->GetPathName(Owner.ToUObject()->GetOutermost());
}
else
{
return Owner.ToField()->GetPathName(GetOutermost());
}
}
else
{
return FString();
}
}
else
{
return GetPathName(GetOutermost());
}
}
struct FFieldDisplayNameHelper
{
static FString Get(const FField& Object)
{
if (const FProperty* Property = CastField<FProperty>(&Object))
{
if (auto OwnerStruct = Property->GetOwnerStruct())
{
return OwnerStruct->GetAuthoredNameForField(Property);
}
}
return Object.GetName();
}
};
/**
* Finds the localized display name or native display name as a fallback.
*
* @return The display name for this object.
*/
FText FField::GetDisplayNameText() const
{
FText LocalizedDisplayName;
static const FString Namespace = TEXT("UObjectDisplayNames");
static const FName NAME_DisplayName(TEXT("DisplayName"));
const FString Key = GetFullGroupName(false);
FString NativeDisplayName;
if (const FString* FoundMetaData = FindMetaData(NAME_DisplayName))
{
NativeDisplayName = *FoundMetaData;
}
else
{
NativeDisplayName = FName::NameToDisplayString(FFieldDisplayNameHelper::Get(*this), IsA<FBoolProperty>());
}
if (!(FText::FindText(Namespace, Key, /*OUT*/ LocalizedDisplayName, &NativeDisplayName)))
{
LocalizedDisplayName = FText::FromString(NativeDisplayName);
}
return LocalizedDisplayName;
}
/**
* Finds the localized tooltip or native tooltip as a fallback.
*
* @return The tooltip for this object.
*/
FText FField::GetToolTipText(bool bShortTooltip) const
{
bool bFoundShortTooltip = false;
static const FName NAME_Tooltip(TEXT("Tooltip"));
static const FName NAME_ShortTooltip(TEXT("ShortTooltip"));
FText LocalizedToolTip;
FString NativeToolTip;
if (bShortTooltip)
{
NativeToolTip = GetMetaData(NAME_ShortTooltip);
if (NativeToolTip.IsEmpty())
{
NativeToolTip = GetMetaData(NAME_Tooltip);
}
else
{
bFoundShortTooltip = true;
}
}
else
{
NativeToolTip = GetMetaData(NAME_Tooltip);
}
const FString Namespace = bFoundShortTooltip ? TEXT("UObjectShortTooltips") : TEXT("UObjectToolTips");
const FString Key = GetFullGroupName(false);
if (!FText::FindText(Namespace, Key, /*OUT*/ LocalizedToolTip, &NativeToolTip))
{
if (NativeToolTip.IsEmpty())
{
NativeToolTip = FName::NameToDisplayString(FFieldDisplayNameHelper::Get(*this), IsA<FBoolProperty>());
}
else
{
static const FString DoxygenSee(TEXT("@see"));
static const FString TooltipSee(TEXT("See:"));
if (NativeToolTip.ReplaceInline(*DoxygenSee, *TooltipSee) > 0)
{
NativeToolTip.TrimEndInline();
}
}
LocalizedToolTip = FText::FromString(NativeToolTip);
}
return LocalizedToolTip;
}
const FString* FField::FindMetaData(const TCHAR* Key) const
{
return FindMetaData(FName(Key, FNAME_Find));
}
const FString* FField::FindMetaData(const FName& Key) const
{
return (MetaDataMap ? MetaDataMap->Find(Key) : nullptr);
}
/**
* Find the metadata value associated with the key
*
* @param Key The key to lookup in the metadata
* @return The value associated with the key
*/
const FString& FField::GetMetaData(const TCHAR* Key) const
{
return GetMetaData(FName(Key, FNAME_Find));
}
const FString& FField::GetMetaData(const FName& Key) const
{
// if not found, return a static empty string
static FString EmptyString;
// every key needs to be valid and meta data needs to exist
if (Key == NAME_None || !MetaDataMap)
{
return EmptyString;
}
// look for the property
const FString* ValuePtr = MetaDataMap->Find(Key);
// if we didn't find it, return NULL
if (!ValuePtr)
{
return EmptyString;
}
// if we found it, return the pointer to the character data
return *ValuePtr;
}
const FText FField::GetMetaDataText(const TCHAR* MetaDataKey, const FString LocalizationNamespace, const FString LocalizationKey) const
{
FString DefaultMetaData;
if (const FString* FoundMetaData = FindMetaData(MetaDataKey))
{
DefaultMetaData = *FoundMetaData;
}
// If attempting to grab the DisplayName metadata, we must correct the source string and output it as a DisplayString for lookup
if (DefaultMetaData.IsEmpty() && FString(MetaDataKey) == TEXT("DisplayName"))
{
DefaultMetaData = FName::NameToDisplayString(GetName(), IsA(FBoolProperty::StaticClass()));
}
FText LocalizedMetaData;
if (!(FText::FindText(LocalizationNamespace, LocalizationKey, /*OUT*/ LocalizedMetaData, &DefaultMetaData)))
{
if (!DefaultMetaData.IsEmpty())
{
LocalizedMetaData = FText::AsCultureInvariant(DefaultMetaData);
}
}
return LocalizedMetaData;
}
const FText FField::GetMetaDataText(const FName& MetaDataKey, const FString LocalizationNamespace, const FString LocalizationKey) const
{
FString DefaultMetaData;
if (const FString* FoundMetaData = FindMetaData(MetaDataKey))
{
DefaultMetaData = *FoundMetaData;
}
// If attempting to grab the DisplayName metadata, we must correct the source string and output it as a DisplayString for lookup
if (DefaultMetaData.IsEmpty() && MetaDataKey == TEXT("DisplayName"))
{
DefaultMetaData = FName::NameToDisplayString(GetName(), IsA(FBoolProperty::StaticClass()));
}
FText LocalizedMetaData;
if (!(FText::FindText(LocalizationNamespace, LocalizationKey, /*OUT*/ LocalizedMetaData, &DefaultMetaData)))
{
if (!DefaultMetaData.IsEmpty())
{
LocalizedMetaData = FText::AsCultureInvariant(DefaultMetaData);
}
}
return LocalizedMetaData;
}
/**
* Sets the metadata value associated with the key
*
* @param Key The key to lookup in the metadata
* @return The value associated with the key
*/
void FField::SetMetaData(const TCHAR* Key, const TCHAR* InValue)
{
SetMetaData(FName(Key), FString(InValue));
}
void FField::SetMetaData(const TCHAR* Key, FString&& InValue)
{
SetMetaData(FName(Key), MoveTemp(InValue));
}
void FField::SetMetaData(const FName& Key, const TCHAR* InValue)
{
SetMetaData(Key, FString(InValue));
}
void FField::SetMetaData(const FName& Key, FString&& InValue)
{
check(Key != NAME_None);
if (!MetaDataMap)
{
MetaDataMap = new TMap<FName, FString>();
}
MetaDataMap->Add(Key, MoveTemp(InValue));
}
UClass* FField::GetClassMetaData(const TCHAR* Key) const
{
const FString& ClassName = GetMetaData(Key);
UClass* const FoundObject = FindObject<UClass>(ANY_PACKAGE, *ClassName);
return FoundObject;
}
UClass* FField::GetClassMetaData(const FName& Key) const
{
const FString& ClassName = GetMetaData(Key);
UClass* const FoundObject = FindObject<UClass>(ANY_PACKAGE, *ClassName);
return FoundObject;
}
void FField::RemoveMetaData(const TCHAR* Key)
{
RemoveMetaData(FName(Key));
}
void FField::RemoveMetaData(const FName& Key)
{
check(Key != NAME_None);
if (MetaDataMap)
{
MetaDataMap->Remove(Key);
}
}
const TMap<FName, FString>* FField::GetMetaDataMap() const
{
return MetaDataMap;
}
void FField::CopyMetaData(const FField* InSourceField, FField* InDestField)
{
check(InSourceField);
check(InDestField);
if (InSourceField->MetaDataMap)
{
if (!InDestField->MetaDataMap)
{
InDestField->MetaDataMap = new TMap<FName, FString>();
}
*InDestField->MetaDataMap = *InSourceField->MetaDataMap;
}
else if (InDestField->MetaDataMap)
{
delete InDestField->MetaDataMap;
InDestField->MetaDataMap = nullptr;
}
}
#endif // WITH_EDITORONLY_DATA
void FField::PostDuplicate(const FField& InField)
{
}
FField* FField::Duplicate(const FField* InField, FFieldVariant DestOwner, const FName DestName, EObjectFlags FlagMask, EInternalObjectFlags InternalFlagsMask)
{
check(InField);
FField* NewField = InField->GetClass()->Construct(DestOwner, DestName == NAME_None ? InField->GetFName() : DestName, InField->GetFlags() & FlagMask);
NewField->PostDuplicate(*InField);
return NewField;
}
FField* FField::Construct(const FName& FieldTypeName, const FFieldVariant& InOwner, const FName& InName, EObjectFlags InFlags)
{
FFieldClass** FieldClassPtr = FFieldClass::GetNameToFieldClassMap().Find(FieldTypeName);
checkf(FieldClassPtr, TEXT("Field type %s does not exist"), *FieldTypeName.ToString());
FField* Instance = (*FieldClassPtr)->Construct(InOwner, InName, InFlags);
return Instance;
}
FName FField::GenerateFFieldName(FFieldVariant InOwner /** Unused yet */, FFieldClass* InClass)
{
check(InClass);
return FName(*InClass->GetName(), InClass->GetNextUniqueNameIndex());
}
#if WITH_EDITORONLY_DATA
FField::FOnConvertCustomUFieldToFField& FField::GetConvertCustomUFieldToFFieldDelegate()
{
static FOnConvertCustomUFieldToFField ConvertCustomUFieldToFFieldDelegate;
return ConvertCustomUFieldToFFieldDelegate;
}
FField* FField::CreateFromUField(UField* InField)
{
FField* NewField = nullptr;
UClass* UFieldClass = InField->GetClass();
if (UFieldClass == UByteProperty::StaticClass())
{
NewField = new FByteProperty(InField);
}
else if (UFieldClass == UInt8Property::StaticClass())
{
NewField = new FInt8Property(InField);
}
else if (UFieldClass == UInt16Property::StaticClass())
{
NewField = new FInt16Property(InField);
}
else if (UFieldClass == UIntProperty::StaticClass())
{
NewField = new FIntProperty(InField);
}
else if (UFieldClass == UInt64Property::StaticClass())
{
NewField = new FInt64Property(InField);
}
else if (UFieldClass == UUInt16Property::StaticClass())
{
NewField = new FUInt16Property(InField);
}
else if (UFieldClass == UUInt32Property::StaticClass())
{
NewField = new FUInt32Property(InField);
}
else if (UFieldClass == UUInt64Property::StaticClass())
{
NewField = new FUInt64Property(InField);
}
else if (UFieldClass == UFloatProperty::StaticClass())
{
NewField = new FFloatProperty(InField);
}
else if (UFieldClass == UDoubleProperty::StaticClass())
{
NewField = new FDoubleProperty(InField);
}
else if (UFieldClass == UBoolProperty::StaticClass())
{
NewField = new FBoolProperty(InField);
}
else if (UFieldClass == UObjectProperty::StaticClass())
{
NewField = new FObjectProperty(InField);
}
else if (UFieldClass == UWeakObjectProperty::StaticClass())
{
NewField = new FWeakObjectProperty(InField);
}
else if (UFieldClass == ULazyObjectProperty::StaticClass())
{
NewField = new FLazyObjectProperty(InField);
}
else if (UFieldClass == USoftObjectProperty::StaticClass())
{
NewField = new FSoftObjectProperty(InField);
}
else if (UFieldClass == UClassProperty::StaticClass())
{
NewField = new FClassProperty(InField);
}
else if (UFieldClass == USoftClassProperty::StaticClass())
{
NewField = new FSoftClassProperty(InField);
}
else if (UFieldClass == UInterfaceProperty::StaticClass())
{
NewField = new FInterfaceProperty(InField);
}
else if (UFieldClass == UNameProperty::StaticClass())
{
NewField = new FNameProperty(InField);
}
else if (UFieldClass == UStrProperty::StaticClass())
{
NewField = new FStrProperty(InField);
}
else if (UFieldClass == UArrayProperty::StaticClass())
{
NewField = new FArrayProperty(InField);
}
else if (UFieldClass == UMapProperty::StaticClass())
{
NewField = new FMapProperty(InField);
}
else if (UFieldClass == USetProperty::StaticClass())
{
NewField = new FSetProperty(InField);
}
else if (UFieldClass == UStructProperty::StaticClass())
{
NewField = new FStructProperty(InField);
}
else if (UFieldClass == UDelegateProperty::StaticClass())
{
NewField = new FDelegateProperty(InField);
}
else if (UFieldClass == UMulticastInlineDelegateProperty::StaticClass())
{
NewField = new FMulticastInlineDelegateProperty(InField);
}
else if (UFieldClass == UMulticastSparseDelegateProperty::StaticClass())
{
NewField = new FMulticastSparseDelegateProperty(InField);
}
else if (UFieldClass == UEnumProperty::StaticClass())
{
NewField = new FEnumProperty(InField);
}
else if (UFieldClass == UTextProperty::StaticClass())
{
NewField = new FTextProperty(InField);
}
else
{
FFieldClass** FieldClassPtr = FFieldClass::GetNameToFieldClassMap().Find(UFieldClass->GetFName());
checkf(FieldClassPtr, TEXT("Cannot create an FField from %s. The class is either abstract or not implemented."), *InField->GetFullName());
GetConvertCustomUFieldToFFieldDelegate().Broadcast(*FieldClassPtr, InField, NewField);
checkf(NewField, TEXT("Cannot create an FField from %s. The class conversion function is not implemented or not bound to FField::GetConvertCustomUFieldToFField() delegate."), *InField->GetFullName());
}
return NewField;
}
#endif // WITH_EDITORONLY_DATA
FString GetFullNameSafe(const FField* InField)
{
if (InField)
{
return InField->GetFullName();
}
else
{
return TEXT("none");
}
}
FString GetPathNameSafe(const FField* InField)
{
if (InField)
{
return InField->GetPathName();
}
else
{
return TEXT("none");
}
}
FField* FindFPropertyByPath(const TCHAR* InFieldPath)
{
// Expected format: FullPackageName.OwnerName:Field
FField* FoundField = nullptr;
TCHAR PathBuffer[NAME_SIZE];
int32 LastSubobjectDelimiterIndex = -1;
for (int32 Index = 0; InFieldPath[Index] != '\0'; ++Index)
{
if (InFieldPath[Index] == SUBOBJECT_DELIMITER_CHAR)
{
LastSubobjectDelimiterIndex = Index;
}
}
if (LastSubobjectDelimiterIndex >= 0)
{
// Get the UObject part
FCString::Strncpy(PathBuffer, InFieldPath, LastSubobjectDelimiterIndex + 1);
// And the FField part
InFieldPath += (LastSubobjectDelimiterIndex + 1);
UStruct* Owner = FindObject<UStruct>(ANY_PACKAGE, PathBuffer);
if (Owner)
{
#if DO_CHECK
for (const TCHAR* TestChar = InFieldPath; *TestChar != '\0'; TestChar++)
{
checkf(*TestChar != ':' && *TestChar != '.', TEXT("FindFieldByPath can't resolve nested properties: %s"), InFieldPath);
}
#endif
FoundField = FindFProperty<FField>(Owner, InFieldPath);
}
}
return FoundField;
}
#include "UObject/DefineUPropertyMacros.h"