Initial Commit

This commit is contained in:
2026-03-16 14:38:46 +02:00
commit b8f7327a21
2327 changed files with 253610 additions and 0 deletions
@@ -0,0 +1,334 @@
#if !NOT_UNITY3D
using System;
using System.Collections.Generic;
using System.Linq;
using ModestTree;
using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Zenject
{
public abstract class Context : MonoBehaviour
{
[SerializeField]
List<ScriptableObjectInstaller> _scriptableObjectInstallers = new List<ScriptableObjectInstaller>();
[FormerlySerializedAs("Installers")]
[FormerlySerializedAs("_installers")]
[SerializeField]
List<MonoInstaller> _monoInstallers = new List<MonoInstaller>();
[SerializeField]
List<MonoInstaller> _installerPrefabs = new List<MonoInstaller>();
List<InstallerBase> _normalInstallers = new List<InstallerBase>();
List<Type> _normalInstallerTypes = new List<Type>();
public IEnumerable<MonoInstaller> Installers
{
get { return _monoInstallers; }
set
{
_monoInstallers.Clear();
_monoInstallers.AddRange(value);
}
}
public IEnumerable<MonoInstaller> InstallerPrefabs
{
get { return _installerPrefabs; }
set
{
_installerPrefabs.Clear();
_installerPrefabs.AddRange(value);
}
}
public IEnumerable<ScriptableObjectInstaller> ScriptableObjectInstallers
{
get { return _scriptableObjectInstallers; }
set
{
_scriptableObjectInstallers.Clear();
_scriptableObjectInstallers.AddRange(value);
}
}
// Unlike other installer types this has to be set through code
public IEnumerable<Type> NormalInstallerTypes
{
get { return _normalInstallerTypes; }
set
{
Assert.That(value.All(x => x != null && x.DerivesFrom<InstallerBase>()));
_normalInstallerTypes.Clear();
_normalInstallerTypes.AddRange(value);
}
}
// Unlike other installer types this has to be set through code
public IEnumerable<InstallerBase> NormalInstallers
{
get { return _normalInstallers; }
set
{
_normalInstallers.Clear();
_normalInstallers.AddRange(value);
}
}
public abstract DiContainer Container
{
get;
}
public abstract IEnumerable<GameObject> GetRootGameObjects();
public void AddNormalInstallerType(Type installerType)
{
Assert.IsNotNull(installerType);
Assert.That(installerType.DerivesFrom<InstallerBase>());
_normalInstallerTypes.Add(installerType);
}
public void AddNormalInstaller(InstallerBase installer)
{
_normalInstallers.Add(installer);
}
void CheckInstallerPrefabTypes(List<MonoInstaller> installers, List<MonoInstaller> installerPrefabs)
{
foreach (var installer in installers)
{
Assert.IsNotNull(installer, "Found null installer in Context '{0}'", name);
#if UNITY_EDITOR
#if UNITY_2018_3_OR_NEWER
Assert.That(!PrefabUtility.IsPartOfPrefabAsset(installer.gameObject),
#else
Assert.That(PrefabUtility.GetPrefabType(installer.gameObject) != PrefabType.Prefab,
#endif
"Found prefab with name '{0}' in the Installer property of Context '{1}'. You should use the property 'InstallerPrefabs' for this instead.", installer.name, name);
#endif
}
foreach (var installerPrefab in installerPrefabs)
{
Assert.IsNotNull(installerPrefab, "Found null prefab in Context");
// We'd like to do this but this is actually a valid case sometimes
// (eg. loading an asset bundle with a scene containing a scene context when inside unity editor)
//#if UNITY_EDITOR
//Assert.That(PrefabUtility.GetPrefabType(installerPrefab.gameObject) == PrefabType.Prefab,
//"Found non-prefab with name '{0}' in the InstallerPrefabs property of Context '{1}'. You should use the property 'Installer' for this instead",
//installerPrefab.name, this.name);
//#endif
Assert.That(installerPrefab.GetComponent<MonoInstaller>() != null,
"Expected to find component with type 'MonoInstaller' on given installer prefab '{0}'", installerPrefab.name);
}
}
protected void InstallInstallers()
{
InstallInstallers(
_normalInstallers, _normalInstallerTypes, _scriptableObjectInstallers, _monoInstallers, _installerPrefabs);
}
protected void InstallInstallers(
List<InstallerBase> normalInstallers,
List<Type> normalInstallerTypes,
List<ScriptableObjectInstaller> scriptableObjectInstallers,
List<MonoInstaller> installers,
List<MonoInstaller> installerPrefabs)
{
CheckInstallerPrefabTypes(installers, installerPrefabs);
// Ideally we would just have one flat list of all the installers
// since that way the user has complete control over the order, but
// that's not possible since Unity does not allow serializing lists of interfaces
// (and it has to be an inteface since the scriptable object installers only share
// the interface)
//
// So the best we can do is have a hard-coded order in terms of the installer type
//
// The order is:
// - Normal installers given directly via code
// - ScriptableObject installers
// - MonoInstallers in the scene
// - Prefab Installers
//
// We put ScriptableObject installers before the MonoInstallers because
// ScriptableObjectInstallers are often used for settings (including settings
// that are injected into other installers like MonoInstallers)
var allInstallers = normalInstallers.Cast<IInstaller>()
.Concat(scriptableObjectInstallers.Cast<IInstaller>())
.Concat(installers.Cast<IInstaller>()).ToList();
foreach (var installerPrefab in installerPrefabs)
{
Assert.IsNotNull(installerPrefab, "Found null installer prefab in '{0}'", GetType());
GameObject installerGameObject;
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("GameObject.Instantiate"))
#endif
{
installerGameObject = GameObject.Instantiate(installerPrefab.gameObject);
}
installerGameObject.transform.SetParent(transform, false);
var installer = installerGameObject.GetComponent<MonoInstaller>();
Assert.IsNotNull(installer, "Could not find installer component on prefab '{0}'", installerPrefab.name);
allInstallers.Add(installer);
}
foreach (var installerType in normalInstallerTypes)
{
var installer = (InstallerBase)Container.Instantiate(installerType);
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("User Code"))
#endif
{
installer.InstallBindings();
}
}
foreach (var installer in allInstallers)
{
Assert.IsNotNull(installer,
"Found null installer in '{0}'", GetType());
Container.Inject(installer);
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("User Code"))
#endif
{
installer.InstallBindings();
}
}
}
protected void InstallSceneBindings(List<MonoBehaviour> injectableMonoBehaviours)
{
foreach (var binding in injectableMonoBehaviours.OfType<ZenjectBinding>())
{
if (binding == null)
{
continue;
}
if (binding.Context == null || (binding.UseSceneContext && this is SceneContext))
{
binding.Context = this;
}
}
// We'd prefer to use GameObject.FindObjectsOfType<ZenjectBinding>() here
// instead but that doesn't find inactive gameobjects
// TODO: Consider changing this
// Maybe ZenjectBinding could add itself to a registry class on Awake/OnEnable
// then we could avoid calling the slow Resources.FindObjectsOfTypeAll here
foreach (var binding in Resources.FindObjectsOfTypeAll<ZenjectBinding>())
{
if (binding == null)
{
continue;
}
// This is necessary for cases where the ZenjectBinding is inside a GameObjectContext
// since it won't be caught in the other loop above
if (this is SceneContext)
{
if (binding.Context == null && binding.UseSceneContext
&& binding.gameObject.scene == gameObject.scene)
{
binding.Context = this;
}
}
if (binding.Context == this)
{
InstallZenjectBinding(binding);
}
}
}
void InstallZenjectBinding(ZenjectBinding binding)
{
if (!binding.enabled)
{
return;
}
if (binding.Components == null || binding.Components.IsEmpty())
{
Log.Warn("Found empty list of components on ZenjectBinding on object '{0}'", binding.name);
return;
}
string identifier = null;
if (binding.Identifier.Trim().Length > 0)
{
identifier = binding.Identifier;
}
foreach (var component in binding.Components)
{
var bindType = binding.BindType;
if (component == null)
{
Log.Warn("Found null component in ZenjectBinding on object '{0}'", binding.name);
continue;
}
var componentType = component.GetType();
switch (bindType)
{
case ZenjectBinding.BindTypes.Self:
{
Container.Bind(componentType).WithId(identifier).FromInstance(component);
break;
}
case ZenjectBinding.BindTypes.BaseType:
{
Container.Bind(componentType.BaseType()).WithId(identifier).FromInstance(component);
break;
}
case ZenjectBinding.BindTypes.AllInterfaces:
{
Container.Bind(componentType.Interfaces()).WithId(identifier).FromInstance(component);
break;
}
case ZenjectBinding.BindTypes.AllInterfacesAndSelf:
{
Container.Bind(componentType.Interfaces().Concat(new[] { componentType }).ToArray()).WithId(identifier).FromInstance(component);
break;
}
default:
{
throw Assert.CreateException();
}
}
}
}
protected abstract void GetInjectableMonoBehaviours(List<MonoBehaviour> components);
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: be0cf56827265f44bbdeba09329d66ab
timeCreated: 1461708053
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,205 @@
#if !NOT_UNITY3D
using System;
using System.Collections.Generic;
using ModestTree;
using UnityEngine;
using UnityEngine.Serialization;
using Zenject.Internal;
#pragma warning disable 649
namespace Zenject
{
public class GameObjectContext : RunnableContext
{
public event Action PreInstall;
public event Action PostInstall;
public event Action PreResolve;
public event Action PostResolve;
[SerializeField]
[Tooltip("Note that this field is optional and can be ignored in most cases. This is really only needed if you want to control the 'Script Execution Order' of your subcontainer. In this case, define a new class that derives from MonoKernel, add it to this game object, then drag it into this field. Then you can set a value for 'Script Execution Order' for this new class and this will control when all ITickable/IInitializable classes bound within this subcontainer get called.")]
[FormerlySerializedAs("_facade")]
MonoKernel _kernel;
DiContainer _container;
// Need to cache this when auto run is false
DiContainer _parentContainer;
bool _hasInstalled;
public override DiContainer Container
{
get { return _container; }
}
public override IEnumerable<GameObject> GetRootGameObjects()
{
return new[] { gameObject };
}
[Inject]
public void Construct(
DiContainer parentContainer)
{
Assert.IsNull(_parentContainer);
_parentContainer = parentContainer;
Initialize();
}
protected override void RunInternal()
{
Install(_parentContainer);
ResolveAndStart();
}
public void Install(DiContainer parentContainer)
{
Assert.That(_parentContainer == null || _parentContainer == parentContainer);
// We allow calling this explicitly instead of relying on the [Inject] event above
// so that we can follow the two-pass construction-injection pattern in the providers
if (_hasInstalled)
{
return;
}
_hasInstalled = true;
Assert.IsNull(_container);
_container = parentContainer.CreateSubContainer();
// Do this after creating DiContainer in case it's needed by the pre install logic
if (PreInstall != null)
{
PreInstall();
}
var injectableMonoBehaviours = new List<MonoBehaviour>();
GetInjectableMonoBehaviours(injectableMonoBehaviours);
foreach (var instance in injectableMonoBehaviours)
{
if (instance is MonoKernel)
{
Assert.That(ReferenceEquals(instance, _kernel),
"Found MonoKernel derived class that is not hooked up to GameObjectContext. If you use MonoKernel, you must indicate this to GameObjectContext by dragging and dropping it to the Kernel field in the inspector");
}
_container.QueueForInject(instance);
}
_container.IsInstalling = true;
try
{
InstallBindings(injectableMonoBehaviours);
}
finally
{
_container.IsInstalling = false;
}
if (PostInstall != null)
{
PostInstall();
}
}
void ResolveAndStart()
{
if (PreResolve != null)
{
PreResolve();
}
_container.ResolveRoots();
if (PostResolve != null)
{
PostResolve();
}
// Normally, the IInitializable.Initialize method would be called during MonoKernel.Start
// However, this behaviour is undesirable for dynamically created objects, since Unity
// has the strange behaviour of waiting until the end of the frame to call Start() on
// dynamically created objects, which means that any GameObjectContext that is created
// dynamically via a factory cannot be used immediately after calling Create(), since
// it will not have been initialized
// So we have chosen to diverge from Unity behaviour here and trigger IInitializable.Initialize
// immediately - but only when the GameObjectContext is created dynamically. For any
// GameObjectContext's that are placed in the scene, we still want to execute
// IInitializable.Initialize during Start()
if (gameObject.scene.isLoaded && !_container.IsValidating)
{
_kernel = _container.Resolve<MonoKernel>();
_kernel.Initialize();
}
}
protected override void GetInjectableMonoBehaviours(List<MonoBehaviour> monoBehaviours)
{
ZenUtilInternal.AddStateMachineBehaviourAutoInjectersUnderGameObject(gameObject);
// We inject on all components on the root except ourself
foreach (var monoBehaviour in GetComponents<MonoBehaviour>())
{
if (monoBehaviour == null)
{
// Missing script
continue;
}
if (!ZenUtilInternal.IsInjectableMonoBehaviourType(monoBehaviour.GetType()))
{
continue;
}
if (monoBehaviour == this)
{
continue;
}
monoBehaviours.Add(monoBehaviour);
}
for (int i = 0; i < transform.childCount; i++)
{
var child = transform.GetChild(i);
if (child != null)
{
ZenUtilInternal.GetInjectableMonoBehavioursUnderGameObject(
child.gameObject, monoBehaviours);
}
}
}
void InstallBindings(List<MonoBehaviour> injectableMonoBehaviours)
{
_container.DefaultParent = transform;
_container.Bind<Context>().FromInstance(this);
_container.Bind<GameObjectContext>().FromInstance(this);
if (_kernel == null)
{
_container.Bind<MonoKernel>()
.To<DefaultGameObjectKernel>().FromNewComponentOn(gameObject).AsSingle().NonLazy();
}
else
{
_container.Bind<MonoKernel>().FromInstance(_kernel).AsSingle().NonLazy();
}
InstallSceneBindings(injectableMonoBehaviours);
InstallInstallers();
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 08eca9f7688a0a24685b89133b020c8e
timeCreated: 1456086415
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,298 @@
#if !NOT_UNITY3D
using System;
using System.Collections.Generic;
using System.Threading;
using ModestTree;
using UnityEngine;
using Zenject.Internal;
namespace Zenject
{
public class ProjectContext : Context
{
public static event Action PreInstall;
public static event Action PostInstall;
public static event Action PreResolve;
public static event Action PostResolve;
public const string ProjectContextResourcePath = "ProjectContext";
public const string ProjectContextResourcePathOld = "ProjectCompositionRoot";
static ProjectContext _instance;
// TODO: Set this to false the next time major version is incremented
[Tooltip("When true, objects that are created at runtime will be parented to the ProjectContext")]
[SerializeField]
bool _parentNewObjectsUnderContext = true;
[SerializeField]
ReflectionBakingCoverageModes _editorReflectionBakingCoverageMode = ReflectionBakingCoverageModes.FallbackToDirectReflection;
[SerializeField]
ReflectionBakingCoverageModes _buildsReflectionBakingCoverageMode = ReflectionBakingCoverageModes.FallbackToDirectReflection;
[SerializeField]
ZenjectSettings _settings = null;
DiContainer _container;
public override DiContainer Container
{
get { return _container; }
}
public static bool HasInstance
{
get { return _instance != null; }
}
public static ProjectContext Instance
{
get
{
if (_instance == null)
{
InstantiateAndInitialize();
Assert.IsNotNull(_instance);
}
return _instance;
}
}
public static bool ValidateOnNextRun
{
get;
set;
}
public override IEnumerable<GameObject> GetRootGameObjects()
{
return new[] { gameObject };
}
public static GameObject TryGetPrefab()
{
var prefabs = Resources.LoadAll(ProjectContextResourcePath, typeof(GameObject));
if (prefabs.Length > 0)
{
Assert.That(prefabs.Length == 1,
"Found multiple project context prefabs at resource path '{0}'", ProjectContextResourcePath);
return (GameObject)prefabs[0];
}
prefabs = Resources.LoadAll(ProjectContextResourcePathOld, typeof(GameObject));
if (prefabs.Length > 0)
{
Assert.That(prefabs.Length == 1,
"Found multiple project context prefabs at resource path '{0}'", ProjectContextResourcePathOld);
return (GameObject)prefabs[0];
}
return null;
}
static void InstantiateAndInitialize()
{
#if UNITY_EDITOR
ProfileBlock.UnityMainThread = Thread.CurrentThread;
#endif
Assert.That(FindObjectsOfType<ProjectContext>().IsEmpty(),
"Tried to create multiple instances of ProjectContext!");
var prefab = TryGetPrefab();
var prefabWasActive = false;
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("GameObject.Instantiate"))
#endif
{
if (prefab == null)
{
_instance = new GameObject("ProjectContext")
.AddComponent<ProjectContext>();
}
else
{
prefabWasActive = prefab.activeSelf;
GameObject gameObjectInstance;
#if UNITY_EDITOR
if(prefabWasActive)
{
// This ensures the prefab's Awake() methods don't fire (and, if in the editor, that the prefab file doesn't get modified)
gameObjectInstance = GameObject.Instantiate(prefab, ZenUtilInternal.GetOrCreateInactivePrefabParent());
gameObjectInstance.SetActive(false);
gameObjectInstance.transform.SetParent(null, false);
}
else
{
gameObjectInstance = GameObject.Instantiate(prefab);
}
#else
if(prefabWasActive)
{
prefab.SetActive(false);
gameObjectInstance = GameObject.Instantiate(prefab);
prefab.SetActive(true);
}
else
{
gameObjectInstance = GameObject.Instantiate(prefab);
}
#endif
_instance = gameObjectInstance.GetComponent<ProjectContext>();
Assert.IsNotNull(_instance,
"Could not find ProjectContext component on prefab 'Resources/{0}.prefab'", ProjectContextResourcePath);
}
}
// Note: We use Initialize instead of awake here in case someone calls
// ProjectContext.Instance while ProjectContext is initializing
_instance.Initialize();
if (prefabWasActive)
{
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("User Code"))
#endif
{
// We always instantiate it as disabled so that Awake and Start events are triggered after inject
_instance.gameObject.SetActive(true);
}
}
}
public bool ParentNewObjectsUnderContext
{
get { return _parentNewObjectsUnderContext; }
set { _parentNewObjectsUnderContext = value; }
}
public void EnsureIsInitialized()
{
// Do nothing - Initialize occurs in Instance property
}
public void Awake()
{
if (Application.isPlaying)
// DontDestroyOnLoad can only be called when in play mode and otherwise produces errors
// ProjectContext is created during design time (in an empty scene) when running validation
// and also when running unit tests
// In these cases we don't need DontDestroyOnLoad so just skip it
{
DontDestroyOnLoad(gameObject);
}
}
void Initialize()
{
Assert.IsNull(_container);
if (Application.isEditor)
{
TypeAnalyzer.ReflectionBakingCoverageMode = _editorReflectionBakingCoverageMode;
}
else
{
TypeAnalyzer.ReflectionBakingCoverageMode = _buildsReflectionBakingCoverageMode;
}
var isValidating = ValidateOnNextRun;
// Reset immediately to ensure it doesn't get used in another run
ValidateOnNextRun = false;
_container = new DiContainer(
new[] { StaticContext.Container }, isValidating);
// Do this after creating DiContainer in case it's needed by the pre install logic
if (PreInstall != null)
{
PreInstall();
}
var injectableMonoBehaviours = new List<MonoBehaviour>();
GetInjectableMonoBehaviours(injectableMonoBehaviours);
foreach (var instance in injectableMonoBehaviours)
{
_container.QueueForInject(instance);
}
_container.IsInstalling = true;
try
{
InstallBindings(injectableMonoBehaviours);
}
finally
{
_container.IsInstalling = false;
}
if (PostInstall != null)
{
PostInstall();
}
if (PreResolve != null)
{
PreResolve();
}
_container.ResolveRoots();
if (PostResolve != null)
{
PostResolve();
}
}
protected override void GetInjectableMonoBehaviours(List<MonoBehaviour> monoBehaviours)
{
ZenUtilInternal.AddStateMachineBehaviourAutoInjectersUnderGameObject(gameObject);
ZenUtilInternal.GetInjectableMonoBehavioursUnderGameObject(gameObject, monoBehaviours);
}
void InstallBindings(List<MonoBehaviour> injectableMonoBehaviours)
{
if (_parentNewObjectsUnderContext)
{
_container.DefaultParent = transform;
}
else
{
_container.DefaultParent = null;
}
_container.Settings = _settings ?? ZenjectSettings.Default;
_container.Bind<ZenjectSceneLoader>().AsSingle();
ZenjectManagersInstaller.Install(_container);
_container.Bind<Context>().FromInstance(this);
_container.Bind(typeof(ProjectKernel), typeof(MonoKernel))
.To<ProjectKernel>().FromNewComponentOn(gameObject).AsSingle().NonLazy();
_container.Bind<SceneContextRegistry>().AsSingle();
InstallSceneBindings(injectableMonoBehaviours);
InstallInstallers();
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a4e6589720da476459dc6dd71624b071
timeCreated: 1487808999
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,54 @@
using ModestTree;
using UnityEngine;
#if !NOT_UNITY3D
namespace Zenject
{
public abstract class RunnableContext : Context
{
[Tooltip("When false, wait until run method is explicitly called. Otherwise run on initialize")]
[SerializeField]
bool _autoRun = true;
static bool _staticAutoRun = true;
public bool Initialized { get; private set; }
protected void Initialize()
{
if (_staticAutoRun && _autoRun)
{
Run();
}
else
{
// True should always be default
_staticAutoRun = true;
}
}
public void Run()
{
Assert.That(!Initialized,
"The context already has been initialized!");
RunInternal();
Initialized = true;
}
protected abstract void RunInternal();
public static T CreateComponent<T>(GameObject gameObject) where T : RunnableContext
{
_staticAutoRun = false;
var result = gameObject.AddComponent<T>();
Assert.That(_staticAutoRun); // Should be reset
return result;
}
}
}
#endif
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 13e9b26d23f6422cb282cc27631fc9e2
timeCreated: 1494725784
@@ -0,0 +1,381 @@
#if !NOT_UNITY3D
using System;
using System.Collections.Generic;
using System.Linq;
using ModestTree;
using ModestTree.Util;
using UnityEngine;
using UnityEngine.Serialization;
using Zenject.Internal;
using UnityEngine.Events;
namespace Zenject
{
public class SceneContext : RunnableContext
{
public event Action PreInstall;
public event Action PostInstall;
public event Action PreResolve;
public event Action PostResolve;
public UnityEvent OnPreInstall;
public UnityEvent OnPostInstall;
public UnityEvent OnPreResolve;
public UnityEvent OnPostResolve;
public static Action<DiContainer> ExtraBindingsInstallMethod;
public static Action<DiContainer> ExtraBindingsLateInstallMethod;
public static IEnumerable<DiContainer> ParentContainers;
[FormerlySerializedAs("ParentNewObjectsUnderRoot")]
[FormerlySerializedAs("_parentNewObjectsUnderRoot")]
[Tooltip("When true, objects that are created at runtime will be parented to the SceneContext")]
[SerializeField]
bool _parentNewObjectsUnderSceneContext;
[Tooltip("Optional contract names for this SceneContext, allowing contexts in subsequently loaded scenes to depend on it and be parented to it, and also for previously loaded decorators to be included")]
[SerializeField]
List<string> _contractNames = new List<string>();
[Tooltip("Optional contract names of SceneContexts in previously loaded scenes that this context depends on and to which it should be parented")]
[SerializeField]
List<string> _parentContractNames = new List<string>();
DiContainer _container;
readonly List<SceneDecoratorContext> _decoratorContexts = new List<SceneDecoratorContext>();
bool _hasInstalled;
bool _hasResolved;
public override DiContainer Container
{
get { return _container; }
}
public bool HasResolved
{
get { return _hasResolved; }
}
public bool HasInstalled
{
get { return _hasInstalled; }
}
public bool IsValidating
{
get
{
return ProjectContext.Instance.Container.IsValidating;
}
}
public IEnumerable<string> ContractNames
{
get { return _contractNames; }
set
{
_contractNames.Clear();
_contractNames.AddRange(value);
}
}
public IEnumerable<string> ParentContractNames
{
get
{
var result = new List<string>();
result.AddRange(_parentContractNames);
return result;
}
set
{
_parentContractNames = value.ToList();
}
}
public bool ParentNewObjectsUnderSceneContext
{
get { return _parentNewObjectsUnderSceneContext; }
set { _parentNewObjectsUnderSceneContext = value; }
}
public void Awake()
{
#if ZEN_INTERNAL_PROFILING
ProfileTimers.ResetAll();
using (ProfileTimers.CreateTimedBlock("Other"))
#endif
{
Initialize();
}
}
public void Validate()
{
Assert.That(IsValidating);
Install();
Resolve();
}
protected override void RunInternal()
{
// We always want to initialize ProjectContext as early as possible
ProjectContext.Instance.EnsureIsInitialized();
#if UNITY_EDITOR
using (ProfileBlock.Start("Zenject.SceneContext.Install"))
#endif
{
Install();
}
#if UNITY_EDITOR
using (ProfileBlock.Start("Zenject.SceneContext.Resolve"))
#endif
{
Resolve();
}
}
public override IEnumerable<GameObject> GetRootGameObjects()
{
return ZenUtilInternal.GetRootGameObjects(gameObject.scene);
}
IEnumerable<DiContainer> GetParentContainers()
{
var parentContractNames = ParentContractNames;
if (parentContractNames.IsEmpty())
{
if (ParentContainers != null)
{
var tempParentContainer = ParentContainers;
// Always reset after using it - it is only used to pass the reference
// between scenes via ZenjectSceneLoader
ParentContainers = null;
return tempParentContainer;
}
return new[] { ProjectContext.Instance.Container };
}
Assert.IsNull(ParentContainers,
"Scene cannot have both a parent scene context name set and also an explicit parent container given");
var parentContainers = UnityUtil.AllLoadedScenes
.Except(gameObject.scene)
.SelectMany(scene => scene.GetRootGameObjects())
.SelectMany(root => root.GetComponentsInChildren<SceneContext>())
.Where(sceneContext => sceneContext.ContractNames.Where(x => parentContractNames.Contains(x)).Any())
.Select(x => x.Container)
.ToList();
if (!parentContainers.Any())
{
throw Assert.CreateException(
"SceneContext on object {0} of scene {1} requires at least one of contracts '{2}', but none of the loaded SceneContexts implements that contract.",
gameObject.name,
gameObject.scene.name,
parentContractNames.Join(", "));
}
return parentContainers;
}
List<SceneDecoratorContext> LookupDecoratorContexts()
{
if (_contractNames.IsEmpty())
{
return new List<SceneDecoratorContext>();
}
return UnityUtil.AllLoadedScenes
.Except(gameObject.scene)
.SelectMany(scene => scene.GetRootGameObjects())
.SelectMany(root => root.GetComponentsInChildren<SceneDecoratorContext>())
.Where(decoratorContext => _contractNames.Contains(decoratorContext.DecoratedContractName))
.ToList();
}
public void Install()
{
Assert.That(!_hasInstalled);
_hasInstalled = true;
Assert.IsNull(_container);
var parents = GetParentContainers();
Assert.That(!parents.IsEmpty());
Assert.That(parents.All(x => x.IsValidating == parents.First().IsValidating));
_container = new DiContainer(parents, parents.First().IsValidating);
// Do this after creating DiContainer in case it's needed by the pre install logic
if (PreInstall != null)
{
PreInstall();
}
if (OnPreInstall != null)
{
OnPreInstall.Invoke();
}
Assert.That(_decoratorContexts.IsEmpty());
_decoratorContexts.AddRange(LookupDecoratorContexts());
if (_parentNewObjectsUnderSceneContext)
{
_container.DefaultParent = transform;
}
else
{
_container.DefaultParent = null;
}
// Record all the injectable components in the scene BEFORE installing the installers
// This is nice for cases where the user calls InstantiatePrefab<>, etc. in their installer
// so that it doesn't inject on the game object twice
// InitialComponentsInjecter will also guarantee that any component that is injected into
// another component has itself been injected
var injectableMonoBehaviours = new List<MonoBehaviour>();
GetInjectableMonoBehaviours(injectableMonoBehaviours);
foreach (var instance in injectableMonoBehaviours)
{
_container.QueueForInject(instance);
}
foreach (var decoratorContext in _decoratorContexts)
{
decoratorContext.Initialize(_container);
}
_container.IsInstalling = true;
try
{
InstallBindings(injectableMonoBehaviours);
}
finally
{
_container.IsInstalling = false;
}
if (PostInstall != null)
{
PostInstall();
}
if (OnPostInstall != null)
{
OnPostInstall.Invoke();
}
}
public void Resolve()
{
if (PreResolve != null)
{
PreResolve();
}
if (OnPreResolve != null)
{
OnPreResolve.Invoke();
}
Assert.That(_hasInstalled);
Assert.That(!_hasResolved);
_hasResolved = true;
_container.ResolveRoots();
if (PostResolve != null)
{
PostResolve();
}
if (OnPostResolve != null)
{
OnPostResolve.Invoke();
}
}
void InstallBindings(List<MonoBehaviour> injectableMonoBehaviours)
{
_container.Bind(typeof(Context), typeof(SceneContext)).To<SceneContext>().FromInstance(this);
_container.BindInterfacesTo<SceneContextRegistryAdderAndRemover>().AsSingle();
// Add to registry first and remove from registry last
_container.BindExecutionOrder<SceneContextRegistryAdderAndRemover>(-1);
foreach (var decoratorContext in _decoratorContexts)
{
decoratorContext.InstallDecoratorSceneBindings();
}
InstallSceneBindings(injectableMonoBehaviours);
_container.Bind(typeof(SceneKernel), typeof(MonoKernel))
.To<SceneKernel>().FromNewComponentOn(gameObject).AsSingle().NonLazy();
_container.Bind<ZenjectSceneLoader>().AsSingle();
if (ExtraBindingsInstallMethod != null)
{
ExtraBindingsInstallMethod(_container);
// Reset extra bindings for next time we change scenes
ExtraBindingsInstallMethod = null;
}
// Always install the installers last so they can be injected with
// everything above
foreach (var decoratorContext in _decoratorContexts)
{
decoratorContext.InstallDecoratorInstallers();
}
InstallInstallers();
foreach (var decoratorContext in _decoratorContexts)
{
decoratorContext.InstallLateDecoratorInstallers();
}
if (ExtraBindingsLateInstallMethod != null)
{
ExtraBindingsLateInstallMethod(_container);
// Reset extra bindings for next time we change scenes
ExtraBindingsLateInstallMethod = null;
}
}
protected override void GetInjectableMonoBehaviours(List<MonoBehaviour> monoBehaviours)
{
var scene = gameObject.scene;
ZenUtilInternal.AddStateMachineBehaviourAutoInjectersInScene(scene);
ZenUtilInternal.GetInjectableMonoBehavioursInScene(scene, monoBehaviours);
}
// These methods can be used for cases where you need to create the SceneContext entirely in code
// Note that if you use these methods that you have to call Run() yourself
// This is useful because it allows you to create a SceneContext and configure it how you want
// and add what installers you want before kicking off the Install/Resolve
public static SceneContext Create()
{
return CreateComponent<SceneContext>(
new GameObject("SceneContext"));
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 89715ad69b973a14899afa2c6730b30b
timeCreated: 1435941958
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: -9999
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,122 @@
#if !NOT_UNITY3D
using System;
using System.Collections.Generic;
using ModestTree;
using UnityEngine;
using UnityEngine.Serialization;
using Zenject.Internal;
namespace Zenject
{
public class SceneDecoratorContext : Context
{
[SerializeField]
List<MonoInstaller> _lateInstallers = new List<MonoInstaller>();
[SerializeField]
List<MonoInstaller> _lateInstallerPrefabs = new List<MonoInstaller>();
[SerializeField]
List<ScriptableObjectInstaller> _lateScriptableObjectInstallers = new List<ScriptableObjectInstaller>();
public IEnumerable<MonoInstaller> LateInstallers
{
get { return _lateInstallers; }
set
{
_lateInstallers.Clear();
_lateInstallers.AddRange(value);
}
}
public IEnumerable<MonoInstaller> LateInstallerPrefabs
{
get { return _lateInstallerPrefabs; }
set
{
_lateInstallerPrefabs.Clear();
_lateInstallerPrefabs.AddRange(value);
}
}
public IEnumerable<ScriptableObjectInstaller> LateScriptableObjectInstallers
{
get { return _lateScriptableObjectInstallers; }
set
{
_lateScriptableObjectInstallers.Clear();
_lateScriptableObjectInstallers.AddRange(value);
}
}
[FormerlySerializedAs("SceneName")]
[SerializeField]
string _decoratedContractName = null;
DiContainer _container;
readonly List<MonoBehaviour> _injectableMonoBehaviours = new List<MonoBehaviour>();
public string DecoratedContractName
{
get { return _decoratedContractName; }
}
public override DiContainer Container
{
get
{
Assert.IsNotNull(_container);
return _container;
}
}
public override IEnumerable<GameObject> GetRootGameObjects()
{
// This method should never be called because SceneDecoratorContext's are not bound
// to the container
throw Assert.CreateException();
}
public void Initialize(DiContainer container)
{
Assert.IsNull(_container);
Assert.That(_injectableMonoBehaviours.IsEmpty());
_container = container;
GetInjectableMonoBehaviours(_injectableMonoBehaviours);
foreach (var instance in _injectableMonoBehaviours)
{
container.QueueForInject(instance);
}
}
public void InstallDecoratorSceneBindings()
{
_container.Bind<SceneDecoratorContext>().FromInstance(this);
InstallSceneBindings(_injectableMonoBehaviours);
}
public void InstallDecoratorInstallers()
{
InstallInstallers();
}
protected override void GetInjectableMonoBehaviours(List<MonoBehaviour> monoBehaviours)
{
var scene = gameObject.scene;
ZenUtilInternal.AddStateMachineBehaviourAutoInjectersInScene(scene);
ZenUtilInternal.GetInjectableMonoBehavioursInScene(scene, monoBehaviours);
}
public void InstallLateDecoratorInstallers()
{
InstallInstallers(new List<InstallerBase>(), new List<Type>(), _lateScriptableObjectInstallers, _lateInstallers, _lateInstallerPrefabs);
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ed7323e8fdd8c26438c6485f2060dad0
timeCreated: 1487808999
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
#if !NOT_UNITY3D
namespace Zenject
{
// For some platforms, it's desirable to be able to add dependencies to Zenject before
// Unity even starts up (eg. WSA as described here https://github.com/svermeulen/Zenject/issues/118)
// In those cases you can call StaticContext.Container.BindX to add dependencies
// Anything you add there will then be injected everywhere, since all other contexts
// should be children of StaticContext
public static class StaticContext
{
static DiContainer _container;
// Useful sometimes to call from play mode tests
public static void Clear()
{
_container = null;
}
public static bool HasContainer
{
get { return _container != null; }
}
public static DiContainer Container
{
get
{
if (_container == null)
{
_container = new DiContainer();
}
return _container;
}
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 668a9feb769954340b35901a0c829397
timeCreated: 1462834162
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: