#if DEBUG && !PROFILE_SVELTO #define ENABLE_DEBUG_FUNC #endif using System; using System.Runtime.CompilerServices; using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { public partial class EntitiesDB { internal EntitiesDB(EnginesRoot enginesRoot, EnginesRoot.LocatorMap entityReferencesMap) { _enginesRoot = enginesRoot; _entityReferencesMap = entityReferencesMap; } EntityCollection InternalQueryEntities (FasterDictionary entitiesInGroupPerType) where T : struct, IEntityComponent { uint count = 0; IBuffer buffer; if (SafeQueryEntityDictionary(out var typeSafeDictionary, entitiesInGroupPerType) == false) buffer = RetrieveEmptyEntityComponentArray(); else { var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary); buffer = safeDictionary.GetValues(out count); } return new EntityCollection(buffer, count); } /// /// 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, IEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false) { var buffer = RetrieveEmptyEntityComponentArray(); return new EntityCollection(buffer, 0); } return InternalQueryEntities(entitiesInGroupPerType); } public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) { return new EntityCollection(new EntityCollection(RetrieveEmptyEntityComponentArray(), 0) , new EntityCollection( RetrieveEmptyEntityComponentArray(), 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()))); #endif return new EntityCollection(T1entities, T2entities); } public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) { return new EntityCollection( new EntityCollection(RetrieveEmptyEntityComponentArray(), 0) , new EntityCollection(RetrieveEmptyEntityComponentArray(), 0) , new EntityCollection(RetrieveEmptyEntityComponentArray(), 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))); #endif return new EntityCollection(T1entities, T2entities, T3entities); } public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent { if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) { return new EntityCollection( new EntityCollection(RetrieveEmptyEntityComponentArray(), 0) , new EntityCollection(RetrieveEmptyEntityComponentArray(), 0) , new EntityCollection(RetrieveEmptyEntityComponentArray(), 0) , new EntityCollection(RetrieveEmptyEntityComponentArray(), 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))); #endif return new EntityCollection(T1entities, T2entities, T3entities, T4entities); } public GroupsEnumerable QueryEntities (in LocalFasterReadOnlyList groups) where T : struct, IEntityComponent { return new GroupsEnumerable(this, groups); } /// /// Note: Remember that EntityViewComponents are always put at the end of the generic parameters tuple. /// It won't compile otherwise /// /// public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent { return new GroupsEnumerable(this, groups); } public GroupsEnumerable QueryEntities (in LocalFasterReadOnlyList groups) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent { return new GroupsEnumerable(this, groups); } public GroupsEnumerable QueryEntities (in LocalFasterReadOnlyList groups) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent { return new GroupsEnumerable(this, groups); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public EGIDMapper QueryMappedEntities(ExclusiveGroupStruct groupStructId) where T : struct, IEntityComponent { 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, IEntityComponent { mapper = default; if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false || typeSafeDictionary.count == 0) return false; mapper = (typeSafeDictionary as ITypeSafeDictionary).ToEGIDMapper(groupStructId); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(EGID entityGID) where T : struct, IEntityComponent { if (SafeQueryEntityDictionary(entityGID.groupID, out var casted) == false) return false; return casted != null && casted.ContainsKey(entityGID.entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent { if (SafeQueryEntityDictionary(group, out var casted) == false) return false; return casted != null && casted.ContainsKey(id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid) { if (groupEntityComponentsDB.TryGetValue( gid, out FasterDictionary group) == true) { return group.count > 0; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasAny(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent { return Count(groupStruct) > 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Count(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent { if (SafeQueryEntityDictionary(groupStruct, out var typeSafeDictionary) == false) return 0; return (int) typeSafeDictionary.count; } public bool FoundInGroups() where T1 : IEntityComponent { return groupsPerEntity.ContainsKey(TypeRefWrapper.wrapper); } public bool IsDisposing => _enginesRoot._isDisposing; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool SafeQueryEntityDictionary (out ITypeSafeDictionary typeSafeDictionary , FasterDictionary entitiesInGroupPerType) where T : IEntityComponent { if (entitiesInGroupPerType.TryGetValue(new RefWrapperType(TypeCache.type), 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 : IEntityComponent { if (UnsafeQueryEntityDictionary(group, TypeCache.type, out var safeDictionary) == false) { typeSafeDictionary = default; return false; } //return the indexes entities if they exist typeSafeDictionary = safeDictionary; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool UnsafeQueryEntityDictionary (ExclusiveGroupStruct group, Type type, out ITypeSafeDictionary typeSafeDictionary) { //search for the group if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false) { typeSafeDictionary = null; return false; } //search for the indexed entities in the group return entitiesInGroupPerType.TryGetValue(new RefWrapperType(type), out typeSafeDictionary); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static IBuffer RetrieveEmptyEntityComponentArray() where T : struct, IEntityComponent { return EmptyList.emptyArray; } static class EmptyList where T : struct, IEntityComponent { internal static readonly IBuffer emptyArray; static EmptyList() { if (ComponentBuilder.IS_ENTITY_VIEW_COMPONENT) { MB b = default; emptyArray = b; } else { NB b = default; emptyArray = b; } } } 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> groupsPerEntity => _enginesRoot._groupsPerEntity; EnginesRoot.LocatorMap _entityReferencesMap; } }