239 lines
8.7 KiB
C++
239 lines
8.7 KiB
C++
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
|
||
|
|
#include "CoreMinimal.h"
|
||
|
|
#include "UObject/ObjectMacros.h"
|
||
|
|
#include "UObject/SoftObjectPtr.h"
|
||
|
|
#include "UObject/PropertyPortFlags.h"
|
||
|
|
#include "UObject/UnrealType.h"
|
||
|
|
#include "UObject/LinkerLoad.h"
|
||
|
|
|
||
|
|
/*-----------------------------------------------------------------------------
|
||
|
|
FSoftObjectProperty.
|
||
|
|
-----------------------------------------------------------------------------*/
|
||
|
|
IMPLEMENT_FIELD(FSoftObjectProperty)
|
||
|
|
|
||
|
|
FString FSoftObjectProperty::GetCPPTypeCustom(FString* ExtendedTypeText, uint32 CPPExportFlags, const FString& InnerNativeTypeName) const
|
||
|
|
{
|
||
|
|
ensure(!InnerNativeTypeName.IsEmpty());
|
||
|
|
return FString::Printf(TEXT("TSoftObjectPtr<%s>"), *InnerNativeTypeName);
|
||
|
|
}
|
||
|
|
FString FSoftObjectProperty::GetCPPMacroType(FString& ExtendedTypeText) const
|
||
|
|
{
|
||
|
|
ExtendedTypeText = FString::Printf(TEXT("TSoftObjectPtr<%s%s>"), PropertyClass->GetPrefixCPP(), *PropertyClass->GetName());
|
||
|
|
return TEXT("SOFTOBJECT");
|
||
|
|
}
|
||
|
|
|
||
|
|
FString FSoftObjectProperty::GetCPPTypeForwardDeclaration() const
|
||
|
|
{
|
||
|
|
return FString::Printf(TEXT("class %s%s;"), PropertyClass->GetPrefixCPP(), *PropertyClass->GetName());
|
||
|
|
}
|
||
|
|
|
||
|
|
FName FSoftObjectProperty::GetID() const
|
||
|
|
{
|
||
|
|
// SoftClass shares the same tag, they are binary compatible
|
||
|
|
return NAME_SoftObjectProperty;
|
||
|
|
}
|
||
|
|
|
||
|
|
// this is always shallow, can't see that we would want it any other way
|
||
|
|
bool FSoftObjectProperty::Identical(const void* A, const void* B, uint32 PortFlags) const
|
||
|
|
{
|
||
|
|
FSoftObjectPtr ObjectA = A ? *((FSoftObjectPtr*)A) : FSoftObjectPtr();
|
||
|
|
FSoftObjectPtr ObjectB = B ? *((FSoftObjectPtr*)B) : FSoftObjectPtr();
|
||
|
|
|
||
|
|
return ObjectA.GetUniqueID() == ObjectB.GetUniqueID();
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::SerializeItem(FStructuredArchive::FSlot Slot, void* Value, void const* Defaults) const
|
||
|
|
{
|
||
|
|
FArchive& UnderlyingArchive = Slot.GetUnderlyingArchive();
|
||
|
|
|
||
|
|
// We never serialize our reference while the garbage collector is harvesting references
|
||
|
|
// to objects, because we don't want soft object pointers to keep objects from being garbage collected
|
||
|
|
// Allow persistent archives so they can keep track of string references. (e.g. FArchiveSaveTagImports)
|
||
|
|
if (!UnderlyingArchive.IsObjectReferenceCollector() || UnderlyingArchive.IsModifyingWeakAndStrongReferences() || UnderlyingArchive.IsPersistent())
|
||
|
|
{
|
||
|
|
FSoftObjectPtr OldValue = *(FSoftObjectPtr*)Value;
|
||
|
|
Slot << *(FSoftObjectPtr*)Value;
|
||
|
|
|
||
|
|
// Check for references to instances of wrong types and null them out
|
||
|
|
#if !(UE_BUILD_TEST || UE_BUILD_SHIPPING)
|
||
|
|
if (UnderlyingArchive.IsLoading() || UnderlyingArchive.IsModifyingWeakAndStrongReferences())
|
||
|
|
{
|
||
|
|
if (OldValue.GetUniqueID() != ((FSoftObjectPtr*)Value)->GetUniqueID())
|
||
|
|
{
|
||
|
|
CheckValidObject(Value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// TODO: This isn't correct, but it keeps binary serialization happy. We should ALWAYS be serializing the pointer
|
||
|
|
// to the archive in this function, and allowing the underlying archive to ignore it if necessary
|
||
|
|
Slot.EnterStream();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool FSoftObjectProperty::NetSerializeItem(FArchive& Ar, UPackageMap* Map, void* Data, TArray<uint8>* MetaData) const
|
||
|
|
{
|
||
|
|
// Serialize directly, will use FBitWriter/Reader
|
||
|
|
Ar << *(FSoftObjectPtr*)Data;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::ExportTextItem(FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const
|
||
|
|
{
|
||
|
|
FSoftObjectPtr& SoftObjectPtr = *(FSoftObjectPtr*)PropertyValue;
|
||
|
|
|
||
|
|
FSoftObjectPath SoftObjectPath;
|
||
|
|
UObject* Object = SoftObjectPtr.Get();
|
||
|
|
|
||
|
|
if (Object)
|
||
|
|
{
|
||
|
|
// Use object in case name has changed.
|
||
|
|
SoftObjectPath = FSoftObjectPath(Object);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
SoftObjectPath = SoftObjectPtr.GetUniqueID();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (0 != (PortFlags & PPF_ExportCpp))
|
||
|
|
{
|
||
|
|
ValueStr += FString::Printf(TEXT("FSoftObjectPath(TEXT(\"%s\"))"), *SoftObjectPath.ToString().ReplaceCharWithEscapedChar());
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
SoftObjectPath.ExportTextItem(ValueStr, SoftObjectPath, Parent, PortFlags, ExportRootScope);
|
||
|
|
}
|
||
|
|
|
||
|
|
const TCHAR* FSoftObjectProperty::ImportText_Internal(const TCHAR* InBuffer, void* Data, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText) const
|
||
|
|
{
|
||
|
|
FSoftObjectPtr& SoftObjectPtr = *(FSoftObjectPtr*)Data;
|
||
|
|
|
||
|
|
FSoftObjectPath SoftObjectPath;
|
||
|
|
|
||
|
|
bool bImportTextSuccess = false;
|
||
|
|
#if WITH_EDITOR
|
||
|
|
if (HasAnyPropertyFlags(CPF_EditorOnly))
|
||
|
|
{
|
||
|
|
FSoftObjectPathSerializationScope SerializationScope(NAME_None, NAME_None, ESoftObjectPathCollectType::EditorOnlyCollect, ESoftObjectPathSerializeType::AlwaysSerialize);
|
||
|
|
bImportTextSuccess = SoftObjectPath.ImportTextItem(InBuffer, PortFlags, Parent, ErrorText, GetLinker());
|
||
|
|
}
|
||
|
|
else
|
||
|
|
#endif // WITH_EDITOR
|
||
|
|
{
|
||
|
|
bImportTextSuccess = SoftObjectPath.ImportTextItem(InBuffer, PortFlags, Parent, ErrorText, GetLinker());
|
||
|
|
}
|
||
|
|
|
||
|
|
if (bImportTextSuccess)
|
||
|
|
{
|
||
|
|
SoftObjectPtr = SoftObjectPath;
|
||
|
|
return InBuffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
else
|
||
|
|
{
|
||
|
|
SoftObjectPtr = nullptr;
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
EConvertFromTypeResult FSoftObjectProperty::ConvertFromType(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot, uint8* Data, UStruct* DefaultsStruct)
|
||
|
|
{
|
||
|
|
static FName NAME_AssetObjectProperty = "AssetObjectProperty";
|
||
|
|
static FName NAME_SoftObjectPath = "SoftObjectPath";
|
||
|
|
static FName NAME_SoftClassPath = "SoftClassPath";
|
||
|
|
static FName NAME_StringAssetReference = "StringAssetReference";
|
||
|
|
static FName NAME_StringClassReference = "StringClassReference";
|
||
|
|
|
||
|
|
FArchive& Archive = Slot.GetUnderlyingArchive();
|
||
|
|
|
||
|
|
if (Tag.Type == NAME_AssetObjectProperty)
|
||
|
|
{
|
||
|
|
// Old name of soft object property, serialize normally
|
||
|
|
uint8* DestAddress = ContainerPtrToValuePtr<uint8>(Data, Tag.ArrayIndex);
|
||
|
|
|
||
|
|
Tag.SerializeTaggedProperty(Slot, this, DestAddress, nullptr);
|
||
|
|
|
||
|
|
if (Archive.IsCriticalError())
|
||
|
|
{
|
||
|
|
return EConvertFromTypeResult::CannotConvert;
|
||
|
|
}
|
||
|
|
|
||
|
|
return EConvertFromTypeResult::Converted;
|
||
|
|
}
|
||
|
|
else if (Tag.Type == NAME_ObjectProperty)
|
||
|
|
{
|
||
|
|
// This property used to be a raw FObjectProperty Foo* but is now a TSoftObjectPtr<Foo>;
|
||
|
|
// Serialize from mismatched tag directly into the FSoftObjectPtr's soft object path to ensure that the delegates needed for cooking
|
||
|
|
// are fired
|
||
|
|
FSoftObjectPtr* PropertyValue = GetPropertyValuePtr_InContainer(Data, Tag.ArrayIndex);
|
||
|
|
check(PropertyValue);
|
||
|
|
|
||
|
|
return PropertyValue->GetUniqueID().SerializeFromMismatchedTag(Tag, Slot) ? EConvertFromTypeResult::Converted : EConvertFromTypeResult::UseSerializeItem;
|
||
|
|
}
|
||
|
|
else if (Tag.Type == NAME_StructProperty && (Tag.StructName == NAME_SoftObjectPath || Tag.StructName == NAME_SoftClassPath || Tag.StructName == NAME_StringAssetReference || Tag.StructName == NAME_StringClassReference))
|
||
|
|
{
|
||
|
|
// This property used to be a FSoftObjectPath but is now a TSoftObjectPtr<Foo>
|
||
|
|
FSoftObjectPath PreviousValue;
|
||
|
|
// explicitly call Serialize to ensure that the various delegates needed for cooking are fired
|
||
|
|
PreviousValue.Serialize(Slot);
|
||
|
|
|
||
|
|
// now copy the value into the object's address space
|
||
|
|
FSoftObjectPtr PreviousValueSoftObjectPtr;
|
||
|
|
PreviousValueSoftObjectPtr = PreviousValue;
|
||
|
|
SetPropertyValue_InContainer(Data, PreviousValueSoftObjectPtr, Tag.ArrayIndex);
|
||
|
|
|
||
|
|
return EConvertFromTypeResult::Converted;
|
||
|
|
}
|
||
|
|
|
||
|
|
return EConvertFromTypeResult::UseSerializeItem;
|
||
|
|
}
|
||
|
|
|
||
|
|
UObject* FSoftObjectProperty::LoadObjectPropertyValue(const void* PropertyValueAddress) const
|
||
|
|
{
|
||
|
|
return GetPropertyValue(PropertyValueAddress).LoadSynchronous();
|
||
|
|
}
|
||
|
|
|
||
|
|
UObject* FSoftObjectProperty::GetObjectPropertyValue(const void* PropertyValueAddress) const
|
||
|
|
{
|
||
|
|
return GetPropertyValue(PropertyValueAddress).Get();
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::SetObjectPropertyValue(void* PropertyValueAddress, UObject* Value) const
|
||
|
|
{
|
||
|
|
SetPropertyValue(PropertyValueAddress, TCppType(Value));
|
||
|
|
}
|
||
|
|
|
||
|
|
bool FSoftObjectProperty::AllowCrossLevel() const
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32 FSoftObjectProperty::GetValueTypeHashInternal(const void* Src) const
|
||
|
|
{
|
||
|
|
return GetTypeHash(GetPropertyValue(Src));
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::CopySingleValueToScriptVM(void* Dest, void const* Src) const
|
||
|
|
{
|
||
|
|
CopySingleValue(Dest, Src);
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::CopyCompleteValueToScriptVM(void* Dest, void const* Src) const
|
||
|
|
{
|
||
|
|
CopyCompleteValue(Dest, Src);
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::CopySingleValueFromScriptVM(void* Dest, void const* Src) const
|
||
|
|
{
|
||
|
|
CopySingleValue(Dest, Src);
|
||
|
|
}
|
||
|
|
|
||
|
|
void FSoftObjectProperty::CopyCompleteValueFromScriptVM(void* Dest, void const* Src) const
|
||
|
|
{
|
||
|
|
CopyCompleteValue(Dest, Src);
|
||
|
|
}
|