diff --git a/src/ComponentDepot.cs b/src/ComponentDepot.cs index 8a6b37f..cdd4d5f 100644 --- a/src/ComponentDepot.cs +++ b/src/ComponentDepot.cs @@ -89,7 +89,7 @@ namespace MoonTools.ECS } } - // these methods used to implement snapshots, templates, and debugging + // these methods used to implement transfers and debugging internal unsafe void* UntypedGet(int entityID, int componentTypeIndex) { diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs index 41117bc..bc71ef7 100644 --- a/src/ComponentStorage.cs +++ b/src/ComponentStorage.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace MoonTools.ECS { @@ -19,16 +21,24 @@ namespace MoonTools.ECS #endif } - internal class ComponentStorage : ComponentStorage where TComponent : unmanaged + internal unsafe class ComponentStorage : ComponentStorage, IDisposable where TComponent : unmanaged { - private int nextID; private readonly Dictionary entityIDToStorageIndex = new Dictionary(16); - private int[] entityIDs = new int[16]; - private TComponent[] components = new TComponent[16]; + private TComponent* components; + private int* entityIDs; + private int count = 0; + private int capacity = 16; + private bool disposed; + + public ComponentStorage() + { + components = (TComponent*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + entityIDs = (int*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + } public bool Any() { - return nextID > 0; + return count > 0; } public ref readonly TComponent Get(int entityID) @@ -38,16 +48,13 @@ namespace MoonTools.ECS internal override unsafe void* UntypedGet(int entityID) { - fixed (void* p = &components[entityIDToStorageIndex[entityID]]) - { - return p; - } + return &components[entityIDToStorageIndex[entityID]]; } public ref readonly TComponent GetFirst() { #if DEBUG - if (nextID == 0) + if (count == 0) { throw new IndexOutOfRangeException("Component storage is empty!"); } @@ -59,13 +66,14 @@ namespace MoonTools.ECS { if (!entityIDToStorageIndex.ContainsKey(entityID)) { - var index = nextID; - nextID += 1; + var index = count; + count += 1; - if (index >= components.Length) + if (index >= capacity) { - Array.Resize(ref components, components.Length * 2); - Array.Resize(ref entityIDs, entityIDs.Length * 2); + capacity *= 2; + components = (TComponent*) NativeMemory.Realloc(components, (nuint) (capacity * Unsafe.SizeOf())); + entityIDs = (int*) NativeMemory.Realloc(entityIDs, (nuint) (capacity * Unsafe.SizeOf())); } entityIDToStorageIndex[entityID] = index; @@ -77,7 +85,7 @@ namespace MoonTools.ECS internal override unsafe void Set(int entityID, void* component) { - Set(entityID, *((TComponent*) component)); + Set(entityID, *(TComponent*) component); } // Returns true if the entity had this component. @@ -87,7 +95,7 @@ namespace MoonTools.ECS { entityIDToStorageIndex.Remove(entityID); - var lastElementIndex = nextID - 1; + var lastElementIndex = count - 1; // move a component into the hole to maintain contiguous memory if (lastElementIndex != storageIndex) @@ -98,7 +106,7 @@ namespace MoonTools.ECS entityIDs[storageIndex] = lastEntityID; } - nextID -= 1; + count -= 1; return true; } @@ -108,19 +116,19 @@ namespace MoonTools.ECS public override void Clear() { - nextID = 0; + count = 0; entityIDToStorageIndex.Clear(); } public ReadOnlySpan AllComponents() { - return new ReadOnlySpan(components, 0, nextID); + return new ReadOnlySpan(components, count); } public Entity FirstEntity() { #if DEBUG - if (nextID == 0) + if (count == 0) { throw new IndexOutOfRangeException("Component storage is empty!"); } @@ -143,6 +151,32 @@ namespace MoonTools.ECS { return entityIDToStorageIndex.Keys; } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + NativeMemory.Free(components); + NativeMemory.Free(entityIDs); + components = null; + entityIDs = null; + + 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); + } #endif } } diff --git a/src/DebugSystem.cs b/src/DebugSystem.cs index de9027a..fcffd9e 100644 --- a/src/DebugSystem.cs +++ b/src/DebugSystem.cs @@ -13,12 +13,9 @@ namespace MoonTools.ECS { } - protected IEnumerable Debug_GetAllComponents(Entity entity) + protected ComponentEnumerator Debug_GetAllComponents(Entity entity) { - foreach (var typeIndex in EntityStorage.ComponentTypeIndices(entity.ID)) - { - yield return ComponentDepot.Debug_Get(entity.ID, typeIndex); - } + return new ComponentEnumerator(ComponentDepot, entity, EntityStorage.ComponentTypeIndices(entity.ID)); } protected IEnumerable Debug_GetEntities(Type componentType) @@ -39,6 +36,28 @@ namespace MoonTools.ECS } } } + + public ref struct ComponentEnumerator + { + private ComponentDepot ComponentDepot; + private Entity Entity; + private ReverseSpanEnumerator ComponentTypeIndices; + + public ComponentEnumerator GetEnumerator() => this; + + internal ComponentEnumerator( + ComponentDepot componentDepot, + Entity entity, + Collections.IndexableSet componentTypeIndices + ) { + ComponentDepot = componentDepot; + Entity = entity; + ComponentTypeIndices = componentTypeIndices.GetEnumerator(); + } + + public bool MoveNext() => ComponentTypeIndices.MoveNext(); + public object Current => ComponentDepot.Debug_Get(Entity.ID, ComponentTypeIndices.Current); + } } } #endif diff --git a/src/DynamicArray.cs b/src/DynamicArray.cs index c16b697..08400fc 100644 --- a/src/DynamicArray.cs +++ b/src/DynamicArray.cs @@ -1,40 +1,69 @@ using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -namespace MoonTools.ECS +namespace MoonTools.ECS.Collections { - public class DynamicArray where T : unmanaged + public unsafe class NativeArray : IDisposable where T : unmanaged { - private T[] Array; - public int Count { get; private set; } + private T* Array; + private int count; + private int capacity; - public Span ToSpan() => new Span(Array, 0, Count); - public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(Array, 0, Count)); + public int Count => count; - public DynamicArray(int capacity = 16) + public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(Array, Count)); + + private bool disposed; + + public NativeArray(int capacity = 16) { - Array = new T[capacity]; - Count = 0; + this.capacity = capacity; + Array = (T*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + count = 0; } - public ref T this[int i] - { - get { return ref Array[i]; } - } + public ref T this[int i] => ref Array[i]; public void Add(T item) { - if (Count >= Array.Length) + if (count >= capacity) { - global::System.Array.Resize(ref Array, Array.Length * 2); + capacity *= 2; + Array = (T*) NativeMemory.Realloc(Array, (nuint) (capacity * Unsafe.SizeOf())); } - Array[Count] = item; - Count += 1; + Array[count] = item; + count += 1; } public void Clear() { - Count = 0; + count = 0; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + NativeMemory.Free(Array); + Array = null; + + disposed = true; + } + } + + ~NativeArray() + { + // 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/EntityStorage.cs b/src/EntityStorage.cs index b73ad05..3ff05de 100644 --- a/src/EntityStorage.cs +++ b/src/EntityStorage.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -10,8 +11,8 @@ namespace MoonTools.ECS // FIXME: this is only needed in debug mode private readonly HashSet availableIDHash = new HashSet(); - private Dictionary> EntityToComponentTypeIndices = new Dictionary>(); - private Dictionary> EntityToRelationTypeIndices = new Dictionary>(); + private Dictionary> EntityToComponentTypeIndices = new Dictionary>(); + private Dictionary> EntityToRelationTypeIndices = new Dictionary>(); public int Count => nextID - availableIDs.Count; @@ -23,12 +24,12 @@ namespace MoonTools.ECS if (!EntityToComponentTypeIndices.ContainsKey(entity.ID)) { - EntityToComponentTypeIndices.Add(entity.ID, new HashSet()); + EntityToComponentTypeIndices.Add(entity.ID, new IndexableSet()); } if (!EntityToRelationTypeIndices.ContainsKey(entity.ID)) { - EntityToRelationTypeIndices.Add(entity.ID, new HashSet()); + EntityToRelationTypeIndices.Add(entity.ID, new IndexableSet()); } Tags[entity.ID] = tag; @@ -86,12 +87,12 @@ namespace MoonTools.ECS return Tags[entityID]; } - public HashSet ComponentTypeIndices(int entityID) + public IndexableSet ComponentTypeIndices(int entityID) { return EntityToComponentTypeIndices[entityID]; } - public HashSet RelationTypeIndices(int entityID) + public IndexableSet RelationTypeIndices(int entityID) { return EntityToRelationTypeIndices[entityID]; } diff --git a/src/Filter.cs b/src/Filter.cs index f841c39..94f5ee9 100644 --- a/src/Filter.cs +++ b/src/Filter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -8,7 +9,7 @@ namespace MoonTools.ECS internal FilterSignature Signature; private FilterStorage FilterStorage; - internal Filter(FilterStorage filterStorage, HashSet included, HashSet excluded) + internal Filter(FilterStorage filterStorage, IndexableSet included, IndexableSet excluded) { FilterStorage = filterStorage; Signature = new FilterSignature(included, excluded); diff --git a/src/FilterBuilder.cs b/src/FilterBuilder.cs index ed1562e..8a8a6d1 100644 --- a/src/FilterBuilder.cs +++ b/src/FilterBuilder.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -7,18 +6,18 @@ namespace MoonTools.ECS { private TypeIndices ComponentTypeIndices; private FilterStorage FilterStorage; - private HashSet Included; - private HashSet Excluded; + private IndexableSet Included; + private IndexableSet Excluded; internal FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices) { FilterStorage = filterStorage; ComponentTypeIndices = componentTypeIndices; - Included = new HashSet(); - Excluded = new HashSet(); + Included = new IndexableSet(); + Excluded = new IndexableSet(); } - private FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices, HashSet included, HashSet excluded) + private FilterBuilder(FilterStorage filterStorage, TypeIndices componentTypeIndices, IndexableSet included, IndexableSet excluded) { FilterStorage = filterStorage; ComponentTypeIndices = componentTypeIndices; diff --git a/src/FilterSignature.cs b/src/FilterSignature.cs index 3877391..7e94922 100644 --- a/src/FilterSignature.cs +++ b/src/FilterSignature.cs @@ -1,14 +1,15 @@ using System; using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { public struct FilterSignature : IEquatable { - public readonly HashSet Included; - public readonly HashSet Excluded; + public readonly IndexableSet Included; + public readonly IndexableSet Excluded; - public FilterSignature(HashSet included, HashSet excluded) + public FilterSignature(IndexableSet included, IndexableSet excluded) { Included = included; Excluded = excluded; @@ -21,8 +22,6 @@ namespace MoonTools.ECS public bool Equals(FilterSignature other) { - // workaround for HashSet.SetEquals generating garbage - // maybe fixed in .NET 8? foreach (var included in Included) { if (!other.Included.Contains(included)) diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index aad179b..7eb11f9 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -8,7 +9,7 @@ namespace MoonTools.ECS private EntityStorage EntityStorage; private TypeIndices ComponentTypeIndices; private Dictionary> filterSignatureToEntityIDs = new Dictionary>(); - private Dictionary> typeToFilterSignatures = new Dictionary>(); + private Dictionary> typeToFilterSignatures = new Dictionary>(); private Dictionary> addCallbacks = new Dictionary>(); private Dictionary> removeCallbacks = new Dictionary>(); @@ -19,7 +20,36 @@ namespace MoonTools.ECS ComponentTypeIndices = componentTypeIndices; } - public Filter CreateFilter(HashSet included, HashSet excluded) + private void CopyTypeCache(Dictionary> typeCache) + { + foreach (var type in typeCache.Keys) + { + if (!typeToFilterSignatures.ContainsKey(type)) + { + typeToFilterSignatures.Add(type, new List()); + + foreach (var signature in typeCache[type]) + { + typeToFilterSignatures[type].Add(signature); + } + } + } + } + + public void CreateMissingStorages(FilterStorage other) + { + foreach (var filterSignature in other.filterSignatureToEntityIDs.Keys) + { + if (!filterSignatureToEntityIDs.ContainsKey(filterSignature)) + { + filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet()); + } + } + + CopyTypeCache(other.typeToFilterSignatures); + } + + public Filter CreateFilter(IndexableSet included, IndexableSet excluded) { var filterSignature = new FilterSignature(included, excluded); if (!filterSignatureToEntityIDs.ContainsKey(filterSignature)) @@ -30,7 +60,7 @@ namespace MoonTools.ECS { if (!typeToFilterSignatures.ContainsKey(type)) { - typeToFilterSignatures.Add(type, new HashSet()); + typeToFilterSignatures.Add(type, new List()); } typeToFilterSignatures[type].Add(filterSignature); @@ -40,7 +70,7 @@ namespace MoonTools.ECS { if (!typeToFilterSignatures.ContainsKey(type)) { - typeToFilterSignatures.Add(type, new HashSet()); + typeToFilterSignatures.Add(type, new List()); } typeToFilterSignatures[type].Add(filterSignature); @@ -59,7 +89,7 @@ namespace MoonTools.ECS return new RandomEntityEnumerator( this, filterSignature, - RandomGenerator.LinearCongruentialGenerator(FilterCount(filterSignature))); + RandomManager.LinearCongruentialSequence(FilterCount(filterSignature))); } public Entity FilterNthEntity(FilterSignature filterSignature, int index) @@ -69,7 +99,7 @@ namespace MoonTools.ECS public Entity FilterRandomEntity(FilterSignature filterSignature) { - var randomIndex = RandomGenerator.Next(FilterCount(filterSignature)); + var randomIndex = RandomManager.Next(FilterCount(filterSignature)); return new Entity(filterSignatureToEntityIDs[filterSignature][randomIndex]); } @@ -147,10 +177,12 @@ namespace MoonTools.ECS } } - filterSignatureToEntityIDs[filterSignature].Add(entityID); - if (addCallbacks.TryGetValue(filterSignature, out var addCallback)) + if (filterSignatureToEntityIDs[filterSignature].Add(entityID)) { - addCallback(entityID); + if (addCallbacks.TryGetValue(filterSignature, out var addCallback)) + { + addCallback(entityID); + } } } @@ -171,6 +203,32 @@ namespace MoonTools.ECS } } + // Used by TransferEntity + public void AddEntity(FilterSignature signature, int entityID) + { + filterSignatureToEntityIDs[signature].Add(entityID); + } + + public void TransferStorage(Dictionary worldToTransferID, FilterStorage other) + { + foreach (var (filterSignature, entityIDs) in filterSignatureToEntityIDs) + { + foreach (var entity in entityIDs) + { + if (worldToTransferID.ContainsKey(entity)) + { + var otherEntityID = worldToTransferID[entity]; + other.AddEntity(filterSignature, otherEntityID); + + if (other.addCallbacks.TryGetValue(filterSignature, out var addCallback)) + { + addCallback(otherEntityID); + } + } + } + } + } + // used by World.Clear, ignores callbacks public void Clear() { diff --git a/src/IndexableSet.cs b/src/IndexableSet.cs index 1a79f71..1956756 100644 --- a/src/IndexableSet.cs +++ b/src/IndexableSet.cs @@ -1,26 +1,31 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -namespace MoonTools.ECS +namespace MoonTools.ECS.Collections { - internal class IndexableSet where T : unmanaged + public unsafe class IndexableSet : IDisposable where T : unmanaged { private Dictionary indices; - private T[] array; - public int Count { get; private set; } - public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(array, 0, Count)); + private T* array; + private int count; + private int capacity; + private bool disposed; - public IndexableSet(int size = 32) + public int Count => count; + public ReverseSpanEnumerator GetEnumerator() => new ReverseSpanEnumerator(new Span(array, count)); + + public IndexableSet(int capacity = 32) { - indices = new Dictionary(size); - array = new T[size]; + this.capacity = capacity; + count = 0; + + indices = new Dictionary(capacity); + array = (T*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); } - public T this[int i] - { - get { return array[i]; } - } + public T this[int i] => array[i]; public bool Contains(T element) { @@ -31,15 +36,16 @@ namespace MoonTools.ECS { if (!Contains(element)) { - indices.Add(element, Count); + indices.Add(element, count); - if (Count >= array.Length) + if (count >= capacity) { - Array.Resize(ref array, array.Length * 2); + capacity *= 2; + array = (T*) NativeMemory.Realloc(array, (nuint) (capacity * Unsafe.SizeOf())); } - array[Count] = element; - Count += 1; + array[count] = element; + count += 1; return true; } @@ -58,7 +64,7 @@ namespace MoonTools.ECS var index = indices[element]; array[index] = lastElement; indices[lastElement] = index; - Count -= 1; + count -= 1; indices.Remove(element); return true; @@ -66,45 +72,32 @@ namespace MoonTools.ECS public void Clear() { - Count = 0; + indices.Clear(); + count = 0; } - public struct Enumerator + protected virtual void Dispose(bool disposing) { - /// The set being enumerated. - private readonly IndexableSet _set; - /// The next index to yield. - private int _index; - - /// Initialize the enumerator. - /// The set to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(IndexableSet set) + if (!disposed) { - _set = set; - _index = _set.Count; - } + NativeMemory.Free(array); + array = null; - /// Advances the enumerator to the next element of the span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int index = _index - 1; - if (index >= 0) - { - _index = index; - return true; - } - - return false; + disposed = true; } + } - /// Gets the element at the current position of the enumerator. - public T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _set[_index]; - } + ~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/MessageStorage.cs b/src/MessageStorage.cs index b2f29d0..c5091a6 100644 --- a/src/MessageStorage.cs +++ b/src/MessageStorage.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -8,17 +11,18 @@ namespace MoonTools.ECS public abstract void Clear(); } - internal class MessageStorage : MessageStorage where TMessage : unmanaged + internal unsafe class MessageStorage : MessageStorage, IDisposable where TMessage : unmanaged { private int count = 0; private int capacity = 128; - private TMessage[] messages; + private TMessage* messages; // duplicating storage here for fast iteration - private Dictionary> entityToMessages = new Dictionary>(); + private Dictionary> entityToMessages = new Dictionary>(); + private bool disposed; public MessageStorage() { - messages = new TMessage[capacity]; + messages = (TMessage*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); } public void Add(in TMessage message) @@ -26,7 +30,7 @@ namespace MoonTools.ECS if (count == capacity) { capacity *= 2; - Array.Resize(ref messages, capacity); + messages = (TMessage*) NativeMemory.Realloc(messages, (nuint) (capacity * Unsafe.SizeOf())); } messages[count] = message; @@ -37,7 +41,7 @@ namespace MoonTools.ECS { if (!entityToMessages.ContainsKey(entityID)) { - entityToMessages.Add(entityID, new DynamicArray()); + entityToMessages.Add(entityID, new NativeArray()); } entityToMessages[entityID].Add(message); @@ -51,7 +55,7 @@ namespace MoonTools.ECS public ReadOnlySpan All() { - return new ReadOnlySpan(messages, 0, count); + return new ReadOnlySpan(messages, count); } public TMessage First() @@ -89,5 +93,39 @@ namespace MoonTools.ECS set.Clear(); } } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + Clear(); + + if (disposing) + { + foreach (var array in entityToMessages.Values) + { + array.Dispose(); + } + } + + NativeMemory.Free(messages); + messages = null; + + disposed = true; + } + } + + ~MessageStorage() + { + // 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/Random.cs b/src/Random.cs index acb62bb..6f8b258 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -1,77 +1,209 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace MoonTools.ECS { - public static class RandomGenerator + /// + /// 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 { - private static Random random = new Random(); + public const int STATE_BYTE_COUNT = 68; // 16 state ints + 1 index int - 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 - }; + uint[] State = new uint[16]; + uint Index = 0; + uint Seed; - public static void SetSeed(int seed) + /// + /// Initializes the RNG with an arbitrary seed. + /// + public Random() { - random = new Random(seed); - } - - internal static int Next(int maxValue) - { - return random.Next(maxValue); + Init((uint) Environment.TickCount); } /// - /// A psuedorandom nonrepeating sequence of integers from 0 to n. + /// Initializes the RNG with a given seed. /// - internal static LinearCongruentialEnumerator LinearCongruentialGenerator(int n) + public void Init(uint seed) { - var x = Primes[random.Next(Primes.Length - 1)]; - while (x % n == 0) + Seed = seed; + uint s = seed; + for (int i = 0; i < 16; i++) { - // not coprime, try again - x = Primes[random.Next(Primes.Length - 1)]; + s = (((s * 214013 + 2531011) >> 16) & 0x7fffffff) | 0; + State[i] = ~ ~s; //i ; } - - 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; + Index = 0; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() + /// + /// Returns the seed that was used to initialize the RNG. + /// + public uint GetSeed() { - current += 1; - if (current < start + count) + 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) { - return true; + s += State[i]; } - - return false; + s += Index; + return s; } - public int Current + /// + /// 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) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (current * prime) % count; + #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; } } } diff --git a/src/RandomManager.cs b/src/RandomManager.cs new file mode 100644 index 0000000..cf90d75 --- /dev/null +++ b/src/RandomManager.cs @@ -0,0 +1,82 @@ +using System.Runtime.CompilerServices; + +namespace MoonTools.ECS +{ + public static class RandomManager + { + private static Random Random = new Random(); + + 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 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/RelationDepot.cs b/src/RelationDepot.cs index c22493b..7963a6c 100644 --- a/src/RelationDepot.cs +++ b/src/RelationDepot.cs @@ -6,11 +6,13 @@ namespace MoonTools.ECS { internal class RelationDepot { + private EntityStorage EntityStorage; private TypeIndices RelationTypeIndices; private RelationStorage[] storages = new RelationStorage[256]; - public RelationDepot(TypeIndices relationTypeIndices) + public RelationDepot(EntityStorage entityStorage, TypeIndices relationTypeIndices) { + EntityStorage = entityStorage; RelationTypeIndices = relationTypeIndices; } @@ -123,26 +125,11 @@ namespace MoonTools.ECS storages[relationTypeIndex].Set(entityA, entityB, relationData); } - public int GetStorageIndex(int relationTypeIndex, int entityA, int entityB) - { - return storages[relationTypeIndex].GetStorageIndex(entityA, entityB); - } - - public unsafe void* Get(int relationTypeIndex, int relationStorageIndex) - { - return storages[relationTypeIndex].Get(relationStorageIndex); - } - public void UnrelateAll(int entityID, int relationTypeIndex) { storages[relationTypeIndex].UnrelateAll(entityID); } - public ReverseSpanEnumerator OutRelations(int entityID, int relationTypeIndex) - { - return storages[relationTypeIndex].OutRelations(entityID); - } - public void Clear() { for (var i = 0; i < storages.Length; i += 1) @@ -174,5 +161,33 @@ namespace MoonTools.ECS } } } + + public unsafe void TransferStorage(Dictionary worldToTransferID, RelationDepot other) + { + for (var i = 0; i < storages.Length; i += 1) + { + if (storages[i] != null) + { + foreach (var (a, b) in storages[i].All()) + { + if (worldToTransferID.TryGetValue(a, out var otherA)) + { + if (worldToTransferID.TryGetValue(b, out var otherB)) + { + var storageIndex = storages[i].GetStorageIndex(a, b); + var relationData = storages[i].Get(storageIndex); + other.Set(otherA, otherB, i, relationData); + other.EntityStorage.AddRelationKind(otherA, i); + other.EntityStorage.AddRelationKind(otherB, i); + } + else + { + throw new InvalidOperationException($"Missing transfer entity! {EntityStorage.Tag(a.ID)} related to {EntityStorage.Tag(b.ID)}"); + } + } + } + } + } + } } } diff --git a/src/RelationStorage.cs b/src/RelationStorage.cs index 009ffe3..50b4e43 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -9,26 +12,35 @@ namespace MoonTools.ECS public abstract int GetStorageIndex(int entityA, int entityB); public abstract unsafe void* Get(int relationStorageIndex); public abstract void UnrelateAll(int entityID); - public abstract ReverseSpanEnumerator OutRelations(int entityID); + public abstract ReverseSpanEnumerator<(Entity, Entity)> All(); public abstract RelationStorage CreateStorage(); public abstract void Clear(); } // Relation is the two entities, A related to B. // TRelation is the data attached to the relation. - internal class RelationStorage : RelationStorage where TRelation : unmanaged + internal unsafe class RelationStorage : RelationStorage, IDisposable where TRelation : unmanaged { private int count = 0; + private int capacity = 16; + private (Entity, Entity)* relations; + private TRelation* relationDatas; private Dictionary<(Entity, Entity), int> indices = new Dictionary<(Entity, Entity), int>(16); - private (Entity, Entity)[] relations = new (Entity, Entity)[16]; - private TRelation[] relationDatas = new TRelation[16]; private Dictionary> outRelations = new Dictionary>(16); private Dictionary> inRelations = new Dictionary>(16); private Stack> listPool = new Stack>(); - public ReverseSpanEnumerator<(Entity, Entity)> All() + private bool disposed; + + public RelationStorage() { - return new ReverseSpanEnumerator<(Entity, Entity)>(new Span<(Entity, Entity)>(relations, 0, count)); + relations = ((Entity, Entity)*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf<(Entity, Entity)>())); + relationDatas = (TRelation*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + } + + public override ReverseSpanEnumerator<(Entity, Entity)> All() + { + return new ReverseSpanEnumerator<(Entity, Entity)>(new Span<(Entity, Entity)>(relations, count)); } public void Set(in Entity entityA, in Entity entityB, TRelation relationData) @@ -56,10 +68,11 @@ namespace MoonTools.ECS } inRelations[idB].Add(idA); - if (count >= relationDatas.Length) + if (count >= capacity) { - Array.Resize(ref relations, relations.Length * 2); - Array.Resize(ref relationDatas, relationDatas.Length * 2); + capacity *= 2; + relations = ((Entity, Entity)*) NativeMemory.Realloc(relations, (nuint) (capacity * Unsafe.SizeOf<(Entity, Entity)>())); + relationDatas = (TRelation*) NativeMemory.Realloc(relationDatas, (nuint) (capacity * Unsafe.SizeOf())); } relations[count] = relation; @@ -78,7 +91,7 @@ namespace MoonTools.ECS return indices.ContainsKey(relation); } - public override ReverseSpanEnumerator OutRelations(int entityID) + public ReverseSpanEnumerator OutRelations(int entityID) { if (outRelations.TryGetValue(entityID, out var entityOutRelations)) { @@ -219,7 +232,7 @@ namespace MoonTools.ECS public override unsafe void Set(int entityA, int entityB, void* relationData) { - Set(entityA, entityB, *((TRelation*) relationData)); + Set(entityA, entityB, *(TRelation*) relationData); } public override int GetStorageIndex(int entityA, int entityB) @@ -229,10 +242,7 @@ namespace MoonTools.ECS public override unsafe void* Get(int relationStorageIndex) { - fixed (void* p = &relations[relationStorageIndex]) - { - return p; - } + return &relationDatas[relationStorageIndex]; } public override void UnrelateAll(int entityID) @@ -282,5 +292,41 @@ namespace MoonTools.ECS } outRelations.Clear(); } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + Clear(); + + if (disposing) + { + foreach (var set in listPool) + { + set.Dispose(); + } + } + + NativeMemory.Free(relations); + NativeMemory.Free(relationDatas); + relations = null; + relationDatas = null; + + disposed = true; + } + } + + ~RelationStorage() + { + // 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/World.cs b/src/World.cs index 79fa68f..9c14ad4 100644 --- a/src/World.cs +++ b/src/World.cs @@ -17,7 +17,7 @@ namespace MoonTools.ECS public World() { ComponentDepot = new ComponentDepot(ComponentTypeIndices); - RelationDepot = new RelationDepot(RelationTypeIndices); + RelationDepot = new RelationDepot(EntityStorage, RelationTypeIndices); FilterStorage = new FilterStorage(EntityStorage, ComponentTypeIndices); } @@ -54,14 +54,11 @@ namespace MoonTools.ECS } // untyped version for Transfer + // no filter check because filter state is copied directly internal unsafe void Set(Entity entity, int componentTypeIndex, void* component) { ComponentDepot.Set(entity.ID, componentTypeIndex, component); - - if (EntityStorage.SetComponent(entity.ID, componentTypeIndex)) - { - FilterStorage.Check(entity.ID, componentTypeIndex); - } + EntityStorage.SetComponent(entity.ID, componentTypeIndex); } public void Remove(in Entity entity) where TComponent : unmanaged @@ -82,14 +79,6 @@ namespace MoonTools.ECS EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex); } - // untyped version for Transfer - internal unsafe void Relate(Entity entityA, Entity entityB, int relationTypeIndex, void* relationData) - { - RelationDepot.Set(entityA, entityB, relationTypeIndex, relationData); - EntityStorage.AddRelationKind(entityA.ID, relationTypeIndex); - EntityStorage.AddRelationKind(entityB.ID, relationTypeIndex); - } - public void Unrelate(in Entity entityA, in Entity entityB) where TRelationKind : unmanaged { var (aEmpty, bEmpty) = RelationDepot.Remove(entityA, entityB); @@ -156,6 +145,14 @@ namespace MoonTools.ECS private Dictionary WorldToTransferID = new Dictionary(); + /// + /// If you are using the World Transfer feature, call this once after your systems/filters have all been initialized. + /// + public void PrepareTransferTo(World other) + { + other.FilterStorage.CreateMissingStorages(FilterStorage); + } + // FIXME: there's probably a better way to handle Filters so they are not world-bound public unsafe void Transfer(World other, Filter filter, Filter otherFilter) { @@ -176,30 +173,8 @@ namespace MoonTools.ECS WorldToTransferID.Add(entity.ID, otherWorldEntity.ID); } - // set relations before components so filters don't freak out - foreach (var entity in filter.Entities) - { - var otherWorldEntityA = WorldToTransferID[entity.ID]; - - foreach (var relationTypeIndex in EntityStorage.RelationTypeIndices(entity.ID)) - { - foreach (var entityB in RelationDepot.OutRelations(entity.ID, relationTypeIndex)) - { - var storageIndex = RelationDepot.GetStorageIndex(relationTypeIndex, entity.ID, entityB); - - int otherWorldEntityB; - if (WorldToTransferID.TryGetValue(entityB, out otherWorldEntityB)) - { - other.Relate(otherWorldEntityA, otherWorldEntityB, relationTypeIndex, RelationDepot.Get(relationTypeIndex, storageIndex)); - } - else - { - // related entity is not in the filter - throw new Exception($"Missing transfer entity! {EntityStorage.Tag(entity.ID)} related to {EntityStorage.Tag(entityB.ID)}"); - } - } - } - } + // transfer relations + RelationDepot.TransferStorage(WorldToTransferID, other.RelationDepot); // set components foreach (var entity in filter.Entities) @@ -211,6 +186,9 @@ namespace MoonTools.ECS other.Set(otherWorldEntity, componentTypeIndex, ComponentDepot.UntypedGet(entity.ID, componentTypeIndex)); } } + + // transfer filters last so callbacks trigger correctly + FilterStorage.TransferStorage(WorldToTransferID, other.FilterStorage); } } }