/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
// If we don't already have a directive telling us what to use...
#if !YARNTASKS_ARE_AWAITABLES && !YARNTASKS_ARE_SYSTEMTASKS && !YARNTASKS_ARE_UNITASKS
// ...then figure out what the best option is. We'll try to use UniTask, if
// installed; then Awaitables, if >= Unity 2023.1; then System.Threading.Tasks.
#if USE_UNITASK
#define YARNTASKS_ARE_UNITASKS
#elif UNITY_2023_1_OR_NEWER
#define YARNTASKS_ARE_AWAITABLES
#else
#define YARNTASKS_ARE_SYSTEMTASKS
#endif
#endif
#if YARNTASKS_ARE_SYSTEMTASKS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
#if USE_ADDRESSABLES
using UnityEngine.ResourceManagement.AsyncOperations;
#endif
namespace Yarn.Unity
{
public partial struct YarnTask
{
public TaskAwaiter GetAwaiter() => Task.GetAwaiter();
Task Task;
readonly public bool IsCompleted() => Task.IsCompleted;
readonly public bool IsCompletedSuccessfully() => Task.IsCompletedSuccessfully;
public static implicit operator Task(YarnTask YarnTask)
{
return YarnTask.Task;
}
public static implicit operator YarnTask(Task task)
{
return new YarnTask { Task = task };
}
#if UNITY_2023_1_OR_NEWER
public static implicit operator YarnTask(Awaitable awaitable)
{
return new YarnTask { Task = awaitable.AsTask() };
}
#endif
#if USE_UNITASK
public static implicit operator YarnTask(Cysharp.Threading.Tasks.UniTask uniTask)
{
return new YarnTask { Task = Cysharp.Threading.Tasks.UniTaskExtensions.AsTask(uniTask) };
}
#endif
readonly public async void Forget()
{
try
{
await Task;
}
catch (System.Exception e)
{
Debug.LogException(e);
}
}
public static partial async YarnTask WaitUntilCanceled(System.Threading.CancellationToken token)
{
while (!token.IsCancellationRequested)
{
await Task.Yield();
}
}
public static YarnTask CompletedTask => Task.CompletedTask;
///
/// Creates a that delays for the time indicated
/// by , and then returns.
///
/// The amount of time to wait.
/// A token that can be used to cancel the
/// task.
/// A new .
public static partial YarnTask Delay(TimeSpan timeSpan, CancellationToken token)
{
return Task.Delay(timeSpan, token);
}
public static partial async YarnTask WaitUntil(System.Func predicate, System.Threading.CancellationToken token)
{
while (!token.IsCancellationRequested && predicate() == false)
{
await Task.Yield();
}
}
public static partial IEnumerator ToCoroutine(Func factory)
{
IEnumerator InnerCoroutine(Task t)
{
while (!t.IsCompleted)
{
yield return null;
}
if (t.IsFaulted)
{
Debug.LogException(t.Exception);
}
}
return InnerCoroutine(factory());
}
public static partial async YarnTask Yield()
{
await Task.Yield();
}
public static partial YarnTask WhenAll(params YarnTask[] tasks)
{
return WhenAll((IEnumerable)tasks);
}
public static partial async YarnTask WhenAll(IEnumerable tasks)
{
// Don't love this allocation here; try and find a better approach
List taskList = new List();
foreach (var task in tasks)
{
taskList.Add(task);
}
await Task.WhenAll(taskList.ToArray());
}
public static async partial YarnTask WhenAll(params YarnTask[] tasks)
{
return await Task.WhenAll(Array.ConvertAll, Task>(tasks, t => t));
}
public static async partial YarnTask WhenAll(IEnumerable> tasks)
{
var uniTasks = new List>();
foreach (var task in tasks)
{
uniTasks.Add(task);
}
return await Task.WhenAll(uniTasks);
}
public readonly async partial YarnTask SuppressCancellationThrow()
{
try
{
await Task;
}
catch (OperationCanceledException)
{
return true;
}
return false;
}
#if USE_ADDRESSABLES
public static partial async YarnTask WaitForAsyncOperation(AsyncOperationHandle operationHandle, CancellationToken cancellationToken)
{
await operationHandle.Task;
}
public static partial async YarnTask WaitForAsyncOperation(AsyncOperationHandle operationHandle, CancellationToken cancellationToken)
{
return await operationHandle.Task;
}
#endif
}
public partial struct YarnTask
{
Task Task;
public TaskAwaiter GetAwaiter() => Task.GetAwaiter();
readonly public bool IsCompleted() => Task.IsCompleted;
readonly public bool IsCompletedSuccessfully() => Task.IsCompletedSuccessfully;
public static implicit operator Task(YarnTask YarnTask)
{
return YarnTask.Task;
}
public static implicit operator YarnTask(Task task)
{
return new YarnTask { Task = task };
}
#if UNITY_2023_1_OR_NEWER
public static implicit operator YarnTask(Awaitable awaitable)
{
return new YarnTask { Task = awaitable.AsTask() };
}
#endif
#if USE_UNITASK
public static implicit operator YarnTask(Cysharp.Threading.Tasks.UniTask uniTask)
{
return new YarnTask { Task = Cysharp.Threading.Tasks.UniTaskExtensions.AsTask(uniTask) };
}
#endif
public static partial YarnTask FromResult(T value)
{
return Task.FromResult(value);
}
readonly public void Forget() { }
}
public partial class YarnTaskCompletionSource
{
private TaskCompletionSource taskCompletionSource = new TaskCompletionSource();
public partial bool TrySetResult()
{
return taskCompletionSource.TrySetResult(1);
}
public partial bool TrySetException(System.Exception exception)
{
return taskCompletionSource.TrySetException(exception);
}
public partial bool TrySetCanceled()
{
return taskCompletionSource.TrySetCanceled();
}
public YarnTask Task => taskCompletionSource.Task;
}
public partial class YarnTaskCompletionSource
{
private TaskCompletionSource taskCompletionSource = new TaskCompletionSource();
public partial bool TrySetResult(T value)
{
return taskCompletionSource.TrySetResult(value);
}
public partial bool TrySetException(System.Exception exception)
{
return taskCompletionSource.TrySetException(exception);
}
public partial bool TrySetCanceled()
{
return taskCompletionSource.TrySetCanceled();
}
public YarnTask Task => taskCompletionSource.Task;
}
static class TaskUtility
{
#if UNITY_2023_1_OR_NEWER
public static async Task AsTask(this Awaitable awaitable)
{
await awaitable;
}
public static async Task AsTask(this Awaitable awaitable)
{
return await awaitable;
}
#endif
}
}
#endif