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.

359 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, IEntityComponent
  21. {
  22. uint count = 0;
  23. IBuffer<T> buffer;
  24. EntityIDs ids = default;
  25. if (SafeQueryEntityDictionary<T>(out var typeSafeDictionary, entitiesInGroupPerType) == false)
  26. buffer = RetrieveEmptyEntityComponentArray<T>();
  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, count, ids);
  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, IEntityComponent
  45. {
  46. if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false)
  47. {
  48. var buffer = RetrieveEmptyEntityComponentArray<T>();
  49. return new EntityCollection<T>(buffer, 0);
  50. }
  51. return InternalQueryEntities<T>(entitiesInGroupPerType);
  52. }
  53. public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct)
  54. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
  55. {
  56. if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
  57. {
  58. return new EntityCollection<T1, T2>(new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0)
  59. , new EntityCollection<T2>(
  60. RetrieveEmptyEntityComponentArray<T2>(), 0));
  61. }
  62. var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
  63. var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
  64. #if DEBUG && !PROFILE_SVELTO
  65. if (T1entities.count != T2entities.count)
  66. throw new ECSException("Entity components count do not match in group. Entity 1: ' count: "
  67. .FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
  68. .FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count)
  69. .FastConcat(" ", typeof(T2).ToString())
  70. .FastConcat(
  71. "' group: ", groupStruct.ToName())));
  72. #endif
  73. return new EntityCollection<T1, T2>(T1entities, T2entities);
  74. }
  75. public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
  76. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
  77. {
  78. if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
  79. {
  80. return new EntityCollection<T1, T2, T3>(
  81. new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0)
  82. , new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0)
  83. , new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0));
  84. }
  85. var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
  86. var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
  87. var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
  88. #if DEBUG && !PROFILE_SVELTO
  89. if (T1entities.count != T2entities.count || T2entities.count != T3entities.count)
  90. throw new ECSException("Entity components count do not match in group. Entity 1: "
  91. .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
  92. .FastConcat(T1entities.count).FastConcat(
  93. " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
  94. .FastConcat(T2entities.count)
  95. .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
  96. .FastConcat(" count: ").FastConcat(T3entities.count)));
  97. #endif
  98. return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
  99. }
  100. public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct)
  101. where T1 : struct, IEntityComponent
  102. where T2 : struct, IEntityComponent
  103. where T3 : struct, IEntityComponent
  104. where T4 : struct, IEntityComponent
  105. {
  106. if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
  107. {
  108. return new EntityCollection<T1, T2, T3, T4>(
  109. new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0)
  110. , new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0)
  111. , new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0)
  112. , new EntityCollection<T4>(RetrieveEmptyEntityComponentArray<T4>(), 0));
  113. }
  114. var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
  115. var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
  116. var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
  117. var T4entities = InternalQueryEntities<T4>(entitiesInGroupPerType);
  118. #if DEBUG && !PROFILE_SVELTO
  119. if (T1entities.count != T2entities.count || T2entities.count != T3entities.count
  120. || T3entities.count != T4entities.count)
  121. throw new ECSException("Entity components count do not match in group. Entity 1: "
  122. .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
  123. .FastConcat(T1entities.count).FastConcat(
  124. " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
  125. .FastConcat(T2entities.count)
  126. .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
  127. .FastConcat(" count: ").FastConcat(T3entities.count)
  128. .FastConcat(" Entity 4: ".FastConcat(typeof(T4).ToString()))
  129. .FastConcat(" count: ").FastConcat(T4entities.count)));
  130. #endif
  131. return new EntityCollection<T1, T2, T3, T4>(T1entities, T2entities, T3entities, T4entities);
  132. }
  133. public GroupsEnumerable<T> QueryEntities<T>
  134. (in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
  135. {
  136. return new GroupsEnumerable<T>(this, groups);
  137. }
  138. /// <summary>
  139. /// Note: Remember that EntityViewComponents are always put at the end of the generic parameters tuple.
  140. /// It won't compile otherwise
  141. /// </summary>
  142. /// <returns></returns>
  143. public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  144. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
  145. {
  146. return new GroupsEnumerable<T1, T2>(this, groups);
  147. }
  148. public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>
  149. (in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  150. where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
  151. {
  152. return new GroupsEnumerable<T1, T2, T3>(this, groups);
  153. }
  154. public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>
  155. (in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  156. where T1 : struct, IEntityComponent
  157. where T2 : struct, IEntityComponent
  158. where T3 : struct, IEntityComponent
  159. where T4 : struct, IEntityComponent
  160. {
  161. return new GroupsEnumerable<T1, T2, T3, T4>(this, groups);
  162. }
  163. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  164. public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)
  165. where T : struct, IEntityComponent
  166. {
  167. if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
  168. throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName());
  169. return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
  170. }
  171. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  172. public bool TryQueryMappedEntities<T>
  173. (ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent
  174. {
  175. mapper = default;
  176. if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false
  177. || typeSafeDictionary.count == 0)
  178. return false;
  179. mapper = (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
  180. return true;
  181. }
  182. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  183. public bool Exists<T>(EGID entityGID) where T : struct, IEntityComponent
  184. {
  185. if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false)
  186. return false;
  187. return casted != null && casted.ContainsKey(entityGID.entityID);
  188. }
  189. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  190. public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent
  191. {
  192. if (SafeQueryEntityDictionary<T>(group, out var casted) == false)
  193. return false;
  194. return casted != null && casted.ContainsKey(id);
  195. }
  196. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  197. public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
  198. {
  199. if (groupEntityComponentsDB.TryGetValue(
  200. gid, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> group) == true)
  201. {
  202. return group.count > 0;
  203. }
  204. return false;
  205. }
  206. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  207. public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
  208. {
  209. return Count<T>(groupStruct) > 0;
  210. }
  211. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  212. public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
  213. {
  214. if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false)
  215. return 0;
  216. return (int) typeSafeDictionary.count;
  217. }
  218. public bool FoundInGroups<T1>() where T1 : IEntityComponent
  219. {
  220. return groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper);
  221. }
  222. public bool IsDisposing => _enginesRoot._isDisposing;
  223. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  224. bool SafeQueryEntityDictionary<T>
  225. (out ITypeSafeDictionary typeSafeDictionary
  226. , FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) where T : IEntityComponent
  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 : IEntityComponent
  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. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  265. static IBuffer<T> RetrieveEmptyEntityComponentArray<T>() where T : struct, IEntityComponent
  266. {
  267. return EmptyList<T>.emptyArray;
  268. }
  269. static class EmptyList<T> where T : struct, IEntityComponent
  270. {
  271. internal static readonly IBuffer<T> emptyArray;
  272. static EmptyList()
  273. {
  274. if (ComponentBuilder<T>.IS_ENTITY_VIEW_COMPONENT)
  275. {
  276. MB<T> b = default;
  277. emptyArray = b;
  278. }
  279. else
  280. {
  281. NB<T> b = default;
  282. emptyArray = b;
  283. }
  284. }
  285. }
  286. static readonly FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> _emptyDictionary =
  287. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  288. readonly EnginesRoot _enginesRoot;
  289. EntitiesStreams _entityStream => _enginesRoot._entityStreams;
  290. //grouped set of entity components, this is the standard way to handle entity components are grouped per
  291. //group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of
  292. //values directly, that can be iterated over, so that is possible to iterate over all the entity components of
  293. //a specific type inside a specific group.
  294. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
  295. groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB;
  296. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  297. //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
  298. //by _groupEntityComponentsDB
  299. // <EntityComponentType <groupID <entityID, EntityComponent>>>
  300. FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> groupsPerEntity =>
  301. _enginesRoot._groupsPerEntity;
  302. EnginesRoot.EntityReferenceMap _entityReferencesMap;
  303. }
  304. }