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.

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