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.

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