//#define PARANOID_CHECK using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { public static class EGIDMultiMapperNBExtension { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EGIDMultiMapper QueryMappedEntities(this EntitiesDB entitiesDb, LocalFasterReadOnlyList groups) where T : struct, _IInternalEntityComponent { var dictionary = new FasterDictionary>((uint) groups.count); foreach (var group in groups) { entitiesDb.QueryOrCreateEntityDictionary(group, out var typeSafeDictionary); //if (typeSafeDictionary.count > 0) avoiding this allows these egidmappers to be precreated and stored dictionary.Add(group, typeSafeDictionary as ITypeSafeDictionary); } return new EGIDMultiMapper(dictionary); } } public partial class EnginesRoot: IDisposable, IUnitTestingInterface { ///-------------------------------------------- /// public IEntityStreamConsumerFactory GenerateConsumerFactory() { return new GenericEntityStreamConsumerFactory(this); } public IEntityFactory GenerateEntityFactory() { return new GenericEntityFactory(this); } public IEntityFunctions GenerateEntityFunctions() { return new GenericEntityFunctions(this); } ///-------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] EntityInitializer BuildEntity(EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType, IEnumerable implementors, string caller) { CheckAddEntityID(entityID, descriptorType, caller); DBC.ECS.Check.Require( entityID.groupID.isInvalid == false, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?"); var reference = _entityLocator.ClaimReference(); _entityLocator.SetReference(reference, entityID); var dic = EntityFactory.BuildGroupedEntities( entityID, _groupedEntityToAdd, componentsToBuild, implementors #if DEBUG && !PROFILE_SVELTO , descriptorType #endif ); return new EntityInitializer(entityID, dic, reference); } /// /// Preallocate memory to avoid the impact to resize arrays when many entities are submitted at once /// internal void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild) { void PreallocateEntitiesToAdd() { _groupedEntityToAdd.Preallocate(groupID, size, entityComponentsToBuild); } void PreallocateDBGroup() { var numberOfEntityComponents = entityComponentsToBuild.Length; FasterDictionary group = GetOrAddDBGroup(groupID); for (var index = 0; index < numberOfEntityComponents; index++) { var entityComponentBuilder = entityComponentsToBuild[index]; var entityComponentType = entityComponentBuilder.getComponentID; var components = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size)); if (components.count != 0) throw new ECSException("Entity already created in this group, cannot preallocate"); entityComponentBuilder.Preallocate(components, size); if (_groupsPerEntity.TryGetValue(entityComponentType, out var groupedGroup) == false) groupedGroup = _groupsPerEntity[entityComponentType] = new FasterDictionary(); groupedGroup[groupID] = components; } } PreallocateDBGroup(); PreallocateEntitiesToAdd(); _entityLocator.PreallocateReferenceMaps(groupID, size); } [MethodImpl(MethodImplOptions.AggressiveInlining)] FasterDictionary GetDBGroup(ExclusiveGroupStruct fromIdGroupId) { if (_groupEntityComponentsDB.TryGetValue( fromIdGroupId, out FasterDictionary fromGroup) == false) throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId.ToName())); return fromGroup; } [MethodImpl(MethodImplOptions.AggressiveInlining)] FasterDictionary GetOrAddDBGroup(ExclusiveGroupStruct toGroupId) { return _groupEntityComponentsDB.GetOrAdd( toGroupId, () => new FasterDictionary()); } IComponentBuilder[] FindRealComponents(EGID fromEntityGID) where T : IEntityDescriptor, new() { var fromGroup = GetDBGroup(fromEntityGID.groupID); if (fromGroup.TryGetValue( ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID, out var entityInfoDic) // && ((ITypeSafeDictionary)entityInfoDic).TryGetValue( fromEntityGID.entityID, 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 { #if PARANOID_CHECK var hash = new HashSet(entityInfo.componentsToBuild, default(ComponentBuilderComparer)); foreach (var component in EntityDescriptorTemplate.descriptor.componentsToBuild) { if (hash.Contains(component) == false) throw new Exception( $"entityInfo.componentsToBuild must contain all the base components {fromEntityGID}," + $" missing component {component}"); hash.Remove(component); } #endif return entityInfo.componentsToBuild; } return EntityDescriptorTemplate.realDescriptor.componentsToBuild; } IComponentBuilder[] FindRealComponents(EGID fromEntityGID, IComponentBuilder[] baseComponents) { var fromGroup = GetDBGroup(fromEntityGID.groupID); if (fromGroup.TryGetValue( ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID, out var entityInfoDic) // && ((ITypeSafeDictionary)entityInfoDic).TryGetValue( fromEntityGID.entityID, 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 { #if PARANOID_CHECK var hash = new HashSet(entityInfo.componentsToBuild, default(ComponentBuilderComparer)); foreach (var component in baseComponents) { if (hash.Contains(component) == false) throw new Exception( $"entityInfo.componentsToBuild must contain all the base components {fromEntityGID}," + $" missing component {component}"); hash.Remove(component); } #endif return entityInfo.componentsToBuild; } return baseComponents; } //one datastructure rule them all: //split by group //split by type per group. It's possible to get all the entities of a give type T per group thanks //to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by //ID. This ID doesn't need to be the EGID, it can be just the entityID //for each group id, save a dictionary indexed by entity type of entities indexed by id // group EntityComponentType entityID, EntityComponent internal readonly FasterDictionary> _groupEntityComponentsDB; //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold //by _groupEntityComponentsDB // >> internal readonly FasterDictionary> _groupsPerEntity; #if SVELTO_LEGACY_FILTERS //The filters stored for each component and group internal readonly FasterDictionary> _groupFilters; #endif readonly EntitiesDB _entitiesDB; EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB; } public interface IUnitTestingInterface { EntitiesDB entitiesForTesting { get; } } }