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.

560 lines
18KB

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