diff --git a/src/Collections/IndexableSet.cs b/src/Collections/IndexableSet.cs index 418749f..dcf3fbb 100644 --- a/src/Collections/IndexableSet.cs +++ b/src/Collections/IndexableSet.cs @@ -3,106 +3,104 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace MoonTools.ECS.Collections +namespace MoonTools.ECS.Collections; + +public unsafe class IndexableSet : IDisposable where T : unmanaged { - public unsafe class IndexableSet : IDisposable where T : unmanaged + private Dictionary Indices; + private T* Array; + public int Count { get; private set; } + private int Capacity; + private bool IsDisposed; + + public Span AsSpan() => new Span(Array, Count); + public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(Array, Count)); + + public IndexableSet(int capacity = 32) { - private Dictionary indices; - private T* array; - private int count; - private int capacity; - private bool disposed; + this.Capacity = capacity; + Count = 0; - public int Count => count; - public Span AsSpan() => new Span(array, count); - public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(array, count)); + Indices = new Dictionary(capacity); + Array = (T*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + } - public IndexableSet(int capacity = 32) + public T this[int i] => Array[i]; + + public bool Contains(T element) + { + return Indices.ContainsKey(element); + } + + public bool Add(T element) + { + if (!Contains(element)) { - this.capacity = capacity; - count = 0; + Indices.Add(element, Count); - indices = new Dictionary(capacity); - array = (T*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); - } - - public T this[int i] => array[i]; - - public bool Contains(T element) - { - return indices.ContainsKey(element); - } - - public bool Add(T element) - { - if (!Contains(element)) + if (Count >= Capacity) { - indices.Add(element, count); - - if (count >= capacity) - { - capacity *= 2; - array = (T*) NativeMemory.Realloc(array, (nuint) (capacity * Unsafe.SizeOf())); - } - - array[count] = element; - count += 1; - - return true; + Capacity *= 2; + Array = (T*) NativeMemory.Realloc(Array, (nuint) (Capacity * Unsafe.SizeOf())); } - return false; - } - - public bool Remove(T element) - { - if (!Contains(element)) - { - return false; - } - - var index = indices[element]; - - for (var i = index; i < Count - 1; i += 1) - { - array[i] = array[i + 1]; - indices[array[i]] = i; - } - - indices.Remove(element); - count -= 1; + Array[Count] = element; + Count += 1; return true; } - public void Clear() + return false; + } + + public bool Remove(T element) + { + if (!Contains(element)) { - indices.Clear(); - count = 0; + return false; } - protected virtual void Dispose(bool disposing) - { - if (!disposed) - { - NativeMemory.Free(array); - array = null; + var index = Indices[element]; - disposed = true; - } + for (var i = index; i < Count - 1; i += 1) + { + Array[i] = Array[i + 1]; + Indices[Array[i]] = i; } - ~IndexableSet() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: false); - } + Indices.Remove(element); + Count -= 1; - public void Dispose() + return true; + } + + public void Clear() + { + Indices.Clear(); + Count = 0; + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); + NativeMemory.Free(Array); + Array = null; + + IsDisposed = true; } } + + ~IndexableSet() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } diff --git a/src/Collections/NativeArray.cs b/src/Collections/NativeArray.cs index a0af02a..eb897a6 100644 --- a/src/Collections/NativeArray.cs +++ b/src/Collections/NativeArray.cs @@ -14,7 +14,7 @@ public unsafe class NativeArray : IDisposable where T : unmanaged public Span ToSpan() => new Span(Elements, Count); public Span.Enumerator GetEnumerator() => new Span(Elements, Count).GetEnumerator(); - private bool disposed; + private bool IsDisposed; public NativeArray(int capacity = 16) { @@ -96,12 +96,12 @@ public unsafe class NativeArray : IDisposable where T : unmanaged protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!IsDisposed) { NativeMemory.Free(Elements); Elements = null; - disposed = true; + IsDisposed = true; } } diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs index 2571446..f53a8a0 100644 --- a/src/ComponentStorage.cs +++ b/src/ComponentStorage.cs @@ -2,140 +2,139 @@ using System.Collections.Generic; using MoonTools.ECS.Collections; -namespace MoonTools.ECS +namespace MoonTools.ECS; + +internal class ComponentStorage : IDisposable { - internal unsafe class ComponentStorage : IDisposable + internal readonly Dictionary EntityIDToStorageIndex = new Dictionary(16); + internal readonly NativeArray Components; + internal readonly NativeArray EntityIDs; + internal readonly TypeId TypeId; + + private bool IsDisposed; + + public ComponentStorage(TypeId typeId, int elementSize) { - internal readonly Dictionary EntityIDToStorageIndex = new Dictionary(16); - internal readonly NativeArray Components; - internal readonly NativeArray EntityIDs; - internal readonly TypeId TypeId; + Components = new NativeArray(elementSize); + EntityIDs = new NativeArray(); + TypeId = typeId; + } - private bool disposed; + public bool Any() + { + return Components.Count > 0; + } - public ComponentStorage(TypeId typeId, int elementSize) - { - Components = new NativeArray(elementSize); - EntityIDs = new NativeArray(); - TypeId = typeId; - } + public bool Has(Entity entity) + { + return EntityIDToStorageIndex.ContainsKey(entity); + } - public bool Any() - { - return Components.Count > 0; - } + public ref T Get(in Entity entity) where T : unmanaged + { + return ref Components.Get(EntityIDToStorageIndex[entity]); + } - public bool Has(Entity entity) - { - return EntityIDToStorageIndex.ContainsKey(entity); - } - - public ref T Get(in Entity entity) where T : unmanaged - { - return ref Components.Get(EntityIDToStorageIndex[entity]); - } - - public ref T GetFirst() where T : unmanaged - { + public ref T GetFirst() where T : unmanaged + { #if DEBUG - if (Components.Count == 0) - { - throw new IndexOutOfRangeException("Component storage is empty!"); - } + if (Components.Count == 0) + { + throw new IndexOutOfRangeException("Component storage is empty!"); + } #endif - return ref Components.Get(0); - } + return ref Components.Get(0); + } - // Returns true if the entity had this component. - public bool Set(in Entity entity, in T component) where T : unmanaged + // Returns true if the entity had this component. + public bool Set(in Entity entity, in T component) where T : unmanaged + { + if (EntityIDToStorageIndex.TryGetValue(entity, out var index)) { - if (EntityIDToStorageIndex.TryGetValue(entity, out var index)) - { - Components.Set(index, component); - return true; - } - else - { - EntityIDToStorageIndex[entity] = Components.Count; - EntityIDs.Append(entity); - Components.Append(component); - return false; - } + Components.Set(index, component); + return true; } - - // Returns true if the entity had this component. - public bool Remove(in Entity entity) + else { - if (EntityIDToStorageIndex.TryGetValue(entity, out int index)) - { - var lastElementIndex = Components.Count - 1; - - var lastEntity = EntityIDs[lastElementIndex]; - - // move a component into the hole to maintain contiguous memory - Components.Delete(index); - EntityIDs.Delete(index); - EntityIDToStorageIndex.Remove(entity); - - // update the index if it changed - if (lastElementIndex != index) - { - EntityIDToStorageIndex[lastEntity] = index; - } - - return true; - } - + EntityIDToStorageIndex[entity] = Components.Count; + EntityIDs.Append(entity); + Components.Append(component); return false; } + } - public void Clear() + // Returns true if the entity had this component. + public bool Remove(in Entity entity) + { + if (EntityIDToStorageIndex.TryGetValue(entity, out int index)) { - Components.Clear(); - EntityIDs.Clear(); - EntityIDToStorageIndex.Clear(); - } + var lastElementIndex = Components.Count - 1; - public Entity FirstEntity() - { -#if DEBUG - if (EntityIDs.Count == 0) + var lastEntity = EntityIDs[lastElementIndex]; + + // move a component into the hole to maintain contiguous memory + Components.Delete(index); + EntityIDs.Delete(index); + EntityIDToStorageIndex.Remove(entity); + + // update the index if it changed + if (lastElementIndex != index) { - throw new IndexOutOfRangeException("Component storage is empty!"); + EntityIDToStorageIndex[lastEntity] = index; } -#endif - return EntityIDs[0]; + + return true; } + return false; + } + + public void Clear() + { + Components.Clear(); + EntityIDs.Clear(); + EntityIDToStorageIndex.Clear(); + } + + public Entity FirstEntity() + { #if DEBUG - internal IEnumerable Debug_GetEntities() + if (EntityIDs.Count == 0) { - return EntityIDToStorageIndex.Keys; + throw new IndexOutOfRangeException("Component storage is empty!"); } #endif + return EntityIDs[0]; + } - protected virtual void Dispose(bool disposing) +#if DEBUG + internal IEnumerable Debug_GetEntities() + { + return EntityIDToStorageIndex.Keys; + } +#endif + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) { - if (!disposed) - { - Components.Dispose(); - EntityIDs.Dispose(); + Components.Dispose(); + EntityIDs.Dispose(); - disposed = true; - } - } - - // ~ComponentStorage() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); + IsDisposed = true; } } + + // ~ComponentStorage() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } diff --git a/src/Entity.cs b/src/Entity.cs index f2d75d9..02ad024 100644 --- a/src/Entity.cs +++ b/src/Entity.cs @@ -1,14 +1,3 @@ -using System; +namespace MoonTools.ECS; -namespace MoonTools.ECS -{ - public readonly record struct Entity - { - public uint ID { get; } - - internal Entity(uint id) - { - ID = id; - } - } -} +public readonly record struct Entity(uint ID); diff --git a/src/Enumerators/ReverseSpanEnumerator.cs b/src/Enumerators/ReverseSpanEnumerator.cs index ce526a5..84563a9 100644 --- a/src/Enumerators/ReverseSpanEnumerator.cs +++ b/src/Enumerators/ReverseSpanEnumerator.cs @@ -1,36 +1,35 @@ using System; using System.Runtime.CompilerServices; -namespace MoonTools.ECS +namespace MoonTools.ECS; + +public ref struct ReverseSpanEnumerator { - public ref struct ReverseSpanEnumerator + private ReadOnlySpan Span; + private int Index; + + public ReverseSpanEnumerator GetEnumerator() => this; + + public T Current => Span[Index]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() { - private ReadOnlySpan Span; - private int index; - - public ReverseSpanEnumerator GetEnumerator() => this; - - public T Current => Span[index]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + if (Index > 0) { - if (index > 0) - { - index -= 1; - return true; - } - - return false; + Index -= 1; + return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReverseSpanEnumerator(Span span) - { - Span = span; - index = span.Length; - } - - public static ReverseSpanEnumerator Empty => new ReverseSpanEnumerator(); + return false; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReverseSpanEnumerator(Span span) + { + Span = span; + Index = span.Length; + } + + public static ReverseSpanEnumerator Empty => new ReverseSpanEnumerator(); } diff --git a/src/FilterBuilder.cs b/src/FilterBuilder.cs index 316052e..3e3ef6d 100644 --- a/src/FilterBuilder.cs +++ b/src/FilterBuilder.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using MoonTools.ECS.Collections; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { diff --git a/src/FilterSignature.cs b/src/FilterSignature.cs index e48fa83..8a27433 100644 --- a/src/FilterSignature.cs +++ b/src/FilterSignature.cs @@ -1,70 +1,69 @@ using System; using MoonTools.ECS.Collections; -namespace MoonTools.ECS +namespace MoonTools.ECS; + +public struct FilterSignature : IEquatable { - public struct FilterSignature : IEquatable + public readonly IndexableSet Included; + public readonly IndexableSet Excluded; + + public FilterSignature(IndexableSet included, IndexableSet excluded) { - public readonly IndexableSet Included; - public readonly IndexableSet Excluded; + Included = included; + Excluded = excluded; + } - public FilterSignature(IndexableSet included, IndexableSet excluded) - { - Included = included; - Excluded = excluded; - } + public override bool Equals(object? obj) + { + return obj is FilterSignature signature && Equals(signature); + } - public override bool Equals(object? obj) + public bool Equals(FilterSignature other) + { + foreach (var included in Included) { - return obj is FilterSignature signature && Equals(signature); - } - - public bool Equals(FilterSignature other) - { - foreach (var included in Included) + if (!other.Included.Contains(included)) { - if (!other.Included.Contains(included)) - { - return false; - } + return false; } - - foreach (var excluded in Excluded) - { - if (!other.Excluded.Contains(excluded)) - { - return false; - } - } - - return true; } - public override int GetHashCode() + foreach (var excluded in Excluded) { - var hashcode = 1; - - foreach (var type in Included) + if (!other.Excluded.Contains(excluded)) { - hashcode = HashCode.Combine(hashcode, type); + return false; } - - foreach (var type in Excluded) - { - hashcode = HashCode.Combine(hashcode, type); - } - - return hashcode; } - public static bool operator ==(FilterSignature left, FilterSignature right) + return true; + } + + public override int GetHashCode() + { + var hashcode = 1; + + foreach (var type in Included) { - return left.Equals(right); + hashcode = HashCode.Combine(hashcode, type); } - public static bool operator !=(FilterSignature left, FilterSignature right) + foreach (var type in Excluded) { - return !(left == right); + hashcode = HashCode.Combine(hashcode, type); } + + return hashcode; + } + + public static bool operator ==(FilterSignature left, FilterSignature right) + { + return left.Equals(right); + } + + public static bool operator !=(FilterSignature left, FilterSignature right) + { + return !(left == right); } } diff --git a/src/Manipulator.cs b/src/Manipulator.cs index 2e7da9a..1e8b6cf 100644 --- a/src/Manipulator.cs +++ b/src/Manipulator.cs @@ -1,19 +1,18 @@ -namespace MoonTools.ECS +namespace MoonTools.ECS; + +public abstract class Manipulator : EntityComponentReader { - public abstract class Manipulator : EntityComponentReader + public Manipulator(World world) : base(world) { - public Manipulator(World world) : base(world) - { - } - - protected Entity CreateEntity(string tag = "") => World.CreateEntity(tag); - protected void Tag(Entity entity, string tag) => World.Tag(entity, tag); - protected void Set(in Entity entity, in TComponent component) where TComponent : unmanaged => World.Set(entity, component); - protected void Remove(in Entity entity) where TComponent : unmanaged => World.Remove(entity); - - protected void Relate(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : unmanaged => World.Relate(entityA, entityB, relationData); - protected void Unrelate(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged => World.Unrelate(entityA, entityB); - protected void UnrelateAll(in Entity entity) where TRelationKind : unmanaged => World.UnrelateAll(entity); - protected void Destroy(in Entity entity) => World.Destroy(entity); } + + protected Entity CreateEntity(string tag = "") => World.CreateEntity(tag); + protected void Tag(Entity entity, string tag) => World.Tag(entity, tag); + protected void Set(in Entity entity, in TComponent component) where TComponent : unmanaged => World.Set(entity, component); + protected void Remove(in Entity entity) where TComponent : unmanaged => World.Remove(entity); + + protected void Relate(in Entity entityA, in Entity entityB, TRelationKind relationData) where TRelationKind : unmanaged => World.Relate(entityA, entityB, relationData); + protected void Unrelate(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged => World.Unrelate(entityA, entityB); + protected void UnrelateAll(in Entity entity) where TRelationKind : unmanaged => World.UnrelateAll(entity); + protected void Destroy(in Entity entity) => World.Destroy(entity); } diff --git a/src/MessageStorage.cs b/src/MessageStorage.cs index 324547a..f7e31d3 100644 --- a/src/MessageStorage.cs +++ b/src/MessageStorage.cs @@ -39,7 +39,7 @@ public class MessageStorage : IDisposable Messages.Clear(); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (!IsDisposed) { diff --git a/src/Random.cs b/src/Random.cs index 6f8b258..ea20df1 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -1,209 +1,208 @@ using System; using System.Runtime.CompilerServices; -namespace MoonTools.ECS +namespace MoonTools.ECS; + +/// +/// This class implements the well equidistributed long-period linear pseudorandom number generator. +/// Code taken from Chris Lomont: http://lomont.org/papers/2008/Lomont_PRNG_2008.pdf +/// +public class Random { + public const int STATE_BYTE_COUNT = 68; // 16 state ints + 1 index int + + uint[] State = new uint[16]; + uint Index = 0; + uint Seed; + /// - /// This class implements the well equidistributed long-period linear pseudorandom number generator. - /// Code taken from Chris Lomont: http://lomont.org/papers/2008/Lomont_PRNG_2008.pdf + /// Initializes the RNG with an arbitrary seed. /// - public class Random + public Random() { - public const int STATE_BYTE_COUNT = 68; // 16 state ints + 1 index int + Init((uint) Environment.TickCount); + } - uint[] State = new uint[16]; - uint Index = 0; - uint Seed; - - /// - /// Initializes the RNG with an arbitrary seed. - /// - public Random() + /// + /// Initializes the RNG with a given seed. + /// + public void Init(uint seed) + { + Seed = seed; + uint s = seed; + for (int i = 0; i < 16; i++) { - Init((uint) Environment.TickCount); + s = (((s * 214013 + 2531011) >> 16) & 0x7fffffff) | 0; + State[i] = ~ ~s; //i ; } + Index = 0; + } - /// - /// Initializes the RNG with a given seed. - /// - public void Init(uint seed) + /// + /// Returns the seed that was used to initialize the RNG. + /// + public uint GetSeed() + { + return Seed; + } + + /// + /// Returns the entire state of the RNG as a string. + /// + public string PrintState() + { + var s = ""; + for (var i = 0; i < 16; i += 1) { - Seed = seed; - uint s = seed; - for (int i = 0; i < 16; i++) - { - s = (((s * 214013 + 2531011) >> 16) & 0x7fffffff) | 0; - State[i] = ~ ~s; //i ; - } - Index = 0; + s += State[i]; } + s += Index; + return s; + } - /// - /// Returns the seed that was used to initialize the RNG. - /// - public uint GetSeed() + /// + /// Saves the entire state of the RNG to a Span. + /// + /// Must be a span of at least STATE_BYTE_COUNT bytes. + /// Thrown if the byte span is too short. + public unsafe void SaveState(Span bytes) + { +#if DEBUG + if (bytes.Length < STATE_BYTE_COUNT) { - return Seed; + throw new ArgumentException("Byte span too short!"); } +#endif - /// - /// Returns the entire state of the RNG as a string. - /// - public string PrintState() + fixed (byte* ptr = bytes) { - var s = ""; + var offset = 0; for (var i = 0; i < 16; i += 1) { - s += State[i]; + Unsafe.Write(ptr + offset, State[i]); + offset += 4; } - s += Index; - return s; - } - /// - /// Saves the entire state of the RNG to a Span. - /// - /// Must be a span of at least STATE_BYTE_COUNT bytes. - /// Thrown if the byte span is too short. - public unsafe void SaveState(Span bytes) - { - #if DEBUG - if (bytes.Length < STATE_BYTE_COUNT) - { - throw new ArgumentException("Byte span too short!"); - } - #endif - - fixed (byte* ptr = bytes) - { - var offset = 0; - for (var i = 0; i < 16; i += 1) - { - Unsafe.Write(ptr + offset, State[i]); - offset += 4; - } - - Unsafe.Write(ptr + offset, Index); - } - } - - /// - /// Loads the entire state of the RNG from a Span. - /// - /// Must be a span of at least STATE_BYTE_COUNT bytes. - /// Thrown if the byte span is too short. - public unsafe void LoadState(Span bytes) - { - #if DEBUG - if (bytes.Length < STATE_BYTE_COUNT) - { - throw new ArgumentException("Byte span too short!"); - } - #endif - - fixed (byte* ptr = bytes) - { - var offset = 0; - - for (var i = 0; i < 16; i += 1) - { - State[i] = Unsafe.Read(ptr + offset); - offset += 4; - } - - Index = Unsafe.Read(ptr + offset); - } - } - - private uint NextInternal() - { - uint a, b, c, d; - a = State[Index]; - c = State[(Index+13)&15]; - b = a^c^(a<<16)^(c<<15); - c = State[(Index+9)&15]; - c ^= (c>>11); - a = State[Index] = b^c; - d = (uint) (a ^((a<<5)&0xDA442D24UL)); - Index = (Index + 15)&15; - a = State[Index]; - State[Index] = a^b^d^(a<<2)^(b<<18)^(c<<28); - return State[Index]; - } - - /// - /// Returns a non-negative signed integer. - /// - public int Next() - { - return (int) (NextInternal() >>> 1); // unsigned bitshift right to get rid of signed bit - } - - /// - /// Returns a non-negative signed integer less than max. - /// - public int Next(int max) - { - return (int) (((double) Next()) * max / int.MaxValue); - } - - /// - /// Returns a signed integer greater than or equal to min and less than max. - /// - public int Next(int min, int max) - { - var diff = max - min; - var next = Next(diff); - return min + next; - } - - /// - /// Returns a non-negative signed 64 bit integer. - /// - public long NextInt64() - { - long next = NextInternal(); - next <<= 32; - next |= NextInternal(); - next >>>= 1; - return next; - } - - /// - /// Returns a non-negative signed 64 bit integer less than max. - /// - public long NextInt64(long max) - { - var next = NextInt64(); - return (long) (((double) next) * max / long.MaxValue); - } - - /// - /// Returns a non-negative signed 64 bit integer greater than or equal to min and less than max. - /// - public long NextInt64(long min, long max) - { - var diff = max - min; - var next = NextInt64(diff); - return min + next; - } - - /// - /// Returns a single-precision floating point value between 0 and 1. - /// - public float NextSingle() - { - var n = NextInternal(); - return ((float) n) / uint.MaxValue; - } - - /// - /// Returns a double-precision floating point value between 0 and 1. - /// - public double NextDouble() - { - var n = NextInternal(); - return ((double) n) / uint.MaxValue; + Unsafe.Write(ptr + offset, Index); } } + + /// + /// Loads the entire state of the RNG from a Span. + /// + /// Must be a span of at least STATE_BYTE_COUNT bytes. + /// Thrown if the byte span is too short. + public unsafe void LoadState(Span bytes) + { +#if DEBUG + if (bytes.Length < STATE_BYTE_COUNT) + { + throw new ArgumentException("Byte span too short!"); + } +#endif + + fixed (byte* ptr = bytes) + { + var offset = 0; + + for (var i = 0; i < 16; i += 1) + { + State[i] = Unsafe.Read(ptr + offset); + offset += 4; + } + + Index = Unsafe.Read(ptr + offset); + } + } + + private uint NextInternal() + { + uint a, b, c, d; + a = State[Index]; + c = State[(Index+13)&15]; + b = a^c^(a<<16)^(c<<15); + c = State[(Index+9)&15]; + c ^= (c>>11); + a = State[Index] = b^c; + d = (uint) (a ^((a<<5)&0xDA442D24UL)); + Index = (Index + 15)&15; + a = State[Index]; + State[Index] = a^b^d^(a<<2)^(b<<18)^(c<<28); + return State[Index]; + } + + /// + /// Returns a non-negative signed integer. + /// + public int Next() + { + return (int) (NextInternal() >>> 1); // unsigned bitshift right to get rid of signed bit + } + + /// + /// Returns a non-negative signed integer less than max. + /// + public int Next(int max) + { + return (int) (((double) Next()) * max / int.MaxValue); + } + + /// + /// Returns a signed integer greater than or equal to min and less than max. + /// + public int Next(int min, int max) + { + var diff = max - min; + var next = Next(diff); + return min + next; + } + + /// + /// Returns a non-negative signed 64 bit integer. + /// + public long NextInt64() + { + long next = NextInternal(); + next <<= 32; + next |= NextInternal(); + next >>>= 1; + return next; + } + + /// + /// Returns a non-negative signed 64 bit integer less than max. + /// + public long NextInt64(long max) + { + var next = NextInt64(); + return (long) (((double) next) * max / long.MaxValue); + } + + /// + /// Returns a non-negative signed 64 bit integer greater than or equal to min and less than max. + /// + public long NextInt64(long min, long max) + { + var diff = max - min; + var next = NextInt64(diff); + return min + next; + } + + /// + /// Returns a single-precision floating point value between 0 and 1. + /// + public float NextSingle() + { + var n = NextInternal(); + return ((float) n) / uint.MaxValue; + } + + /// + /// Returns a double-precision floating point value between 0 and 1. + /// + public double NextDouble() + { + var n = NextInternal(); + return ((double) n) / uint.MaxValue; + } } diff --git a/src/RandomManager.cs b/src/RandomManager.cs index cf90d75..b0d00dd 100644 --- a/src/RandomManager.cs +++ b/src/RandomManager.cs @@ -1,82 +1,81 @@ using System.Runtime.CompilerServices; -namespace MoonTools.ECS +namespace MoonTools.ECS; + +public static class RandomManager { - public static class RandomManager + private static Random Random = new Random(); + + private static int[] Primes = { - private static Random Random = new Random(); + 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999,2003,2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099,2111,2113,2129,2131,2137,2141,2143,2153,2161,2179,2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297,2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399,2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521,2531,2539,2543,2549,2551,2557,2579,2591,2593,2609,2617,2621,2633,2647,2657,2659,2663,2671,2677,2683,2687,2689,2693,2699,2707,2711,2713,2719,2729,2731,2741,2749,2753,2767,2777,2789,2791,2797,2801,2803,2819,2833,2837,2843,2851,2857,2861,2879,2887,2897,2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011,3019,3023,3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163,3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257,3259,3271,3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359,3361,3371,3373,3389,3391,3407,3413,3433,3449,3457,3461,3463,3467,3469,3491,3499,3511,3517,3527,3529,3533,3539,3541,3547,3557,3559,3571,3581,3583,3593,3607,3613,3617,3623,3631,3637,3643,3659,3671,3673,3677,3691,3697,3701,3709,3719,3727,3733,3739,3761,3767,3769,3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877,3881,3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989,4001,4003,4007,4013,4019,4021,4027,4049,4051,4057,4073,4079,4091,4093,4099,4111,4127,4129,4133,4139,4153,4157,4159,4177,4201,4211,4217,4219,4229,4231,4241,4243,4253,4259,4261,4271,4273,4283,4289,4297,4327,4337,4339,4349,4357,4363,4373,4391,4397,4409,4421,4423,4441,4447,4451,4457,4463,4481,4483,4493,4507,4513,4517,4519,4523,4547,4549,4561,4567,4583,4591,4597,4603,4621,4637,4639,4643,4649,4651,4657,4663,4673,4679,4691,4703,4721,4723,4729,4733,4751,4759,4783,4787,4789,4793,4799,4801,4813,4817,4831,4861,4871,4877,4889,4903,4909,4919,4931,4933,4937,4943,4951,4957,4967,4969,4973,4987,4993,4999,5003,5009,5011,5021,5023,5039,5051,5059,5077,5081,5087,5099,5101,5107,5113,5119,5147,5153,5167,5171,5179,5189,5197,5209,5227,5231,5233,5237,5261,5273,5279,5281,5297,5303,5309,5323,5333,5347,5351,5381,5387,5393,5399,5407,5413,5417,5419,5431,5437,5441,5443,5449,5471,5477,5479,5483,5501,5503,5507,5519,5521,5527,5531,5557,5563,5569,5573,5581,5591,5623,5639,5641,5647,5651,5653,5657,5659,5669,5683,5689,5693,5701,5711,5717,5737,5741,5743,5749,5779,5783,5791,5801,5807,5813,5821,5827,5839,5843,5849,5851,5857,5861,5867,5869,5879,5881,5897,5903,5923,5927,5939,5953,5981,5987,6007,6011,6029,6037,6043,6047,6053,6067,6073,6079,6089,6091,6101,6113,6121,6131,6133,6143,6151,6163,6173,6197,6199,6203,6211,6217,6221,6229,6247,6257,6263,6269,6271,6277,6287,6299,6301,6311,6317,6323,6329,6337,6343,6353,6359,6361,6367,6373,6379,6389,6397,6421,6427,6449,6451,6469,6473,6481,6491,6521,6529,6547,6551,6553,6563,6569,6571,6577,6581,6599,6607,6619,6637,6653,6659,6661,6673,6679,6689,6691,6701,6703,6709,6719,6733,6737,6761,6763,6779,6781,6791,6793,6803,6823,6827,6829,6833,6841,6857,6863,6869,6871,6883,6899,6907,6911,6917,6947,6949,6959,6961,6967,6971,6977,6983,6991,6997,7001,7013,7019,7027,7039,7043,7057,7069,7079,7103,7109,7121,7127,7129,7151,7159,7177,7187,7193,7207,7211,7213,7219,7229,7237,7243,7247,7253,7283,7297,7307,7309,7321,7331,7333,7349,7351,7369,7393,7411,7417,7433,7451,7457,7459,7477,7481,7487,7489,7499,7507,7517,7523,7529,7537,7541,7547,7549,7559,7561,7573,7577,7583,7589,7591,7603,7607,7621,7639,7643,7649,7669,7673,7681,7687,7691,7699,7703,7717,7723,7727,7741,7753,7757,7759,7789,7793,7817,7823,7829,7841,7853,7867,7873,7877,7879,7883,7901,7907,7919 + }; - private static int[] Primes = - { - 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999,2003,2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099,2111,2113,2129,2131,2137,2141,2143,2153,2161,2179,2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297,2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399,2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521,2531,2539,2543,2549,2551,2557,2579,2591,2593,2609,2617,2621,2633,2647,2657,2659,2663,2671,2677,2683,2687,2689,2693,2699,2707,2711,2713,2719,2729,2731,2741,2749,2753,2767,2777,2789,2791,2797,2801,2803,2819,2833,2837,2843,2851,2857,2861,2879,2887,2897,2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011,3019,3023,3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163,3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257,3259,3271,3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359,3361,3371,3373,3389,3391,3407,3413,3433,3449,3457,3461,3463,3467,3469,3491,3499,3511,3517,3527,3529,3533,3539,3541,3547,3557,3559,3571,3581,3583,3593,3607,3613,3617,3623,3631,3637,3643,3659,3671,3673,3677,3691,3697,3701,3709,3719,3727,3733,3739,3761,3767,3769,3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877,3881,3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989,4001,4003,4007,4013,4019,4021,4027,4049,4051,4057,4073,4079,4091,4093,4099,4111,4127,4129,4133,4139,4153,4157,4159,4177,4201,4211,4217,4219,4229,4231,4241,4243,4253,4259,4261,4271,4273,4283,4289,4297,4327,4337,4339,4349,4357,4363,4373,4391,4397,4409,4421,4423,4441,4447,4451,4457,4463,4481,4483,4493,4507,4513,4517,4519,4523,4547,4549,4561,4567,4583,4591,4597,4603,4621,4637,4639,4643,4649,4651,4657,4663,4673,4679,4691,4703,4721,4723,4729,4733,4751,4759,4783,4787,4789,4793,4799,4801,4813,4817,4831,4861,4871,4877,4889,4903,4909,4919,4931,4933,4937,4943,4951,4957,4967,4969,4973,4987,4993,4999,5003,5009,5011,5021,5023,5039,5051,5059,5077,5081,5087,5099,5101,5107,5113,5119,5147,5153,5167,5171,5179,5189,5197,5209,5227,5231,5233,5237,5261,5273,5279,5281,5297,5303,5309,5323,5333,5347,5351,5381,5387,5393,5399,5407,5413,5417,5419,5431,5437,5441,5443,5449,5471,5477,5479,5483,5501,5503,5507,5519,5521,5527,5531,5557,5563,5569,5573,5581,5591,5623,5639,5641,5647,5651,5653,5657,5659,5669,5683,5689,5693,5701,5711,5717,5737,5741,5743,5749,5779,5783,5791,5801,5807,5813,5821,5827,5839,5843,5849,5851,5857,5861,5867,5869,5879,5881,5897,5903,5923,5927,5939,5953,5981,5987,6007,6011,6029,6037,6043,6047,6053,6067,6073,6079,6089,6091,6101,6113,6121,6131,6133,6143,6151,6163,6173,6197,6199,6203,6211,6217,6221,6229,6247,6257,6263,6269,6271,6277,6287,6299,6301,6311,6317,6323,6329,6337,6343,6353,6359,6361,6367,6373,6379,6389,6397,6421,6427,6449,6451,6469,6473,6481,6491,6521,6529,6547,6551,6553,6563,6569,6571,6577,6581,6599,6607,6619,6637,6653,6659,6661,6673,6679,6689,6691,6701,6703,6709,6719,6733,6737,6761,6763,6779,6781,6791,6793,6803,6823,6827,6829,6833,6841,6857,6863,6869,6871,6883,6899,6907,6911,6917,6947,6949,6959,6961,6967,6971,6977,6983,6991,6997,7001,7013,7019,7027,7039,7043,7057,7069,7079,7103,7109,7121,7127,7129,7151,7159,7177,7187,7193,7207,7211,7213,7219,7229,7237,7243,7247,7253,7283,7297,7307,7309,7321,7331,7333,7349,7351,7369,7393,7411,7417,7433,7451,7457,7459,7477,7481,7487,7489,7499,7507,7517,7523,7529,7537,7541,7547,7549,7559,7561,7573,7577,7583,7589,7591,7603,7607,7621,7639,7643,7649,7669,7673,7681,7687,7691,7699,7703,7717,7723,7727,7741,7753,7757,7759,7789,7793,7817,7823,7829,7841,7853,7867,7873,7877,7879,7883,7901,7907,7919 - }; - - public static void SetRandom(Random random) - { - Random = random; - } - - internal static int Next(int maxValue) - { - return Random.Next(maxValue); - } - - /// - /// A psuedorandom nonrepeating sequence of integers from 0 to n. - /// - internal static LinearCongruentialEnumerator LinearCongruentialSequence(int n) - { - if (n == 0) - { - // bail out, empty enumerator - return new LinearCongruentialEnumerator(0, 0, 0); - } - - var x = Primes[Random.Next(Primes.Length - 1)]; - while (x % n == 0) - { - // not coprime, try again - x = Primes[Random.Next(Primes.Length - 1)]; - } - - return new LinearCongruentialEnumerator(Random.Next(n), x, n); - } + public static void SetRandom(Random random) + { + Random = random; } - public struct LinearCongruentialEnumerator + internal static int Next(int maxValue) { - private readonly int start; - private readonly int count; - private readonly int prime; - private int current; + return Random.Next(maxValue); + } - public LinearCongruentialEnumerator GetEnumerator() => this; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal LinearCongruentialEnumerator(int start, int prime, int count) + /// + /// A psuedorandom nonrepeating sequence of integers from 0 to n. + /// + internal static LinearCongruentialEnumerator LinearCongruentialSequence(int n) + { + if (n == 0) { - current = start; - this.start = start; - this.prime = prime; - this.count = count; + // bail out, empty enumerator + return new LinearCongruentialEnumerator(0, 0, 0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + var x = Primes[Random.Next(Primes.Length - 1)]; + while (x % n == 0) { - current += 1; - if (current < start + count) - { - return true; - } - - return false; + // not coprime, try again + x = Primes[Random.Next(Primes.Length - 1)]; } - public int Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (current * prime) % count; - } + return new LinearCongruentialEnumerator(Random.Next(n), x, n); + } +} + +public struct LinearCongruentialEnumerator +{ + private readonly int start; + private readonly int count; + private readonly int prime; + private int current; + + public LinearCongruentialEnumerator GetEnumerator() => this; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal LinearCongruentialEnumerator(int start, int prime, int count) + { + current = start; + this.start = start; + this.prime = prime; + this.count = count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + current += 1; + if (current < start + count) + { + return true; + } + + return false; + } + + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (current * prime) % count; } } diff --git a/src/RelationStorage.cs b/src/RelationStorage.cs index 31a12fa..40b64ef 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -8,67 +8,67 @@ namespace MoonTools.ECS; // TODO: implement this entire class with NativeMemory equivalents, can just memcpy for snapshots internal class RelationStorage { - internal NativeArray relations; - internal NativeArray relationDatas; - internal Dictionary<(Entity, Entity), int> indices = new Dictionary<(Entity, Entity), int>(16); - internal Dictionary> outRelations = new Dictionary>(16); - internal Dictionary> inRelations = new Dictionary>(16); - private Stack> listPool = new Stack>(); + internal NativeArray Relations; + internal NativeArray RelationDatas; + internal Dictionary<(Entity, Entity), int> Indices = new Dictionary<(Entity, Entity), int>(16); + internal Dictionary> OutRelationSets = new Dictionary>(16); + internal Dictionary> InRelationSets = new Dictionary>(16); + private Stack> ListPool = new Stack>(); - private bool disposed; + private bool IsDisposed; public RelationStorage(int relationDataSize) { - relations = new NativeArray(Unsafe.SizeOf<(Entity, Entity)>()); - relationDatas = new NativeArray(relationDataSize); + Relations = new NativeArray(Unsafe.SizeOf<(Entity, Entity)>()); + RelationDatas = new NativeArray(relationDataSize); } public ReverseSpanEnumerator<(Entity, Entity)> All() { - return new ReverseSpanEnumerator<(Entity, Entity)>(relations.ToSpan<(Entity, Entity)>()); + return new ReverseSpanEnumerator<(Entity, Entity)>(Relations.ToSpan<(Entity, Entity)>()); } public unsafe void Set(in Entity entityA, in Entity entityB, in T relationData) where T : unmanaged { var relation = (entityA, entityB); - if (indices.TryGetValue(relation, out var index)) + if (Indices.TryGetValue(relation, out var index)) { - relationDatas.Set(index, relationData); + RelationDatas.Set(index, relationData); return; } - if (!outRelations.ContainsKey(entityA)) + if (!OutRelationSets.ContainsKey(entityA)) { - outRelations[entityA] = AcquireHashSetFromPool(); + OutRelationSets[entityA] = AcquireHashSetFromPool(); } - outRelations[entityA].Add(entityB); + OutRelationSets[entityA].Add(entityB); - if (!inRelations.ContainsKey(entityB)) + if (!InRelationSets.ContainsKey(entityB)) { - inRelations[entityB] = AcquireHashSetFromPool(); + InRelationSets[entityB] = AcquireHashSetFromPool(); } - inRelations[entityB].Add(entityA); + InRelationSets[entityB].Add(entityA); - relations.Append(relation); - relationDatas.Append(relationData); - indices.Add(relation, relations.Count - 1); + Relations.Append(relation); + RelationDatas.Append(relationData); + Indices.Add(relation, Relations.Count - 1); } public ref T Get(in Entity entityA, in Entity entityB) where T : unmanaged { - var relationIndex = indices[(entityA, entityB)]; - return ref relationDatas.Get(relationIndex); + var relationIndex = Indices[(entityA, entityB)]; + return ref RelationDatas.Get(relationIndex); } public bool Has(Entity entityA, Entity entityB) { - return indices.ContainsKey((entityA, entityB)); + return Indices.ContainsKey((entityA, entityB)); } public ReverseSpanEnumerator OutRelations(Entity Entity) { - if (outRelations.TryGetValue(Entity, out var entityOutRelations)) + if (OutRelationSets.TryGetValue(Entity, out var entityOutRelations)) { return entityOutRelations.GetEnumerator(); } @@ -86,27 +86,27 @@ internal class RelationStorage public Entity OutNth(Entity Entity, int n) { #if DEBUG - if (!outRelations.ContainsKey(Entity) || outRelations[Entity].Count == 0) + if (!OutRelationSets.ContainsKey(Entity) || OutRelationSets[Entity].Count == 0) { throw new KeyNotFoundException("No out relations to this entity!"); } #endif - return outRelations[Entity][n]; + return OutRelationSets[Entity][n]; } public bool HasOutRelation(Entity Entity) { - return outRelations.ContainsKey(Entity) && outRelations[Entity].Count > 0; + return OutRelationSets.ContainsKey(Entity) && OutRelationSets[Entity].Count > 0; } public int OutRelationCount(Entity Entity) { - return outRelations.TryGetValue(Entity, out var entityOutRelations) ? entityOutRelations.Count : 0; + return OutRelationSets.TryGetValue(Entity, out var entityOutRelations) ? entityOutRelations.Count : 0; } public ReverseSpanEnumerator InRelations(Entity Entity) { - if (inRelations.TryGetValue(Entity, out var entityInRelations)) + if (InRelationSets.TryGetValue(Entity, out var entityInRelations)) { return entityInRelations.GetEnumerator(); } @@ -124,23 +124,23 @@ internal class RelationStorage public Entity InNth(Entity Entity, int n) { #if DEBUG - if (!inRelations.ContainsKey(Entity) || inRelations[Entity].Count == 0) + if (!InRelationSets.ContainsKey(Entity) || InRelationSets[Entity].Count == 0) { throw new KeyNotFoundException("No in relations to this entity!"); } #endif - return inRelations[Entity][n]; + return InRelationSets[Entity][n]; } public bool HasInRelation(Entity Entity) { - return inRelations.ContainsKey(Entity) && inRelations[Entity].Count > 0; + return InRelationSets.ContainsKey(Entity) && InRelationSets[Entity].Count > 0; } public int InRelationCount(Entity Entity) { - return inRelations.TryGetValue(Entity, out var entityInRelations) ? entityInRelations.Count : 0; + return InRelationSets.TryGetValue(Entity, out var entityInRelations) ? entityInRelations.Count : 0; } public (bool, bool) Remove(in Entity entityA, in Entity entityB) @@ -149,39 +149,39 @@ internal class RelationStorage var bEmpty = false; var relation = (entityA, entityB); - if (outRelations.TryGetValue(entityA, out var entityOutRelations)) + if (OutRelationSets.TryGetValue(entityA, out var entityOutRelations)) { entityOutRelations.Remove(entityB); - if (outRelations[entityA].Count == 0) + if (OutRelationSets[entityA].Count == 0) { aEmpty = true; } } - if (inRelations.TryGetValue(entityB, out var entityInRelations)) + if (InRelationSets.TryGetValue(entityB, out var entityInRelations)) { entityInRelations.Remove(entityA); - if (inRelations[entityB].Count == 0) + if (InRelationSets[entityB].Count == 0) { bEmpty = true; } } - if (indices.TryGetValue(relation, out var index)) + if (Indices.TryGetValue(relation, out var index)) { - var lastElementIndex = relations.Count - 1; + var lastElementIndex = Relations.Count - 1; // move an element into the hole if (index != lastElementIndex) { - var lastRelation = relations.Get<(Entity, Entity)>(lastElementIndex); - indices[lastRelation] = index; + var lastRelation = Relations.Get<(Entity, Entity)>(lastElementIndex); + Indices[lastRelation] = index; } - relationDatas.Delete(index); - relations.Delete(index); + RelationDatas.Delete(index); + Relations.Delete(index); - indices.Remove(relation); + Indices.Remove(relation); } return (aEmpty, bEmpty); @@ -189,7 +189,7 @@ internal class RelationStorage public void RemoveEntity(in Entity entity) { - if (outRelations.TryGetValue(entity, out var entityOutRelations)) + if (OutRelationSets.TryGetValue(entity, out var entityOutRelations)) { foreach (var entityB in entityOutRelations) { @@ -197,10 +197,10 @@ internal class RelationStorage } ReturnHashSetToPool(entityOutRelations); - outRelations.Remove(entity); + OutRelationSets.Remove(entity); } - if (inRelations.TryGetValue(entity, out var entityInRelations)) + if (InRelationSets.TryGetValue(entity, out var entityInRelations)) { foreach (var entityA in entityInRelations) { @@ -208,64 +208,64 @@ internal class RelationStorage } ReturnHashSetToPool(entityInRelations); - inRelations.Remove(entity); + InRelationSets.Remove(entity); } } internal IndexableSet AcquireHashSetFromPool() { - if (listPool.Count == 0) + if (ListPool.Count == 0) { - listPool.Push(new IndexableSet()); + ListPool.Push(new IndexableSet()); } - return listPool.Pop(); + return ListPool.Pop(); } private void ReturnHashSetToPool(IndexableSet hashSet) { hashSet.Clear(); - listPool.Push(hashSet); + ListPool.Push(hashSet); } public void Clear() { - indices.Clear(); + Indices.Clear(); - foreach (var set in inRelations.Values) + foreach (var set in InRelationSets.Values) { ReturnHashSetToPool(set); } - inRelations.Clear(); + InRelationSets.Clear(); - foreach (var set in outRelations.Values) + foreach (var set in OutRelationSets.Values) { ReturnHashSetToPool(set); } - outRelations.Clear(); + OutRelationSets.Clear(); - relations.Clear(); - relationDatas.Clear(); + Relations.Clear(); + RelationDatas.Clear(); } protected virtual void Dispose(bool disposing) { - if (!disposed) + if (!IsDisposed) { Clear(); if (disposing) { - foreach (var set in listPool) + foreach (var set in ListPool) { set.Dispose(); } - relations.Dispose(); - relationDatas.Dispose(); + Relations.Dispose(); + RelationDatas.Dispose(); } - disposed = true; + IsDisposed = true; } } diff --git a/src/Renderer.cs b/src/Renderer.cs index f6118c2..4bccf51 100644 --- a/src/Renderer.cs +++ b/src/Renderer.cs @@ -1,7 +1,6 @@ -namespace MoonTools.ECS +namespace MoonTools.ECS; + +public abstract class Renderer : EntityComponentReader { - public abstract class Renderer : EntityComponentReader - { - public Renderer(World world) : base(world) { } - } + public Renderer(World world) : base(world) { } } diff --git a/src/Snapshot.cs b/src/Snapshot.cs index 10a080d..5a64679 100644 --- a/src/Snapshot.cs +++ b/src/Snapshot.cs @@ -205,7 +205,7 @@ public class Snapshot { if (!RelationSnapshots.TryGetValue(typeId, out var snapshot)) { - snapshot = new RelationSnapshot(relationStorage.relationDatas.ElementSize); + snapshot = new RelationSnapshot(relationStorage.RelationDatas.ElementSize); RelationSnapshots.Add(typeId, snapshot); } @@ -256,39 +256,39 @@ public class Snapshot public void Take(RelationStorage relationStorage) { - relationStorage.relations.CopyAllTo(Relations); - relationStorage.relationDatas.CopyAllTo(RelationDatas); + relationStorage.Relations.CopyAllTo(Relations); + relationStorage.RelationDatas.CopyAllTo(RelationDatas); } public void Restore(RelationStorage relationStorage) { relationStorage.Clear(); - Relations.CopyAllTo(relationStorage.relations); - RelationDatas.CopyAllTo(relationStorage.relationDatas); + Relations.CopyAllTo(relationStorage.Relations); + RelationDatas.CopyAllTo(relationStorage.RelationDatas); for (int index = 0; index < Relations.Count; index += 1) { var relation = Relations.Get<(Entity, Entity)>(index); - relationStorage.indices[relation] = index; + relationStorage.Indices[relation] = index; - relationStorage.indices[relation] = index; + relationStorage.Indices[relation] = index; - if (!relationStorage.outRelations.ContainsKey(relation.Item1)) + if (!relationStorage.OutRelationSets.ContainsKey(relation.Item1)) { - relationStorage.outRelations[relation.Item1] = + relationStorage.OutRelationSets[relation.Item1] = relationStorage.AcquireHashSetFromPool(); } - relationStorage.outRelations[relation.Item1].Add(relation.Item2); + relationStorage.OutRelationSets[relation.Item1].Add(relation.Item2); - if (!relationStorage.inRelations.ContainsKey(relation.Item2)) + if (!relationStorage.InRelationSets.ContainsKey(relation.Item2)) { - relationStorage.inRelations[relation.Item2] = + relationStorage.InRelationSets[relation.Item2] = relationStorage.AcquireHashSetFromPool(); } - relationStorage.inRelations[relation.Item2].Add(relation.Item1); + relationStorage.InRelationSets[relation.Item2].Add(relation.Item1); } } } diff --git a/src/World.cs b/src/World.cs index 4653a34..70aad7d 100644 --- a/src/World.cs +++ b/src/World.cs @@ -3,519 +3,518 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using MoonTools.ECS.Collections; -namespace MoonTools.ECS +namespace MoonTools.ECS; + +public class World : IDisposable { - public class World : IDisposable + // Get TypeId from a Type + private readonly Dictionary TypeToId = new Dictionary(); + +#if DEBUG + private Dictionary IdToType = new Dictionary(); +#endif + + // Get element size from a TypeId + private readonly Dictionary ElementSizes = new Dictionary(); + + // Filters + internal readonly Dictionary FilterIndex = new Dictionary(); + private readonly Dictionary> TypeToFilter = new Dictionary>(); + + // TODO: can we make the tag an native array of chars at some point? + internal Dictionary EntityTags = new Dictionary(); + + // Relation Storages + internal Dictionary RelationIndex = new Dictionary(); + internal Dictionary> EntityRelationIndex = new Dictionary>(); + + // Message Storages + private Dictionary MessageIndex = new Dictionary(); + + public FilterBuilder FilterBuilder => new FilterBuilder(this); + + internal readonly Dictionary ComponentIndex = new Dictionary(); + internal Dictionary> EntityComponentIndex = new Dictionary>(); + + internal IdAssigner EntityIdAssigner = new IdAssigner(); + private IdAssigner TypeIdAssigner = new IdAssigner(); + + private bool IsDisposed; + + internal TypeId GetTypeId() where T : unmanaged { - // Get TypeId from a Type - private readonly Dictionary TypeToId = new Dictionary(); - - #if DEBUG - private Dictionary IdToType = new Dictionary(); - #endif - - // Get element size from a TypeId - private readonly Dictionary ElementSizes = new Dictionary(); - - // Filters - internal readonly Dictionary FilterIndex = new Dictionary(); - private readonly Dictionary> TypeToFilter = new Dictionary>(); - - // TODO: can we make the tag an native array of chars at some point? - internal Dictionary EntityTags = new Dictionary(); - - // Relation Storages - internal Dictionary RelationIndex = new Dictionary(); - internal Dictionary> EntityRelationIndex = new Dictionary>(); - - // Message Storages - private Dictionary MessageIndex = new Dictionary(); - - public FilterBuilder FilterBuilder => new FilterBuilder(this); - - internal readonly Dictionary ComponentIndex = new Dictionary(); - internal Dictionary> EntityComponentIndex = new Dictionary>(); - - internal IdAssigner EntityIdAssigner = new IdAssigner(); - private IdAssigner TypeIdAssigner = new IdAssigner(); - - private bool IsDisposed; - - internal TypeId GetTypeId() where T : unmanaged + if (TypeToId.ContainsKey(typeof(T))) { - if (TypeToId.ContainsKey(typeof(T))) - { - return TypeToId[typeof(T)]; - } + return TypeToId[typeof(T)]; + } - var typeId = new TypeId(TypeIdAssigner.Assign()); - TypeToId.Add(typeof(T), typeId); - ElementSizes.Add(typeId, Unsafe.SizeOf()); + var typeId = new TypeId(TypeIdAssigner.Assign()); + TypeToId.Add(typeof(T), typeId); + ElementSizes.Add(typeId, Unsafe.SizeOf()); - #if DEBUG - IdToType.Add(typeId, typeof(T)); - #endif +#if DEBUG + IdToType.Add(typeId, typeof(T)); +#endif + return typeId; + } + + internal TypeId GetComponentTypeId() where T : unmanaged + { + var typeId = GetTypeId(); + if (ComponentIndex.TryGetValue(typeId, out var componentStorage)) + { return typeId; } - internal TypeId GetComponentTypeId() where T : unmanaged + componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]); + ComponentIndex.Add(typeId, componentStorage); + TypeToFilter.Add(typeId, new List()); + return typeId; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ComponentStorage GetComponentStorage() where T : unmanaged + { + var typeId = GetTypeId(); + if (ComponentIndex.TryGetValue(typeId, out var componentStorage)) { - var typeId = GetTypeId(); - if (ComponentIndex.TryGetValue(typeId, out var componentStorage)) - { - return typeId; - } - - componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]); - ComponentIndex.Add(typeId, componentStorage); - TypeToFilter.Add(typeId, new List()); - return typeId; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ComponentStorage GetComponentStorage() where T : unmanaged - { - var typeId = GetTypeId(); - if (ComponentIndex.TryGetValue(typeId, out var componentStorage)) - { - return componentStorage; - } - - componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]); - ComponentIndex.Add(typeId, componentStorage); - TypeToFilter.Add(typeId, new List()); return componentStorage; } - // FILTERS + componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]); + ComponentIndex.Add(typeId, componentStorage); + TypeToFilter.Add(typeId, new List()); + return componentStorage; + } - internal Filter GetFilter(FilterSignature signature) + // FILTERS + + internal Filter GetFilter(FilterSignature signature) + { + if (!FilterIndex.TryGetValue(signature, out var filter)) { - if (!FilterIndex.TryGetValue(signature, out var filter)) + filter = new Filter(this, signature); + + foreach (var typeId in signature.Included) { - filter = new Filter(this, signature); - - foreach (var typeId in signature.Included) - { - TypeToFilter[typeId].Add(filter); - } - - foreach (var typeId in signature.Excluded) - { - TypeToFilter[typeId].Add(filter); - } - - FilterIndex.Add(signature, filter); + TypeToFilter[typeId].Add(filter); } - return filter; - } - - // ENTITIES - - public Entity CreateEntity(string tag = "") - { - var entity = new Entity(EntityIdAssigner.Assign()); - - if (!EntityComponentIndex.ContainsKey(entity)) + foreach (var typeId in signature.Excluded) { - EntityRelationIndex.Add(entity, new IndexableSet()); - EntityComponentIndex.Add(entity, new IndexableSet()); + TypeToFilter[typeId].Add(filter); } - EntityTags[entity] = tag; - - return entity; + FilterIndex.Add(signature, filter); } - public void Tag(Entity entity, string tag) + return filter; + } + + // ENTITIES + + public Entity CreateEntity(string tag = "") + { + var entity = new Entity(EntityIdAssigner.Assign()); + + if (!EntityComponentIndex.ContainsKey(entity)) { - EntityTags[entity] = tag; + EntityRelationIndex.Add(entity, new IndexableSet()); + EntityComponentIndex.Add(entity, new IndexableSet()); } - public string GetTag(Entity entity) - { - return EntityTags[entity]; - } + EntityTags[entity] = tag; - public void Destroy(in Entity entity) + return entity; + } + + public void Tag(Entity entity, string tag) + { + EntityTags[entity] = tag; + } + + public string GetTag(Entity entity) + { + return EntityTags[entity]; + } + + public void Destroy(in Entity entity) + { + // remove all components from storages + foreach (var componentTypeIndex in EntityComponentIndex[entity]) { - // remove all components from storages - foreach (var componentTypeIndex in EntityComponentIndex[entity]) + var componentStorage = ComponentIndex[componentTypeIndex]; + componentStorage.Remove(entity); + + foreach (var filter in TypeToFilter[componentTypeIndex]) { - var componentStorage = ComponentIndex[componentTypeIndex]; - componentStorage.Remove(entity); - - foreach (var filter in TypeToFilter[componentTypeIndex]) - { - filter.RemoveEntity(entity); - } - } - - // remove all relations from storage - foreach (var relationTypeIndex in EntityRelationIndex[entity]) - { - var relationStorage = RelationIndex[relationTypeIndex]; - relationStorage.RemoveEntity(entity); - } - - EntityComponentIndex[entity].Clear(); - EntityRelationIndex[entity].Clear(); - - // recycle ID - EntityIdAssigner.Unassign(entity.ID); - } - - // COMPONENTS - - public bool Has(in Entity entity) where T : unmanaged - { - var storage = GetComponentStorage(); - return storage.Has(entity); - } - - internal bool Has(in Entity entity, in TypeId typeId) - { - return EntityComponentIndex[entity].Contains(typeId); - } - - public bool Some() where T : unmanaged - { - var storage = GetComponentStorage(); - return storage.Any(); - } - - public ref T Get(in Entity entity) where T : unmanaged - { - var storage = GetComponentStorage(); - return ref storage.Get(entity); - } - - public ref T GetSingleton() where T : unmanaged - { - var storage = GetComponentStorage(); - return ref storage.GetFirst(); - } - - public Entity GetSingletonEntity() where T : unmanaged - { - var storage = GetComponentStorage(); - return storage.FirstEntity(); - } - - public void Set(in Entity entity, in T component) where T : unmanaged - { - var componentStorage = GetComponentStorage(); - - if (!componentStorage.Set(entity, component)) - { - EntityComponentIndex[entity].Add(componentStorage.TypeId); - - foreach (var filter in TypeToFilter[componentStorage.TypeId]) - { - filter.Check(entity); - } + filter.RemoveEntity(entity); } } - public void Remove(in Entity entity) where T : unmanaged + // remove all relations from storage + foreach (var relationTypeIndex in EntityRelationIndex[entity]) { - var componentStorage = GetComponentStorage(); - - if (componentStorage.Remove(entity)) - { - EntityComponentIndex[entity].Remove(componentStorage.TypeId); - - foreach (var filter in TypeToFilter[componentStorage.TypeId]) - { - filter.Check(entity); - } - } - } - - // RELATIONS - - private RelationStorage RegisterRelationType(TypeId typeId) - { - var relationStorage = new RelationStorage(ElementSizes[typeId]); - RelationIndex.Add(typeId, relationStorage); - return relationStorage; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private RelationStorage GetRelationStorage() where T : unmanaged - { - var typeId = GetTypeId(); - if (RelationIndex.TryGetValue(typeId, out var relationStorage)) - { - return relationStorage; - } - - return RegisterRelationType(typeId); - } - - public void Relate(in Entity entityA, in Entity entityB, in T relation) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - relationStorage.Set(entityA, entityB, relation); - EntityRelationIndex[entityA].Add(TypeToId[typeof(T)]); - EntityRelationIndex[entityB].Add(TypeToId[typeof(T)]); - } - - public void Unrelate(in Entity entityA, in Entity entityB) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - relationStorage.Remove(entityA, entityB); - } - - public void UnrelateAll(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); + var relationStorage = RelationIndex[relationTypeIndex]; relationStorage.RemoveEntity(entity); } - public bool Related(in Entity entityA, in Entity entityB) where T : unmanaged + EntityComponentIndex[entity].Clear(); + EntityRelationIndex[entity].Clear(); + + // recycle ID + EntityIdAssigner.Unassign(entity.ID); + } + + // COMPONENTS + + public bool Has(in Entity entity) where T : unmanaged + { + var storage = GetComponentStorage(); + return storage.Has(entity); + } + + internal bool Has(in Entity entity, in TypeId typeId) + { + return EntityComponentIndex[entity].Contains(typeId); + } + + public bool Some() where T : unmanaged + { + var storage = GetComponentStorage(); + return storage.Any(); + } + + public ref T Get(in Entity entity) where T : unmanaged + { + var storage = GetComponentStorage(); + return ref storage.Get(entity); + } + + public ref T GetSingleton() where T : unmanaged + { + var storage = GetComponentStorage(); + return ref storage.GetFirst(); + } + + public Entity GetSingletonEntity() where T : unmanaged + { + var storage = GetComponentStorage(); + return storage.FirstEntity(); + } + + public void Set(in Entity entity, in T component) where T : unmanaged + { + var componentStorage = GetComponentStorage(); + + if (!componentStorage.Set(entity, component)) { - var relationStorage = GetRelationStorage(); - return relationStorage.Has(entityA, entityB); - } + EntityComponentIndex[entity].Add(componentStorage.TypeId); - public T GetRelationData(in Entity entityA, in Entity entityB) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.Get(entityA, entityB); - } - - public ReverseSpanEnumerator<(Entity, Entity)> Relations() where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.All(); - } - - public ReverseSpanEnumerator OutRelations(Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.OutRelations(entity); - } - - public Entity OutRelationSingleton(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.OutFirst(entity); - } - - public bool HasOutRelation(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.HasOutRelation(entity); - } - - public int OutRelationCount(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.OutRelationCount(entity); - } - - public Entity NthOutRelation(in Entity entity, int n) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.OutNth(entity, n); - } - - public ReverseSpanEnumerator InRelations(Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.InRelations(entity); - } - - public Entity InRelationSingleton(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.InFirst(entity); - } - - public bool HasInRelation(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.HasInRelation(entity); - } - - public int InRelationCount(in Entity entity) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.InRelationCount(entity); - } - - public Entity NthInRelation(in Entity entity, int n) where T : unmanaged - { - var relationStorage = GetRelationStorage(); - return relationStorage.InNth(entity, n); - } - - // MESSAGES - - private TypeId GetMessageTypeId() where T : unmanaged - { - var typeId = GetTypeId(); - - if (!MessageIndex.ContainsKey(typeId)) + foreach (var filter in TypeToFilter[componentStorage.TypeId]) { - MessageIndex.Add(typeId, new MessageStorage(Unsafe.SizeOf())); + filter.Check(entity); } - - return typeId; - } - - public void Send(in T message) where T : unmanaged - { - var typeId = GetMessageTypeId(); - MessageIndex[typeId].Add(message); - } - - public bool SomeMessage() where T : unmanaged - { - var typeId = GetMessageTypeId(); - return MessageIndex[typeId].Some(); - } - - public ReadOnlySpan ReadMessages() where T : unmanaged - { - var typeId = GetMessageTypeId(); - return MessageIndex[typeId].All(); - } - - public T ReadMessage() where T : unmanaged - { - var typeId = GetMessageTypeId(); - return MessageIndex[typeId].First(); - } - - public void ClearMessages() where T : unmanaged - { - var typeId = GetMessageTypeId(); - MessageIndex[typeId].Clear(); - } - - // TODO: temporary component storage? - public void FinishUpdate() - { - foreach (var (_, messageStorage) in MessageIndex) - { - messageStorage.Clear(); - } - } - - // DEBUG - // NOTE: these methods are very inefficient - // they should only be used in debugging contexts!! - #if DEBUG - public ComponentTypeEnumerator Debug_GetAllComponentTypes(Entity entity) - { - return new ComponentTypeEnumerator(this, EntityComponentIndex[entity]); - } - - public IEnumerable Debug_GetEntities(Type componentType) - { - var storage = ComponentIndex[TypeToId[componentType]]; - return storage.Debug_GetEntities(); - } - - public IEnumerable Debug_SearchComponentType(string typeString) - { - foreach (var type in TypeToId.Keys) - { - if (type.ToString().ToLower().Contains(typeString.ToLower())) - { - yield return type; - } - } - } - - public ref struct ComponentTypeEnumerator - { - private World World; - private IndexableSet Types; - private int ComponentIndex; - - public ComponentTypeEnumerator GetEnumerator() => this; - - internal ComponentTypeEnumerator( - World world, - IndexableSet types - ) - { - World = world; - Types = types; - ComponentIndex = -1; - } - - public bool MoveNext() - { - ComponentIndex += 1; - return ComponentIndex < Types.Count; - } - - public unsafe Type Current => World.IdToType[Types[ComponentIndex]]; - } - #endif - - protected virtual void Dispose(bool disposing) - { - if (!IsDisposed) - { - if (disposing) - { - foreach (var componentStorage in ComponentIndex.Values) - { - componentStorage.Dispose(); - } - - foreach (var relationStorage in RelationIndex.Values) - { - relationStorage.Dispose(); - } - - foreach (var messageStorage in MessageIndex.Values) - { - messageStorage.Dispose(); - } - - foreach (var typeSet in EntityComponentIndex.Values) - { - typeSet.Dispose(); - } - - foreach (var typeSet in EntityRelationIndex.Values) - { - typeSet.Dispose(); - } - - foreach (var filter in FilterIndex.Values) - { - filter.Dispose(); - } - - EntityIdAssigner.Dispose(); - TypeIdAssigner.Dispose(); - } - - IsDisposed = true; - } - } - - // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~World() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); } } + + public void Remove(in Entity entity) where T : unmanaged + { + var componentStorage = GetComponentStorage(); + + if (componentStorage.Remove(entity)) + { + EntityComponentIndex[entity].Remove(componentStorage.TypeId); + + foreach (var filter in TypeToFilter[componentStorage.TypeId]) + { + filter.Check(entity); + } + } + } + + // RELATIONS + + private RelationStorage RegisterRelationType(TypeId typeId) + { + var relationStorage = new RelationStorage(ElementSizes[typeId]); + RelationIndex.Add(typeId, relationStorage); + return relationStorage; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private RelationStorage GetRelationStorage() where T : unmanaged + { + var typeId = GetTypeId(); + if (RelationIndex.TryGetValue(typeId, out var relationStorage)) + { + return relationStorage; + } + + return RegisterRelationType(typeId); + } + + public void Relate(in Entity entityA, in Entity entityB, in T relation) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + relationStorage.Set(entityA, entityB, relation); + EntityRelationIndex[entityA].Add(TypeToId[typeof(T)]); + EntityRelationIndex[entityB].Add(TypeToId[typeof(T)]); + } + + public void Unrelate(in Entity entityA, in Entity entityB) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + relationStorage.Remove(entityA, entityB); + } + + public void UnrelateAll(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + relationStorage.RemoveEntity(entity); + } + + public bool Related(in Entity entityA, in Entity entityB) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.Has(entityA, entityB); + } + + public T GetRelationData(in Entity entityA, in Entity entityB) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.Get(entityA, entityB); + } + + public ReverseSpanEnumerator<(Entity, Entity)> Relations() where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.All(); + } + + public ReverseSpanEnumerator OutRelations(Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.OutRelations(entity); + } + + public Entity OutRelationSingleton(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.OutFirst(entity); + } + + public bool HasOutRelation(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.HasOutRelation(entity); + } + + public int OutRelationCount(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.OutRelationCount(entity); + } + + public Entity NthOutRelation(in Entity entity, int n) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.OutNth(entity, n); + } + + public ReverseSpanEnumerator InRelations(Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.InRelations(entity); + } + + public Entity InRelationSingleton(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.InFirst(entity); + } + + public bool HasInRelation(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.HasInRelation(entity); + } + + public int InRelationCount(in Entity entity) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.InRelationCount(entity); + } + + public Entity NthInRelation(in Entity entity, int n) where T : unmanaged + { + var relationStorage = GetRelationStorage(); + return relationStorage.InNth(entity, n); + } + + // MESSAGES + + private TypeId GetMessageTypeId() where T : unmanaged + { + var typeId = GetTypeId(); + + if (!MessageIndex.ContainsKey(typeId)) + { + MessageIndex.Add(typeId, new MessageStorage(Unsafe.SizeOf())); + } + + return typeId; + } + + public void Send(in T message) where T : unmanaged + { + var typeId = GetMessageTypeId(); + MessageIndex[typeId].Add(message); + } + + public bool SomeMessage() where T : unmanaged + { + var typeId = GetMessageTypeId(); + return MessageIndex[typeId].Some(); + } + + public ReadOnlySpan ReadMessages() where T : unmanaged + { + var typeId = GetMessageTypeId(); + return MessageIndex[typeId].All(); + } + + public T ReadMessage() where T : unmanaged + { + var typeId = GetMessageTypeId(); + return MessageIndex[typeId].First(); + } + + public void ClearMessages() where T : unmanaged + { + var typeId = GetMessageTypeId(); + MessageIndex[typeId].Clear(); + } + + // TODO: temporary component storage? + public void FinishUpdate() + { + foreach (var (_, messageStorage) in MessageIndex) + { + messageStorage.Clear(); + } + } + + // DEBUG + // NOTE: these methods are very inefficient + // they should only be used in debugging contexts!! +#if DEBUG + public ComponentTypeEnumerator Debug_GetAllComponentTypes(Entity entity) + { + return new ComponentTypeEnumerator(this, EntityComponentIndex[entity]); + } + + public IEnumerable Debug_GetEntities(Type componentType) + { + var storage = ComponentIndex[TypeToId[componentType]]; + return storage.Debug_GetEntities(); + } + + public IEnumerable Debug_SearchComponentType(string typeString) + { + foreach (var type in TypeToId.Keys) + { + if (type.ToString().ToLower().Contains(typeString.ToLower())) + { + yield return type; + } + } + } + + public ref struct ComponentTypeEnumerator + { + private World World; + private IndexableSet Types; + private int ComponentIndex; + + public ComponentTypeEnumerator GetEnumerator() => this; + + internal ComponentTypeEnumerator( + World world, + IndexableSet types + ) + { + World = world; + Types = types; + ComponentIndex = -1; + } + + public bool MoveNext() + { + ComponentIndex += 1; + return ComponentIndex < Types.Count; + } + + public Type Current => World.IdToType[Types[ComponentIndex]]; + } +#endif + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) + { + foreach (var componentStorage in ComponentIndex.Values) + { + componentStorage.Dispose(); + } + + foreach (var relationStorage in RelationIndex.Values) + { + relationStorage.Dispose(); + } + + foreach (var messageStorage in MessageIndex.Values) + { + messageStorage.Dispose(); + } + + foreach (var typeSet in EntityComponentIndex.Values) + { + typeSet.Dispose(); + } + + foreach (var typeSet in EntityRelationIndex.Values) + { + typeSet.Dispose(); + } + + foreach (var filter in FilterIndex.Values) + { + filter.Dispose(); + } + + EntityIdAssigner.Dispose(); + TypeIdAssigner.Dispose(); + } + + IsDisposed = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~World() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } }