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.

551 lines
19KB

  1. using System;
  2. using System.Collections.Generic;
  3. using Svelto.DataStructures;
  4. using Svelto.ECS.Internal;
  5. using Svelto.ECS.NodeSchedulers;
  6. using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>;
  7. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  8. using Svelto.ECS.Profiler;
  9. #endif
  10. #if NETFX_CORE
  11. using System.Reflection;
  12. #endif
  13. namespace Svelto.ECS
  14. {
  15. public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
  16. {
  17. public EnginesRoot(NodeSubmissionScheduler nodeScheduler)
  18. {
  19. _nodeEngines = new Dictionary<Type, FasterList<IEngine>>();
  20. _activableEngines = new Dictionary<Type, FasterList<IEngine>>();
  21. _otherEngines = new FasterList<IEngine>();
  22. _engineRootWeakReference = new WeakReference(this);
  23. _nodesDB = new Dictionary<Type, FasterList<INode>>();
  24. _nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();
  25. _nodesToAdd = new FasterList<INode>();
  26. _metaNodesToAdd = new FasterList<INode>();
  27. _metaNodesDB = new Dictionary<Type, FasterList<INode>>();
  28. _sharedStructNodeLists = new SharedStructNodeLists();
  29. _sharedGroupedStructNodeLists = new SharedGroupedStructNodesLists();
  30. _internalRemove = InternalRemove;
  31. _internalDisable = InternalDisable;
  32. _internalEnable = InternalEnable;
  33. _internalMetaRemove = InternalMetaRemove;
  34. _scheduler = nodeScheduler;
  35. _scheduler.Schedule(SubmitNodes);
  36. _structNodeEngineType = typeof(IStructNodeEngine<>);
  37. _groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>);
  38. _activableNodeEngineType = typeof(IActivableNodeEngine<>);
  39. _implementedInterfaceTypes = new Dictionary<Type, Type[]>();
  40. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  41. GameObject debugEngineObject = new GameObject("Engine Debugger");
  42. debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
  43. #endif
  44. }
  45. void SubmitNodes()
  46. {
  47. int metaNodesCount = _metaNodesToAdd.Count;
  48. int nodesCount = _nodesToAdd.Count;
  49. if (metaNodesCount + nodesCount == 0) return;
  50. bool newNodesHaveBeenAddedWhileIterating;
  51. int startNodes = 0;
  52. int startMetaNodes = 0;
  53. int numberOfReenteringLoops = 0;
  54. do
  55. {
  56. var nodesToAdd = _nodesToAdd.ToArrayFast();
  57. for (int i = startNodes; i < nodesCount; i++)
  58. {
  59. var node = nodesToAdd[i];
  60. var nodeType = node.GetType();
  61. AddNodeToTheDB(node, nodeType);
  62. var nodeWithId = node as INodeWithID;
  63. if (nodeWithId != null)
  64. AddNodeToNodesDictionary(nodeWithId, nodeType);
  65. }
  66. var metaNodesToAdd = _metaNodesToAdd.ToArrayFast();
  67. for (int i = startMetaNodes; i < metaNodesCount; i++)
  68. {
  69. var node = metaNodesToAdd[i];
  70. var nodeType = node.GetType();
  71. AddNodeToMetaDB(node, nodeType);
  72. var nodeWithId = node as INodeWithID;
  73. if (nodeWithId != null)
  74. AddNodeToNodesDictionary(nodeWithId, nodeType);
  75. }
  76. for (int i = startNodes; i < nodesCount; i++)
  77. {
  78. var node = nodesToAdd[i];
  79. AddNodeToTheSuitableEngines(node, node.GetType());
  80. }
  81. for (int i = startMetaNodes; i < metaNodesCount; i++)
  82. {
  83. var node = metaNodesToAdd[i];
  84. AddNodeToTheSuitableEngines(node, node.GetType());
  85. }
  86. newNodesHaveBeenAddedWhileIterating =
  87. _metaNodesToAdd.Count > metaNodesCount ||
  88. _nodesToAdd.Count > nodesCount;
  89. startNodes = nodesCount;
  90. startMetaNodes = metaNodesCount;
  91. if (numberOfReenteringLoops > 5)
  92. throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");
  93. numberOfReenteringLoops++;
  94. metaNodesCount = _metaNodesToAdd.Count;
  95. nodesCount = _nodesToAdd.Count;
  96. } while (newNodesHaveBeenAddedWhileIterating);
  97. _nodesToAdd.Clear();
  98. _metaNodesToAdd.Clear();
  99. }
  100. public void AddEngine(IEngine engine)
  101. {
  102. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  103. EngineProfiler.AddEngine(engine);
  104. #endif
  105. var queryableNodeEngine = engine as IQueryableNodeEngine;
  106. if (queryableNodeEngine != null)
  107. queryableNodeEngine.nodesDB =
  108. new EngineNodeDB(_nodesDB, _nodesDBdic, _metaNodesDB);
  109. var engineType = engine.GetType();
  110. var implementedInterfaces = engineType.GetInterfaces();
  111. CollectImplementedInterfaces(implementedInterfaces);
  112. var engineAdded = CheckGenericEngines(engine);
  113. if (CheckLegacyNodesEngine(engine, ref engineAdded) == false)
  114. CheckNodesEngine(engine, engineType, ref engineAdded);
  115. if (engineAdded == false)
  116. _otherEngines.Add(engine);
  117. var callBackOnAddEngine = engine as ICallBackOnAddEngine;
  118. if (callBackOnAddEngine != null)
  119. callBackOnAddEngine.Ready();
  120. }
  121. void CollectImplementedInterfaces(Type[] implementedInterfaces)
  122. {
  123. _implementedInterfaceTypes.Clear();
  124. for (int index = 0; index < implementedInterfaces.Length; index++)
  125. {
  126. var interfaceType = implementedInterfaces[index];
  127. #if !NETFX_CORE
  128. if (false == interfaceType.IsGenericType)
  129. #else
  130. if (false == interfaceType.IsConstructedGenericType)
  131. #endif
  132. {
  133. continue;
  134. }
  135. var genericTypeDefinition = interfaceType.GetGenericTypeDefinition();
  136. _implementedInterfaceTypes[genericTypeDefinition] = interfaceType.GetGenericArguments();
  137. }
  138. }
  139. bool CheckGenericEngines(IEngine engine)
  140. {
  141. if (_implementedInterfaceTypes.Count == 0) return false;
  142. bool engineAdded = false;
  143. if (_implementedInterfaceTypes.ContainsKey(_structNodeEngineType))
  144. {
  145. ((IStructNodeEngine)engine).CreateStructNodes
  146. (_sharedStructNodeLists);
  147. }
  148. if (_implementedInterfaceTypes.ContainsKey(_groupedStructNodesEngineType))
  149. {
  150. ((IGroupedStructNodesEngine)engine).CreateStructNodes
  151. (_sharedGroupedStructNodeLists);
  152. }
  153. Type[] arguments;
  154. if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType,
  155. out arguments))
  156. {
  157. AddEngine(engine, arguments, _activableEngines);
  158. engineAdded = true;
  159. }
  160. return engineAdded;
  161. }
  162. bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded)
  163. {
  164. var nodesEngine = engine as INodesEngine;
  165. if (nodesEngine != null)
  166. {
  167. AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);
  168. engineAdded = true;
  169. return true;
  170. }
  171. return false;
  172. }
  173. bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded)
  174. {
  175. #if !NETFX_CORE
  176. var baseType = engineType.BaseType;
  177. if (baseType.IsGenericType
  178. #else
  179. var baseType = engineType.GetTypeInfo().BaseType;
  180. if (baseType.IsConstructedGenericType
  181. #endif
  182. && engine is INodeEngine)
  183. {
  184. AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines);
  185. engineAdded = true;
  186. return true;
  187. }
  188. return false;
  189. }
  190. public void BuildEntity(int ID, EntityDescriptor ed)
  191. {
  192. var entityNodes = ed.BuildNodes(ID,
  193. _internalRemove,
  194. _internalEnable,
  195. _internalDisable
  196. );
  197. _nodesToAdd.AddRange(entityNodes);
  198. }
  199. /// <summary>
  200. /// A meta entity is a way to manage a set of entitites that are not easily
  201. /// queriable otherwise. For example you may want to group existing entities
  202. /// by size and type and then use the meta entity node to manage the data
  203. /// shared among the single entities of the same type and size. This will
  204. /// prevent the scenario where the coder is forced to parse all the entities to
  205. /// find the ones of the same size and type.
  206. /// Since the entities are managed through the shared node, the same
  207. /// shared node must be found on the single entities of the same type and size.
  208. /// The shared node of the meta entity is then used by engines that are meant
  209. /// to manage a group of entities through a single node.
  210. /// The same engine can manage several meta entities nodes too.
  211. /// The Engine manages the logic of the Meta Node data and other engines
  212. /// can read back this data through the normal entity as the shared node
  213. /// will be present in their descriptor too.
  214. /// </summary>
  215. /// <param name="metaEntityID"></param>
  216. /// <param name="ed"></param>
  217. public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed)
  218. {
  219. var entityNodes = ed.BuildNodes(metaEntityID,
  220. _internalMetaRemove,
  221. _internalEnable,
  222. _internalDisable
  223. );
  224. _metaNodesToAdd.AddRange(entityNodes);
  225. }
  226. /// <summary>
  227. /// Using this function is like building a normal entity, but the nodes
  228. /// are grouped by groupID to be better processed inside engines and
  229. /// improve cache locality. Only IGroupStructNodeWithID nodes are grouped
  230. /// other nodes are managed as usual.
  231. /// </summary>
  232. /// <param name="entityID"></param>
  233. /// <param name="groupID"></param>
  234. /// <param name="ed"></param>
  235. public void BuildEntityInGroup(int entityID, int groupID,
  236. EntityDescriptor ed)
  237. {
  238. var entityNodes = ed.BuildNodes(entityID,
  239. _internalRemove,
  240. _internalEnable,
  241. _internalDisable
  242. );
  243. _nodesToAdd.AddRange(entityNodes);
  244. for (int i = 0; i < entityNodes.Count; i++)
  245. {
  246. var groupNode = entityNodes[i] as IGroupedStructNodeWithID;
  247. if (groupNode != null)
  248. groupNode.groupID = groupID;
  249. }
  250. }
  251. static void AddEngine(IEngine engine, Type[] types,
  252. Dictionary<Type, FasterList<IEngine>> engines)
  253. {
  254. for (int i = 0; i < types.Length; i++)
  255. {
  256. FasterList<IEngine> list;
  257. var type = types[i];
  258. if (engines.TryGetValue(type, out list) == false)
  259. {
  260. list = new FasterList<IEngine>();
  261. engines.Add(type, list);
  262. }
  263. list.Add(engine);
  264. }
  265. }
  266. void AddNodeToMetaDB(INode node, Type nodeType)
  267. {
  268. FasterList<INode> nodes;
  269. if (_metaNodesDB.TryGetValue(nodeType, out nodes) == false)
  270. nodes = _metaNodesDB[nodeType] = new FasterList<INode>();
  271. nodes.Add(node);
  272. }
  273. void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode
  274. {
  275. FasterList<INode> nodes;
  276. if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
  277. nodes = _nodesDB[nodeType] = new FasterList<INode>();
  278. nodes.Add(node);
  279. }
  280. void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
  281. {
  282. Dictionary<int, INode> nodesDic;
  283. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
  284. nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();
  285. nodesDic.Add(node.ID, node);
  286. }
  287. void AddNodeToTheSuitableEngines(INode node, Type nodeType)
  288. {
  289. FasterList<IEngine> enginesForNode;
  290. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  291. {
  292. for (int j = 0; j < enginesForNode.Count; j++)
  293. {
  294. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  295. EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node);
  296. #else
  297. (enginesForNode[j] as INodeEngine).Add(node);
  298. #endif
  299. }
  300. }
  301. }
  302. void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
  303. {
  304. FasterList<INode> nodes;
  305. if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
  306. nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
  307. }
  308. void RemoveNodeFromMetaDB<T>(T node, Type nodeType) where T : INode
  309. {
  310. FasterList<INode> nodes;
  311. if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true)
  312. nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
  313. }
  314. void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
  315. {
  316. Dictionary<int, INode> nodesDic;
  317. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
  318. nodesDic.Remove(node.ID);
  319. }
  320. void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
  321. {
  322. FasterList<IEngine> enginesForNode;
  323. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  324. {
  325. for (int j = 0; j < enginesForNode.Count; j++)
  326. {
  327. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  328. EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node);
  329. #else
  330. (enginesForNode[j] as INodeEngine).Remove(node);
  331. #endif
  332. }
  333. }
  334. }
  335. void DisableNodeFromEngines(INode node, Type nodeType)
  336. {
  337. FasterList<IEngine> enginesForNode;
  338. if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
  339. {
  340. for (int j = 0; j < enginesForNode.Count; j++)
  341. {
  342. (enginesForNode[j] as IActivableNodeEngine).Disable(node);
  343. }
  344. }
  345. }
  346. void EnableNodeFromEngines(INode node, Type nodeType)
  347. {
  348. FasterList<IEngine> enginesForNode;
  349. if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
  350. {
  351. for (int j = 0; j < enginesForNode.Count; j++)
  352. {
  353. (enginesForNode[j] as IActivableNodeEngine).Enable(node);
  354. }
  355. }
  356. }
  357. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  358. void AddNodeToEngine(IEngine engine, INode node)
  359. {
  360. (engine as INodeEngine).Add(node);
  361. }
  362. void RemoveNodeFromEngine(IEngine engine, INode node)
  363. {
  364. (engine as INodeEngine).Remove(node);
  365. }
  366. #endif
  367. void InternalDisable(FasterList<INode> nodes)
  368. {
  369. if (_engineRootWeakReference.IsValid == false)
  370. return;
  371. for (int i = 0; i < nodes.Count; i++)
  372. {
  373. var node = nodes[i];
  374. Type nodeType = node.GetType();
  375. DisableNodeFromEngines(node, nodeType);
  376. }
  377. }
  378. void InternalEnable(FasterList<INode> nodes)
  379. {
  380. if (_engineRootWeakReference.IsValid == false)
  381. return;
  382. for (int i = 0; i < nodes.Count; i++)
  383. {
  384. var node = nodes[i];
  385. Type nodeType = node.GetType();
  386. EnableNodeFromEngines(node, nodeType);
  387. }
  388. }
  389. void InternalRemove(FasterList<INode> nodes)
  390. {
  391. if (_engineRootWeakReference.IsValid == false)
  392. return;
  393. for (int i = 0; i < nodes.Count; i++)
  394. {
  395. var node = nodes[i];
  396. Type nodeType = node.GetType();
  397. RemoveNodeFromEngines(node, nodeType);
  398. RemoveNodeFromTheDB(node, node.GetType());
  399. var nodeWithId = node as INodeWithID;
  400. if (nodeWithId != null)
  401. RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
  402. }
  403. }
  404. void InternalMetaRemove(FasterList<INode> nodes)
  405. {
  406. for (int i = 0; i < nodes.Count; i++)
  407. {
  408. var node = nodes[i];
  409. Type nodeType = node.GetType();
  410. RemoveNodeFromEngines(node, nodeType);
  411. RemoveNodeFromMetaDB(node, nodeType);
  412. var nodeWithId = node as INodeWithID;
  413. if (nodeWithId != null)
  414. RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
  415. }
  416. }
  417. readonly Dictionary<Type, FasterList<IEngine>> _nodeEngines;
  418. readonly Dictionary<Type, FasterList<IEngine>> _activableEngines;
  419. readonly FasterList<IEngine> _otherEngines;
  420. readonly Dictionary<Type, FasterList<INode>> _nodesDB;
  421. readonly Dictionary<Type, FasterList<INode>> _metaNodesDB;
  422. readonly Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
  423. readonly FasterList<INode> _nodesToAdd;
  424. readonly FasterList<INode> _metaNodesToAdd;
  425. readonly WeakReference _engineRootWeakReference;
  426. readonly SharedStructNodeLists _sharedStructNodeLists;
  427. readonly SharedGroupedStructNodesLists _sharedGroupedStructNodeLists;
  428. readonly NodeSubmissionScheduler _scheduler;
  429. readonly Action<FasterList<INode>> _internalRemove;
  430. readonly Action<FasterList<INode>> _internalEnable;
  431. readonly Action<FasterList<INode>> _internalDisable;
  432. readonly Action<FasterList<INode>> _internalMetaRemove;
  433. readonly Type _structNodeEngineType;
  434. readonly Type _groupedStructNodesEngineType;
  435. readonly Type _activableNodeEngineType;
  436. readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
  437. }
  438. }