// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= SoftObjectPtr.h: Pointer to UObject asset, keeps extra information so that it is works even if the asset is not in memory =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "Templates/Casts.h" #include "UObject/PersistentObjectPtr.h" #include "UObject/SoftObjectPath.h" /** * FSoftObjectPtr is a type of weak pointer to a UObject, that also keeps track of the path to the object on disk. * It will change back and forth between being Valid and Pending as the referenced object loads or unloads. * It has no impact on if the object is garbage collected or not. * * This is useful to specify assets that you may want to asynchronously load on demand. */ struct FSoftObjectPtr: public TPersistentObjectPtr { public: /** Default constructor, will be null */ FORCEINLINE FSoftObjectPtr() { } /** Construct from another soft pointer */ FORCEINLINE FSoftObjectPtr(const FSoftObjectPtr& Other) { (*this) = Other; } /** Construct from a soft object path */ explicit FORCEINLINE FSoftObjectPtr(const FSoftObjectPath& ObjectPath) : TPersistentObjectPtr(ObjectPath) { } /** Construct from an object already in memory */ explicit FORCEINLINE FSoftObjectPtr(const UObject* Object) { (*this) = Object; } /** Synchronously load (if necessary) and return the asset object represented by this asset ptr */ UObject* LoadSynchronous() const { UObject* Asset = Get(); if (Asset == nullptr && IsPending()) { ToSoftObjectPath().TryLoad(); // TryLoad will have loaded this pointer if it is valid Asset = Get(); } return Asset; } /** Returns the StringObjectPath that is wrapped by this SoftObjectPtr */ FORCEINLINE const FSoftObjectPath& ToSoftObjectPath() const { return GetUniqueID(); } UE_DEPRECATED(4.18, "ToStringReference was renamed to ToSoftObjectPath") FORCEINLINE const FSoftObjectPath& ToStringReference() const { return GetUniqueID(); } /** Returns string representation of reference, in form /package/path.assetname */ FORCEINLINE FString ToString() const { return ToSoftObjectPath().ToString(); } /** Returns /package/path string, leaving off the asset name */ FORCEINLINE FString GetLongPackageName() const { return ToSoftObjectPath().GetLongPackageName(); } /** Returns assetname string, leaving off the /package/path. part */ FORCEINLINE FString GetAssetName() const { return ToSoftObjectPath().GetAssetName(); } #if WITH_EDITOR /** Overridden to deal with PIE lookups */ FORCEINLINE UObject* Get() const { if (GPlayInEditorID != INDEX_NONE) { // Cannot use or set the cached value in PIE as it may affect other PIE instances or the editor TWeakObjectPtr Result = GetUniqueID().ResolveObject(); // If this object is pending kill or otherwise invalid, this will return nullptr just like TPersistentObjectPtr::Get() return Result.Get(); } return TPersistentObjectPtr::Get(); } #endif using TPersistentObjectPtr::operator=; }; template <> struct TIsPODType { enum { Value = TIsPODType>::Value }; }; template <> struct TIsWeakPointerType { enum { Value = TIsWeakPointerType>::Value }; }; /** * TSoftObjectPtr is templatized wrapper of the generic FSoftObjectPtr, it can be used in UProperties */ template struct TSoftObjectPtr { template friend struct TSoftObjectPtr; public: /** Default constructor, will be null */ FORCEINLINE TSoftObjectPtr() { } /** Construct from another soft pointer */ template ((U*)nullptr))> FORCEINLINE TSoftObjectPtr(const TSoftObjectPtr& Other) : SoftObjectPtr(Other.SoftObjectPtr) { } /** Construct from a moveable soft pointer */ template ((U*)nullptr))> FORCEINLINE TSoftObjectPtr(TSoftObjectPtr&& Other) : SoftObjectPtr(MoveTemp(Other.SoftObjectPtr)) { } /** Construct from an object already in memory */ template FORCEINLINE TSoftObjectPtr(const U* Object) : SoftObjectPtr(Object) { } /** Construct from a nullptr */ FORCEINLINE TSoftObjectPtr(TYPE_OF_NULLPTR) : SoftObjectPtr(nullptr) { } /** Construct from a soft object path */ explicit FORCEINLINE TSoftObjectPtr(FSoftObjectPath ObjectPath) : SoftObjectPtr(MoveTemp(ObjectPath)) { } /** Reset the soft pointer back to the null state */ FORCEINLINE void Reset() { SoftObjectPtr.Reset(); } /** Resets the weak ptr only, call this when ObjectId may change */ FORCEINLINE void ResetWeakPtr() { SoftObjectPtr.ResetWeakPtr(); } /** Copy from an object already in memory */ template FORCEINLINE TSoftObjectPtr& operator=(const U* Object) { SoftObjectPtr = Object; return *this; } /** Assign from a nullptr */ FORCEINLINE TSoftObjectPtr& operator=(TYPE_OF_NULLPTR) { SoftObjectPtr = nullptr; return *this; } /** Copy from a soft object path */ FORCEINLINE TSoftObjectPtr& operator=(FSoftObjectPath ObjectPath) { SoftObjectPtr = MoveTemp(ObjectPath); return *this; } /** Copy from a weak pointer to an object already in memory */ template ((U*)nullptr))> FORCEINLINE TSoftObjectPtr& operator=(const TWeakObjectPtr& Other) { SoftObjectPtr = Other; return *this; } /** Copy from another soft pointer */ template ((U*)nullptr))> FORCEINLINE TSoftObjectPtr& operator=(TSoftObjectPtr Other) { SoftObjectPtr = MoveTemp(Other.SoftObjectPtr); return *this; } /** * Compare soft pointers for equality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator==(const TSoftObjectPtr& Lhs, const TSoftObjectPtr& Rhs) { return Lhs.SoftObjectPtr == Rhs.SoftObjectPtr; } /** * Compare soft pointers for equality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator==(const TSoftObjectPtr& Lhs, TYPE_OF_NULLPTR) { return Lhs.SoftObjectPtr == nullptr; } /** * Compare soft pointers for equality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator==(TYPE_OF_NULLPTR, const TSoftObjectPtr& Rhs) { return nullptr == Rhs.SoftObjectPtr; } /** * Compare soft pointers for inequality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator!=(const TSoftObjectPtr& Lhs, const TSoftObjectPtr& Rhs) { return Lhs.SoftObjectPtr != Rhs.SoftObjectPtr; } /** * Compare soft pointers for inequality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator!=(const TSoftObjectPtr& Lhs, TYPE_OF_NULLPTR) { return Lhs.SoftObjectPtr != nullptr; } /** * Compare soft pointers for inequality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator!=(TYPE_OF_NULLPTR, const TSoftObjectPtr& Rhs) { return nullptr != Rhs.SoftObjectPtr; } /** * Dereference the soft pointer. * * @return nullptr if this object is gone or the lazy pointer was null, otherwise a valid UObject pointer */ FORCEINLINE T* Get() const { return dynamic_cast(SoftObjectPtr.Get()); } /** Dereference the soft pointer */ FORCEINLINE T& operator*() const { return *Get(); } /** Dereference the soft pointer */ FORCEINLINE T* operator->() const { return Get(); } /** Synchronously load (if necessary) and return the asset object represented by this asset ptr */ T* LoadSynchronous() const { UObject* Asset = SoftObjectPtr.LoadSynchronous(); return Cast(Asset); } /** * Test if this points to a live UObject * * @return true if Get() would return a valid non-null pointer */ FORCEINLINE bool IsValid() const { // This does the runtime type check return Get() != nullptr; } /** * Test if this does not point to a live UObject, but may in the future * * @return true if this does not point to a real object, but could possibly */ FORCEINLINE bool IsPending() const { return SoftObjectPtr.IsPending(); } /** * Test if this can never point to a live UObject * * @return true if this is explicitly pointing to no object */ FORCEINLINE bool IsNull() const { return SoftObjectPtr.IsNull(); } /** Returns the StringObjectPath that is wrapped by this SoftObjectPtr */ FORCEINLINE const FSoftObjectPath& GetUniqueID() const { return SoftObjectPtr.GetUniqueID(); } /** Returns the StringObjectPath that is wrapped by this SoftObjectPtr */ FORCEINLINE const FSoftObjectPath& ToSoftObjectPath() const { return SoftObjectPtr.GetUniqueID(); } UE_DEPRECATED(4.18, "ToStringReference was renamed to ToSoftObjectPath") FORCEINLINE const FSoftObjectPath& ToStringReference() const { return SoftObjectPtr.GetUniqueID(); } /** Returns string representation of reference, in form /package/path.assetname */ FORCEINLINE FString ToString() const { return ToSoftObjectPath().ToString(); } /** Returns /package/path string, leaving off the asset name */ FORCEINLINE FString GetLongPackageName() const { return ToSoftObjectPath().GetLongPackageName(); } /** Returns assetname string, leaving off the /package/path part */ FORCEINLINE FString GetAssetName() const { return ToSoftObjectPath().GetAssetName(); } /** Dereference soft pointer to see if it points somewhere valid */ FORCEINLINE explicit operator bool() const { return IsValid(); } /** Hash function */ FORCEINLINE friend uint32 GetTypeHash(const TSoftObjectPtr& Other) { return GetTypeHash(static_cast&>(Other.SoftObjectPtr)); } friend FArchive& operator<<(FArchive& Ar, TSoftObjectPtr& Other) { Ar << Other.SoftObjectPtr; return Ar; } private: FSoftObjectPtr SoftObjectPtr; }; template struct TIsPODType> { enum { Value = TIsPODType::Value }; }; template struct TIsWeakPointerType> { enum { Value = TIsWeakPointerType::Value }; }; /** * TSoftClassPtr is a templatized wrapper around FSoftObjectPtr that works like a TSubclassOf, it can be used in UProperties for blueprint subclasses */ template class TSoftClassPtr { template friend class TSoftClassPtr; public: /** Default constructor, will be null */ FORCEINLINE TSoftClassPtr() { } /** Construct from another soft pointer */ template ((TClassA*)nullptr))> FORCEINLINE TSoftClassPtr(const TSoftClassPtr& Other) : SoftObjectPtr(Other.SoftObjectPtr) { } /** Construct from a class already in memory */ FORCEINLINE TSoftClassPtr(const UClass* From) : SoftObjectPtr(From) { } /** Construct from a soft object path */ explicit FORCEINLINE TSoftClassPtr(const FSoftObjectPath& ObjectPath) : SoftObjectPtr(ObjectPath) { } /** Reset the soft pointer back to the null state */ FORCEINLINE void Reset() { SoftObjectPtr.Reset(); } /** Resets the weak ptr only, call this when ObjectId may change */ FORCEINLINE void ResetWeakPtr() { SoftObjectPtr.ResetWeakPtr(); } /** Copy from a class already in memory */ FORCEINLINE void operator=(const UClass* From) { SoftObjectPtr = From; } /** Copy from a soft object path */ FORCEINLINE void operator=(const FSoftObjectPath& ObjectPath) { SoftObjectPtr = ObjectPath; } /** Copy from a weak pointer already in memory */ template ((TClassA*)nullptr))> FORCEINLINE TSoftClassPtr& operator=(const TWeakObjectPtr& Other) { SoftObjectPtr = Other; return *this; } /** Copy from another soft pointer */ template ((TClassA*)nullptr))> FORCEINLINE TSoftClassPtr& operator=(const TSoftObjectPtr& Other) { SoftObjectPtr = Other.SoftObjectPtr; return *this; } /** * Compare soft pointers for equality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator==(const TSoftClassPtr& Lhs, const TSoftClassPtr& Rhs) { return Lhs.SoftObjectPtr == Rhs.SoftObjectPtr; } /** * Compare soft pointers for inequality * Caution: Two soft pointers might not be equal to each other, but they both might return nullptr * * @param Other soft pointer to compare to */ FORCEINLINE friend bool operator!=(const TSoftClassPtr& Lhs, const TSoftClassPtr& Rhs) { return Lhs.SoftObjectPtr != Rhs.SoftObjectPtr; } /** * Dereference the soft pointer * * @return nullptr if this object is gone or the soft pointer was null, otherwise a valid UClass pointer */ FORCEINLINE UClass* Get() const { UClass* Class = dynamic_cast(SoftObjectPtr.Get()); if (!Class || !Class->IsChildOf(TClass::StaticClass())) { return nullptr; } return Class; } /** Dereference the soft pointer */ FORCEINLINE UClass& operator*() const { return *Get(); } /** Dereference the soft pointer */ FORCEINLINE UClass* operator->() const { return Get(); } /** * Test if this points to a live UObject * * @return true if Get() would return a valid non-null pointer */ FORCEINLINE bool IsValid() const { // This also does the UClass type check return Get() != nullptr; } /** * Test if this does not point to a live UObject, but may in the future * * @return true if this does not point to a real object, but could possibly */ FORCEINLINE bool IsPending() const { return SoftObjectPtr.IsPending(); } /** * Test if this can never point to a live UObject * * @return true if this is explicitly pointing to no object */ FORCEINLINE bool IsNull() const { return SoftObjectPtr.IsNull(); } /** Returns the StringObjectPath that is wrapped by this SoftObjectPtr */ FORCEINLINE const FSoftObjectPath& GetUniqueID() const { return SoftObjectPtr.GetUniqueID(); } /** Returns the StringObjectPath that is wrapped by this SoftObjectPtr */ FORCEINLINE const FSoftObjectPath& ToSoftObjectPath() const { return SoftObjectPtr.GetUniqueID(); } UE_DEPRECATED(4.18, "ToStringReference was renamed to ToSoftObjectPath") FORCEINLINE const FSoftObjectPath& ToStringReference() const { return SoftObjectPtr.GetUniqueID(); } /** Returns string representation of reference, in form /package/path.assetname */ FORCEINLINE FString ToString() const { return ToSoftObjectPath().ToString(); } /** Returns /package/path string, leaving off the asset name */ FORCEINLINE FString GetLongPackageName() const { return ToSoftObjectPath().GetLongPackageName(); } /** Returns assetname string, leaving off the /package/path part */ FORCEINLINE FString GetAssetName() const { return ToSoftObjectPath().GetAssetName(); } /** Dereference soft pointer to see if it points somewhere valid */ FORCEINLINE explicit operator bool() const { return IsValid(); } /** Hash function */ FORCEINLINE friend uint32 GetTypeHash(const TSoftClassPtr& Other) { return GetTypeHash(static_cast&>(Other.SoftObjectPtr)); } /** Synchronously load (if necessary) and return the asset object represented by this asset ptr */ UClass* LoadSynchronous() const { UObject* Asset = SoftObjectPtr.LoadSynchronous(); UClass* Class = dynamic_cast(Asset); if (!Class || !Class->IsChildOf(TClass::StaticClass())) { return nullptr; } return Class; } friend FArchive& operator<<(FArchive& Ar, TSoftClassPtr& Other) { Ar << static_cast(Other.SoftObjectPtr); return Ar; } private: FSoftObjectPtr SoftObjectPtr; }; template struct TIsPODType> { enum { Value = TIsPODType::Value }; }; template struct TIsWeakPointerType> { enum { Value = TIsWeakPointerType::Value }; }; /** Fast non-alphabetical order that is only stable during this process' lifetime. */ struct FSoftObjectPtrFastLess: private FSoftObjectPathFastLess { template bool operator()(const SoftObjectPtrType& Lhs, const SoftObjectPtrType& Rhs) const { return FSoftObjectPathFastLess::operator()(Lhs.ToSoftObjectPath(), Rhs.ToSoftObjectPath()); } }; /** Slow alphabetical order that is stable / deterministic over process runs. */ struct FSoftObjectPtrLexicalLess: private FSoftObjectPathLexicalLess { template bool operator()(const SoftObjectPtrType& Lhs, const SoftObjectPtrType& Rhs) const { return FSoftObjectPathLexicalLess::operator()(Lhs.ToSoftObjectPath(), Rhs.ToSoftObjectPath()); } }; UE_DEPRECATED(4.18, "FAssetPtr was renamed to FSoftObjectPtr as it is not necessarily an asset") typedef FSoftObjectPtr FAssetPtr; // Not deprecating these yet as it will lead to too many warnings in games // UE_DEPRECATED(4.18, "TAssetPtr was renamed to TSoftObjectPtr as it is not necessarily an asset") template using TAssetPtr = TSoftObjectPtr; // UE_DEPRECATED(4.18, "TAssetSubclassOf was renamed to TSoftClassPtr") template using TAssetSubclassOf = TSoftClassPtr;