// Copyright (c) Alexander Bogarsukov.
// Licensed under the MIT license. See the LICENSE.md file in the project root for more information.
using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace UnityFx.Tasks
{
///
/// Extension methods for .
///
public static class AsyncOperationExtensions
{
///
/// Creates an wrapper for the Unity .
///
/// The source operation.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this AsyncOperation op)
{
return ToTask(op, CancellationToken.None);
}
///
/// Creates an wrapper for the Unity .
///
/// The source operation.
/// A token that can be used to cancel the request.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this AsyncOperation op, CancellationToken cancellationToken)
{
if (op.isDone)
{
return Task.CompletedTask;
}
else
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
var result = new TaskCompletionSource(op);
if (cancellationToken.CanBeCanceled)
{
cancellationToken.Register(() => result.TrySetCanceled(cancellationToken));
}
op.completed += o =>
{
result.TrySetResult(null);
};
return result.Task;
}
}
///
/// Creates an wrapper for the Unity .
///
/// The source operation.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this ResourceRequest op)
{
return ToTask(op, CancellationToken.None);
}
///
/// Creates an wrapper for the Unity .
///
/// The source operation.
/// A token that can be used to cancel the request.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this ResourceRequest op, CancellationToken cancellationToken)
{
return ToTask(op, cancellationToken);
}
///
/// Creates an wrapper for the Unity .
///
/// Type of the operation result value.
/// The source operation.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this ResourceRequest op) where T : UnityEngine.Object
{
return ToTask(op, CancellationToken.None);
}
///
/// Creates an wrapper for the Unity .
///
/// Type of the operation result value.
/// The source operation.
/// A token that can be used to cancel the request.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this ResourceRequest op, CancellationToken cancellationToken) where T : UnityEngine.Object
{
if (op.isDone)
{
if (op.asset is T)
{
return Task.FromResult(op.asset as T);
}
else
{
return Task.FromException(new UnityAssetLoadException(typeof(T)));
}
}
else
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
var result = new TaskCompletionSource(op);
if (cancellationToken.CanBeCanceled)
{
cancellationToken.Register(() => result.TrySetCanceled(cancellationToken));
}
op.completed += o =>
{
var asset = op.asset as T;
if (asset)
{
// NOTE: TrySetResult() failure means that the operation was cancelled, thus the resource should be unloaded.
if (!result.TrySetResult(asset))
{
Resources.UnloadAsset(asset);
}
}
else
{
result.TrySetException(new UnityAssetLoadException(typeof(T)));
}
};
return result.Task;
}
}
///
/// Creates an wrapper for the Unity .
///
/// Type of the operation result value.
/// The source operation.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this AssetBundleRequest op) where T : UnityEngine.Object
{
return ToTask(op, CancellationToken.None);
}
///
/// Creates an wrapper for the Unity .
///
/// Type of the operation result value.
/// The source operation.
/// A token that can be used to cancel the request.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this AssetBundleRequest op, CancellationToken cancellationToken) where T : class
{
if (op.isDone)
{
var result = GetResult(op);
if (result != null)
{
return Task.FromResult(result);
}
else
{
return Task.FromException(new UnityAssetLoadException(typeof(T)));
}
}
else
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
var tcs = new TaskCompletionSource(op);
if (cancellationToken.CanBeCanceled)
{
cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken));
}
op.completed += o =>
{
var result = GetResult(op);
if (result != null)
{
// NOTE: TrySetResult() failure means that the operation was cancelled, thus all assets should be unloaded.
if (!tcs.TrySetResult(result))
{
if (op.allAssets != null)
{
foreach (var obj in op.allAssets)
{
UnityEngine.Object.Destroy(obj);
}
}
if (op.asset != null)
{
UnityEngine.Object.Destroy(op.asset);
}
}
}
else
{
tcs.TrySetException(new UnityAssetLoadException(typeof(T)));
}
};
return tcs.Task;
}
}
///
/// Creates an wrapper for the Unity .
///
/// The source operation.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this AssetBundleCreateRequest op)
{
return ToTask(op, CancellationToken.None);
}
///
/// Creates an wrapper for the Unity .
///
/// The source operation.
/// A token that can be used to cancel the request.
/// Returns a instance that can be used to track the operation state.
///
public static Task ToTask(this AssetBundleCreateRequest op, CancellationToken cancellationToken)
{
if (op.isDone)
{
if (op.assetBundle)
{
return Task.FromResult(op.assetBundle);
}
else
{
return Task.FromException(new UnityAssetLoadException(typeof(AssetBundle)));
}
}
else
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
var result = new TaskCompletionSource(op);
if (cancellationToken.CanBeCanceled)
{
cancellationToken.Register(() => result.TrySetCanceled(cancellationToken));
}
op.completed += o =>
{
if (!op.assetBundle)
{
result.TrySetException(new UnityAssetLoadException(typeof(AssetBundle)));
}
else if (!result.TrySetResult(op.assetBundle))
{
// NOTE: TrySetResult() failure means that the operation was cancelled, thus the asset bundle should be unloaded.
op.assetBundle.Unload(true);
}
};
return result.Task;
}
}
internal static T GetResult(AssetBundleRequest op) where T : class
{
if (typeof(T).IsArray)
{
var assets = op.allAssets;
// NOTE: Cannot just cast op.allAssets to T (this would return null), have to create new array.
if (assets != null && assets.Length >= 0)
{
var elementType = typeof(T).GetElementType();
var result = Array.CreateInstance(elementType, assets.Length);
assets.CopyTo(result, 0);
return result as T;
}
}
else if (op.asset != null)
{
return op.asset as T;
}
else if (op.allAssets != null && op.allAssets.Length > 0)
{
return op.allAssets[0] as T;
}
return default(T);
}
}
}