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.

572 lines
21KB

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