diff --git a/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs new file mode 100644 index 00000000..c2569a44 --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace VContainer.Unity +{ + public sealed class VContainerParentTypeReferenceNotFound : Exception + { + public readonly Type ParentType; + + public VContainerParentTypeReferenceNotFound(Type parentType, string message) + : base(message) + { + ParentType = parentType; + } + } + + + public partial class LifetimeScope + { + static readonly List<(LifetimeScope, VContainerParentTypeReferenceNotFound)> WaitingList = + new List<(LifetimeScope, VContainerParentTypeReferenceNotFound)>(); + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void SubscribeSceneEvents() + { + SceneManager.sceneLoaded -= ReleaseWaitingList; + SceneManager.sceneLoaded += ReleaseWaitingList; + } + + static void WaitForAwake(LifetimeScope lifetimeScope, VContainerParentTypeReferenceNotFound ex) + { + WaitingList.Add((lifetimeScope, ex)); + } + + static void CancelWaiting(LifetimeScope lifetimeScope) + { + for (var i = WaitingList.Count - 1; i >= 0; i--) + { + if (WaitingList[i].Item1 == lifetimeScope) + { + WaitingList.RemoveAt(i); + } + } + } + + static void ReleaseWaitingList(Scene scene, LoadSceneMode mode) + { + for (var i = WaitingList.Count - 1; i >= 0; i--) + { + var (waiting, ex) = WaitingList[i]; + if (waiting.gameObject.scene == scene) + { + WaitingList.RemoveAt(i); + waiting.Awake(); // Re-throw if parent not found. + } + } + } + + static void ReleaseWaitingListFrom(LifetimeScope awakedParent) + { + if (WaitingList.Count < 0) return; + + var type = awakedParent.GetType(); + + for (var i = WaitingList.Count - 1; i >= 0; i--) + { + var (waiting, ex) = WaitingList[i]; + if (ex.ParentType == type) + { + waiting.parentReference.Object = awakedParent; + WaitingList.RemoveAt(i); + waiting.Awake(); + } + } + } + } +} \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta new file mode 100644 index 00000000..ae741b5a --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.AwakeScheduler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69462a1b64a3d4de2bcbd8707dd57ca1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.Dispatcher.cs b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.Dispatcher.cs new file mode 100644 index 00000000..66a18f7d --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.Dispatcher.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; + +namespace VContainer.Unity +{ + public partial class LifetimeScope + { + void DispatchEntryPoints() + { + PlayerLoopHelper.Initialize(); + + EntryPointExceptionHandler exceptionHandler = null; + try + { + exceptionHandler = Container.Resolve(); + } + catch (VContainerException ex) when (ex.InvalidType == typeof(EntryPointExceptionHandler)) + { + } + + var initializables = Container.Resolve>(); + if (initializables.Count > 0) + { + var loopItem = new InitializationLoopItem(initializables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Initialization, loopItem); + } + + var postInitializables = Container.Resolve>(); + if (postInitializables.Count > 0) + { + var loopItem = new PostInitializationLoopItem(postInitializables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostInitialization, loopItem); + } + + var startables = Container.Resolve>(); + if (startables.Count > 0) + { + var loopItem = new StartableLoopItem(startables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Startup, loopItem); + } + + var postStartables = Container.Resolve>(); + if (postStartables.Count > 0) + { + var loopItem = new PostStartableLoopItem(postStartables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostStartup, loopItem); + } + + var fixedTickables = Container.Resolve>(); + if (fixedTickables.Count > 0) + { + var loopItem = new FixedTickableLoopItem(fixedTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.FixedUpdate, loopItem); + } + + var postFixedTickables = Container.Resolve>(); + if (postFixedTickables.Count > 0) + { + var loopItem = new PostFixedTickableLoopItem(postFixedTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostFixedUpdate, loopItem); + } + + var tickables = Container.Resolve>(); + if (tickables.Count > 0) + { + var loopItem = new TickableLoopItem(tickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Update, loopItem); + } + + var postTickables = Container.Resolve>(); + if (postTickables.Count > 0) + { + var loopItem = new PostTickableLoopItem(postTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostUpdate, loopItem); + } + + var lateTickables = Container.Resolve>(); + if (lateTickables.Count > 0) + { + var loopItem = new LateTickableLoopItem(lateTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.LateUpdate, loopItem); + } + + var postLateTickables = Container.Resolve>(); + if (postLateTickables.Count > 0) + { + var loopItem = new PostLateTickableLoopItem(postLateTickables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostLateUpdate, loopItem); + } + +#if VCONTAINER_UNITASK_INTEGRATION + var asyncStartables = Container.Resolve>(); + if (asyncStartables.Count > 0) + { + var loopItem = new AsyncStartableLoopItem(asyncStartables, exceptionHandler); + disposable.Add(loopItem); + PlayerLoopHelper.Dispatch(PlayerLoopTiming.Startup, loopItem); + } +#endif + +#if VCONTAINER_ECS_INTEGRATION + Container.Resolve>(); + + var worldHelpers = Container.Resolve>(); + foreach (var x in worldHelpers) + { + x.SortSystems(); + } +#endif + } + + } +} diff --git a/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.Dispatcher.cs.meta b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.Dispatcher.cs.meta new file mode 100644 index 00000000..98a0266a --- /dev/null +++ b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.Dispatcher.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 63579785bdc34e98a580706ae28fd472 +timeCreated: 1612845023 \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.cs b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.cs index 55eb9e24..fd05dbd0 100644 --- a/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.cs +++ b/VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.cs @@ -10,7 +10,7 @@ namespace VContainer.Unity { - public class LifetimeScope : MonoBehaviour, IDisposable + public partial class LifetimeScope : MonoBehaviour, IDisposable { public readonly struct ParentOverrideScope : IDisposable { @@ -24,16 +24,6 @@ public class LifetimeScope : MonoBehaviour, IDisposable void IDisposable.Dispose() => RemoveExtra(); } - public sealed class ParentTypeNotFoundException : Exception - { - public readonly Type ParentType; - - public ParentTypeNotFoundException(Type parentType, string message) : base(message) - { - ParentType = parentType; - } - } - [SerializeField] public ParentReference parentReference; @@ -45,7 +35,6 @@ public ParentTypeNotFoundException(Type parentType, string message) : base(messa static LifetimeScope overrideParent; static ExtraInstaller extraInstaller; - static readonly List WaitingList = new List(); static readonly object SyncRoot = new object(); static LifetimeScope Create(IInstaller installer = null) @@ -85,13 +74,6 @@ public static ExtraInstallationScope Enqueue(IInstaller installer) public static LifetimeScope Find(Scene scene) where T : LifetimeScope => Find(typeof(T), scene); public static LifetimeScope Find() where T : LifetimeScope => Find(typeof(T)); - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] - static void SubscribeSceneEvents() - { - SceneManager.sceneLoaded -= ThrowIfParentReferenceNotFound; - SceneManager.sceneLoaded += ThrowIfParentReferenceNotFound; - } - static LifetimeScope Find(Type type, Scene scene) { var buffer = UnityEngineObjectListBuffer.Get(); @@ -136,25 +118,6 @@ static void RemoveExtra() lock (SyncRoot) extraInstaller = null; } - static void ThrowIfParentReferenceNotFound(Scene scene, LoadSceneMode mode) - { - foreach (var waiting in WaitingList) - { - if (waiting.parentTypeNotFoundException is ParentTypeNotFoundException ex && - waiting.gameObject.scene == scene) - { - waiting.parentTypeNotFoundException = null; - waiting.Parent = Find(ex.ParentType); - - if (waiting.Parent == null) - throw ex; - - if (waiting.autoRun) - waiting.Build(); - } - } - } - public IObjectResolver Container { get; private set; } public LifetimeScope Parent { get; private set; } public bool IsRoot { get; set; } @@ -162,8 +125,6 @@ static void ThrowIfParentReferenceNotFound(Scene scene, LoadSceneMode mode) readonly CompositeDisposable disposable = new CompositeDisposable(); readonly List extraInstallers = new List(); - ParentTypeNotFoundException parentTypeNotFoundException; - protected virtual void Awake() { try @@ -174,13 +135,12 @@ protected virtual void Awake() Build(); } } - catch (ParentTypeNotFoundException ex) + catch (VContainerParentTypeReferenceNotFound ex) { if (gameObject.scene.isLoaded) throw; - parentTypeNotFoundException = ex; - WaitingList.Add(this); + WaitForAwake(this, ex); } } @@ -218,21 +178,10 @@ public void Build() } extraInstallers.Clear(); - AutoInjectAll(); - ActivateEntryPoints(); - var type = GetType(); - for (var i = WaitingList.Count - 1; i >= 0; i--) - { - var waiting = WaitingList[i]; - if (waiting.parentTypeNotFoundException?.ParentType == type) - { - waiting.parentReference.Object = this; - waiting.parentTypeNotFoundException = null; - WaitingList.RemoveAt(i); - waiting.Awake(); - } - } + AutoInjectAll(); + DispatchEntryPoints(); + ReleaseWaitingListFrom(this); } public LifetimeScope CreateChild(IInstaller installer = null) @@ -314,7 +263,7 @@ LifetimeScope GetRuntimeParent() { return found; } - throw new ParentTypeNotFoundException( + throw new VContainerParentTypeReferenceNotFound( parentReference.Type, $"{name} could not found parent reference of type : {parentReference.Type}"); } @@ -338,8 +287,7 @@ void DisposeCore() disposable.Dispose(); Container?.Dispose(); Container = null; - parentTypeNotFoundException = null; - WaitingList.Remove(this); + CancelWaiting(this); } void AutoInjectAll() @@ -355,119 +303,5 @@ void AutoInjectAll() } } } - - void ActivateEntryPoints() - { - PlayerLoopHelper.Initialize(); - - EntryPointExceptionHandler exceptionHandler = null; - try - { - exceptionHandler = Container.Resolve(); - } - catch (VContainerException ex) when (ex.InvalidType == typeof(EntryPointExceptionHandler)) - { - } - - var initializables = Container.Resolve>(); - if (initializables.Count > 0) - { - var loopItem = new InitializationLoopItem(initializables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.Initialization, loopItem); - } - - var postInitializables = Container.Resolve>(); - if (postInitializables.Count > 0) - { - var loopItem = new PostInitializationLoopItem(postInitializables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostInitialization, loopItem); - } - - var startables = Container.Resolve>(); - if (startables.Count > 0) - { - var loopItem = new StartableLoopItem(startables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.Startup, loopItem); - } - - var postStartables = Container.Resolve>(); - if (postStartables.Count > 0) - { - var loopItem = new PostStartableLoopItem(postStartables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostStartup, loopItem); - } - - var fixedTickables = Container.Resolve>(); - if (fixedTickables.Count > 0) - { - var loopItem = new FixedTickableLoopItem(fixedTickables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.FixedUpdate, loopItem); - } - - var postFixedTickables = Container.Resolve>(); - if (postFixedTickables.Count > 0) - { - var loopItem = new PostFixedTickableLoopItem(postFixedTickables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostFixedUpdate, loopItem); - } - - var tickables = Container.Resolve>(); - if (tickables.Count > 0) - { - var loopItem = new TickableLoopItem(tickables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.Update, loopItem); - } - - var postTickables = Container.Resolve>(); - if (postTickables.Count > 0) - { - var loopItem = new PostTickableLoopItem(postTickables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostUpdate, loopItem); - } - - var lateTickables = Container.Resolve>(); - if (lateTickables.Count > 0) - { - var loopItem = new LateTickableLoopItem(lateTickables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.LateUpdate, loopItem); - } - - var postLateTickables = Container.Resolve>(); - if (postLateTickables.Count > 0) - { - var loopItem = new PostLateTickableLoopItem(postLateTickables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.PostLateUpdate, loopItem); - } - -#if VCONTAINER_UNITASK_INTEGRATION - var asyncStartables = Container.Resolve>(); - if (asyncStartables.Count > 0) - { - var loopItem = new AsyncStartableLoopItem(asyncStartables, exceptionHandler); - disposable.Add(loopItem); - PlayerLoopHelper.Dispatch(PlayerLoopTiming.Startup, loopItem); - } -#endif - -#if VCONTAINER_ECS_INTEGRATION - Container.Resolve>(); - - var worldHelpers = Container.Resolve>(); - foreach (var x in worldHelpers) - { - x.SortSystems(); - } -#endif - } } } diff --git a/VContainer/Assets/VContainer/Tests/Unity/Fixtures/SampleChildLifetimeScope.cs b/VContainer/Assets/VContainer/Tests/Unity/Fixtures/SampleChildLifetimeScope.cs index 609215ff..bd95f6f2 100644 --- a/VContainer/Assets/VContainer/Tests/Unity/Fixtures/SampleChildLifetimeScope.cs +++ b/VContainer/Assets/VContainer/Tests/Unity/Fixtures/SampleChildLifetimeScope.cs @@ -4,6 +4,5 @@ namespace VContainer.Tests.Unity { public class SampleChildLifetimeScope : LifetimeScope { - } } \ No newline at end of file diff --git a/VContainer/Assets/VContainer/Tests/Unity/LifetimeScopeTest.cs b/VContainer/Assets/VContainer/Tests/Unity/LifetimeScopeTest.cs index f73f0bdb..067136a7 100644 --- a/VContainer/Assets/VContainer/Tests/Unity/LifetimeScopeTest.cs +++ b/VContainer/Assets/VContainer/Tests/Unity/LifetimeScopeTest.cs @@ -126,7 +126,7 @@ public void ParentTypeReference() Assert.That(child.Parent, Is.EqualTo(parent)); } - // [UnityTest] + // [Test] // public void ParentTypeReferenceInvalid() // { // var prefab = Resources.Load("ParentChildRelationshipInvalid");