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.

491 lines
18KB

  1. using System;
  2. using System.Collections.Generic;
  3. using Svelto.DataStructures;
  4. using Svelto.ECS.Internal;
  5. using Svelto.ECS.NodeSchedulers;
  6. using System.Reflection;
  7. using Svelto.Utilities;
  8. using UnityEngine.UI;
  9. using UnityEngine.XR.WSA.Persistence;
  10. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  11. using Svelto.ECS.Profiler;
  12. #endif
  13. namespace Svelto.ECS.Internal
  14. {
  15. struct BuildNodeCallbackStruct
  16. {
  17. public Action<ITypeSafeList> _internalRemove;
  18. public Action<ITypeSafeList> _internalEnable;
  19. public Action<ITypeSafeList> _internalDisable;
  20. }
  21. }
  22. namespace Svelto.ECS
  23. {
  24. public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
  25. {
  26. public EnginesRoot(NodeSubmissionScheduler nodeScheduler)
  27. {
  28. _nodeEngines = new Dictionary<Type, FasterList<IEngine>>();
  29. _activableEngines = new Dictionary<Type, FasterList<IEngine>>();
  30. _otherEngines = new FasterList<IEngine>();
  31. //_engineRootWeakReference = new DataStructures.WeakReference<EnginesRoot>(this);
  32. _nodesDB = new Dictionary<Type, ITypeSafeList>();
  33. _metaNodesDB = new Dictionary<Type, ITypeSafeList>();
  34. _nodesDBdic = new Dictionary<Type, ITypeSafeDictionary>();
  35. _nodesToAdd = new Dictionary<Type, ITypeSafeList>();
  36. _metaNodesToAdd = new Dictionary<Type, ITypeSafeList>();
  37. _groupedNodesToAdd = new Dictionary<Type, Dictionary<int, ITypeSafeList>>();
  38. _callBackStruct = new BuildNodeCallbackStruct();
  39. /* _callBackStruct._internalRemove = InternalRemove;
  40. _callBackStruct._internalDisable = InternalDisable;
  41. _callBackStruct._internalEnable = InternalEnable;
  42. _callBackStruct._internalMetaRemove = InternalMetaRemove;*/
  43. _scheduler = nodeScheduler;
  44. _scheduler.Schedule(SubmitNodes);
  45. _structNodeEngineType = typeof(IStructNodeEngine<>);
  46. _groupedStructNodesEngineType = typeof(IGroupedStructNodesEngine<>);
  47. _activableNodeEngineType = typeof(IActivableNodeEngine<>);
  48. _implementedInterfaceTypes = new Dictionary<Type, Type[]>();
  49. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  50. GameObject debugEngineObject = new GameObject("Engine Debugger");
  51. debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
  52. #endif
  53. }
  54. public void BuildEntity(int ID, EntityDescriptor ed)
  55. {
  56. ed.BuildNodes(ID, _nodesToAdd, ref _callBackStruct);
  57. }
  58. /// <summary>
  59. /// A meta entity is a way to manage a set of entitites that are not easily
  60. /// queriable otherwise. For example you may want to group existing entities
  61. /// by size and type and then use the meta entity node to manage the data
  62. /// shared among the single entities of the same type and size. This will
  63. /// prevent the scenario where the coder is forced to parse all the entities to
  64. /// find the ones of the same size and type.
  65. /// Since the entities are managed through the shared node, the same
  66. /// shared node must be found on the single entities of the same type and size.
  67. /// The shared node of the meta entity is then used by engines that are meant
  68. /// to manage a group of entities through a single node.
  69. /// The same engine can manage several meta entities nodes too.
  70. /// The Engine manages the logic of the Meta Node data and other engines
  71. /// can read back this data through the normal entity as the shared node
  72. /// will be present in their descriptor too.
  73. /// It's a way to control a group of Entities through a node only.
  74. /// This set of entities can share exactly the same node reference if
  75. /// built through this function. In this way, if you need to set a variable
  76. /// on a group of entities, instead to inject N nodes and iterate over
  77. /// them to set the same value, you can inject just one node, set the value
  78. /// and be sure that the value is shared between entities.
  79. /// </summary>
  80. /// <param name="metaEntityID"></param>
  81. /// <param name="ed"></param>
  82. public void BuildMetaEntity(int metaEntityID, EntityDescriptor ed)
  83. {
  84. ed.BuildNodes(metaEntityID, _metaNodesToAdd, ref _callBackStruct);
  85. }
  86. /// <summary>
  87. /// Using this function is like building a normal entity, but the nodes
  88. /// are grouped by groupID to be better processed inside engines and
  89. /// improve cache locality. Only IGroupStructNodeWithID nodes are grouped
  90. /// other nodes are managed as usual.
  91. /// </summary>
  92. /// <param name="entityID"></param>
  93. /// <param name="groupID"></param>
  94. /// <param name="ed"></param>
  95. public void BuildEntityInGroup(int entityID, int groupID,
  96. EntityDescriptor ed)
  97. {
  98. ed.BuildGroupedNodes(entityID, groupID, _groupedNodesToAdd, ref _callBackStruct);
  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. var type = typeof(IEngine);
  125. for (int index = 0; index < implementedInterfaces.Length; index++)
  126. {
  127. var interfaceType = implementedInterfaces[index];
  128. if (type.IsAssignableFrom(interfaceType) == false)
  129. continue;
  130. if (false == interfaceType.IsGenericTypeEx())
  131. {
  132. continue;
  133. }
  134. var genericTypeDefinition = interfaceType.GetGenericTypeDefinition();
  135. _implementedInterfaceTypes.Add(genericTypeDefinition, interfaceType.GetGenericArguments());
  136. }
  137. }
  138. bool CheckGenericEngines(IEngine engine)
  139. {
  140. if (_implementedInterfaceTypes.Count == 0) return false;
  141. bool engineAdded = false;
  142. Type[] arguments;
  143. if (_implementedInterfaceTypes.TryGetValue(_activableNodeEngineType,
  144. out arguments))
  145. {
  146. AddEngine(engine, arguments, _activableEngines);
  147. engineAdded = true;
  148. }
  149. return engineAdded;
  150. }
  151. bool CheckLegacyNodesEngine(IEngine engine, ref bool engineAdded)
  152. {
  153. var nodesEngine = engine as INodesEngine;
  154. if (nodesEngine != null)
  155. {
  156. AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);
  157. engineAdded = true;
  158. return true;
  159. }
  160. return false;
  161. }
  162. bool CheckNodesEngine(IEngine engine, Type engineType, ref bool engineAdded)
  163. {
  164. var baseType = engineType.GetBaseType();
  165. if (baseType.IsGenericTypeEx()
  166. && engine is INodeEngine)
  167. {
  168. AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines);
  169. engineAdded = true;
  170. return true;
  171. }
  172. return false;
  173. }
  174. static void AddEngine(IEngine engine, Type[] types,
  175. Dictionary<Type, FasterList<IEngine>> engines)
  176. {
  177. for (int i = 0; i < types.Length; i++)
  178. {
  179. FasterList<IEngine> list;
  180. var type = types[i];
  181. if (engines.TryGetValue(type, out list) == false)
  182. {
  183. list = new FasterList<IEngine>();
  184. engines.Add(type, list);
  185. }
  186. list.Add(engine);
  187. }
  188. }
  189. static void AddNodesToTheDBAndSuitableEngines(Dictionary<Type, ITypeSafeList> nodesToAdd,
  190. Dictionary<Type, FasterList<IEngine>> nodeEngines,
  191. Dictionary<Type, ITypeSafeDictionary> nodesDBdic,
  192. Dictionary<Type, ITypeSafeList> nodesDB)
  193. {
  194. foreach (var nodeList in nodesToAdd)
  195. {
  196. ITypeSafeList dbList;
  197. if (nodesDB.TryGetValue(nodeList.Key, out dbList) == false)
  198. dbList = nodesDB[nodeList.Key] = nodeList.Value.Create();
  199. dbList.AddRange(nodeList.Value);
  200. AddNodeToNodesDictionary(nodesDBdic, nodeList.Value, nodeList.Key);
  201. AddNodesToTheSuitableEngines(nodeEngines, nodeList.Value, nodeList.Key);
  202. }
  203. }
  204. static void AddNodeToNodesDictionary(Dictionary<Type, ITypeSafeDictionary> nodesDBdic, ITypeSafeList nodes, Type nodeType)
  205. {
  206. ITypeSafeDictionary nodesDic;
  207. if (nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
  208. nodesDic = nodesDBdic[nodeType] = nodes.CreateIndexedDictionary();
  209. nodes.AddToIndexedDictionary(nodesDic);
  210. }
  211. static void AddNodesToTheSuitableEngines(Dictionary<Type, FasterList<IEngine>> nodeEngines, ITypeSafeList nodes, Type nodeType)
  212. {
  213. FasterList<IEngine> enginesForNode;
  214. if (nodeEngines.TryGetValue(nodeType, out enginesForNode))
  215. {
  216. for (int j = 0; j < enginesForNode.Count; j++)
  217. {
  218. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  219. EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j] as INodeEngine, node);
  220. #else
  221. (enginesForNode[j] as INodeEngine).Add(nodes);
  222. #endif
  223. }
  224. }
  225. }
  226. /*
  227. void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
  228. {
  229. FasterList<INode> nodes;
  230. if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
  231. nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
  232. }
  233. void RemoveNodeFromMetaDB<T>(T node, Type nodeType) where T : INode
  234. {
  235. FasterList<INode> nodes;
  236. if (_metaNodesDB.TryGetValue(nodeType, out nodes) == true)
  237. nodes.UnorderedRemove(node); //should I remove it from the dictionary if length is zero?
  238. }
  239. void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INodeWithID
  240. {
  241. Dictionary<int, INode> nodesDic;
  242. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
  243. nodesDic.Remove(node.ID);
  244. }
  245. void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
  246. {
  247. FasterList<IEngine> enginesForNode;
  248. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  249. {
  250. for (int j = 0; j < enginesForNode.Count; j++)
  251. {
  252. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  253. EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, (enginesForNode[j] as INodeEngine), node);
  254. #else
  255. (enginesForNode[j] as INodeEngine).Remove(node);
  256. #endif
  257. }
  258. }
  259. }
  260. void DisableNodeFromEngines(INode node, Type nodeType)
  261. {
  262. FasterList<IEngine> enginesForNode;
  263. if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
  264. {
  265. for (int j = 0; j < enginesForNode.Count; j++)
  266. {
  267. (enginesForNode[j] as IActivableNodeEngine).Disable(node);
  268. }
  269. }
  270. }
  271. void EnableNodeFromEngines(INode node, Type nodeType)
  272. {
  273. FasterList<IEngine> enginesForNode;
  274. if (_activableEngines.TryGetValue(nodeType, out enginesForNode))
  275. {
  276. for (int j = 0; j < enginesForNode.Count; j++)
  277. {
  278. (enginesForNode[j] as IActivableNodeEngine).Enable(node);
  279. }
  280. }
  281. }
  282. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  283. void AddNodeToEngine(IEngine engine, INode node)
  284. {
  285. (engine as INodeEngine).Add(node);
  286. }
  287. void RemoveNodeFromEngine(IEngine engine, INode node)
  288. {
  289. (engine as INodeEngine).Remove(node);
  290. }
  291. #endif
  292. /*
  293. void InternalDisable(FasterList<INode> nodes)
  294. {
  295. if (_engineRootWeakReference.IsValid == false)
  296. return;
  297. for (int i = 0; i < nodes.Count; i++)
  298. {
  299. var node = nodes[i];
  300. Type nodeType = node.GetType();
  301. DisableNodeFromEngines(node, nodeType);
  302. }
  303. }
  304. void InternalEnable(FasterList<INode> nodes)
  305. {
  306. if (_engineRootWeakReference.IsValid == false)
  307. return;
  308. for (int i = 0; i < nodes.Count; i++)
  309. {
  310. var node = nodes[i];
  311. Type nodeType = node.GetType();
  312. EnableNodeFromEngines(node, nodeType);
  313. }
  314. }
  315. void InternalRemove(IFasterList nodes)
  316. {
  317. if (_engineRootWeakReference.IsValid == false)
  318. return;
  319. for (int i = 0; i < nodes.Count; i++)
  320. {
  321. var node = nodes[i];
  322. Type nodeType = node.GetType();
  323. RemoveNodeFromEngines(node, nodeType);
  324. RemoveNodeFromTheDB(node, node.GetType());
  325. var nodeWithId = node as INodeWithID;
  326. if (nodeWithId != null)
  327. RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
  328. }
  329. }
  330. void InternalMetaRemove(FasterList<INode> nodes)
  331. {
  332. for (int i = 0; i < nodes.Count; i++)
  333. {
  334. var node = nodes[i];
  335. Type nodeType = node.GetType();
  336. RemoveNodeFromEngines(node, nodeType);
  337. RemoveNodeFromMetaDB(node, nodeType);
  338. var nodeWithId = node as INodeWithID;
  339. if (nodeWithId != null)
  340. RemoveNodeFromNodesDictionary(nodeWithId, nodeType);
  341. }
  342. }
  343. readonly DataStructures.WeakReference<EnginesRoot> _engineRootWeakReference;
  344. */
  345. void SubmitNodes()
  346. {
  347. int metaNodesCount = _metaNodesToAdd.Count;
  348. int nodesCount = _nodesToAdd.Count;
  349. if (metaNodesCount + nodesCount == 0) return;
  350. bool newNodesHaveBeenAddedWhileIterating;
  351. int startNodes = 0;
  352. int startMetaNodes = 0;
  353. int numberOfReenteringLoops = 0;
  354. do
  355. {
  356. AddNodesToTheDBAndSuitableEngines(_nodesToAdd, _nodeEngines, _nodesDBdic, _nodesDB);
  357. AddNodesToTheDBAndSuitableEngines(_metaNodesToAdd, _nodeEngines, _nodesDBdic, _metaNodesDB);
  358. newNodesHaveBeenAddedWhileIterating =
  359. _metaNodesToAdd.Count > metaNodesCount ||
  360. _nodesToAdd.Count > nodesCount;
  361. startNodes = nodesCount;
  362. startMetaNodes = metaNodesCount;
  363. if (numberOfReenteringLoops > 5)
  364. throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");
  365. numberOfReenteringLoops++;
  366. metaNodesCount = _metaNodesToAdd.Count;
  367. nodesCount = _nodesToAdd.Count;
  368. } while (newNodesHaveBeenAddedWhileIterating);
  369. _nodesToAdd.Clear();
  370. _metaNodesToAdd.Clear();
  371. }
  372. readonly Dictionary<Type, FasterList<IEngine>> _nodeEngines;
  373. readonly Dictionary<Type, FasterList<IEngine>> _activableEngines;
  374. readonly FasterList<IEngine> _otherEngines;
  375. readonly Dictionary<Type, ITypeSafeList> _nodesDB;
  376. readonly Dictionary<Type, ITypeSafeList> _metaNodesDB;
  377. readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _groupNodesDB;
  378. readonly Dictionary<Type, ITypeSafeDictionary> _nodesDBdic;
  379. /// <summary>
  380. /// Need to think about how to make BuildEntity thread safe as well
  381. /// </summary>
  382. readonly Dictionary<Type, ITypeSafeList> _nodesToAdd;
  383. readonly Dictionary<Type, ITypeSafeList> _metaNodesToAdd;
  384. readonly Dictionary<Type, Dictionary<int, ITypeSafeList>> _groupedNodesToAdd;
  385. readonly NodeSubmissionScheduler _scheduler;
  386. readonly Type _structNodeEngineType;
  387. readonly Type _groupedStructNodesEngineType;
  388. readonly Type _activableNodeEngineType;
  389. readonly Dictionary<Type, Type[]> _implementedInterfaceTypes;
  390. BuildNodeCallbackStruct _callBackStruct;
  391. }
  392. }