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.

553 lines
19KB

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