Files
UnrealEngine/Engine/Source/Runtime/UniversalObjectLocator/Private/UniversalObjectLocatorFragment.cpp
2025-05-18 13:04:45 +08:00

771 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UniversalObjectLocatorFragment.h"
#include "Algo/Find.h"
#include "UniversalObjectLocatorFragmentType.h"
#include "UniversalObjectLocatorStringParams.h"
#include "UniversalObjectLocatorInitializeParams.h"
#include "UniversalObjectLocatorInitializeResult.h"
#include "UniversalObjectLocatorFragmentDebugging.h"
#include "UniversalObjectLocatorRegistry.h"
#include "UObject/SoftObjectPath.h"
#include "UObject/UObjectThreadContext.h"
#include "Containers/SparseArray.h"
#include "Logging/MessageLog.h"
#include "Misc/AsciiSet.h"
#include "Misc/UObjectToken.h"
#include "Templates/AlignmentTemplates.h"
#define LOCTEXT_NAMESPACE "UOL"
DECLARE_LOG_CATEGORY_EXTERN(LogUniversalObjectLocator, Log, Log);
DEFINE_LOG_CATEGORY(LogUniversalObjectLocator);
namespace UE::UniversalObjectLocator
{
const FFragmentType* FindBestFragmentType(const UObject* Object, UObject* Context)
{
// Loop through all our FragmentTypes to find the most supported one
uint32 BestFragmentTypePriority = 0;
const FFragmentType* BestFragmentType = nullptr;
for (const FFragmentType& FragmentType : FRegistry::Get().FragmentTypes)
{
const uint32 ThisFragmentTypePriority = FragmentType.ComputePriority(Object, Context);
if (ThisFragmentTypePriority > BestFragmentTypePriority)
{
BestFragmentTypePriority = ThisFragmentTypePriority;
BestFragmentType = &FragmentType;
}
}
if (BestFragmentType && BestFragmentType->PayloadType != nullptr)
{
return BestFragmentType;
}
return nullptr;
}
FFragmentType* FFragmentTypeHandle::Resolve() const
{
return Handle == 0xff ? nullptr : &FRegistry::Get().FragmentTypes[Handle];
}
FFragmentTypeHandle MakeFragmentTypeHandle(const FFragmentType* FragmentType)
{
check(FragmentType);
const uint64 FragmentTypeOffset = static_cast<uint64>(FragmentType - FRegistry::Get().FragmentTypes.GetData());
checkf(FragmentTypeOffset < std::numeric_limits<uint8>::max(), TEXT("Maximum number of UOL FragmentTypes reached"));
return FFragmentTypeHandle(static_cast<uint8>(FragmentTypeOffset));
}
/**
* Compute the size required for the debug header of a fragment with a certain size and alignment constraint
*
* Since our byte array is explicitly aligned to 8 bytes, we can insert the debug header right at the start of
* our bytes without changing the alignment of the proceeding type.
*
* If however our type's requested alignment is greater, we use the alignment itself and allocate the header at
* the tail of that space. For instance, for a 16 byte aligned payload:
* 0.. 8.. 16.. 16 + sizeof(T)
* [ TFragmentPayload<T> | T Payload ]
*
* For a 32 byte aligned payload:
* 0.. 24.. 32.. 32 + sizeof(T)
* [ TFragmentPayload<T> | T Payload ]
*
*/
uint8 ComputeDebugHeaderLog2(size_t Alignment)
{
#if UE_UNIVERSALOBJECTLOCATOR_DEBUG
static_assert(alignof(IFragmentPayload) == 8 && sizeof(IFragmentPayload) == 8, "Unexpected alignment/size of IFragmentPayload!");
const uint32 HeaderCapacityLog2 = FMath::CeilLogTwo((uint32)FMath::Max(Alignment, sizeof(IFragmentPayload)));
// This value is stored in 6 bits of a uint8
// We should never encounter a type aligned > 2^63!
check(HeaderCapacityLog2 <= 63);
return static_cast<uint8>(HeaderCapacityLog2);
#else
return 0;
#endif
}
} // UE::UniversalObjectLocator
FUniversalObjectLocatorFragment::FUniversalObjectLocatorFragment(const UObject* InObject, UObject* Context)
: bIsInitialized(0)
, bIsInline(0)
, DebugHeaderSizeLog2(0)
{
Reset(InObject, Context);
}
FUniversalObjectLocatorFragment::FUniversalObjectLocatorFragment(const UE::UniversalObjectLocator::FFragmentType& InFragmentType)
: FragmentType(MakeFragmentTypeHandle(&InFragmentType))
, bIsInitialized(0)
, bIsInline(0)
, DebugHeaderSizeLog2(0)
{
this->DefaultConstructPayload(InFragmentType);
}
FUniversalObjectLocatorFragment::FUniversalObjectLocatorFragment()
: bIsInitialized(0)
, bIsInline(0)
, DebugHeaderSizeLog2(0)
{
static_assert(sizeof(FUniversalObjectLocatorFragment) == FUniversalObjectLocatorFragment::SizeInMemory, "Unexpected size for FUniversalObjectLocatorFragment");
static_assert(offsetof(FUniversalObjectLocatorFragment, Data) == 0, "FUniversalObjectLocatorFragment inline data is not aligned properly");
}
FUniversalObjectLocatorFragment::~FUniversalObjectLocatorFragment()
{
if (bIsInitialized && !IsEngineExitRequested())
{
DestroyPayload();
}
}
FUniversalObjectLocatorFragment::FUniversalObjectLocatorFragment(const FUniversalObjectLocatorFragment& RHS)
: FragmentType(RHS.FragmentType)
, bIsInitialized(0)
, bIsInline(0)
, DebugHeaderSizeLog2(0)
{
using namespace UE::UniversalObjectLocator;
if (RHS.bIsInitialized)
{
const FFragmentType* ResolvedFragmentType = GetFragmentType();
check(ResolvedFragmentType);
this->DefaultConstructPayload(*ResolvedFragmentType);
ResolvedFragmentType->PayloadType->CopyScriptStruct(this->GetPayload(), RHS.GetPayload());
}
else
{
this->bIsInitialized = false;
}
}
FUniversalObjectLocatorFragment& FUniversalObjectLocatorFragment::operator=(const FUniversalObjectLocatorFragment& RHS)
{
using namespace UE::UniversalObjectLocator;
this->DestroyPayload();
if (RHS.bIsInitialized)
{
const FFragmentType* ResolvedFragmentType = GetFragmentType();
check(ResolvedFragmentType);
// Assign the FragmentType and copy the payload
this->FragmentType = RHS.FragmentType;
this->DefaultConstructPayload(*ResolvedFragmentType);
ResolvedFragmentType->PayloadType->CopyScriptStruct(this->GetPayload(), RHS.GetPayload());
}
else
{
this->bIsInitialized = false;
this->FragmentType = FFragmentTypeHandle();
}
return *this;
}
FUniversalObjectLocatorFragment::FUniversalObjectLocatorFragment(FUniversalObjectLocatorFragment&& RHS)
: FragmentType(RHS.FragmentType)
, bIsInitialized(RHS.bIsInitialized)
, bIsInline(RHS.bIsInline)
, DebugHeaderSizeLog2(RHS.DebugHeaderSizeLog2)
{
using namespace UE::UniversalObjectLocator;
FMemory::Memcpy(this->Data, RHS.Data, sizeof(Data));
RHS.bIsInitialized = false;
RHS.bIsInline = false;
RHS.DebugHeaderSizeLog2 = 0;
RHS.FragmentType = FFragmentTypeHandle();
}
FUniversalObjectLocatorFragment& FUniversalObjectLocatorFragment::operator=(FUniversalObjectLocatorFragment&& RHS)
{
using namespace UE::UniversalObjectLocator;
this->DestroyPayload();
this->bIsInitialized = RHS.bIsInitialized;
this->bIsInline = RHS.bIsInline;
this->DebugHeaderSizeLog2 = RHS.DebugHeaderSizeLog2;
this->FragmentType = RHS.FragmentType;
FMemory::Memcpy(this->Data, RHS.Data, sizeof(Data));
RHS.bIsInitialized = false;
RHS.bIsInline = false;
RHS.DebugHeaderSizeLog2 = 0;
RHS.FragmentType = FFragmentTypeHandle();
return *this;
}
bool operator==(const FUniversalObjectLocatorFragment& A, const FUniversalObjectLocatorFragment& B)
{
using namespace UE::UniversalObjectLocator;
if (A.bIsInitialized != B.bIsInitialized)
{
return false;
}
else if (!A.bIsInitialized)
{
// 2 uninitialized references are the same
return true;
}
else if (A.FragmentType != B.FragmentType)
{
// Different fragment types
return false;
}
else
{
const UScriptStruct* FragmentStruct = A.GetFragmentStruct();
check(FragmentStruct);
// Same fragment types - compare payloads
const void* PayloadA = A.GetPayload();
const void* PayloadB = B.GetPayload();
return FragmentStruct->CompareScriptStruct(PayloadA, PayloadB, 0);
}
}
bool operator!=(const FUniversalObjectLocatorFragment& A, const FUniversalObjectLocatorFragment& B)
{
return !(A == B);
}
uint32 GetTypeHash(const FUniversalObjectLocatorFragment& Fragment)
{
using namespace UE::UniversalObjectLocator;
if (!Fragment.bIsInitialized)
{
return 0;
}
uint32 Hash = GetTypeHash(Fragment.FragmentType);
if (const FFragmentType* FragmentTypePtr = Fragment.GetFragmentType())
{
UScriptStruct* Struct = FragmentTypePtr->GetStruct();
if (Struct)
{
const uint32 PayloadHash = Struct->GetStructTypeHash(Fragment.GetPayload());
Hash = HashCombineFast(Hash, PayloadHash);
}
}
return Hash;
}
#if DO_CHECK
void FUniversalObjectLocatorFragment::CheckPayloadType(UScriptStruct* TypeToCompare) const
{
using namespace UE::UniversalObjectLocator;
const FFragmentType* FragmentTypePtr = GetFragmentType();
checkf(FragmentTypePtr == nullptr || FragmentTypePtr->PayloadType == TypeToCompare,
TEXT("Type mismatch when accessing payload data! Attempting to access a stored %s payload as %s."),
FragmentTypePtr->PayloadType ? *FragmentTypePtr->PayloadType->GetName() : TEXT("<expired>"),
TypeToCompare ? *TypeToCompare->GetName() : TEXT("<nullptr>"));
}
#endif
void FUniversalObjectLocatorFragment::ToString(FStringBuilderBase& OutString) const
{
using namespace UE::UniversalObjectLocator;
const FFragmentType* FragmentTypePtr = FragmentType.Resolve();
if (FragmentTypePtr && FragmentTypePtr->PayloadType)
{
FragmentTypePtr->FragmentTypeID.AppendString(OutString);
TStringBuilder<128> PayloadString;
FragmentTypePtr->ToString(GetPayload(), PayloadString);
if (PayloadString.Len() != 0)
{
OutString += '=';
OutString.Append(PayloadString.ToView());
}
}
}
UE::UniversalObjectLocator::FParseStringResult FUniversalObjectLocatorFragment::TryParseString(FStringView InString, const FParseStringParams& InParams)
{
using namespace UE::UniversalObjectLocator;
if (InString.Len() == 0)
{
Reset();
return FParseStringResult().Success();
}
// Check for a literal "none" text
static constexpr FStringView NoneString = TEXTVIEW("none");
if (InString.Compare(NoneString, ESearchCase::IgnoreCase) == 0)
{
Reset();
return FParseStringResult().Success(NoneString.Len());
}
FStringView FragmentTypeString = InString;
FStringView FragmentPayloadString;
const int32 Delimiter = UE::String::FindFirstChar(InString, '=');
if (Delimiter != INDEX_NONE)
{
// We have a payload
FragmentTypeString = InString.Left(Delimiter);
FragmentPayloadString = InString.RightChop(Delimiter + 1);
if (FragmentTypeString.Len() == 0)
{
return FParseStringResult().Failure(UE_UOL_PARSE_ERROR(InParams, LOCTEXT("Error_UnexpectedEquals", "Unexpected '='' when expecting a fragment type.")));
}
}
FParseStringResult TypeResult = TryParseFragmentType(FragmentTypeString, InParams);
if (!TypeResult)
{
return TypeResult;
}
FParseStringResult PayloadResult = TryParseFragmentPayload(FragmentPayloadString, InParams);
// Add the type chars and = to the total num parsed
PayloadResult.NumCharsParsed += TypeResult.NumCharsParsed + 1;
return PayloadResult;
}
UE::UniversalObjectLocator::FParseStringResult FUniversalObjectLocatorFragment::TryParseFragmentType(FStringView InString, const FParseStringParams& InParams)
{
using namespace UE::UniversalObjectLocator;
if (InString.Len() == 0)
{
return FParseStringResult().Failure(UE_UOL_PARSE_ERROR(InParams, LOCTEXT("Error_EmptyFragmentType", "Fragment type specifier is empty.")));
}
// Check for a literal "none" text
static constexpr FStringView NoneString = TEXTVIEW("none");
if (InString.Compare(NoneString, ESearchCase::IgnoreCase) == 0)
{
Reset();
return FParseStringResult().Success(NoneString.Len());
}
// Try and find the FragmentType as a name
FName FragmentTypeID(InString.Len(), InString.GetData(), FNAME_Find);
if (FragmentTypeID != NAME_None)
{
// Find the FragmentType
const FFragmentType* SerializedFragmentType = FRegistry::Get().FindFragmentType(FragmentTypeID);
if (SerializedFragmentType != nullptr && SerializedFragmentType->PayloadType != nullptr)
{
this->DestroyPayload();
this->FragmentType = MakeFragmentTypeHandle(SerializedFragmentType);
this->DefaultConstructPayload(*SerializedFragmentType);
return FParseStringResult().Success(InString.Len());
}
}
// Not a valid fragment type string
return FParseStringResult().Failure(
UE_UOL_PARSE_ERROR(InParams,
FText::Format(
LOCTEXT("Error_UnknownFragmentType", "Unknown fragment type specifier {0}."),
FText::FromStringView(InString)
)
)
);
}
UE::UniversalObjectLocator::FParseStringResult FUniversalObjectLocatorFragment::TryParseFragmentPayload(FStringView InString, const FParseStringParams& InParams)
{
using namespace UE::UniversalObjectLocator;
if (!bIsInitialized)
{
return FParseStringResult().Failure(UE_UOL_PARSE_ERROR(InParams, LOCTEXT("Error_Uninitialized", "Unable to parse a payload for an uninitialized fragment.")));
}
const FFragmentType* FragmentTypePtr = GetFragmentType();
const UScriptStruct* FragmentStruct = FragmentTypePtr ? FragmentTypePtr->GetStruct() : nullptr;
if (!FragmentStruct)
{
return FParseStringResult().Failure(UE_UOL_PARSE_ERROR(InParams, LOCTEXT("Error_Expired", "Unable to parse a payload for a fragment whose type has expired.")));
}
void* Payload = GetPayload();
if (InString.Len() == 0)
{
// Empty payload string means a default payload
FragmentStruct->ClearScriptStruct(Payload);
return FParseStringResult().Success();
}
return FragmentTypePtr->TryParseString(Payload, InString, InParams);
}
const UE::UniversalObjectLocator::FFragmentType* FUniversalObjectLocatorFragment::GetFragmentType() const
{
return FragmentType.Resolve();
}
UScriptStruct* FUniversalObjectLocatorFragment::GetFragmentStruct() const
{
using namespace UE::UniversalObjectLocator;
if (const FFragmentType* Type = GetFragmentType())
{
return Type->GetStruct();
}
return nullptr;
}
UE::UniversalObjectLocator::FFragmentTypeHandle FUniversalObjectLocatorFragment::GetFragmentTypeHandle() const
{
using namespace UE::UniversalObjectLocator;
const FFragmentType* FragmentTypePtr = GetFragmentType();
if (FragmentTypePtr)
{
return MakeFragmentTypeHandle(FragmentTypePtr);
}
return FFragmentTypeHandle();
}
void FUniversalObjectLocatorFragment::DestroyPayload()
{
using namespace UE::UniversalObjectLocator;
if (!bIsInitialized)
{
return;
}
uint8* Payload = (uint8*)GetPayload();
const UScriptStruct* FragmentStruct = GetFragmentStruct();
if (ensureMsgf(FragmentStruct, TEXT("FUniversalObjectLocatorFragment has outlived its FragmentType's payload type struct! This could leak memory if the type allocated it.")))
{
FragmentStruct->DestroyStruct(Payload);
}
if (!bIsInline)
{
Payload -= GetDebugHeaderOffset();
FMemory::Free(Payload);
}
bIsInitialized = false;
}
void* FUniversalObjectLocatorFragment::GetPayload()
{
check(bIsInitialized);
uint8* Payload = bIsInline ? Data : *((uint8**)Data);
return Payload + GetDebugHeaderOffset();
}
const void* FUniversalObjectLocatorFragment::GetPayload() const
{
check(bIsInitialized);
const uint8* Payload = bIsInline ? Data : *((const uint8* const *)Data);
return Payload + GetDebugHeaderOffset();
}
FUniversalObjectLocatorFragment::FAllocatedPayload FUniversalObjectLocatorFragment::AllocatePayload(size_t Size, size_t Alignment)
{
using namespace UE::UniversalObjectLocator;
check(!bIsInitialized);
bIsInitialized = true;
#if UE_UNIVERSALOBJECTLOCATOR_DEBUG
DebugHeaderSizeLog2 = ComputeDebugHeaderLog2(Alignment);
Alignment = FMath::Max(Alignment, alignof(IFragmentPayload));
Size += GetDebugHeaderOffset();
#else
DebugHeaderSizeLog2 = 0;
#endif
uint8* Payload = nullptr;
if (Size <= sizeof(FUniversalObjectLocatorFragment::Data) && Alignment <= alignof(FUniversalObjectLocatorFragment))
{
// We can placement new this into the payload data
bIsInline = true;
Payload = Data;
}
else
{
// We have to allocate this struct on the heap
Payload = (uint8*)FMemory::Malloc(Size, Alignment);
*reinterpret_cast<void**>(Data) = Payload;
bIsInline = false;
}
return FAllocatedPayload{
#if UE_UNIVERSALOBJECTLOCATOR_DEBUG
Payload + GetDebugHeaderOffset() - sizeof(IFragmentPayload),
#endif
Payload + GetDebugHeaderOffset()
};
}
void FUniversalObjectLocatorFragment::DefaultConstructPayload(const UE::UniversalObjectLocator::FFragmentType& InFragmentType)
{
using namespace UE::UniversalObjectLocator;
const UScriptStruct* PayloadType = InFragmentType.PayloadType.Get();
FAllocatedPayload Allocation = AllocatePayload((size_t)PayloadType->GetStructureSize(), (size_t)PayloadType->GetMinAlignment());
#if UE_UNIVERSALOBJECTLOCATOR_DEBUG
InFragmentType.StaticBindings.FragmentDebugInitializer(Allocation.DebugVFTablePtr);
#endif
PayloadType->InitializeStruct(Allocation.Payload);
}
void FUniversalObjectLocatorFragment::Reset()
{
using namespace UE::UniversalObjectLocator;
DestroyPayload();
FragmentType = FFragmentTypeHandle();
}
void FUniversalObjectLocatorFragment::Reset(const UObject* InObject, UObject* Context)
{
using namespace UE::UniversalObjectLocator;
Reset();
if (const FFragmentType* BestFragmentType = FindBestFragmentType(InObject, Context))
{
FragmentType = MakeFragmentTypeHandle(BestFragmentType);
DefaultConstructPayload(*BestFragmentType);
BestFragmentType->InitializePayload(GetPayload(), FInitializeParams{ InObject, Context });
}
}
void FUniversalObjectLocatorFragment::Reset(const UObject* InObject, UObject* Context, TFunctionRef<bool(UE::UniversalObjectLocator::FFragmentTypeHandle)> CanUseFragmentType)
{
using namespace UE::UniversalObjectLocator;
Reset();
// Loop through all our FragmentTypes to find the most supported one
uint32 BestFragmentTypePriority = 0;
const FFragmentType* BestFragmentType = nullptr;
TArray<FFragmentType>& FragmentTypes = FRegistry::Get().FragmentTypes;
if (!ensure(FragmentTypes.Num() < 255))
{
return;
}
const uint8 Num = static_cast<uint8>(FragmentTypes.Num());
for (uint8 Index = 0; Index < Num; ++Index)
{
if (!CanUseFragmentType(FFragmentTypeHandle(Index)))
{
continue;
}
const FFragmentType& ThisFragmentType = FragmentTypes[Index];
const uint32 ThisFragmentTypePriority = ThisFragmentType.ComputePriority(InObject, Context);
if (ThisFragmentTypePriority > BestFragmentTypePriority)
{
BestFragmentTypePriority = ThisFragmentTypePriority;
BestFragmentType = &ThisFragmentType;
}
}
if (BestFragmentType && BestFragmentType->PayloadType != nullptr)
{
FragmentType = MakeFragmentTypeHandle(BestFragmentType);
DefaultConstructPayload(*BestFragmentType);
BestFragmentType->InitializePayload(GetPayload(), FInitializeParams{ InObject, Context });
}
}
UE::UniversalObjectLocator::FResolveResult FUniversalObjectLocatorFragment::Resolve(const UE::UniversalObjectLocator::FResolveParams& Params) const
{
using namespace UE::UniversalObjectLocator;
// Find our FragmentType
const FFragmentType* FragmentTypePtr = FragmentType.Resolve();
if (FragmentTypePtr && bIsInitialized)
{
return FragmentTypePtr->ResolvePayload(GetPayload(), Params);
}
return FResolveResult();
}
bool FUniversalObjectLocatorFragment::Serialize(FArchive& Ar)
{
using namespace UE::UniversalObjectLocator;
if (Ar.IsLoading())
{
FName FragmentTypeID;
Ar << FragmentTypeID;
if (FragmentTypeID == NAME_None)
{
Reset();
}
else
{
// Find the FragmentType
const FFragmentType* SerializedFragmentType = FRegistry::Get().FindFragmentType(FragmentTypeID);
if (!SerializedFragmentType || !SerializedFragmentType->PayloadType)
{
Reset();
// Big error - what do we do?
FMessageLog Log("UOL");
TSharedRef<FTokenizedMessage> Message = Log.Error(FText::Format(LOCTEXT("DataLossWarning", "WARNING: POTENTIAL DATA LOSS! Universal Object Reference FragmentType {0}! This reference will be lost if re-saved."), FText::FromString(FragmentTypeID.ToString())));
FUObjectSerializeContext* SerializeContext = FUObjectThreadContext::Get().GetSerializeContext();
if (SerializeContext && SerializeContext->SerializedObject)
{
Message->AddToken(FUObjectToken::Create(SerializeContext->SerializedObject));
}
Log.Open(EMessageSeverity::Error);
// Deserialize an empty payload so we don't corrupt the serialization data
FUniversalObjectLocatorEmptyPayload Empty;
FUniversalObjectLocatorEmptyPayload::StaticStruct()->SerializeItem(Ar, &Empty, nullptr);
return true;
}
FragmentType = MakeFragmentTypeHandle(SerializedFragmentType);
UScriptStruct* Struct = SerializedFragmentType->GetStruct();
Ar.Preload(Struct);
DefaultConstructPayload(*SerializedFragmentType);
Struct->SerializeItem(Ar, GetPayload(), nullptr);
}
}
else if (Ar.IsSaving() || Ar.IsTransacting())
{
const FFragmentType* FragmentTypePtr = FragmentType.Resolve();
if (FragmentTypePtr == nullptr)
{
FName None;
// FragmentType ID
Ar << None;
}
else
{
FName FragmentTypeID = FragmentTypePtr->FragmentTypeID;
// FragmentType ID
Ar << FragmentTypeID;
// FragmentType payload
FragmentTypePtr->GetStruct()->SerializeItem(Ar, GetPayload(), nullptr);
}
}
else if (Ar.IsModifyingWeakAndStrongReferences())
{
UScriptStruct* FragmentStruct = GetFragmentStruct();
if (FragmentStruct && bIsInitialized)
{
FragmentStruct->SerializeItem(Ar, GetPayload(), nullptr);
}
}
return true;
}
void FUniversalObjectLocatorFragment::AddStructReferencedObjects(FReferenceCollector& Collector)
{
using namespace UE::UniversalObjectLocator;
if (FFragmentType* FragmentTypePtr = FragmentType.Resolve())
{
Collector.AddReferencedObject(FragmentTypePtr->PayloadType);
if (bIsInitialized && FragmentTypePtr->PayloadType)
{
Collector.AddReferencedObjects(FragmentTypePtr->PayloadType, GetPayload());
}
}
}
bool FUniversalObjectLocatorFragment::ExportTextItem(FString& ValueStr, const FUniversalObjectLocatorFragment& DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const
{
TStringBuilder<32> PayloadString;
ToString(PayloadString);
ValueStr.AppendChar('(');
ValueStr.Append(PayloadString.ToString(), PayloadString.Len());
ValueStr.AppendChar(')');
return true;
}
bool FUniversalObjectLocatorFragment::ImportTextItem(const TCHAR*& Buffer, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText, FArchive* InSerializingArchive)
{
using namespace UE::UniversalObjectLocator;
if (Buffer && *Buffer == '(')
{
const TCHAR* BufferEnd = FCString::Strchr(Buffer, ')');
if (Buffer != BufferEnd && (BufferEnd - Buffer) < std::numeric_limits<int32>::max())
{
FStringView View(Buffer+1, int32(BufferEnd-Buffer)-1);
if (TryParseString(View, FParseStringParams()))
{
return true;
}
}
}
return false;
}
bool FUniversalObjectLocatorFragment::SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot)
{
return false;
}
void FUniversalObjectLocatorFragment::GetPreloadDependencies(TArray<UObject*>& OutDeps)
{
}
#undef LOCTEXT_NAMESPACE