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.

260 lines
11KB

  1. using System;
  2. using System.Collections.Generic;
  3. using Svelto.Common;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.Internal;
  6. using Svelto.ECS.Schedulers;
  7. namespace Svelto.ECS
  8. {
  9. public sealed partial class EnginesRoot
  10. {
  11. public readonly struct EntitiesSubmitter
  12. {
  13. public EntitiesSubmitter(EnginesRoot enginesRoot)
  14. {
  15. _weakReference = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot);
  16. }
  17. public bool IsUnused => _weakReference.IsValid == false;
  18. public void Invoke()
  19. {
  20. if (_weakReference.IsValid)
  21. _weakReference.Target.SubmitEntityComponents();
  22. }
  23. readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference;
  24. }
  25. readonly EntitiesSubmissionScheduler _scheduler;
  26. public IEntitiesSubmissionScheduler scheduler => _scheduler;
  27. /// <summary>
  28. /// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
  29. /// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks
  30. /// periodically if new entity must be submitted to the database and the engines. It's an external
  31. /// dependencies to be independent by the running platform as the user can define it.
  32. /// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
  33. /// it must receive a weak reference of the EnginesRoot callback.
  34. /// </summary>
  35. public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
  36. {
  37. _entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>();
  38. serializationDescriptorMap = new SerializationDescriptorMap();
  39. _reactiveEnginesAddRemove = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
  40. _reactiveEnginesSwap = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
  41. _reactiveEnginesSubmission = new FasterList<IReactOnSubmission>();
  42. _enginesSet = new FasterList<IEngine>();
  43. _enginesTypeSet = new HashSet<Type>();
  44. _disposableEngines = new FasterList<IDisposable>();
  45. _transientEntitiesOperations = new FasterList<EntitySubmitOperation>();
  46. _groupEntityComponentsDB =
  47. new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();
  48. _groupsPerEntity =
  49. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>();
  50. _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();
  51. _entityStreams = EntitiesStreams.Create();
  52. _groupFilters =
  53. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>();
  54. _entitiesDB = new EntitiesDB(this);
  55. _scheduler = entitiesComponentScheduler;
  56. _scheduler.onTick = new EntitiesSubmitter(this);
  57. #if UNITY_NATIVE
  58. AllocateNativeOperations();
  59. #endif
  60. }
  61. public EnginesRoot
  62. (EntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
  63. this(entitiesComponentScheduler)
  64. {
  65. _isDeserializationOnly = isDeserializationOnly;
  66. }
  67. /// <summary>
  68. /// Dispose an EngineRoot once not used anymore, so that all the
  69. /// engines are notified with the entities removed.
  70. /// It's a clean up process.
  71. /// </summary>
  72. public void Dispose()
  73. {
  74. _isDisposing = true;
  75. using (var profiler = new PlatformProfiler("Final Dispose"))
  76. {
  77. //Note: The engines are disposed before the the remove callback to give the chance to behave
  78. //differently if a remove happens as a consequence of a dispose
  79. //The pattern is to implement the IDisposable interface and set a flag in the engine. The
  80. //remove callback will then behave differently according the flag.
  81. foreach (var engine in _disposableEngines)
  82. {
  83. try
  84. {
  85. if (engine is IDisposingEngine dengine)
  86. dengine.isDisposing = true;
  87. engine.Dispose();
  88. }
  89. catch (Exception e)
  90. {
  91. Svelto.Console.LogException(e);
  92. }
  93. }
  94. foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>.
  95. KeyValuePairFast groups in _groupEntityComponentsDB)
  96. {
  97. foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups
  98. .Value)
  99. try
  100. {
  101. entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
  102. , new ExclusiveGroupStruct(groups.Key));
  103. }
  104. catch (Exception e)
  105. {
  106. Svelto.Console.LogException(e);
  107. }
  108. }
  109. foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>.
  110. KeyValuePairFast groups in _groupEntityComponentsDB)
  111. {
  112. foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups
  113. .Value)
  114. entityList.Value.Dispose();
  115. }
  116. foreach (FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>.
  117. KeyValuePairFast type in _groupFilters)
  118. foreach (FasterDictionary<ExclusiveGroupStruct, GroupFilters>.KeyValuePairFast group in type.Value)
  119. group.Value.Dispose();
  120. _groupFilters.Clear();
  121. #if UNITY_NATIVE
  122. _addOperationQueue.Dispose();
  123. _removeOperationQueue.Dispose();
  124. _swapOperationQueue.Dispose();
  125. #endif
  126. _groupEntityComponentsDB.Clear();
  127. _groupsPerEntity.Clear();
  128. _disposableEngines.Clear();
  129. _enginesSet.Clear();
  130. _enginesTypeSet.Clear();
  131. _reactiveEnginesSwap.Clear();
  132. _reactiveEnginesAddRemove.Clear();
  133. _reactiveEnginesSubmission.Clear();
  134. _entitiesOperations.Clear();
  135. _transientEntitiesOperations.Clear();
  136. _groupedEntityToAdd.Dispose();
  137. _entityStreams.Dispose();
  138. scheduler.Dispose();
  139. }
  140. GC.SuppressFinalize(this);
  141. }
  142. ~EnginesRoot()
  143. {
  144. Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");
  145. Dispose();
  146. }
  147. public void AddEngine(IEngine engine)
  148. {
  149. var type = engine.GetType();
  150. var refWrapper = new RefWrapperType(type);
  151. DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null");
  152. DBC.ECS.Check.Require(
  153. _enginesTypeSet.Contains(refWrapper) == false
  154. || type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true
  155. , "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
  156. .FastConcat(engine.ToString()));
  157. try
  158. {
  159. if (engine is IReactOnAddAndRemove viewEngine)
  160. CheckReactEngineComponents(viewEngine, _reactiveEnginesAddRemove);
  161. if (engine is IReactOnSwap viewEngineSwap)
  162. CheckReactEngineComponents(viewEngineSwap, _reactiveEnginesSwap);
  163. if (engine is IReactOnSubmission submissionEngine)
  164. _reactiveEnginesSubmission.Add(submissionEngine);
  165. _enginesTypeSet.Add(refWrapper);
  166. _enginesSet.Add(engine);
  167. if (engine is IDisposable)
  168. _disposableEngines.Add(engine as IDisposable);
  169. if (engine is IQueryingEntitiesEngine queryableEntityComponentEngine)
  170. {
  171. queryableEntityComponentEngine.entitiesDB = _entitiesDB;
  172. queryableEntityComponentEngine.Ready();
  173. }
  174. }
  175. catch (Exception e)
  176. {
  177. throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ")
  178. , e);
  179. }
  180. }
  181. void CheckReactEngineComponents<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
  182. where T : class, IReactEngine
  183. {
  184. var interfaces = engine.GetType().GetInterfaces();
  185. foreach (var interf in interfaces)
  186. {
  187. if (interf.IsGenericTypeEx() && typeof(T).IsAssignableFrom(interf))
  188. {
  189. var genericArguments = interf.GetGenericArgumentsEx();
  190. AddEngine(engine, genericArguments, engines);
  191. }
  192. }
  193. }
  194. static void AddEngine<T>
  195. (T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
  196. where T : class, IReactEngine
  197. {
  198. for (var i = 0; i < entityComponentTypes.Length; i++)
  199. {
  200. var type = entityComponentTypes[i];
  201. AddEngine(engine, engines, type);
  202. }
  203. }
  204. static void AddEngine<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, Type type)
  205. where T : class, IReactEngine
  206. {
  207. if (engines.TryGetValue(new RefWrapperType(type), out var list) == false)
  208. {
  209. list = new FasterList<IReactEngine>();
  210. engines.Add(new RefWrapperType(type), list);
  211. }
  212. list.Add(engine);
  213. }
  214. readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesAddRemove;
  215. readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesSwap;
  216. readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission;
  217. readonly FasterList<IDisposable> _disposableEngines;
  218. readonly FasterList<IEngine> _enginesSet;
  219. readonly HashSet<Type> _enginesTypeSet;
  220. internal bool _isDisposing;
  221. }
  222. }