using System.Linq; using System.Collections.Generic; using UnityEngine; using System; using System.Reflection; namespace GameplayIngredients { [HelpURL(Help.URL + "managers")] public abstract class Manager : MonoBehaviour { private static Dictionary s_Managers = new Dictionary(); public static bool TryGet(out T manager) where T: Manager { manager = null; if(s_Managers.ContainsKey(typeof(T))) { manager = (T)s_Managers[typeof(T)]; return true; } else return false; } public static T Get() where T: Manager { if(s_Managers.ContainsKey(typeof(T))) return (T)s_Managers[typeof(T)]; else { Debug.LogError($"Manager of type '{typeof(T)}' could not be accessed. Check the excludedManagers list in your GameplayIngredientsSettings configuration file."); return null; } } public static bool Has() where T:Manager { return(s_Managers.ContainsKey(typeof(T))); } static readonly Type[] kAllManagerTypes = TypeUtility.GetConcreteTypes(); [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoCreateAll() { s_Managers.Clear(); var exclusionList = GameplayIngredientsSettings.currentSettings.excludedeManagers; if(GameplayIngredientsSettings.currentSettings.verboseCalls) Debug.Log("Initializing all Managers..."); DependencyGraph dg = new DependencyGraph(); foreach (var type in kAllManagerTypes) { // Check for any Do Not Create Attribute var doNotCreateAttr = type.GetCustomAttribute(); if (doNotCreateAttr != null) continue; dg.Add(type); var dependencies = type.GetCustomAttribute(); if(dependencies != null) { foreach(var depType in dependencies.dependantTypes) { dg.AddDependency(type, depType); } } } if (exclusionList != null) { foreach (var type in exclusionList) { if(dg.TryExclude(type, exclusionList)) { if (GameplayIngredientsSettings.currentSettings.verboseCalls) Debug.LogWarning($"Manager : Excluded {type} from manager creation"); } else { if (GameplayIngredientsSettings.currentSettings.verboseCalls) Debug.LogWarning($"Manager : Could not exclude {type} from manager creation because it has dependencies"); } } } List toBeCreated = dg.GetOrderedList(); // Finally, create all managers foreach (var type in toBeCreated) { var prefabAttr = type.GetCustomAttribute(); GameObject gameObject; if (prefabAttr != null) { var prefab = Resources.Load(prefabAttr.prefab); if (prefab == null) // Try loading the "Default_" prefixed version of the prefab { prefab = Resources.Load("Default_" + prefabAttr.prefab); } if (prefab != null) { gameObject = GameObject.Instantiate(prefab); } else { Debug.LogError($"Could not instantiate default prefab for {type.ToString()} : No prefab '{prefabAttr.prefab}' found in resources folders. Ignoring..."); continue; } } else { gameObject = new GameObject(); gameObject.AddComponent(type); } gameObject.name = type.Name; GameObject.DontDestroyOnLoad(gameObject); var comp = (Manager)gameObject.GetComponent(type); s_Managers.Add(type, comp); if (GameplayIngredientsSettings.currentSettings.verboseCalls) Debug.Log(string.Format(" -> <{0}> OK", type.Name)); } } class DependencyGraph { List nodes; public DependencyGraph() { nodes = new List(); } public void Add(Type o) { if (!nodes.Any(n => n.target == o)) { var node = new DependencyNode(o); nodes.Add(node); } } DependencyNode Get(Type o) { return nodes.Where(n => n.target == o).FirstOrDefault(); } public void AddDependency(Type o, Type dependency) { Add(o); Add(dependency); var node = Get(o); if(!IsDependentOn(dependency, o)) // Prevent Circular Dependency { node.dependencies.Add(Get(dependency)); } else { if (GameplayIngredientsSettings.currentSettings.verboseCalls) Debug.LogWarning($"Managers : Found circular dependency {o} -> {dependency}, ignoring"); } } // Try exclude a type by its name, if it's not a dependency of another node public bool TryExclude(string type, string[] exclusionList) { var n = nodes.Where(n => n.target.Name == type).FirstOrDefault(); foreach(var node in nodes) { if (node == n) continue; // If type our type is a dependency or is already excluded, then we don't remove the node if (IsDependentOn(node.target, n.target) || exclusionList.Contains(node.target.Name)) return false; } n.excluded = true; return true; } // Walk the tree to find dependencies public bool IsDependentOn(Type type, Type dependency) { foreach(var dep in Get(type).dependencies) { if (dep.target == type) return true; else return (IsDependentOn(dep.target, dependency)); } return false; } // Return all dependencies of a given type public List GetDependencies(Type type) { List t = new List(); t.Add(type); foreach(var dep in Get(type).dependencies) { if (!t.Contains(dep.target)) t.Concat(GetDependencies(dep.target)); } return t; } // Builds a list of loading order public List GetOrderedList() { Dictionary d = new Dictionary(); foreach(var node in nodes) { if (node.excluded) continue; if (!d.ContainsKey(node.target)) d.Add(node.target, 0); else d[node.target] += 1; foreach(var dep in node.dependencies) { if (dep.excluded) continue; if (!d.ContainsKey(dep.target)) d.Add(dep.target, 0); else d[dep.target] += 1; } } return d.OrderBy(x => x.Value).Reverse().ToDictionary(x => x.Key, x => x.Value).Keys.ToList(); } class DependencyNode { public List dependencies; public readonly Type target; public bool excluded; public DependencyNode(Type target) { dependencies = new List(); this.target = target; this.excluded = false; } public override string ToString() { return target.Name; } } } } }