using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { public abstract partial class EcsWorld { public readonly short id; private bool _isDestroyed; private IntDispenser _entityDispenser; private int _entitiesCount; private int _entitesCapacity; private short[] _gens; //старший бит указывает на то жива ли сущность private short[] _componentCounts; private EcsGroup _allEntites; private int[] _delEntBuffer; private int _delEntBufferCount; private int _delEntBufferMinCount; private int _freeSpace; private bool _isEnableReleaseDelEntBuffer = true; private List> _groups = new List>(); private Stack _groupsPool = new Stack(64); private List _listeners = new List(); private List _entityListeners = new List(); internal int[][] _entitiesComponentMasks; private readonly PoolsMediator _poolsMediator; #region Properties public bool IsDestroyed => _isDestroyed; public int Count => _entitiesCount; public int Capacity => _entitesCapacity; //_denseEntities.Length; public int DelEntBufferCount => _delEntBufferCount; public bool IsEnableReleaseDelEntBuffer => _isEnableReleaseDelEntBuffer; public EcsReadonlyGroup Entities => _allEntites.Readonly; public ReadOnlySpan AllPools => _pools;// new ReadOnlySpan(pools, 0, _poolsCount); #endregion #region Constructors/Destroy public EcsWorld() : this(true) { } internal EcsWorld(bool isIndexable) { _poolsMediator = new PoolsMediator(this); _entitesCapacity = 512; if (isIndexable) { id = (short)_worldIdDispenser.UseFree(); if (id >= Worlds.Length) Array.Resize(ref Worlds, Worlds.Length << 1); Worlds[id] = this; } _entityDispenser = new IntDispenser(0); _pools = new IEcsPoolImplementation[512]; ArrayUtility.Fill(_pools, _nullPool); _gens = new short[_entitesCapacity]; _componentCounts = new short[_entitesCapacity]; ArrayUtility.Fill(_gens, DEATH_GEN_BIT); _delEntBufferCount = 0; //_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET]; _delEntBuffer = new int[_entitesCapacity]; _entitiesComponentMasks = new int[_entitesCapacity][]; for (int i = 0; i < _entitesCapacity; i++) _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); _allEntites = GetFreeGroup(); } public void Destroy() { _entityDispenser = null; _gens = null; _pools = null; _nullPool = null; Worlds[id] = null; ReleaseData(id); _worldIdDispenser.Release(id); _isDestroyed = true; _poolIds = null; _componentIds = null; } #endregion #region Getters #if UNITY_2020_3_OR_NEWER [UnityEngine.Scripting.Preserve] #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public TAspect GetAspect() where TAspect : EcsAspect { return Get>().instance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TExecutor GetExecutor() where TExecutor : EcsQueryExecutor, new() { return Get>().instance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get() where T : struct { return ref WorldComponentPool.GetForWorld(id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T GetUnchecked() where T : struct { return ref WorldComponentPool.GetForWorldUnchecked(id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Get(int worldID) where T : struct { return ref WorldComponentPool.GetForWorld(worldID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetUnchecked(int worldID) where T : struct { return ref WorldComponentPool.GetForWorldUnchecked(worldID); } #endregion #region Where Query public EcsReadonlyGroup WhereToGroupFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { if(_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.ExecuteFor(span); } public EcsReadonlyGroup WhereToGroupFor(EcsSpan span) where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } return GetExecutor>().ExecuteFor(span); } public EcsReadonlyGroup WhereToGroup(out TAspect aspect) where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.Execute(); } public EcsReadonlyGroup WhereToGroup() where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } return GetExecutor>().Execute(); } public EcsSpan WhereFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.ExecuteFor(span); } public EcsSpan WhereFor(EcsSpan span) where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } return GetExecutor>().ExecuteFor(span); } public EcsSpan Where(out TAspect aspect) where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } var executor = GetExecutor>(); aspect = executor.Aspect; return executor.Execute(); } public EcsSpan Where() where TAspect : EcsAspect { if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } return GetExecutor>().Execute(); } #endregion #region Entity public int NewEntity() { //if (_isEnableReleaseDelEntBuffer && _freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount) // ReleaseDelEntityBufferAll(); int entityID = _entityDispenser.GetFree(); _freeSpace--; _entitiesCount++; if (_gens.Length <= entityID) Upsize(); _gens[entityID] &= GEN_BITS; _allEntites.Add(entityID); _entityListeners.InvokeOnNewEntity(entityID); return entityID; } public entlong NewEntityLong() { int e = NewEntity(); return GetEntityLong(e); } public void DelEntity(int entityID) { _allEntites.Remove(entityID); _delEntBuffer[_delEntBufferCount++] = entityID; _gens[entityID] |= DEATH_GEN_BIT; _entitiesCount--; _entityListeners.InvokeOnDelEntity(entityID); //if (_delEntBufferCount >= _delEntBuffer.Length) // ReleaseDelEntityBufferAll(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe entlong GetEntityLong(int entityID) { long x = (long)id << 48 | (long)_gens[entityID] << 32 | (long)entityID; return *(entlong*)&x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsUsed(int entityID) => _gens[entityID] >= 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] public short GetGen(int entityID) => _gens[entityID]; [MethodImpl(MethodImplOptions.AggressiveInlining)] public short GetComponentsCount(int entityID) => _componentCounts[entityID]; public bool IsMatchesMask(EcsMask mask, int entityID) { #if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS if (mask.worldID != id) throw new EcsFrameworkException("The types of the target world of the mask and this world are different."); #endif for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++) { if (!_pools[mask.inc[i]].Has(entityID)) return false; } for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++) { if (_pools[mask.exc[i]].Has(entityID)) return false; } return true; } public void DeleteEmptyEntites() { foreach (var e in _allEntites) { if (_componentCounts[e] <= 0) { DelEntity(e); } } } #region Copy/Clone public void CopyEntity(int fromEntityID, int toEntityID) { foreach (var pool in _pools) { if (pool.Has(fromEntityID)) pool.Copy(fromEntityID, toEntityID); } } public void CopyEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) { foreach (var pool in _pools) { if (pool.Has(fromEntityID)) pool.Copy(fromEntityID, toWorld, toEntityID); } } public int CloneEntity(int fromEntityID) { int newEntity = NewEntity(); CopyEntity(fromEntityID, newEntity); return newEntity; } public int CloneEntity(int fromEntityID, EcsWorld toWorld) { int newEntity = NewEntity(); CopyEntity(fromEntityID, toWorld, newEntity); return newEntity; } public void CloneEntity(int fromEntityID, int toEntityID) { CopyEntity(fromEntityID, toEntityID); foreach (var pool in _pools) { if (!pool.Has(fromEntityID) && pool.Has(toEntityID)) pool.Del(toEntityID); } } //public void CloneEntity(int fromEntityID, EcsWorld toWorld, int toEntityID) #endregion #region Pools mediation [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { _componentCounts[entityID]++; _entitiesComponentMasks[entityID][maskBit.chankIndex] |= maskBit.mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { var count = --_componentCounts[entityID]; _entitiesComponentMasks[entityID][maskBit.chankIndex] &= ~maskBit.mask; if (count == 0 && _allEntites.Has(entityID)) DelEntity(entityID); #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (count < 0) Throw.World_InvalidIncrementComponentsBalance(); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool HasEntityComponent(int entityID, EcsMaskBit maskBit) { return (_entitiesComponentMasks[entityID][maskBit.chankIndex] & maskBit.mask) != maskBit.mask; } #endregion #endregion #region DelEntBuffer public AutoReleaseDelEntBufferLonkUnloker DisableAutoReleaseDelEntBuffer() { _isEnableReleaseDelEntBuffer = false; return new AutoReleaseDelEntBufferLonkUnloker(this); } public void EnableAutoReleaseDelEntBuffer() { _isEnableReleaseDelEntBuffer = true; } public readonly struct AutoReleaseDelEntBufferLonkUnloker : IDisposable { private readonly EcsWorld _source; public AutoReleaseDelEntBufferLonkUnloker(EcsWorld source) { _source = source; } public void Dispose() { _source.EnableAutoReleaseDelEntBuffer(); } } public void ReleaseDelEntityBufferAll() { ReleaseDelEntityBuffer(-1); } public void ReleaseDelEntityBuffer(int count) { if (_delEntBufferCount <= 0) { return; } if (count < 0) { count = _delEntBufferCount; } else if (count > _delEntBufferCount) { count = _delEntBufferCount; } _delEntBufferCount -= count; ReadOnlySpan buffser = new ReadOnlySpan(_delEntBuffer, _delEntBufferCount, count); for (int i = 0; i < _poolsCount; i++) { _pools[i].OnReleaseDelEntityBuffer(buffser); } _listeners.InvokeOnReleaseDelEntityBuffer(buffser); for (int i = 0; i < buffser.Length; i++) { _entityDispenser.Release(buffser[i]); } _freeSpace += count;// _entitesCapacity - _entitiesCount; } #endregion #region Upsize [MethodImpl(MethodImplOptions.NoInlining)] private void Upsize() { Array.Resize(ref _gens, _gens.Length << 1); Array.Resize(ref _componentCounts, _gens.Length); Array.Resize(ref _delEntBuffer, _gens.Length); Array.Resize(ref _entitiesComponentMasks, _gens.Length); for (int i = _entitesCapacity; i < _gens.Length; i++) _entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1]; _delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE); ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity); _entitesCapacity = _gens.Length; for (int i = 0; i < _groups.Count; i++) { if (_groups[i].TryGetTarget(out EcsGroup group)) { group.OnWorldResize(_gens.Length); } else { int last = _groups.Count - 1; _groups[i--] = _groups[last]; _groups.RemoveAt(last); } } foreach (var item in _pools) item.OnWorldResize(_gens.Length); _listeners.InvokeOnWorldResize(_gens.Length); } #endregion #region Groups Pool internal void RegisterGroup(EcsGroup group) { _groups.Add(new WeakReference(group)); } internal EcsGroup GetFreeGroup() { EcsGroup result = _groupsPool.Count <= 0 ? new EcsGroup(this) : _groupsPool.Pop(); result._isReleased = false; return result; } internal void ReleaseGroup(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS if (group.World != this) Throw.World_GroupDoesNotBelongWorld(); #endif group._isReleased = true; group.Clear(); _groupsPool.Push(group); } #endregion #region Listeners public void AddListener(IEcsWorldEventListener worldEventListener) { _listeners.Add(worldEventListener); } public void RemoveListener(IEcsWorldEventListener worldEventListener) { _listeners.Remove(worldEventListener); } public void AddListener(IEcsEntityEventListener entityEventListener) { _entityListeners.Add(entityEventListener); } public void RemoveListener(IEcsEntityEventListener entityEventListener) { _entityListeners.Remove(entityEventListener); } #endregion #region Debug public void GetComponents(int entityID, List list) { list.Clear(); var itemsCount = GetComponentsCount(entityID); for (var i = 0; i < _poolsCount; i++) { if (_pools[i].Has(entityID)) { itemsCount--; list.Add(_pools[i].GetRaw(entityID)); if (itemsCount <= 0) break; } } } public void GetComponentTypes(int entityID, HashSet typeSet) { typeSet.Clear(); var itemsCount = GetComponentsCount(entityID); for (var i = 0; i < _poolsCount; i++) { if (_pools[i].Has(entityID)) { itemsCount--; typeSet.Add(_pools[i].ComponentType); if (itemsCount <= 0) break; } } } #endregion #region PoolsMediator public readonly struct PoolsMediator { private readonly EcsWorld _world; internal PoolsMediator(EcsWorld world) { if (world == null) { throw new ArgumentNullException(); } if (world._poolsMediator._world != null) { throw new MethodAccessException(); } _world = world; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { _world.RegisterEntityComponent(entityID, componentTypeID, maskBit); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { _world.UnregisterEntityComponent(entityID, componentTypeID, maskBit); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(int entityID, EcsMaskBit maskBit) { return _world.HasEntityComponent(entityID, maskBit); } } #endregion } #region Callbacks Interface public interface IEcsWorldEventListener { void OnWorldResize(int newSize); void OnReleaseDelEntityBuffer(ReadOnlySpan buffer); void OnWorldDestroy(); } public interface IEcsEntityEventListener { void OnNewEntity(int entityID); void OnDelEntity(int entityID); } internal static class WorldEventListExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InvokeOnWorldResize(this List self, int newSize) { for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnWorldResize(newSize); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InvokeOnReleaseDelEntityBuffer(this List self, ReadOnlySpan buffer) { for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnReleaseDelEntityBuffer(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InvokeOnWorldDestroy(this List self) { for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnWorldDestroy(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InvokeOnNewEntity(this List self, int entityID) { for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnNewEntity(entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InvokeOnDelEntity(this List self, int entityID) { for (int i = 0, iMax = self.Count; i < iMax; i++) self[i].OnDelEntity(entityID); } } #endregion #region Extensions public static class IntExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static entlong ToEntityLong(this int self, EcsWorld world) => world.GetEntityLong(self); } #endregion }