From a05eb94b7df4c819eb8bda673e102d8720fc3f9c Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 5 Oct 2023 15:56:45 -0700 Subject: [PATCH 01/14] add MoonTools.ECS.Random --- src/FilterStorage.cs | 4 +- src/LinearCongruentialGenerator.cs | 87 ++++++++++++++ src/Random.cs | 187 +++++++++++++++++++++-------- 3 files changed, 223 insertions(+), 55 deletions(-) create mode 100644 src/LinearCongruentialGenerator.cs diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index aad179b..e4780c5 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -59,7 +59,7 @@ namespace MoonTools.ECS return new RandomEntityEnumerator( this, filterSignature, - RandomGenerator.LinearCongruentialGenerator(FilterCount(filterSignature))); + LinearCongruentialGenerator.Generate(FilterCount(filterSignature))); } public Entity FilterNthEntity(FilterSignature filterSignature, int index) @@ -69,7 +69,7 @@ namespace MoonTools.ECS public Entity FilterRandomEntity(FilterSignature filterSignature) { - var randomIndex = RandomGenerator.Next(FilterCount(filterSignature)); + var randomIndex = LinearCongruentialGenerator.Next(FilterCount(filterSignature)); return new Entity(filterSignatureToEntityIDs[filterSignature][randomIndex]); } diff --git a/src/LinearCongruentialGenerator.cs b/src/LinearCongruentialGenerator.cs new file mode 100644 index 0000000..5190ce0 --- /dev/null +++ b/src/LinearCongruentialGenerator.cs @@ -0,0 +1,87 @@ +using System.Runtime.CompilerServices; + +namespace MoonTools.ECS +{ + public static class LinearCongruentialGenerator + { + 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 uint GetSeed() + { + return Random.GetSeed(); + } + + 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 Generate(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/Random.cs b/src/Random.cs index acb62bb..0779e46 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -1,77 +1,158 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace MoonTools.ECS { - public static class RandomGenerator + // WELLRNG512 + // https://stackoverflow.com/a/1227137 + 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) + public Random() { - random = new Random(seed); + Init((uint) Environment.TickCount); } - internal static int Next(int maxValue) + public void Init(uint seed) { - return random.Next(maxValue); - } - - /// - /// A psuedorandom nonrepeating sequence of integers from 0 to n. - /// - internal static LinearCongruentialEnumerator LinearCongruentialGenerator(int n) - { - 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() + public uint GetSeed() { - current += 1; - if (current < start + count) + return Seed; + } + + 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 + // expects a span of 512 bytes + 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); + } + } + + // expects a span of 512 bytes + 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]; + } + + // .NET Random API always returns a non-negative signed int. Fun! + public int Next() + { + return (int) (NextInternal() >> 1); // bitshift right to get rid of signed bit + } + + public int Next(int n) + { + var next = NextInternal(); + return (int) (((double) next) * n / uint.MaxValue); + } + + public int Next(int min, int max) + { + var diff = max - min; + var next = Next(diff); + return min + next; + } + + public long NextInt64() + { + long next = NextInternal(); + next <<= 32; + next |= NextInternal(); + return next; + } + + public long NextInt64(long n) + { + var next = NextInt64(); + return (long) (((double) next) * n / ulong.MaxValue); + } + + public long NextInt64(long min, long max) + { + var diff = max - min; + var next = NextInt64(diff); + return min + next; + } + + public double NextDouble() + { + var n = NextInternal(); + return ((double) n) / uint.MaxValue; } } } -- 2.25.1 From d1983bf168d1c0317d585130fc7a68be811bdc15 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 6 Oct 2023 12:27:11 -0700 Subject: [PATCH 02/14] fix an insanely stupid world transfer relation bug --- src/RelationStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RelationStorage.cs b/src/RelationStorage.cs index 009ffe3..ec990f8 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -229,7 +229,7 @@ namespace MoonTools.ECS public override unsafe void* Get(int relationStorageIndex) { - fixed (void* p = &relations[relationStorageIndex]) + fixed (void* p = &relationDatas[relationStorageIndex]) { return p; } -- 2.25.1 From 231575757e70b99dacea66f9cb4e42316703215d Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 6 Oct 2023 13:38:17 -0700 Subject: [PATCH 03/14] some rng fixes --- src/Random.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Random.cs b/src/Random.cs index 0779e46..0a38c21 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -117,8 +117,7 @@ namespace MoonTools.ECS public int Next(int n) { - var next = NextInternal(); - return (int) (((double) next) * n / uint.MaxValue); + return (int) (((double) Next()) * n / int.MaxValue); } public int Next(int min, int max) @@ -131,7 +130,7 @@ namespace MoonTools.ECS public long NextInt64() { long next = NextInternal(); - next <<= 32; + next <<= 31; next |= NextInternal(); return next; } @@ -139,7 +138,7 @@ namespace MoonTools.ECS public long NextInt64(long n) { var next = NextInt64(); - return (long) (((double) next) * n / ulong.MaxValue); + return (long) (((double) next) * n / long.MaxValue); } public long NextInt64(long min, long max) -- 2.25.1 From 4722445f11beafbcda11728585b798eda0351fe5 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 9 Oct 2023 12:03:48 -0700 Subject: [PATCH 04/14] refactor collections to use NativeMemory --- src/ComponentDepot.cs | 2 +- src/ComponentStorage.cs | 76 +++++++++++++++++++++++---------- src/DynamicArray.cs | 65 ++++++++++++++++++++-------- src/FilterStorage.cs | 1 + src/IndexableSet.cs | 93 +++++++++++++++++++---------------------- src/MessageStorage.cs | 52 +++++++++++++++++++---- src/RelationStorage.cs | 70 +++++++++++++++++++++++++------ 7 files changed, 250 insertions(+), 109 deletions(-) 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/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/FilterStorage.cs b/src/FilterStorage.cs index e4780c5..fc7718f 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 { 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/RelationStorage.cs b/src/RelationStorage.cs index ec990f8..166f3c7 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 { @@ -16,19 +19,28 @@ namespace MoonTools.ECS // 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>(); + private bool disposed; + + public RelationStorage() + { + relations = ((Entity, Entity)*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf<(Entity, Entity)>())); + relationDatas = (TRelation*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); + } + public ReverseSpanEnumerator<(Entity, Entity)> All() { - return new ReverseSpanEnumerator<(Entity, Entity)>(new Span<(Entity, Entity)>(relations, 0, count)); + 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; @@ -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 = &relationDatas[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); + } } } -- 2.25.1 From 3cb3c64459ef5ffb340d1f6be28a1ab546349319 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 9 Oct 2023 14:06:13 -0700 Subject: [PATCH 05/14] update random state comment --- src/Random.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Random.cs b/src/Random.cs index 0a38c21..a6fb7b7 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -46,7 +46,7 @@ namespace MoonTools.ECS return s; } - // expects a span of 512 bytes + // expects a span of STATE_BYTE_COUNT bytes public unsafe void SaveState(Span bytes) { #if DEBUG @@ -69,7 +69,7 @@ namespace MoonTools.ECS } } - // expects a span of 512 bytes + // expects a span of STATE_BYTE_COUNT bytes public unsafe void LoadState(Span bytes) { #if DEBUG -- 2.25.1 From 7b519ed4b27ae47e624daa74ed8e662acaaa8d34 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 9 Oct 2023 16:48:58 -0700 Subject: [PATCH 06/14] filter determinism --- src/Filter.cs | 3 ++- src/FilterBuilder.cs | 13 ++++++------- src/FilterSignature.cs | 7 ++++--- src/FilterStorage.cs | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) 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..f0eeaba 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; diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index fc7718f..163751e 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -20,7 +20,7 @@ namespace MoonTools.ECS ComponentTypeIndices = componentTypeIndices; } - public Filter CreateFilter(HashSet included, HashSet excluded) + public Filter CreateFilter(IndexableSet included, IndexableSet excluded) { var filterSignature = new FilterSignature(included, excluded); if (!filterSignatureToEntityIDs.ContainsKey(filterSignature)) -- 2.25.1 From 1ef141422ccd764f598173c921c2620640dc4dc5 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Mon, 9 Oct 2023 17:34:55 -0700 Subject: [PATCH 07/14] more determinism --- src/FilterStorage.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index 163751e..3cdf170 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -9,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>(); @@ -31,7 +31,7 @@ namespace MoonTools.ECS { if (!typeToFilterSignatures.ContainsKey(type)) { - typeToFilterSignatures.Add(type, new HashSet()); + typeToFilterSignatures.Add(type, new List()); } typeToFilterSignatures[type].Add(filterSignature); @@ -41,7 +41,7 @@ namespace MoonTools.ECS { if (!typeToFilterSignatures.ContainsKey(type)) { - typeToFilterSignatures.Add(type, new HashSet()); + typeToFilterSignatures.Add(type, new List()); } typeToFilterSignatures[type].Add(filterSignature); -- 2.25.1 From a2a81bf47790dc88102afd6c12a0986de1c08b85 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 10 Oct 2023 11:59:28 -0700 Subject: [PATCH 08/14] world transfer determinism --- src/DebugSystem.cs | 29 ++++++++++++++++++++++++----- src/EntityStorage.cs | 13 +++++++------ src/FilterSignature.cs | 2 -- src/FilterStorage.cs | 8 +++++--- 4 files changed, 36 insertions(+), 16 deletions(-) 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/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/FilterSignature.cs b/src/FilterSignature.cs index f0eeaba..7e94922 100644 --- a/src/FilterSignature.cs +++ b/src/FilterSignature.cs @@ -22,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 3cdf170..9d10c2c 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -148,10 +148,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); + } } } -- 2.25.1 From 3cba3e047c04ec4ec5d0ed7ca6a8d361ac782a0c Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Wed, 11 Oct 2023 15:35:44 -0700 Subject: [PATCH 09/14] fix filter results being out of order on world transfer --- src/FilterStorage.cs | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/World.cs | 14 +++++++---- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index 9d10c2c..55c9b9b 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -20,6 +20,35 @@ namespace MoonTools.ECS ComponentTypeIndices = componentTypeIndices; } + 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); @@ -174,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/World.cs b/src/World.cs index 79fa68f..e0400e0 100644 --- a/src/World.cs +++ b/src/World.cs @@ -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 @@ -163,6 +160,10 @@ namespace MoonTools.ECS other.ComponentDepot.CreateMissingStorages(ComponentDepot); other.RelationDepot.CreateMissingStorages(RelationDepot); + // FIXME: we could just do this once on startup + // Could have a PrepareTransfer method or something + other.FilterStorage.CreateMissingStorages(FilterStorage); + // destroy all entities matching the filter foreach (var entity in otherFilter.Entities) { @@ -176,6 +177,7 @@ namespace MoonTools.ECS WorldToTransferID.Add(entity.ID, otherWorldEntity.ID); } + // FIXME: make sure this preserves relation order, should probably do something similar to filter storage // set relations before components so filters don't freak out foreach (var entity in filter.Entities) { @@ -211,6 +213,8 @@ namespace MoonTools.ECS other.Set(otherWorldEntity, componentTypeIndex, ComponentDepot.UntypedGet(entity.ID, componentTypeIndex)); } } + + FilterStorage.TransferStorage(WorldToTransferID, other.FilterStorage); } } } -- 2.25.1 From 2aa8fc87e7f581a105c83979bbb59a192cffa88d Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 12 Oct 2023 12:27:13 -0700 Subject: [PATCH 10/14] transfer relations in order --- src/RelationDepot.cs | 47 ++++++++++++++++++++++++++++-------------- src/RelationStorage.cs | 6 +++--- src/World.cs | 38 ++++------------------------------ 3 files changed, 38 insertions(+), 53 deletions(-) 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 166f3c7..50b4e43 100644 --- a/src/RelationStorage.cs +++ b/src/RelationStorage.cs @@ -12,7 +12,7 @@ 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(); } @@ -38,7 +38,7 @@ namespace MoonTools.ECS relationDatas = (TRelation*) NativeMemory.Alloc((nuint) (capacity * Unsafe.SizeOf())); } - public ReverseSpanEnumerator<(Entity, Entity)> All() + public override ReverseSpanEnumerator<(Entity, Entity)> All() { return new ReverseSpanEnumerator<(Entity, Entity)>(new Span<(Entity, Entity)>(relations, count)); } @@ -91,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)) { diff --git a/src/World.cs b/src/World.cs index e0400e0..a26f243 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); } @@ -79,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); @@ -177,31 +169,8 @@ namespace MoonTools.ECS WorldToTransferID.Add(entity.ID, otherWorldEntity.ID); } - // FIXME: make sure this preserves relation order, should probably do something similar to filter storage - // 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) @@ -214,6 +183,7 @@ namespace MoonTools.ECS } } + // transfer filters last so callbacks trigger correctly FilterStorage.TransferStorage(WorldToTransferID, other.FilterStorage); } } -- 2.25.1 From 855b8d6487128672f26d55349a9cb121e8c76696 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 12 Oct 2023 12:46:25 -0700 Subject: [PATCH 11/14] PrepareTransferTo method to speed up transfers --- src/World.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/World.cs b/src/World.cs index a26f243..9c14ad4 100644 --- a/src/World.cs +++ b/src/World.cs @@ -145,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) { @@ -152,10 +160,6 @@ namespace MoonTools.ECS other.ComponentDepot.CreateMissingStorages(ComponentDepot); other.RelationDepot.CreateMissingStorages(RelationDepot); - // FIXME: we could just do this once on startup - // Could have a PrepareTransfer method or something - other.FilterStorage.CreateMissingStorages(FilterStorage); - // destroy all entities matching the filter foreach (var entity in otherFilter.Entities) { -- 2.25.1 From 35934baec0e5644039ab58e2b260a1279462adbb Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 13 Oct 2023 12:29:38 -0700 Subject: [PATCH 12/14] fix random sometimes returning negative integers --- src/Random.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Random.cs b/src/Random.cs index a6fb7b7..385bfb2 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -112,7 +112,7 @@ namespace MoonTools.ECS // .NET Random API always returns a non-negative signed int. Fun! public int Next() { - return (int) (NextInternal() >> 1); // bitshift right to get rid of signed bit + return (int) (NextInternal() >>> 1); // unsigned bitshift right to get rid of signed bit } public int Next(int n) @@ -130,8 +130,9 @@ namespace MoonTools.ECS public long NextInt64() { long next = NextInternal(); - next <<= 31; + next <<= 32; next |= NextInternal(); + next >>>= 1; return next; } @@ -148,6 +149,12 @@ namespace MoonTools.ECS return min + next; } + public float NextFloat() + { + var n = NextInternal(); + return ((float) n) / uint.MaxValue; + } + public double NextDouble() { var n = NextInternal(); -- 2.25.1 From 3a7c9da309d961dd69b84b620f71db6056414e0d Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 13 Oct 2023 13:18:14 -0700 Subject: [PATCH 13/14] document the Random class --- src/Random.cs | 65 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/Random.cs b/src/Random.cs index 385bfb2..6f8b258 100644 --- a/src/Random.cs +++ b/src/Random.cs @@ -3,8 +3,10 @@ using System.Runtime.CompilerServices; namespace MoonTools.ECS { - // WELLRNG512 - // https://stackoverflow.com/a/1227137 + /// + /// 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 @@ -13,11 +15,17 @@ namespace MoonTools.ECS uint Index = 0; uint Seed; + /// + /// Initializes the RNG with an arbitrary seed. + /// public Random() { Init((uint) Environment.TickCount); } + /// + /// Initializes the RNG with a given seed. + /// public void Init(uint seed) { Seed = seed; @@ -30,11 +38,17 @@ namespace MoonTools.ECS Index = 0; } + /// + /// 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 = ""; @@ -46,7 +60,11 @@ namespace MoonTools.ECS return s; } - // expects a span of STATE_BYTE_COUNT bytes + /// + /// 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 @@ -69,7 +87,11 @@ namespace MoonTools.ECS } } - // expects a span of STATE_BYTE_COUNT bytes + /// + /// 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 @@ -109,17 +131,25 @@ namespace MoonTools.ECS return State[Index]; } - // .NET Random API always returns a non-negative signed int. Fun! + /// + /// Returns a non-negative signed integer. + /// public int Next() { return (int) (NextInternal() >>> 1); // unsigned bitshift right to get rid of signed bit } - public int Next(int n) + /// + /// Returns a non-negative signed integer less than max. + /// + public int Next(int max) { - return (int) (((double) Next()) * n / int.MaxValue); + 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; @@ -127,6 +157,9 @@ namespace MoonTools.ECS return min + next; } + /// + /// Returns a non-negative signed 64 bit integer. + /// public long NextInt64() { long next = NextInternal(); @@ -136,12 +169,18 @@ namespace MoonTools.ECS return next; } - public long NextInt64(long n) + /// + /// Returns a non-negative signed 64 bit integer less than max. + /// + public long NextInt64(long max) { var next = NextInt64(); - return (long) (((double) next) * n / long.MaxValue); + 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; @@ -149,12 +188,18 @@ namespace MoonTools.ECS return min + next; } - public float NextFloat() + /// + /// 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(); -- 2.25.1 From 500f0a6903ac99f40f9ff45b604081f73898a22f Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 13 Oct 2023 13:36:28 -0700 Subject: [PATCH 14/14] rename some random methods --- src/FilterStorage.cs | 4 ++-- src/{LinearCongruentialGenerator.cs => RandomManager.cs} | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) rename src/{LinearCongruentialGenerator.cs => RandomManager.cs} (97%) diff --git a/src/FilterStorage.cs b/src/FilterStorage.cs index 55c9b9b..7eb11f9 100644 --- a/src/FilterStorage.cs +++ b/src/FilterStorage.cs @@ -89,7 +89,7 @@ namespace MoonTools.ECS return new RandomEntityEnumerator( this, filterSignature, - LinearCongruentialGenerator.Generate(FilterCount(filterSignature))); + RandomManager.LinearCongruentialSequence(FilterCount(filterSignature))); } public Entity FilterNthEntity(FilterSignature filterSignature, int index) @@ -99,7 +99,7 @@ namespace MoonTools.ECS public Entity FilterRandomEntity(FilterSignature filterSignature) { - var randomIndex = LinearCongruentialGenerator.Next(FilterCount(filterSignature)); + var randomIndex = RandomManager.Next(FilterCount(filterSignature)); return new Entity(filterSignatureToEntityIDs[filterSignature][randomIndex]); } diff --git a/src/LinearCongruentialGenerator.cs b/src/RandomManager.cs similarity index 97% rename from src/LinearCongruentialGenerator.cs rename to src/RandomManager.cs index 5190ce0..cf90d75 100644 --- a/src/LinearCongruentialGenerator.cs +++ b/src/RandomManager.cs @@ -2,7 +2,7 @@ namespace MoonTools.ECS { - public static class LinearCongruentialGenerator + public static class RandomManager { private static Random Random = new Random(); @@ -11,11 +11,6 @@ namespace MoonTools.ECS 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 uint GetSeed() - { - return Random.GetSeed(); - } - public static void SetRandom(Random random) { Random = random; @@ -29,7 +24,7 @@ namespace MoonTools.ECS /// /// A psuedorandom nonrepeating sequence of integers from 0 to n. /// - internal static LinearCongruentialEnumerator Generate(int n) + internal static LinearCongruentialEnumerator LinearCongruentialSequence(int n) { if (n == 0) { -- 2.25.1