From 14c886ea8fa022de5b899b608915614b75f2d8f7 Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Tue, 25 Jul 2023 16:20:26 +0800 Subject: [PATCH] =?UTF-8?q?[+]=20UniTask=20External=20YooAsset=E3=80=81Dot?= =?UTF-8?q?ween=E3=80=81TextMeshPro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [+] UniTask External YooAsset、Dotween、TextMeshPro --- .../GameFramework/Resource/AssetGroup.cs | 2 +- .../GameFramework/Resource/LruAssetGroup.cs | 3 +- .../GameFramework/Resource/ResourceManager.cs | 9 +- Assets/TEngine/Runtime/UniTask/External.meta | 8 + .../Runtime/UniTask/External/DOTween.meta | 8 + .../DOTween/DOTweenAsyncExtensions.cs | 470 ++++++++++++++++++ .../DOTween/DOTweenAsyncExtensions.cs.meta | 11 + .../Runtime/UniTask/External/TextMeshPro.meta | 8 + .../TextMeshProAsyncExtensions.InputField.cs | 224 +++++++++ ...tMeshProAsyncExtensions.InputField.cs.meta | 11 + .../TextMeshProAsyncExtensions.InputField.tt | 66 +++ ...tMeshProAsyncExtensions.InputField.tt.meta | 7 + .../TextMeshPro/TextMeshProAsyncExtensions.cs | 130 +++++ .../TextMeshProAsyncExtensions.cs.meta | 11 + .../Runtime/UniTask/External/YooAsset.meta | 8 + .../YooAsset/AsyncOperationBaseExtensions.cs | 145 ++++++ .../AsyncOperationBaseExtensions.cs.meta | 3 + .../YooAsset/OperationHandleBaseExtensions.cs | 230 +++++++++ .../OperationHandleBaseExtensions.cs.meta | 3 + .../Runtime/UniTask/_InternalVisibleTo.cs | 4 +- 20 files changed, 1354 insertions(+), 7 deletions(-) create mode 100644 Assets/TEngine/Runtime/UniTask/External.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/DOTween.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs create mode 100644 Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs create mode 100644 Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/YooAsset.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs create mode 100644 Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs.meta create mode 100644 Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs create mode 100644 Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs.meta diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/AssetGroup.cs b/Assets/TEngine/Runtime/GameFramework/Resource/AssetGroup.cs index 52d6619f..3cc05bde 100644 --- a/Assets/TEngine/Runtime/GameFramework/Resource/AssetGroup.cs +++ b/Assets/TEngine/Runtime/GameFramework/Resource/AssetGroup.cs @@ -383,7 +383,7 @@ namespace TEngine Reference(handle); - bool cancelOrFailed = await handle.ToUniTask(cancellationToken: cancellationToken).SuppressCancellationThrow(); + bool cancelOrFailed = await handle.ToUniTask().SuppressCancellationThrow(); if (cancelOrFailed) { diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruAssetGroup.cs b/Assets/TEngine/Runtime/GameFramework/Resource/LruAssetGroup.cs index d8e2dc05..0c552151 100644 --- a/Assets/TEngine/Runtime/GameFramework/Resource/LruAssetGroup.cs +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruAssetGroup.cs @@ -300,8 +300,7 @@ namespace TEngine Reference(handle); - bool cancelOrFailed = - await handle.ToUniTask(cancellationToken: cancellationToken).SuppressCancellationThrow(); + bool cancelOrFailed = await handle.ToUniTask().SuppressCancellationThrow(); if (cancelOrFailed) { diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs b/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs index 85c62519..9eab4587 100644 --- a/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ResourceManager.cs @@ -421,8 +421,9 @@ namespace TEngine public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken) where T : Object { AssetOperationHandle handle = LoadAssetAsyncHandle(assetName); - - await handle.ToUniTask(cancellationToken:cancellationToken); + UniTask uniTask = handle.ToUniTask(); + uniTask.AttachExternalCancellation(cancellationToken); + await uniTask; if (typeof(T) == typeof(GameObject)) { @@ -445,7 +446,9 @@ namespace TEngine public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken) { AssetOperationHandle handle = LoadAssetAsyncHandle(assetName); - await handle.ToUniTask(cancellationToken:cancellationToken); + UniTask uniTask = handle.ToUniTask(); + uniTask.AttachExternalCancellation(cancellationToken); + await uniTask; GameObject ret = handle.InstantiateSync(); AssetReference.BindAssetReference(ret, handle, assetName); diff --git a/Assets/TEngine/Runtime/UniTask/External.meta b/Assets/TEngine/Runtime/UniTask/External.meta new file mode 100644 index 00000000..a3f3abcb --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d061d185023b94440bd65594db58c6a0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/DOTween.meta b/Assets/TEngine/Runtime/UniTask/External/DOTween.meta new file mode 100644 index 00000000..c08509af --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/DOTween.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 25cb2f742bfeb1d48a4e65d3140b955d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs b/Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs new file mode 100644 index 00000000..80ae27c0 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs @@ -0,0 +1,470 @@ +// asmdef Version Defines, enabled when com.demigiant.dotween is imported. + +#if UNITASK_DOTWEEN_SUPPORT + +using Cysharp.Threading.Tasks.Internal; +using DG.Tweening; +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Cysharp.Threading.Tasks +{ + public enum TweenCancelBehaviour + { + Kill, + KillWithCompleteCallback, + Complete, + CompleteWithSequenceCallback, + CancelAwait, + + // AndCancelAwait + KillAndCancelAwait, + KillWithCompleteCallbackAndCancelAwait, + CompleteAndCancelAwait, + CompleteWithSequenceCallbackAndCancelAwait + } + + public static class DOTweenAsyncExtensions + { + enum CallbackType + { + Kill, + Complete, + Pause, + Play, + Rewind, + StepComplete + } + + public static TweenAwaiter GetAwaiter(this Tween tween) + { + return new TweenAwaiter(tween); + } + + public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token); + } + + public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token); + } + + public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token); + } + + public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token); + } + + public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token); + } + + public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token); + } + + public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + + if (!tween.IsActive()) return UniTask.CompletedTask; + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token); + } + + public struct TweenAwaiter : ICriticalNotifyCompletion + { + readonly Tween tween; + + // killed(non active) as completed. + public bool IsCompleted => !tween.IsActive(); + + public TweenAwaiter(Tween tween) + { + this.tween = tween; + } + + public TweenAwaiter GetAwaiter() + { + return this; + } + + public void GetResult() + { + } + + public void OnCompleted(System.Action continuation) + { + UnsafeOnCompleted(continuation); + } + + public void UnsafeOnCompleted(System.Action continuation) + { + // onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true). + tween.onKill = PooledTweenCallback.Create(continuation); + } + } + + sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode + { + static TaskPool pool; + TweenConfiguredSource nextNode; + public ref TweenConfiguredSource NextNode => ref nextNode; + + static TweenConfiguredSource() + { + TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size); + } + + readonly TweenCallback onCompleteCallbackDelegate; + readonly TweenCallback onUpdateDelegate; + + Tween tween; + TweenCancelBehaviour cancelBehaviour; + CancellationToken cancellationToken; + CallbackType callbackType; + bool canceled; + + TweenCallback originalUpdateAction; + TweenCallback originalCompleteAction; + UniTaskCompletionSourceCore core; + + TweenConfiguredSource() + { + onCompleteCallbackDelegate = OnCompleteCallbackDelegate; + onUpdateDelegate = OnUpdate; + } + + public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token) + { + if (cancellationToken.IsCancellationRequested) + { + DoCancelBeforeCreate(tween, cancelBehaviour); + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); + } + + if (!pool.TryPop(out var result)) + { + result = new TweenConfiguredSource(); + } + + result.tween = tween; + result.cancelBehaviour = cancelBehaviour; + result.cancellationToken = cancellationToken; + result.callbackType = callbackType; + + result.originalUpdateAction = tween.onUpdate; + result.canceled = false; + + if (result.originalUpdateAction == result.onUpdateDelegate) + { + result.originalUpdateAction = null; + } + + tween.onUpdate = result.onUpdateDelegate; + + switch (callbackType) + { + case CallbackType.Kill: + result.originalCompleteAction = tween.onKill; + tween.onKill = result.onCompleteCallbackDelegate; + break; + case CallbackType.Complete: + result.originalCompleteAction = tween.onComplete; + tween.onComplete = result.onCompleteCallbackDelegate; + break; + case CallbackType.Pause: + result.originalCompleteAction = tween.onPause; + tween.onPause = result.onCompleteCallbackDelegate; + break; + case CallbackType.Play: + result.originalCompleteAction = tween.onPlay; + tween.onPlay = result.onCompleteCallbackDelegate; + break; + case CallbackType.Rewind: + result.originalCompleteAction = tween.onRewind; + tween.onRewind = result.onCompleteCallbackDelegate; + break; + case CallbackType.StepComplete: + result.originalCompleteAction = tween.onStepComplete; + tween.onStepComplete = result.onCompleteCallbackDelegate; + break; + default: + break; + } + + if (result.originalCompleteAction == result.onCompleteCallbackDelegate) + { + result.originalCompleteAction = null; + } + + TaskTracker.TrackActiveTask(result, 3); + + token = result.core.Version; + return result; + } + + void OnCompleteCallbackDelegate() + { + if (cancellationToken.IsCancellationRequested) + { + if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait + || this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait + || this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait + || this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait + || this.cancelBehaviour == TweenCancelBehaviour.CancelAwait) + { + canceled = true; + } + } + if (canceled) + { + core.TrySetCanceled(cancellationToken); + } + else + { + originalCompleteAction?.Invoke(); + core.TrySetResult(AsyncUnit.Default); + } + } + + void OnUpdate() + { + originalUpdateAction?.Invoke(); + + if (!cancellationToken.IsCancellationRequested) + { + return; + } + + switch (this.cancelBehaviour) + { + case TweenCancelBehaviour.Kill: + default: + this.tween.Kill(false); + break; + case TweenCancelBehaviour.KillAndCancelAwait: + this.canceled = true; + this.tween.Kill(false); + break; + case TweenCancelBehaviour.KillWithCompleteCallback: + this.tween.Kill(true); + break; + case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait: + this.canceled = true; + this.tween.Kill(true); + break; + case TweenCancelBehaviour.Complete: + this.tween.Complete(false); + break; + case TweenCancelBehaviour.CompleteAndCancelAwait: + this.canceled = true; + this.tween.Complete(false); + break; + case TweenCancelBehaviour.CompleteWithSequenceCallback: + this.tween.Complete(true); + break; + case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait: + this.canceled = true; + this.tween.Complete(true); + break; + case TweenCancelBehaviour.CancelAwait: + // restore to original callback + switch (callbackType) + { + case CallbackType.Kill: + tween.onKill = originalCompleteAction; + break; + case CallbackType.Complete: + tween.onComplete = originalCompleteAction; + break; + case CallbackType.Pause: + tween.onPause = originalCompleteAction; + break; + case CallbackType.Play: + tween.onPlay = originalCompleteAction; + break; + case CallbackType.Rewind: + tween.onRewind = originalCompleteAction; + break; + case CallbackType.StepComplete: + tween.onStepComplete = originalCompleteAction; + break; + default: + break; + } + + this.core.TrySetCanceled(this.cancellationToken); + break; + } + } + + static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour) + { + + switch (tweenCancelBehaviour) + { + case TweenCancelBehaviour.Kill: + default: + tween.Kill(false); + break; + case TweenCancelBehaviour.KillAndCancelAwait: + tween.Kill(false); + break; + case TweenCancelBehaviour.KillWithCompleteCallback: + tween.Kill(true); + break; + case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait: + tween.Kill(true); + break; + case TweenCancelBehaviour.Complete: + tween.Complete(false); + break; + case TweenCancelBehaviour.CompleteAndCancelAwait: + tween.Complete(false); + break; + case TweenCancelBehaviour.CompleteWithSequenceCallback: + tween.Complete(true); + break; + case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait: + tween.Complete(true); + break; + case TweenCancelBehaviour.CancelAwait: + break; + } + } + + public void GetResult(short token) + { + try + { + core.GetResult(token); + } + finally + { + TryReturn(); + } + } + + public UniTaskStatus GetStatus(short token) + { + return core.GetStatus(token); + } + + public UniTaskStatus UnsafeGetStatus() + { + return core.UnsafeGetStatus(); + } + + public void OnCompleted(Action continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + bool TryReturn() + { + TaskTracker.RemoveTracking(this); + core.Reset(); + tween.onUpdate = originalUpdateAction; + + switch (callbackType) + { + case CallbackType.Kill: + tween.onKill = originalCompleteAction; + break; + case CallbackType.Complete: + tween.onComplete = originalCompleteAction; + break; + case CallbackType.Pause: + tween.onPause = originalCompleteAction; + break; + case CallbackType.Play: + tween.onPlay = originalCompleteAction; + break; + case CallbackType.Rewind: + tween.onRewind = originalCompleteAction; + break; + case CallbackType.StepComplete: + tween.onStepComplete = originalCompleteAction; + break; + default: + break; + } + + tween = default; + cancellationToken = default; + originalUpdateAction = default; + originalCompleteAction = default; + return pool.TryPush(this); + } + } + } + + sealed class PooledTweenCallback + { + static readonly ConcurrentQueue pool = new ConcurrentQueue(); + + readonly TweenCallback runDelegate; + + Action continuation; + + + PooledTweenCallback() + { + runDelegate = Run; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TweenCallback Create(Action continuation) + { + if (!pool.TryDequeue(out var item)) + { + item = new PooledTweenCallback(); + } + + item.continuation = continuation; + return item.runDelegate; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void Run() + { + var call = continuation; + continuation = null; + if (call != null) + { + pool.Enqueue(this); + call.Invoke(); + } + } + } +} + +#endif diff --git a/Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs.meta b/Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs.meta new file mode 100644 index 00000000..63131b04 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/DOTween/DOTweenAsyncExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f448d5bc5b232e4f98d89d5d1832e8e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro.meta b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro.meta new file mode 100644 index 00000000..ac860aa8 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f89da606bde9a4e4e94ae1189a029887 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs new file mode 100644 index 00000000..22f081c2 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs @@ -0,0 +1,224 @@ +#if UNITASK_TEXTMESHPRO_SUPPORT + +using System; +using System.Threading; +using TMPro; + +namespace Cysharp.Threading.Tasks +{ + public static partial class TextMeshProAsyncExtensions + { + public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncValueChangedEventHandler GetAsyncValueChangedEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onValueChanged, cancellationToken, false); + } + + public static UniTask OnValueChangedAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask OnValueChangedAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onValueChanged, cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onValueChanged, cancellationToken); + } + + public static IAsyncEndEditEventHandler GetAsyncEndEditEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncEndEditEventHandler GetAsyncEndEditEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onEndEdit, cancellationToken, false); + } + + public static UniTask OnEndEditAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask OnEndEditAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onEndEdit, cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable OnEndEditAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable OnEndEditAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onEndEdit, cancellationToken); + } + + public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, false); + } + + public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken); + } + + public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, false); + } + + public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken); + } + + public static IAsyncDeselectEventHandler GetAsyncDeselectEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncDeselectEventHandler GetAsyncDeselectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onDeselect, cancellationToken, false); + } + + public static UniTask OnDeselectAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask OnDeselectAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onDeselect, cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable OnDeselectAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable OnDeselectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onDeselect, cancellationToken); + } + + public static IAsyncSelectEventHandler GetAsyncSelectEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncSelectEventHandler GetAsyncSelectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onSelect, cancellationToken, false); + } + + public static UniTask OnSelectAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask OnSelectAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onSelect, cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable OnSelectAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onSelect, inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable OnSelectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onSelect, cancellationToken); + } + + public static IAsyncSubmitEventHandler GetAsyncSubmitEventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsyncSubmitEventHandler GetAsyncSubmitEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onSubmit, cancellationToken, false); + } + + public static UniTask OnSubmitAsync(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask OnSubmitAsync(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler(inputField.onSubmit, cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable OnSubmitAsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable OnSubmitAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable(inputField.onSubmit, cancellationToken); + } + + } +} + +#endif \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs.meta b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs.meta new file mode 100644 index 00000000..2e39d2e8 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79f4f2475e0b2c44e97ed1dee760627b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt new file mode 100644 index 00000000..f9acf4dc --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt @@ -0,0 +1,66 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +<# + var handlers = new (string name, string type)[] { + ("ValueChanged", "string"), + ("EndEdit", "string"), + ("EndTextSelection", "(string, int, int)"), + ("TextSelection", "(string, int, int)"), + ("Deselect", "string"), + ("Select", "string"), + ("Submit", "string"), + }; + + Func shouldConvert = x => x.EndsWith("TextSelection"); + Func eventName = x => shouldConvert(x) ? $"new TextSelectionEventConverter(inputField.on{x})" : $"inputField.on{x}"; +#> +#if UNITASK_TEXTMESHPRO_SUPPORT + +using System; +using System.Threading; +using TMPro; + +namespace Cysharp.Threading.Tasks +{ + public static partial class TextMeshProAsyncExtensions + { +<# foreach(var (name, type) in handlers) { #> + public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), false); + } + + public static IAsync<#= (name) #>EventHandler<<#= type #>> GetAsync<#= (name) #>EventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, false); + } + + public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField) + { + return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); + } + + public static UniTask<<#= type #>> On<#= (name) #>Async(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new AsyncUnityEventHandler<<#= type #>>(<#= eventName(name) #>, cancellationToken, true).OnInvokeAsync(); + } + + public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField) + { + return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, inputField.GetCancellationTokenOnDestroy()); + } + + public static IUniTaskAsyncEnumerable<<#= type #>> On<#= (name) #>AsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) + { + return new UnityEventHandlerAsyncEnumerable<<#= type #>>(<#= eventName(name) #>, cancellationToken); + } + +<# } #> + } +} + +#endif \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt.meta b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt.meta new file mode 100644 index 00000000..d27b2e05 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.InputField.tt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e9bb9fc551a975d44a7180e022a2debe +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs new file mode 100644 index 00000000..362aa830 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs @@ -0,0 +1,130 @@ +#if UNITASK_TEXTMESHPRO_SUPPORT + +using System; +using System.Threading; +using TMPro; +using UnityEngine.Events; + +namespace Cysharp.Threading.Tasks +{ + public static partial class TextMeshProAsyncExtensions + { + // -> Text + public static void BindTo(this IUniTaskAsyncEnumerable source, TMP_Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + public static void BindTo(this IUniTaskAsyncEnumerable source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true) + { + BindToCore(source, text, cancellationToken, rebindOnError).Forget(); + } + + static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError) + { + var repeat = false; + BIND_AGAIN: + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (true) + { + bool moveNext; + try + { + moveNext = await e.MoveNextAsync(); + repeat = false; + } + catch (Exception ex) + { + if (ex is OperationCanceledException) return; + + if (rebindOnError && !repeat) + { + repeat = true; + goto BIND_AGAIN; + } + else + { + throw; + } + } + + if (!moveNext) return; + + text.text = e.Current; + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + + // -> Text + + public static void BindTo(this IUniTaskAsyncEnumerable source, TMP_Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + public static void BindTo(this IUniTaskAsyncEnumerable source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true) + { + BindToCore(source, text, cancellationToken, rebindOnError).Forget(); + } + + public static void BindTo(this AsyncReactiveProperty source, TMP_Text text, bool rebindOnError = true) + { + BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); + } + + static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError) + { + var repeat = false; + BIND_AGAIN: + var e = source.GetAsyncEnumerator(cancellationToken); + try + { + while (true) + { + bool moveNext; + try + { + moveNext = await e.MoveNextAsync(); + repeat = false; + } + catch (Exception ex) + { + if (ex is OperationCanceledException) return; + + if (rebindOnError && !repeat) + { + repeat = true; + goto BIND_AGAIN; + } + else + { + throw; + } + } + + if (!moveNext) return; + + text.text = e.Current.ToString(); + } + } + finally + { + if (e != null) + { + await e.DisposeAsync(); + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs.meta b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs.meta new file mode 100644 index 00000000..752d125c --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/TextMeshPro/TextMeshProAsyncExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6ba480edafb67d4e91bb10feb64fae5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/YooAsset.meta b/Assets/TEngine/Runtime/UniTask/External/YooAsset.meta new file mode 100644 index 00000000..3d9fada4 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/YooAsset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a55ef7d1bba14cc982b478d482c4461 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs b/Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs new file mode 100644 index 00000000..f55159ec --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs @@ -0,0 +1,145 @@ +using System; +using YooAsset; +using static Cysharp.Threading.Tasks.Internal.Error; + +namespace Cysharp.Threading.Tasks +{ + public static class AsyncOperationBaseExtensions + { + public static UniTask.Awaiter GetAwaiter(this AsyncOperationBase handle) + { + return ToUniTask(handle).GetAwaiter(); + } + + public static UniTask ToUniTask(this AsyncOperationBase handle, + IProgress progress = null, + PlayerLoopTiming timing = PlayerLoopTiming.Update) + { + ThrowArgumentNullException(handle, nameof(handle)); + + if(handle.IsDone) + { + return UniTask.CompletedTask; + } + + return new UniTask( + AsyncOperationBaserConfiguredSource.Create( + handle, + timing, + progress, + out var token + ), + token + ); + } + + sealed class AsyncOperationBaserConfiguredSource : IUniTaskSource, + IPlayerLoopItem, + ITaskPoolNode + { + private static TaskPool pool; + + private AsyncOperationBaserConfiguredSource nextNode; + + public ref AsyncOperationBaserConfiguredSource NextNode => ref nextNode; + + static AsyncOperationBaserConfiguredSource() + { + TaskPool.RegisterSizeGetter(typeof(AsyncOperationBaserConfiguredSource), () => pool.Size); + } + + private readonly Action continuationAction; + private AsyncOperationBase handle; + private IProgress progress; + private bool completed; + private UniTaskCompletionSourceCore core; + + AsyncOperationBaserConfiguredSource() { continuationAction = Continuation; } + + public static IUniTaskSource Create(AsyncOperationBase handle, + PlayerLoopTiming timing, + IProgress progress, + out short token) + { + if(!pool.TryPop(out var result)) + { + result = new AsyncOperationBaserConfiguredSource(); + } + + result.handle = handle; + result.progress = progress; + result.completed = false; + TaskTracker.TrackActiveTask(result, 3); + + if(progress != null) + { + PlayerLoopHelper.AddAction(timing, result); + } + + handle.Completed += result.continuationAction; + + token = result.core.Version; + + return result; + } + + private void Continuation(AsyncOperationBase _) + { + handle.Completed -= continuationAction; + + if(completed) + { + TryReturn(); + } + else + { + completed = true; + if(handle.Status == EOperationStatus.Failed) + { + core.TrySetException(new Exception(handle.Error)); + } + else + { + core.TrySetResult(AsyncUnit.Default); + } + } + } + + bool TryReturn() + { + TaskTracker.RemoveTracking(this); + core.Reset(); + handle = default; + progress = default; + return pool.TryPush(this); + } + + public UniTaskStatus GetStatus(short token) => core.GetStatus(token); + + public void OnCompleted(Action continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + public void GetResult(short token) { core.GetResult(token); } + + public UniTaskStatus UnsafeGetStatus() => core.UnsafeGetStatus(); + + public bool MoveNext() + { + if(completed) + { + TryReturn(); + return false; + } + + if(!handle.IsDone) + { + progress?.Report(handle.Progress); + } + + return true; + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs.meta b/Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs.meta new file mode 100644 index 00000000..db5f0453 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/YooAsset/AsyncOperationBaseExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2cceff5ec1f84bd0a6a9b4ed7719527c +timeCreated: 1651978895 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs b/Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs new file mode 100644 index 00000000..06e5b07e --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs @@ -0,0 +1,230 @@ +#if UNITY_2020_1_OR_NEWER && ! UNITY_2021 +#define UNITY_2020_BUG +#endif + +using System; +using System.Runtime.CompilerServices; +using YooAsset; +using static Cysharp.Threading.Tasks.Internal.Error; + + +namespace Cysharp.Threading.Tasks +{ + public static class OperationHandleBaseExtensions + { + public static UniTask.Awaiter GetAwaiter(this OperationHandleBase handle) + { + return ToUniTask(handle).GetAwaiter(); + } + + public static UniTask ToUniTask(this OperationHandleBase handle, + IProgress progress = null, + PlayerLoopTiming timing = PlayerLoopTiming.Update) + { + ThrowArgumentNullException(handle, nameof(handle)); + + if(!handle.IsValid) + { + return UniTask.CompletedTask; + } + + return new UniTask( + OperationHandleBaserConfiguredSource.Create( + handle, + timing, + progress, + out var token + ), + token + ); + } + + sealed class OperationHandleBaserConfiguredSource : IUniTaskSource, + IPlayerLoopItem, + ITaskPoolNode + { + private static TaskPool pool; + + private OperationHandleBaserConfiguredSource nextNode; + + public ref OperationHandleBaserConfiguredSource NextNode => ref nextNode; + + static OperationHandleBaserConfiguredSource() + { + TaskPool.RegisterSizeGetter(typeof(OperationHandleBaserConfiguredSource), () => pool.Size); + } + + private readonly Action continuationAction; + private OperationHandleBase handle; + private IProgress progress; + private bool completed; + private UniTaskCompletionSourceCore core; + + OperationHandleBaserConfiguredSource() { continuationAction = Continuation; } + + public static IUniTaskSource Create(OperationHandleBase handle, + PlayerLoopTiming timing, + IProgress progress, + out short token) + { + if(!pool.TryPop(out var result)) + { + result = new OperationHandleBaserConfiguredSource(); + } + + result.handle = handle; + result.progress = progress; + result.completed = false; + TaskTracker.TrackActiveTask(result, 3); + + if(progress != null) + { + PlayerLoopHelper.AddAction(timing, result); + } + + // BUG 在 Unity 2020.3.36 版本测试中, IL2Cpp 会报 如下错误 + // BUG ArgumentException: Incompatible Delegate Types. First is System.Action`1[[YooAsset.AssetOperationHandle, YooAsset, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] second is System.Action`1[[YooAsset.OperationHandleBase, YooAsset, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] + // BUG 也可能报的是 Action '1' Action '1' 的 InvalidCastException + // BUG 此处不得不这么修改, 如果后续 Unity 修复了这个问题, 可以恢复之前的写法 +#if UNITY_2020_BUG + switch(handle) + { + case AssetOperationHandle asset_handle: + asset_handle.Completed += result.AssetContinuation; + break; + case SceneOperationHandle scene_handle: + scene_handle.Completed += result.SceneContinuation; + break; + case SubAssetsOperationHandle sub_asset_handle: + sub_asset_handle.Completed += result.SubContinuation; + break; + case RawFileOperationHandle raw_file_handle: + raw_file_handle.Completed += result.RawFileContinuation; + break; + } +#else + switch (handle) + { + case AssetOperationHandle asset_handle: + asset_handle.Completed += result.continuationAction; + break; + case SceneOperationHandle scene_handle: + scene_handle.Completed += result.continuationAction; + break; + case SubAssetsOperationHandle sub_asset_handle: + sub_asset_handle.Completed += result.continuationAction; + break; + case RawFileOperationHandle raw_file_handle: + raw_file_handle.Completed += result.continuationAction; + break; + } +#endif + token = result.core.Version; + + return result; + } +#if UNITY_2020_BUG + private void AssetContinuation(AssetOperationHandle handle) + { + handle.Completed -= AssetContinuation; + BaseContinuation(); + } + + private void SceneContinuation(SceneOperationHandle handle) + { + handle.Completed -= SceneContinuation; + BaseContinuation(); + } + + private void SubContinuation(SubAssetsOperationHandle handle) + { + handle.Completed -= SubContinuation; + BaseContinuation(); + } + + private void RawFileContinuation(RawFileOperationHandle handle) + { + handle.Completed -= RawFileContinuation; + BaseContinuation(); + } +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void BaseContinuation() + { + if(completed) + { + TryReturn(); + } + else + { + completed = true; + if(handle.Status == EOperationStatus.Failed) + { + core.TrySetException(new Exception(handle.LastError)); + } + else + { + core.TrySetResult(AsyncUnit.Default); + } + } + } + + private void Continuation(OperationHandleBase _) + { + switch(handle) + { + case AssetOperationHandle asset_handle: + asset_handle.Completed -= continuationAction; + break; + case SceneOperationHandle scene_handle: + scene_handle.Completed -= continuationAction; + break; + case SubAssetsOperationHandle sub_asset_handle: + sub_asset_handle.Completed -= continuationAction; + break; + case RawFileOperationHandle raw_file_handle: + raw_file_handle.Completed -= continuationAction; + break; + } + + BaseContinuation(); + } + + bool TryReturn() + { + TaskTracker.RemoveTracking(this); + core.Reset(); + handle = default; + progress = default; + return pool.TryPush(this); + } + + public UniTaskStatus GetStatus(short token) => core.GetStatus(token); + + public void OnCompleted(Action continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + public void GetResult(short token) { core.GetResult(token); } + + public UniTaskStatus UnsafeGetStatus() => core.UnsafeGetStatus(); + + public bool MoveNext() + { + if(completed) + { + TryReturn(); + return false; + } + + if(handle.IsValid) + { + progress?.Report(handle.Progress); + } + + return true; + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs.meta b/Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs.meta new file mode 100644 index 00000000..b43f66e7 --- /dev/null +++ b/Assets/TEngine/Runtime/UniTask/External/YooAsset/OperationHandleBaseExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e1c9a3a6de2246bf88547a6b59b99b9f +timeCreated: 1650851321 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/UniTask/_InternalVisibleTo.cs b/Assets/TEngine/Runtime/UniTask/_InternalVisibleTo.cs index ab7c10c9..2a500280 100644 --- a/Assets/TEngine/Runtime/UniTask/_InternalVisibleTo.cs +++ b/Assets/TEngine/Runtime/UniTask/_InternalVisibleTo.cs @@ -1,6 +1,8 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("TEngine.Runtime")] [assembly: InternalsVisibleTo("UniTask.Linq")] [assembly: InternalsVisibleTo("UniTask.Addressables")] [assembly: InternalsVisibleTo("UniTask.DOTween")] -[assembly: InternalsVisibleTo("UniTask.TextMeshPro")] \ No newline at end of file +[assembly: InternalsVisibleTo("UniTask.TextMeshPro")] +[assembly: InternalsVisibleTo("UniTask.YooAsset")]