diff --git a/Server/Random.cs b/Server/Random.cs
new file mode 100644
index 000000000..812aad1e4
--- /dev/null
+++ b/Server/Random.cs
@@ -0,0 +1,332 @@
+/***************************************************************************
+ *
+ * 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;
+
+namespace Server {
+ ///
+ /// Handles random number generation.
+ ///
+ public static class RandomImpl {
+ private static readonly IRandomImpl _Random;
+
+ static RandomImpl() {
+ if ( Core.Unix ) {
+ _Random = new SimpleRandom();
+ } else if (Core.Is64Bit && File.Exists("rdrand64.dll")) {
+ _Random = new RDRand64();
+ } else if ( !Core.Is64Bit && File.Exists("rdrand32.dll") ) {
+ _Random = new RDRand32();
+ } else {
+ _Random = new CSPRandom();
+ }
+
+ if (_Random is IHardwareRNG) {
+ if (!((IHardwareRNG)_Random).IsSupported()) {
+ _Random = new CSPRandom();
+ }
+ }
+ }
+
+ public static double NextDouble() {
+ return _Random.NextDouble();
+ }
+
+ public static int Next(int c) {
+ return _Random.Next(c);
+ }
+
+ public static void NextBytes(byte[] b) {
+ _Random.NextBytes(b);
+ }
+ }
+
+ public interface IRandomImpl {
+ double NextDouble();
+ int Next(int c);
+ void NextBytes(byte[] b);
+ }
+
+ public interface IHardwareRNG {
+ bool IsSupported();
+ }
+
+ public sealed class SimpleRandom : IRandomImpl {
+ private Random m_Random = new Random();
+
+ public SimpleRandom() {
+ }
+
+ public double NextDouble() {
+ double r;
+ lock (m_Random)
+ r = m_Random.NextDouble();
+ return r;
+ }
+
+ public int Next(int c) {
+ int r;
+ lock (m_Random)
+ r = m_Random.Next(c);
+ return r;
+ }
+
+ public void NextBytes(byte[] b) {
+ lock (m_Random)
+ m_Random.NextBytes(b);
+ }
+ }
+
+ public sealed class CSPRandom : IRandomImpl {
+ private RNGCryptoServiceProvider _CSP = new RNGCryptoServiceProvider();
+
+ private static int BUFFER_SIZE = 0x4000;
+ private static int LARGE_REQUEST = BUFFER_SIZE / 0x100;
+
+ private byte[] _Working = new byte[BUFFER_SIZE];
+ private byte[] _Buffer = new byte[BUFFER_SIZE];
+
+ private int _Index = 0;
+
+ private object _sync = new object();
+
+ public CSPRandom() {
+ _CSP.GetBytes(_Working);
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
+ }
+
+ private void CheckSwap(int c) {
+ lock (_sync) {
+ if (_Index + c < BUFFER_SIZE)
+ return;
+
+ lock (_Buffer) {
+ byte[] b = _Working;
+ _Working = _Buffer;
+ _Buffer = b;
+ _Index = 0;
+ }
+ }
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
+ }
+
+ private void Fill(object o) {
+ lock (_Buffer)
+ lock (_CSP)
+ _CSP.GetBytes(_Buffer);
+ }
+
+ private void _GetBytes(byte[] b) {
+ int c = b.Length;
+
+ CheckSwap(c);
+
+ lock (_sync) {
+ Buffer.BlockCopy(_Working, _Index, b, 0, c);
+ _Index += c;
+ }
+ }
+
+ public double NextDouble() {
+ byte[] b = new byte[8];
+
+ _GetBytes(b);
+
+ return (double)BitConverter.ToUInt64(b, 0) / ulong.MaxValue;
+ }
+
+ public int Next(int c) {
+ return (int)(c * NextDouble());
+ }
+
+ public void NextBytes(byte[] b) {
+ int c = b.Length;
+
+ if (c >= LARGE_REQUEST) {
+ lock (_CSP)
+ _CSP.GetBytes(b);
+ return;
+ }
+ _GetBytes(b);
+ }
+ }
+
+ public sealed class RDRand32 : IRandomImpl, IHardwareRNG {
+ [DllImport("rdrand32")]
+ private static extern RDRandError rdrand_32(ref uint rand, bool retry);
+
+ [DllImport("rdrand32")]
+ private static extern RDRandError rdrand_get_bytes(int n, byte[] buffer);
+
+ private static int BUFFER_SIZE = 0x4000;
+ private static int LARGE_REQUEST = BUFFER_SIZE / 0x100;
+
+ private byte[] _Working = new byte[BUFFER_SIZE];
+ private byte[] _Buffer = new byte[BUFFER_SIZE];
+
+ private int _Index = 0;
+
+ private object _sync = new object();
+
+ public RDRand32() {
+ rdrand_get_bytes(BUFFER_SIZE, _Working);
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
+ }
+
+ public bool IsSupported() {
+ uint r = 0;
+ return rdrand_32(ref r, true) == RDRandError.Success;
+ }
+
+ private void CheckSwap(int c) {
+ lock (_sync) {
+ if (_Index + c < BUFFER_SIZE)
+ return;
+
+ lock (_Buffer) {
+ byte[] b = _Working;
+ _Working = _Buffer;
+ _Buffer = b;
+ _Index = 0;
+ }
+ }
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
+ }
+
+ private void Fill(object o) {
+ lock (_Buffer)
+ rdrand_get_bytes(BUFFER_SIZE, _Buffer);
+ }
+
+ private void _GetBytes(byte[] b) {
+ int c = b.Length;
+
+ CheckSwap(c);
+
+ lock (_sync) {
+ Buffer.BlockCopy(_Working, _Index, b, 0, c);
+ _Index += c;
+ }
+ }
+
+ public double NextDouble() {
+ byte[] b = new byte[8];
+ _GetBytes(b);
+ return (double)BitConverter.ToUInt64(b, 0) / ulong.MaxValue;
+ }
+
+ public int Next(int c) {
+ return (int)(c * NextDouble());
+ }
+
+ public void NextBytes(byte[] b) {
+ int c = b.Length;
+
+ if (c >= LARGE_REQUEST) {
+ rdrand_get_bytes(c, b);
+ return;
+ }
+ _GetBytes(b);
+ }
+ }
+
+ public sealed class RDRand64 : IRandomImpl, IHardwareRNG {
+ [DllImport("rdrand64")]
+ private static extern RDRandError rdrand_64(ref ulong rand, bool retry);
+
+ [DllImport("rdrand64")]
+ private static extern RDRandError rdrand_get_bytes(int n, byte[] buffer);
+
+ private static int BUFFER_SIZE = 0x4000;
+ private static int LARGE_REQUEST = BUFFER_SIZE / 0x100;
+
+ private byte[] _Working = new byte[BUFFER_SIZE];
+ private byte[] _Buffer = new byte[BUFFER_SIZE];
+
+ private int _Index = 0;
+
+ private object _sync = new object();
+
+ public RDRand64() {
+ rdrand_get_bytes(BUFFER_SIZE, _Working);
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
+ }
+
+ public bool IsSupported() {
+ ulong r = 0;
+ return rdrand_64(ref r, true) == RDRandError.Success;
+ }
+
+ private void CheckSwap(int c) {
+ lock (_sync) {
+ if (_Index + c < BUFFER_SIZE)
+ return;
+
+ lock (_Buffer) {
+ byte[] b = _Working;
+ _Working = _Buffer;
+ _Buffer = b;
+ _Index = 0;
+ }
+ }
+ ThreadPool.QueueUserWorkItem(new WaitCallback(Fill));
+ }
+
+ private void Fill(object o) {
+ lock (_Buffer)
+ rdrand_get_bytes(BUFFER_SIZE, _Buffer);
+ }
+
+ private void _GetBytes(byte[] b) {
+ int c = b.Length;
+
+ CheckSwap(c);
+
+ lock (_sync) {
+ Buffer.BlockCopy(_Working, _Index, b, 0, c);
+ _Index += c;
+ }
+ }
+
+ public double NextDouble() {
+ byte[] b = new byte[8];
+ _GetBytes(b);
+ return (double)BitConverter.ToUInt64(b, 0) / ulong.MaxValue;
+ }
+
+ public int Next(int c) {
+ return (int)(c * NextDouble());
+ }
+
+ public void NextBytes(byte[] b) {
+ int c = b.Length;
+
+ if (c >= LARGE_REQUEST) {
+ rdrand_get_bytes(c, b);
+ return;
+ }
+ _GetBytes(b);
+ }
+ }
+
+ public enum RDRandError : int {
+ Unknown = -4,
+ Unsupported = -3,
+ Supported = -2,
+ NotReady = -1,
+
+ Failure = 0,
+
+ Success = 1,
+ }
+}
\ No newline at end of file
diff --git a/Server/Server.csproj b/Server/Server.csproj
index a1883c89e..cf58d8dc8 100644
--- a/Server/Server.csproj
+++ b/Server/Server.csproj
@@ -150,6 +150,7 @@
+
diff --git a/Server/Utility.cs b/Server/Utility.cs
index 74503ddad..977b2926f 100644
--- a/Server/Utility.cs
+++ b/Server/Utility.cs
@@ -35,8 +35,6 @@ namespace Server
{
public static class Utility
{
- // TODO: ThreadLocal for .NET 4.0?
- private static Random m_Random = new Random();
private static Encoding m_UTF8, m_UTF8WithEncoding;
public static Encoding UTF8
@@ -777,11 +775,8 @@ namespace Server
{
int total = 0;
- lock (m_Random)
- {
- for (int i = 0; i < numDice; ++i)
- total += m_Random.Next(numSides) + 1;
- }
+ for (int i = 0; i < numDice; ++i)
+ total += RandomImpl.Next(numSides) + 1;
total += bonus;
return total;
@@ -789,18 +784,12 @@ namespace Server
public static int RandomList( params int[] list )
{
- int r;
- lock (m_Random)
- r = m_Random.Next( list.Length );
- return list[r];
+ return list[RandomImpl.Next(list.Length)];
}
public static bool RandomBool()
{
- bool r;
- lock (m_Random)
- r = ( m_Random.Next( 2 ) == 0 );
- return r;
+ return (RandomImpl.Next(2) == 0);
}
public static int RandomMinMax( int min, int max )
@@ -816,11 +805,7 @@ namespace Server
return min;
}
- int r;
- lock (m_Random)
- r = m_Random.Next( (max - min) + 1 );
-
- return min + r;
+ return min + RandomImpl.Next((max - min) + 1);
}
public static int Random( int from, int count )
@@ -831,40 +816,27 @@ namespace Server
}
else if ( count > 0 )
{
- int r;
- lock (m_Random)
- r = m_Random.Next( count );
- return from + r;
+ return from + RandomImpl.Next(count);
}
else
{
- int r;
- lock (m_Random)
- r = m_Random.Next( -count );
- return from - r;
+ return from - RandomImpl.Next(-count);
}
}
public static int Random( int count )
{
- int r;
- lock (m_Random)
- r = m_Random.Next( count );
- return r;
+ return RandomImpl.Next(count);
}
public static void RandomBytes( byte[] buffer )
{
- lock (m_Random)
- m_Random.NextBytes(buffer);
+ RandomImpl.NextBytes(buffer);
}
public static double RandomDouble()
{
- double r;
- lock (m_Random)
- r = m_Random.NextDouble();
- return r;
+ return RandomImpl.NextDouble();
}
#endregion