diff --git a/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs b/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs deleted file mode 100644 index ecc08a60e..000000000 --- a/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using Server.Collections; -using Server.Random; -using Xunit; - -namespace Server.Tests; - -public sealed class PooledRefQueueTests : IDisposable -{ - private class MockedRandomSource : IRandomSource - { - private int _queueCount; - private int _mockedValue; - - public MockedRandomSource(int queueCount, int mockedValue) - { - _queueCount = queueCount; - _mockedValue = mockedValue; - } - - public int Next() => throw new NotImplementedException(); - - public int Next(int maxValue) => _mockedValue; - - public int Next(int minValue, int count) => throw new NotImplementedException(); - - public uint Next(uint maxValue) => throw new NotImplementedException(); - - public uint Next(uint minValue, uint count) => throw new NotImplementedException(); - - public long Next(long maxValue) => throw new NotImplementedException(); - - public long Next(long minValue, long count) => throw new NotImplementedException(); - - public double NextDouble() => throw new NotImplementedException(); - - public void NextBytes(Span buffer) - { - throw new NotImplementedException(); - } - - public int NextInt() => throw new NotImplementedException(); - - public uint NextUInt() => throw new NotImplementedException(); - - public ulong NextULong() => throw new NotImplementedException(); - - public bool NextBool() => throw new NotImplementedException(); - - public byte NextByte() => throw new NotImplementedException(); - - public float NextFloat() => throw new NotImplementedException(); - - public float NextFloatNonZero() => throw new NotImplementedException(); - - public double NextDoubleNonZero() => throw new NotImplementedException(); - - public double NextDoubleHighRes() => throw new NotImplementedException(); - } - - public void Dispose() => RandomSources.SetRng(null); - - private static void PrepareRng(int queueCount, int rngValue) - { - var mockedRng = new MockedRandomSource(queueCount, rngValue); - RandomSources.SetRng(mockedRng); - } - - [Fact] - public void TestPeekRandom1() - { - // Random value for _head = 0, _tail = 5, _size = 5, - using var queue = PooledRefQueue.Create(10); - queue.Enqueue(0); - queue.Enqueue(1); - queue.Enqueue(2); - queue.Enqueue(3); // <----- - queue.Enqueue(4); - queue.Enqueue(5); - - PrepareRng(6, 3); - Assert.Equal(3, queue.PeekRandom()); - } - - [Fact] - public void TestPeekRandom2() - { - // Random value for _head = 3, _tail = 10, _size = 7, - using var queue = PooledRefQueue.Create(10); - queue.Enqueue(0); - queue.Enqueue(1); - queue.Enqueue(2); - - queue.Enqueue(3); - queue.Enqueue(4); - queue.Enqueue(5); - queue.Enqueue(6); // <--- - queue.Enqueue(7); - queue.Enqueue(8); - queue.Enqueue(9); - - queue.Dequeue(); - queue.Dequeue(); - queue.Dequeue(); - - PrepareRng(7, 3); - Assert.Equal(6, queue.PeekRandom()); - } - - [Theory] - [InlineData(3, 6)] - [InlineData(8, 11)] - [InlineData(6, 9)] - [InlineData(7, 10)] - public void TestPeekRandom3(int rngValue, int expectedIndex) - { - // Random value for _head = 3, _tail = 2, _size = 10, - using var queue = PooledRefQueue.Create(10); - queue.Enqueue(0); - queue.Enqueue(1); - queue.Enqueue(2); - - queue.Enqueue(3); - queue.Enqueue(4); - queue.Enqueue(5); - queue.Enqueue(6); - queue.Enqueue(7); - queue.Enqueue(8); - queue.Enqueue(9); - - queue.Dequeue(); - queue.Dequeue(); - queue.Dequeue(); - - queue.Enqueue(10); - queue.Enqueue(11); - queue.Enqueue(12); - - PrepareRng(10, rngValue); - Assert.Equal(expectedIndex, queue.PeekRandom()); - } -} diff --git a/Projects/Server/Random/BaseRandomSource.cs b/Projects/Server/Random/BaseRandomSource.cs deleted file mode 100644 index 3d2cae993..000000000 --- a/Projects/Server/Random/BaseRandomSource.cs +++ /dev/null @@ -1,190 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BaseRandomSource.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Server.Random; - -public abstract class BaseRandomSource : IRandomSource -{ - private const double INCR_DOUBLE = 1.0 / (1UL << 53); - private const float INCR_FLOAT = 1f / (1U << 24); - - public abstract ulong NextULong(); - public abstract void NextBytes(Span buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Next() - { - ulong rtn; - do - { - rtn = NextULong() >> 33; - } while (rtn == 0x7fff_ffffUL); - - return (int)rtn; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Next(int count) - { - Debug.Assert(count != 0, $"{nameof(count)} must not be 0"); - - if (count is -1 or 0 or 1) - { - return 0; - } - - var negative = count < 0; - - var max = negative ? -count : count; - - var bits = Log2((uint)max); - - int x; - do - { - x = (int)(NextULong() >> (64 - bits)); - } while (x >= max); - - return negative ? -x : x; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Next(int minValue, int count) => minValue + Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint Next(uint count) - { - Debug.Assert(count != 0, $"{nameof(count)} must not be 0"); - - if (count is 0 or 1) - { - return 0; - } - - var bits = Log2(count); - - uint x; - do - { - x = (uint)(NextULong() >> (64 - bits)); - } while (x >= count); - - return x; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint Next(uint minValue, uint count) => minValue + Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long Next(long count) - { - Debug.Assert(count != 0, $"{nameof(count)} must not be 0"); - - if (count is -1 or 0 or 1) - { - return 0; - } - - var negative = count < 0; - - var max = negative ? -count : count; - - var bits = Log2((ulong)max); - - long x; - do - { - x = (long)(NextULong() >> (64 - bits)); - } while (x >= max); - - return negative ? -x : x; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long Next(long minValue, long count) => minValue + Next(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double NextDouble() => (NextULong() >> 11) * INCR_DOUBLE; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int NextInt() => (int)(NextULong() >> 33); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint NextUInt() => (uint)NextULong(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool NextBool() => (NextULong() & 0x8000000000000000) != 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte NextByte() => (byte)(NextULong() >> 56); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float NextFloat() => (NextULong() >> 40) * INCR_FLOAT; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float NextFloatNonZero() => NextFloat() + INCR_FLOAT; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double NextDoubleNonZero() => NextDouble() + INCR_DOUBLE; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public double NextDoubleHighRes() - { - var exponent = -64; - ulong significand; - int shift; - - while ((significand = NextULong()) == 0) - { - exponent -= 64; - - if (exponent < -1074) - { - return 0; - } - } - - shift = BitOperations.LeadingZeroCount(significand); - if (shift != 0) - { - exponent -= shift; - significand <<= shift; - significand |= NextULong() >> (64 - shift); - } - - significand |= 1; - - return significand * Math.Pow(2, exponent); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Log2(uint v) - { - var exp = BitOperations.Log2(v); - return v == 1 << exp ? exp : exp + 1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Log2(ulong v) - { - var exp = BitOperations.Log2(v); - return v == 1UL << exp ? exp : exp + 1; - } -} diff --git a/Projects/Server/Random/BuiltInRng.cs b/Projects/Server/Random/BuiltInRng.cs new file mode 100644 index 000000000..31d52b590 --- /dev/null +++ b/Projects/Server/Random/BuiltInRng.cs @@ -0,0 +1,47 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: BuiltInRng.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; + +namespace Server.Random; + +public static class BuiltInRng +{ + public static System.Random Generator { get; private set; } = new(); + + public static void Reset() => Generator = new System.Random(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Next() => Generator.Next(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Next(int maxValue) => Generator.Next(maxValue); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Next(int minValue, int count) => minValue + Generator.Next(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Next(long maxValue) => Generator.NextInt64(maxValue); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Next(long minValue, long count) => minValue + Generator.NextInt64(count); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double NextDouble() => Generator.NextDouble(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NextBytes(Span buffer) => Generator.NextBytes(buffer); +} diff --git a/Projects/Server/Random/SecureRandom.cs b/Projects/Server/Random/BuiltInSecureRng.cs similarity index 68% rename from Projects/Server/Random/SecureRandom.cs rename to Projects/Server/Random/BuiltInSecureRng.cs index 5ca4ad473..dec408338 100644 --- a/Projects/Server/Random/SecureRandom.cs +++ b/Projects/Server/Random/BuiltInSecureRng.cs @@ -14,27 +14,15 @@ *************************************************************************/ using System; -using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Security.Cryptography; -using Server.Random; namespace Server; -public class SecureRandom : BaseRandomSource +public static class BuiltInSecureRng { - private RandomNumberGenerator m_Random; - - public RandomNumberGenerator Generator => m_Random ??= RandomNumberGenerator.Create(); + public static RandomNumberGenerator Generator { get; } = RandomNumberGenerator.Create(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override ulong NextULong() - { - Span buffer = stackalloc byte[sizeof(ulong)]; - NextBytes(buffer); - return BinaryPrimitives.ReadUInt64BigEndian(buffer); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override void NextBytes(Span buffer) => Generator.GetBytes(buffer); + public static void NextBytes(Span buffer) => Generator.GetBytes(buffer); } diff --git a/Projects/Server/Random/IRandomSource.cs b/Projects/Server/Random/IRandomSource.cs deleted file mode 100644 index 9deadd44f..000000000 --- a/Projects/Server/Random/IRandomSource.cs +++ /dev/null @@ -1,40 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: IRandomSource.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; - -namespace Server.Random; - -public interface IRandomSource -{ - int Next(); - int Next(int maxValue); - int Next(int minValue, int count); - uint Next(uint maxValue); - uint Next(uint minValue, uint count); - long Next(long maxValue); - long Next(long minValue, long count); - double NextDouble(); - void NextBytes(Span buffer); - int NextInt(); - uint NextUInt(); - ulong NextULong(); - bool NextBool(); - byte NextByte(); - float NextFloat(); - float NextFloatNonZero(); - double NextDoubleNonZero(); - double NextDoubleHighRes(); -} diff --git a/Projects/Server/Random/RandomSources.cs b/Projects/Server/Random/RandomSources.cs deleted file mode 100644 index c4bda348a..000000000 --- a/Projects/Server/Random/RandomSources.cs +++ /dev/null @@ -1,29 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: RandomSources.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -namespace Server.Random; - -public static class RandomSources -{ - private static IRandomSource _source; - private static IRandomSource _secureSource; - - public static IRandomSource Source => _source ??= new Xoshiro256PlusPlus(); - public static IRandomSource SecureSource => _secureSource ??= new SecureRandom(); - - public static void SetRng(IRandomSource newSource) => _source = newSource; - - public static void SetSecureRng(IRandomSource newSource) => _secureSource = newSource; -} diff --git a/Projects/Server/Random/StableRandom.cs b/Projects/Server/Random/StableRandom.cs new file mode 100644 index 000000000..c79cbfb58 --- /dev/null +++ b/Projects/Server/Random/StableRandom.cs @@ -0,0 +1,81 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: StableRandom.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Server.Random; + +public static class StableRandom +{ + // Returns the first long of a new Rng. Used for stable values given a specific set of inputs. + public static long First(ulong seed, long maxValue) + { + Span state = stackalloc ulong[4]; + + var x = seed; + state[0] = NextSplitMix(ref x); + state[1] = NextSplitMix(ref x); + state[2] = NextSplitMix(ref x); + state[3] = NextSplitMix(ref x); + + return (long)NextUInt64(state, (ulong)maxValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong NextUInt64(Span state, ulong maxValue) + { + ulong randomProduct = Math.BigMul(maxValue, NextUInt64(ref state), out ulong lowPart); + + if (lowPart < maxValue) + { + ulong remainder = (0ul - maxValue) % maxValue; + + while (lowPart < remainder) + { + randomProduct = Math.BigMul(maxValue, NextUInt64(ref state), out lowPart); + } + } + + return randomProduct; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong NextUInt64(ref Span state) + { + ulong result = BitOperations.RotateLeft(state[1] * 5, 7) * 9; + ulong t = state[1] << 17; + + state[2] ^= state[0]; + state[3] ^= state[1]; + state[1] ^= state[2]; + state[0] ^= state[3]; + + state[2] ^= t; + state[3] = BitOperations.RotateLeft(state[3], 45); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong NextSplitMix(ref ulong x) + { + var z = x += 0x9e3779b97f4a7c15; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); + } +} diff --git a/Projects/Server/Random/Xoshiro256PlusPlus.cs b/Projects/Server/Random/Xoshiro256PlusPlus.cs deleted file mode 100644 index 47cc1d6a4..000000000 --- a/Projects/Server/Random/Xoshiro256PlusPlus.cs +++ /dev/null @@ -1,222 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Xoshiro256PlusPlus.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Buffers.Binary; -using System.Runtime.CompilerServices; - -namespace Server.Random; - -public class Xoshiro256PlusPlus : BaseRandomSource -{ - private static readonly ulong[] JUMP = - { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; - - private static readonly ulong[] LONG_JUMP = - { 0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635 }; - - private ulong _s0, _s1, _s2, _s3; - - public Xoshiro256PlusPlus() - { - Span states = stackalloc byte[32]; - RandomSources.SecureSource.NextBytes(states); - _s0 = BinaryPrimitives.ReadUInt64LittleEndian(states[..8]); - _s1 = BinaryPrimitives.ReadUInt64LittleEndian(states[8..16]); - _s2 = BinaryPrimitives.ReadUInt64LittleEndian(states[16..24]); - _s3 = BinaryPrimitives.ReadUInt64LittleEndian(states[24..32]); - } - - public Xoshiro256PlusPlus(ulong seed) - { - var mix = new SplitMix64(seed); - Span state = stackalloc ulong[4]; - mix.FillArray(state); - _s0 = state[0]; - _s1 = state[1]; - _s2 = state[2]; - _s3 = state[3]; - } - - private Xoshiro256PlusPlus(ulong s0, ulong s1, ulong s2, ulong s3) - { - _s0 = s0; - _s1 = s1; - _s2 = s2; - _s3 = s3; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override ulong NextULong() - { - var r1 = (_s1 << 2) + _s1; - var r2 = (r1 << 7) | (r1 >> 57); - var rslt = (r2 << 3) + r2; - - var t = _s1 << 17; - - _s2 ^= _s0; - _s3 ^= _s1; - _s1 ^= _s2; - _s0 ^= _s3; - - _s2 ^= t; - - _s3 = (_s3 << 45) | (_s3 >> 19); - - return rslt; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override unsafe void NextBytes(Span b) - { - if (b.Length == 0) - { - return; - } - - var s0 = _s0; - var s1 = _s1; - var s2 = _s2; - var s3 = _s3; - - var i = 0; - - fixed (byte* pBuffer = b) - { - var pULong = (ulong*)pBuffer; - - for (var bound = b.Length / sizeof(ulong); i < bound; i++) - { - var r1 = (s1 << 2) + s1; - var r2 = (r1 << 7) | (r1 >> 57); - pULong[i] = (r2 << 3) + r2; - - var t = s1 << 17; - s2 ^= s0; - s3 ^= s1; - s1 ^= s2; - s0 ^= s3; - - s2 ^= t; - - s3 = (s3 << 45) | (s3 >> 19); - } - } - - i *= 8; - - if (i < b.Length) - { - var r1 = (s1 << 2) + s1; - var r2 = (r1 << 7) | (r1 >> 57); - var rslt = (r2 << 3) + r2; - - var t = s1 << 17; - - s2 ^= s0; - s3 ^= s1; - s1 ^= s2; - s0 ^= s3; - - s2 ^= t; - - s3 = (s3 << 45) | (s3 >> 19); - - while (i < b.Length) - { - b[i++] = (byte)rslt; - rslt >>= 8; - } - } - - _s0 = s0; - _s1 = s1; - _s2 = s2; - _s3 = s3; - } - - public void Jump() => Jump(JUMP); - - public void LongJump() => Jump(LONG_JUMP); - - private void Jump(in ulong[] jumps) - { - ulong s0 = 0; - ulong s1 = 0; - ulong s2 = 0; - ulong s3 = 0; - - for (var i = 0; i < jumps.Length; i++) - { - for (var b = 0; b < 64; b++) - { - if ((jumps[i] & (1ul << b)) != 0) - { - s0 ^= _s0; - s1 ^= _s1; - s2 ^= _s2; - s3 ^= _s3; - } - - NextULong(); - } - } - - _s0 = s0; - _s1 = s1; - _s2 = s2; - _s3 = s3; - } - - public Xoshiro256PlusPlus Split() - { - var rng = new Xoshiro256PlusPlus(_s0, _s1, _s2, _s3); - rng.Jump(); - return rng; - } - - public Xoshiro256PlusPlus LongSplit() - { - var rng = new Xoshiro256PlusPlus(_s0, _s1, _s2, _s3); - rng.LongJump(); - return rng; - } -} - -public class SplitMix64 -{ - private ulong x; - - public SplitMix64(ulong seed) => x = seed; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ulong Next() - { - var z = x += 0x9e3779b97f4a7c15; - z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; - z = (z ^ (z >> 27)) * 0x94d049bb133111eb; - return z ^ (z >> 31); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FillArray(Span arr) - { - for (var i = 0; i < arr.Length; i++) - { - arr[i] = Next(); - } - } -} diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index b3f2e965b..3e3d7a5d8 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -6,6 +6,7 @@ using System.IO; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using System.Xml; using Server.Buffers; @@ -968,44 +969,24 @@ public static class Utility for (var i = 0; i < amount; ++i) { - total += RandomSources.Source.Next(1, sides); + total += BuiltInRng.Next(1, sides); } return total + bonus; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Shuffle(this IList list) - { - var count = list.Count; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } - } + public static void Shuffle(this T[] array) => BuiltInRng.Generator.Shuffle(array); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Shuffle(this Span list) - { - var count = list.Length; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } - } + public static void Shuffle(this List list) => BuiltInRng.Generator.Shuffle(CollectionsMarshal.AsSpan(list)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Shuffle(this PooledRefList list) - { - var count = list.Count; - for (var i = 0; i < count; i++) - { - var r = RandomMinMax(i, count - 1); - (list[r], list[i]) = (list[i], list[r]); - } - } + public static void Shuffle(this Span span) => BuiltInRng.Generator.Shuffle(span); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Shuffle(this PooledRefList list) => + BuiltInRng.Generator.Shuffle(list._items.AsSpan(0, list.Count)); /** * Gets a random sample from the source list. @@ -1201,7 +1182,7 @@ public static class Utility list.Count == 0 ? valueIfZero : list[Random(list.Count)]; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool RandomBool() => RandomSources.Source.NextBool(); + public static bool RandomBool() => BuiltInRng.Next(2) == 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TimeSpan RandomMinMax(TimeSpan min, TimeSpan max) => new(RandomMinMax(min.Ticks, max.Ticks)); @@ -1218,22 +1199,7 @@ public static class Utility return min; } - return min + RandomSources.Source.NextDouble() * (max - min); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint RandomMinMax(uint min, uint max) - { - if (min > max) - { - (min, max) = (max, min); - } - else if (min == max) - { - return min; - } - - return min + RandomSources.Source.Next(max - min + 1); + return min + BuiltInRng.NextDouble() * (max - min); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1248,7 +1214,7 @@ public static class Utility return min; } - return min + (int)RandomSources.Source.Next((uint)(max - min + 1)); + return min + BuiltInRng.Next(max - min + 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1263,23 +1229,26 @@ public static class Utility return min; } - return min + RandomSources.Source.Next(max - min + 1); + return min + BuiltInRng.Next(max - min + 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Random(int from, int count) => RandomSources.Source.Next(from, count); + public static int Random(int from, int count) => BuiltInRng.Next(from, count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Random(int count) => RandomSources.Source.Next(count); + public static int Random(int count) => BuiltInRng.Next(count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Random(uint count) => RandomSources.Source.Next(count); + public static long Random(long from, long count) => BuiltInRng.Next(from, count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RandomBytes(Span buffer) => RandomSources.Source.NextBytes(buffer); + public static long Random(long count) => BuiltInRng.Next(count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double RandomDouble() => RandomSources.Source.NextDouble(); + public static void RandomBytes(Span buffer) => BuiltInRng.NextBytes(buffer); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RandomDouble() => BuiltInRng.NextDouble(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point3D RandomPointIn(Rectangle2D rect, Map map) diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index f887667bf..ed2a37ed1 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -413,7 +413,7 @@ public static class World } else { - logger.Warning($"Attempted to call World.AddEntity with '{entity.GetType()}'. Must be a mobile or item."); + logger.Warning("Attempted to call World.AddEntity with '{Entity}'. Must be a mobile or item.", entity.GetType()); } } diff --git a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs index 7f6152e95..f8fdbd978 100644 --- a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs @@ -22,9 +22,7 @@ namespace Server.Accounting.Security { public static IPasswordProtection Instance = new Argon2PasswordProtection(); - private readonly Argon2PasswordHasher m_PasswordHasher = new( - rng: (RandomSources.SecureSource as SecureRandom)?.Generator - ); + private readonly Argon2PasswordHasher m_PasswordHasher = new(rng: BuiltInSecureRng.Generator); public string EncryptPassword(string plainPassword) => m_PasswordHasher.Hash(plainPassword); diff --git a/Projects/UOContent/Commands/ResetRng.cs b/Projects/UOContent/Commands/ResetRng.cs index 7927a0a4b..4b2329694 100644 --- a/Projects/UOContent/Commands/ResetRng.cs +++ b/Projects/UOContent/Commands/ResetRng.cs @@ -15,6 +15,6 @@ public class ResetRng )] private static void ResetRng_OnCommand(CommandEventArgs e) { - RandomSources.SetRng(new Xoshiro256PlusPlus()); + BuiltInRng.Reset(); } } diff --git a/Projects/UOContent/Engines/Harvest/Core/HarvestBank.cs b/Projects/UOContent/Engines/Harvest/Core/HarvestBank.cs index 32957abc7..9d560005d 100644 --- a/Projects/UOContent/Engines/Harvest/Core/HarvestBank.cs +++ b/Projects/UOContent/Engines/Harvest/Core/HarvestBank.cs @@ -60,7 +60,7 @@ namespace Server.Engines.Harvest if (Definition.RandomizeVeins) { - m_DefaultVein = Definition.GetVeinFrom(Utility.Random(Definition.VeinWeights)); + m_DefaultVein = Definition.GetVeinFrom((uint)Utility.Random(Definition.VeinWeights)); } m_Vein = m_DefaultVein; diff --git a/Projects/UOContent/Engines/Harvest/Core/HarvestDefinition.cs b/Projects/UOContent/Engines/Harvest/Core/HarvestDefinition.cs index e3cc82a56..02a5b6248 100644 --- a/Projects/UOContent/Engines/Harvest/Core/HarvestDefinition.cs +++ b/Projects/UOContent/Engines/Harvest/Core/HarvestDefinition.cs @@ -135,12 +135,12 @@ namespace Server.Engines.Harvest if (RandomizeVeins) { - return GetVeinFrom(Utility.Random(1000u)); + return GetVeinFrom((uint)Utility.Random(1000)); } // TODO: Introduce pulling primes from a config and writing them if they don't exist to the config - var random = new Xoshiro256PlusPlus((ulong)(x * 17 + y * 11 + map.MapID * 3)); - return GetVeinFrom(random.Next(VeinWeights)); + var seed = (ulong)(x * 17 + y * 11 + map.MapID * 3); + return GetVeinFrom((uint)StableRandom.First(seed, VeinWeights)); } public HarvestVein GetVeinFrom(uint randomValue)