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.

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