#if DISABLE_DEBUG #undef DEBUG #endif using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Core.Internal; using DCFApixels.DragonECS.PoolsCore; 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 { /// Value type component [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Standard component.")] [MetaID("DragonECS_84D2537C9201D6F6B92FEC1C8883A07A")] public interface IEcsValueComponent : IEcsComponentMember { } internal unsafe struct EcsValuePoolSharedStore { public int _componentTypeID; public int* _mapping; public void* _items; } internal readonly unsafe struct EcsValuePoolNative { private readonly EcsValuePoolSharedStore* _store; } /// Pool for IEcsValueComponent components #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] #endif [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] [MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsValueComponent components.")] [MetaID("DragonECS_C501547C9201A4B03FC25632E4FAAFD7")] [DebuggerDisplay("Count: {Count} Type: {ComponentType}")] public sealed unsafe class EcsValuePool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack where T : unmanaged, IEcsValueComponent { private short _worldID; private EcsWorld _world; private int _componentTypeID; private EcsMaskChunck _maskBit; public int* _mapping; // index = entityID / value = itemIndex;/ value = 0 = no entityID. public void* _items; // dense; _items[0] - fake component. public int _itemsLength; public int _itemsCount; public int* _recycledItems; public int _recycledItemsLength; public int _recycledItemsCount; private readonly EcsValuePoolSharedStore* _sharedStore; private readonly IEcsComponentLifecycle _customLifecycle = EcsComponentLifecycle.CustomHandler; private readonly bool _isCustomLifecycle = EcsComponentLifecycle.IsCustom; private readonly IEcsComponentCopy _customCopy = EcsComponentCopy.CustomHandler; private readonly bool _isCustomCopy = EcsComponentCopy.IsCustom; private bool _isLocked; private EcsWorld.PoolsMediator _mediator; #region Properites public int Count { get { return _itemsCount; } } public int Capacity { get { return _itemsLength; } } public int ComponentTypeID { get { return _componentTypeID; } } public Type ComponentType { get { return typeof(T); } } public EcsWorld World { get { return _world; } } public bool IsReadOnly { get { return false; } } public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref Get(index); } } #endregion #region Constructors/Init/Destroy public EcsValuePool() { if (_sharedStore == null) { _sharedStore = MemoryAllocator.Alloc(1).Ptr; } } public EcsValuePool(int capacity, int recycledCapacity = -1) { if (_sharedStore == null) { _sharedStore = MemoryAllocator.Alloc(1).Ptr; } capacity = ArrayUtility.NextPow2(capacity); if (recycledCapacity < 0) { recycledCapacity = capacity / 2; } _itemsLength = capacity; _items = MemoryAllocator.Alloc(_itemsLength).Ptr; _sharedStore->_items = _items; _recycledItemsLength = recycledCapacity; _recycledItems = MemoryAllocator.Alloc(_recycledItemsLength).Ptr; } void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { _world = world; _mediator = mediator; _componentTypeID = componentTypeID; _maskBit = EcsMaskChunck.FromID(componentTypeID); _mapping = MemoryAllocator.Alloc(world.Capacity).Ptr; _sharedStore->_mapping = _mapping; var worldConfig = world.Configs.GetWorldConfigOrDefault(); if (_items == null) { _itemsLength = ArrayUtility.NextPow2(worldConfig.PoolComponentsCapacity); _items = MemoryAllocator.Alloc(_itemsLength).Ptr; _sharedStore->_items = _items; } if (_recycledItems == null) { _recycledItemsLength = worldConfig.PoolRecycledComponentsCapacity; _recycledItems = MemoryAllocator.Alloc(_recycledItemsLength).Ptr; } } void IEcsPoolImplementation.OnWorldDestroy() { } ~EcsValuePool() { MemoryAllocator.Free(_mapping); MemoryAllocator.Free(_items); MemoryAllocator.Free(_recycledItems); } #endregion #region Methods public ref T Add(int entityID) { ref int itemIndex = ref _mapping[entityID]; #if DEBUG if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); } if (_world.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_world, entityID); } if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent(entityID); } if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (itemIndex > 0) { return ref Get(entityID); } if (_isLocked | _world.IsUsed(entityID) == false) { return ref _items[0]; } #endif if (_recycledItemsCount > 0) { itemIndex = _recycledItems[--_recycledItemsCount]; _itemsCount++; } else { itemIndex = ++_itemsCount; if (itemIndex >= _itemsLength) { _itemsLength = ArrayUtility.NextPow2(_itemsLength << 1); _items = MemoryAllocator.Realloc(_items, _itemsLength).Ptr; _sharedStore->_items = _items; } } _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); ref T result = ref ((T*)_items)[itemIndex]; EcsComponentLifecycle.OnAdd(_isCustomLifecycle, _customLifecycle, ref result, _worldID, entityID); return ref result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get(int entityID) { #if DEBUG // не нужен STAB_MODE if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } #endif return ref ((T*)_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 ((T*)_items)[_mapping[entityID]]; } public ref T TryAddOrGet(int entityID) { #if DEBUG if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); } #endif 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 if (_recycledItemsCount > 0) { itemIndex = _recycledItems[--_recycledItemsCount]; _itemsCount++; } else { itemIndex = ++_itemsCount; if (itemIndex >= _itemsLength) { _itemsLength = ArrayUtility.NextPow2(_itemsLength << 1); _items = MemoryAllocator.Realloc(_items, _itemsLength).Ptr; _sharedStore->_items = _items; } } _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); EcsComponentLifecycle.OnAdd(_isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID); } //Add block end return ref ((T*)_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 (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); } if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } #elif DRAGONECS_STABILITY_MODE if (itemIndex <= 0) { return; } if (_isLocked) { return; } #endif EcsComponentLifecycle.OnDel( _isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID); if (_recycledItemsCount >= _recycledItemsLength) { _recycledItemsLength = ArrayUtility.NextPow2(_recycledItemsLength << 1); _recycledItems = MemoryAllocator.Realloc(_recycledItems, _recycledItemsLength).Ptr; } _recycledItems[_recycledItemsCount++] = itemIndex; itemIndex = 0; _itemsCount--; _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); } 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 EcsComponentCopy.Copy(_isCustomCopy, _customCopy, 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 EcsComponentCopy.Copy(_isCustomCopy, _customCopy, 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 _recycledItemsCount = 0; // спереди чтобы обнулялось, так как Del не обнуляет if (_itemsCount <= 0) { return; } var span = _world.Where(out SinglePoolAspect> _); foreach (var entityID in span) { ref int itemIndex = ref _mapping[entityID]; EcsComponentLifecycle.OnDel(_isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID); itemIndex = 0; _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); } _itemsCount = 0; _recycledItemsCount = 0; } #endregion #region Callbacks void IEcsPoolImplementation.OnWorldResize(int newSize) { _mapping = MemoryAllocator.Realloc(_mapping, newSize).Ptr; _sharedStore->_mapping = _mapping; } 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; } #endregion #region Listeners #if !DRAGONECS_DISABLE_POOLS_EVENTS public void AddListener(IEcsPoolEventListener listener) { EcsDebug.PrintWarning($"the {nameof(AddListener)} method is not supported for the {nameof(EcsValuePool)}."); } public void RemoveListener(IEcsPoolEventListener listener) { EcsDebug.PrintWarning($"the {nameof(RemoveListener)} method is not supported for the {nameof(EcsValuePool)}."); } #endif #endregion #region IEnumerator - IntelliSense hack IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion #region Convertors public static implicit operator EcsValuePool(IncludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsValuePool(ExcludeMarker a) { return a.GetInstance>(); } public static implicit operator EcsValuePool(AnyMarker a) { return a.GetInstance>(); } public static implicit operator EcsValuePool(OptionalMarker a) { return a.GetInstance>(); } public static implicit operator EcsValuePool(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, EcsValuePool pool) { pool.TryAddOrGet(entityID) = component; } #endregion } #if ENABLE_IL2CPP [Il2CppSetOption(Option.NullChecks, false)] #endif [EditorBrowsable(EditorBrowsableState.Never)] public readonly struct ReadonlyEcsValuePool : IEcsReadonlyPool //IEnumerable - IntelliSense hack where T : unmanaged, IEcsValueComponent { private readonly EcsValuePool _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 ReadonlyEcsValuePool(EcsValuePool 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 ReadonlyEcsValuePool(EcsValuePool a) { return new ReadonlyEcsValuePool(a); } public static implicit operator ReadonlyEcsValuePool(IncludeMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsValuePool(ExcludeMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsValuePool(AnyMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsValuePool(OptionalMarker a) { return a.GetInstance>(); } public static implicit operator ReadonlyEcsValuePool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } #endregion } public static class EcsValuePoolExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsValuePool GetPool(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent { return self.GetPoolInstance>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsValuePool GetPoolUnchecked(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent { return self.GetPoolInstanceUnchecked>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsValuePool Inc(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent { return self.IncludePool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsValuePool Exc(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent { return self.ExcludePool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsValuePool Opt(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent { return self.OptionalPool>(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsValuePool Any(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent { return self.AnyPool>(); } } }