#if DISABLE_DEBUG #undef DEBUG #endif using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.PoolsCore; using DCFApixels.DragonECS.UncheckedCore; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; #if ENABLE_IL2CPP using Unity.IL2CPP.CompilerServices; #endif namespace DCFApixels.DragonECS { /// Standard component [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Standard component.")] [MetaID("DragonECS_84D2537C9201D6F6B92FEC1C8883A07A")] public interface IEcsComponent : IEcsComponentMember { } /// Pool for IEcsComponent components #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] #endif [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsComponent components.")] [MetaID("DragonECS_C501547C9201A4B03FC25632E4FAAFD7")] [DebuggerDisplay("Count: {Count} Type: {ComponentType}")] public sealed class EcsPool : IEcsPoolImplementation, IEcsStructPool, IEntityStorage, 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; _items[0] - fake component. private int _itemsCount = 0; private int _capacity = 0; private int[] _sparseEntities; private int[] _denseEntitiesDelayed; private int _denseEntitiesDelayedCount = 0; private bool _isDenseEntitiesDelayedValid = false; private int _recycledCount = 0; private readonly IEcsComponentLifecycle _componentLifecycleHandler = EcsComponentLifecycleHandler.instance; private readonly bool _isHasComponentLifecycleHandler = EcsComponentLifecycleHandler.isHasHandler; private readonly IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; private readonly bool _isHasComponentCopyHandler = EcsComponentCopyHandler.isHasHandler; #if !DRAGONECS_DISABLE_POOLS_EVENTS private StructList _listeners = new StructList(2); private int _listenersCachedCount = 0; #endif private bool _isLocked; 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; } } public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref Get(index); } } #endregion #region Methods public ref T Add(int entityID) { ref int itemIndex = ref _mapping[entityID]; #if DEBUG if (_source.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent(entityID); } if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (itemIndex > 0) { return ref Get(entityID); } if (_isLocked | _source.IsUsed(entityID) == false) { return ref _items[0]; } #endif itemIndex = GetFreeItemIndex(entityID); _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); ref T result = ref _items[itemIndex]; _sparseEntities[itemIndex] = entityID; EnableComponent(ref result); #if !DRAGONECS_DISABLE_POOLS_EVENTS _listeners.InvokeOnAddAndGet(entityID, _listenersCachedCount); #endif #if DRAGONECS_DEEP_DEBUG if (_mediator.GetComponentCount(_componentTypeID) != _itemsCount) { Throw.UndefinedException(); } #endif return ref result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get(int entityID) { #if DEBUG // �� ����� STAB_MODE if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } #endif #if !DRAGONECS_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 // �� ����� STAB_MODE if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } #endif return ref _items[_mapping[entityID]]; } public ref T TryAddOrGet(int entityID) { //if (Has(entityID)) //{ // return ref Get(entityID); //} //else //{ // return ref Add(entityID); //} ref int itemIndex = ref _mapping[entityID]; if (itemIndex <= 0) { //Add block #if DEBUG if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (_isLocked) { return ref _items[0]; } #endif itemIndex = GetFreeItemIndex(entityID); _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _sparseEntities[itemIndex] = entityID; EnableComponent(ref _items[itemIndex]); #if !DRAGONECS_DISABLE_POOLS_EVENTS _listeners.InvokeOnAdd(entityID, _listenersCachedCount); #endif } //Add block end #if !DRAGONECS_DISABLE_POOLS_EVENTS _listeners.InvokeOnGet(entityID, _listenersCachedCount); #endif #if DRAGONECS_DEEP_DEBUG if (_mediator.GetComponentCount(_componentTypeID) != _itemsCount) { Throw.UndefinedException(); } #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 if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (itemIndex <= 0) { return; } if (_isLocked) { return; } #endif DisableComponent(ref _items[itemIndex]); _sparseEntities[itemIndex] = 0; itemIndex = 0; _itemsCount--; _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); _isDenseEntitiesDelayedValid = false; #if !DISABLE_POOLS_EVENTS _listeners.InvokeOnDel(entityID, _listenersCachedCount); #endif #if DRAGONECS_DEEP_DEBUG if (_mediator.GetComponentCount(_componentTypeID) != _itemsCount) { Throw.UndefinedException(); } #endif } public void TryDel(int entityID) { if (Has(entityID)) { Del(entityID); } } public void Copy(int fromEntityID, int toEntityID) { #if DEBUG if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(fromEntityID); } #elif DRAGONECS_STABILITY_MODE if (!Has(fromEntityID)) { return; } #endif CopyComponent(ref Get(fromEntityID), ref TryAddOrGet(toEntityID)); } public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) { #if DEBUG if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(fromEntityID); } #elif DRAGONECS_STABILITY_MODE if (!Has(fromEntityID)) { return; } #endif CopyComponent(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID)); } public void ClearAll() { #if DEBUG if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (_isLocked) { return; } #endif if (_itemsCount <= 0) { return; } _itemsCount = 0; var span = _source.Where(out SingleAspect _); foreach (var entityID in span) { ref int itemIndex = ref _mapping[entityID]; DisableComponent(ref _items[itemIndex]); itemIndex = 0; _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); #if !DRAGONECS_DISABLE_POOLS_EVENTS _listeners.InvokeOnDel(entityID, _listenersCachedCount); #endif } } #endregion #region Callbacks void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { #if DEBUG AllowedInWorldsAttribute.CheckAllows(world); #endif _source = world; _mediator = mediator; _componentTypeID = componentTypeID; _maskBit = EcsMaskChunck.FromID(componentTypeID); _mapping = new int[world.Capacity]; Resize(ArrayUtility.NextPow2(world.Configs.GetWorldConfigOrDefault().PoolComponentsCapacity)); } 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); } } void IEcsPoolImplementation.OnLockedChanged_Debug(bool locked) { _isLocked = locked; } #endregion #region Other void IEcsPool.AddEmpty(int entityID) { Add(entityID); } void IEcsPool.AddRaw(int entityID, object dataRaw) { Add(entityID) = dataRaw == null ? default : (T)dataRaw; } object IEcsReadonlyPool.GetRaw(int entityID) { return Read(entityID); } void IEcsPool.SetRaw(int entityID, object dataRaw) { Get(entityID) = dataRaw == null ? default : (T)dataRaw; } #if DRAGONECS_DEEP_DEBUG private bool _lockToSpan = false; #endif public EcsSpan ToSpan() { #if DRAGONECS_DEEP_DEBUG if (_lockToSpan) { return _source.Entities; } #endif UpdateDenseEntities(); var span = new EcsSpan(_source.ID, _denseEntitiesDelayed, 1, _itemsCount); #if DRAGONECS_DEEP_DEBUG if (UncheckedCoreUtility.CheckSpanValideDebug(span) == false) { Throw.UndefinedException(); } #endif return span; } private void UpdateDenseEntities() { if (_isDenseEntitiesDelayedValid) { return; } _denseEntitiesDelayedCount = 1; _denseEntitiesDelayed[0] = 0; _recycledCount = 0; for (int i = 1, jRight = _itemsCount + 1; _denseEntitiesDelayedCount <= _itemsCount; i++) { if (_sparseEntities[i] == 0) { _denseEntitiesDelayed[jRight] = i; jRight++; _recycledCount++; } else { _denseEntitiesDelayed[_denseEntitiesDelayedCount++] = _sparseEntities[i]; } } #if DRAGONECS_DEEP_DEBUG _lockToSpan = true; if (_denseEntitiesDelayed[0] != 0) { Throw.UndefinedException(); } if (_denseEntitiesDelayedCount != _itemsCount + 1) { Throw.UndefinedException(); } int coreCount = _mediator.GetComponentCount(_componentTypeID); if (coreCount != _itemsCount) { Throw.UndefinedException(); } using (_source.DisableAutoReleaseDelEntBuffer()) using (var group = EcsGroup.New(_source)) { EcsStaticMask.Inc().Build().ToMask(_source).GetIterator().IterateTo(World.Entities, group); var span = new EcsSpan(_source.ID, _denseEntitiesDelayed, 1, _itemsCount); if (group.SetEquals(span) == false) { Throw.UndefinedException(); } } _lockToSpan = false; for (int i = _denseEntitiesDelayedCount; i < _denseEntitiesDelayedCount + _recycledCount; i++) { var index = _denseEntitiesDelayed[i]; if (_sparseEntities[index] != 0) { Throw.UndefinedException(); } } #endif _isDenseEntitiesDelayedValid = true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetFreeItemIndex(int entityID) { if (_denseEntitiesDelayedCount >= _capacity) { UpdateDenseEntities(); } if (_itemsCount + 1 >= _capacity) { Resize(_items.Length << 1); } int result = 0; if(_recycledCount > 0) { _recycledCount--; result = _denseEntitiesDelayed[_denseEntitiesDelayedCount]; } else { result = _denseEntitiesDelayedCount; } _denseEntitiesDelayed[_denseEntitiesDelayedCount] = entityID; _itemsCount++; _denseEntitiesDelayedCount++; #if DRAGONECS_DEEP_DEBUG if (_sparseEntities[result] != 0) { Throw.UndefinedException(); } if (result == 0) { Throw.UndefinedException(); } #endif return result; } private void CheckOrUpsize() { if (_itemsCount < _capacity) { return; } Resize(_items.Length << 1); } private void Resize(int newSize) { if (newSize <= _capacity) { return; } ArrayUtility.ResizeOrCreate(ref _sparseEntities, newSize); ArrayUtility.ResizeOrCreate(ref _items, newSize); _denseEntitiesDelayed = new int[newSize]; _denseEntitiesDelayedCount = 0; _capacity = newSize; _isDenseEntitiesDelayedValid = false; UpdateDenseEntities(); } #endregion #region Listeners #if !DRAGONECS_DISABLE_POOLS_EVENTS public void AddListener(IEcsPoolEventListener listener) { if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); } _listeners.Add(listener); _listenersCachedCount++; } public void RemoveListener(IEcsPoolEventListener listener) { if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); } if (_listeners.RemoveWithOrder(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 Convertors 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>(); } public static implicit operator EcsPool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } #endregion #region Apply public static void Apply(ref T component, int entityID, short worldID) { EcsWorld.GetPoolInstance>(worldID).TryAddOrGet(entityID) = component; } public static void Apply(ref T component, int entityID, EcsPool pool) { pool.TryAddOrGet(entityID) = component; } #endregion } #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] #endif [EditorBrowsable(EditorBrowsableState.Never)] public readonly struct ReadonlyEcsPool : IEcsReadonlyPool //IEnumerable - IntelliSense hack where T : struct, IEcsComponent { private readonly EcsPool _pool; #region Properties public int ComponentTypeID { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _pool.ComponentTypeID; } } public Type ComponentType { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _pool.ComponentType; } } public EcsWorld World { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _pool.World; } } public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _pool.Count; } } public bool IsReadOnly { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _pool.IsReadOnly; } } public ref readonly T this[int entityID] { get { return ref _pool.Read(entityID); } } #endregion #region Constructors internal ReadonlyEcsPool(EcsPool pool) { _pool = pool; } #endregion #region Methods [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Has(int entityID) { return _pool.Has(entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Get(int entityID) { return ref _pool.Read(entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Read(int entityID) { return ref _pool.Read(entityID); } object IEcsReadonlyPool.GetRaw(int entityID) { return _pool.Read(entityID); } #if !DRAGONECS_DISABLE_POOLS_EVENTS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } #endif #endregion #region Convertors public static implicit operator ReadonlyEcsPool(EcsPool a) { return new ReadonlyEcsPool(a); } public static implicit operator ReadonlyEcsPool(IncludeMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsPool(ExcludeMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsPool(OptionalMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsPool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } #endregion } public static class EcsPoolExtensions { [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 Inc(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.IncludePool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Exc(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.ExcludePool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Opt(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.OptionalPool>(); } #region Obsolete [Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "()")] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Include(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.IncludePool>(); } [Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "()")] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Exclude(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.ExcludePool>(); } [Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "()")] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsPool Optional(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent { return self.OptionalPool>(); } #endregion } }