#if DEBUG && !PROFILE_SVELTO #define ENABLE_DEBUG_FUNC #endif using System.Runtime.CompilerServices; using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { public partial class EntitiesDB { internal EntitiesDB(EnginesRoot enginesRoot, EnginesRoot.EntityReferenceMap entityReferencesMap) { _enginesRoot = enginesRoot; _entityReferencesMap = entityReferencesMap; } public void PreallocateEntitySpace(ExclusiveGroupStruct groupStructId, uint numberOfEntities) where T : IEntityDescriptor, new() { _enginesRoot.Preallocate(groupStructId, numberOfEntities, EntityDescriptorTemplate.realDescriptor.componentsToBuild); } /// /// The QueryEntities follows the rule that entities could always be iterated regardless if they /// are 0, 1 or N. In case of 0 it returns an empty array. This allows to use the same for iteration /// regardless the number of entities built. /// /// /// /// public EntityCollection QueryEntities(ExclusiveGroupStruct groupStructId) where T : struct, _IInternalEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false) { return new EntityCollection(default, default, 0); } return InternalQueryEntities(entitiesInGroupPerType); } public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) { return new EntityCollection( new EntityCollection(default, default, 0) , new EntityCollection(default, default, 0)); } var T1entities = InternalQueryEntities(entitiesInGroupPerType); var T2entities = InternalQueryEntities(entitiesInGroupPerType); #if DEBUG && !PROFILE_SVELTO if (T1entities.count != T2entities.count) throw new ECSException( "Entity components count do not match in group. Entity 1: ' count: " .FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString()) .FastConcat( "'. Entity 2: ' count: ".FastConcat(T2entities.count) .FastConcat(" ", typeof(T2).ToString()) .FastConcat("' group: ", groupStruct.ToName())).FastConcat( " this means that you are mixing descriptors in the same group that do not share the components that you are querying")); #endif return new EntityCollection(T1entities, T2entities); } public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent where T3 : struct, _IInternalEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) { return new EntityCollection( new EntityCollection(default, default, 0) , new EntityCollection(default, default, 0) , new EntityCollection(default, default, 0)); } var T1entities = InternalQueryEntities(entitiesInGroupPerType); var T2entities = InternalQueryEntities(entitiesInGroupPerType); var T3entities = InternalQueryEntities(entitiesInGroupPerType); #if DEBUG && !PROFILE_SVELTO if (T1entities.count != T2entities.count || T2entities.count != T3entities.count) throw new ECSException( "Entity components count do not match in group. Entity 1: " .FastConcat(typeof(T1).ToString()).FastConcat(" count: ") .FastConcat(T1entities.count).FastConcat( " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ") .FastConcat(T2entities.count) .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) .FastConcat(" count: ").FastConcat(T3entities.count)).FastConcat( " this means that you are mixing descriptors in the same group that do not share the components that you are querying")); #endif return new EntityCollection(T1entities, T2entities, T3entities); } public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent where T3 : struct, _IInternalEntityComponent where T4 : struct, _IInternalEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) { return new EntityCollection( new EntityCollection(default, default, 0) , new EntityCollection(default, default, 0) , new EntityCollection(default, default, 0) , new EntityCollection(default, default, 0)); } var T1entities = InternalQueryEntities(entitiesInGroupPerType); var T2entities = InternalQueryEntities(entitiesInGroupPerType); var T3entities = InternalQueryEntities(entitiesInGroupPerType); var T4entities = InternalQueryEntities(entitiesInGroupPerType); #if DEBUG && !PROFILE_SVELTO if (T1entities.count != T2entities.count || T2entities.count != T3entities.count || T3entities.count != T4entities.count) throw new ECSException( "Entity components count do not match in group. Entity 1: " .FastConcat(typeof(T1).ToString()).FastConcat(" count: ") .FastConcat(T1entities.count).FastConcat( " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ") .FastConcat(T2entities.count) .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) .FastConcat(" count: ").FastConcat(T3entities.count) .FastConcat(" Entity 4: ".FastConcat(typeof(T4).ToString())) .FastConcat(" count: ").FastConcat(T4entities.count)).FastConcat( " this means that you are mixing descriptors in the same group that do not share the components that you are querying")); #endif return new EntityCollection(T1entities, T2entities, T3entities, T4entities); } public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups) where T : struct, _IInternalEntityComponent { return new GroupsEnumerable(this, groups); } /// /// Note: Remember that EntityViewComponents are always put at the end of the generic parameters tuple. /// The Query entity code won't inexplicably compile otherwise /// /// public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups) where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent { return new GroupsEnumerable(this, groups); } public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups) where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent where T3 : struct, _IInternalEntityComponent { return new GroupsEnumerable(this, groups); } public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups) where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent where T3 : struct, _IInternalEntityComponent where T4 : struct, _IInternalEntityComponent { return new GroupsEnumerable(this, groups); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public EGIDMapper QueryMappedEntities(ExclusiveGroupStruct groupStructId) where T : struct, _IInternalEntityComponent { if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false) throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName()); return (typeSafeDictionary as ITypeSafeDictionary).ToEGIDMapper(groupStructId); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryQueryMappedEntities(ExclusiveGroupStruct groupStructId, out EGIDMapper mapper) where T : struct, _IInternalEntityComponent { mapper = default; if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false || typeSafeDictionary.count == 0) return false; mapper = (typeSafeDictionary as ITypeSafeDictionary).ToEGIDMapper(groupStructId); return true; } /// /// determine if component with specific ID exists in group /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(EGID entityGID) where T : struct, _IInternalEntityComponent { if (SafeQueryEntityDictionary(entityGID.groupID, out var casted) == false) return false; return casted != null && casted.ContainsKey(entityGID.entityID); } /// /// determine if component with specific ID exists in group /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(uint id, ExclusiveGroupStruct group) where T : struct, _IInternalEntityComponent { if (SafeQueryEntityDictionary(group, out var casted) == false) return false; return casted != null && casted.ContainsKey(id); } /// /// determine if group exists and is not empty /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid) { if (groupEntityComponentsDB.TryGetValue(gid, out FasterDictionary group) == true) { return group.count > 0; } return false; } /// /// determine if entities we specific components are found in group /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasAny(ExclusiveGroupStruct groupStruct) where T : struct, _IInternalEntityComponent { return Count(groupStruct) > 0; } /// /// count the number of components in a group /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Count(ExclusiveGroupStruct groupStruct) where T : struct, _IInternalEntityComponent { if (SafeQueryEntityDictionary(groupStruct, out var typeSafeDictionary) == false) return 0; return (int)typeSafeDictionary.count; } public bool FoundInGroups() where T : struct, _IInternalEntityComponent { return groupsPerComponent.ContainsKey(ComponentTypeID.id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] bool SafeQueryEntityDictionary(FasterDictionary entitiesInGroupPerType, out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent { if (entitiesInGroupPerType.TryGetValue(ComponentTypeID.id, out var safeDictionary) == false) { typeSafeDictionary = default; return false; } //return the indexes entities if they exist typeSafeDictionary = safeDictionary; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool SafeQueryEntityDictionary(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent { ITypeSafeDictionary safeDictionary; bool ret; //search for the group if (groupEntityComponentsDB.TryGetValue(group, out FasterDictionary entitiesInGroupPerType) == false) { safeDictionary = null; ret = false; } else { ret = entitiesInGroupPerType.TryGetValue(ComponentTypeID.id, out safeDictionary); } //search for the indexed entities in the group if (ret == false) { typeSafeDictionary = default; return false; } //return the indexes entities if they exist typeSafeDictionary = safeDictionary; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void QueryOrCreateEntityDictionary(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent { //search for the group FasterDictionary entitiesInGroupPerType = groupEntityComponentsDB.GetOrAdd(group, () => new FasterDictionary()); typeSafeDictionary = entitiesInGroupPerType.GetOrAdd(ComponentTypeID.id, () => TypeSafeDictionaryFactory.Create(0)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool UnsafeQueryEntityDictionary(ExclusiveGroupStruct groupID, ComponentID id, out ITypeSafeDictionary typeSafeDictionary) { //search for the group if (groupEntityComponentsDB.TryGetValue(groupID, out FasterDictionary entitiesInGroupPerType) == false) { typeSafeDictionary = null; return false; } //search for the indexed entities in the group return entitiesInGroupPerType.TryGetValue(id, out typeSafeDictionary); } [MethodImpl(MethodImplOptions.AggressiveInlining)] EntityCollection InternalQueryEntities(FasterDictionary entitiesInGroupPerType) where T : struct, _IInternalEntityComponent { uint count = 0; IBuffer buffer; IEntityIDs ids = default; if (SafeQueryEntityDictionary(entitiesInGroupPerType, out var typeSafeDictionary) == false) buffer = default; else { ITypeSafeDictionary safeDictionary = (typeSafeDictionary as ITypeSafeDictionary); buffer = safeDictionary.GetValues(out count); ids = safeDictionary.entityIDs; } return new EntityCollection(buffer, ids, count); } static readonly FasterDictionary _emptyDictionary = new FasterDictionary(); readonly EnginesRoot _enginesRoot; EntitiesStreams _entityStream => _enginesRoot._entityStreams; //grouped set of entity components, this is the standard way to handle entity components are grouped per //group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of //values directly, that can be iterated over, so that is possible to iterate over all the entity components of //a specific type inside a specific group. FasterDictionary> groupEntityComponentsDB => _enginesRoot._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 // >> FasterDictionary> groupsPerComponent => _enginesRoot._groupsPerEntity; EnginesRoot.EntityReferenceMap _entityReferencesMap; } }