您不能選擇超過 %s 個話題 話題必須以字母或數字為開頭,可包含連接號 ('-') 且最長為 35 個字
 
 
 

727 行
18 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Reflection;
  6. using System.Xml;
  7. using SUISS.Core;
  8. using SUISS.Core.Utilities;
  9. using SUISS.Storage;
  10. using UnityEngine;
  11. namespace Engine.DependencyTree
  12. {
  13. public sealed class DependencyTree : SingletonMonobehaviour<DependencyTree>
  14. {
  15. //[DebuggerBrowsable(DebuggerBrowsableState.Never)]
  16. public event Dependency.AchievedChangedEventHandler DependencyAchievedChangedEvent;
  17. //[DebuggerBrowsable(DebuggerBrowsableState.Never)]
  18. public event Dependency.ActiveChangedEventHandler DependencyActiveChangedEvent;
  19. //[DebuggerBrowsable(DebuggerBrowsableState.Never)]
  20. public event Dependency.RewardClaimedEventHandler DependencyRewardClaimedEvent;
  21. protected override void Awake()
  22. {
  23. base.Awake();
  24. if (!this._isValidNewInstance)
  25. {
  26. return;
  27. }
  28. this._mutating = 0;
  29. this._persistentState = Storage.Get(this.storageLifecycle).GetDictionary(this.storageKey);
  30. this._communicator = new DependencyTree.CommunicatorInstance(this);
  31. this._cache = new Dictionary<string, Dependency>();
  32. this._templates = new Dictionary<string, DependencyTemplate>();
  33. this._treeState = DependencyState.Created;
  34. this.BeginMutating();
  35. try
  36. {
  37. this._root = this.CreateDependency("root", null);
  38. this._cache[this._root.Identifier] = this._root;
  39. if (this.treeXML != null)
  40. {
  41. this.ParseTreeXML(this.treeXML.text);
  42. }
  43. }
  44. finally
  45. {
  46. this.EndMutating();
  47. }
  48. }
  49. private void Start()
  50. {
  51. this._treeState = DependencyState.Started;
  52. this.Traverse(delegate(Dependency dependency)
  53. {
  54. if (dependency.State == DependencyState.Created)
  55. {
  56. dependency.OnStart();
  57. }
  58. });
  59. }
  60. protected override void OnDestroy()
  61. {
  62. base.OnDestroy();
  63. this._treeState = DependencyState.Destroyed;
  64. this.Traverse(delegate(Dependency dependency)
  65. {
  66. if (dependency.State != DependencyState.Destroyed)
  67. {
  68. dependency.OnDestroy();
  69. }
  70. });
  71. }
  72. public Dependency Root
  73. {
  74. get
  75. {
  76. return this._root;
  77. }
  78. }
  79. public Dependency this[string identifier]
  80. {
  81. get
  82. {
  83. if (identifier == null)
  84. {
  85. throw new ArgumentNullException("identifier");
  86. }
  87. Dependency result;
  88. if (this._cache.TryGetValue(identifier, out result))
  89. {
  90. return result;
  91. }
  92. throw new KeyNotFoundException("No dependency with identifier " + identifier + " exists.");
  93. }
  94. }
  95. public bool IsMutating
  96. {
  97. get
  98. {
  99. return this._mutating > 0;
  100. }
  101. }
  102. public bool ContainsDependency(string identifier)
  103. {
  104. if (identifier == null)
  105. {
  106. throw new ArgumentNullException("identifier");
  107. }
  108. return this._cache.ContainsKey(identifier);
  109. }
  110. public bool TryGetDependency(string identifier, out Dependency dependency)
  111. {
  112. if (identifier == null)
  113. {
  114. throw new ArgumentNullException("identifier");
  115. }
  116. return this._cache.TryGetValue(identifier, out dependency);
  117. }
  118. public bool ContainsTemplate(string identifier)
  119. {
  120. if (identifier == null)
  121. {
  122. throw new ArgumentNullException("identifier");
  123. }
  124. return this._templates.ContainsKey(identifier);
  125. }
  126. public DependencyTemplate GetTemplate(string identifier)
  127. {
  128. if (identifier == null)
  129. {
  130. throw new ArgumentNullException("identifier");
  131. }
  132. return this._templates[identifier];
  133. }
  134. public bool TryGetTemplate(string identifier, out DependencyTemplate template)
  135. {
  136. if (identifier == null)
  137. {
  138. throw new ArgumentNullException("identifier");
  139. }
  140. return this._templates.TryGetValue(identifier, out template);
  141. }
  142. public void BeginMutating()
  143. {
  144. this._mutating++;
  145. if (this._mutating == 1)
  146. {
  147. this._dependencyTypeCache = new Dictionary<string, ConstructorInfo>();
  148. this.Traverse(delegate(Dependency dependency)
  149. {
  150. dependency.OnBeginMutating();
  151. });
  152. }
  153. }
  154. public void EndMutating()
  155. {
  156. if (this._mutating == 1)
  157. {
  158. this.Traverse(delegate(Dependency dependency)
  159. {
  160. dependency.OnEndMutating();
  161. });
  162. this._dependencyTypeCache = null;
  163. }
  164. this._mutating--;
  165. }
  166. public Dependency CreateDependency(string identifier, Dictionary<string, string> properties)
  167. {
  168. return this.CreateDependency(identifier, properties, null);
  169. }
  170. public Dependency CreateDependency(string identifier, Dictionary<string, string> properties, string className)
  171. {
  172. this.EnsureMutating();
  173. if (className == null)
  174. {
  175. className = typeof(Dependency).FullName;
  176. }
  177. ConstructorInfo constructor;
  178. if (!this._dependencyTypeCache.TryGetValue(className, out constructor))
  179. {
  180. Type type = null;
  181. try
  182. {
  183. type = Type.GetType(className);
  184. }
  185. catch (Exception ex)
  186. {
  187. UnityEngine.Debug.LogError("Exception while trying to resolve type from string '" + className + "': " + ex.ToString());
  188. }
  189. if (type == null)
  190. {
  191. UnityEngine.Debug.LogError("Could not resolve type from string '" + className + "'. This is probably a big error.");
  192. return null;
  193. }
  194. if (!typeof(Dependency).GetCompatibleTypeInfo().IsAssignableFrom(type.GetCompatibleTypeInfo()))
  195. {
  196. UnityEngine.Debug.LogError("Type '" + className + "' is not a subclass of Dependency. This is probably a big error.");
  197. return null;
  198. }
  199. Type[] types = new Type[]
  200. {
  201. typeof(string),
  202. typeof(Dictionary<string, object>),
  203. typeof(Dictionary<string, string>),
  204. typeof(DependencyTree.Communicator)
  205. };
  206. constructor = type.GetConstructor(types);
  207. if (constructor == null)
  208. {
  209. UnityEngine.Debug.LogError("Dependency type '" + className + "' does not have a constructor in the form (string, Dictionary<string, object>, Dictionary<string, string>, DependencyTree.Communicator). Defaulting to type Dependency, but this is probably a big error.");
  210. constructor = typeof(Dependency).GetConstructor(types);
  211. }
  212. this._dependencyTypeCache[className] = constructor;
  213. }
  214. object[] parameters = new object[]
  215. {
  216. identifier,
  217. this.GetDependencyPersistentState(identifier),
  218. properties,
  219. this._communicator
  220. };
  221. return (Dependency)constructor.Invoke(parameters);
  222. }
  223. public void Traverse(Dependency.Traverser traverser)
  224. {
  225. if (this._root != null)
  226. {
  227. this._root.Traverse(traverser);
  228. }
  229. }
  230. private void OnSubTreeAttached(Dependency parent, Dependency child)
  231. {
  232. this.EnsureMutating();
  233. this.AddToCache(child);
  234. if (this._treeState == DependencyState.Destroyed)
  235. {
  236. child.Traverse(delegate(Dependency c)
  237. {
  238. if (c.State != DependencyState.Destroyed)
  239. {
  240. c.OnDestroy();
  241. }
  242. });
  243. }
  244. else if (this._treeState == DependencyState.Started)
  245. {
  246. child.Traverse(delegate(Dependency c)
  247. {
  248. if (c.State == DependencyState.Created)
  249. {
  250. c.OnStart();
  251. }
  252. });
  253. }
  254. }
  255. private void OnSubTreeDetached(Dependency parent, Dependency child)
  256. {
  257. this.EnsureMutating();
  258. this.RemoveFromCache(child);
  259. }
  260. private void OnSubTreeAchievedChanged(Dependency subTree, bool achieved)
  261. {
  262. this.FireDependencyAchievedChangedEvent(subTree, achieved);
  263. }
  264. private void OnDependencyActiveChanged(Dependency dependency, bool active)
  265. {
  266. this.FireDependencyActiveChangedEvent(dependency, active);
  267. }
  268. private void OnDependencyRewardClaimed(Dependency dependency, bool claimed)
  269. {
  270. this.FireDependencyRewardClaimedEvent(dependency, claimed);
  271. }
  272. private void FireDependencyAchievedChangedEvent(Dependency dependency, bool achieved)
  273. {
  274. if (this.DependencyAchievedChangedEvent != null)
  275. {
  276. this.DependencyAchievedChangedEvent(dependency, achieved);
  277. }
  278. }
  279. private void FireDependencyActiveChangedEvent(Dependency dependency, bool active)
  280. {
  281. if (this.DependencyActiveChangedEvent != null)
  282. {
  283. this.DependencyActiveChangedEvent(dependency, active);
  284. }
  285. }
  286. private void FireDependencyRewardClaimedEvent(Dependency dependency, bool claimed)
  287. {
  288. if (this.DependencyRewardClaimedEvent != null)
  289. {
  290. this.DependencyRewardClaimedEvent(dependency, claimed);
  291. }
  292. }
  293. private Dictionary<string, object> GetDependencyPersistentState(string identifier)
  294. {
  295. if (identifier == null)
  296. {
  297. return null;
  298. }
  299. object obj;
  300. if (this._persistentState.TryGetValue(identifier, out obj) && obj is Dictionary<string, object>)
  301. {
  302. return (Dictionary<string, object>)obj;
  303. }
  304. Dictionary<string, object> dictionary = new Dictionary<string, object>();
  305. this._persistentState[identifier] = dictionary;
  306. return dictionary;
  307. }
  308. private void EnsureMutating()
  309. {
  310. if (this._mutating <= 0)
  311. {
  312. throw new InvalidOperationException("Can't mutate DependencyTree outside of calls to BeginMutate() and EndMutate().");
  313. }
  314. }
  315. private void ParseTreeXML(string xml)
  316. {
  317. XmlDocument xmlDocument = new XmlDocument();
  318. xmlDocument.XmlResolver = null;
  319. xmlDocument.LoadXml(xml);
  320. XmlNode lastChild = xmlDocument.LastChild;
  321. if (lastChild.Name != "dependency-tree")
  322. {
  323. throw new FormatException("Missing root <dependency-tree> node.");
  324. }
  325. this.BeginMutating();
  326. try
  327. {
  328. Dictionary<string, string> dictionary = new Dictionary<string, string>();
  329. IEnumerator enumerator = lastChild.GetEnumerator();
  330. try
  331. {
  332. while (enumerator.MoveNext())
  333. {
  334. object obj = enumerator.Current;
  335. XmlNode xmlNode = (XmlNode)obj;
  336. if (xmlNode.Name == "class-shortcut")
  337. {
  338. string text = null;
  339. XmlAttribute xmlAttribute = xmlNode.Attributes["name"];
  340. if (xmlAttribute != null)
  341. {
  342. text = xmlAttribute.Value;
  343. if (text.Length == 0)
  344. {
  345. text = null;
  346. }
  347. }
  348. if (text == null)
  349. {
  350. throw new FormatException("Class-shortcut node is missing a 'name' attribute.");
  351. }
  352. string innerText = xmlNode.InnerText;
  353. if (string.IsNullOrEmpty(innerText))
  354. {
  355. throw new FormatException("Class-shortcut node is missing a class name as its InnerText.");
  356. }
  357. dictionary[text] = innerText;
  358. }
  359. else if (xmlNode.Name == "dependency")
  360. {
  361. this.ParseDependencyXmlNode(this._root, xmlNode, dictionary);
  362. }
  363. else if (xmlNode.Name == "template")
  364. {
  365. this.ParseTemplateXmlNode(this._root, xmlNode, dictionary);
  366. }
  367. }
  368. }
  369. finally
  370. {
  371. IDisposable disposable;
  372. if ((disposable = (enumerator as IDisposable)) != null)
  373. {
  374. disposable.Dispose();
  375. }
  376. }
  377. }
  378. finally
  379. {
  380. this.EndMutating();
  381. }
  382. }
  383. private void ParseDependencyXmlNode(Dependency parent, XmlNode node, Dictionary<string, string> classShortcuts)
  384. {
  385. string text = null;
  386. XmlAttribute xmlAttribute = node.Attributes["id"];
  387. if (xmlAttribute != null)
  388. {
  389. text = xmlAttribute.Value;
  390. if (text.Length == 0)
  391. {
  392. text = null;
  393. }
  394. }
  395. string text2 = null;
  396. xmlAttribute = node.Attributes["class"];
  397. if (xmlAttribute != null)
  398. {
  399. text2 = xmlAttribute.Value;
  400. if (text2.Length == 0)
  401. {
  402. text2 = null;
  403. }
  404. }
  405. if (text2 != null && classShortcuts.ContainsKey(text2))
  406. {
  407. text2 = classShortcuts[text2];
  408. }
  409. Dictionary<string, string> dictionary = null;
  410. List<XmlNode> list = null;
  411. List<XmlNode> list2 = null;
  412. IEnumerator enumerator = node.GetEnumerator();
  413. try
  414. {
  415. while (enumerator.MoveNext())
  416. {
  417. object obj = enumerator.Current;
  418. XmlNode xmlNode = (XmlNode)obj;
  419. if (xmlNode.Name == "property")
  420. {
  421. if (dictionary == null)
  422. {
  423. dictionary = new Dictionary<string, string>();
  424. }
  425. KeyValuePair<string, string> keyValuePair = this.ParsePropertyXmlNode(xmlNode);
  426. if (dictionary.ContainsKey(keyValuePair.Key))
  427. {
  428. throw new FormatException(string.Concat(new string[]
  429. {
  430. "Dependency '",
  431. text,
  432. "' contains a duplicate property '",
  433. keyValuePair.Key,
  434. "'."
  435. }));
  436. }
  437. dictionary[keyValuePair.Key] = keyValuePair.Value;
  438. }
  439. else if (xmlNode.Name == "dependency")
  440. {
  441. if (list == null)
  442. {
  443. list = new List<XmlNode>();
  444. }
  445. list.Add(xmlNode);
  446. }
  447. else if (xmlNode.Name == "template")
  448. {
  449. if (list2 == null)
  450. {
  451. list2 = new List<XmlNode>();
  452. }
  453. list2.Add(xmlNode);
  454. }
  455. }
  456. }
  457. finally
  458. {
  459. IDisposable disposable;
  460. if ((disposable = (enumerator as IDisposable)) != null)
  461. {
  462. disposable.Dispose();
  463. }
  464. }
  465. Dependency dependency = this.CreateDependency(text, dictionary, text2);
  466. if (dependency != null)
  467. {
  468. parent.AddChild(dependency);
  469. if (list != null)
  470. {
  471. foreach (XmlNode node2 in list)
  472. {
  473. this.ParseDependencyXmlNode(dependency, node2, classShortcuts);
  474. }
  475. }
  476. if (list2 != null)
  477. {
  478. foreach (XmlNode node3 in list2)
  479. {
  480. this.ParseTemplateXmlNode(dependency, node3, classShortcuts);
  481. }
  482. }
  483. }
  484. }
  485. private void ParseTemplateXmlNode(Dependency parent, XmlNode node, Dictionary<string, string> classShortcuts)
  486. {
  487. string text = null;
  488. XmlAttribute xmlAttribute = node.Attributes["id"];
  489. if (xmlAttribute != null)
  490. {
  491. text = xmlAttribute.Value;
  492. if (text.Length == 0)
  493. {
  494. text = null;
  495. }
  496. }
  497. if (text == null)
  498. {
  499. throw new FormatException("Template node is missing an 'id' attribute.");
  500. }
  501. if (this._templates.ContainsKey(text))
  502. {
  503. throw new FormatException("A template with identifier '" + text + "' already exists.");
  504. }
  505. string text2 = null;
  506. xmlAttribute = node.Attributes["class"];
  507. if (xmlAttribute != null)
  508. {
  509. text2 = xmlAttribute.Value;
  510. if (text2.Length == 0)
  511. {
  512. text2 = null;
  513. }
  514. }
  515. if (text2 != null && classShortcuts.ContainsKey(text2))
  516. {
  517. text2 = classShortcuts[text2];
  518. }
  519. Dictionary<string, string> dictionary = new Dictionary<string, string>();
  520. IEnumerator enumerator = node.GetEnumerator();
  521. try
  522. {
  523. while (enumerator.MoveNext())
  524. {
  525. object obj = enumerator.Current;
  526. XmlNode xmlNode = (XmlNode)obj;
  527. if (xmlNode.Name == "property")
  528. {
  529. KeyValuePair<string, string> keyValuePair = this.ParsePropertyXmlNode(xmlNode);
  530. if (dictionary.ContainsKey(keyValuePair.Key))
  531. {
  532. throw new FormatException(string.Concat(new string[]
  533. {
  534. "Template '",
  535. text,
  536. "' contains a duplicate property '",
  537. keyValuePair.Key,
  538. "'."
  539. }));
  540. }
  541. dictionary[keyValuePair.Key] = keyValuePair.Value;
  542. }
  543. else if (xmlNode.Name == "dependency" || xmlNode.Name == "template")
  544. {
  545. throw new FormatException("Dependencies and templates cannot be nested within a template.");
  546. }
  547. }
  548. }
  549. finally
  550. {
  551. IDisposable disposable;
  552. if ((disposable = (enumerator as IDisposable)) != null)
  553. {
  554. disposable.Dispose();
  555. }
  556. }
  557. DependencyTemplate dependencyTemplate = new DependencyTemplate(text, text2, dictionary);
  558. this._templates[dependencyTemplate.Identifier] = dependencyTemplate;
  559. }
  560. private KeyValuePair<string, string> ParsePropertyXmlNode(XmlNode node)
  561. {
  562. string text = null;
  563. XmlAttribute xmlAttribute = node.Attributes["name"];
  564. if (xmlAttribute != null)
  565. {
  566. text = xmlAttribute.Value;
  567. if (text.Length == 0)
  568. {
  569. text = null;
  570. }
  571. }
  572. if (text == null)
  573. {
  574. throw new FormatException("Property node is missing a 'name' attribute.");
  575. }
  576. return new KeyValuePair<string, string>(text, node.InnerText);
  577. }
  578. private void AddToCache(Dependency dep)
  579. {
  580. if (dep.Identifier != null)
  581. {
  582. if (this._cache.ContainsKey(dep.Identifier))
  583. {
  584. throw new ArgumentException("A dependency with identifier '" + dep.Identifier + "' already exists.", "dep");
  585. }
  586. this._cache[dep.Identifier] = dep;
  587. }
  588. foreach (Dependency dep2 in dep.Children)
  589. {
  590. this.AddToCache(dep2);
  591. }
  592. }
  593. private void RemoveFromCache(Dependency dep)
  594. {
  595. if (dep.Identifier != null)
  596. {
  597. this._cache.Remove(dep.Identifier);
  598. }
  599. foreach (Dependency dep2 in dep.Children)
  600. {
  601. this.RemoveFromCache(dep2);
  602. }
  603. }
  604. public const string RootIdentifier = "root";
  605. public StorageLifecycle storageLifecycle = StorageLifecycle.Game;
  606. public string storageKey = "DependencyTree";
  607. public TextAsset treeXML;
  608. private int _mutating;
  609. private Dictionary<string, ConstructorInfo> _dependencyTypeCache;
  610. private Dictionary<string, object> _persistentState;
  611. private DependencyState _treeState;
  612. private DependencyTree.Communicator _communicator;
  613. private Dependency _root;
  614. private Dictionary<string, Dependency> _cache;
  615. private Dictionary<string, DependencyTemplate> _templates;
  616. public interface Communicator
  617. {
  618. DependencyTree Tree { get; }
  619. void NotifySubTreeAttached(Dependency parent, Dependency child);
  620. void NotifySubTreeDetached(Dependency parent, Dependency child);
  621. void NotifySubTreeAchievedChanged(Dependency subTree, bool achieved);
  622. void NotifyActiveChanged(Dependency dependency, bool active);
  623. void NotifyRewardClaimed(Dependency dependency, bool claimed);
  624. }
  625. private class CommunicatorInstance : DependencyTree.Communicator
  626. {
  627. public CommunicatorInstance(DependencyTree tree)
  628. {
  629. if (tree._communicator != null)
  630. {
  631. throw new InvalidOperationException("The specified tree already has a communicator.");
  632. }
  633. this._tree = tree;
  634. }
  635. public DependencyTree Tree
  636. {
  637. get
  638. {
  639. return this._tree;
  640. }
  641. }
  642. public void NotifySubTreeAttached(Dependency parent, Dependency child)
  643. {
  644. this._tree.OnSubTreeAttached(parent, child);
  645. }
  646. public void NotifySubTreeDetached(Dependency parent, Dependency child)
  647. {
  648. this._tree.OnSubTreeDetached(parent, child);
  649. }
  650. public void NotifySubTreeAchievedChanged(Dependency subTree, bool achieved)
  651. {
  652. this._tree.OnSubTreeAchievedChanged(subTree, achieved);
  653. }
  654. public void NotifyActiveChanged(Dependency dependency, bool active)
  655. {
  656. this._tree.OnDependencyActiveChanged(dependency, active);
  657. }
  658. public void NotifyRewardClaimed(Dependency dependency, bool claimed)
  659. {
  660. this._tree.OnDependencyRewardClaimed(dependency, claimed);
  661. }
  662. private DependencyTree _tree;
  663. }
  664. }
  665. }