diff --git a/Projects/Scripts/Accounting/Accounts.cs b/Projects/Scripts/Accounting/Accounts.cs index 8bf7e5446..d5d01b165 100644 --- a/Projects/Scripts/Accounting/Accounts.cs +++ b/Projects/Scripts/Accounting/Accounts.cs @@ -90,4 +90,4 @@ namespace Server.Accounting xml.Close(); } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Gumps/ResurrectGump.cs b/Projects/Scripts/Gumps/ResurrectGump.cs index 71b3ecac6..0a13fc88d 100644 --- a/Projects/Scripts/Gumps/ResurrectGump.cs +++ b/Projects/Scripts/Gumps/ResurrectGump.cs @@ -182,7 +182,7 @@ namespace Server.Gumps { List items = new List( corpse.Items ); - for( int i = 0; i < items.Count; ++i ) + for ( int i = 0; i < items.Count; ++i ) { Item item = items[i]; @@ -215,7 +215,7 @@ namespace Server.Gumps if ( from.RawDex * loss > 10 ) from.RawDex = (int)(from.RawDex * loss); - for( int s = 0; s < from.Skills.Length; s++ ) + for ( int s = 0; s < from.Skills.Length; s++ ) if ( from.Skills[s].Base * loss > 35 ) from.Skills[s].Base *= loss; } diff --git a/Projects/Scripts/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs b/Projects/Scripts/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs index 699edc9e9..871f21c20 100644 --- a/Projects/Scripts/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs +++ b/Projects/Scripts/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs @@ -85,7 +85,7 @@ namespace Server.Engines.Events if ( DateTime.UtcNow <= HolidaySettings.FinishHalloween ) { - for( int index = 0; m_DeathQueue.Count > 0 && index < m_DeathQueue.Count; index++ ) + for ( int index = 0; m_DeathQueue.Count > 0 && index < m_DeathQueue.Count; index++ ) if ( !ReAnimated.ContainsKey( m_DeathQueue[ index ] ) ) { player = m_DeathQueue[ index ]; diff --git a/Projects/Scripts/Items/Weapons/BaseWeapon.cs b/Projects/Scripts/Items/Weapons/BaseWeapon.cs index c3a156618..eeb7197c6 100644 --- a/Projects/Scripts/Items/Weapons/BaseWeapon.cs +++ b/Projects/Scripts/Items/Weapons/BaseWeapon.cs @@ -1420,7 +1420,7 @@ namespace Server.Items } } - public virtual double GetAosDamage(Mobile attacker, int bonus, int dice, int sides) + public virtual double GetAosDamage(Mobile attacker, int bonus, uint dice, uint sides) { int damage = Utility.Dice(dice, sides, bonus) * 100; int damageBonus = 0; @@ -1449,7 +1449,7 @@ namespace Server.Items damage = AOS.Scale(damage, 100 + damageBonus); - return damage / 100; + return damage / 100.0; } public virtual CheckSlayerResult CheckSlayers(Mobile attacker, Mobile defender) diff --git a/Projects/Scripts/Spells/Base/Spell.cs b/Projects/Scripts/Spells/Base/Spell.cs index 035f8a3ec..b012411b8 100644 --- a/Projects/Scripts/Spells/Base/Spell.cs +++ b/Projects/Scripts/Spells/Base/Spell.cs @@ -150,7 +150,7 @@ namespace Server.Spells (m as BaseCreature)?.OnHarmfulSpell(Caster); } - public virtual int GetNewAosDamage(int bonus, int dice, int sides, Mobile singleTarget) + public virtual int GetNewAosDamage(int bonus, uint dice, uint sides, Mobile singleTarget) { if (singleTarget != null) return GetNewAosDamage(bonus, dice, sides, Caster.Player && singleTarget.Player, @@ -159,9 +159,9 @@ namespace Server.Spells return GetNewAosDamage(bonus, dice, sides, false); } - public virtual int GetNewAosDamage(int bonus, int dice, int sides, bool playerVsPlayer) => GetNewAosDamage(bonus, dice, sides, playerVsPlayer, 1.0); + public virtual int GetNewAosDamage(int bonus, uint dice, uint sides, bool playerVsPlayer) => GetNewAosDamage(bonus, dice, sides, playerVsPlayer, 1.0); - public virtual int GetNewAosDamage(int bonus, int dice, int sides, bool playerVsPlayer, double scalar) + public virtual int GetNewAosDamage(int bonus, uint dice, uint sides, bool playerVsPlayer, double scalar) { int damage = Utility.Dice(dice, sides, bonus) * 100; int damageBonus = 0; diff --git a/Projects/Scripts/Spells/Base/SpellHelper.cs b/Projects/Scripts/Spells/Base/SpellHelper.cs index a197a62c8..3283b17f1 100644 --- a/Projects/Scripts/Spells/Base/SpellHelper.cs +++ b/Projects/Scripts/Spells/Base/SpellHelper.cs @@ -498,7 +498,7 @@ namespace Server.Spells /* int offset = Utility.Random( 8 ) * 2; - for( int i = 0; i < m_Offsets.Length; i += 2 ) + for ( int i = 0; i < m_Offsets.Length; i += 2 ) { int x = caster.X + m_Offsets[(offset + i) % m_Offsets.Length]; int y = caster.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length]; diff --git a/Projects/Server/Assemblies/libdrng.dll b/Projects/Server/Assemblies/libdrng.dll new file mode 100644 index 000000000..1fa4d7477 Binary files /dev/null and b/Projects/Server/Assemblies/libdrng.dll differ diff --git a/Projects/Server/Assemblies/libuv.linux.so b/Projects/Server/Assemblies/libuv.linux.so deleted file mode 100755 index d9caeba12..000000000 Binary files a/Projects/Server/Assemblies/libuv.linux.so and /dev/null differ diff --git a/Projects/Server/Assemblies/libuv.osx.so b/Projects/Server/Assemblies/libuv.osx.so deleted file mode 100755 index 8b98042f1..000000000 Binary files a/Projects/Server/Assemblies/libuv.osx.so and /dev/null differ diff --git a/Projects/Server/Assemblies/rdrand.dll b/Projects/Server/Assemblies/rdrand.dll deleted file mode 100644 index 0c6723f7e..000000000 Binary files a/Projects/Server/Assemblies/rdrand.dll and /dev/null differ diff --git a/Projects/Server/Assemblies/rdrand.so b/Projects/Server/Assemblies/rdrand.so deleted file mode 100755 index 8205f7af2..000000000 Binary files a/Projects/Server/Assemblies/rdrand.so and /dev/null differ diff --git a/Projects/Server/Collections/ArraySet.cs b/Projects/Server/Collections/ArraySet.cs index bd044bd18..7a3eb5729 100644 --- a/Projects/Server/Collections/ArraySet.cs +++ b/Projects/Server/Collections/ArraySet.cs @@ -1,3 +1,23 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ArraySet.cs - Created: 2019/10/04 - Updated: 2019/12/30 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.Collections; using System.Collections.Generic; diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index bb1b29bc9..1f394a095 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -431,8 +431,8 @@ namespace Server Console.WriteLine("Core: Requested high resolution timing ({0})", UsingHighResolutionTiming ? "Supported" : "Unsupported"); - Console.WriteLine("RandomImpl: {0} ({1})", RandomImpl.Type.Name, - RandomImpl.IsHardwareRNG ? "Hardware" : "Software"); + Console.WriteLine("SecureRandomImpl: {0} ({1})", SecureRandomImpl.Name, + SecureRandomImpl.IsHardwareRNG ? "Hardware" : "Software"); // Load Assembly Scripts.CS.dll AssemblyHandler.LoadScripts(); diff --git a/Projects/Server/Network/SendQueue.cs b/Projects/Server/Network/SendQueue.cs deleted file mode 100644 index c074de17e..000000000 --- a/Projects/Server/Network/SendQueue.cs +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************** - * SendQueue.cs - * ------------------- - * begin : May 1, 2002 - * copyright : (C) The RunUO Software Team - * email : info@runuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * 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 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ - -using System.Collections.Concurrent; -using System.Threading.Tasks; - -namespace Server.Network -{ - public class SendQueue - { - private BlockingCollection m_Queue = new BlockingCollection(new ConcurrentQueue()); - - public void Enqueue(T t) - { - m_Queue.Add(t); - } - - public Task DequeueAsync() - { - TaskCompletionSource taskCompletion = new TaskCompletionSource(); - Task.Run(() => taskCompletion.SetResult(Dequeue())); - return taskCompletion.Task; - } - - public T Dequeue() => m_Queue.Take(); - } -} diff --git a/Projects/Server/Random.cs b/Projects/Server/Random.cs deleted file mode 100644 index 80409a56e..000000000 --- a/Projects/Server/Random.cs +++ /dev/null @@ -1,260 +0,0 @@ -/*************************************************************************** - * - * 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 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ - -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; - -namespace Server -{ - /// - /// Handles random number generation. - /// - public static class RandomImpl - { - private static readonly IRandomImpl _Random; - - static RandomImpl() - { - if (Core.Unix && File.Exists("rdrand.so")) - _Random = new RDRandUnix(); - else if (File.Exists("rdrand.dll")) - _Random = new RDRand64(); - - if (_Random == null || _Random is IHardwareRNG rng && !rng.IsSupported()) - _Random = new CSPRandom(); - } - - public static bool IsHardwareRNG => _Random is IHardwareRNG; - - public static Type Type => _Random.GetType(); - - public static int Next(int c) => _Random.Next(c); - - public static bool NextBool() => _Random.NextBool(); - - public static void NextBytes(Span b) => _Random.NextBytes(b); - - public static double NextDouble() => _Random.NextDouble(); - } - - public interface IRandomImpl - { - int Next(int c); - bool NextBool(); - void NextBytes(Span b); - double NextDouble(); - } - - public interface IHardwareRNG - { - bool IsSupported(); - } - - public abstract class BaseRandom : IRandomImpl - { - internal abstract void GetBytes(Span b); - internal abstract void GetBytes(byte[] b, int offset, int count); - - public virtual void NextBytes(Span b) => GetBytes(b); - public virtual int Next(int c) => (int)(c * NextDouble()); - public virtual bool NextBool() => (NextByte() & 1) == 1; - - public virtual byte NextByte() - { - byte[] b = new byte[1]; - GetBytes(b, 0, 1); - return b[0]; - } - - public virtual unsafe double NextDouble() - { - byte[] b = new byte[8]; - - if (BitConverter.IsLittleEndian) - { - b[7] = 0; - GetBytes(b, 0, 7); - } - else - { - b[0] = 0; - GetBytes(b, 1, 7); - } - - ulong r; - fixed (byte* buf = b) - { - r = *(ulong*)&buf[0] >> 3; - } - - /* double: 53 bits of significand precision - * ulong.MaxValue >> 11 = 9007199254740991 - * 2^53 = 9007199254740992 - */ - - return (double)r / 9007199254740992; - } - } - - public sealed class CSPRandom : BaseRandom - { - private static int BUFFER_SIZE = 0x4000; - private static int LARGE_REQUEST = 0x40; - private byte[] _Buffer = new byte[BUFFER_SIZE]; - private RNGCryptoServiceProvider _CSP = new RNGCryptoServiceProvider(); - - private ManualResetEvent _filled = new ManualResetEvent(false); - - private int _Index; - - private object _sync = new object(); - - private byte[] _Working = new byte[BUFFER_SIZE]; - - public CSPRandom() - { - _CSP.GetBytes(_Working); - Task.Run(Fill); - } - - public override void NextBytes(Span b) - { - int c = b.Length; - - if (c >= LARGE_REQUEST) - { - lock (_sync) - { - _CSP.GetBytes(b); - } - - return; - } - - GetBytes(b); - } - - private void CheckSwap(int c) - { - if (_Index + c < BUFFER_SIZE) - return; - - _filled.WaitOne(); - - byte[] b = _Working; - _Working = _Buffer; - _Buffer = b; - _Index = 0; - - _filled.Reset(); - - Task.Run(Fill); - } - - private void Fill() - { - _CSP.GetBytes(_Buffer); - _filled.Set(); - } - - internal override void GetBytes(Span b) - { - int c = b.Length; - - lock (_sync) - { - CheckSwap(c); - _Working.AsSpan(0, c).CopyTo(b); - _Index += c; - } - } - - internal override void GetBytes(byte[] b, int offset, int count) - { - GetBytes(b.AsSpan(offset, count)); - } - - public override byte NextByte() - { - lock (_sync) - { - CheckSwap(1); - return _Working[_Index++]; - } - } - } - - public sealed class RDRandUnix : BaseRandom, IHardwareRNG - { - [DllImport("rdrand.so")] - internal static extern RDRandError rdrand_32(ref uint rand, bool retry); - - [DllImport("rdrand.so")] - internal static extern unsafe RDRandError rdrand_get_bytes(int n, byte* buffer); - - public bool IsSupported() - { - uint r = 0; - return rdrand_32(ref r, true) == RDRandError.Success; - } - - internal override unsafe void GetBytes(Span b) - { - fixed(byte* ptr = b) - rdrand_get_bytes(b.Length, ptr); - } - - internal override void GetBytes(byte[] b, int offset, int count) - { - GetBytes(b.AsSpan().Slice(offset, count)); - } - } - - public sealed class RDRand64 : BaseRandom, IHardwareRNG - { - [DllImport("rdrand")] - internal static extern RDRandError rdrand_64(ref ulong rand, bool retry); - - [DllImport("rdrand")] - internal static extern unsafe RDRandError rdrand_get_bytes(int n, byte* buffer); - - public bool IsSupported() - { - ulong r = 0; - return rdrand_64(ref r, true) == RDRandError.Success; - } - - internal override unsafe void GetBytes(Span b) - { - fixed (byte* ptr = b) - rdrand_get_bytes(b.Length, ptr); - } - - internal override void GetBytes(byte[] b, int offset, int count) - { - GetBytes(b.AsSpan(offset, count)); - } - } - - public enum RDRandError - { - Unknown = -4, - Unsupported = -3, - Supported = -2, - NotReady = -1, - - Failure = 0, - - Success = 1 - } -} diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index 94457e180..7ce476529 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -43,27 +43,14 @@ libuv.dll Always - - libuv.so - Always - - - libuv.so - libuv.so - Always - zlib.dll Always - + rdrand.dll Always - - rdrand.so - Always - diff --git a/Projects/Server/Utilities/DRng64.cs b/Projects/Server/Utilities/DRng64.cs new file mode 100644 index 000000000..551dbe145 --- /dev/null +++ b/Projects/Server/Utilities/DRng64.cs @@ -0,0 +1,107 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: DRng64.cs - Created: 2019/12/30 - Updated: 2019/12/30 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace Server +{ + public sealed class DRng64 : RandomNumberGenerator, IHardwareRNG + { + public enum RDRandError + { + Unknown = -4, + Unsupported = -3, + Supported = -2, + NotReady = -1, + + Failure = 0, + + Success = 1 + } + + [DllImport("libdrng", CallingConvention = CallingConvention.Cdecl)] + internal static extern RDRandError rdrand_64(ref ulong rand, bool retry); + + [DllImport("libdrng", CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe RDRandError rdrand_get_bytes(int n, byte* buffer); + + public bool IsSupported() + { + ulong r = 0; + return rdrand_64(ref r, true) == RDRandError.Success; + } + + private static unsafe void GetBytes(byte* pBuffer, int count) + { + rdrand_get_bytes(count, pBuffer); + } + + public override void GetBytes(byte[] data) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + GetBytes(new Span(data)); + } + + public override void GetBytes(byte[] data, int offset, int count) + { + GetBytes(new Span(data, offset, count)); + } + + public override unsafe void GetBytes(Span data) + { + if (data.Length > 0) + fixed (byte* ptr = data) GetBytes(ptr, data.Length); + } + + public override void GetNonZeroBytes(byte[] data) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + GetNonZeroBytes(new Span(data)); + } + + public override void GetNonZeroBytes(Span data) + { + while (data.Length > 0) + { + // Fill the remaining portion of the span with random bytes. + GetBytes(data); + + // Find the first zero in the remaining portion. + int indexOfFirst0Byte = data.Length; + for (int i = 0; i < data.Length; i++) + if (data[i] == 0) + { + indexOfFirst0Byte = i; + break; + } + + // If there were any zeros, shift down all non-zeros. + for (int i = indexOfFirst0Byte + 1; i < data.Length; i++) + if (data[i] != 0) data[indexOfFirst0Byte++] = data[i]; + + // Request new random bytes if necessary; dont re-use + // existing bytes since they were shifted down. + data = data.Slice(indexOfFirst0Byte); + } + } + } +} diff --git a/Projects/Server/Network/SocketExtensions.cs b/Projects/Server/Utilities/Random.cs similarity index 51% rename from Projects/Server/Network/SocketExtensions.cs rename to Projects/Server/Utilities/Random.cs index 3fdf9d3b2..c0af61c83 100644 --- a/Projects/Server/Network/SocketExtensions.cs +++ b/Projects/Server/Utilities/Random.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: SocketExtensions.cs - Created: 2019/08/02 - Updated: 2019/12/24 * + * File: Random.cs - Created: 2019/12/30 - Updated: 2019/01/05 * * * * 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 * @@ -19,33 +19,53 @@ *************************************************************************/ using System; -using System.Buffers; -using System.Net.Sockets; -using System.Runtime.InteropServices; -using System.Threading.Tasks; +using System.Security.Cryptography; -namespace Server.Network +namespace Server { - public static class SocketExtensions + /// + /// Handles random number generation. + /// + public static class RandomImpl { - public static Task ReceiveAsync(this Socket socket, Memory memory, SocketFlags socketFlags) => SocketTaskExtensions.ReceiveAsync(socket, GetArray(memory), socketFlags); + private static readonly Xoshiro256PlusPlus _Random = new Xoshiro256PlusPlus(); - public static ArraySegment GetArray(this Memory memory) => ((ReadOnlyMemory)memory).GetArray(); + public static uint Next(uint c) => _Random.Next(c); - public static ArraySegment GetArray(this ReadOnlyMemory memory) + public static bool NextBool() => _Random.NextBool(); + + public static void GetBytes(Span b) => _Random.GetBytes(b); + + public static double NextDouble() => _Random.NextDouble(); + } + + public static class SecureRandomImpl + { + public static readonly RandomNumberGenerator _Random; + + static SecureRandomImpl() { - if (MemoryMarshal.TryGetArray(memory, out ArraySegment result)) - return result; - - throw new InvalidOperationException("Buffer backed by array was expected"); + try + { + _Random = new DRng64(); + if (_Random is IHardwareRNG rng && rng?.IsSupported() != true) + _Random = new RNGCryptoServiceProvider(); + } + catch (Exception) + { + _Random = new RNGCryptoServiceProvider(); + } } - public static ArraySegment GetArray(this ReadOnlySequence memory) - { - if (SequenceMarshal.TryGetArray(memory, out ArraySegment result)) - return result; + public static bool IsHardwareRNG => _Random is IHardwareRNG; - throw new InvalidOperationException("Buffer backed by array was expected"); - } + public static string Name => _Random.GetType().Name; + + public static void GetBytes(Span buffer) => _Random.GetBytes(buffer); + } + + public interface IHardwareRNG + { + bool IsSupported(); } } diff --git a/Projects/Server/Utility.cs b/Projects/Server/Utilities/Utility.cs similarity index 93% rename from Projects/Server/Utility.cs rename to Projects/Server/Utilities/Utility.cs index d4c4a1b70..52bd579ee 100644 --- a/Projects/Server/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -969,15 +969,15 @@ namespace Server #region Random //4d6+8 would be: Utility.Dice( 4, 6, 8 ) - public static int Dice(int numDice, int numSides, int bonus) + public static int Dice(uint numDice, uint numSides, int bonus) { int total = 0; + uint sides = numSides; for (int i = 0; i < numDice; ++i) - total += RandomImpl.Next(numSides) + 1; + total += (int)RandomImpl.Next(sides) + 1; - total += bonus; - return total; + return total + bonus; } public static void Shuffle(IList list) @@ -985,7 +985,7 @@ namespace Server int count = list.Count; for (int i = count - 1; i > 0; i--) { - int r = RandomImpl.Next(count); + int r = (int)RandomImpl.Next((uint)count); T swap = list[r]; list[r] = list[i]; list[i] = swap; @@ -994,7 +994,7 @@ namespace Server public static int RandomList(params int[] list) => RandomList(list); - public static T RandomList(IList list) => list[RandomImpl.Next(list.Count)]; + public static T RandomList(IList list) => list[Random(list.Count)]; public static bool RandomBool() => RandomImpl.NextBool(); @@ -1007,27 +1007,27 @@ namespace Server max = copy; } else if (min == max) - { return min; - } - return min + RandomImpl.Next(max - min + 1); + return min + (int)RandomImpl.Next((uint)(max - min + 1)); } public static int Random(int from, int count) { if (count == 0) return from; - if (count > 0) return from + RandomImpl.Next(count); - return from - RandomImpl.Next(-count); + if (count > 0) return (int)(from + RandomImpl.Next((uint)count)); + + return (int)(from - RandomImpl.Next((uint)-count)); } - public static int Random(int count) => RandomImpl.Next(count); + public static int Random(int count) => (int)RandomImpl.Next((uint)count); - public static void RandomBytes(byte[] buffer) - { - RandomImpl.NextBytes(buffer); - } + public static int Random(uint count) => (int)RandomImpl.Next(count); + + public static void RandomBytes(byte[] buffer) => RandomImpl.GetBytes(buffer); + + public static void RandomBytes(Span buffer) => RandomImpl.GetBytes(buffer); public static double RandomDouble() => RandomImpl.NextDouble(); diff --git a/Projects/Server/Utilities/Xoshiro256PlusPlus.cs b/Projects/Server/Utilities/Xoshiro256PlusPlus.cs new file mode 100644 index 000000000..e436fe314 --- /dev/null +++ b/Projects/Server/Utilities/Xoshiro256PlusPlus.cs @@ -0,0 +1,265 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Xoshiro256PlusPlus.cs * + * Created: 2019/12/29 - Updated: 2019/12/30 * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +#nullable enable + +using System; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; + +namespace Server +{ + public class Xoshiro256PlusPlus : RandomNumberGenerator + { + private ulong _s0, _s1, _s2, _s3; + + public Xoshiro256PlusPlus() : this((ulong)Environment.TickCount64) + { + } + + public Xoshiro256PlusPlus(ulong seed) + { + var mix = new SplitMix64(seed); + ulong[] state = new 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 uint Next(uint max) + { + if (max <= 1u << 12) + { + if (max == 0) throw new ArgumentOutOfRangeException(); + return (uint)(((ulong)NextUInt32() * max) >> 32); + } + + uint r, v, limit = (uint)-(int)max; + do + { + r = NextUInt32(); + v = r % max; + } while (r - v > limit); + + return v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint NextUInt32() => (uint)NextUInt64(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong NextUInt64() + { + ulong r1 = (_s1 << 2) + _s1; + ulong r2 = (r1 << 7) | (r1 >> 57); + ulong rslt = (r2 << 3) + r2; + + ulong t = _s1 << 17; + + _s2 ^= _s0; + _s3 ^= _s1; + _s1 ^= _s2; + _s0 ^= _s3; + + _s2 ^= t; + + _s3 = (_s3 << 45) | (_s3 >> 19); + + return rslt; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong Next(ulong max) + { + if (max <= uint.MaxValue) return Next((uint)max); + + if (max <= 1ul << 38) return ((NextUInt32() * max >> 32) + (NextUInt32() & ((1u << 26) - 1)) * max) >> 26; + + ulong r, v, limit = (ulong)-(long)max; + do + { + r = NextUInt64(); + v = r % max; + } while (r - v > limit); + + return v; + } + + public bool NextBool() => NextUInt64() < 1ul << 63; + + public override void GetBytes(byte[] data) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + GetBytes(new Span(data)); + } + + public override unsafe void GetBytes(Span b) + { + if (b.Length == 0) return; + + ulong s0 = _s0; + ulong s1 = _s1; + ulong s2 = _s2; + ulong s3 = _s3; + + int i = 0; + + fixed (byte* pBuffer = b) + { + ulong* pULong = (ulong*)pBuffer; + + for (int bound = b.Length / sizeof(ulong); i < bound; i++) + { + ulong r1 = (s1 << 2) + s1; + ulong r2 = (r1 << 7) | (r1 >> 57); + pULong[i] = (r2 << 3) + r2; + + ulong t = s1 << 17; + s2 ^= s0; + s3 ^= s1; + s1 ^= s2; + s0 ^= s3; + + s2 ^= t; + + s3 = (s3 << 45) | (s3 >> 19); + } + } + + i *= 8; + + if (i < b.Length) + { + ulong r1 = (s1 << 2) + s1; + ulong r2 = (r1 << 7) | (r1 >> 57); + ulong rslt = (r2 << 3) + r2; + + ulong 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 double NextDouble() => (NextUInt64() >> 11) * (1.0 / (1ul << 53)); + + private static readonly ulong[] JUMP = + {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c}; + + private static readonly ulong[] LONG_JUMP = + {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635}; + + 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 (int i = 0; i < jumps.Length; i++) + for (int b = 0; b < 64; b++) + { + if ((jumps[i] & 1ul << b) != 0) + { + s0 ^= _s0; + s1 ^= _s1; + s2 ^= _s2; + s3 ^= _s3; + } + + NextUInt64(); + } + + _s0 = s0; + _s1 = s1; + _s2 = s2; + _s3 = s3; + } + + public Xoshiro256PlusPlus Split() + { + Xoshiro256PlusPlus rng = new Xoshiro256PlusPlus(_s0, _s1, _s2, _s3); + rng.Jump(); + return rng; + } + + public Xoshiro256PlusPlus LongSplit() + { + Xoshiro256PlusPlus 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() + { + ulong z = x += 0x9e3779b97f4a7c15; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FillArray(ulong[] arr) + { + for (int i = 0; i < arr.Length; i++) arr[i] = Next(); + } + } +} diff --git a/README.md b/README.md index ec073ff19..d8812a82f 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,24 @@ ModernUO Ultima Online Server Emulator for the modern era! ### Join and Follow! -[Discord Channel](https://discord.gg/VdyCpjQ) -[Twitter](https://www.twitter.com/modernuo) -[Reddit](https://www.reddit.com/r/modernuo) +[Discord Channel](https://discord.gg/VdyCpjQ) +[Twitter](https://www.twitter.com/modernuo) +[Reddit](https://www.reddit.com/r/modernuo) ### Goals - See [Goals](./GOALS.md) # Requirements to Compile - [.NET Core 3.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.0) -- [System.IO.Pipelines 4.6.0](https://www.nuget.org/packages/System.IO.Pipelines/) (Restored automatically during publishing) ### Requirements to Run -- zlib (Linux only) + +#### Linux +- `apt get zlib libuv` or equiv for that distribution +- Optional: compile and install [Intel DRNG](https://github.com/modernuo/libdrng) +#### OSX +- `brew install zlib libuv` +- Optional: compile and install [Intel DRNG](https://github.com/modernuo/libdrng) ### Building with .NET Core 3.0 SDK `dotnet publish /p:PublishProfile=[platform][-SelfContained]`