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.

378 lines
18KB

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