370 lines
12 KiB
C#
370 lines
12 KiB
C#
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<GameObject> autoInjectGameObjects;
|
|
|
|
string scopeName;
|
|
|
|
static readonly Stack<LifetimeScope> GlobalOverrideParents = new Stack<LifetimeScope>();
|
|
static readonly Stack<IInstaller> GlobalExtraInstallers = new Stack<IInstaller>();
|
|
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<LifetimeScope>();
|
|
if (installer != null)
|
|
{
|
|
newScope.localExtraInstallers.Add(installer);
|
|
}
|
|
gameObject.SetActive(true);
|
|
return newScope;
|
|
}
|
|
|
|
public static LifetimeScope Create(Action<IContainerBuilder> configuration, string name = null)
|
|
=> Create(new ActionInstaller(configuration), name);
|
|
|
|
public static ParentOverrideScope EnqueueParent(LifetimeScope parent)
|
|
=> new ParentOverrideScope(parent);
|
|
|
|
public static ExtraInstallationScope Enqueue(Action<IContainerBuilder> 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<IContainerBuilder> 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<T>(Scene scene) where T : LifetimeScope => Find(typeof(T), scene);
|
|
public static LifetimeScope Find<T>() where T : LifetimeScope => Find(typeof(T));
|
|
|
|
static LifetimeScope Find(Type type, Scene scene)
|
|
{
|
|
using (ListPool<GameObject>.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<IInstaller> localExtraInstallers = new List<IInstaller>();
|
|
|
|
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<TScope>(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<TScope>();
|
|
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<LifetimeScope>(installer, childScopeName);
|
|
|
|
public TScope CreateChild<TScope>(Action<IContainerBuilder> installation, string childScopeName = null)
|
|
where TScope : LifetimeScope
|
|
=> CreateChild<TScope>(new ActionInstaller(installation), childScopeName);
|
|
|
|
public LifetimeScope CreateChild(Action<IContainerBuilder> installation, string childScopeName = null)
|
|
=> CreateChild<LifetimeScope>(new ActionInstaller(installation), childScopeName);
|
|
|
|
public TScope CreateChildFromPrefab<TScope>(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>(TScope prefab, Action<IContainerBuilder> 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<LifetimeScope>(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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|