/* 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