DragonECS/src/EcsGroup.cs

229 lines
6.6 KiB
C#
Raw Normal View History

using System;
using System.Runtime.CompilerServices;
2023-03-30 10:46:57 +08:00
using delayedOp = System.Int32;
namespace DCFApixels.DragonECS
{
2023-03-30 10:46:57 +08:00
public interface IEcsReadonlyGroup
2023-02-14 03:26:34 +08:00
{
2023-03-30 10:46:57 +08:00
int Count { get; }
bool Contains(int entityID);
EcsGroup.Enumerator GetEnumerator();
2023-02-14 03:26:34 +08:00
}
public interface IEcsGroup : IEcsReadonlyGroup
{
2023-03-30 10:46:57 +08:00
void Add(int entityID);
void Remove(int entityID);
2023-02-14 03:26:34 +08:00
}
2023-03-30 10:46:57 +08:00
// не может содержать значение 0
// _delayedOps это int[] для отложенных операций, хранятся отложенные операции в виде int значения, если старший бит = 0 то это опреация добавленияб если = 1 то это операция вычитания
// this collection can only store numbers greater than 0
public class EcsGroup : IEcsGroup
{
2023-03-30 10:46:57 +08:00
public const int DEALAYED_ADD = 0;
public const int DEALAYED_REMOVE = int.MinValue;
2023-03-02 14:42:44 +08:00
private IEcsWorld _source;
2023-03-30 10:46:57 +08:00
private int[] _dense;
private int[] _sparse;
private int _count;
private delayedOp[] _delayedOps;
private int _delayedOpsCount;
private int _lockCount;
#region Properties
2023-03-02 14:42:44 +08:00
public IEcsWorld World => _source;
2023-03-30 10:46:57 +08:00
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
#endregion
#region Constrcutors
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup(IEcsWorld source, int denseCapacity = 64, int sparseCapacity = 256, int delayedOpsCapacity = 128)
{
2023-03-30 10:46:57 +08:00
_source = source;
_dense = new int[denseCapacity];
_sparse = new int[sparseCapacity];
_delayedOps = new delayedOp[delayedOpsCapacity];
_lockCount = 0;
2023-03-30 10:46:57 +08:00
_delayedOpsCount = 0;
_count = 0;
}
#endregion
2023-03-30 10:46:57 +08:00
#region Contains
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID)
{
return /*entityID > 0 && */ entityID < _sparse.Length && _sparse[entityID] > 0;
}
#endregion
#region add/remove
public void Add(int entityID)
{
2023-03-30 10:46:57 +08:00
if (_lockCount > 0)
{
AddDelayedOp(entityID, DEALAYED_ADD);
return;
}
if (Contains(entityID))
return;
if(++_count >= _dense.Length)
Array.Resize(ref _dense, _dense.Length << 1);
if (entityID > _sparse.Length)
{
int neadedSpace = _sparse.Length;
while (entityID >= neadedSpace)
neadedSpace <<= 1;
Array.Resize(ref _sparse, neadedSpace);
}
_dense[_count] = entityID;
_sparse[entityID] = _count;
}
public void Remove(int entityID)
{
if (_lockCount > 0)
2023-03-30 10:46:57 +08:00
{
AddDelayedOp(entityID, DEALAYED_REMOVE);
return;
}
if (!Contains(entityID))
return;
_dense[_sparse[entityID]] = _dense[_count];
_sparse[_dense[_count--]] = _sparse[entityID];
_sparse[entityID] = 0;
}
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddDelayedOp(int entityID, int isAddBitFlag)
{
if (_delayedOpsCount >= _delayedOps.Length)
{
Array.Resize(ref _delayedOps, _delayedOps.Length << 1);
}
2023-03-30 10:46:57 +08:00
_delayedOps[_delayedOpsCount++] = entityID | isAddBitFlag; // delayedOp = entityID add isAddBitFlag
}
#endregion
2023-03-30 10:46:57 +08:00
//TODO добавить автосоритровку при каждом GetEnumerator
2023-03-26 11:19:03 +08:00
2023-02-14 03:26:34 +08:00
#region AddGroup/RemoveGroup
public void AddGroup(IEcsReadonlyGroup group)
{
foreach (var item in group)
{
2023-03-30 10:46:57 +08:00
Add(item.id);
2023-02-14 03:26:34 +08:00
}
}
public void RemoveGroup(IEcsReadonlyGroup group)
{
foreach (var item in group)
{
2023-03-30 10:46:57 +08:00
Remove(item.id);
2023-02-14 03:26:34 +08:00
}
}
#endregion
#region GetEnumerator
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Unlock()
{
2023-03-30 10:46:57 +08:00
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
if (_lockCount <= 0)
{
2023-03-30 10:46:57 +08:00
throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}.");
}
#endif
if (--_lockCount <= 0)
{
for (int i = 0; i < _delayedOpsCount; i++)
{
2023-03-30 10:46:57 +08:00
delayedOp op = _delayedOps[i];
if (op >= 0) //delayedOp.IsAdded
{
2023-03-30 10:46:57 +08:00
Add(op & int.MaxValue); //delayedOp.Entity
}
else
{
2023-03-30 10:46:57 +08:00
Remove(op & int.MaxValue); //delayedOp.Entity
}
}
}
}
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
_lockCount++;
return new Enumerator(this);
}
#endregion
#region Utils
2023-03-30 10:46:57 +08:00
public struct Enumerator : IDisposable
{
private readonly EcsGroup _source;
2023-03-30 10:46:57 +08:00
private int _pointer;
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(EcsGroup group)
{
_source = group;
2023-03-30 10:46:57 +08:00
_pointer = 0;
}
2023-03-30 10:46:57 +08:00
private static EcsProfilerMarker _marker = new EcsProfilerMarker("EcsGroup.Enumerator.Current");
2023-03-26 11:19:03 +08:00
public ent Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-03-30 10:46:57 +08:00
get
{
using (_marker.Auto())
return _source.World.GetEntity(_source._dense[_pointer]);
// return _source._dense[_pointer];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-03-30 10:46:57 +08:00
public bool MoveNext()
{
return ++_pointer <= _source.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-03-30 10:46:57 +08:00
public void Dispose()
{
_source.Unlock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-03-30 10:46:57 +08:00
public void Reset()
{
_pointer = -1;
}
}
#endregion
}
}