diff --git a/src/EcsFilter.cs b/src/EcsFilter.cs index 401ee7e..da03b2e 100644 --- a/src/EcsFilter.cs +++ b/src/EcsFilter.cs @@ -6,196 +6,37 @@ namespace DCFApixels.DragonECS #region Incs/Excs base public interface ICondition { - public int[] GetComponentsIDs(); + public int[] GetComponentsIDs() where TWorldArchetype : IWorldArchetype; } #endregion #region Incs public interface IInc : ICondition { } + public struct Inc : IInc + { + public int[] GetComponentsIDs() where TWorldArchetype : IWorldArchetype => Array.Empty(); + } public struct Inc : IInc { - public int[] GetComponentsIDs() + public int[] GetComponentsIDs() + where TWorldArchetype : IWorldArchetype { return new int[] { - ComponentType.globalID + EcsWorld.ComponentType.uniqueID }; } } public struct Inc : IInc { - public int[] GetComponentsIDs() + public int[] GetComponentsIDs() + where TWorldArchetype : IWorldArchetype { return new int[] { - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } - } - public struct Inc : IInc - { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID + EcsWorld.ComponentType.uniqueID, + EcsWorld.ComponentType.uniqueID }; } } @@ -204,112 +45,114 @@ namespace DCFApixels.DragonECS #region Excs public interface IExc : ICondition { } + public struct Exc : IExc + { + public int[] GetComponentsIDs() where TWorldArchetype : IWorldArchetype => Array.Empty(); + } public struct Exc : IExc { - public int[] GetComponentsIDs() + public int[] GetComponentsIDs() + where TWorldArchetype : IWorldArchetype { return new int[] { - ComponentType.globalID + EcsWorld.ComponentType.uniqueID, }; } } public struct Exc : IExc { - public int[] GetComponentsIDs() + public int[] GetComponentsIDs() + where TWorldArchetype : IWorldArchetype { return new int[] { - ComponentType.globalID, - ComponentType.globalID + EcsWorld.ComponentType.uniqueID, + EcsWorld.ComponentType.uniqueID }; } } - public struct Exc : IExc + #endregion + + #region BakedMask + public abstract class BakedMask { - public int[] GetComponentsIDs() + internal readonly int[] Inc; + internal readonly int[] Exc; + internal readonly Mask Mask; + + internal int IncCount { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Inc.Length; + } + internal int ExcCount + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Exc.Length; + } + + //Уникальный айди в рамках одного архиетипа мира + internal abstract int UniqueID { get; } + internal abstract Type WorldArchetypeType { get; } + + protected BakedMask(int[] inc, int[] exc, Mask mask) + { + Inc = inc; + Exc = exc; + Mask = mask; } } - public struct Exc : IExc + + public abstract class BakedMask : BakedMask { - public int[] GetComponentsIDs() - { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; - } + internal static int increment = 1; + internal static int capacity = 512; + + protected BakedMask(int[] inc, int[] exc, Mask mask) : base(inc, exc, mask) { } } - public struct Exc : IExc + + public sealed class BakedMask : BakedMask + where TWorldArchetype : IWorldArchetype + where TMask : MaskSingleton { - public int[] GetComponentsIDs() + public static readonly int uniqueID; + + static BakedMask() { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; + uniqueID = increment++; +#if DEBUG || DCFAECS_NO_SANITIZE_CHECKS + if (uniqueID >= ushort.MaxValue) + throw new EcsFrameworkException($"No more room for new BakedMask for this {typeof(TWorldArchetype).FullName} IWorldArchetype"); +#endif + if (increment > capacity) + capacity <<= 1; + _instance = new BakedMask(); } - } - public struct Exc : IExc - { - public int[] GetComponentsIDs() + + private BakedMask() : base( + MaskSingleton.Instance.MakeInc(), + MaskSingleton.Instance.MakeExc(), + MaskSingleton.Instance) + { } + + private static readonly BakedMask _instance = new BakedMask(); + public static BakedMask Instance { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _instance; } - } - public struct Exc : IExc - { - public int[] GetComponentsIDs() + + internal override int UniqueID { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => uniqueID; } - } - public struct Exc : IExc - { - public int[] GetComponentsIDs() + + internal override Type WorldArchetypeType { - return new int[] - { - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID, - ComponentType.globalID - }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => typeof(IWorldArchetype); } } #endregion @@ -317,111 +160,76 @@ namespace DCFApixels.DragonECS #region Masks public abstract class Mask { - protected internal static int _typeIDIncrement = 0; - - internal abstract int[] Include { get; } - internal abstract int[] Exclude { get; } - - public abstract int ID { get; } - - public int IncCount - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Include.Length; - } - public int ExcCount - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Exclude.Length; - } + internal abstract int[] MakeInc() where TWorldArchetype : IWorldArchetype; + internal abstract int[] MakeExc() where TWorldArchetype : IWorldArchetype; + public abstract BakedMask GetBaked() where TWorldArchetype : IWorldArchetype; } - public sealed class Mask : Mask - where TInc : struct, IInc + public abstract class MaskSingleton : Mask + where TSelf : Mask { - internal static readonly int[] include = new TInc().GetComponentsIDs(); - internal static readonly int[] exclude = Array.Empty(); - public static readonly int id = _typeIDIncrement++; - private static Mask _instance = new Mask(); - - private Mask() { } - - public static Mask Instance + protected static TSelf _instance; + internal static TSelf Instance { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _instance; } - public override int ID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => id; - } - internal override int[] Include - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => include; - } - internal override int[] Exclude - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => exclude; - } } - public sealed class Mask : Mask + + public class Mask : MaskSingleton> + where TInc : struct, IInc + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int[] MakeInc() => new TInc().GetComponentsIDs(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int[] MakeExc() => Array.Empty(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override BakedMask GetBaked() + { + return BakedMask>.Instance; + } + + static Mask() { _instance = new Mask(); } + } + public class Mask : MaskSingleton> where TInc : struct, IInc where TExc : struct, IExc { - internal static readonly int[] include = new TInc().GetComponentsIDs(); - internal static readonly int[] exclude = new TExc().GetComponentsIDs(); - public static readonly int id = _typeIDIncrement++; - private static Mask _instance = new Mask(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int[] MakeInc() => new TInc().GetComponentsIDs(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int[] MakeExc() => new TExc().GetComponentsIDs(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override BakedMask GetBaked() + { + return BakedMask>.Instance; + } - private Mask() { } - - public static Mask Instance - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _instance; - } - public override int ID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => id; - } - internal override int[] Include - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => include; - } - internal override int[] Exclude - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => exclude; - } + static Mask() { _instance = new Mask(); } } #endregion #region Filter public interface IEcsFilter { - public EcsWorld World { get; } - public Mask Mask { get; } + public IEcsWorld World { get; } + public BakedMask Mask { get; } public IEcsReadonlyGroup Entities { get; } public int EntitiesCount { get; } } public class EcsFilter : IEcsFilter { - private readonly EcsWorld _source; + private readonly IEcsWorld _source; private readonly EcsGroup _entities; - private readonly Mask _mask; + private readonly BakedMask _mask; #region Properties - public EcsWorld World + public IEcsWorld World { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _source; } - public Mask Mask + public BakedMask Mask { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _mask; @@ -439,7 +247,7 @@ namespace DCFApixels.DragonECS #endregion #region Constrcutors - internal EcsFilter(EcsWorld source, Mask mask, int capasity) + internal EcsFilter(IEcsWorld source, BakedMask mask, int capasity) { _source = source; _mask = mask; diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 30bfe66..198e63b 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -5,7 +5,7 @@ namespace DCFApixels.DragonECS { public interface IEcsReadonlyGroup { - public EcsWorld World { get; } + public IEcsWorld World { get; } public int Count { get; } public EcsGroup.Enumerator GetEnumerator(); } @@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS public class EcsGroup : IEcsGroup { - private EcsWorld _source; + private IEcsWorld _source; private SparseSet _entities; private DelayedOp[] _delayedOps; @@ -26,12 +26,12 @@ namespace DCFApixels.DragonECS private int _lockCount; #region Properties - public EcsWorld World => _source; + public IEcsWorld World => _source; public int Count => _entities.Count; #endregion #region Constrcutors - public EcsGroup(EcsWorld world, int entitiesCapacity, int delayedOpsCapacity = 128) + public EcsGroup(IEcsWorld world, int entitiesCapacity, int delayedOpsCapacity = 128) { _source = world; _entities = new SparseSet(entitiesCapacity); diff --git a/src/EcsPool.cs b/src/EcsPool.cs index f7f73ec..c3dca5c 100644 --- a/src/EcsPool.cs +++ b/src/EcsPool.cs @@ -10,28 +10,34 @@ namespace DCFApixels.DragonECS { public interface IEcsPool { - public EcsWorld World { get; } + public IEcsWorld World { get; } public int ID { get; } public bool Has(int index); public void Write(int index); public void Del(int index); } - public class EcsPool : IEcsPool + public interface IEcsPool : IEcsPool + where T : struct + { + public ref readonly T Read(int entity); + public new ref T Write(int entity); + } + public class EcsPool : IEcsPool where T : struct { private readonly int _id; - private readonly EcsWorld _source; + private readonly IEcsWorld _source; private readonly SparseSet _sparseSet; private T[] _denseItems; #region Properites - public EcsWorld World => _source; + public IEcsWorld World => _source; public int ID => _id; #endregion #region Constructors - public EcsPool(EcsWorld source, int capacity) + public EcsPool(IEcsWorld source, int capacity) { _source = source; _sparseSet = new SparseSet(capacity); @@ -42,34 +48,36 @@ namespace DCFApixels.DragonECS #region Read/Write/Has/Del [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(int index) + public ref readonly T Read(int entity) { - return ref _denseItems[_sparseSet[index]]; + return ref _denseItems[_sparseSet[entity]]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Write(int index) + public ref T Write(int entity) { - if (_sparseSet.Contains(index)) + if (_sparseSet.Contains(entity)) { - return ref _denseItems[_sparseSet[index]]; + return ref _denseItems[_sparseSet[entity]]; } else { - _sparseSet.Add(index); + _sparseSet.Add(entity); _sparseSet.Normalize(ref _denseItems); - return ref _denseItems[_sparseSet.IndexOf(index)]; + _source.OnEntityComponentAdded(entity, _id); + return ref _denseItems[_sparseSet.IndexOf(entity)]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(int index) + public bool Has(int entity) { - return _sparseSet.IndexOf(index) > 0; + return _sparseSet.IndexOf(entity) > 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(int index) + public void Del(int entity) { - _sparseSet.RemoveAt(index); + _sparseSet.RemoveAt(entity); + _source.OnEntityComponentRemoved(entity, _id); } #endregion diff --git a/src/EcsSession.cs b/src/EcsSession.cs index ca29b1f..cab57b6 100644 --- a/src/EcsSession.cs +++ b/src/EcsSession.cs @@ -19,8 +19,8 @@ namespace DCFApixels.DragonECS private bool _isDestoryed = false; private int _worldIdIncrement; - private Dictionary _worldsDict = new Dictionary(); - private List _worlds = new List(); + private Dictionary _worldsDict = new Dictionary(); + private List _worlds = new List(); private Dictionary _runners; private Dictionary _messengers; @@ -89,11 +89,12 @@ namespace DCFApixels.DragonECS _allProcessors.Add(system); return this; } - public EcsSession AddWorld(string name) + public EcsSession AddWorld(EcsWorld world, string name = "") + where TArchetype : IWorldArchetype { CheckInitForMethod(nameof(AddWorld)); - //_worlds.Add(new EcsWorld(_worldIdIncrement++)); + _worlds.Add(new EcsWorld(_worldIdIncrement++)); return this; } diff --git a/src/EcsTable.cs b/src/EcsTable.cs deleted file mode 100644 index c8a52f9..0000000 --- a/src/EcsTable.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace DCFApixels.DragonECS -{ - public abstract class EcsTable - { - internal EcsFilter _filter; - - public EcsTable(ref TableBuilder builder) { } - - public EcsFilter Filter - { - get => _filter; - } - - } -} - diff --git a/src/EcsTable.cs.meta b/src/EcsTable.cs.meta deleted file mode 100644 index fa90f26..0000000 --- a/src/EcsTable.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ce8e0e17f18931e4284946bddba80e02 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs deleted file mode 100644 index c743e53..0000000 --- a/src/EcsWorld.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace DCFApixels.DragonECS -{ - - public class EcsWorldMap - { - - } - public class EcsWorld - { - public const int MAX_WORLDS = byte.MaxValue; //Номер последнего мира 254 - public const int DEAD_WORLD_ID = byte.MaxValue; //Зарезервированный номер мира для мертвых сущьностей - - private byte _id = DEAD_WORLD_ID; - - //private float _timeScale;//TODO реализовать собсвенныйтайм склей для разных миров - - private IEcsPool[] _pools; - private SparseSet _componentIDToPoolID; - - private SparseSet _entities = new SparseSet(); - private short[] _gens; - - private List[] _filtersByIncludedComponents; - private List[] _filtersByExcludedComponents; - - private EcsFilter[] _filters; - private SparseSet _maskIDToFilterID; - - #region Properties - public int ID => _id; - public bool IsAlive => _id != DEAD_WORLD_ID; - public bool IsEmpty => _entities.Count < 0; - #endregion - - #region Constructors - public EcsWorld() - { - _pools = new IEcsPool[512]; - _entities = new SparseSet(512); - _componentIDToPoolID = new SparseSet(512); - _maskIDToFilterID = new SparseSet(512); - _filters = new EcsFilter[512]; - } - #endregion - - #region Pools - public EcsPool GetPool() - where T : struct - { - int uniqueID = ComponentType.globalID; - int poolIndex = _componentIDToPoolID.IndexOf(uniqueID); - if (poolIndex >= 0) - { - return (EcsPool)_pools[poolIndex]; - } -#if DEBUG - if (_componentIDToPoolID.Count >= ushort.MaxValue) - { - throw new EcsFrameworkException("No more room for new component into this world."); - } -#endif - var pool = new EcsPool(this, 512); - _componentIDToPoolID.Add(uniqueID); - _componentIDToPoolID.Normalize(ref _pools); - _componentIDToPoolID.Normalize(ref _filtersByIncludedComponents); - _componentIDToPoolID.Normalize(ref _filtersByExcludedComponents); - - _pools[_componentIDToPoolID.IndexOf(poolIndex)] = pool; - return pool; - } - #endregion - - #region Filters - public EcsFilter GetFilter(TMask mask) where TMask : Mask - { - if (_maskIDToFilterID.TryAdd(mask.ID, ref _filters)) - { - EcsFilter filter = new EcsFilter(this, mask, 512); - _filters[_maskIDToFilterID.IndexOf(mask.ID)] = filter; - return filter; - } - else - { - return _filters[_maskIDToFilterID.IndexOf(mask.ID)]; - } - } - #endregion - - #region NewEntity - public ent NewEntity() - { - int entityID = _entities.GetFree(); - _entities.Normalize(ref _gens); - _gens[entityID]++; - - - return new ent(entityID, _gens[entityID], _id); - } - #endregion - - #region Destroy - public void Destroy() - { - _id = DEAD_WORLD_ID; - } - #endregion - - #region IsMaskCompatible/IsMaskCompatibleWithout - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsMaskCompatible(Mask mask, int entity) - { - for (int i = 0, iMax = mask.IncCount; i < iMax; i++) - { - if (!_pools[_componentIDToPoolID[mask.Include[i]]].Has(entity)) - { - return false; - } - } - for (int i = 0, iMax = mask.ExcCount; i < iMax; i++) - { - if (_pools[_componentIDToPoolID[mask.Exclude[i]]].Has(entity)) - { - return false; - } - } - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsMaskCompatibleWithout(Mask mask, int entity, int otherPoolID) - { - for (int i = 0, iMax = mask.IncCount; i < iMax; i++) - { - int poolID = _componentIDToPoolID[mask.Include[i]]; - if (poolID == otherPoolID || !_pools[poolID].Has(entity)) - { - return false; - } - } - for (int i = 0, iMax = mask.ExcCount; i < iMax; i++) - { - int poolID = _componentIDToPoolID[mask.Exclude[i]]; - if (poolID != otherPoolID && _pools[poolID].Has(entity)) - { - return false; - } - } - return true; - } - #endregion - - #region EntityChangedReact - internal void OnEntityComponentAdded(int entityID, int changedPoolID) - { - var includeList = _filtersByIncludedComponents[changedPoolID]; - var excludeList = _filtersByExcludedComponents[changedPoolID]; - - if (includeList != null) - { - foreach (var filter in includeList) - { - if (IsMaskCompatible(filter.Mask, entityID)) - { - filter.Add(entityID); - } - } - } - if (excludeList != null) - { - foreach (var filter in excludeList) - { - if (IsMaskCompatibleWithout(filter.Mask, entityID, changedPoolID)) - { - filter.Remove(entityID); - } - } - } - } - - internal void OnEntityComponentRemoved(int entityID, int changedPoolID) - { - var includeList = _filtersByIncludedComponents[changedPoolID]; - var excludeList = _filtersByExcludedComponents[changedPoolID]; - - if (includeList != null) - { - foreach (var filter in includeList) - { - if (IsMaskCompatible(filter.Mask, entityID)) - { - filter.Remove(entityID); - } - } - } - if (excludeList != null) - { - foreach (var filter in excludeList) - { - if (IsMaskCompatibleWithout(filter.Mask, entityID, changedPoolID)) - { - filter.Add(entityID); - } - } - } - } - #endregion - } -} diff --git a/src/IEcsWorld.cs b/src/IEcsWorld.cs new file mode 100644 index 0000000..cd01122 --- /dev/null +++ b/src/IEcsWorld.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace DCFApixels.DragonECS +{ + public interface IWorldArchetype { } + public struct DefaultArchetype : IWorldArchetype { } + + public interface IEcsWorld + { + public const int MAX_WORLDS = byte.MaxValue; //Номер последнего мира 254 + public const int DEAD_WORLD_ID = byte.MaxValue; //Зарезервированный номер мира для мертвых сущьностей + + //private float _timeScale;//TODO реализовать собсвенныйтайм склей для разных миров + + #region Properties + public ushort ID { get; internal set; } + public bool IsAlive { get; } + public bool IsEmpty { get; } + public Type ArchetypeType { get; } + #endregion + + public EcsPool GetPool() where T : struct; + public EcsFilter GetFilter() where TMask : MaskSingleton; + public ent NewEntity(); + public void Destroy(); + + public bool IsMaskCompatible(Mask mask, int entity); + public bool IsMaskCompatibleWithout(Mask mask, int entity, int otherPoolID); + + internal void OnEntityComponentAdded(int entityID, int changedPoolID); + internal void OnEntityComponentRemoved(int entityID, int changedPoolID); + } + + public class EcsWorld : IEcsWorld + where TArchetype : IWorldArchetype + { + private ushort _id = IEcsWorld.DEAD_WORLD_ID; + + private SparseSet _componentIDToPoolID; + + private SparseSet _entities = new SparseSet(); + private short[] _gens; + + private IEcsPool[] _pools; + + private List[] _filtersByIncludedComponents; + private List[] _filtersByExcludedComponents; + + private EcsFilter[] _filters; + + #region Properties + public ushort ID => _id; + ushort IEcsWorld.ID { get => _id; set => _id = value; } + + public bool IsAlive => _id != IEcsWorld.DEAD_WORLD_ID; + public bool IsEmpty => _entities.Count < 0; + public Type ArchetypeType => typeof(TArchetype); + + #endregion + + #region Constructors + public EcsWorld() + { + _pools = new IEcsPool[512]; + _entities = new SparseSet(512); + _componentIDToPoolID = new SparseSet(512); + _filters = new EcsFilter[512]; + } + #endregion + + #region GetPool + public EcsPool GetPool() where T : struct + { + int uniqueID = ComponentType.uniqueID; + + if (uniqueID >= _pools.Length) + { + Array.Resize(ref _pools, ComponentType.capacity); + Array.Resize(ref _filtersByIncludedComponents, ComponentType.capacity); + Array.Resize(ref _filtersByExcludedComponents, ComponentType.capacity); + } + + if (_pools[uniqueID] == null) + { + _pools[uniqueID] = new EcsPool(this, 512); + } + return (EcsPool)_pools[uniqueID]; + } + #endregion + + #region GetFilter + public EcsFilter GetFilter() where TMask : MaskSingleton + { + var bakedmask = BakedMask.Instance; + + if (_filters.Length >= BakedMask.capacity) + { + Array.Resize(ref _filters, BakedMask.capacity); + } + + if (_filters[bakedmask.UniqueID] == null) + { + _filters[bakedmask.UniqueID] = new EcsFilter(this, bakedmask, 512); + } + return _filters[bakedmask.UniqueID]; + } + #endregion + + #region IsMaskCompatible/IsMaskCompatibleWithout + public bool IsMaskCompatible(Mask mask, int entity) + { + BakedMask bakedMask = mask.GetBaked(); + for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++) + { + if (!_pools[_componentIDToPoolID[bakedMask.Inc[i]]].Has(entity)) + { + return false; + } + } + for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++) + { + if (_pools[_componentIDToPoolID[bakedMask.Exc[i]]].Has(entity)) + { + return false; + } + } + return true; + } + + public bool IsMaskCompatibleWithout(Mask mask, int entity, int otherPoolID) + { + BakedMask bakedMask = mask.GetBaked(); + for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++) + { + int poolID = _componentIDToPoolID[bakedMask.Inc[i]]; + if (poolID == otherPoolID || !_pools[poolID].Has(entity)) + { + return false; + } + } + for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++) + { + int poolID = _componentIDToPoolID[bakedMask.Exc[i]]; + if (poolID != otherPoolID && _pools[poolID].Has(entity)) + { + return false; + } + } + return true; + } + #endregion + + #region EntityChangedReact + void IEcsWorld.OnEntityComponentAdded(int entityID, int changedPoolID) + { + var includeList = _filtersByIncludedComponents[changedPoolID]; + var excludeList = _filtersByExcludedComponents[changedPoolID]; + + if (includeList != null) + { + foreach (var filter in includeList) + { + if (IsMaskCompatible(filter.Mask.Mask, entityID)) + { + filter.Add(entityID); + } + } + } + if (excludeList != null) + { + foreach (var filter in excludeList) + { + if (IsMaskCompatibleWithout(filter.Mask.Mask, entityID, changedPoolID)) + { + filter.Remove(entityID); + } + } + } + } + + void IEcsWorld.OnEntityComponentRemoved(int entityID, int changedPoolID) + { + var includeList = _filtersByIncludedComponents[changedPoolID]; + var excludeList = _filtersByExcludedComponents[changedPoolID]; + + if (includeList != null) + { + foreach (var filter in includeList) + { + if (IsMaskCompatible(filter.Mask.Mask, entityID)) + { + filter.Remove(entityID); + } + } + } + if (excludeList != null) + { + foreach (var filter in excludeList) + { + if (IsMaskCompatibleWithout(filter.Mask.Mask, entityID, changedPoolID)) + { + filter.Add(entityID); + } + } + } + } + #endregion + + #region NewEntity + public Entity NewEntity() + { + int entityID = _entities.GetFree(); + _entities.Normalize(ref _gens); + _gens[entityID]++; + + return new Entity(this, entityID); + } + #endregion + + #region Destroy + public void Destroy() + { + _id = IEcsWorld.DEAD_WORLD_ID; + } + #endregion + + #region Utils + internal abstract class ComponentType + { + internal static int increment = 1; + internal static int capacity = 512; + } + internal sealed class ComponentType : ComponentType + { + internal static int uniqueID; + + static ComponentType() + { + uniqueID = increment++; +#if DEBUG || DCFAECS_NO_SANITIZE_CHECKS + if (increment + 1 > ushort.MaxValue) + { + throw new EcsFrameworkException($"No more room for new component for this {typeof(TArchetype).FullName} IWorldArchetype"); + } +#endif + + if (increment > capacity) + { + capacity <<= 1; + } + } + } + #endregion + } +} diff --git a/src/EcsWorld.cs.meta b/src/IEcsWorld.cs.meta similarity index 100% rename from src/EcsWorld.cs.meta rename to src/IEcsWorld.cs.meta diff --git a/src/Primitives/Entity.cs b/src/Primitives/Entity.cs index 3a3ef35..f3d61d0 100644 --- a/src/Primitives/Entity.cs +++ b/src/Primitives/Entity.cs @@ -8,7 +8,7 @@ namespace DCFApixels.DragonECS [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] public readonly struct ent : IEquatable, IEquatable { - public static readonly long NULL = 0; + public static readonly ent NULL = default; // id - 32 bits // gen - 16 bits @@ -23,25 +23,25 @@ namespace DCFApixels.DragonECS get => (int)(_full >> 32); } [EditorBrowsable(EditorBrowsableState.Never)] - public short gen + public ushort gen { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (short)((_full << 32) >> 48); + get => (ushort)((_full << 32) >> 48); } // но чтобы значене default было NULL сульностью, мир хранится в виде ID + 1 [EditorBrowsable(EditorBrowsableState.Never)] - public short world + public ushort world { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (short)(((_full << 48) >> 48) - 1); + get => (ushort)(((_full << 48) >> 48) - 1); } #endregion #region Constructors [EditorBrowsable(EditorBrowsableState.Never)] - public ent(int id, short gen, short world) + public ent(int id, short gen, ushort world) { _full = ((long)id) << 32; _full += ((long)gen) << 16; @@ -112,11 +112,11 @@ namespace DCFApixels.DragonECS } } - public ref struct Entity + public readonly ref struct Entity { - internal EcsWorld world; - internal int id; - public Entity(EcsWorld world, int id) + internal readonly IEcsWorld world; + internal readonly int id; + public Entity(IEcsWorld world, ent id) { this.world = world; this.id = id; diff --git a/src/TableBuilder.cs b/src/TableBuilder.cs deleted file mode 100644 index 0b97d80..0000000 --- a/src/TableBuilder.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public readonly ref struct TableBuilder - { - private readonly EcsWorld _world; - private readonly EcsWorld.Mask _mask; - - public TableBuilder(EcsWorld world, EcsWorld.Mask mask) - { - _world = world; - _mask = mask; - } - - public EcsPool Cache(mem member) - where T : struct - { - return _world.GetPool(member); - } - public EcsPool Inc(mem member) - where T : struct - { - _mask.Inc(member); - return _world.GetPool(member); - } - public EcsPool Exc(mem member) - where T : struct - { - _mask.Exc(member); - return _world.GetPool(member); - } - } -} diff --git a/src/Utils/BitMask.cs b/src/Utils/BitMask.cs new file mode 100644 index 0000000..6ee6662 --- /dev/null +++ b/src/Utils/BitMask.cs @@ -0,0 +1,38 @@ +using System; + +namespace DCFApixels.DragonECS +{ + public class BitMask + { + private int[] data; + + public int Length { get; private set; } + + public BitMask(int length) + { + Length = length; + data = new int[(length + 31) / 32]; + } + + public void Resize(int newLength) + { + Length = newLength; + Array.Resize(ref data, (newLength + 31) / 32); + } + + public void Set1(int index) + { + data[index / 32] |= 1 << (index % 32); + } + + public void Set0(int index) + { + data[index / 32] &= ~(1 << (index % 32)); + } + + public bool Get(int index) + { + return (data[index / 32] & (1 << (index % 32))) != 0; + } + } +} \ No newline at end of file diff --git a/src/TableBuilder.cs.meta b/src/Utils/BitMask.cs.meta similarity index 83% rename from src/TableBuilder.cs.meta rename to src/Utils/BitMask.cs.meta index 52eba19..d44f22e 100644 --- a/src/TableBuilder.cs.meta +++ b/src/Utils/BitMask.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3ae306a1fd41f78428c59018eeaf21f3 +guid: 88bc1ea51d98c0843a48314fbe00ad9e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/Utils/ComponentTypeMap.cs b/src/Utils/ComponentTypeMap.cs deleted file mode 100644 index 39c5ef3..0000000 --- a/src/Utils/ComponentTypeMap.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace DCFApixels.DragonECS -{ - internal abstract class ComponentType - { - internal static int increment = 1; - internal static int capacity = 512; - } - internal sealed class ComponentType : ComponentType - { - internal static int globalID; - - static ComponentType() - { - globalID = increment++; - if (increment > capacity) - { - capacity <<= 1; - } - } - } - - public class ComponentTypeMap - { - private int[] _dense; - private int[] _sparse; - - private int _count; - - #region Properties - public int Count - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _count; - } - #endregion - - #region Constrcutors - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ComponentTypeMap(int denseCapacity = 64) - { - _dense = new int[denseCapacity]; - _sparse = new int[ComponentType.capacity]; - - _count = 0; - } - #endregion - - #region Contains - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Contains() => Contains(ComponentType.globalID); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool Contains(int globalID) - { - return globalID > 0 && globalID < _sparse.Length && _sparse[globalID] > 0; - } - #endregion - - #region GetID - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetID() - { - int globalID = ComponentType.globalID; - - if (!Contains(globalID)) - { - Add(globalID); - } - - return _dense[globalID]; - } - #endregion - - #region Add - private void Add(int entityID) - { - 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; - } - #endregion - } -} diff --git a/src/Utils/ComponentTypeMap.cs.meta b/src/Utils/ComponentTypeMap.cs.meta deleted file mode 100644 index a34a1ce..0000000 --- a/src/Utils/ComponentTypeMap.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: cef3dbf379d584346bc8a9313c22c563 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/Utils/IntSet.cs b/src/Utils/IntSet.cs deleted file mode 100644 index c1c4852..0000000 --- a/src/Utils/IntSet.cs +++ /dev/null @@ -1,1036 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Security; - -namespace Kibnet -{ - [Serializable] - public class IntSet : ISet, IReadOnlyCollection - { - #region IntSet - - public IntSet() : this(false, false) { } - - public IntSet(IEnumerable items) : this(false, false) - { - UnionWith(items); - } - - public IntSet(bool isFastest) : this(isFastest, false) { } - - public IntSet(bool isFastest, bool isFull) - { - _isFastest = isFastest; - if (isFull) - { - _count = UInt32.MaxValue; - root.Full = true; - root.Cards = null; - } - } - - protected bool _isFastest; - - protected long _count; - - public long LongCount => _count; - - public bool IsFastest - { - get => _isFastest; - set => _isFastest = value; - } - - public Card root = new Card(0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void IntersectWithIntSet(ISet other) - { - foreach (var item in this) - { - if (!other.Contains(item)) - { - InternalRemove(item, true); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void IntersectWithEnumerable(IEnumerable other) - { - var result = new IntSet(); - foreach (var item in other) - { - if (Contains(item)) - { - result.Add(item); - } - } - - root = result.root; - _count = result._count; - } - - /// - /// Checks if this contains of other's elements. Iterates over other's elements and - /// returns false as soon as it finds an element in other that's not in this. - /// Used by SupersetOf, ProperSupersetOf, and SetEquals. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected bool ContainsAllElements(IEnumerable other) - { - foreach (var element in other) - { - if (!Contains(element)) - { - return false; - } - } - - return true; - } - - /// - /// Implementation Notes: - /// If other is a intset and is using same equality comparer, then checking subset is - /// faster. Simply check that each element in this is in other. - /// - /// Note: if other doesn't use same equality comparer, then Contains check is invalid, - /// which is why callers must take are of this. - /// - /// If callers are concerned about whether this is a proper subset, they take care of that. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected bool IsSubsetOfHashSetWithSameComparer(IntSet other) - { - foreach (var item in this) - { - if (!other.Contains(item)) - { - return false; - } - } - - return true; - } - - /// - /// Determines counts that can be used to determine equality, subset, and superset. This - /// is only used when other is an IEnumerable and not a HashSet. If other is a HashSet - /// these properties can be checked faster without use of marking because we can assume - /// other has no duplicates. - /// - /// The following count checks are performed by callers: - /// 1. Equals: checks if unfoundCount = 0 and uniqueFoundCount = _count; i.e. everything - /// in other is in this and everything in this is in other - /// 2. Subset: checks if unfoundCount >= 0 and uniqueFoundCount = _count; i.e. other may - /// have elements not in this and everything in this is in other - /// 3. Proper subset: checks if unfoundCount > 0 and uniqueFoundCount = _count; i.e - /// other must have at least one element not in this and everything in this is in other - /// 4. Proper superset: checks if unfound count = 0 and uniqueFoundCount strictly less - /// than _count; i.e. everything in other was in this and this had at least one element - /// not contained in other. - /// - /// An earlier implementation used delegates to perform these checks rather than returning - /// an ElementCount struct; however this was changed due to the perf overhead of delegates. - /// - /// - /// Allows us to finish faster for equals and proper superset - /// because unfoundCount must be 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected (int UniqueCount, int UnfoundCount) CheckUniqueAndUnfoundElements(IEnumerable other, bool returnIfUnfound) - { - // Need special case in case this has no elements. - if (_count == 0) - { - int numElementsInOther = 0; - foreach (int item in other) - { - numElementsInOther++; - break; // break right away, all we want to know is whether other has 0 or 1 elements - } - - return (UniqueCount: 0, UnfoundCount: numElementsInOther); - } - - Debug.Assert((root.Cards != null) && (_count > 0), "root.Cards was null but count greater than 0"); - - int unfoundCount = 0; // count of items in other not found in this - int uniqueFoundCount = 0; // count of unique items in other found in this - - - var otherAsSet = new IntSet(); - - foreach (int item in other) - { - if (Contains(item)) - { - if (!otherAsSet.Contains(item)) - { - // Item hasn't been seen yet. - otherAsSet.Add(item); - uniqueFoundCount++; - } - } - else - { - unfoundCount++; - if (returnIfUnfound) - { - break; - } - } - } - - return (uniqueFoundCount, unfoundCount); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void SymmetricExceptWithUniqueHashSet(ISet other) - { - foreach (int item in other) - { - if (!Remove(item)) - { - Add(item); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void SymmetricExceptWithEnumerable(IEnumerable other) - { - var itemsToRemove = new IntSet(); - - var itemsAddedFromOther = new IntSet(); - - foreach (int item in other) - { - if (Add(item)) - { - // wasn't already present in collection; flag it as something not to remove - // *NOTE* if location is out of range, we should ignore. BitHelper will - // detect that it's out of bounds and not try to mark it. But it's - // expected that location could be out of bounds because adding the item - // will increase _lastIndex as soon as all the free spots are filled. - itemsAddedFromOther.Add(item); - } - else - { - // already there...if not added from other, mark for remove. - // *NOTE* Even though BitHelper will check that location is in range, we want - // to check here. There's no point in checking items beyond originalCount - // because they could not have been in the original collection - if (!itemsAddedFromOther.Contains(item)) - { - itemsToRemove.Add(item); - } - } - } - - // if anything marked, remove it - foreach (var item in itemsToRemove) - { - Remove(item); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected bool InternalRemove(int item, bool isFastest) - { - var card = root; - for (int i = 0; i < 5; i++) - { - if (card.Full) - { - //TODO Split card - card.Init(i, true); - card.Full = false; - } - - var index = Card.GetIndex(item, i); - if (i < 4) - { - if (card.Cards[index] == null) - { - return false; - } - - card = card.Cards[index]; - } - else - { - var bindex = index >> 3; - var mask = (byte) (1 << (index & 7)); - if ((card.Bytes[bindex] & mask) == 0) - { - return false; - } - - card.Bytes[bindex] ^= mask; - _count--; - if (isFastest == false && card.Bytes[bindex] == byte.MinValue) - { - var isEmpty = card.CheckEmpty(); - if (isEmpty) - { - var parentCard0 = root.Cards[Card.GetIndex(item, 0)]; - var parentCard1 = parentCard0.Cards[Card.GetIndex(item, 1)]; - var parentCard2 = parentCard1.Cards[Card.GetIndex(item, 2)]; - var parentCard3 = parentCard2.Cards[Card.GetIndex(item, 3)]; - //var parentCard4 = parentCard3.Cards[indexes[4]]; - //if (parentCard4.CheckFull()) - if (parentCard3.CheckEmpty()) - if (parentCard2.CheckEmpty()) - if (parentCard1.CheckEmpty()) - if (parentCard0.CheckEmpty()) - ; - } - } - - return true; - } - } - - return false; - } - - #endregion - - #region Card - - [Serializable] - public class Card : IEnumerable, IEnumerable - { - public Card() { } - - public Card(int level) - { - Init(level); - } - - public Card Init(int level, bool isFull = false) - { - if (level < 4) - { - Cards = new Card[64]; - if (isFull) - { - for (int i = 0; i < 64; i++) - { - Cards[i] = new Card { Full = true }; - } - } - } - else - { - Bytes = new byte[32]; - if (isFull) - { - for (int i = 0; i < 32; i++) - { - Bytes[i] = byte.MaxValue; - } - } - } - return this; - } - - public Card[] Cards; - public byte[] Bytes; - public bool Full; - - [SecuritySafeCritical] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetIndex(int value, int level) - { - var temp = (uint)(uint*)(value); - var index = level switch - { - 0 => (temp) >> 26, - 1 => (temp << 6) >> 26, - 2 => (temp << 12) >> 26, - 3 => (temp << 18) >> 26, - 4 => (temp << 24) >> 24, - }; - //TODO переписать на получение указателей - return (int)(int*)index; - } - - IEnumerator IEnumerable.GetEnumerator() - { - if (Cards != null) - { - for (int i = 0; i < 64; i++) - { - if (Cards[i] != null) - { - yield return i; - } - } - } - else if (Full) - { - for (int i = 0; i < 64; i++) - { - yield return i; - } - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - if (Bytes != null) - { - for (int i = 0; i < 32; i++) - { - yield return Bytes[i]; - } - } - else if (Full) - { - for (int i = 0; i < 32; i++) - { - yield return byte.MaxValue; - } - } - } - - public IEnumerator GetEnumerator() - { - if (Bytes != null) - { - return ((IEnumerable)this).GetEnumerator(); - } - else - { - return ((IEnumerable)this).GetEnumerator(); - } - } - - public override string ToString() - { - if (Bytes != null) - { - return String.Create(256, this, GetMask); - } - else - { - return String.Create(64, this, GetMask); - } - } - - public bool CheckFull() - { - if (Bytes != null) - { - Full = Bytes.All(b => b == byte.MaxValue); - if (Full == true) Bytes = null; - } - if (Cards != null) - { - Full = Cards.All(c => c != null && c.Full); - if (Full == true) Cards = null; - } - return Full; - } - - public bool CheckEmpty() - { - if (Bytes != null) - { - if (Bytes.All(b => b == byte.MinValue)) - { - Full = false; - Bytes = null; - return true; - } - return false; - } - if (Cards != null) - { - if (Cards.All(c => c == null || c.CheckEmpty())) - { - Full = false; - Cards = null; - return true; - } - return false; - } - return !Full; - } - - public static void GetMask(Span span, Card arg) - { - if (arg.Full == true) - { - span.Fill('1'); - } - else - { - if (arg.Cards != null) - { - var count = arg.Cards.Length; - for (int i = 0; i < count; i++) - { - span[i] = arg.Cards[i] != null ? '1' : '0'; - } - return; - } - if (arg.Bytes != null) - { - var count = arg.Bytes.Length; - var bytesize = 8; - var p = 0; - for (int i = 0; i < count; i++) - { - var b = arg.Bytes[i]; - for (int j = 0; j < bytesize; j++) - { - span[p++] = ((b & 1) == 1) ? '1' : '0'; - b >>= 1; - } - } - } - } - } - } - - #endregion - - #region IEnumerable - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public IEnumerator GetEnumerator() - { - foreach (var i0 in (IEnumerable)root) - { - var cards1 = root.Full ? root : root.Cards[i0]; - if (cards1 != null) - { - foreach (var i1 in (IEnumerable)cards1) - { - var cards2 = cards1.Full ? cards1 : cards1.Cards[i1]; - if (cards2 != null) - { - foreach (var i2 in (IEnumerable)cards2) - { - var cards3 = cards2.Full ? cards2 : cards2.Cards[i2]; - if (cards3 != null) - { - foreach (var i3 in (IEnumerable)cards3) - { - var bytes = cards3.Full ? cards3 : cards3.Cards[i3]; - if (bytes != null) - { - var bytecount = 0; - foreach (var i4 in (IEnumerable)bytes) - { - for (int j = 0; j < 8; j++) - { - var tail = i4 & (1 << j); - if (tail != 0) - { - var value = i0 << 26; - value |= i1 << 20; - value |= i2 << 14; - value |= i3 << 8; - value |= bytecount << 3; - value |= j; - yield return value; - } - } - - bytecount++; - } - } - } - } - } - } - } - } - } - } - - #endregion - - #region ICollection - - public int Count => (int)_count; - - public bool IsReadOnly { get; } - - void ICollection.Add(int item) => Add(item); - - public void Clear() - { - root = new Card(0); - _count = 0; - } - - bool ICollection.Contains(int item) => Contains(item); - - public bool Contains(int item) - { - var card = root; - for (int i = 0; i < 5; i++) - { - if (card.Full) - { - return true; - } - - var index = Card.GetIndex(item, i); - - if (i < 4) - { - if (card.Cards?[index] == null) - return false; - card = card.Cards[index]; - } - else - { - if (card.Bytes == null) - { - return false; - } - var bindex = index >> 3; - var mask = (byte)(1 << (index & 7)); - return (card.Bytes[bindex] & mask) != 0; - } - } - return false; - } - - public void CopyTo(int[] array) => CopyTo(array, 0, Count); - - public void CopyTo(int[] array, int arrayIndex) => CopyTo(array, arrayIndex, Count); - - public void CopyTo(int[] array, int arrayIndex, int count) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - // Check array index valid index into array. - if (arrayIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, "Non-negative number required."); - } - - // Also throw if count less than 0. - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), count, "Non-negative number required."); - } - - // Will the array, starting at arrayIndex, be able to hold elements? Note: not - // checking arrayIndex >= array.Length (consistency with list of allowing - // count of 0; subsequent check takes care of the rest) - if (arrayIndex > array.Length || count > array.Length - arrayIndex) - { - throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); - } - - var enumerator = GetEnumerator(); - for (int i = 0; i < _count && count != 0; i++) - { - if (enumerator.MoveNext()) - { - array[arrayIndex++] = enumerator.Current; - count--; - } - } - } - - public bool Remove(int item) => InternalRemove(item, _isFastest); - - #endregion - - #region ISet - - public bool Add(int item) - { - var card = root; - for (int i = 0; i < 5; i++) - { - if (card.Full) - { - return false; - } - - var index = Card.GetIndex(item, i); - if (i < 4) - { - if (card.Cards == null) - { - card.Init(i); - } - if (card.Cards[index] == null) - { - var newcard = new Card(i + 1); - card.Cards[index] = newcard; - card = newcard; - } - else - { - card = card.Cards[index]; - } - } - else - { - if (card.Bytes == null) - { - card.Init(i); - } - var bindex = index >> 3; - var mask = (byte)(1 << (index & 7)); - if ((card.Bytes[bindex] & mask) == 0) - { - card.Bytes[bindex] |= mask; - _count++; - if (_isFastest == false && card.Bytes[bindex] == byte.MaxValue) - { - var full = card.CheckFull(); - if (full) - { - var parentCard0 = root.Cards[Card.GetIndex(item, 0)]; - var parentCard1 = parentCard0.Cards[Card.GetIndex(item, 1)]; - var parentCard2 = parentCard1.Cards[Card.GetIndex(item, 2)]; - var parentCard3 = parentCard2.Cards[Card.GetIndex(item, 3)]; - //var parentCard4 = parentCard3.Cards[indexes[4]]; - //if (parentCard4.CheckFull()) - if (parentCard3.CheckFull()) - if (parentCard2.CheckFull()) - if (parentCard1.CheckFull()) - if (parentCard0.CheckFull()) ; - } - } - return true; - } - } - } - return false; - } - - public void UnionWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - foreach (var item in other) - { - Add(item); - } - } - - public void IntersectWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // Intersection of anything with empty set is empty set, so return if count is 0. - // Same if the set intersecting with itself is the same set. - if (Count == 0 || ReferenceEquals(other, this)) - { - return; - } - - // If other is known to be empty, intersection is empty set; remove all elements, and we're done. - if (other is ICollection otherAsCollection) - { - if (otherAsCollection.Count == 0) - { - Clear(); - return; - } - - if (other is ISet otherAsSet) - { - IntersectWithIntSet(otherAsSet); - return; - } - } - - IntersectWithEnumerable(other); - } - - public void ExceptWith(IEnumerable other) - { - foreach (var item in other) - { - Remove(item); - } - } - - public void SymmetricExceptWith(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // If set is empty, then symmetric difference is other. - if (Count == 0) - { - UnionWith(other); - return; - } - - // Special-case this; the symmetric difference of a set with itself is the empty set. - if (other == this) - { - Clear(); - return; - } - - // If other is a HashSet, it has unique elements according to its equality comparer, - // but if they're using different equality comparers, then assumption of uniqueness - // will fail. So first check if other is a hashset using the same equality comparer; - // symmetric except is a lot faster and avoids bit array allocations if we can assume - // uniqueness. - if (other is ISet otherAsSet) - { - SymmetricExceptWithUniqueHashSet(otherAsSet); - } - else - { - SymmetricExceptWithEnumerable(other); - } - } - - public bool IsSubsetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // The empty set is a subset of any set, and a set is a subset of itself. - // Set is always a subset of itself - if (Count == 0 || other == this) - { - return true; - } - - // Faster if other has unique elements according to this equality comparer; so check - // that other is a hashset using the same equality comparer. - if (other is IntSet otherAsSet) - { - // if this has more elements then it can't be a subset - if (Count > otherAsSet.Count) - { - return false; - } - - // already checked that we're using same equality comparer. simply check that - // each element in this is contained in other. - return IsSubsetOfHashSetWithSameComparer(otherAsSet); - } - - (int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: false); - return uniqueCount == Count && unfoundCount >= 0; - } - - public bool IsSupersetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // A set is always a superset of itself. - if (other == this) - { - return true; - } - - // Try to fall out early based on counts. - if (other is ICollection otherAsCollection) - { - // If other is the empty set then this is a superset. - if (otherAsCollection.Count == 0) - { - return true; - } - - // Try to compare based on counts alone if other is a hashset with same equality comparer. - if (other is IntSet otherAsSet && otherAsSet.Count > Count) - { - return false; - } - } - - return ContainsAllElements(other); - } - - public bool IsProperSupersetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // The empty set isn't a proper superset of any set, and a set is never a strict superset of itself. - if (Count == 0 || other == this) - { - return false; - } - - if (other is ICollection otherAsCollection) - { - // If other is the empty set then this is a superset. - if (otherAsCollection.Count == 0) - { - // Note that this has at least one element, based on above check. - return true; - } - - // Faster if other is a hashset with the same equality comparer - if (other is IntSet otherAsSet) - { - if (otherAsSet.Count >= Count) - { - return false; - } - - // Now perform element check. - return ContainsAllElements(otherAsSet); - } - } - - // Couldn't fall out in the above cases; do it the long way - (int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: true); - return uniqueCount < Count && unfoundCount == 0; - } - - public bool IsProperSubsetOf(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // No set is a proper subset of itself. - if (other == this) - { - return false; - } - - if (other is ICollection otherAsCollection) - { - // No set is a proper subset of an empty set. - if (otherAsCollection.Count == 0) - { - return false; - } - - // The empty set is a proper subset of anything but the empty set. - if (Count == 0) - { - return otherAsCollection.Count > 0; - } - - // Faster if other is a hashset (and we're using same equality comparer). - if (other is IntSet otherAsSet) - { - if (Count >= otherAsSet.Count) - { - return false; - } - - // This has strictly less than number of items in other, so the following - // check suffices for proper subset. - return IsSubsetOfHashSetWithSameComparer(otherAsSet); - } - } - - (int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: false); - return uniqueCount == Count && unfoundCount > 0; - } - - public bool Overlaps(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - if (Count == 0) - { - return false; - } - - // Set overlaps itself - if (other == this) - { - return true; - } - - foreach (var element in other) - { - if (Contains(element)) - { - return true; - } - } - - return false; - } - - public bool SetEquals(IEnumerable other) - { - if (other == null) - { - throw new ArgumentNullException(nameof(other)); - } - - // A set is equal to itself. - if (other == this) - { - return true; - } - - // Faster if other is a hashset and we're using same equality comparer. - if (other is IntSet otherAsSet) - { - // Attempt to return early: since both contain unique elements, if they have - // different counts, then they can't be equal. - if (Count != otherAsSet.Count) - { - return false; - } - - // Already confirmed that the sets have the same number of distinct elements, so if - // one is a superset of the other then they must be equal. - return ContainsAllElements(otherAsSet); - } - else - { - // If this count is 0 but other contains at least one element, they can't be equal. - if (Count == 0 && - other is ICollection otherAsCollection && - otherAsCollection.Count > 0) - { - return false; - } - - (int uniqueCount, int unfoundCount) = CheckUniqueAndUnfoundElements(other, returnIfUnfound: true); - return uniqueCount == Count && unfoundCount == 0; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/src/Utils/IntSet.cs.meta b/src/Utils/IntSet.cs.meta deleted file mode 100644 index d56fb85..0000000 --- a/src/Utils/IntSet.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9cad30d5b37df1d48bc2abe5d1743649 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/test/TransformTable.cs b/test/TransformTable.cs deleted file mode 100644 index 3ada8c7..0000000 --- a/test/TransformTable.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using DCFApixels; -using UnityEngine; - -namespace DCFApixels.DragonECS -{ - public class TransformTable : EcsTable - { - public readonly EcsPool position; - public readonly EcsPool rotation; - public readonly EcsPool scale; - - public TransformTable(ref TableBuilder tableBuilder) : base(ref tableBuilder) - { - position = tableBuilder.Inc(Mems.position); - rotation = tableBuilder.Inc(Mems.rotation); - scale = tableBuilder.Inc(Mems.scale); - } - } - - public class PositionTable : EcsTable - { - public readonly EcsPool position; - public readonly EcsPool rotation; - public readonly EcsPool scale; - - public PositionTable(ref TableBuilder tableBuilder) : base(ref tableBuilder) - { - position = tableBuilder.Inc(Mems.position); - rotation = tableBuilder.Cache(Mems.rotation); - scale = tableBuilder.Cache(Mems.scale); - } - } - - public class RotationTable : EcsTable - { - public readonly EcsPool position; - public readonly EcsPool rotation; - public readonly EcsPool scale; - - public RotationTable(ref TableBuilder tableBuilder) : base(ref tableBuilder) - { - position = tableBuilder.Cache(Mems.position); - rotation = tableBuilder.Inc(Mems.rotation); - scale = tableBuilder.Cache(Mems.scale); - } - } - - public class ScaleTable : EcsTable - { - public readonly EcsPool position; - public readonly EcsPool rotation; - public readonly EcsPool scale; - - public ScaleTable(ref TableBuilder tableBuilder) : base(ref tableBuilder) - { - position = tableBuilder.Cache(Mems.position); - rotation = tableBuilder.Cache(Mems.rotation); - scale = tableBuilder.Inc(Mems.scale); - } - } -} diff --git a/test/TransformTable.cs.meta b/test/TransformTable.cs.meta deleted file mode 100644 index 2a12223..0000000 --- a/test/TransformTable.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d6c6320184f942444b499e3f027a72a7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: