using System; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { public interface IEcsReadonlyGroup { public IEcsWorld World { get; } public int Count { get; } public EcsGroup.Enumerator GetEnumerator(); } public interface IEcsGroup : IEcsReadonlyGroup { public void Add(int entityID); public void Remove(int entityID); } public class EcsGroup : IEcsGroup { private IEcsWorld _source; private SparseSet _entities; private DelayedOp[] _delayedOps; private int _delayedOpsCount; private int _lockCount; #region Properties public IEcsWorld World => _source; public int Count => _entities.Count; #endregion #region Constrcutors public EcsGroup(IEcsWorld world, int entitiesCapacity, int delayedOpsCapacity = 32) { _source = world; _entities = new SparseSet(entitiesCapacity, entitiesCapacity); _delayedOps = new DelayedOp[delayedOpsCapacity]; _lockCount = 0; } #endregion #region Add/Remove public void Add(int entityID) { if (_lockCount > 0) AddDelayedOp(entityID, true); _entities.Add(entityID); } public void Remove(int entityID) { if (_lockCount > 0) AddDelayedOp(entityID, false); _entities.Remove(entityID); } private void AddDelayedOp(int entityID, bool isAdd) { if (_delayedOpsCount >= _delayedOps.Length) { Array.Resize(ref _delayedOps, _delayedOps.Length << 1); } ref DelayedOp delayedOd = ref _delayedOps[_delayedOpsCount++]; delayedOd.Entity = entityID; delayedOd.Added = isAdd; } #endregion #region Contains [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(int entityID) => _entities.Contains(entityID); #endregion #region AddGroup/RemoveGroup public void AddGroup(IEcsReadonlyGroup group) { foreach (var item in group) { _entities.TryAdd(item.id); } } public void RemoveGroup(IEcsReadonlyGroup group) { foreach (var item in group) { _entities.TryRemove(item.id); } } #endregion #region GetEnumerator private void Unlock() { #if DEBUG if (_lockCount <= 0) { throw new Exception($"Invalid lock-unlock balance for {nameof(EcsFilter)}."); } #endif if (--_lockCount <= 0) { for (int i = 0; i < _delayedOpsCount; i++) { ref DelayedOp op = ref _delayedOps[i]; if (op.Added) { Add(op.Entity); } else { Remove(op.Entity); } } _delayedOpsCount = 0; } } public Enumerator GetEnumerator() { _lockCount++; return new Enumerator(this); } #endregion #region Utils public ref struct Enumerator { private readonly EcsGroup _source; private readonly SparseSet _entities; private int _index; public Enumerator(EcsGroup group) { _source = group; _entities = group._entities; _index = -1; } public ent Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _source.World.GetEntity(_entities[_index]); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => ++_index < _entities.Count; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() => _source.Unlock(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() => _index = -1; } private struct DelayedOp { public bool Added; public int Entity; } #endregion } }