using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.PoolsCore; using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #if ENABLE_IL2CPP using Unity.IL2CPP.CompilerServices; #endif namespace DCFApixels.DragonECS { /// Standard component public interface IEcsComponent : IEcsMember { } #if ENABLE_IL2CPP [Il2CppSetOption (Option.NullChecks, false)] #endif [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.FRAMEWORK_NAME)] /// Pool for IEcsComponent components public sealed class EcsPool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack where T : struct, IEcsComponent { private EcsWorld _source; private int _componentTypeID; private EcsMaskChunck _maskBit; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense private int _itemsCount = 0; private int[] _recycledItems; private int _recycledItemsCount = 0; private IEcsComponentLifecycle _componentLifecycleHandler = EcsComponentResetHandler.instance; private bool _isHasComponentLifecycleHandler = EcsComponentResetHandler.isHasHandler; private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; private bool _isHasComponentCopyHandler = EcsComponentCopyHandler.isHasHandler; #if !DISABLE_POOLS_EVENTS private List _listeners = new List(); private int _listenersCachedCount = 0; #endif private EcsWorld.PoolsMediator _mediator; #region Properites public int Count { get { return _itemsCount; } } public int Capacity { get { return _items.Length; } } public int ComponentTypeID { get { return _componentTypeID; } } public Type ComponentType { get { return typeof(T); } } public EcsWorld World { get { return _source; } } public bool IsReadOnly { get { return false; } } #endregion #region Methods public ref T Add(int entityID) { 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); } } _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); EnableComponent(ref _items[itemIndex]); #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnAddAndGet(entityID, _listenersCachedCount); #endif return ref _items[itemIndex]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get(int entityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(entityID)) { EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); } #endif #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnGet(entityID, _listenersCachedCount); #endif return ref _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]]; } public ref T TryAddOrGet(int entityID) { ref int itemIndex = ref _mapping[entityID]; if (itemIndex <= 0) { if (_recycledItemsCount > 0) { itemIndex = _recycledItems[--_recycledItemsCount]; _itemsCount++; } else { itemIndex = ++_itemsCount; if (itemIndex >= _items.Length) { Array.Resize(ref _items, _items.Length << 1); } } _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); EnableComponent(ref _items[itemIndex]); #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnAdd(entityID, _listenersCachedCount); #endif } #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnGet(entityID, _listenersCachedCount); #endif return ref _items[itemIndex]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Has(int entityID) { return _mapping[entityID] > 0; } public void Del(int entityID) { ref int itemIndex = ref _mapping[entityID]; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (itemIndex <= 0) { EcsPoolThrowHalper.ThrowNotHaveComponent(entityID); } #endif DisableComponent(ref _items[itemIndex]); if (_recycledItemsCount >= _recycledItems.Length) { Array.Resize(ref _recycledItems, _recycledItems.Length << 1); } _recycledItems[_recycledItemsCount++] = itemIndex; itemIndex = 0; _itemsCount--; _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnDel(entityID, _listenersCachedCount); #endif } 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 CopyComponent(ref Get(fromEntityID), ref TryAddOrGet(toEntityID)); } public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) { #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (!Has(fromEntityID)) { EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID); } #endif CopyComponent(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID)); } public void ClearAll() { var span = _source.Where(out SingleAspect> _); _itemsCount = 0; _recycledItemsCount = 0; foreach (var entityID in span) { ref int itemIndex = ref _mapping[entityID]; DisableComponent(ref _items[itemIndex]); itemIndex = 0; _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnDel(entityID, _listenersCachedCount); #endif } } #endregion #region Callbacks void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { _source = world; _mediator = mediator; _componentTypeID = componentTypeID; _maskBit = EcsMaskChunck.FromID(componentTypeID); _mapping = new int[world.Capacity]; _items = new T[ArrayUtility.NormalizeSizeToPowerOfTwo(world.Configs.GetWorldConfigOrDefault().PoolComponentsCapacity)]; _recycledItems = new int[world.Configs.GetWorldConfigOrDefault().PoolRecycledComponentsCapacity]; } void IEcsPoolImplementation.OnWorldResize(int newSize) { Array.Resize(ref _mapping, newSize); } void IEcsPoolImplementation.OnWorldDestroy() { } void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) { if (_itemsCount <= 0) { return; } foreach (var entityID in buffer) { TryDel(entityID); } } #endregion #region Other void IEcsPool.AddRaw(int entityID, object dataRaw) { Add(entityID) = dataRaw == null ? default : (T)dataRaw; } object IEcsReadonlyPool.GetRaw(int entityID) { return Get(entityID); } void IEcsPool.SetRaw(int entityID, object dataRaw) { Get(entityID) = dataRaw == null ? default : (T)dataRaw; } #endregion #region Listeners #if !DISABLE_POOLS_EVENTS public void AddListener(IEcsPoolEventListener listener) { if (listener == null) { EcsPoolThrowHalper.ThrowNullListener(); } _listeners.Add(listener); _listenersCachedCount++; } public void RemoveListener(IEcsPoolEventListener listener) { if (listener == null) { EcsPoolThrowHalper.ThrowNullListener(); } if (_listeners.Remove(listener)) { _listenersCachedCount--; } } #endif #endregion #region Enable/Disable/Copy [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnableComponent(ref T component) { if (_isHasComponentLifecycleHandler) { _componentLifecycleHandler.Enable(ref component); } else { component = default; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DisableComponent(ref T component) { if (_isHasComponentLifecycleHandler) { _componentLifecycleHandler.Disable(ref component); } else { component = default; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CopyComponent(ref T from, ref T to) { if (_isHasComponentCopyHandler) { _componentCopyHandler.Copy(ref from, ref to); } else { to = from; } } #endregion #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion #region MarkersConverter public static implicit operator EcsPool(IncludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsPool(ExcludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsPool(OptionalMarker a) { return a.GetInstance>(); } #endregion } public static class EcsPoolExt { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool GetPool(this EcsWorld self) where TComponent : struct, IEcsComponent { return self.GetPoolInstance>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool GetPoolUnchecked(this EcsWorld self) where TComponent : struct, IEcsComponent { return self.GetPoolInstanceUnchecked>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Include(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.IncludePool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Exclude(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.ExcludePool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Optional(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.OptionalPool>(); } } }