using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using VContainer.Diagnostics; using VContainer.Internal; namespace VContainer.Unity { [DefaultExecutionOrder(-5000)] public partial class LifetimeScope : MonoBehaviour, IDisposable { public readonly struct ParentOverrideScope : IDisposable { public ParentOverrideScope(LifetimeScope nextParent) { lock (SyncRoot) { GlobalOverrideParents.Push(nextParent); } } public void Dispose() { lock (SyncRoot) { GlobalOverrideParents.Pop(); } } } public readonly struct ExtraInstallationScope : IDisposable { public ExtraInstallationScope(IInstaller installer) { lock (SyncRoot) { GlobalExtraInstallers.Push(installer); } } void IDisposable.Dispose() { lock (SyncRoot) { GlobalExtraInstallers.Pop(); } } } [SerializeField] public ParentReference parentReference; [SerializeField] public bool autoRun = true; [SerializeField] protected List autoInjectGameObjects; string scopeName; static readonly Stack GlobalOverrideParents = new Stack(); static readonly Stack GlobalExtraInstallers = new Stack(); static readonly object SyncRoot = new object(); public static LifetimeScope Create(IInstaller installer = null, string name = null) { var gameObject = new GameObject(name ?? "LifetimeScope"); gameObject.SetActive(false); var newScope = gameObject.AddComponent(); if (installer != null) { newScope.localExtraInstallers.Add(installer); } gameObject.SetActive(true); return newScope; } public static LifetimeScope Create(Action configuration, string name = null) => Create(new ActionInstaller(configuration), name); public static ParentOverrideScope EnqueueParent(LifetimeScope parent) => new ParentOverrideScope(parent); public static ExtraInstallationScope Enqueue(Action installing) => new ExtraInstallationScope(new ActionInstaller(installing)); public static ExtraInstallationScope Enqueue(IInstaller installer) => new ExtraInstallationScope(installer); [Obsolete("LifetimeScope.PushParent is obsolete. Use LifetimeScope.EnqueueParent instead.", false)] public static ParentOverrideScope PushParent(LifetimeScope parent) => new ParentOverrideScope(parent); [Obsolete("LifetimeScope.Push is obsolete. Use LifetimeScope.Enqueue instead.", false)] public static ExtraInstallationScope Push(Action installing) => Enqueue(installing); [Obsolete("LifetimeScope.Push is obsolete. Use LifetimeScope.Enqueue instead.", false)] public static ExtraInstallationScope Push(IInstaller installer) => Enqueue(installer); public static LifetimeScope Find(Scene scene) where T : LifetimeScope => Find(typeof(T), scene); public static LifetimeScope Find() where T : LifetimeScope => Find(typeof(T)); static LifetimeScope Find(Type type, Scene scene) { using (ListPool.Get(out var buffer)) { scene.GetRootGameObjects(buffer); foreach (var gameObject in buffer) { var found = gameObject.GetComponentInChildren(type) as LifetimeScope; if (found != null) return found; } } return null; } static LifetimeScope Find(Type type) { #if UNITY_2022_1_OR_NEWER return (LifetimeScope)FindAnyObjectByType(type); #else return (LifetimeScope)FindObjectOfType(type); #endif } public IObjectResolver Container { get; private set; } public LifetimeScope Parent { get; private set; } public bool IsRoot => VContainerSettings.Instance != null && VContainerSettings.Instance.IsRootLifetimeScopeInstance(this); readonly List localExtraInstallers = new List(); protected virtual void Awake() { if (VContainerSettings.DiagnosticsEnabled && string.IsNullOrEmpty(scopeName)) { scopeName = $"{name} ({gameObject.GetInstanceID()})"; } try { if (autoRun) { Build(); } } catch (VContainerParentTypeReferenceNotFound) when(!IsRoot) { if (WaitingList.Contains(this)) { throw; } EnqueueAwake(this); } } protected virtual void OnDestroy() { DisposeCore(); } protected virtual void Configure(IContainerBuilder builder) { } public void Dispose() { DisposeCore(); if (this != null) { Destroy(gameObject); } } public void DisposeCore() { Container?.Dispose(); Container = null; CancelAwake(this); if (VContainerSettings.DiagnosticsEnabled) { DiagnositcsContext.RemoveCollector(scopeName); } } public void Build() { if (Parent == null) Parent = GetRuntimeParent(); if (Parent != null) { if (VContainerSettings.Instance != null && Parent.IsRoot) { if (Parent.Container == null) Parent.Build(); } // ReSharper disable once PossibleNullReferenceException Parent.Container.CreateScope(builder => { builder.RegisterBuildCallback(SetContainer); builder.ApplicationOrigin = this; builder.Diagnostics = VContainerSettings.DiagnosticsEnabled ? DiagnositcsContext.GetCollector(scopeName) : null; InstallTo(builder); }); } else { var builder = new ContainerBuilder { ApplicationOrigin = this, Diagnostics = VContainerSettings.DiagnosticsEnabled ? DiagnositcsContext.GetCollector(scopeName) : null, }; builder.RegisterBuildCallback(SetContainer); InstallTo(builder); builder.Build(); } AwakeWaitingChildren(this); } void SetContainer(IObjectResolver container) { Container = container; AutoInjectAll(); } public TScope CreateChild(IInstaller installer = null, string childScopeName = null) where TScope : LifetimeScope { var childGameObject = new GameObject(childScopeName ?? "LifetimeScope (Child)"); childGameObject.SetActive(false); childGameObject.transform.SetParent(transform, false); var child = childGameObject.AddComponent(); if (installer != null) { child.localExtraInstallers.Add(installer); } child.parentReference.Object = this; childGameObject.SetActive(true); return child; } public LifetimeScope CreateChild(IInstaller installer = null, string childScopeName = null) => CreateChild(installer, childScopeName); public TScope CreateChild(Action installation, string childScopeName = null) where TScope : LifetimeScope => CreateChild(new ActionInstaller(installation), childScopeName); public LifetimeScope CreateChild(Action installation, string childScopeName = null) => CreateChild(new ActionInstaller(installation), childScopeName); public TScope CreateChildFromPrefab(TScope prefab, IInstaller installer = null) where TScope : LifetimeScope { var wasActive = prefab.gameObject.activeSelf; using (new ObjectResolverUnityExtensions.PrefabDirtyScope(prefab.gameObject)) { if (wasActive) { prefab.gameObject.SetActive(false); } var child = Instantiate(prefab, transform, false); if (installer != null) { child.localExtraInstallers.Add(installer); } child.parentReference.Object = this; if (wasActive) { prefab.gameObject.SetActive(true); child.gameObject.SetActive(true); } return child; } } public TScope CreateChildFromPrefab(TScope prefab, Action installation) where TScope : LifetimeScope => CreateChildFromPrefab(prefab, new ActionInstaller(installation)); void InstallTo(IContainerBuilder builder) { Configure(builder); foreach (var installer in localExtraInstallers) { installer.Install(builder); } localExtraInstallers.Clear(); lock (SyncRoot) { foreach (var installer in GlobalExtraInstallers) { installer.Install(builder); } } builder.RegisterInstance(this).AsSelf(); EntryPointsBuilder.EnsureDispatcherRegistered(builder); } protected virtual LifetimeScope FindParent() => null; LifetimeScope GetRuntimeParent() { if (IsRoot) return null; if (parentReference.Object != null) return parentReference.Object; // Find via implementation var implParent = FindParent(); if (implParent != null) { if (parentReference.Type != null && parentReference.Type != implParent.GetType()) { UnityEngine.Debug.LogWarning($"FindParent returned {implParent.GetType()} but parent reference type is {parentReference.Type}. This may be unintentional."); } return implParent; } // Find in scene via type if (parentReference.Type != null && parentReference.Type != GetType()) { var found = Find(parentReference.Type); if (found != null && found.Container != null) { return found; } throw new VContainerParentTypeReferenceNotFound( parentReference.Type, $"{name} could not found parent reference of type : {parentReference.Type}"); } lock (SyncRoot) { if (GlobalOverrideParents.Count > 0) { return GlobalOverrideParents.Peek(); } } // Find root from settings if (VContainerSettings.Instance != null) { return VContainerSettings.Instance.GetOrCreateRootLifetimeScopeInstance(); } return null; } void AutoInjectAll() { if (autoInjectGameObjects == null) return; foreach (var target in autoInjectGameObjects) { if (target != null) // Check missing reference { Container.InjectGameObject(target); } } } } }