Rework runner system, SparseSet etc
This commit is contained in:
Mikhail 2023-03-11 17:11:40 +08:00
parent b73652cb37
commit 66b136df92
19 changed files with 449 additions and 322 deletions

View File

@ -1,15 +0,0 @@
namespace DCFApixels.DragonECS
{
public class FiltersProcessor : IEcsGReceive<_OnComponentAdded>, IEcsGReceive<_OnComponentRemoved>
{
void IEcsGReceive<_OnComponentAdded>.Do<T>(EcsSession session, in _OnComponentAdded message, in T obj)
{
}
void IEcsGReceive<_OnComponentRemoved>.Do<T>(EcsSession session, in _OnComponentRemoved message, in T obj)
{
}
}
}

View File

@ -17,9 +17,8 @@ namespace DCFApixels.DragonECS
void IDo<_PreInit>.Do(EcsSession session)
{
_OnInject<T> m = new _OnInject<T>(_injectedData);
var messenger = session.GetMessenger<_OnInject<T>>();
messenger.Send(in m);
messenger.Send(new _OnInject<T>(_injectedData));
messenger.Destroy();
}
}

View File

@ -34,13 +34,13 @@ namespace DCFApixels.DragonECS
public EcsGroup(IEcsWorld world, int entitiesCapacity, int delayedOpsCapacity = 128)
{
_source = world;
_entities = new SparseSet(entitiesCapacity);
_entities = new SparseSet(entitiesCapacity, entitiesCapacity);
_delayedOps = new DelayedOp[delayedOpsCapacity];
_lockCount = 0;
}
#endregion
#region add/remove
#region Add/Remove
public void Add(int entityID)
{
if (_lockCount > 0)

View File

@ -40,7 +40,7 @@ namespace DCFApixels.DragonECS
public EcsPool(IEcsWorld source, int capacity)
{
_source = source;
_sparseSet = new SparseSet(capacity);
_sparseSet = new SparseSet(capacity, capacity);
_denseItems =new T[capacity];
}
@ -96,28 +96,4 @@ namespace DCFApixels.DragonECS
public override int GetHashCode() => _source.GetHashCode() + ID;
#endregion
}
public static partial class EntityExtensions
{
public static ref readonly T Read<T>(this in Entity self)
where T : struct
{
return ref self.world.GetPool<T>().Read(self.id);
}
public static ref T Write<T>(this in Entity self)
where T : struct
{
return ref self.world.GetPool<T>().Write(self.id);
}
public static bool Has<T>(this in Entity self)
where T : struct
{
return self.world.GetPool<T>().Has(self.id);
}
public static void Del<T>(this in Entity self)
where T : struct
{
self.world.GetPool<T>().Del(self.id);
}
}
}

View File

@ -6,6 +6,34 @@ using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public class EcsWorldMap
{
private Dictionary<(Type, string), IEcsWorld> _worlds = new Dictionary<(Type, string), IEcsWorld>(8);
private bool _built = false;
public void Add<TArchetype>(EcsWorld<TArchetype> world, string name = "")
where TArchetype : IWorldArchetype
{
if(_built) { throw new Exception($"Cant change built {nameof(EcsWorldMap)}"); }
_worlds.Add((typeof(TArchetype), name), world);
}
public EcsWorld<TArchetype> Get<TArchetype>(string name ="")
where TArchetype : IWorldArchetype
{
return (EcsWorld<TArchetype>)_worlds[(typeof(TArchetype), name)];
}
public IEcsWorld Get(Type type, string name = "")
{
return _worlds[(type, name)];
}
public void Build()
{
_built = true;
}
}
public class EcsSession
{
@ -18,14 +46,13 @@ namespace DCFApixels.DragonECS
private bool _isInit = false;
private bool _isDestoryed = false;
private int _worldIdIncrement;
private Dictionary<string, IEcsWorld> _worldsDict = new Dictionary<string, IEcsWorld>();
private List<IEcsWorld> _worlds = new List<IEcsWorld>();
private Dictionary<Type, IEcsProcessorsRunner> _runners;
private Dictionary<Type, IEcsProcessorsMessenger> _messengers;
private EcsProcessorsRunner<_Run> _runRunnerCache;
private EcsWorldMap _worldMap = new EcsWorldMap();
#region Properties
public ReadOnlyCollection<IEcsProcessor> AllProcessors => _allProcessorsSealed;
@ -33,7 +60,6 @@ namespace DCFApixels.DragonECS
#region React Runners/Messengers
public EcsProcessorsRunner<TDoTag> GetRunner<TDoTag>()
where TDoTag : IEcsDoTag
{
Type type = typeof(TDoTag);
if (_runners.TryGetValue(type, out IEcsProcessorsRunner result))
@ -45,7 +71,6 @@ namespace DCFApixels.DragonECS
return (EcsProcessorsRunner<TDoTag>)result;
}
internal void OnRunnerDetroyed<TDoTag>(EcsProcessorsRunner<TDoTag> target)
where TDoTag : IEcsDoTag
{
_runners.Remove(typeof(TDoTag));
}
@ -62,18 +87,6 @@ namespace DCFApixels.DragonECS
_messengers.Add(type, result);
return (EcsProcessorsMessenger<TMessege>)result;
}
public EcsProcessorsGMessenger<TMessege> GetGMessenger<TMessege>()
where TMessege : IEcsMessage
{
Type type = typeof(EcsProcessorsGMessenger<TMessege>);
if (_messengers.TryGetValue(type, out IEcsProcessorsMessenger result))
{
return (EcsProcessorsGMessenger<TMessege>)result;
}
result = new EcsProcessorsMessenger<TMessege>(this);
_messengers.Add(type, result);
return (EcsProcessorsGMessenger<TMessege>)result;
}
internal void OnMessengerDetroyed<TMessege>(IEcsProcessorsMessenger<TMessege> target)
where TMessege : IEcsMessage
{
@ -85,7 +98,6 @@ namespace DCFApixels.DragonECS
public EcsSession Add(IEcsProcessor system)
{
CheckInitForMethod(nameof(AddWorld));
_allProcessors.Add(system);
return this;
}
@ -93,8 +105,7 @@ namespace DCFApixels.DragonECS
where TArchetype : IWorldArchetype
{
CheckInitForMethod(nameof(AddWorld));
_worlds.Add(new EcsWorld(_worldIdIncrement++));
_worldMap.Add(world, name);
return this;
}
@ -104,9 +115,12 @@ namespace DCFApixels.DragonECS
public void Init()
{
CheckInitForMethod(nameof(Init));
_worldMap.Build();
_allProcessorsSealed = _allProcessors.AsReadOnly();
_isInit = true;
GetMessenger<_OnInject<EcsWorldMap>>().Send(new _OnInject<EcsWorldMap>(_worldMap));
GetRunner<_PreInit>().Run();
GetRunner<_Init>().Run();
@ -158,6 +172,7 @@ namespace DCFApixels.DragonECS
#region Utils
#endregion
}
}

View File

@ -8,20 +8,16 @@ using System.Threading.Tasks;
namespace DCFApixels.DragonECS
{
public interface IWorldArchetype { }
public struct DefaultArchetype : IWorldArchetype { }
public struct DefaultWorld : 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; }
public int ID { get; }
#endregion
public EcsPool<T> GetPool<T>() where T : struct;
@ -36,15 +32,29 @@ namespace DCFApixels.DragonECS
internal void OnEntityComponentRemoved(int entityID, int changedPoolID);
}
public class EcsWorld<TArchetype> : IEcsWorld
public abstract class EcsWorld
{
internal static IEcsWorld[] Worlds = new IEcsWorld[8];
private static IntDispenser _worldIdDispenser = new IntDispenser();
public readonly short id;
public EcsWorld()
{
id = (short)_worldIdDispenser.GetFree();
Worlds[id] = (IEcsWorld)this;
}
}
public sealed class EcsWorld<TArchetype> : EcsWorld, IEcsWorld
where TArchetype : IWorldArchetype
{
private ushort _id = IEcsWorld.DEAD_WORLD_ID;
private IntDispenser _entityDispenser;
private EcsGroup _entities;
private SparseSet _componentIDToPoolID;
private SparseSet _entities = new SparseSet();
private short[] _gens;
private short[] _componentCounts;
private IEcsPool[] _pools;
@ -54,22 +64,18 @@ namespace DCFApixels.DragonECS
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);
public int ID => id;
#endregion
#region Constructors
public EcsWorld()
{
_entityDispenser = new IntDispenser();
_pools = new IEcsPool[512];
_entities = new SparseSet(512);
_componentIDToPoolID = new SparseSet(512);
_filters = new EcsFilter[512];
_filters = new EcsFilter[64];
_entities = new EcsGroup(this, 512);
}
#endregion
@ -117,14 +123,14 @@ namespace DCFApixels.DragonECS
BakedMask bakedMask = mask.GetBaked<TArchetype>();
for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++)
{
if (!_pools[_componentIDToPoolID[bakedMask.Inc[i]]].Has(entity))
if (!_pools[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))
if (_pools[bakedMask.Exc[i]].Has(entity))
{
return false;
}
@ -137,7 +143,7 @@ namespace DCFApixels.DragonECS
BakedMask bakedMask = mask.GetBaked<TArchetype>();
for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++)
{
int poolID = _componentIDToPoolID[bakedMask.Inc[i]];
int poolID = bakedMask.Inc[i];
if (poolID == otherPoolID || !_pools[poolID].Has(entity))
{
return false;
@ -145,7 +151,7 @@ namespace DCFApixels.DragonECS
}
for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++)
{
int poolID = _componentIDToPoolID[bakedMask.Exc[i]];
int poolID = bakedMask.Exc[i];
if (poolID != otherPoolID && _pools[poolID].Has(entity))
{
return false;
@ -212,20 +218,18 @@ namespace DCFApixels.DragonECS
#endregion
#region NewEntity
public Entity NewEntity()
public ent NewEntity()
{
int entityID = _entities.GetFree();
_entities.Normalize(ref _gens);
_gens[entityID]++;
return new Entity(this, entityID);
int entid = _entityDispenser.GetFree();
if(_gens.Length < entid) Array.Resize(ref _gens, _gens.Length << 1);
return new ent(entid, _gens[entid]++, id);
}
#endregion
#region Destroy
public void Destroy()
{
_id = IEcsWorld.DEAD_WORLD_ID;
}
#endregion

View File

@ -1,3 +1,6 @@
Мервтый мир - значение byte 255, зарезервированный адишник мира, все что ссылается на мертвый мир считается так же мертвым.
DCFAECS_NO_SANITIZE_CHECKS - отвключение дополнительных проверок
DCFAECS_NO_SANITIZE_CHECKS - отвключение дополнительных проверок
public const int MAX_WORLDS = byte.MaxValue; //Номер последнего мира 254
public const int DEAD_WORLD_ID = byte.MaxValue; //Зарезервированный номер мира для мертвых сущьностей

View File

@ -2,14 +2,12 @@
{
public interface IEcsProcessor { }
public interface IEcsDoTag { }
public struct _PreInit : IEcsDoTag { }
public struct _Init : IEcsDoTag { }
public struct _Run : IEcsDoTag { }
public struct _Destroy : IEcsDoTag { }
public struct _PostDestroy : IEcsDoTag { }
public struct _PreInit { }
public struct _Init { }
public struct _Run { }
public struct _Destroy { }
public struct _PostDestroy { }
public interface IDo<TTag> : IEcsProcessor
where TTag : IEcsDoTag
{
public void Do(EcsSession session);
}
@ -53,19 +51,4 @@
{
public void Do(EcsSession session, in TMessage m);
}
public struct _OnComponentRemoved : IEcsMessage
{
public int entityID;
}
public struct _OnComponentAdded : IEcsMessage
{
public int entityID;
}
public interface IEcsGReceive<TMessage> : IEcsProcessor
where TMessage : IEcsMessage
{
public void Do<T>(EcsSession session, in TMessage m, in T obj);
}
}

View File

@ -31,17 +31,17 @@ namespace DCFApixels.DragonECS
}
// но чтобы значене default было NULL сульностью, мир хранится в виде ID + 1
[EditorBrowsable(EditorBrowsableState.Never)]
public ushort world
public short world
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (ushort)(((_full << 48) >> 48) - 1);
get => (short)(((_full << 48) >> 48) - 1);
}
#endregion
#region Constructors
[EditorBrowsable(EditorBrowsableState.Never)]
public ent(int id, short gen, ushort world)
public ent(int id, short gen, short world)
{
_full = ((long)id) << 32;
_full += ((long)gen) << 16;
@ -104,22 +104,83 @@ namespace DCFApixels.DragonECS
#endregion
}
public static class entExtensions
public static partial class entExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNull(this in ent self)
{
return self == ent.NULL;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T Read<T>(this in ent self)
where T : struct
{
return ref EcsWorld.Worlds[self.world].GetPool<T>().Read(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Write<T>(this in ent self)
where T : struct
{
return ref EcsWorld.Worlds[self.world].GetPool<T>().Write(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Has<T>(this in ent self)
where T : struct
{
return EcsWorld.Worlds[self.world].GetPool<T>().Has(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Del<T>(this in ent self)
where T : struct
{
EcsWorld.Worlds[self.world].GetPool<T>().Del(self.id);
}
}
public readonly ref struct Entity
public struct Entity
{
internal readonly IEcsWorld world;
internal readonly int id;
public Entity(IEcsWorld world, ent id)
public IEcsWorld world;
public int id;
public Entity(IEcsWorld world, int id)
{
this.world = world;
this.id = id;
}
}
public static partial class EntityExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNull(this in Entity self)
{
return self.world == null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T Read<T>(this in Entity self)
where T : struct
{
return ref self.world.GetPool<T>().Read(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Write<T>(this in Entity self)
where T : struct
{
return ref self.world.GetPool<T>().Write(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Has<T>(this in Entity self)
where T : struct
{
return self.world.GetPool<T>().Has(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Del<T>(this in Entity self)
where T : struct
{
self.world.GetPool<T>().Del(self.id);
}
}
}

View File

@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
namespace DCFApixels.DragonECS
{
public class EcsProcessorsGMessenger<TMessage> : IEcsProcessorsMessenger<TMessage>, IDisposable
where TMessage : IEcsMessage
{
private readonly EcsSession _source;
private readonly IEcsGReceive<TMessage>[] _targets;
public EcsSession Source => _source;
public IReadOnlyList<IEcsGReceive<TMessage>> Targets => _targets;
internal EcsProcessorsGMessenger(EcsSession source)
{
_source = source;
List<IEcsGReceive<TMessage>> list = new List<IEcsGReceive<TMessage>>();
foreach (var item in _source.AllProcessors)
{
if (item is IEcsGReceive<TMessage> targetItem)
{
list.Add(targetItem);
}
}
_targets = list.ToArray();
}
public void Send<T>(in TMessage message, in T obj)
{
foreach (var item in _targets)
{
item.Do(_source, in message, in obj);
}
}
public void Destroy() => _source.OnMessengerDetroyed(this);
void IDisposable.Dispose() => Destroy();
}
}

View File

@ -32,6 +32,10 @@ namespace DCFApixels.DragonECS
_targets = list.ToArray();
}
public void Send(TMessage message)
{
Send(in message);
}
public void Send(in TMessage message)
{
foreach (var item in _targets)

View File

@ -9,7 +9,6 @@ namespace DCFApixels.DragonECS
public void Run();
}
public class EcsProcessorsRunner<TDoTag> : IEcsProcessorsRunner, IDisposable
where TDoTag : IEcsDoTag
{
private readonly EcsSession _source;
private readonly IDo<TDoTag>[] _targets;

126
src/React/RunnerHandler.cs Normal file
View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
sealed class RunnerFilterAttribute : Attribute
{
public readonly Type interfaceType;
public readonly object filter;
public RunnerFilterAttribute(Type interfaceType, object filter)
{
this.interfaceType = interfaceType;
this.filter = filter;
}
}
public interface IProcessor { }
public static class IProcessorExtensions
{
public static bool IsRunner(this IProcessor self)
{
return self is IRunner;
}
}
internal static class RunnerActivator
{
private static bool _isInit = false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Init()
{
if (_isInit) return;
Type targetType = typeof(Runner<>);
var subclasses = Assembly.GetAssembly(targetType).GetTypes().Where(type => type.BaseType != null && type.BaseType.IsGenericType && targetType == type.BaseType.GetGenericTypeDefinition());
foreach (var item in subclasses)
{
item.BaseType.GetMethod("Init", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { item });
}
_isInit = true;
}
}
public interface IRunner { }
public abstract class Runner<TInterface> : IProcessor, IRunner
where TInterface : IProcessor
{
internal static void Init(Type subclass)
{
#if DEBUG || DCFAECS_NO_SANITIZE_CHECKS
if (_subclass != null)
{
throw new ArgumentException($"The Runner<{typeof(TInterface).FullName}> can only have one subclass");
}
Type interfaceType = typeof(TInterface);
var interfaces = interfaceType.GetInterfaces();
if (interfaceType.IsInterface == false)
{
throw new ArgumentException($"{typeof(TInterface).FullName} is not interface");
}
if (interfaces.Length != 1 || interfaces[0] != typeof(IProcessor))
{
throw new ArgumentException($"{typeof(TInterface).FullName} does not directly inherit the {nameof(IProcessor)} interface");
}
#endif
_subclass = subclass;
}
public static TInterface Instantiate(IEnumerable<IProcessor> targets, object filter)
{
Type interfaceType = typeof(TInterface);
IEnumerable<IProcessor> newTargets;
if (filter != null)
{
newTargets = targets.Where(o =>
{
if (o is TInterface == false) return false;
var atr = o.GetType().GetCustomAttribute<RunnerFilterAttribute>();
return atr != null && atr.interfaceType == interfaceType && atr.filter.Equals(filter);
});
}
else
{
newTargets = targets.Where(o =>
{
if (o is TInterface == false) return false;
var atr = o.GetType().GetCustomAttribute<RunnerFilterAttribute>();
return atr == null || atr.interfaceType == interfaceType && atr.filter == null;
});
}
return Instantiate(newTargets.Select(o => (TInterface)o).ToArray());
}
public static TInterface Instantiate(IEnumerable<IProcessor> targets)
{
return Instantiate(targets.Where(o => o is TInterface).Select(o => (TInterface)o).ToArray());
}
internal static TInterface Instantiate(TInterface[] targets)
{
RunnerActivator.Init();
var instance = (Runner<TInterface>)Activator.CreateInstance(_subclass);
return (TInterface)(IProcessor)instance.Set(targets);
}
private static Type _subclass;
protected TInterface[] targets;
private Runner<TInterface> Set(TInterface[] targets)
{
this.targets = targets;
return this;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d340db685521a624792157f350923088
guid: ae6eb3472cd282b46b26ab9f1e97ec81
MonoImporter:
externalObjects: {}
serializedVersion: 2

44
src/Utils/IntDispenser.cs Normal file
View File

@ -0,0 +1,44 @@
using System.Collections.Concurrent;
using System.Threading;
namespace DCFApixels.DragonECS
{
internal sealed class IntDispenser
{
private readonly ConcurrentStack<int> _freeInts;
private int _increment;
#region Properties
public int LastInt => _increment;
#endregion
#region Constructor
public IntDispenser()
{
_freeInts = new ConcurrentStack<int>();
_increment = 0;
}
public IntDispenser(int startIncrement)
{
_freeInts = new ConcurrentStack<int>();
_increment = startIncrement;
}
#endregion
#region GetFree/Release
public int GetFree()
{
if (!_freeInts.TryPop(out int result))
{
result = Interlocked.Increment(ref _increment);
}
return result;
}
public void Release(int released)
{
_freeInts.Push(released);
}
#endregion
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: db219c3a9acc0964f8808af51e32afc3
guid: a20cbd833c35ff3438f9fe003903c0c3
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -3,115 +3,124 @@ using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using coretype = System.Int32;
namespace DCFApixels.DragonECS
{
public class SparseSet : IEnumerable<coretype>, ICollection<coretype>, IReadOnlyCollection<coretype>
public class SparseSet : IEnumerable<int>, ICollection<int>, IReadOnlyCollection<int>
{
public const int DEFAULT_CAPACITY = 16;
public const int MAX_CAPACITY = coretype.MaxValue;
public const int DEFAULT_DENSE_CAPACITY = 8;
public const int DEFAULT_SPARSE_CAPACITY = 16;
private coretype[] _dense;
private coretype[] _sparse;
public const int MIN_CAPACITY = 4;
private coretype _count;
public const int MAX_CAPACITY = int.MaxValue;
private coretype _denseCapacity;
private int[] _dense;
private int[] _sparse;
private int _count;
#region Properties
public int Count => _count;
public int CapacityDense
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _denseCapacity;
get => _count;
}
public int CapacitySparse
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense.Length;
}
public coretype this[int index]
public int CapacitySparse
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _sparse.Length;
}
public int this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#if DEBUG
get
{
ThrowHalper.CheckOutOfRange(this, (coretype)index);
#if DEBUG
ThrowHalper.CheckOutOfRange(this, index);
#endif
return _dense[index];
}
#else
get => _dense[index];
#endif
}
#endregion
#region Constructors
public SparseSet() : this(DEFAULT_CAPACITY) { }
public SparseSet(coretype capacity)
public SparseSet() : this(DEFAULT_DENSE_CAPACITY, DEFAULT_SPARSE_CAPACITY) { }
public SparseSet(int denseCapacity, int sparseCapacity)
{
#if DEBUG
ThrowHalper.CheckCapacity(capacity);
#endif
_dense = new coretype[capacity];
_sparse = new coretype[capacity];
for (coretype i = 0; i < _sparse.Length; i++)
{
_dense[i] = i;
_sparse[i] = i;
}
_count = 0;
_denseCapacity = 0;
denseCapacity = denseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(denseCapacity);
sparseCapacity = sparseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(sparseCapacity);
_dense = new int[denseCapacity];
_sparse = new int[sparseCapacity];
Reset();
}
#endregion
#region Add/AddRange/GetFree
public void Add<T>(coretype value, ref T[] normalizedArray)
#region Add/AddRange
public void Add<T>(int value, ref T[] normalizedArray)
{
Add(value);
Normalize(ref normalizedArray);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(coretype value)
public void Add(int value)
{
#if DEBUG
ThrowHalper.CheckValueIsPositive(value);
ThrowHalper.CheckValueNotContained(this, value);
#endif
if (value > CapacitySparse)
if (_count >= _dense.Length)
Array.Resize(ref _dense, _dense.Length << 1);
if (value > _sparse.Length)
{
coretype neadedSpace = (coretype)_dense.Length;
while (value >= neadedSpace) neadedSpace <<= 1;
Resize(neadedSpace);
int neadedSpace = _sparse.Length;
while (value >= neadedSpace)
neadedSpace <<= 1;
int i = _sparse.Length;
Array.Resize(ref _sparse, neadedSpace);
//loop unwinding
for (; i < neadedSpace;)
{
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
}
}
Swap(value, _count++);
if (_count > _denseCapacity) _denseCapacity <<= 1;
_dense[_count] = value;
_sparse[value] = _count++;
}
public bool TryAdd<T>(coretype value, ref T[] normalizedArray)
public bool TryAdd<T>(int value, ref T[] normalizedArray)
{
if (Contains(value)) return false;
Add(value);
Normalize(ref normalizedArray);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryAdd(coretype value)
public bool TryAdd(int value)
{
if (Contains(value)) return false;
Add(value);
return true;
}
public void AddRange<T>(IEnumerable<coretype> range, ref T[] normalizedArray)
public void AddRange<T>(IEnumerable<int> range, ref T[] normalizedArray)
{
AddRange(range);
Normalize(ref normalizedArray);
}
public void AddRange(IEnumerable<coretype> range)
public void AddRange(IEnumerable<int> range)
{
foreach (var item in range)
{
@ -119,46 +128,28 @@ namespace DCFApixels.DragonECS
Add(item);
}
}
/// <summary>Adds a value between 0 and Capacity to the array and returns it.</summary>
/// <returns>Value between 0 and Capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public coretype GetFree<T>(ref T[] normalizedArray)
{
coretype result = GetFree();
Normalize(ref normalizedArray);
return result;
}
/// <summary>Adds a value between 0 and Capacity to the array and returns it.</summary>
/// <returns>Value between 0 and Capacity</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public coretype GetFree()
{
if (++_count >= CapacitySparse) AddSpaces();
if (_count > _denseCapacity) _denseCapacity <<= 1;
return _dense[_count - 1];
}
#endregion
#region Contains
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(coretype value)
public bool Contains(int value)
{
return value >= 0 && value < CapacitySparse && _sparse[value] < _count;
return value >= 0 && value < CapacitySparse && _sparse[value] >= 0;
}
#endregion
#region Remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Remove(coretype value)
public void Remove(int value)
{
#if DEBUG
ThrowHalper.CheckValueContained(this, value);
#endif
Swap(_sparse[value], --_count);
_dense[_sparse[value]] = _dense[--_count];
_sparse[_dense[_count]] = _sparse[value];
_sparse[value] = -1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryRemove(coretype value)
public bool TryRemove(int value)
{
if (!Contains(value)) return false;
Remove(value);
@ -166,7 +157,7 @@ namespace DCFApixels.DragonECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(coretype index)
public void RemoveAt(int index)
{
#if DEBUG
ThrowHalper.CheckOutOfRange(this, index);
@ -183,7 +174,7 @@ namespace DCFApixels.DragonECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(coretype value)
public int IndexOf(int value)
{
if (value < 0 || !Contains(value)) return -1;
return _sparse[value];
@ -191,8 +182,8 @@ namespace DCFApixels.DragonECS
public void Sort()
{
coretype increment = 0;
for (coretype i = 0; i < CapacitySparse; i++)
int increment = 0;
for (int i = 0; i < CapacitySparse; i++)
{
if (_sparse[i] < _count)
{
@ -204,18 +195,17 @@ namespace DCFApixels.DragonECS
public void HardSort()
{
coretype inc = 0;
coretype inc2 = _count;
for (coretype i = 0; i < CapacitySparse; i++)
int inc = 0;
int inc2 = _count;
for (int i = 0; i < CapacitySparse; i++)
{
if (_sparse[i] < _count)
if (_sparse[i] >= 0)
{
_sparse[i] = inc;
_dense[inc++] = i;
}
else
{
_sparse[i] = inc2;
_dense[inc2++] = i;
}
}
@ -225,14 +215,14 @@ namespace DCFApixels.DragonECS
{
other._count = _count;
if (CapacitySparse != other.CapacitySparse)
{
other.Resize(CapacitySparse);
}
_dense.CopyTo(other._dense, 0);
Array.Resize(ref other._sparse, CapacitySparse);
if (CapacityDense != other.CapacityDense)
Array.Resize(ref other._dense, CapacityDense);
_sparse.CopyTo(other._sparse, 0);
_dense.CopyTo(other._dense, 0);
}
public void CopyTo(coretype[] array, int arrayIndex)
public void CopyTo(int[] array, int arrayIndex)
{
#if DEBUG
if (arrayIndex < 0) throw new ArgumentException("arrayIndex is less than 0");
@ -243,6 +233,12 @@ namespace DCFApixels.DragonECS
array[arrayIndex] = this[i];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int NormalizeCapacity(int value)
{
return value + (MIN_CAPACITY - (value % MIN_CAPACITY));
}
#endregion
#region Clear/Reset
@ -251,52 +247,26 @@ namespace DCFApixels.DragonECS
public void Reset()
{
Clear();
for (coretype i = 0; i < _dense.Length; i++)
//loop unwinding
for (int i = 0; i < _sparse.Length;)
{
_dense[i] = i;
_sparse[i] = i;
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
_sparse[i++] = -1;
}
}
public void Reset(coretype newCapacity)
public void Reset(int newDenseCapacity, int newSparseCapacity)
{
#if DEBUG
ThrowHalper.CheckCapacity(newCapacity);
#endif
newDenseCapacity = newDenseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(newDenseCapacity);
newSparseCapacity = newSparseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(newSparseCapacity);
if (CapacitySparse != newSparseCapacity)
Array.Resize(ref _sparse, newSparseCapacity);
if (CapacityDense != newDenseCapacity)
Array.Resize(ref _dense, newDenseCapacity);
Reset();
Resize(newCapacity);
}
#endregion
#region AddSpace/Resize
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddSpaces() => Resize((_count << 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize(int newSpace)
{
coretype oldspace = (short)_dense.Length;
Array.Resize(ref _dense, newSpace);
Array.Resize(ref _sparse, newSpace);
for (coretype i = oldspace; i < newSpace; i++)
{
_dense[i] = i;
_sparse[i] = i;
}
}
#endregion
#region Swap
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Swap(coretype fromIndex, coretype toIndex)
{
coretype value = _dense[toIndex];
coretype oldValue = _dense[fromIndex];
_dense[toIndex] = oldValue;
_dense[fromIndex] = value;
_sparse[_dense[fromIndex]] = fromIndex;
_sparse[_dense[toIndex]] = toIndex;
}
#endregion
@ -306,19 +276,19 @@ namespace DCFApixels.DragonECS
public ref struct RefEnumerator
{
private readonly coretype[] _dense;
private readonly coretype _count;
private coretype _index;
private readonly int[] _dense;
private readonly int _count;
private int _index;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RefEnumerator(coretype[] values, coretype count)
public RefEnumerator(int[] values, int count)
{
_dense = values;
_count = count;
_index = -1;
}
public coretype Current
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense[_index];
@ -334,20 +304,20 @@ namespace DCFApixels.DragonECS
public void Reset() => _index = -1;
}
IEnumerator<coretype> IEnumerable<coretype>.GetEnumerator() => new Enumerator(_dense, _count);
IEnumerator<int> IEnumerable<int>.GetEnumerator() => new Enumerator(_dense, _count);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dense, _count);
public struct Enumerator : IEnumerator<coretype> //to implement the IEnumerable interface and use the ref structure, 2 Enumerators were created.
public struct Enumerator : IEnumerator<int> //to implement the IEnumerable interface and use the ref structure, 2 Enumerators were created.
{
private readonly coretype[] _dense;
private readonly coretype _count;
private coretype _index;
public Enumerator(coretype[] values, coretype count)
private readonly int[] _dense;
private readonly int _count;
private int _index;
public Enumerator(int[] values, int count)
{
_dense = values;
_count = count;
_index = -1;
}
public coretype Current => _dense[_index];
public int Current => _dense[_index];
object IEnumerator.Current => _dense[_index];
public void Dispose() { }
public bool MoveNext() => ++_index < _count;
@ -356,16 +326,16 @@ namespace DCFApixels.DragonECS
#endregion
#region ICollection
bool ICollection<coretype>.IsReadOnly => false;
bool ICollection<int>.IsReadOnly => false;
bool ICollection<coretype>.Remove(coretype value) => TryRemove(value);
bool ICollection<int>.Remove(int value) => TryRemove(value);
#endregion
#region Debug
public string Log()
{
StringBuilder logbuild = new StringBuilder();
for (int i = 0; i < CapacitySparse; i++)
for (int i = 0; i < CapacityDense; i++)
{
logbuild.Append(_dense[i] + ", ");
}
@ -376,14 +346,14 @@ namespace DCFApixels.DragonECS
}
logbuild.Append("\n\r --------------------------");
logbuild.Append("\n\r");
for (int i = 0; i < CapacitySparse; i++)
for (int i = 0; i < CapacityDense; i++)
{
logbuild.Append((i < _count ? _dense[i].ToString() : "_") + ", ");
}
logbuild.Append("\n\r");
for (int i = 0; i < CapacitySparse; i++)
{
logbuild.Append((_sparse[i] < _count ? _sparse[i].ToString() : "_") + ", ");
logbuild.Append((_sparse[i] >= 0 ? _sparse[i].ToString() : "_") + ", ");
}
logbuild.Append("\n\r Count: " + _count);
logbuild.Append("\n\r Capacity: " + CapacitySparse);
@ -396,7 +366,7 @@ namespace DCFApixels.DragonECS
public bool IsValide_Debug()
{
bool isPass = true;
for (int index = 0; index < CapacitySparse; index++)
for (int index = 0; index < _count; index++)
{
int value = _dense[index];
isPass = isPass && _sparse[value] == index;
@ -407,27 +377,27 @@ namespace DCFApixels.DragonECS
#if DEBUG
private static class ThrowHalper
{
public static void CheckCapacity(coretype capacity)
public static void CheckCapacity(int capacity)
{
if (capacity < 0)
throw new ArgumentException("Capacity cannot be a negative number");
}
public static void CheckValueIsPositive(coretype value)
public static void CheckValueIsPositive(int value)
{
if (value < 0)
throw new ArgumentException("The SparseSet can only contain positive numbers");
}
public static void CheckValueContained(SparseSet source, coretype value)
public static void CheckValueContained(SparseSet source, int value)
{
if (!source.Contains(value))
throw new ArgumentException($"Value {value} is not contained");
}
public static void CheckValueNotContained(SparseSet source, coretype value)
public static void CheckValueNotContained(SparseSet source, int value)
{
if (source.Contains(value))
throw new ArgumentException($"Value {value} is already contained");
}
public static void CheckOutOfRange(SparseSet source, coretype index)
public static void CheckOutOfRange(SparseSet source, int index)
{
if (index < 0 || index >= source.Count)
throw new ArgumentOutOfRangeException($"Index {index} was out of range. Must be non-negative and less than the size of the collection.");

View File

@ -14,7 +14,6 @@ namespace DCFApixels.DragonECS
private void Start()
{
_ecsSession
.AddWorld("")
.Add(new TestSystem())
.Inject(_data)
.Init();