using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Xml; using SUISS.Core; using SUISS.Core.Utilities; using SUISS.Storage; using UnityEngine; namespace Engine.DependencyTree { public sealed class DependencyTree : SingletonMonobehaviour { //[DebuggerBrowsable(DebuggerBrowsableState.Never)] public event Dependency.AchievedChangedEventHandler DependencyAchievedChangedEvent; //[DebuggerBrowsable(DebuggerBrowsableState.Never)] public event Dependency.ActiveChangedEventHandler DependencyActiveChangedEvent; //[DebuggerBrowsable(DebuggerBrowsableState.Never)] public event Dependency.RewardClaimedEventHandler DependencyRewardClaimedEvent; protected override void Awake() { base.Awake(); if (!this._isValidNewInstance) { return; } this._mutating = 0; this._persistentState = Storage.Get(this.storageLifecycle).GetDictionary(this.storageKey); this._communicator = new DependencyTree.CommunicatorInstance(this); this._cache = new Dictionary(); this._templates = new Dictionary(); this._treeState = DependencyState.Created; this.BeginMutating(); try { this._root = this.CreateDependency("root", null); this._cache[this._root.Identifier] = this._root; if (this.treeXML != null) { this.ParseTreeXML(this.treeXML.text); } } finally { this.EndMutating(); } } private void Start() { this._treeState = DependencyState.Started; this.Traverse(delegate(Dependency dependency) { if (dependency.State == DependencyState.Created) { dependency.OnStart(); } }); } protected override void OnDestroy() { base.OnDestroy(); this._treeState = DependencyState.Destroyed; this.Traverse(delegate(Dependency dependency) { if (dependency.State != DependencyState.Destroyed) { dependency.OnDestroy(); } }); } public Dependency Root { get { return this._root; } } public Dependency this[string identifier] { get { if (identifier == null) { throw new ArgumentNullException("identifier"); } Dependency result; if (this._cache.TryGetValue(identifier, out result)) { return result; } throw new KeyNotFoundException("No dependency with identifier " + identifier + " exists."); } } public bool IsMutating { get { return this._mutating > 0; } } public bool ContainsDependency(string identifier) { if (identifier == null) { throw new ArgumentNullException("identifier"); } return this._cache.ContainsKey(identifier); } public bool TryGetDependency(string identifier, out Dependency dependency) { if (identifier == null) { throw new ArgumentNullException("identifier"); } return this._cache.TryGetValue(identifier, out dependency); } public bool ContainsTemplate(string identifier) { if (identifier == null) { throw new ArgumentNullException("identifier"); } return this._templates.ContainsKey(identifier); } public DependencyTemplate GetTemplate(string identifier) { if (identifier == null) { throw new ArgumentNullException("identifier"); } return this._templates[identifier]; } public bool TryGetTemplate(string identifier, out DependencyTemplate template) { if (identifier == null) { throw new ArgumentNullException("identifier"); } return this._templates.TryGetValue(identifier, out template); } public void BeginMutating() { this._mutating++; if (this._mutating == 1) { this._dependencyTypeCache = new Dictionary(); this.Traverse(delegate(Dependency dependency) { dependency.OnBeginMutating(); }); } } public void EndMutating() { if (this._mutating == 1) { this.Traverse(delegate(Dependency dependency) { dependency.OnEndMutating(); }); this._dependencyTypeCache = null; } this._mutating--; } public Dependency CreateDependency(string identifier, Dictionary properties) { return this.CreateDependency(identifier, properties, null); } public Dependency CreateDependency(string identifier, Dictionary properties, string className) { this.EnsureMutating(); if (className == null) { className = typeof(Dependency).FullName; } ConstructorInfo constructor; if (!this._dependencyTypeCache.TryGetValue(className, out constructor)) { Type type = null; try { type = Type.GetType(className); } catch (Exception ex) { UnityEngine.Debug.LogError("Exception while trying to resolve type from string '" + className + "': " + ex.ToString()); } if (type == null) { UnityEngine.Debug.LogError("Could not resolve type from string '" + className + "'. This is probably a big error."); return null; } if (!typeof(Dependency).GetCompatibleTypeInfo().IsAssignableFrom(type.GetCompatibleTypeInfo())) { UnityEngine.Debug.LogError("Type '" + className + "' is not a subclass of Dependency. This is probably a big error."); return null; } Type[] types = new Type[] { typeof(string), typeof(Dictionary), typeof(Dictionary), typeof(DependencyTree.Communicator) }; constructor = type.GetConstructor(types); if (constructor == null) { UnityEngine.Debug.LogError("Dependency type '" + className + "' does not have a constructor in the form (string, Dictionary, Dictionary, DependencyTree.Communicator). Defaulting to type Dependency, but this is probably a big error."); constructor = typeof(Dependency).GetConstructor(types); } this._dependencyTypeCache[className] = constructor; } object[] parameters = new object[] { identifier, this.GetDependencyPersistentState(identifier), properties, this._communicator }; return (Dependency)constructor.Invoke(parameters); } public void Traverse(Dependency.Traverser traverser) { if (this._root != null) { this._root.Traverse(traverser); } } private void OnSubTreeAttached(Dependency parent, Dependency child) { this.EnsureMutating(); this.AddToCache(child); if (this._treeState == DependencyState.Destroyed) { child.Traverse(delegate(Dependency c) { if (c.State != DependencyState.Destroyed) { c.OnDestroy(); } }); } else if (this._treeState == DependencyState.Started) { child.Traverse(delegate(Dependency c) { if (c.State == DependencyState.Created) { c.OnStart(); } }); } } private void OnSubTreeDetached(Dependency parent, Dependency child) { this.EnsureMutating(); this.RemoveFromCache(child); } private void OnSubTreeAchievedChanged(Dependency subTree, bool achieved) { this.FireDependencyAchievedChangedEvent(subTree, achieved); } private void OnDependencyActiveChanged(Dependency dependency, bool active) { this.FireDependencyActiveChangedEvent(dependency, active); } private void OnDependencyRewardClaimed(Dependency dependency, bool claimed) { this.FireDependencyRewardClaimedEvent(dependency, claimed); } private void FireDependencyAchievedChangedEvent(Dependency dependency, bool achieved) { if (this.DependencyAchievedChangedEvent != null) { this.DependencyAchievedChangedEvent(dependency, achieved); } } private void FireDependencyActiveChangedEvent(Dependency dependency, bool active) { if (this.DependencyActiveChangedEvent != null) { this.DependencyActiveChangedEvent(dependency, active); } } private void FireDependencyRewardClaimedEvent(Dependency dependency, bool claimed) { if (this.DependencyRewardClaimedEvent != null) { this.DependencyRewardClaimedEvent(dependency, claimed); } } private Dictionary GetDependencyPersistentState(string identifier) { if (identifier == null) { return null; } object obj; if (this._persistentState.TryGetValue(identifier, out obj) && obj is Dictionary) { return (Dictionary)obj; } Dictionary dictionary = new Dictionary(); this._persistentState[identifier] = dictionary; return dictionary; } private void EnsureMutating() { if (this._mutating <= 0) { throw new InvalidOperationException("Can't mutate DependencyTree outside of calls to BeginMutate() and EndMutate()."); } } private void ParseTreeXML(string xml) { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.XmlResolver = null; xmlDocument.LoadXml(xml); XmlNode lastChild = xmlDocument.LastChild; if (lastChild.Name != "dependency-tree") { throw new FormatException("Missing root node."); } this.BeginMutating(); try { Dictionary dictionary = new Dictionary(); IEnumerator enumerator = lastChild.GetEnumerator(); try { while (enumerator.MoveNext()) { object obj = enumerator.Current; XmlNode xmlNode = (XmlNode)obj; if (xmlNode.Name == "class-shortcut") { string text = null; XmlAttribute xmlAttribute = xmlNode.Attributes["name"]; if (xmlAttribute != null) { text = xmlAttribute.Value; if (text.Length == 0) { text = null; } } if (text == null) { throw new FormatException("Class-shortcut node is missing a 'name' attribute."); } string innerText = xmlNode.InnerText; if (string.IsNullOrEmpty(innerText)) { throw new FormatException("Class-shortcut node is missing a class name as its InnerText."); } dictionary[text] = innerText; } else if (xmlNode.Name == "dependency") { this.ParseDependencyXmlNode(this._root, xmlNode, dictionary); } else if (xmlNode.Name == "template") { this.ParseTemplateXmlNode(this._root, xmlNode, dictionary); } } } finally { IDisposable disposable; if ((disposable = (enumerator as IDisposable)) != null) { disposable.Dispose(); } } } finally { this.EndMutating(); } } private void ParseDependencyXmlNode(Dependency parent, XmlNode node, Dictionary classShortcuts) { string text = null; XmlAttribute xmlAttribute = node.Attributes["id"]; if (xmlAttribute != null) { text = xmlAttribute.Value; if (text.Length == 0) { text = null; } } string text2 = null; xmlAttribute = node.Attributes["class"]; if (xmlAttribute != null) { text2 = xmlAttribute.Value; if (text2.Length == 0) { text2 = null; } } if (text2 != null && classShortcuts.ContainsKey(text2)) { text2 = classShortcuts[text2]; } Dictionary dictionary = null; List list = null; List list2 = null; IEnumerator enumerator = node.GetEnumerator(); try { while (enumerator.MoveNext()) { object obj = enumerator.Current; XmlNode xmlNode = (XmlNode)obj; if (xmlNode.Name == "property") { if (dictionary == null) { dictionary = new Dictionary(); } KeyValuePair keyValuePair = this.ParsePropertyXmlNode(xmlNode); if (dictionary.ContainsKey(keyValuePair.Key)) { throw new FormatException(string.Concat(new string[] { "Dependency '", text, "' contains a duplicate property '", keyValuePair.Key, "'." })); } dictionary[keyValuePair.Key] = keyValuePair.Value; } else if (xmlNode.Name == "dependency") { if (list == null) { list = new List(); } list.Add(xmlNode); } else if (xmlNode.Name == "template") { if (list2 == null) { list2 = new List(); } list2.Add(xmlNode); } } } finally { IDisposable disposable; if ((disposable = (enumerator as IDisposable)) != null) { disposable.Dispose(); } } Dependency dependency = this.CreateDependency(text, dictionary, text2); if (dependency != null) { parent.AddChild(dependency); if (list != null) { foreach (XmlNode node2 in list) { this.ParseDependencyXmlNode(dependency, node2, classShortcuts); } } if (list2 != null) { foreach (XmlNode node3 in list2) { this.ParseTemplateXmlNode(dependency, node3, classShortcuts); } } } } private void ParseTemplateXmlNode(Dependency parent, XmlNode node, Dictionary classShortcuts) { string text = null; XmlAttribute xmlAttribute = node.Attributes["id"]; if (xmlAttribute != null) { text = xmlAttribute.Value; if (text.Length == 0) { text = null; } } if (text == null) { throw new FormatException("Template node is missing an 'id' attribute."); } if (this._templates.ContainsKey(text)) { throw new FormatException("A template with identifier '" + text + "' already exists."); } string text2 = null; xmlAttribute = node.Attributes["class"]; if (xmlAttribute != null) { text2 = xmlAttribute.Value; if (text2.Length == 0) { text2 = null; } } if (text2 != null && classShortcuts.ContainsKey(text2)) { text2 = classShortcuts[text2]; } Dictionary dictionary = new Dictionary(); IEnumerator enumerator = node.GetEnumerator(); try { while (enumerator.MoveNext()) { object obj = enumerator.Current; XmlNode xmlNode = (XmlNode)obj; if (xmlNode.Name == "property") { KeyValuePair keyValuePair = this.ParsePropertyXmlNode(xmlNode); if (dictionary.ContainsKey(keyValuePair.Key)) { throw new FormatException(string.Concat(new string[] { "Template '", text, "' contains a duplicate property '", keyValuePair.Key, "'." })); } dictionary[keyValuePair.Key] = keyValuePair.Value; } else if (xmlNode.Name == "dependency" || xmlNode.Name == "template") { throw new FormatException("Dependencies and templates cannot be nested within a template."); } } } finally { IDisposable disposable; if ((disposable = (enumerator as IDisposable)) != null) { disposable.Dispose(); } } DependencyTemplate dependencyTemplate = new DependencyTemplate(text, text2, dictionary); this._templates[dependencyTemplate.Identifier] = dependencyTemplate; } private KeyValuePair ParsePropertyXmlNode(XmlNode node) { string text = null; XmlAttribute xmlAttribute = node.Attributes["name"]; if (xmlAttribute != null) { text = xmlAttribute.Value; if (text.Length == 0) { text = null; } } if (text == null) { throw new FormatException("Property node is missing a 'name' attribute."); } return new KeyValuePair(text, node.InnerText); } private void AddToCache(Dependency dep) { if (dep.Identifier != null) { if (this._cache.ContainsKey(dep.Identifier)) { throw new ArgumentException("A dependency with identifier '" + dep.Identifier + "' already exists.", "dep"); } this._cache[dep.Identifier] = dep; } foreach (Dependency dep2 in dep.Children) { this.AddToCache(dep2); } } private void RemoveFromCache(Dependency dep) { if (dep.Identifier != null) { this._cache.Remove(dep.Identifier); } foreach (Dependency dep2 in dep.Children) { this.RemoveFromCache(dep2); } } public const string RootIdentifier = "root"; public StorageLifecycle storageLifecycle = StorageLifecycle.Game; public string storageKey = "DependencyTree"; public TextAsset treeXML; private int _mutating; private Dictionary _dependencyTypeCache; private Dictionary _persistentState; private DependencyState _treeState; private DependencyTree.Communicator _communicator; private Dependency _root; private Dictionary _cache; private Dictionary _templates; public interface Communicator { DependencyTree Tree { get; } void NotifySubTreeAttached(Dependency parent, Dependency child); void NotifySubTreeDetached(Dependency parent, Dependency child); void NotifySubTreeAchievedChanged(Dependency subTree, bool achieved); void NotifyActiveChanged(Dependency dependency, bool active); void NotifyRewardClaimed(Dependency dependency, bool claimed); } private class CommunicatorInstance : DependencyTree.Communicator { public CommunicatorInstance(DependencyTree tree) { if (tree._communicator != null) { throw new InvalidOperationException("The specified tree already has a communicator."); } this._tree = tree; } public DependencyTree Tree { get { return this._tree; } } public void NotifySubTreeAttached(Dependency parent, Dependency child) { this._tree.OnSubTreeAttached(parent, child); } public void NotifySubTreeDetached(Dependency parent, Dependency child) { this._tree.OnSubTreeDetached(parent, child); } public void NotifySubTreeAchievedChanged(Dependency subTree, bool achieved) { this._tree.OnSubTreeAchievedChanged(subTree, achieved); } public void NotifyActiveChanged(Dependency dependency, bool active) { this._tree.OnDependencyActiveChanged(dependency, active); } public void NotifyRewardClaimed(Dependency dependency, bool claimed) { this._tree.OnDependencyRewardClaimed(dependency, claimed); } private DependencyTree _tree; } } }