DragonECS/src/EcsGroup.cs

442 lines
15 KiB
C#
Raw Normal View History

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
2023-04-08 23:01:10 +08:00
using static UnityEngine.Networking.UnityWebRequest;
2023-03-30 10:46:57 +08:00
using delayedOp = System.Int32;
namespace DCFApixels.DragonECS
{
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
public readonly ref struct EcsReadonlyGroup
2023-02-14 03:26:34 +08:00
{
private readonly EcsGroup _source;
2023-04-01 22:29:34 +08:00
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsReadonlyGroup(EcsGroup source) => _source = source;
2023-04-01 22:29:34 +08:00
#endregion
#region Properties
2023-04-09 02:52:39 +08:00
public IEcsWorld World
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.World;
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.Count;
}
2023-04-01 20:45:37 +08:00
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.CapacityDense;
}
public int CapacitySparce
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.CapacitySparce;
}
2023-04-01 22:29:34 +08:00
#endregion
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID) => _source.Contains(entityID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator();
2023-04-08 21:29:18 +08:00
public EcsGroup Extract()
{
return new EcsGroup(_source);
}
2023-04-01 22:29:34 +08:00
#endregion
2023-04-08 23:01:10 +08:00
#region Object
public override string ToString()
{
if (_source != null)
return _source.ToString();
return "NULL";
}
#endregion
#region Internal
internal void Release()
{
_source.World.ReleaseGroup(_source);
}
2023-04-09 02:52:39 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-08 23:01:10 +08:00
internal EcsGroup GetGroupInternal() => _source;
#endregion
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
2023-04-01 21:16:08 +08:00
public class EcsGroup
{
2023-04-09 02:52:39 +08:00
private const int DEALAYED_ADD = 0;
private const int DEALAYED_REMOVE = int.MinValue;
2023-03-30 10:46:57 +08:00
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;
}
2023-04-01 20:45:37 +08:00
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense.Length;
}
public int CapacitySparce
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _sparse.Length;
}
public EcsReadonlyGroup Readonly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new EcsReadonlyGroup(this);
}
#endregion
#region Constrcutors
2023-04-01 20:45:37 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup(IEcsWorld source, int denseCapacity = 64, int delayedOpsCapacity = 128)
{
_source = source;
2023-04-08 21:29:18 +08:00
_source.RegisterGroup(this);
2023-04-01 20:45:37 +08:00
_dense = new int[denseCapacity];
_sparse = new int[source.EntitesCapacity];
2023-03-30 10:46:57 +08:00
_delayedOps = new delayedOp[delayedOpsCapacity];
_lockCount = 0;
2023-03-30 10:46:57 +08:00
_delayedOpsCount = 0;
2023-04-08 21:29:18 +08:00
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup(EcsGroup copyFrom, int delayedOpsCapacity = 128)
{
_source = copyFrom._source;
_source.RegisterGroup(this);
_dense = new int[copyFrom._dense.Length];
_sparse = new int[copyFrom._sparse.Length];
_delayedOps = new delayedOp[delayedOpsCapacity];
2023-03-30 10:46:57 +08:00
2023-04-08 21:29:18 +08:00
_lockCount = 0;
_delayedOpsCount = 0;
2023-03-30 10:46:57 +08:00
_count = 0;
2023-04-08 21:29:18 +08:00
foreach (var item in copyFrom)
AggressiveAdd(item.id);
}
#endregion
2023-03-30 10:46:57 +08:00
#region Contains
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID)
{
2023-04-01 20:45:37 +08:00
//TODO добавить проверку на больше 0 в #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#endif
return /*entityID > 0 && entityID < _sparse.Length && */ _sparse[entityID] > 0;
}
#endregion
#region IndexOf
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(int entityID)
{
return _sparse[entityID];
2023-03-30 10:46:57 +08:00
}
#endregion
2023-04-08 21:29:18 +08:00
#region Add/Remove
2023-04-01 20:45:37 +08:00
public void UncheckedAdd(int entityID) => AddInternal(entityID);
public void Add(int entityID)
{
2023-04-01 20:45:37 +08:00
if (Contains(entityID)) return;
AddInternal(entityID);
2023-04-01 20:45:37 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AddInternal(int entityID)
2023-04-01 20:45:37 +08:00
{
if (_lockCount > 0)
2023-03-30 10:46:57 +08:00
{
AddDelayedOp(entityID, DEALAYED_ADD);
return;
}
2023-04-08 21:29:18 +08:00
AggressiveAdd(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AggressiveAdd(int entityID)
{
2023-04-01 20:45:37 +08:00
if (++_count >= _dense.Length)
2023-03-30 10:46:57 +08:00
Array.Resize(ref _dense, _dense.Length << 1);
_dense[_count] = entityID;
_sparse[entityID] = _count;
}
2023-04-01 20:45:37 +08:00
public void UncheckedRemove(int entityID) => RemoveInternal(entityID);
public void Remove(int entityID)
2023-04-01 20:45:37 +08:00
{
if (!Contains(entityID)) return;
RemoveInternal(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RemoveInternal(int entityID)
{
if (_lockCount > 0)
2023-03-30 10:46:57 +08:00
{
AddDelayedOp(entityID, DEALAYED_REMOVE);
return;
}
2023-04-08 21:29:18 +08:00
AggressiveRemove(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AggressiveRemove(int entityID)
{
2023-03-30 10:46:57 +08:00
_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-04-09 02:52:39 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-01 20:45:37 +08:00
internal void OnWorldResize(int newSize)
{
Array.Resize(ref _sparse, newSize);
}
2023-04-07 03:12:56 +08:00
public void Sort()
{
int increment = 1;
for (int i = 0; i < _dense.Length; i++)
{
if (_sparse[i] > 0)
{
_sparse[i] = increment;
_dense[increment++] = i;
}
}
}
2023-04-09 02:52:39 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-08 21:29:18 +08:00
public void Clear() => _count = 0;
2023-04-07 03:12:56 +08:00
2023-04-08 23:01:10 +08:00
public void CopyFrom(EcsGroup group)
2023-04-08 21:29:18 +08:00
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
2023-04-09 02:52:39 +08:00
if (group.World != _source) throw new ArgumentException("groupFilter.World != World");
#endif
Clear();
foreach (var item in group)
AggressiveAdd(item.id);
}
public void CopyFrom(EcsReadonlyGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (group.World != _source) throw new ArgumentException("groupFilter.World != World");
2023-04-08 21:29:18 +08:00
#endif
2023-04-08 23:01:10 +08:00
Clear();
foreach (var item in group)
AggressiveAdd(item.id);
}
2023-04-01 21:16:08 +08:00
2023-04-08 23:01:10 +08:00
#region Set operations
/// <summary>as Union sets</summary>
public void AddGroup(EcsGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
2023-04-09 02:52:39 +08:00
if (_source != group.World) throw new ArgumentException("World != groupFilter.World");
#endif
foreach (var item in group)
if (!Contains(item.id))
AggressiveAdd(item.id);
}
/// <summary>as Union sets</summary>
public void AddGroup(EcsReadonlyGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_source != group.World) throw new ArgumentException("World != groupFilter.World");
2023-04-08 23:01:10 +08:00
#endif
foreach (var item in group)
if (!Contains(item.id))
AggressiveAdd(item.id);
2023-04-08 21:29:18 +08:00
}
2023-04-08 23:01:10 +08:00
/// <summary>as Except sets</summary>
public void RemoveGroup(EcsGroup group)
2023-04-08 21:29:18 +08:00
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
2023-04-09 02:52:39 +08:00
if (_source != group.World) throw new ArgumentException("World != groupFilter.World");
#endif
foreach (var item in group)
if (Contains(item.id))
AggressiveRemove(item.id);
}
/// <summary>as Except sets</summary>
public void RemoveGroup(EcsReadonlyGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_source != group.World) throw new ArgumentException("World != groupFilter.World");
2023-04-08 21:29:18 +08:00
#endif
2023-04-08 23:01:10 +08:00
foreach (var item in group)
if (Contains(item.id))
AggressiveRemove(item.id);
2023-04-08 21:29:18 +08:00
}
2023-04-08 23:01:10 +08:00
/// <summary>as Intersect sets</summary>
public void AndWith(EcsGroup group)
2023-04-08 21:29:18 +08:00
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
2023-04-09 02:52:39 +08:00
if (World != group.World) throw new ArgumentException("World != groupFilter.World");
#endif
foreach (var item in this)
if (!group.Contains(item.id))
AggressiveRemove(item.id);
}
/// <summary>as Intersect sets</summary>
public void AndWith(EcsReadonlyGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (World != group.World) throw new ArgumentException("World != groupFilter.World");
2023-04-08 21:29:18 +08:00
#endif
2023-04-08 23:01:10 +08:00
foreach (var item in this)
2023-04-09 02:52:39 +08:00
if (!group.Contains(item.id))
2023-04-08 23:01:10 +08:00
AggressiveRemove(item.id);
2023-04-08 21:29:18 +08:00
}
2023-04-08 23:01:10 +08:00
/// <summary>as Symmetric Except sets</summary>
public void XorWith(EcsGroup group)
2023-04-08 21:29:18 +08:00
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
2023-04-09 02:52:39 +08:00
if (_source != group.World) throw new ArgumentException("World != groupFilter.World");
#endif
foreach (var item in group)
if (Contains(item.id))
AggressiveRemove(item.id);
else
AggressiveAdd(item.id);
}
/// <summary>as Symmetric Except sets</summary>
public void XorWith(EcsReadonlyGroup group)
{
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_source != group.World) throw new ArgumentException("World != groupFilter.World");
2023-04-08 21:29:18 +08:00
#endif
2023-04-08 23:01:10 +08:00
foreach (var item in group)
if (Contains(item.id))
AggressiveRemove(item.id);
else
AggressiveAdd(item.id);
2023-04-08 21:29:18 +08:00
}
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-04-01 20:45:37 +08:00
#if (DEBUG && !DISABLE_DRAGONECS_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-04-09 02:52:39 +08:00
AggressiveAdd(op & int.MaxValue); //delayedOp.EcsEntity
else
2023-04-09 02:52:39 +08:00
AggressiveRemove(op & int.MaxValue); //delayedOp.EcsEntity
}
}
}
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
2023-04-07 19:23:07 +08:00
Sort();
_lockCount++;
return new Enumerator(this);
}
#endregion
#region Enumerator
2023-04-09 02:52:39 +08:00
public struct Enumerator : IDisposable
{
private readonly EcsGroup _source;
2023-04-01 21:16:08 +08:00
private readonly int[] _dense;
private readonly int _count;
2023-04-06 23:40:47 +08:00
private int _index;
2023-03-30 10:46:57 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(EcsGroup group)
{
_source = group;
2023-04-01 21:16:08 +08:00
_dense = group._dense;
_count = group.Count;
2023-04-06 23:40:47 +08:00
_index = 0;
}
2023-03-26 11:19:03 +08:00
public ent Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-08 05:50:44 +08:00
get => new ent(_dense[_index]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-08 00:47:35 +08:00
public bool MoveNext() => ++_index <= _count && _count<_dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-04-08 00:47:35 +08:00
public void Dispose() => _source.Unlock();
}
#endregion
2023-04-08 23:01:10 +08:00
2023-04-09 02:52:39 +08:00
#region Object
2023-04-08 23:01:10 +08:00
public override string ToString()
{
return string.Join(", ", _dense.AsSpan(1, _count).ToArray());
}
#endregion
}
2023-04-01 20:45:37 +08:00
public static class EcsGroupExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Normalize<T>(this EcsGroup self, ref T[] array)
{
if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Normalize<T>(this EcsReadonlyGroup self, ref T[] array)
{
if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense);
}
}
}