using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { /// Pool for IEcsComponent components public sealed class EcsHybridPool : IEcsPoolImplementation, IEcsHybridPool, IEnumerable //IEnumerable - IntelliSense hack where T : IEcsHybridComponent { private EcsWorld _source; private int _componentID; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense private int[] _entities; private int _itemsCount; private int[] _recycledItems; private int _recycledItemsCount; private List _listeners = new List(); #region Properites public int Count => _itemsCount; public int Capacity => _items.Length; public int ComponentID => _componentID; public Type ComponentType => typeof(T); public EcsWorld World => _source; #endregion #region Init void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { _source = world; _componentID = componentID; const int capacity = 512; _mapping = new int[world.Capacity]; _recycledItems = new int[128]; _recycledItemsCount = 0; _items = new T[capacity]; _entities = new int[capacity]; _itemsCount = 0; } #endregion #region Methods public void Add(int entityID, T component) { ref int itemIndex = ref _mapping[entityID]; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (itemIndex > 0) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID); #endif if (_recycledItemsCount > 0) { itemIndex = _recycledItems[--_recycledItemsCount]; _itemsCount++; } else { itemIndex = ++_itemsCount; if (itemIndex >= _items.Length) { Array.Resize(ref _items, _items.Length << 1); Array.Resize(ref _entities, _items.Length); } } this.IncrementEntityComponentCount(entityID); _listeners.InvokeOnAdd(entityID); component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; _entities[itemIndex] = entityID; } public void Set(int entityID, T component) { ref int itemIndex = ref _mapping[entityID]; if(itemIndex <= 0) {//null if (_recycledItemsCount > 0) { itemIndex = _recycledItems[--_recycledItemsCount]; _itemsCount++; } else { itemIndex = ++_itemsCount; if (itemIndex >= _items.Length) { Array.Resize(ref _items, _items.Length << 1); Array.Resize(ref _entities, _items.Length); } } this.IncrementEntityComponentCount(entityID); } else {//not null _listeners.InvokeOnDel(entityID); _items[itemIndex].OnDelFromPool(_source.GetEntityLong(entityID)); } _listeners.InvokeOnAdd(entityID); component.OnAddToPool(_source.GetEntityLong(entityID)); _items[itemIndex] = component; _entities[itemIndex] = entityID; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Get(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif _listeners.InvokeOnGet(entityID); return _items[_mapping[entityID]]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Read(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif return ref _items[_mapping[entityID]]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Has(int entityID) { return _mapping[entityID] > 0; } public void Del(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); #endif ref int itemIndex = ref _mapping[entityID]; T component = _items[itemIndex]; component.OnDelFromPool(_source.GetEntityLong(entityID)); if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = itemIndex; _mapping[entityID] = 0; _entities[itemIndex] = 0; _itemsCount--; this.DecrementEntityComponentCount(entityID); _listeners.InvokeOnDel(entityID); } public void TryDel(int entityID) { if (Has(entityID)) Del(entityID); } public void Copy(int fromEntityID, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); #endif Set(toEntityID, Get(fromEntityID)); } public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); #endif toWorld.GetPool().Set(toEntityID, Get(fromEntityID)); } public void ClearNotAliveComponents() { for (int i = _itemsCount - 1; i >= 0; i--) { if (!_items[i].IsAlive) Del(_entities[i]); } } #endregion #region Callbacks void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); } void IEcsPoolImplementation.OnWorldDestroy() { } void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { foreach (var entityID in buffer) TryDel(entityID); } #endregion #region Other void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID, (T)dataRaw); object IEcsPool.GetRaw(int entityID) => Read(entityID); void IEcsPool.SetRaw(int entityID, object dataRaw) => Set(entityID, (T)dataRaw); #endregion #region Listeners public void AddListener(IEcsPoolEventListener listener) { if (listener == null) { throw new ArgumentNullException("listener is null"); } _listeners.Add(listener); } public void RemoveListener(IEcsPoolEventListener listener) { if (listener == null) { throw new ArgumentNullException("listener is null"); } _listeners.Remove(listener); } #endregion #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); #endregion } /// Hybrid component public interface IEcsHybridComponent { bool IsAlive { get; } void OnAddToPool(entlong entity); void OnDelFromPool(entlong entity); } public static class EcsHybridPoolExt { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool GetPool(this EcsWorld self) where T : IEcsHybridComponent { return self.GetPool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool UncheckedGetPool(this EcsWorld self) where T : IEcsHybridComponent { return self.UncheckedGetPool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool Include(this EcsAspectBuilderBase self) where T : IEcsHybridComponent { return self.Include>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool Exclude(this EcsAspectBuilderBase self) where T : IEcsHybridComponent { return self.Exclude>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsHybridPool Optional(this EcsAspectBuilderBase self) where T : IEcsHybridComponent { return self.Optional>(); } } }