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.

357 lines
16KB

  1. #if DEBUG && !PROFILE_SVELTO
  2. #define ENABLE_DEBUG_FUNC
  3. #endif
  4. using System;
  5. using System.Runtime.CompilerServices;
  6. using Svelto.Common;
  7. using Svelto.DataStructures;
  8. using Svelto.ECS.Internal;
  9. namespace Svelto.ECS
  10. {
  11. public partial class EntitiesDB
  12. {
  13. internal EntitiesDB(EnginesRoot enginesRoot, EnginesRoot.LocatorMap entityReferencesMap)
  14. {
  15. _enginesRoot = enginesRoot;
  16. _entityReferencesMap = entityReferencesMap;
  17. }
  18. EntityCollection<T> InternalQueryEntities<T>
  19. (FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
  20. where T : struct, IEntityComponent
  21. {
  22. uint count = 0;
  23. IBuffer<T> buffer;
  24. if (SafeQueryEntityDictionary<T>(out var typeSafeDictionary, entitiesInGroupPerType) == false)
  25. buffer = RetrieveEmptyEntityComponentArray<T>();
  26. else
  27. {
  28. var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
  29. buffer = safeDictionary.GetValues(out count);
  30. }
  31. return new EntityCollection<T>(buffer, count);
  32. }
  33. /// <summary>
  34. /// The QueryEntities<T> follows the rule that entities could always be iterated regardless if they
  35. /// are 0, 1 or N. In case of 0 it returns an empty array. This allows to use the same for iteration
  36. /// regardless the number of entities built.
  37. /// </summary>
  38. /// <param name="groupStructId"></param>
  39. /// <typeparam name="T"></typeparam>
  40. /// <returns></returns>
  41. public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId)
  42. where T : struct, IEntityComponent
  43. {
  44. if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false)
  45. {
  46. var buffer = RetrieveEmptyEntityComponentArray<T>();
  47. return new EntityCollection<T>(buffer, 0);
  48. }
  49. return InternalQueryEntities<T>(entitiesInGroupPerType);
  50. }
  51. public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct)
  52. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
  53. {
  54. if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
  55. {
  56. return new EntityCollection<T1, T2>(new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0)
  57. , new EntityCollection<T2>(
  58. RetrieveEmptyEntityComponentArray<T2>(), 0));
  59. }
  60. var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
  61. var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
  62. #if DEBUG && !PROFILE_SVELTO
  63. if (T1entities.count != T2entities.count)
  64. throw new ECSException("Entity components count do not match in group. Entity 1: ' count: "
  65. .FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
  66. .FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count)
  67. .FastConcat(" ", typeof(T2).ToString())
  68. .FastConcat(
  69. "' group: ", groupStruct.ToName())));
  70. #endif
  71. return new EntityCollection<T1, T2>(T1entities, T2entities);
  72. }
  73. public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
  74. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
  75. {
  76. if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
  77. {
  78. return new EntityCollection<T1, T2, T3>(
  79. new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0)
  80. , new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0)
  81. , new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0));
  82. }
  83. var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
  84. var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
  85. var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
  86. #if DEBUG && !PROFILE_SVELTO
  87. if (T1entities.count != T2entities.count || T2entities.count != T3entities.count)
  88. throw new ECSException("Entity components count do not match in group. Entity 1: "
  89. .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
  90. .FastConcat(T1entities.count).FastConcat(
  91. " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
  92. .FastConcat(T2entities.count)
  93. .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
  94. .FastConcat(" count: ").FastConcat(T3entities.count)));
  95. #endif
  96. return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
  97. }
  98. public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct)
  99. where T1 : struct, IEntityComponent
  100. where T2 : struct, IEntityComponent
  101. where T3 : struct, IEntityComponent
  102. where T4 : struct, IEntityComponent
  103. {
  104. if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
  105. {
  106. return new EntityCollection<T1, T2, T3, T4>(
  107. new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0)
  108. , new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0)
  109. , new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0)
  110. , new EntityCollection<T4>(RetrieveEmptyEntityComponentArray<T4>(), 0));
  111. }
  112. var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
  113. var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
  114. var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
  115. var T4entities = InternalQueryEntities<T4>(entitiesInGroupPerType);
  116. #if DEBUG && !PROFILE_SVELTO
  117. if (T1entities.count != T2entities.count || T2entities.count != T3entities.count
  118. || T3entities.count != T4entities.count)
  119. throw new ECSException("Entity components count do not match in group. Entity 1: "
  120. .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
  121. .FastConcat(T1entities.count).FastConcat(
  122. " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
  123. .FastConcat(T2entities.count)
  124. .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
  125. .FastConcat(" count: ").FastConcat(T3entities.count)
  126. .FastConcat(" Entity 4: ".FastConcat(typeof(T4).ToString()))
  127. .FastConcat(" count: ").FastConcat(T4entities.count)));
  128. #endif
  129. return new EntityCollection<T1, T2, T3, T4>(T1entities, T2entities, T3entities, T4entities);
  130. }
  131. public GroupsEnumerable<T> QueryEntities<T>
  132. (in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
  133. {
  134. return new GroupsEnumerable<T>(this, groups);
  135. }
  136. /// <summary>
  137. /// Note: Remember that EntityViewComponents are always put at the end of the generic parameters tuple.
  138. /// It won't compile otherwise
  139. /// </summary>
  140. /// <returns></returns>
  141. public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  142. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
  143. {
  144. return new GroupsEnumerable<T1, T2>(this, groups);
  145. }
  146. public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>
  147. (in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  148. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
  149. {
  150. return new GroupsEnumerable<T1, T2, T3>(this, groups);
  151. }
  152. public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>
  153. (in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  154. where T1 : struct, IEntityComponent
  155. where T2 : struct, IEntityComponent
  156. where T3 : struct, IEntityComponent
  157. where T4 : struct, IEntityComponent
  158. {
  159. return new GroupsEnumerable<T1, T2, T3, T4>(this, groups);
  160. }
  161. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  162. public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)
  163. where T : struct, IEntityComponent
  164. {
  165. if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
  166. throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName());
  167. return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
  168. }
  169. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  170. public bool TryQueryMappedEntities<T>
  171. (ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent
  172. {
  173. mapper = default;
  174. if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false
  175. || typeSafeDictionary.count == 0)
  176. return false;
  177. mapper = (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
  178. return true;
  179. }
  180. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  181. public bool Exists<T>(EGID entityGID) where T : struct, IEntityComponent
  182. {
  183. if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false)
  184. return false;
  185. return casted != null && casted.ContainsKey(entityGID.entityID);
  186. }
  187. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  188. public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent
  189. {
  190. if (SafeQueryEntityDictionary<T>(group, out var casted) == false)
  191. return false;
  192. return casted != null && casted.ContainsKey(id);
  193. }
  194. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  195. public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
  196. {
  197. if (groupEntityComponentsDB.TryGetValue(
  198. gid, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> group) == true)
  199. {
  200. return group.count > 0;
  201. }
  202. return false;
  203. }
  204. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  205. public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
  206. {
  207. return Count<T>(groupStruct) > 0;
  208. }
  209. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  210. public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
  211. {
  212. if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false)
  213. return 0;
  214. return (int) typeSafeDictionary.count;
  215. }
  216. public bool FoundInGroups<T1>() where T1 : IEntityComponent
  217. {
  218. return groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper);
  219. }
  220. public bool IsDisposing => _enginesRoot._isDisposing;
  221. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  222. internal bool SafeQueryEntityDictionary<T>
  223. (out ITypeSafeDictionary typeSafeDictionary
  224. , FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) where T : IEntityComponent
  225. {
  226. if (entitiesInGroupPerType.TryGetValue(new RefWrapperType(TypeCache<T>.type), out var safeDictionary)
  227. == false)
  228. {
  229. typeSafeDictionary = default;
  230. return false;
  231. }
  232. //return the indexes entities if they exist
  233. typeSafeDictionary = safeDictionary;
  234. return true;
  235. }
  236. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  237. internal bool SafeQueryEntityDictionary<T>
  238. (ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : IEntityComponent
  239. {
  240. if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
  241. {
  242. typeSafeDictionary = default;
  243. return false;
  244. }
  245. //return the indexes entities if they exist
  246. typeSafeDictionary = safeDictionary;
  247. return true;
  248. }
  249. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  250. internal bool UnsafeQueryEntityDictionary
  251. (ExclusiveGroupStruct group, Type type, out ITypeSafeDictionary typeSafeDictionary)
  252. {
  253. //search for the group
  254. if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
  255. {
  256. typeSafeDictionary = null;
  257. return false;
  258. }
  259. //search for the indexed entities in the group
  260. return entitiesInGroupPerType.TryGetValue(new RefWrapperType(type), out typeSafeDictionary);
  261. }
  262. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  263. static IBuffer<T> RetrieveEmptyEntityComponentArray<T>() where T : struct, IEntityComponent
  264. {
  265. return EmptyList<T>.emptyArray;
  266. }
  267. static class EmptyList<T> where T : struct, IEntityComponent
  268. {
  269. internal static readonly IBuffer<T> emptyArray;
  270. static EmptyList()
  271. {
  272. if (ComponentBuilder<T>.IS_ENTITY_VIEW_COMPONENT)
  273. {
  274. MB<T> b = default;
  275. emptyArray = b;
  276. }
  277. else
  278. {
  279. NB<T> b = default;
  280. emptyArray = b;
  281. }
  282. }
  283. }
  284. static readonly FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> _emptyDictionary =
  285. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  286. readonly EnginesRoot _enginesRoot;
  287. EntitiesStreams _entityStream => _enginesRoot._entityStreams;
  288. //grouped set of entity components, this is the standard way to handle entity components are grouped per
  289. //group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of
  290. //values directly, that can be iterated over, so that is possible to iterate over all the entity components of
  291. //a specific type inside a specific group.
  292. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
  293. groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB;
  294. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  295. //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
  296. //by _groupEntityComponentsDB
  297. // <EntityComponentType <groupID <entityID, EntityComponent>>>
  298. FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> groupsPerEntity =>
  299. _enginesRoot._groupsPerEntity;
  300. EnginesRoot.LocatorMap _entityReferencesMap;
  301. }
  302. }