using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
///
/// Handle for internal operations. This allows for reference counting and checking for valid references.
///
/// The object type of the underlying operation.
public struct AsyncOperationHandle : IEnumerator, IEquatable>
{
internal AsyncOperationBase m_InternalOp;
int m_Version;
string m_LocationName;
internal string LocationName
{
get { return m_LocationName; }
set { m_LocationName = value; }
}
bool m_UnloadSceneOpExcludeReleaseCallback;
internal bool UnloadSceneOpExcludeReleaseCallback
{
get { return m_UnloadSceneOpExcludeReleaseCallback; }
set { m_UnloadSceneOpExcludeReleaseCallback = value; }
}
///
/// Conversion from typed to non typed handles. This does not increment the reference count.
/// To convert from non-typed back, use AsyncOperationHandle.Convert<T>()
///
/// The typed handle to convert.
/// Returns the converted operation handle.
static public implicit operator AsyncOperationHandle(AsyncOperationHandle obj)
{
return new AsyncOperationHandle(obj.m_InternalOp, obj.m_Version, obj.m_LocationName);
}
internal AsyncOperationHandle(AsyncOperationBase op)
{
m_InternalOp = op;
m_Version = op?.Version ?? 0;
m_LocationName = null;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
///
/// Return the current download status for this operation and its dependencies.
///
/// The download status.
public DownloadStatus GetDownloadStatus()
{
return InternalGetDownloadStatus(new HashSet());
}
internal DownloadStatus InternalGetDownloadStatus(HashSet visited)
{
if (visited == null)
visited = new HashSet();
return visited.Add(InternalOp) ? InternalOp.GetDownloadStatus(visited) : new DownloadStatus() {IsDone = IsDone};
}
internal AsyncOperationHandle(IAsyncOperation op)
{
m_InternalOp = (AsyncOperationBase)op;
m_Version = op?.Version ?? 0;
m_LocationName = null;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
internal AsyncOperationHandle(IAsyncOperation op, int version)
{
m_InternalOp = (AsyncOperationBase)op;
m_Version = version;
m_LocationName = null;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
internal AsyncOperationHandle(IAsyncOperation op, string locationName)
{
m_InternalOp = (AsyncOperationBase)op;
m_Version = op?.Version ?? 0;
m_LocationName = locationName;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName)
{
m_InternalOp = (AsyncOperationBase)op;
m_Version = version;
m_LocationName = locationName;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
///
/// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released.
///
/// A new handle to the operation. This handle must also be released.
internal AsyncOperationHandle Acquire()
{
InternalOp.IncrementReferenceCount();
return this;
}
///
/// Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
///
public event Action> Completed
{
add { InternalOp.Completed += value; }
remove { InternalOp.Completed -= value; }
}
///
/// Completion event for non-typed callback handlers. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
///
public event Action CompletedTypeless
{
add { InternalOp.CompletedTypeless += value; }
remove { InternalOp.CompletedTypeless -= value; }
}
///
/// Debug name of the operation.
///
public string DebugName
{
get
{
if (!IsValid())
return "InvalidHandle";
return ((IAsyncOperation)InternalOp).DebugName;
}
}
///
/// Get dependency operations.
///
/// The list of AsyncOperationHandles that are dependencies of a given AsyncOperationHandle
public void GetDependencies(List deps)
{
InternalOp.GetDependencies(deps);
}
///
/// Event for handling the destruction of the operation.
///
public event Action Destroyed
{
add { InternalOp.Destroyed += value; }
remove { InternalOp.Destroyed -= value; }
}
///
/// Provide equality for this struct.
///
/// The operation to compare to.
/// True if the the operation handles reference the same AsyncOperation and the version is the same.
public bool Equals(AsyncOperationHandle other)
{
return m_Version == other.m_Version && m_InternalOp == other.m_InternalOp;
}
///
/// Get hash code of this struct.
///
///
public override int GetHashCode()
{
return m_InternalOp == null ? 0 : m_InternalOp.GetHashCode() * 17 + m_Version;
}
///
/// Synchronously complete the async operation.
///
/// The result of the operation or null.
public TObject WaitForCompletion()
{
#if !UNITY_2021_1_OR_NEWER
AsyncOperationHandle.IsWaitingForCompletion = true;
try
{
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
if (IsValid())
return Result;
}
finally
{
AsyncOperationHandle.IsWaitingForCompletion = false;
m_InternalOp?.m_RM?.Update(Time.unscaledDeltaTime);
}
#else
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
m_InternalOp?.m_RM?.Update(Time.unscaledDeltaTime);
if (IsValid())
return Result;
#endif
return default(TObject);
}
internal AsyncOperationBase InternalOp
{
get
{
if (m_InternalOp == null || m_InternalOp.Version != m_Version)
throw new Exception("Attempting to use an invalid operation handle");
return m_InternalOp;
}
}
///
/// True if the operation is complete.
///
public bool IsDone
{
get { return !IsValid() || InternalOp.IsDone; }
}
///
/// Check if the handle references an internal operation.
///
/// True if valid.
public bool IsValid()
{
return m_InternalOp != null && m_InternalOp.Version == m_Version;
}
///
/// The exception for a failed operation. This will be null unless Status is failed.
///
public Exception OperationException
{
get { return InternalOp.OperationException; }
}
///
/// The progress of the internal operation.
/// This is evenly weighted between all sub-operations. For example, a LoadAssetAsync call could potentially
/// be chained with InitializeAsync and have multiple dependent operations that download and load content.
/// In that scenario, PercentComplete would reflect how far the overal operation was, and would not accurately
/// represent just percent downloaded or percent loaded into memory.
/// For accurate download percentages, use GetDownloadStatus().
///
public float PercentComplete
{
get { return InternalOp.PercentComplete; }
}
///
/// The current reference count of the internal operation.
///
internal int ReferenceCount
{
get { return InternalOp.ReferenceCount; }
}
///
/// Release the handle. If the internal operation reference count reaches 0, the resource will be released.
///
internal void Release()
{
InternalOp.DecrementReferenceCount();
m_InternalOp = null;
}
///
/// The result object of the operations.
///
public TObject Result
{
get { return InternalOp.Result; }
}
///
/// The status of the internal operation.
///
public AsyncOperationStatus Status
{
get { return InternalOp.Status; }
}
///
/// Return a Task object to wait on when using async await.
///
public System.Threading.Tasks.Task Task
{
get { return InternalOp.Task; }
}
object IEnumerator.Current
{
get { return Result; }
}
///
/// Overload for .
///
/// Returns true if the enumerator can advance to the next element in the collectin. Returns false otherwise.
bool IEnumerator.MoveNext()
{
return !IsDone;
}
///
/// Overload for .
///
void IEnumerator.Reset()
{
}
}
///
/// Non typed operation handle. This allows for reference counting and checking for valid references.
///
public struct AsyncOperationHandle : IEnumerator
{
#if !UNITY_2021_1_OR_NEWER
private static bool m_IsWaitingForCompletion = false;
///
/// Indicates that the async operation is in the process of being completed synchronously.
///
public static bool IsWaitingForCompletion
{
get { return m_IsWaitingForCompletion; }
internal set { m_IsWaitingForCompletion = value; }
}
#endif
internal IAsyncOperation m_InternalOp;
int m_Version;
string m_LocationName;
internal string LocationName
{
get { return m_LocationName; }
set { m_LocationName = value; }
}
internal AsyncOperationHandle(IAsyncOperation op)
{
m_InternalOp = op;
m_Version = op?.Version ?? 0;
m_LocationName = null;
}
internal AsyncOperationHandle(IAsyncOperation op, int version)
{
m_InternalOp = op;
m_Version = version;
m_LocationName = null;
}
internal AsyncOperationHandle(IAsyncOperation op, string locationName)
{
m_InternalOp = op;
m_Version = op?.Version ?? 0;
m_LocationName = locationName;
}
internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName)
{
m_InternalOp = op;
m_Version = version;
m_LocationName = locationName;
}
///
/// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released.
///
/// A new handle to the operation. This handle must also be released.
internal AsyncOperationHandle Acquire()
{
InternalOp.IncrementReferenceCount();
return this;
}
///
/// Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
///
public event Action Completed
{
add { InternalOp.CompletedTypeless += value; }
remove { InternalOp.CompletedTypeless -= value; }
}
///
/// Converts handle to be typed. This does not increment the reference count.
/// To convert back to non-typed, implicit conversion is available.
///
/// The type of the handle.
/// A new handle that is typed.
public AsyncOperationHandle Convert()
{
return new AsyncOperationHandle(InternalOp, m_Version, m_LocationName);
}
///
/// Provide equality for this struct.
///
/// The operation to compare to.
/// True if the the operation handles reference the same AsyncOperation and the version is the same.
public bool Equals(AsyncOperationHandle other)
{
return m_Version == other.m_Version && m_InternalOp == other.m_InternalOp;
}
///
/// Debug name of the operation.
///
public string DebugName
{
get
{
if (!IsValid())
return "InvalidHandle";
return InternalOp.DebugName;
}
}
///
/// Event for handling the destruction of the operation.
///
public event Action Destroyed
{
add { InternalOp.Destroyed += value; }
remove { InternalOp.Destroyed -= value; }
}
///
/// Get dependency operations.
///
///
public void GetDependencies(List deps)
{
InternalOp.GetDependencies(deps);
}
///
/// Get hash code of this struct.
///
///
public override int GetHashCode()
{
return m_InternalOp == null ? 0 : m_InternalOp.GetHashCode() * 17 + m_Version;
}
IAsyncOperation InternalOp
{
get
{
if (m_InternalOp == null || m_InternalOp.Version != m_Version)
throw new Exception("Attempting to use an invalid operation handle");
return m_InternalOp;
}
}
///
/// True if the operation is complete.
///
public bool IsDone
{
get { return !IsValid() || InternalOp.IsDone; }
}
///
/// Check if the internal operation is not null and has the same version of this handle.
///
/// True if valid.
public bool IsValid()
{
return m_InternalOp != null && m_InternalOp.Version == m_Version;
}
///
/// The exception for a failed operation. This will be null unless Status is failed.
///
public Exception OperationException
{
get { return InternalOp.OperationException; }
}
///
/// The progress of the internal operation.
/// This is evenly weighted between all sub-operations. For example, a LoadAssetAsync call could potentially
/// be chained with InitializeAsync and have multiple dependent operations that download and load content.
/// In that scenario, PercentComplete would reflect how far the overal operation was, and would not accurately
/// represent just percent downloaded or percent loaded into memory.
/// For accurate download percentages, use GetDownloadStatus().
///
public float PercentComplete
{
get { return InternalOp.PercentComplete; }
}
///
/// Return the current download status for this operation and its dependencies. In some instances, the information will not be available. This can happen if the operation
/// is dependent on the initialization operation for addressables. Once the initialization operation completes, the information returned will be accurate.
///
/// The download status.
public DownloadStatus GetDownloadStatus()
{
return InternalGetDownloadStatus(new HashSet());
}
internal DownloadStatus InternalGetDownloadStatus(HashSet visited)
{
if (visited == null)
visited = new HashSet();
return visited.Add(InternalOp) ? InternalOp.GetDownloadStatus(visited) : new DownloadStatus() {IsDone = IsDone};
}
///
/// The current reference count of the internal operation.
///
internal int ReferenceCount
{
get { return InternalOp.ReferenceCount; }
}
///
/// Release the handle. If the internal operation reference count reaches 0, the resource will be released.
///
internal void Release()
{
InternalOp.DecrementReferenceCount();
m_InternalOp = null;
}
///
/// The result object of the operations.
///
public object Result
{
get { return InternalOp.GetResultAsObject(); }
}
///
/// The status of the internal operation.
///
public AsyncOperationStatus Status
{
get { return InternalOp.Status; }
}
///
/// Return a Task object to wait on when using async await.
///
public System.Threading.Tasks.Task Task
{
get { return InternalOp.Task; }
}
object IEnumerator.Current
{
get { return Result; }
}
///
/// Overload for .
///
/// Returns true if the enumerator can advance to the next element in the collectin. Returns false otherwise.
bool IEnumerator.MoveNext()
{
return !IsDone;
}
///
/// Overload for .
///
void IEnumerator.Reset()
{
}
///
/// Synchronously complete the async operation.
///
/// The result of the operation or null.
public object WaitForCompletion()
{
#if !UNITY_2021_1_OR_NEWER
IsWaitingForCompletion = true;
try
{
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
if (IsValid())
return Result;
}
finally
{
IsWaitingForCompletion = false;
}
#else
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
if (IsValid())
return Result;
#endif
return null;
}
}
}