Files
dungeons/Assets/Plugins/Zenject/Source/Util/ZenUtilInternal.cs

273 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using ModestTree;
using ModestTree.Util;
#if !NOT_UNITY3D
using UnityEngine.SceneManagement;
using UnityEngine;
#endif
namespace Zenject.Internal
{
public static class ZenUtilInternal
{
#if UNITY_EDITOR
static GameObject _disabledIndestructibleGameObject;
#endif
// Due to the way that Unity overrides the Equals operator,
// normal null checks such as (x == null) do not always work as
// expected
// In those cases you can use this function which will also
// work with non-unity objects
public static bool IsNull(System.Object obj)
{
return obj == null || obj.Equals(null);
}
#if UNITY_EDITOR
// This can be useful if you are running code outside unity
// since in that case you have to make sure to avoid calling anything
// inside Unity DLLs
public static bool IsOutsideUnity()
{
return AppDomain.CurrentDomain.FriendlyName != "Unity Child Domain";
}
#endif
public static bool AreFunctionsEqual(Delegate left, Delegate right)
{
return left.Target == right.Target && left.Method() == right.Method();
}
// Taken from here:
// http://stackoverflow.com/questions/28937324/in-c-how-could-i-get-a-classs-inheritance-distance-to-base-class/28937542#28937542
public static int GetInheritanceDelta(Type derived, Type parent)
{
Assert.That(derived.DerivesFromOrEqual(parent));
if (parent.IsInterface())
{
// Not sure if we can calculate this so just return 1
return 1;
}
if (derived == parent)
{
return 0;
}
int distance = 1;
Type child = derived;
while ((child = child.BaseType()) != parent)
{
distance++;
}
return distance;
}
#if !NOT_UNITY3D
public static IEnumerable<SceneContext> GetAllSceneContexts()
{
foreach (var scene in UnityUtil.AllLoadedScenes)
{
var contexts = scene.GetRootGameObjects()
.SelectMany(root => root.GetComponentsInChildren<SceneContext>()).ToList();
if (contexts.IsEmpty())
{
continue;
}
Assert.That(contexts.Count == 1,
"Found multiple scene contexts in scene '{0}'", scene.name);
yield return contexts[0];
}
}
public static void AddStateMachineBehaviourAutoInjectersInScene(Scene scene)
{
foreach (var rootObj in GetRootGameObjects(scene))
{
if (rootObj != null)
{
AddStateMachineBehaviourAutoInjectersUnderGameObject(rootObj);
}
}
}
// Call this before calling GetInjectableMonoBehavioursUnderGameObject to ensure that the StateMachineBehaviour's
// also get injected properly
// The StateMachineBehaviour's cannot be retrieved until after the Start() method so we
// need to use ZenjectStateMachineBehaviourAutoInjecter to do the injection at that
// time for us
public static void AddStateMachineBehaviourAutoInjectersUnderGameObject(GameObject root)
{
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
#endif
{
var animators = root.GetComponentsInChildren<Animator>(true);
foreach (var animator in animators)
{
if (animator.gameObject.GetComponent<ZenjectStateMachineBehaviourAutoInjecter>() == null)
{
animator.gameObject.AddComponent<ZenjectStateMachineBehaviourAutoInjecter>();
}
}
}
}
public static void GetInjectableMonoBehavioursInScene(
Scene scene, List<MonoBehaviour> monoBehaviours)
{
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
#endif
{
foreach (var rootObj in GetRootGameObjects(scene))
{
if (rootObj != null)
{
GetInjectableMonoBehavioursUnderGameObjectInternal(rootObj, monoBehaviours);
}
}
}
}
// NOTE: This method will not return components that are within a GameObjectContext
// It returns monobehaviours in a bottom-up order
public static void GetInjectableMonoBehavioursUnderGameObject(
GameObject gameObject, List<MonoBehaviour> injectableComponents)
{
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
#endif
{
GetInjectableMonoBehavioursUnderGameObjectInternal(gameObject, injectableComponents);
}
}
static void GetInjectableMonoBehavioursUnderGameObjectInternal(
GameObject gameObject, List<MonoBehaviour> injectableComponents)
{
if (gameObject == null)
{
return;
}
var monoBehaviours = gameObject.GetComponents<MonoBehaviour>();
for (int i = 0; i < monoBehaviours.Length; i++)
{
var monoBehaviour = monoBehaviours[i];
// Can be null for broken component references
if (monoBehaviour != null
&& monoBehaviour.GetType().DerivesFromOrEqual<GameObjectContext>())
{
// Need to make sure we don't inject on any MonoBehaviour's that are below a GameObjectContext
// Since that is the responsibility of the GameObjectContext
// BUT we do want to inject on the GameObjectContext itself
injectableComponents.Add(monoBehaviour);
return;
}
}
// Recurse first so it adds components bottom up though it shouldn't really matter much
// because it should always inject in the dependency order
for (int i = 0; i < gameObject.transform.childCount; i++)
{
var child = gameObject.transform.GetChild(i);
if (child != null)
{
GetInjectableMonoBehavioursUnderGameObjectInternal(child.gameObject, injectableComponents);
}
}
for (int i = 0; i < monoBehaviours.Length; i++)
{
var monoBehaviour = monoBehaviours[i];
// Can be null for broken component references
if (monoBehaviour != null
&& IsInjectableMonoBehaviourType(monoBehaviour.GetType()))
{
injectableComponents.Add(monoBehaviour);
}
}
}
public static bool IsInjectableMonoBehaviourType(Type type)
{
// Do not inject on installers since these are always injected before they are installed
return type != null && !type.DerivesFrom<MonoInstaller>() && TypeAnalyzer.HasInfo(type);
}
public static IEnumerable<GameObject> GetRootGameObjects(Scene scene)
{
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("Searching Hierarchy"))
#endif
{
if (scene.isLoaded)
{
return scene.GetRootGameObjects()
.Where(x => x.GetComponent<ProjectContext>() == null);
}
// Note: We can't use scene.GetRootObjects() here because that apparently fails with an exception
// about the scene not being loaded yet when executed in Awake
// We also can't use GameObject.FindObjectsOfType<Transform>() because that does not include inactive game objects
// So we use Resources.FindObjectsOfTypeAll, even though that may include prefabs. However, our assumption here
// is that prefabs do not have their "scene" property set correctly so this should work
//
// It's important here that we only inject into root objects that are part of our scene, to properly support
// multi-scene editing features of Unity 5.x
//
// Also, even with older Unity versions, if there is an object that is marked with DontDestroyOnLoad, then it will
// be injected multiple times when another scene is loaded
//
// We also make sure not to inject into the project root objects which are injected by ProjectContext.
return Resources.FindObjectsOfTypeAll<GameObject>()
.Where(x => x.transform.parent == null
&& x.GetComponent<ProjectContext>() == null
&& x.scene == scene);
}
}
#if UNITY_EDITOR
// Returns a Transform in the DontDestroyOnLoad scene (or, if we're not in play mode, within the current active scene)
// whose GameObject is inactive, and whose hide flags are set to HideAndDontSave. We can instantiate prefabs in here
// without any of their Awake() methods firing.
public static Transform GetOrCreateInactivePrefabParent()
{
if(_disabledIndestructibleGameObject == null || (!Application.isPlaying && _disabledIndestructibleGameObject.scene != SceneManager.GetActiveScene()))
{
var go = new GameObject("ZenUtilInternal_PrefabParent");
go.hideFlags = HideFlags.HideAndDontSave;
go.SetActive(false);
if(Application.isPlaying)
{
UnityEngine.Object.DontDestroyOnLoad(go);
}
_disabledIndestructibleGameObject = go;
}
return _disabledIndestructibleGameObject.transform;
}
#endif
#endif
}
}