using DCFApixels.DragonECS.Internal; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { public interface IEcsWorld : IEcsReadonlyTable { #region Properties //private float _timeScale;//TODO реализовать собсвенныйтайм склей для разных миров public int ID { get; } public EcsPipeline Pipeline { get; } public int EntitesCount { get; } public int EntitesCapacity { get; } #endregion #region Entities public TArhetype Entities(out TArhetype entities) where TArhetype : IEcsQuery; public ent NewEntity(); public void DelEntity(ent entity); public bool EntityIsAlive(int entityID, short gen); public ent GetEntity(int entityID); public void Destroy(); #endregion } public abstract class EcsWorld { internal static IEcsWorld[] Worlds = new IEcsWorld[8]; private static IntDispenser _worldIdDispenser = new IntDispenser(0); public readonly short id; protected EcsWorld(bool isIndexed) { if(isIndexed == true) { id = (short)_worldIdDispenser.GetFree(); if (id >= Worlds.Length) Array.Resize(ref Worlds, Worlds.Length << 1); Worlds[id] = (IEcsWorld)this; } else { id = -1; } } protected void Realeze() { Worlds[id] = null; _worldIdDispenser.Release(id); } } public abstract class EcsWorld : EcsWorld, IEcsWorld where TWorldArchetype : EcsWorld { private IntDispenser _entityDispenser; private int[] _denseEntities; private int _entitiesCount; private short[] _gens; //старший бит указывает на то жива ли сущьность. //private short[] _componentCounts; //TODO private IEcsPool[] _pools; private EcsNullPool _nullPool; private List[] _filtersByIncludedComponents; private List[] _filtersByExcludedComponents; private IEcsQuery[] _archetypes; private EcsPipeline _pipeline; private List _groups; #region RunnersCache private PoolRunnres _poolRunnres; private IEcsEntityCreate _entityCreate; private IEcsEntityDestroy _entityDestry; #endregion #region GetterMethods public ReadOnlySpan GetAllPools() => new ReadOnlySpan(_pools); public int GetComponentID() => ComponentType.uniqueID; #endregion #region Internal Properties int IEcsReadonlyTable.Count => _entitiesCount; int IEcsReadonlyTable.Capacity => _denseEntities.Length; #endregion #region Properties public Type ArchetypeType => typeof(TWorldArchetype); public int ID => id; public EcsPipeline Pipeline => _pipeline; public int EntitesCount => _entitiesCount; public int EntitesCapacity => _denseEntities.Length; #endregion #region Constructors public EcsWorld(EcsPipeline pipline = null) : base(true) { _pipeline = pipline ?? EcsPipeline.Empty; if (!_pipeline.IsInit) pipline.Init(); _entityDispenser = new IntDispenser(0); _nullPool = EcsNullPool.instance; _pools = new IEcsPool[512]; FillArray(_pools, _nullPool); _gens = new short[512]; _archetypes = new EcsQuery[EntityArhetype.capacity]; _groups = new List(128); _denseEntities = new int[512]; _filtersByIncludedComponents = new List[16]; _filtersByExcludedComponents = new List[16]; _poolRunnres = new PoolRunnres(_pipeline); _entityCreate = _pipeline.GetRunner(); _entityDestry = _pipeline.GetRunner(); _pipeline.GetRunner>().Inject((TWorldArchetype)this); _pipeline.GetRunner>().Inject(this); _pipeline.GetRunner().OnWorldCreate(this); } #endregion #region GetPool public EcsPool GetPool() where T : struct { int uniqueID = ComponentType.uniqueID; if (uniqueID >= _pools.Length) { int oldCapacity = _pools.Length; Array.Resize(ref _pools, ComponentType.Capacity); FillArray(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); Array.Resize(ref _filtersByIncludedComponents, ComponentType.Capacity); Array.Resize(ref _filtersByExcludedComponents, ComponentType.Capacity); } if (_pools[uniqueID] == _nullPool) { _pools[uniqueID] = new EcsPool(this, ComponentType.uniqueID, 512, _poolRunnres); } return (EcsPool)_pools[uniqueID]; } public EcsPool UncheckedGetPool() where T : struct { return (EcsPool)_pools[ComponentType.uniqueID]; } #endregion #region Entities public TEntityArhetype Entities(out TEntityArhetype entities) where TEntityArhetype : IEcsQuery { int uniqueID = EntityArhetype.uniqueID; if (_archetypes.Length < EntityArhetype.capacity) Array.Resize(ref _archetypes, EntityArhetype.capacity); if (_archetypes[uniqueID] == null) { EcsQuery.Builder builder = new EcsQuery.Builder(this); _archetypes[uniqueID] = (TEntityArhetype)Activator.CreateInstance(typeof(TEntityArhetype), builder); builder.End(out EcsQueryMask mask); var filter = (EcsQueryBase)_archetypes[uniqueID]; ((EcsQuery)_archetypes[uniqueID]).group = new EcsGroup(this); ((EcsQuery)_archetypes[uniqueID]).mask = mask; for (int i = 0; i < mask.IncCount; i++) { int componentID = mask.Inc[i]; var list = _filtersByIncludedComponents[componentID]; if (list == null) { list = new List(8); _filtersByIncludedComponents[componentID] = list; } list.Add(filter); } for (int i = 0; i < mask.ExcCount; i++) { int componentID = mask.Exc[i]; var list = _filtersByExcludedComponents[componentID]; if (list == null) { list = new List(8); _filtersByExcludedComponents[componentID] = list; } list.Add(filter); } // scan exist entities for compatibility with new filter. for (int i = 0; i < _entitiesCount && _entitiesCount <= _denseEntities.Length; i++) { int entity = _denseEntities[i]; if (IsMaskCompatible(mask.Inc, mask.Exc, entity)) filter.AddEntity(entity); } } entities = (TEntityArhetype)_archetypes[uniqueID]; return entities; } private bool IsMaskCompatible(int[] inc, int[] exc, int entity) { for (int i = 0, iMax = inc.Length; i < iMax; i++) { if (!_pools[inc[i]].Has(entity)) return false; } for (int i = 0, iMax = exc.Length; i < iMax; i++) { if (_pools[exc[i]].Has(entity)) return false; } return true; } #endregion #region IsMaskCompatible/IsMaskCompatibleWithout [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMaskCompatible(int entityID) where TInc : struct, IInc { return IsMaskCompatible(EcsMaskMap.GetMask(), entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMaskCompatible(int entityID) where TInc : struct, IInc where TExc : struct, IExc { return IsMaskCompatible(EcsMaskMap.GetMask(), entityID); } public bool IsMaskCompatible(EcsMaskBase mask, int entity) { #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (mask.WorldArchetypeType != typeof(TWorldArchetype)) throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TWorldArchetype)"); #endif for (int i = 0, iMax = mask.IncCount; i < iMax; i++) { if (!_pools[mask.Inc[i]].Has(entity)) return false; } for (int i = 0, iMax = mask.ExcCount; i < iMax; i++) { if (_pools[mask.Exc[i]].Has(entity)) return false; } return true; } public bool IsMaskCompatibleWithout(EcsMaskBase mask, int entity, int otherComponentID) { #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (mask.WorldArchetypeType != typeof(TWorldArchetype)) throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TWorldArchetype)"); #endif for (int i = 0, iMax = mask.IncCount; i < iMax; i++) { int componentID = mask.Inc[i]; if (componentID == otherComponentID || !_pools[componentID].Has(entity)) return false; } for (int i = 0, iMax = mask.ExcCount; i < iMax; i++) { int componentID = mask.Exc[i]; if (componentID != otherComponentID && _pools[componentID].Has(entity)) return false; } return true; } #endregion #region EntityChangedReact void IEcsReadonlyTable.OnEntityComponentAdded(int entityID, int componentID) { var includeList = _filtersByIncludedComponents[componentID]; var excludeList = _filtersByExcludedComponents[componentID]; if (includeList != null) { foreach (var filter in includeList) { if (IsMaskCompatible(filter.mask, entityID)) { filter.AddEntity(entityID); } } } if (excludeList != null) { foreach (var filter in excludeList) { if (IsMaskCompatibleWithout(filter.mask, entityID, componentID)) { filter.RemoveEntity(entityID); } } } //TODO провести стресс тест для варианта выши и закоментированного ниже // if (includeList != null) foreach (var filter in includeList) filter.Add(entityID); // if (excludeList != null) foreach (var filter in excludeList) filter.Remove(entityID); } void IEcsReadonlyTable.OnEntityComponentRemoved(int entityID, int componentID) { var includeList = _filtersByIncludedComponents[componentID]; var excludeList = _filtersByExcludedComponents[componentID]; if (includeList != null) { foreach (var filter in includeList) { if (IsMaskCompatible(filter.mask, entityID)) { filter.RemoveEntity(entityID); } } } if (excludeList != null) { foreach (var filter in excludeList) { if (IsMaskCompatibleWithout(filter.mask, entityID, componentID)) { filter.AddEntity(entityID); } } } //TODO провести стресс тест для варианта выши и закоментированного ниже // if (includeList != null) foreach (var filter in includeList) filter.Remove(entityID); // if (excludeList != null) foreach (var filter in excludeList) filter.Add(entityID); } #endregion #region Entity public ent NewEntity() { int entityID = _entityDispenser.GetFree(); if (_entityDispenser.LastInt >= _denseEntities.Length) Array.Resize(ref _denseEntities, _denseEntities.Length << 1); _denseEntities[_entitiesCount++] = entityID; if (_gens.Length <= entityID) { Array.Resize(ref _gens, _gens.Length << 1); foreach (var item in _groups) item.OnWorldResize(_gens.Length); foreach (var item in _pools) item.OnWorldResize(_gens.Length); } _gens[entityID] |= short.MinValue; ent entity = new ent(entityID, _gens[entityID]++, id); _entityCreate.OnEntityCreate(entity); return entity; } public void DelEntity(ent entity) { _entityDispenser.Release(entity.id); _gens[entity.id] |= short.MinValue; _entitiesCount--; _entityDestry.OnEntityDestroy(entity); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ent GetEntity(int entityID) { return new ent(entityID, _gens[entityID], id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EntityIsAlive(int entityID, short gen) { return _gens[entityID] == gen; } #endregion #region Destroy public void Destroy() { _entityDispenser = null; _denseEntities = null; _gens = null; _pools = null; _nullPool = null; _filtersByIncludedComponents = null; _filtersByExcludedComponents = null; _archetypes = null; Realeze(); } public void DestryWithPipeline() { Destroy(); _pipeline.Destroy(); } #endregion #region Other void IEcsReadonlyTable.RegisterGroup(EcsGroup group) { _groups.Add(group); } #endregion #region Utils internal static class EntityArhetype { public static int increment = 0; public static int capacity = 128; } internal static class EntityArhetype { public static int uniqueID; static EntityArhetype() { uniqueID = EntityArhetype.increment++; if (EntityArhetype.increment > EntityArhetype.capacity) EntityArhetype.capacity <<= 1; } } internal static class ComponentType { internal static int increment = 1; internal static int Capacity { get => types.Length; } internal static Type[] types = new Type[64]; } internal static class ComponentType { internal static int uniqueID; static ComponentType() { uniqueID = ComponentType.increment++; #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (ComponentType.increment + 1 > ushort.MaxValue) { throw new EcsFrameworkException($"No more room for new component for this {typeof(TWorldArchetype).FullName} IWorldArchetype"); } #endif if (uniqueID >= ComponentType.types.Length) { Array.Resize(ref ComponentType.types, ComponentType.types.Length << 1); } ComponentType.types[uniqueID] = typeof(T); } } private void FillArray(T[] array, T value, int startIndex = 0, int length = -1) { if (length < 0) { length = array.Length; } else { length = startIndex + length; } for (int i = startIndex; i < length; i++) { array[i] = value; } } #endregion } [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 24)] internal readonly struct PoolRunnres { public readonly IEcsComponentAdd add; public readonly IEcsComponentWrite write; public readonly IEcsComponentDel del; public PoolRunnres(EcsPipeline pipeline) { add = pipeline.GetRunner(); write = pipeline.GetRunner(); del = pipeline.GetRunner(); } } }