// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreTypes.h" #include "Async/Future.h" #include "Misc/TVariant.h" #include "UniversalObjectLocatorFwd.h" #include "UniversalObjectLocatorResolveParameterBuffer.h" #include "UniversalObjectLocatorResolveParams.generated.h" class UObject; UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) enum class ELocatorResolveFlags : uint8 { None, /** Flag to indicate whether the object should be loaded if it is not currently findable */ Load = 1 << 0, /** Flag to indicate whether the object should be unloaded or destroyed. Mutually exclusive with bLoad. */ Unload = 1 << 1, /** * Indicates that the operation should be performed asynchronously if possible. * When not combined with WillWait, the caller will never block waiting for the result. * When combined with WillWait, the caller will block on this thread until the result is available, * so care needs to be taken avoid a deadlock if there are additional threading constraints on the load. */ Async = 1 << 2, /** Indicates the calling code is going to block waiting for the result. */ WillWait = 1 << 3, /** Combination of Async and WillWait. */ AsyncWait = Async | WillWait, }; ENUM_CLASS_FLAGS(ELocatorResolveFlags) namespace UE::UniversalObjectLocator { /** * Parameters required to resolve a universal object locator */ struct FResolveParams { FResolveParams() : Context(nullptr) , ParameterBuffer(nullptr) , Flags(ELocatorResolveFlags::None) { } FResolveParams(UObject* InContext) : Context(InContext) , ParameterBuffer(nullptr) , Flags(ELocatorResolveFlags::None) { } FResolveParams(UObject* InContext, ELocatorResolveFlags InFlags) : Context(InContext) , ParameterBuffer(nullptr) , Flags(InFlags) { } template const T* FindParameter() const { return FindParameter(T::ParameterType); } template const T* FindParameter(TParameterTypeHandle ParameterType) const { return ParameterBuffer ? ParameterBuffer->FindParameter(ParameterType) : nullptr; } /** * Utility function to indicate that the object should be found, but not loaded or created if it is not currently */ static FResolveParams AsyncFind(UObject* InContext = nullptr) { return FResolveParams(InContext, ELocatorResolveFlags::Async); } /** * Utility function to indicate that the object should be loaded (or dynamically created) if it is not currently */ static FResolveParams AsyncLoad(UObject* InContext = nullptr) { return FResolveParams(InContext, ELocatorResolveFlags::Async | ELocatorResolveFlags::Load); } /** * Utility function to indicate that the object should be unloaded (or dynamically destroyed) if it is not currently */ static FResolveParams AsyncUnload(UObject* InContext) { return FResolveParams(InContext, ELocatorResolveFlags::Async | ELocatorResolveFlags::Unload); } /** * Utility function to indicate that the object should be found, but not loaded or created if it is not currently */ static FResolveParams SyncFind(UObject* InContext = nullptr) { return FResolveParams(InContext); } /** * Utility function to indicate that the object should be loaded (or dynamically created) if it is not currently */ static FResolveParams SyncLoad(UObject* InContext = nullptr) { return FResolveParams(InContext, ELocatorResolveFlags::Load); } /** * Utility function to indicate that the object should be unloaded (or dynamically destroyed) if it is not currently */ static FResolveParams SyncUnload(UObject* InContext) { return FResolveParams(InContext, ELocatorResolveFlags::Unload); } /** (Optional) Object to use as a context for resolution. Normally this is the object that owns the reference being resolved */ UObject* Context; /** (Optional) Resolve buffer */ FResolveParameterBuffer* ParameterBuffer; /** Flag structure */ ELocatorResolveFlags Flags; }; template struct TResolveParamsWithBuffer : TInlineResolveParameterBuffer, FResolveParams { TResolveParamsWithBuffer() { ParameterBuffer = this; } TResolveParamsWithBuffer(UObject* InContext) : FResolveParams(InContext) { ParameterBuffer = this; } TResolveParamsWithBuffer(UObject* InContext, ELocatorResolveFlags InFlags) : FResolveParams(InContext, InFlags) { ParameterBuffer = this; } }; /** * Flag structure returned from an attempt to resolve a Universal Object Locator */ struct FResolveResultFlags { FResolveResultFlags() : bWasLoaded(0) , bWasLoadedIndirectly(0) { } /** Indicates the object was loaded as a result of this request */ uint8 bWasLoaded : 1; /** Indicates that an outer object was loaded in order to fulfil this request, and the final object itself was loaded as a result */ uint8 bWasLoadedIndirectly : 1; }; struct FResolveResultData { FResolveResultData() : Object(nullptr) { } FResolveResultData(UObject* InObject, FResolveResultFlags InFlags = FResolveResultFlags()) : Object(InObject) , Flags(InFlags) { } /** * The resulting object or nullptr if it could not be found or loaded per the request params. * Should only be set if Params.Flags.bAsync is false. */ UObject* Object = nullptr; /** Flags relating to the operation */ FResolveResultFlags Flags; }; struct FResolveResult { FResolveResult() : Value(TInPlaceType()) { } FResolveResult(FResolveResultData InValue) : Value(TInPlaceType(), MoveTemp(InValue)) { } FResolveResult(TFuture&& InFuture) : Value(TInPlaceType>(), MoveTemp(InFuture)) { } FResolveResult(const FResolveResult&) = delete; void operator=(const FResolveResult&) = delete; FResolveResult(FResolveResult&&) = default; FResolveResult& operator=(FResolveResult&&) = default; public: /** * Check whether this result is asynchronous */ bool IsAsync() const { return Value.IsType>(); } /** * Release the future from this type if it is set. * WARNING: This can only be called once per instance. Invalidates this class. * @return This result's future, or an empty one if the result has already been applied. */ TFuture ReleaseFuture() { if (TFuture* Future = Value.TryGet>()) { return MoveTemp(*Future); } return TFuture(); } /** * Check whether the result needs to be waited on. * @return true if the result hasn't been set yet: SyncGet will block, and SyncGetNoWait will return null. If false, SyncGet will always return immediately, and SyncGetNoWait will never return null. */ bool NeedsWait() const { if (const TFuture* Future = Value.TryGet>()) { return !Future->IsReady(); } return false; } /** * Retrieve the result if it is currently available without waiting. Returns null if it is still being processed. */ const FResolveResultData* SyncGetNoWait() { const TFuture* Future = Value.TryGet>(); if (Future) { if (!Future->IsReady()) { return nullptr; } FResolveResultData Data = Future->Get(); Value.Emplace(Data); } return &Value.Get(); } /** * Retrieve the result, blocking this thread if necessary. */ FResolveResultData SyncGet() const { const TFuture* Future = Value.TryGet>(); if (Future) { return Future->Get(); } else { return Value.Get(); } } /** * Retrieve this result asynchronously. * WARNING: This can only be called once per instance. */ void AsyncGet(TUniqueFunction&& OnComplete) { TFuture* Future = Value.TryGet>(); if (Future) { if (Future->IsReady()) { OnComplete(Future->Get()); } else { Future->Then( [OnComplete = MoveTemp(OnComplete)](TFuture&& InValue) { OnComplete(InValue.Get()); } ); } } else { OnComplete(MoveTemp(Value.Get())); } } private: TVariant< FResolveResultData, TFuture > Value; }; } // namespace UE::UniversalObjectLocator