diff --git a/Documentation/Async.md b/Documentation/Async.md new file mode 100644 index 000000000..84454e255 --- /dev/null +++ b/Documentation/Async.md @@ -0,0 +1,89 @@ + +## Async Extensions *Experimental* + + +## Table Of Contents + +* Introduction + * `async` in DI + * Example +* Advanced + * Static Memory Pools + * Using statements and dispose pattern + + + +## Introduction +### Async in DI + +In dependency injection, the injector resolves dependencies of the target class only once, often after class is first created. In other words, injection is a one time process that does not track the injected dependencies to update them later on. If a dependency is not ready at the moment of injection, either the binding wouldn't resolve in case of optional bindings or would fail completely throwing an error. + +This creates a dilemma while implementing dependencies that are resolved asynchronous. You can design around the DI limitations by carefully designing your code so that the injection happens after the `async` process is completed. This requires careful planning, which leads to an increased complexity in the setup, and is also prone to errors. + +Alternatively you can inject an intermediary object that tracks the result of the `async` operation. When you need to access the dependency, you can use this intermediary object to check if the `async` task is completed and get the resulting object. With the experimental `async` support, we would like to provide ways to tackle this problem in Extenject. You can find `async` extensions in the folder **Plugins/Zenject/OptionalExtras/Async**. + +### Example + +Lets see how we can inject async dependencies through an intermediary object. Async extensions implements `AsyncInject` as this intermediary. You can use it as follows. + + +```csharp +public class TestInstaller : MonoInstaller +{ + public override void InstallBindings() + { + Container.BindAsync().FromMethod(async () => + { + await Task.Delay(100); + return (IFoo) new Foo(); + }).AsCached(); + } +} + +public class Bar : IInitializable, IDisposable +{ + readonly AsyncInject _asyncFoo; + IFoo _foo; + public Bar(AsyncInject asyncFoo) + { + _asyncFoo = asyncFoo; + } + + public void Initialize() + { + if (!_asyncFoo.TryGetResult(out _foo)) + { + _asyncFoo.Completed += OnFooReady; + } + } + + private void OnFooReady(IFoo foo) + { + _foo = foo; + } + + public void Dispose() + { + _asyncFoo.Completed -= OnFooReady; + } +} +``` + +Here we use `BindAsync().FromMethod()` to pass an `async` lambda that waits for 100 ms and then returns a newly created `Foo` instance. This method can be any other method with the signature `Task Method()`. *Note: the `async` keyword is an implementation detail and thus not part of the signature. The `BindAsync` extension method provides a separate binder for `async` operations. This binder is currently limited to a few `FromX()` providers. Features like Pooling and Factories are not supported at the moment. + +With the above `AsyncInject` binding, the instance is added to the container. Since the scope is set to `AsCached()` the operation will run only once and `AsyncInject` will keep the result. It is important to note that `async` operations won't start before this binding is getting resolved. If you want `async` operation to start immediately after installing, use `NonLazy()` option. + +Once injected to `Bar`, we can check whether the return value of the `async` operation is already available by calling the `TryGetResult`. method. This method will return `false` if there is no result to return. If result is not ready yet, we can listen to the `Completed` event to get the return value when the `async` operation completes. + +Alternatively we can use the following methods to check the results availability. +```csharp +// Use HasResult to check if result exists +if (_asyncFoo.HasResult) +{ + // Result will throw error if prematurely used. + var foo = _asyncFoo.Result; +} + +// AsyncInject provides custom awaiter +IFoo foo = await _asyncFoo; +``` \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime.meta new file mode 100644 index 000000000..213c02997 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fc8c4aa98540d8d45914c45695c00cc1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AddressableInject.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AddressableInject.cs new file mode 100644 index 000000000..51773f9e2 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AddressableInject.cs @@ -0,0 +1,25 @@ +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS +using System; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine.ResourceManagement.AsyncOperations; +using Zenject; + +[ZenjectAllowDuringValidation] +[NoReflectionBaking] +public class AddressableInject : AsyncInject where T : UnityEngine.Object +{ + private AsyncOperationHandle _handle; + public AsyncOperationHandle AssetReferenceHandle => _handle; + + public AddressableInject(InjectContext context, Func>> asyncMethod) + : base(context) + { + StartAsync(async (ct) => + { + _handle = await asyncMethod(ct); + return _handle.Result; + }, cancellationTokenSource.Token); + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AddressableInject.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AddressableInject.cs.meta new file mode 100644 index 000000000..d02f93e0b --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AddressableInject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a6d9c621b22426bb466edc56d2c97bb +timeCreated: 1595196971 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncDiContainerExtensions.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncDiContainerExtensions.cs new file mode 100644 index 000000000..cf22a7be3 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncDiContainerExtensions.cs @@ -0,0 +1,42 @@ +using ModestTree; + +namespace Zenject +{ + public static class AsyncDiContainerExtensions + { + public static +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS + ConcreteAddressableIdBinderGeneric +#else + ConcreteAsyncIdBinderGeneric +#endif + BindAsync(this DiContainer container) + { + return BindAsync(container, container.StartBinding()); + } + + public static +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS + ConcreteAddressableIdBinderGeneric +#else + ConcreteAsyncIdBinderGeneric +#endif + BindAsync(this DiContainer container, BindStatement bindStatement) + { + var bindInfo = bindStatement.SpawnBindInfo(); + + Assert.That(!typeof(TContract).DerivesFrom(), + "You should not use Container.BindAsync for factory classes. Use Container.BindFactory instead."); + + Assert.That(!bindInfo.ContractTypes.Contains(typeof(AsyncInject))); + bindInfo.ContractTypes.Add(typeof(IAsyncInject)); + bindInfo.ContractTypes.Add(typeof(AsyncInject)); + +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS + return new ConcreteAddressableIdBinderGeneric(container, bindInfo, bindStatement); +#else + return new ConcreteAsyncIdBinderGeneric(container, bindInfo, bindStatement); +#endif + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncDiContainerExtensions.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncDiContainerExtensions.cs.meta new file mode 100644 index 000000000..beb735e49 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncDiContainerExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 552755346c0745d7bd238b074b30353d +timeCreated: 1593918171 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncInject.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncInject.cs new file mode 100644 index 000000000..e1f598eed --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncInject.cs @@ -0,0 +1,138 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using ModestTree; + +namespace Zenject +{ + public interface IAsyncInject + { + bool HasResult { get; } + bool IsCancelled { get; } + bool IsFaulted { get; } + bool IsCompleted { get; } + + TaskAwaiter GetAwaiter(); + } + + + [ZenjectAllowDuringValidation] + [NoReflectionBaking] + public class AsyncInject : IAsyncInject + { + protected readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + protected readonly InjectContext _context; + + public event Action Completed; + public event Action Faulted; + public event Action Cancelled; + + public bool HasResult { get; protected set; } + public bool IsSuccessful { get; protected set; } + public bool IsCancelled { get; protected set; } + public bool IsFaulted { get; protected set; } + + public bool IsCompleted => IsSuccessful || IsCancelled || IsFaulted; + + T _result; + Task task; + + protected AsyncInject(InjectContext context) + { + _context = context; + } + + public AsyncInject(InjectContext context, Func> asyncMethod) + { + _context = context; + + StartAsync(asyncMethod, cancellationTokenSource.Token); + } + + public void Cancel() + { + cancellationTokenSource.Cancel(); + } + + protected async void StartAsync(Func> asyncMethod, CancellationToken token) + { + try + { + task = asyncMethod(token); + await task; + } + catch (AggregateException e) + { + HandleFaulted(e); + return; + } + catch (Exception e) + { + HandleFaulted(new AggregateException(e)); + return; + } + + if (token.IsCancellationRequested) + { + HandleCancelled(); + return; + } + + if (task.IsCompleted) + { + HandleCompleted(task.Result); + }else if (task.IsCanceled) + { + HandleCancelled(); + }else if (task.IsFaulted) + { + HandleFaulted(task.Exception); + } + } + + private void HandleCompleted(T result) + { + _result = result; + HasResult = !result.Equals(default(T)); + IsSuccessful = true; + Completed?.Invoke(result); + } + + private void HandleCancelled() + { + IsCancelled = true; + Cancelled?.Invoke(); + } + + private void HandleFaulted(AggregateException exception) + { + IsFaulted = true; + Faulted?.Invoke(exception); + } + + public bool TryGetResult(out T result) + { + if (HasResult) + { + result = _result; + return true; + } + result = default; + return false; + } + + public T Result + { + get + { + Assert.That(HasResult, "AsyncInject does not have a result. "); + return _result; + } + } + + public TaskAwaiter GetAwaiter() => task.GetAwaiter(); + + TaskAwaiter IAsyncInject.GetAwaiter() => task.ContinueWith(task => { }).GetAwaiter(); + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncInject.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncInject.cs.meta new file mode 100644 index 000000000..5c6580985 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/AsyncInject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ccd159e3fca64594aed21b2c23728200 +timeCreated: 1593623842 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders.meta new file mode 100644 index 000000000..81a4f2fd7 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea94649cc87248d6b7c83152035c58ef +timeCreated: 1593628209 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AddressableFromBinderGeneric.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AddressableFromBinderGeneric.cs new file mode 100644 index 000000000..7f2a8905a --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AddressableFromBinderGeneric.cs @@ -0,0 +1,43 @@ +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS +using System; +using System.Threading.Tasks; +using UnityEngine.AddressableAssets; +using UnityEngine.Assertions; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Zenject +{ + [NoReflectionBaking] + public class AddressableFromBinderGeneric : AsyncFromBinderGeneric + where TConcrete : TContract + { + public AddressableFromBinderGeneric( + DiContainer container, BindInfo bindInfo, + BindStatement bindStatement) + : base(container, bindInfo, bindStatement) + {} + + public AsyncFromBinderBase FromAssetReferenceT(AssetReferenceT reference) + where TConcreteObj:UnityEngine.Object, TConcrete + { + BindInfo.RequireExplicitScope = false; + + var contractType = typeof(TContract); + if (typeof(UnityEngine.Object).IsAssignableFrom(contractType)) + { + var addressableInjectType = typeof(AddressableInject<>).MakeGenericType(typeof(TContract)); + BindInfo.ContractTypes.Add(addressableInjectType); + } + + // Don't know how it's created so can't assume here that it violates AsSingle + BindInfo.MarkAsCreationBinding = false; + SubFinalizer = new ScopableBindingFinalizer( + BindInfo, + (container, originalType) => new AddressableProviderSimple(reference)); + + return this; + } + + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AddressableFromBinderGeneric.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AddressableFromBinderGeneric.cs.meta new file mode 100644 index 000000000..4960a1fa4 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AddressableFromBinderGeneric.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4617aa76923941fca100087c6a1c0f47 +timeCreated: 1595185267 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderBase.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderBase.cs new file mode 100644 index 000000000..4dad5c65f --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderBase.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.Contracts; + +namespace Zenject +{ + [NoReflectionBaking] + public class AsyncFromBinderBase : ScopeConcreteIdArgConditionCopyNonLazyBinder + { + public AsyncFromBinderBase(DiContainer bindContainer, Type contractType, BindInfo bindInfo) + : base(bindInfo) + { + BindContainer = bindContainer; + ContractType = contractType; + } + + internal DiContainer BindContainer + { + get; private set; + } + + protected Type ContractType + { + get; private set; + } + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderBase.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderBase.cs.meta new file mode 100644 index 000000000..43f8530c3 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: afefb93ca4194de4a97fe68d8ce948e4 +timeCreated: 1593628300 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderGeneric.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderGeneric.cs new file mode 100644 index 000000000..27818ff1a --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderGeneric.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Zenject +{ + [NoReflectionBaking] + public class AsyncFromBinderGeneric : AsyncFromBinderBase where TConcrete : TContract + { + public AsyncFromBinderGeneric( + DiContainer container, BindInfo bindInfo, + BindStatement bindStatement) + : base(container, typeof(TContract), bindInfo) + { + BindStatement = bindStatement; + } + + protected BindStatement BindStatement + { + get; private set; + } + + protected IBindingFinalizer SubFinalizer + { + set { BindStatement.SetFinalizer(value); } + } + + public AsyncFromBinderBase FromMethod(Func> method) + { + BindInfo.RequireExplicitScope = false; + // Don't know how it's created so can't assume here that it violates AsSingle + BindInfo.MarkAsCreationBinding = false; + SubFinalizer = new ScopableBindingFinalizer( + BindInfo, + (container, originalType) => new AsyncMethodProviderSimple(method)); + + return this; + } + + public AsyncFromBinderBase FromMethod(Func> method) + { + BindInfo.RequireExplicitScope = false; + // Don't know how it's created so can't assume here that it violates AsSingle + BindInfo.MarkAsCreationBinding = false; + SubFinalizer = new ScopableBindingFinalizer( + BindInfo, + (container, originalType) => new AsyncMethodProviderSimple(method)); + + return this; + } + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderGeneric.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderGeneric.cs.meta new file mode 100644 index 000000000..d76b01e99 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/AsyncFromBinderGeneric.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a90ebf554b2241dd89c69883e3bd399e +timeCreated: 1593628851 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableBinderGeneric.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableBinderGeneric.cs new file mode 100644 index 000000000..3d167639b --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableBinderGeneric.cs @@ -0,0 +1,23 @@ +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS +namespace Zenject +{ + [NoReflectionBaking] + public class ConcreteAddressableBinderGeneric : AddressableFromBinderGeneric + { + public ConcreteAddressableBinderGeneric( + DiContainer bindContainer, BindInfo bindInfo, + BindStatement bindStatement) + : base(bindContainer, bindInfo, bindStatement) + { + bindInfo.ToChoice = ToChoices.Self; + } + + public AddressableFromBinderGeneric To() + where TConcrete : TContract + { + return new AddressableFromBinderGeneric( + BindContainer, BindInfo, BindStatement); + } + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableBinderGeneric.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableBinderGeneric.cs.meta new file mode 100644 index 000000000..c3aeb0f01 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableBinderGeneric.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8f00f9577e78451f9c5bba42164bbbe6 +timeCreated: 1595185142 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableIdBinderGeneric.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableIdBinderGeneric.cs new file mode 100644 index 000000000..f1ce025ed --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableIdBinderGeneric.cs @@ -0,0 +1,22 @@ +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS + +namespace Zenject +{ + [NoReflectionBaking] + public class ConcreteAddressableIdBinderGeneric : ConcreteAddressableBinderGeneric + { + public ConcreteAddressableIdBinderGeneric( + DiContainer bindContainer, BindInfo bindInfo, + BindStatement bindStatement) + : base(bindContainer, bindInfo, bindStatement) + {} + + public ConcreteAddressableIdBinderGeneric WithId(object identifier) + { + BindInfo.Identifier = identifier; + return this; + } + + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableIdBinderGeneric.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableIdBinderGeneric.cs.meta new file mode 100644 index 000000000..f812811c4 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAddressableIdBinderGeneric.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6b8a4f8f2d7d4987bf374adc67374d87 +timeCreated: 1595184977 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncBinderGeneric.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncBinderGeneric.cs new file mode 100644 index 000000000..cd7d1df84 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncBinderGeneric.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ModestTree; + +namespace Zenject +{ + [NoReflectionBaking] + public class ConcreteAsyncBinderGeneric : AsyncFromBinderGeneric + { + public ConcreteAsyncBinderGeneric( + DiContainer bindContainer, BindInfo bindInfo, + BindStatement bindStatement) + : base(bindContainer, bindInfo, bindStatement) + { + bindInfo.ToChoice = ToChoices.Self; + } + + public AsyncFromBinderGeneric To() + where TConcrete : TContract + { + return new AsyncFromBinderGeneric( + BindContainer, BindInfo, BindStatement); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncBinderGeneric.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncBinderGeneric.cs.meta new file mode 100644 index 000000000..cbf761034 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncBinderGeneric.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 11dfa5bdf00747969d6004197d2b5474 +timeCreated: 1593631454 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncIdBinderGeneric.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncIdBinderGeneric.cs new file mode 100644 index 000000000..719aa6443 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncIdBinderGeneric.cs @@ -0,0 +1,19 @@ +namespace Zenject +{ + [NoReflectionBaking] + public class ConcreteAsyncIdBinderGeneric : ConcreteAsyncBinderGeneric + { + public ConcreteAsyncIdBinderGeneric( + DiContainer bindContainer, BindInfo bindInfo, + BindStatement bindStatement) + : base(bindContainer, bindInfo, bindStatement) + { + } + + public ConcreteAsyncBinderGeneric WithId(object identifier) + { + BindInfo.Identifier = identifier; + return this; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncIdBinderGeneric.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncIdBinderGeneric.cs.meta new file mode 100644 index 000000000..9c5f9ce6f --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Binders/ConcreteAsyncIdBinderGeneric.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7e53ee42d6a14260b7836ce3b9121958 +timeCreated: 1593626138 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Extenject-Async.asmdef b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Extenject-Async.asmdef new file mode 100644 index 000000000..7c9a79716 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Extenject-Async.asmdef @@ -0,0 +1,23 @@ +{ + "name": "Extenject-Async", + "references": [ + "GUID:0d8beb7f090555447a6cf5ce9e54dbb4", + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:84651a3751eca9349aac36a66bba901b" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.addressables", + "expression": "0.0.0", + "define": "EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Extenject-Async.asmdef.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Extenject-Async.asmdef.meta new file mode 100644 index 000000000..99b329fd9 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Extenject-Async.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9bf848f31e601a249a6bebdc53287470 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers.meta new file mode 100644 index 000000000..5401adf0f --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 07ce4df13cf04773a2251f822737f454 +timeCreated: 1593623009 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AddressableProviderSimple.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AddressableProviderSimple.cs new file mode 100644 index 000000000..9522258de --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AddressableProviderSimple.cs @@ -0,0 +1,52 @@ +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using ModestTree; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Zenject +{ + [NoReflectionBaking] + public class AddressableProviderSimple : IProvider where TConcrete : UnityEngine.Object, TContract + { + private AssetReferenceT assetReference; + + public AddressableProviderSimple(AssetReferenceT assetReference) + { + this.assetReference = assetReference; + } + + public bool TypeVariesBasedOnMemberType => false; + public bool IsCached => false; + public Type GetInstanceType(InjectContext context) => typeof(TConcrete); + + public void GetAllInstancesWithInjectSplit(InjectContext context, List args, out Action injectAction, List buffer) + { + Assert.IsEmpty(args); + Assert.IsNotNull(context); + + injectAction = null; + + Func>> addressableLoadDelegate = async (_) => + { + AsyncOperationHandle loadHandle = Addressables.LoadAssetAsync(assetReference); + await loadHandle.Task; + + if (loadHandle.Status == AsyncOperationStatus.Failed) + { + throw new Exception("Async operation failed", loadHandle.OperationException); + } + + return loadHandle; + }; + + var asyncInject = new AddressableInject(context, addressableLoadDelegate); + + buffer.Add(asyncInject); + } + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AddressableProviderSimple.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AddressableProviderSimple.cs.meta new file mode 100644 index 000000000..e9efac348 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AddressableProviderSimple.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a46dbe0d4a1445c3bb8c5e58739a07a3 +timeCreated: 1595192404 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AsyncMethodProviderSimple.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AsyncMethodProviderSimple.cs new file mode 100644 index 000000000..0274d0eca --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AsyncMethodProviderSimple.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Threading; +using System.Threading.Tasks; +using ModestTree; + +namespace Zenject +{ + [NoReflectionBaking] + public class AsyncMethodProviderSimple : IProvider where TConcrete : TContract + { + readonly Func> _method; + readonly Func> _methodCancellable; + + public AsyncMethodProviderSimple(Func> method) + { + _method = method; + } + + public AsyncMethodProviderSimple(Func> method) + { + _methodCancellable = method; + } + + public bool TypeVariesBasedOnMemberType => false; + public bool IsCached => false; + public Type GetInstanceType(InjectContext context) => typeof(TConcrete); + + public void GetAllInstancesWithInjectSplit(InjectContext context, List args, out Action injectAction, List buffer) + { + Assert.IsEmpty(args); + Assert.IsNotNull(context); + + injectAction = null; + + Func> typeCastAsyncCall = null; + if (_methodCancellable != null) + { + typeCastAsyncCall = async ct => + { + var task = _methodCancellable(ct); + await task; + return (TContract) task.Result; + }; + }else if (_method != null) + { + typeCastAsyncCall = async _ => + { + var task = _method(); + await task; + return (TContract) task.Result; + }; + } + Assert.IsNotNull(typeCastAsyncCall); + + var asyncInject = new AsyncInject(context, typeCastAsyncCall); + + buffer.Add(asyncInject); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AsyncMethodProviderSimple.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AsyncMethodProviderSimple.cs.meta new file mode 100644 index 000000000..6ac833e2c --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Runtime/Providers/AsyncMethodProviderSimple.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4d44f5c255f640aa8da6d4c74628f22f +timeCreated: 1593623062 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests.meta new file mode 100644 index 000000000..d18d50db8 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 97397223be0f3fb4b8288ac430e957ec +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable.meta new file mode 100644 index 000000000..47584d167 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e74ebac8913e9334586de36dff73257d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/Extenject-Addressable-Tests.asmdef b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/Extenject-Addressable-Tests.asmdef new file mode 100644 index 000000000..46474692d --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/Extenject-Addressable-Tests.asmdef @@ -0,0 +1,31 @@ +{ + "name": "Extenject-Addressable-Tests", + "references": [ + "GUID:0d8beb7f090555447a6cf5ce9e54dbb4", + "GUID:a2f2239355369ba4fb6909aeaa41def5", + "GUID:9bf848f31e601a249a6bebdc53287470", + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:84651a3751eca9349aac36a66bba901b", + "GUID:27619889b8ba8c24980f49ee34dbb44a" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "Zenject-usage.dll" + ], + "autoReferenced": true, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [ + { + "name": "com.unity.scriptablebuildpipeline", + "expression": "0.0.0", + "define": "EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/Extenject-Addressable-Tests.asmdef.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/Extenject-Addressable-Tests.asmdef.meta new file mode 100644 index 000000000..26aa90077 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/Extenject-Addressable-Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3c4567cf868de4741acbfbbf604db477 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/ReadMe.txt b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/ReadMe.txt new file mode 100644 index 000000000..b6b92a5be --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/ReadMe.txt @@ -0,0 +1,3 @@ +Create a GameObject prefab and add it to addressable with id "TestAddressablePrefab". This id is used in test. Unfortunately we cannot distribute this with package. + +Add Addressables and ResourceManager references to the test assembly definition. \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/ReadMe.txt.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/ReadMe.txt.meta new file mode 100644 index 000000000..564bd3b24 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/ReadMe.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d987748cb574f84419d80025765708da +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/TestAddressable.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/TestAddressable.cs new file mode 100644 index 000000000..d3a3901d6 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/TestAddressable.cs @@ -0,0 +1,176 @@ +#if EXTENJECT_INCLUDE_ADDRESSABLE_BINDINGS +using System; +using Zenject; +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceLocations; +using Assert = NUnit.Framework.Assert; + +public class TestAddressable : ZenjectIntegrationTestFixture +{ + private AssetReferenceT addressablePrefabReference; + + [TearDown] + public void Teardown() + { + addressablePrefabReference = null; + Resources.UnloadUnusedAssets(); + } + + [UnityTest] + public IEnumerator TestAddressableAsyncLoad() + { + yield return ValidateTestDependency(); + + PreInstall(); + AsyncOperationHandle handle = default; + Container.BindAsync().FromMethod(async () => + { + try + { + var locationsHandle = Addressables.LoadResourceLocationsAsync("TestAddressablePrefab"); + await locationsHandle.Task; + Assert.Greater(locationsHandle.Result.Count, 0, "Key required for test is not configured. Check Readme.txt in addressable test folder"); + + IResourceLocation location = locationsHandle.Result[0]; + handle = Addressables.LoadAsset(location); + await handle.Task; + return handle.Result; + } + catch (InvalidKeyException) + { + + } + return null; + }).AsCached(); + PostInstall(); + + yield return null; + + AsyncInject asycFoo = Container.Resolve>(); + + int frameCounter = 0; + while (!asycFoo.HasResult && !asycFoo.IsFaulted) + { + frameCounter++; + if (frameCounter > 10000) + { + Addressables.Release(handle); + Assert.Fail(); + } + yield return null; + } + + Addressables.Release(handle); + Assert.Pass(); + } + + [UnityTest] + public IEnumerator TestAssetReferenceTMethod() + { + yield return ValidateTestDependency(); + + PreInstall(); + + Container.BindAsync() + .FromAssetReferenceT(addressablePrefabReference) + .AsCached(); + PostInstall(); + + AddressableInject asyncPrefab = Container.Resolve>(); + + int frameCounter = 0; + while (!asyncPrefab.HasResult && !asyncPrefab.IsFaulted) + { + frameCounter++; + if (frameCounter > 10000) + { + Assert.Fail(); + } + yield return null; + } + + Addressables.Release(asyncPrefab.AssetReferenceHandle); + Assert.Pass(); + } + + [UnityTest] + [Timeout(10500)] + public IEnumerator TestFailedLoad() + { + PreInstall(); + + Container.BindAsync().FromMethod(async () => + { + FailedOperation failingOperation = new FailedOperation(); + var customHandle = Addressables.ResourceManager.StartOperation(failingOperation, default(AsyncOperationHandle)); + await customHandle.Task; + + if (customHandle.Status == AsyncOperationStatus.Failed) + { + throw new Exception("Async operation failed", customHandle.OperationException); + } + + return customHandle.Result; + }).AsCached(); + PostInstall(); + + yield return new WaitForEndOfFrame(); + + LogAssert.ignoreFailingMessages = true; + AsyncInject asyncGameObj = Container.Resolve>(); + LogAssert.ignoreFailingMessages = false; + + Assert.IsFalse(asyncGameObj.HasResult); + Assert.IsTrue(asyncGameObj.IsCompleted); + Assert.IsTrue(asyncGameObj.IsFaulted); + } + + private class FailedOperation : AsyncOperationBase + { + protected override void Execute() + { + Complete(null, false, "Intentionally failed message"); + } + } + + private IEnumerator ValidateTestDependency() + { + AsyncOperationHandle> locationsHandle = default(AsyncOperationHandle>); + try + { + locationsHandle = Addressables.LoadResourceLocationsAsync("TestAddressablePrefab"); + } + catch (Exception e) + { + Assert.Inconclusive("You need to set TestAddressablePrefab key to run this test"); + yield break; + } + + while (!locationsHandle.IsDone) + { + yield return null; + } + + var locations = locationsHandle.Result; + if (locations == null || locations.Count == 0) + { + Assert.Inconclusive("You need to set TestAddressablePrefab key to run this test"); + } + + var resourceLocation = locations[0]; + + if (resourceLocation.ResourceType != typeof(GameObject)) + { + Assert.Inconclusive("TestAddressablePrefab should be a GameObject"); + } + + addressablePrefabReference = new AssetReferenceT(resourceLocation.PrimaryKey); + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/TestAddressable.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/TestAddressable.cs.meta new file mode 100644 index 000000000..527ba0420 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Addressable/TestAddressable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0467565a36ec4624caee4cedb95898b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async.meta new file mode 100644 index 000000000..b92a35b41 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 008eb6ebb56c4b35ba2c89cd91b20186 +timeCreated: 1593627945 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async/TestAsync.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async/TestAsync.cs new file mode 100644 index 000000000..fc937c54e --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async/TestAsync.cs @@ -0,0 +1,180 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace Zenject.Tests.Bindings +{ + public class TestAsync : ZenjectIntegrationTestFixture + { + [UnityTest] + public IEnumerator TestSimpleMethod() + { + PreInstall(); + + Container.BindAsync().FromMethod(async () => + { + await Task.Delay(100); + return (IFoo) new Foo(); + }).AsCached(); + PostInstall(); + + var asycFoo = Container.Resolve>(); + + while (!asycFoo.HasResult) + { + yield return null; + } + + if (asycFoo.TryGetResult(out IFoo fooAfterLoad)) + { + Assert.NotNull(fooAfterLoad); + yield break; + } + Assert.Fail(); + } + + [UnityTest] + public IEnumerator TestUntypedInject() + { + PreInstall(); + + Container.BindAsync().FromMethod(async () => + { + await Task.Delay(100); + return (IFoo) new Foo(); + }).AsCached(); + PostInstall(); + + var asycFoo = Container.Resolve(); + yield return null; + + Assert.NotNull(asycFoo); + } + + + private IFoo awaitReturn; + [UnityTest] + [Timeout(300)] + public IEnumerator TestSimpleMethodAwaitable() + { + PreInstall(); + + Container.BindAsync().FromMethod(async () => + { + await Task.Delay(100); + return (IFoo) new Foo(); + }).AsCached(); + PostInstall(); + + awaitReturn = null; + var asycFoo = Container.Resolve>(); + + TestAwait(asycFoo); + + while (awaitReturn == null) + { + yield return null; + } + Assert.Pass(); + } + + [UnityTest] + [Timeout(10500)] + public IEnumerator TestPreloading() + { + PreInstall(); + for (int i = 0; i < 4; i++) + { + var index = i; + Container.BindAsync().FromMethod(async () => + { + var delayAmount = 100 * (4 - index); + await Task.Delay(delayAmount); + return (IFoo) new Foo(); + }).AsCached(); + } + + Container.BindInterfacesTo().AsCached(); + Container.Decorate() + .With(); + + PostInstall(); + + var testKernel = Container.Resolve() as PreloadAsyncKernel; + while (!testKernel.IsPreloadCompleted) + { + yield return null; + } + + foreach (var asycFooUntyped in testKernel.asyncInjects) + { + var asycFoo = asycFooUntyped as AsyncInject; + if (asycFoo.TryGetResult(out IFoo fooAfterLoad)) + { + Assert.NotNull(fooAfterLoad); + yield break; + } + } + } + + private async void TestAwait(AsyncInject asycFoo) + { + awaitReturn = await asycFoo; + } + + public interface IFoo + { + + } + + public class Foo : IFoo + { + + } + + public class PreloadAsyncKernel: BaseMonoKernelDecorator + { + [Inject] + public List asyncInjects; + + public bool IsPreloadCompleted { get; private set; } + + public async override void Initialize() + { + foreach (IAsyncInject inject in asyncInjects) + { + if (!inject.IsCompleted) + { + await Task.Delay(1); + } + } + + IsPreloadCompleted = true; + DecoratedMonoKernel.Initialize(); + } + + public override void Update() + { + if (!IsPreloadCompleted) + { + return; + } + DecoratedMonoKernel.Update(); + } + + public override void FixedUpdate() + { + if (!IsPreloadCompleted) + { + + } + DecoratedMonoKernel.FixedUpdate(); + } + } + } + +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async/TestAsync.cs.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async/TestAsync.cs.meta new file mode 100644 index 000000000..dddbcbfa9 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Async/TestAsync.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5f4f3bb942f34f42b8d797b8037ee7bd +timeCreated: 1593628006 \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Extenject-Async-Tests.asmdef b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Extenject-Async-Tests.asmdef new file mode 100644 index 000000000..aa53c0918 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Extenject-Async-Tests.asmdef @@ -0,0 +1,23 @@ +{ + "name": "Async.Tests", + "references": [ + "Zenject-TestFramework", + "Zenject", + "UnityEngine.TestRunner", + "Extenject-Async" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "Zenject-usage.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Extenject-Async-Tests.asmdef.meta b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Extenject-Async-Tests.asmdef.meta new file mode 100644 index 000000000..b6db8bb5f --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/Async/Tests/Extenject-Async-Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5939cea733ea13b4cbb397e50020fbc3 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: