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.

301 lines
13KB

  1. #if DEBUG && !PROFILER
  2. #define ENABLE_DEBUG_FUNC
  3. #endif
  4. using System;
  5. using System.Runtime.CompilerServices;
  6. using Svelto.DataStructures;
  7. namespace Svelto.ECS.Internal
  8. {
  9. partial class EntitiesDB : IEntitiesDB
  10. {
  11. internal EntitiesDB(
  12. FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityViewsDB,
  13. FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity,
  14. EntitiesStream entityStream)
  15. {
  16. _groupEntityViewsDB = groupEntityViewsDB;
  17. _groupsPerEntity = groupsPerEntity;
  18. _entityStream = entityStream;
  19. }
  20. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  21. public ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
  22. {
  23. var entities = QueryEntities<T>(group, out var count);
  24. if (count == 0)
  25. throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
  26. if (count != 1)
  27. throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
  28. .FastConcat("'"));
  29. return ref entities[0];
  30. }
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityStruct
  33. {
  34. T[] array;
  35. if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null)
  36. return ref array[index];
  37. throw new EntityNotFoundException(entityGID, typeof(T));
  38. }
  39. public ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
  40. {
  41. return ref QueryEntity<T>(new EGID(id, group));
  42. }
  43. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  44. public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
  45. where T : struct, IEntityStruct
  46. {
  47. uint group = groupStruct;
  48. count = 0;
  49. if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false)
  50. return RetrieveEmptyEntityViewArray<T>();
  51. return typeSafeDictionary.GetValuesArray(out count);
  52. }
  53. public EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
  54. where T : struct, IEntityStruct
  55. {
  56. return new EntityCollection<T>(QueryEntities<T>(groupStruct, out var count), count);
  57. }
  58. public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
  59. where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
  60. {
  61. return new EntityCollection<T1, T2>(QueryEntities<T1, T2>(groupStruct, out var count), count);
  62. }
  63. public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
  64. where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
  65. {
  66. return new EntityCollection<T1, T2, T3>(QueryEntities<T1, T2, T3>(groupStruct, out var count), count);
  67. }
  68. public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct
  69. {
  70. return new EntityCollections<T>(this, groups);
  71. }
  72. public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups)
  73. where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
  74. {
  75. return new EntityCollections<T1, T2>(this, groups);
  76. }
  77. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  78. public (T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
  79. where T1 : struct, IEntityStruct
  80. where T2 : struct, IEntityStruct
  81. {
  82. var T1entities = QueryEntities<T1>(groupStruct, out var countCheck);
  83. var T2entities = QueryEntities<T2>(groupStruct, out count);
  84. if (count != countCheck)
  85. {
  86. throw new ECSException("Entity views count do not match in group. Entity 1: ' count: "
  87. .FastConcat(countCheck)
  88. .FastConcat(typeof(T1).ToString())
  89. .FastConcat("'. Entity 2: ' count: ".FastConcat(count)
  90. .FastConcat(typeof(T2).ToString())
  91. .FastConcat("'")));
  92. }
  93. return (T1entities, T2entities);
  94. }
  95. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  96. public (T1[], T2[], T3[]) QueryEntities
  97. <T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
  98. where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
  99. {
  100. var T1entities = QueryEntities<T1>(groupStruct, out var countCheck1);
  101. var T2entities = QueryEntities<T2>(groupStruct, out var countCheck2);
  102. var T3entities = QueryEntities<T3>(groupStruct, out count);
  103. if (count != countCheck1 || count != countCheck2)
  104. throw new ECSException("Entity views count do not match in group. Entity 1: "
  105. .FastConcat(typeof(T1).ToString()).FastConcat(" count: ").FastConcat(countCheck1).FastConcat(
  106. " Entity 2: ".FastConcat(typeof(T2).ToString())
  107. .FastConcat(" count: ").FastConcat(countCheck2)
  108. .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())).FastConcat(" count: ")
  109. .FastConcat(count)));
  110. return (T1entities, T2entities, T3entities);
  111. }
  112. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  113. public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId)
  114. where T : struct, IEntityStruct
  115. {
  116. if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false)
  117. throw new EntityGroupNotFoundException(groupStructId, typeof(T));
  118. EGIDMapper<T> mapper;
  119. mapper.map = typeSafeDictionary;
  120. return mapper;
  121. }
  122. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  123. public bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId,
  124. out EGIDMapper<T> mapper)
  125. where T : struct, IEntityStruct
  126. {
  127. mapper = default;
  128. if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false)
  129. return false;
  130. mapper.map = typeSafeDictionary;
  131. return true;
  132. }
  133. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  134. public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
  135. {
  136. T[] array;
  137. if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null)
  138. return array;
  139. throw new EntityNotFoundException(entityGID, typeof(T));
  140. }
  141. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  142. public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array)
  143. where T : struct, IEntityStruct
  144. {
  145. if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null)
  146. return true;
  147. return false;
  148. }
  149. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  150. public T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index)
  151. where T : struct, IEntityStruct
  152. {
  153. return QueryEntitiesAndIndex<T>(new EGID(id, group), out index);
  154. }
  155. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  156. public bool TryQueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index,
  157. out T[] array) where T : struct, IEntityStruct
  158. {
  159. return TryQueryEntitiesAndIndex(new EGID(id, group), out index, out array);
  160. }
  161. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  162. public bool Exists<T>(EGID entityGID) where T : struct, IEntityStruct
  163. {
  164. if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> casted) == false) return false;
  165. return casted != null && casted.ContainsKey(entityGID.entityID);
  166. }
  167. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  168. public bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
  169. {
  170. if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> casted) == false) return false;
  171. return casted != null && casted.ContainsKey(id);
  172. }
  173. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  174. public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid)
  175. {
  176. return _groupEntityViewsDB.ContainsKey(gid);
  177. }
  178. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  179. public bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct
  180. {
  181. QueryEntities<T>(groupStruct, out var count);
  182. return count > 0;
  183. }
  184. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  185. public uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct
  186. {
  187. QueryEntities<T>(groupStruct, out var count);
  188. return count;
  189. }
  190. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  191. public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct
  192. {
  193. _entityStream.PublishEntity(ref QueryEntity<T>(egid), egid);
  194. }
  195. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  196. T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
  197. {
  198. index = 0;
  199. if (SafeQueryEntityDictionary(entityGID.groupID, out TypeSafeDictionary<T> safeDictionary) == false)
  200. return null;
  201. if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
  202. return null;
  203. return safeDictionary.GetValuesArray(out _);
  204. }
  205. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  206. bool SafeQueryEntityDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary)
  207. where T : struct, IEntityStruct
  208. {
  209. if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
  210. {
  211. typeSafeDictionary = default;
  212. return false;
  213. }
  214. //return the indexes entities if they exist
  215. typeSafeDictionary = safeDictionary as TypeSafeDictionary<T>;
  216. return true;
  217. }
  218. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  219. internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary)
  220. {
  221. //search for the group
  222. if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
  223. {
  224. typeSafeDictionary = null;
  225. return false;
  226. }
  227. //search for the indexed entities in the group
  228. return entitiesInGroupPerType.TryGetValue(new RefWrapper<Type>(type), out typeSafeDictionary);
  229. }
  230. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  231. static T[] RetrieveEmptyEntityViewArray<T>()
  232. {
  233. return EmptyList<T>.emptyArray;
  234. }
  235. //grouped set of entity views, this is the standard way to handle entity views entity views are grouped per
  236. //group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of
  237. //values directly, that can be iterated over, so that is possible to iterate over all the entity views of
  238. //a specific type inside a specific group.
  239. readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;
  240. //needed to be able to iterate over all the entities of the same type regardless the group
  241. //may change in future
  242. readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
  243. readonly EntitiesStream _entityStream;
  244. static class EmptyList<T>
  245. {
  246. internal static readonly T[] emptyArray = new T[0];
  247. }
  248. }
  249. }