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.

361 lines
13KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Svelto.DataStructures;
  5. using UnityEngine;
  6. using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>;
  7. using Svelto.ECS.NodeSchedulers;
  8. using Svelto.ECS.Internal;
  9. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  10. using Svelto.ECS.Profiler;
  11. #endif
  12. #if NETFX_CORE
  13. using System.Reflection;
  14. #endif
  15. namespace Svelto.ECS
  16. {
  17. public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
  18. {
  19. public EnginesRoot(NodeSubmissionScheduler nodeScheduler)
  20. {
  21. _nodeEngines = new Dictionary<Type, FasterList<INodeEngine>>();
  22. _engineRootWeakReference = new WeakReference(this);
  23. _otherEnginesReferences = new FasterList<IEngine>();
  24. _nodesDB = new Dictionary<Type, FasterList<INode>>();
  25. _nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();
  26. _nodesToAdd = new FasterList<INode>();
  27. _groupNodesToAdd = new FasterList<INode>();
  28. _nodesDBgroups = new Dictionary<Type, FasterList<INode>>();
  29. _scheduler = nodeScheduler;
  30. _scheduler.Schedule(SubmitNodes);
  31. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  32. GameObject debugEngineObject = new GameObject("Engine Debugger");
  33. debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
  34. #endif
  35. }
  36. void SubmitNodes()
  37. {
  38. int groupNodesCount;
  39. int nodesCount;
  40. bool newNodesHaveBeenAddedWhileIterating;
  41. int startNodes = 0;
  42. int startGroupNodes = 0;
  43. int numberOfReenteringLoops = 0;
  44. do
  45. {
  46. groupNodesCount = _groupNodesToAdd.Count;
  47. nodesCount = _nodesToAdd.Count;
  48. for (int i = startNodes; i < nodesCount; i++)
  49. {
  50. var node = _nodesToAdd[i];
  51. AddNodeToTheDB(node, node.GetType());
  52. }
  53. for (int i = startGroupNodes; i < groupNodesCount; i++)
  54. {
  55. var node = _groupNodesToAdd[i];
  56. AddNodeToGroupDB(node, node.GetType());
  57. }
  58. for (int i = startNodes; i < nodesCount; i++)
  59. {
  60. var node = _nodesToAdd[i];
  61. AddNodeToTheSuitableEngines(node, node.GetType());
  62. }
  63. for (int i = startGroupNodes; i < groupNodesCount; i++)
  64. {
  65. var node = _groupNodesToAdd[i];
  66. AddNodeToTheSuitableEngines(node, node.GetType());
  67. }
  68. newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount;
  69. startNodes = nodesCount;
  70. startGroupNodes = groupNodesCount;
  71. if (numberOfReenteringLoops > 5)
  72. throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");
  73. numberOfReenteringLoops++;
  74. } while (newNodesHaveBeenAddedWhileIterating);
  75. _nodesToAdd.Clear();
  76. _groupNodesToAdd.Clear();
  77. }
  78. public void AddEngine<T>(INodeEngine<T> engine) where T:class, INode
  79. {
  80. AddEngine(new NodeEngineWrapper<T>(engine));
  81. if (engine is IQueryableNodeEngine)
  82. (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups);
  83. }
  84. public void AddEngine<T, U>(INodeEngine<T, U> engine) where T:class, INode where U:class, INode
  85. {
  86. AddEngine(new NodeEngineWrapper<T, U>(engine));
  87. AddEngine((INodeEngine<U>)(engine));
  88. }
  89. public void AddEngine<T, U, V>(INodeEngine<T, U, V> engine) where T:class, INode
  90. where U:class, INode
  91. where V:class, INode
  92. {
  93. AddEngine(new NodeEngineWrapper<T, U, V>(engine));
  94. AddEngine((INodeEngine<U, V>)(engine));
  95. }
  96. public void AddEngine(IEngine engine)
  97. {
  98. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  99. EngineProfiler.AddEngine(engine);
  100. #endif
  101. if (engine is IQueryableNodeEngine)
  102. (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups);
  103. if (engine is INodesEngine)
  104. {
  105. var nodesEngine = engine as INodesEngine;
  106. AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);
  107. }
  108. else
  109. {
  110. var engineType = engine.GetType();
  111. #if !NETFX_CORE
  112. var baseType = engineType.BaseType;
  113. if (baseType.IsGenericType
  114. #else
  115. var baseType = engineType.GetTypeInfo().BaseType;
  116. if (baseType.IsConstructedGenericType
  117. #endif
  118. && baseType.GetGenericTypeDefinition() == typeof(SingleNodeEngine<>))
  119. {
  120. AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines);
  121. }
  122. else
  123. {
  124. bool found = false;
  125. for (int i = 0, maxLength = engineType.GetInterfaces().Length; i < maxLength; i++)
  126. {
  127. var type = engineType.GetInterfaces()[i];
  128. if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(INodeEngine<>))
  129. {
  130. AddEngine(engine as INodeEngine, type.GetGenericArguments(), _nodeEngines);
  131. found = true;
  132. }
  133. }
  134. if (found == false)
  135. _otherEnginesReferences.Add(engine);
  136. }
  137. }
  138. if (engine is ICallBackOnAddEngine)
  139. (engine as ICallBackOnAddEngine).Ready();
  140. }
  141. public void BuildEntity(int ID, EntityDescriptor ed)
  142. {
  143. var entityNodes = ed.BuildNodes(ID, (nodes) =>
  144. {
  145. if (_engineRootWeakReference.IsValid == true)
  146. InternalRemove(nodes);
  147. });
  148. _nodesToAdd.AddRange(entityNodes);
  149. }
  150. /// <summary>
  151. /// An entity group is a meta entity. It's a way to create a set of entitites that
  152. /// are not easily queriable otherwise. For example you may group existing entities
  153. /// by size and type and then use the groupID to retrieve a single node that is shared
  154. /// among the single entities of the same type and size. This willwd prevent the scenario
  155. /// where the coder is forced to parse all the entities to find the ones of the same
  156. /// size and type. Since the entity group is managed through the shared node, the same
  157. /// shared node must be found on the single entities of the same type and size.
  158. /// The shared node is then used by engines that are meant to manage a group of entities
  159. /// through a single node. The same engine can manage several groups of entitites.
  160. /// </summary>
  161. /// <param name="groupID"></param>
  162. /// <param name="ed"></param>
  163. public void BuildEntityGroup(int groupID, EntityDescriptor ed)
  164. {
  165. var entityNodes = ed.BuildNodes(groupID, (nodes) =>
  166. {
  167. if (_engineRootWeakReference.IsValid == true)
  168. InternalGroupRemove(nodes);
  169. });
  170. _groupNodesToAdd.AddRange(entityNodes);
  171. }
  172. static void AddEngine(INodeEngine engine, Type[] types, Dictionary<Type, FasterList<INodeEngine>> engines)
  173. {
  174. for (int i = 0; i < types.Length; i++)
  175. {
  176. FasterList<INodeEngine> list;
  177. var type = types[i];
  178. if (engines.TryGetValue(type, out list) == false)
  179. {
  180. list = new FasterList<INodeEngine>();
  181. engines.Add(type, list);
  182. }
  183. list.Add(engine);
  184. }
  185. }
  186. void AddNodeToGroupDB(INode node, Type nodeType)
  187. {
  188. FasterList<INode> nodes;
  189. if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false)
  190. nodes = _nodesDBgroups[nodeType] = new FasterList<INode>();
  191. nodes.Add(node);
  192. AddNodeToNodesDictionary(node, nodeType);
  193. }
  194. void AddNodeToTheDB(INode node, Type nodeType)
  195. {
  196. FasterList<INode> nodes;
  197. if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
  198. nodes = _nodesDB[nodeType] = new FasterList<INode>();
  199. nodes.Add(node);
  200. AddNodeToNodesDictionary(node, nodeType);
  201. }
  202. void AddNodeToNodesDictionary(INode node, Type nodeType)
  203. {
  204. if (node is NodeWithID)
  205. {
  206. Dictionary<int, INode> nodesDic;
  207. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
  208. nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();
  209. nodesDic[(node as NodeWithID).ID] = node;
  210. }
  211. }
  212. void AddNodeToTheSuitableEngines(INode node, Type nodeType)
  213. {
  214. FasterList<INodeEngine> enginesForNode;
  215. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  216. {
  217. for (int j = 0; j < enginesForNode.Count; j++)
  218. {
  219. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  220. EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j], node);
  221. #else
  222. enginesForNode[j].Add(node);
  223. #endif
  224. }
  225. }
  226. }
  227. void RemoveNodesFromDB(Dictionary<Type, FasterList<INode>> DB, FasterReadOnlyList<INode> nodes)
  228. {
  229. for (int i = 0; i < nodes.Count; i++)
  230. {
  231. FasterList<INode> nodesInDB;
  232. var node = nodes[i];
  233. var nodeType = node.GetType();
  234. if (DB.TryGetValue(nodeType, out nodesInDB) == true)
  235. nodesInDB.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?
  236. if (node is NodeWithID)
  237. {
  238. Dictionary<int, INode> nodesDic;
  239. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
  240. nodesDic.Remove((node as NodeWithID).ID);
  241. }
  242. }
  243. }
  244. void RemoveNodesFromEngines(FasterReadOnlyList<INode> nodes)
  245. {
  246. for (int i = 0; i < nodes.Count; i++)
  247. {
  248. FasterList<INodeEngine> enginesForNode;
  249. var node = nodes[i];
  250. if (_nodeEngines.TryGetValue(node.GetType(), out enginesForNode))
  251. {
  252. for (int j = 0; j < enginesForNode.Count; j++)
  253. {
  254. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  255. EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node);
  256. #else
  257. enginesForNode[j].Remove(node);
  258. #endif
  259. }
  260. }
  261. }
  262. }
  263. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  264. static void AddNodeToEngine(INodeEngine engine, INode node)
  265. {
  266. engine.Add(node);
  267. }
  268. static void RemoveNodeFromEngine(INodeEngine engine, INode node)
  269. {
  270. engine.Remove(node);
  271. }
  272. #endif
  273. void InternalRemove(FasterReadOnlyList<INode> nodes)
  274. {
  275. RemoveNodesFromEngines(nodes);
  276. RemoveNodesFromDB(_nodesDB, nodes);
  277. }
  278. void InternalGroupRemove(FasterReadOnlyList<INode> nodes)
  279. {
  280. RemoveNodesFromEngines(nodes);
  281. RemoveNodesFromDB(_nodesDBgroups, nodes);
  282. }
  283. Dictionary<Type, FasterList<INodeEngine>> _nodeEngines;
  284. FasterList<IEngine> _otherEnginesReferences;
  285. Dictionary<Type, FasterList<INode>> _nodesDB;
  286. Dictionary<Type, FasterList<INode>> _nodesDBgroups;
  287. Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
  288. FasterList<INode> _nodesToAdd;
  289. FasterList<INode> _groupNodesToAdd;
  290. WeakReference _engineRootWeakReference;
  291. NodeSubmissionScheduler _scheduler;
  292. }
  293. }