Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
11KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Svelto.DataStructures;
  5. using UnityEngine;
  6. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  7. using Svelto.ECS.Profiler;
  8. #endif
  9. namespace Svelto.ECS
  10. {
  11. class Scheduler : MonoBehaviour
  12. {
  13. IEnumerator Start()
  14. {
  15. while (true)
  16. {
  17. yield return new WaitForEndOfFrame();
  18. OnTick();
  19. }
  20. }
  21. internal Action OnTick;
  22. }
  23. public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
  24. {
  25. public EnginesRoot()
  26. {
  27. _nodeEngines = new Dictionary<Type, FasterList<INodeEngine<INode>>>();
  28. _engineRootWeakReference = new WeakReference<EnginesRoot>(this);
  29. _otherEnginesReferences = new FasterList<IEngine>();
  30. _nodesDB = new Dictionary<Type, FasterList<INode>>();
  31. _nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();
  32. _nodesToAdd = new FasterList<INode>();
  33. _groupNodesToAdd = new FasterList<INode>();
  34. _nodesDBgroups = new Dictionary<Type, FasterList<INode>>();
  35. GameObject go = new GameObject("ECSScheduler");
  36. go.AddComponent<Scheduler>().OnTick += SubmitNodes;
  37. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  38. var debugEngineObject = GameObject.Find("Svelto.ECS.Profiler");
  39. if (debugEngineObject == null)
  40. {
  41. debugEngineObject = new GameObject("Svelto.ECS.Profiler");
  42. debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
  43. UnityEngine.Object.DontDestroyOnLoad(debugEngineObject);
  44. }
  45. #endif
  46. }
  47. void SubmitNodes()
  48. {
  49. int groupNodesCount;
  50. int nodesCount;
  51. bool newNodesHaveBeenAddedWhileIterating;
  52. int startNodes = 0;
  53. int startGroupNodes = 0;
  54. int numberOfReenteringLoops = 0;
  55. do
  56. {
  57. groupNodesCount = _groupNodesToAdd.Count;
  58. nodesCount = _nodesToAdd.Count;
  59. for (int i = startNodes; i < nodesCount; i++)
  60. {
  61. var node = _nodesToAdd[i];
  62. AddNodeToTheDB(node, node.GetType());
  63. }
  64. for (int i = startGroupNodes; i < groupNodesCount; i++)
  65. {
  66. var node = _groupNodesToAdd[i];
  67. AddNodeToGroupDB(node, node.GetType());
  68. }
  69. for (int i = startNodes; i < nodesCount; i++)
  70. {
  71. var node = _nodesToAdd[i];
  72. AddNodeToTheSuitableEngines(node, node.GetType());
  73. }
  74. for (int i = startGroupNodes; i < groupNodesCount; i++)
  75. {
  76. var node = _groupNodesToAdd[i];
  77. AddNodeToTheSuitableEngines(node, node.GetType());
  78. }
  79. newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount;
  80. startNodes = nodesCount;
  81. startGroupNodes = groupNodesCount;
  82. if (numberOfReenteringLoops > 5)
  83. throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");
  84. numberOfReenteringLoops++;
  85. } while (newNodesHaveBeenAddedWhileIterating);
  86. _nodesToAdd.Clear();
  87. _groupNodesToAdd.Clear();
  88. }
  89. public void AddEngine(IEngine engine)
  90. {
  91. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  92. EngineProfiler.AddEngine(engine);
  93. #endif
  94. if (engine is IQueryableNodeEngine)
  95. (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups);
  96. if (engine is INodesEngine)
  97. {
  98. var nodesEngine = engine as INodesEngine;
  99. AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);
  100. return;
  101. }
  102. var baseType = engine.GetType().BaseType;
  103. if (baseType.IsGenericType)
  104. {
  105. var genericType = baseType.GetGenericTypeDefinition();
  106. if (genericType == typeof(SingleNodeEngine<>))
  107. {
  108. AddEngine(engine as INodeEngine<INode>, baseType.GetGenericArguments(), _nodeEngines);
  109. return;
  110. }
  111. }
  112. _otherEnginesReferences.Add(engine);
  113. }
  114. public void BuildEntity(int ID, EntityDescriptor ed)
  115. {
  116. var entityNodes = ed.BuildNodes(ID, (node) =>
  117. {
  118. if (_engineRootWeakReference.IsValid == true)
  119. InternalRemove(node);
  120. });
  121. _nodesToAdd.AddRange(entityNodes);
  122. }
  123. public void BuildEntityGroup(int groupID, EntityDescriptor ed)
  124. {
  125. var entityNodes = ed.BuildNodes(groupID, (node) =>
  126. {
  127. if (_engineRootWeakReference.IsValid == true)
  128. InternalGroupRemove(node);
  129. });
  130. _groupNodesToAdd.AddRange(entityNodes);
  131. }
  132. static void AddEngine<T>(T engine, Type[] types, Dictionary<Type, FasterList<INodeEngine<INode>>> engines) where T : INodeEngine<INode>
  133. {
  134. for (int i = 0; i < types.Length; i++)
  135. {
  136. FasterList<INodeEngine<INode>> list;
  137. var type = types[i];
  138. if (engines.TryGetValue(type, out list) == false)
  139. {
  140. list = new FasterList<INodeEngine<INode>>();
  141. engines.Add(type, list);
  142. }
  143. list.Add(engine);
  144. }
  145. }
  146. void AddNodeToGroupDB(INode node, Type nodeType)
  147. {
  148. FasterList<INode> nodes;
  149. if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false)
  150. nodes = _nodesDBgroups[nodeType] = new FasterList<INode>();
  151. nodes.Add(node);
  152. AddNodeToNodesDictionary(node, nodeType);
  153. }
  154. void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode
  155. {
  156. FasterList<INode> nodes;
  157. if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
  158. nodes = _nodesDB[nodeType] = new FasterList<INode>();
  159. nodes.Add(node);
  160. AddNodeToNodesDictionary(node, nodeType);
  161. }
  162. void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INode
  163. {
  164. if (node is NodeWithID)
  165. {
  166. Dictionary<int, INode> nodesDic;
  167. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
  168. nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();
  169. nodesDic[(node as NodeWithID).ID] = node;
  170. }
  171. }
  172. void AddNodeToTheSuitableEngines<T>(T node, Type nodeType) where T : INode
  173. {
  174. FasterList<INodeEngine<INode>> enginesForNode;
  175. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  176. {
  177. for (int j = 0; j < enginesForNode.Count; j++)
  178. {
  179. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  180. EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j], node);
  181. #else
  182. enginesForNode[j].Add(node);
  183. #endif
  184. }
  185. }
  186. }
  187. void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
  188. {
  189. FasterList<INode> nodes;
  190. if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
  191. nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?
  192. RemoveNodeFromNodesDictionary(node, nodeType);
  193. }
  194. void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INode
  195. {
  196. if (node is NodeWithID)
  197. {
  198. Dictionary<int, INode> nodesDic;
  199. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
  200. nodesDic.Remove((node as NodeWithID).ID);
  201. }
  202. }
  203. void RemoveNodeFromGroupDB<T>(T node, Type nodeType) where T : INode
  204. {
  205. FasterList<INode> nodes;
  206. if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == true)
  207. nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?
  208. RemoveNodeFromNodesDictionary(node, nodeType);
  209. }
  210. void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
  211. {
  212. FasterList<INodeEngine<INode>> enginesForNode;
  213. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  214. {
  215. for (int j = 0; j < enginesForNode.Count; j++)
  216. {
  217. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  218. EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node);
  219. #else
  220. enginesForNode[j].Remove(node);
  221. #endif
  222. }
  223. }
  224. }
  225. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  226. void AddNodeToEngine(INodeEngine<INode> engine, INode node)
  227. {
  228. engine.Add(node);
  229. }
  230. void RemoveNodeFromEngine(INodeEngine<INode> engine, INode node)
  231. {
  232. engine.Remove(node);
  233. }
  234. #endif
  235. void InternalRemove<T>(T node) where T : INode
  236. {
  237. Type nodeType = node.GetType();
  238. RemoveNodeFromEngines(node, nodeType);
  239. RemoveNodeFromTheDB(node, node.GetType());
  240. }
  241. void InternalGroupRemove<T>(T node) where T : INode
  242. {
  243. Type nodeType = node.GetType();
  244. RemoveNodeFromEngines(node, nodeType);
  245. RemoveNodeFromGroupDB(node, node.GetType());
  246. }
  247. Dictionary<Type, FasterList<INodeEngine<INode>>> _nodeEngines;
  248. FasterList<IEngine> _otherEnginesReferences;
  249. Dictionary<Type, FasterList<INode>> _nodesDB;
  250. Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
  251. Dictionary<Type, FasterList<INode>> _nodesDBgroups;
  252. FasterList<INode> _nodesToAdd;
  253. FasterList<INode> _groupNodesToAdd;
  254. WeakReference<EnginesRoot> _engineRootWeakReference;
  255. //integrated pooling system
  256. //add debug panel like Entitas has
  257. //GCHandle should be used to reduce the number of strong references
  258. //datastructure could be thread safe
  259. //future enhancements:
  260. }
  261. }