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.

221 lines
9.6KB

  1. //#define PARANOID_CHECK
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Runtime.CompilerServices;
  5. using Svelto.DataStructures;
  6. using Svelto.ECS.Internal;
  7. namespace Svelto.ECS
  8. {
  9. public static class EGIDMultiMapperNBExtension
  10. {
  11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  12. public static EGIDMultiMapper<T> QueryMappedEntities<T>(this EntitiesDB entitiesDb, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
  13. where T : struct, _IInternalEntityComponent
  14. {
  15. var dictionary = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary<T>>((uint) groups.count);
  16. foreach (var group in groups)
  17. {
  18. entitiesDb.QueryOrCreateEntityDictionary<T>(group, out var typeSafeDictionary);
  19. //if (typeSafeDictionary.count > 0) avoiding this allows these egidmappers to be precreated and stored
  20. dictionary.Add(group, typeSafeDictionary as ITypeSafeDictionary<T>);
  21. }
  22. return new EGIDMultiMapper<T>(dictionary);
  23. }
  24. }
  25. public partial class EnginesRoot: IDisposable, IUnitTestingInterface
  26. {
  27. ///--------------------------------------------
  28. ///
  29. public IEntityStreamConsumerFactory GenerateConsumerFactory()
  30. {
  31. return new GenericEntityStreamConsumerFactory(this);
  32. }
  33. public IEntityFactory GenerateEntityFactory()
  34. {
  35. return new GenericEntityFactory(this);
  36. }
  37. public IEntityFunctions GenerateEntityFunctions()
  38. {
  39. return new GenericEntityFunctions(this);
  40. }
  41. ///--------------------------------------------
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. EntityInitializer BuildEntity(EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType,
  44. IEnumerable<object> implementors, string caller)
  45. {
  46. CheckAddEntityID(entityID, descriptorType, caller);
  47. DBC.ECS.Check.Require(
  48. entityID.groupID.isInvalid == false,
  49. "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");
  50. var reference = _entityLocator.ClaimReference();
  51. _entityLocator.SetReference(reference, entityID);
  52. var dic = EntityFactory.BuildGroupedEntities(
  53. entityID, _groupedEntityToAdd, componentsToBuild, implementors
  54. #if DEBUG && !PROFILE_SVELTO
  55. , descriptorType
  56. #endif
  57. );
  58. return new EntityInitializer(entityID, dic, reference);
  59. }
  60. /// <summary>
  61. /// Preallocate memory to avoid the impact to resize arrays when many entities are submitted at once
  62. /// </summary>
  63. internal void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild)
  64. {
  65. void PreallocateEntitiesToAdd()
  66. {
  67. _groupedEntityToAdd.Preallocate(groupID, size, entityComponentsToBuild);
  68. }
  69. void PreallocateDBGroup()
  70. {
  71. var numberOfEntityComponents = entityComponentsToBuild.Length;
  72. FasterDictionary<ComponentID, ITypeSafeDictionary> group = GetOrAddDBGroup(groupID);
  73. for (var index = 0; index < numberOfEntityComponents; index++)
  74. {
  75. var entityComponentBuilder = entityComponentsToBuild[index];
  76. var entityComponentType = entityComponentBuilder.getComponentID;
  77. var dbList = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size));
  78. entityComponentBuilder.Preallocate(dbList, size);
  79. if (_groupsPerEntity.TryGetValue(entityComponentType, out var groupedGroup) == false)
  80. groupedGroup = _groupsPerEntity[entityComponentType] =
  81. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  82. groupedGroup[groupID] = dbList;
  83. }
  84. }
  85. PreallocateDBGroup();
  86. PreallocateEntitiesToAdd();
  87. _entityLocator.PreallocateReferenceMaps(groupID, size);
  88. }
  89. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  90. FasterDictionary<ComponentID, ITypeSafeDictionary> GetDBGroup(ExclusiveGroupStruct fromIdGroupId)
  91. {
  92. if (_groupEntityComponentsDB.TryGetValue(
  93. fromIdGroupId,
  94. out FasterDictionary<ComponentID, ITypeSafeDictionary> fromGroup) == false)
  95. throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId.ToName()));
  96. return fromGroup;
  97. }
  98. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  99. FasterDictionary<ComponentID, ITypeSafeDictionary> GetOrAddDBGroup(ExclusiveGroupStruct toGroupId)
  100. {
  101. return _groupEntityComponentsDB.GetOrAdd(
  102. toGroupId,
  103. () => new FasterDictionary<ComponentID, ITypeSafeDictionary>());
  104. }
  105. IComponentBuilder[] FindRealComponents<T>(EGID fromEntityGID) where T : IEntityDescriptor, new()
  106. {
  107. var fromGroup = GetDBGroup(fromEntityGID.groupID);
  108. if (fromGroup.TryGetValue(
  109. ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
  110. out var entityInfoDic) //<entity ID, EntityInfoComponent>
  111. && ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(
  112. fromEntityGID.entityID,
  113. out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
  114. {
  115. #if PARANOID_CHECK
  116. var hash = new HashSet<IComponentBuilder>(entityInfo.componentsToBuild,
  117. default(ComponentBuilderComparer));
  118. foreach (var component in EntityDescriptorTemplate<T>.descriptor.componentsToBuild)
  119. {
  120. if (hash.Contains(component) == false)
  121. throw new Exception(
  122. $"entityInfo.componentsToBuild must contain all the base components {fromEntityGID}," +
  123. $" missing component {component}");
  124. hash.Remove(component);
  125. }
  126. #endif
  127. return entityInfo.componentsToBuild;
  128. }
  129. return EntityDescriptorTemplate<T>.realDescriptor.componentsToBuild;
  130. }
  131. IComponentBuilder[] FindRealComponents(EGID fromEntityGID, IComponentBuilder[] baseComponents)
  132. {
  133. var fromGroup = GetDBGroup(fromEntityGID.groupID);
  134. if (fromGroup.TryGetValue(
  135. ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
  136. out var entityInfoDic) //<entity ID, EntityInfoComponent>
  137. && ((ITypeSafeDictionary<EntityInfoComponent>)entityInfoDic).TryGetValue(
  138. fromEntityGID.entityID,
  139. out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
  140. {
  141. #if PARANOID_CHECK
  142. var hash = new HashSet<IComponentBuilder>(entityInfo.componentsToBuild,
  143. default(ComponentBuilderComparer));
  144. foreach (var component in baseComponents)
  145. {
  146. if (hash.Contains(component) == false)
  147. throw new Exception(
  148. $"entityInfo.componentsToBuild must contain all the base components {fromEntityGID}," +
  149. $" missing component {component}");
  150. hash.Remove(component);
  151. }
  152. #endif
  153. return entityInfo.componentsToBuild;
  154. }
  155. return baseComponents;
  156. }
  157. //one datastructure rule them all:
  158. //split by group
  159. //split by type per group. It's possible to get all the entities of a give type T per group thanks
  160. //to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by
  161. //ID. This ID doesn't need to be the EGID, it can be just the entityID
  162. //for each group id, save a dictionary indexed by entity type of entities indexed by id
  163. // group EntityComponentType entityID, EntityComponent
  164. internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  165. _groupEntityComponentsDB;
  166. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  167. //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
  168. //by _groupEntityComponentsDB
  169. // <EntityComponentType <groupID <entityID, EntityComponent>>>
  170. internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
  171. _groupsPerEntity;
  172. #if SVELTO_LEGACY_FILTERS
  173. //The filters stored for each component and group
  174. internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>>
  175. _groupFilters;
  176. #endif
  177. readonly EntitiesDB _entitiesDB;
  178. EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
  179. }
  180. public interface IUnitTestingInterface
  181. {
  182. EntitiesDB entitiesForTesting { get; }
  183. }
  184. }