diff --git a/Lidgren.Network/Lidgren.Network.csproj b/Lidgren.Network/Lidgren.Network.csproj
index 55da57b..7bff054 100644
--- a/Lidgren.Network/Lidgren.Network.csproj
+++ b/Lidgren.Network/Lidgren.Network.csproj
@@ -85,7 +85,6 @@
-
@@ -104,6 +103,8 @@
+
+
diff --git a/Lidgren.Network/NetConnection.Latency.cs b/Lidgren.Network/NetConnection.Latency.cs
index bbcfe37..2f6f825 100644
--- a/Lidgren.Network/NetConnection.Latency.cs
+++ b/Lidgren.Network/NetConnection.Latency.cs
@@ -51,7 +51,7 @@ namespace Lidgren.Network
// randomize ping sent time (0.25 - 1.0 x ping interval)
m_sentPingTime = now;
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while
- m_sentPingTime -= (NetRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
+ m_sentPingTime -= (MWCRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0f); // initially allow a little more time
// make it better, quick :-)
diff --git a/Lidgren.Network/NetHash.cs b/Lidgren.Network/NetHash.cs
deleted file mode 100644
index 3cb3708..0000000
--- a/Lidgren.Network/NetHash.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-
-namespace Lidgren.Network
-{
- ///
- /// Murmur2 hash code
- ///
- public static class NetHash
- {
- ///
- /// Hash values into a single UInt32
- ///
- [CLSCompliant(false)]
- public static uint Hash(params int[] data)
- {
- unchecked
- {
- const uint m = 0x5bd1e995;
- const int r = 24;
-
- UInt32 h = 0xc58f1a7b ^ (uint)data.Length;
- for (int i = 0; i < data.Length; i++)
- {
- var k = (uint)data[i] * m;
- k ^= k >> r; k *= m;
- h *= m; h ^= k;
- }
-
- // final mix
- h ^= h >> 13; h *= m; h ^= h >> 15;
-
- return h;
- }
- }
- }
-}
diff --git a/Lidgren.Network/NetPeer.Internal.cs b/Lidgren.Network/NetPeer.Internal.cs
index a176d85..ec90677 100644
--- a/Lidgren.Network/NetPeer.Internal.cs
+++ b/Lidgren.Network/NetPeer.Internal.cs
@@ -154,7 +154,7 @@ namespace Lidgren.Network
m_readHelperMessage.m_data = m_receiveBuffer;
byte[] macBytes = new byte[8];
- NetRandom.Instance.NextBytes(macBytes);
+ MWCRandom.Instance.NextBytes(macBytes);
#if IS_MAC_AVAILABLE
try
diff --git a/Lidgren.Network/NetPeer.LatencySimulation.cs b/Lidgren.Network/NetPeer.LatencySimulation.cs
index 1f9760f..6e90b71 100644
--- a/Lidgren.Network/NetPeer.LatencySimulation.cs
+++ b/Lidgren.Network/NetPeer.LatencySimulation.cs
@@ -48,7 +48,7 @@ namespace Lidgren.Network
float loss = m_configuration.m_loss;
if (loss > 0.0f)
{
- if ((float)NetRandom.Instance.NextDouble() < loss)
+ if ((float)MWCRandom.Instance.NextDouble() < loss)
{
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
return; // packet "lost"
@@ -67,20 +67,20 @@ namespace Lidgren.Network
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
// TODO: handle wasSent == false?
- if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
+ if (m_configuration.m_duplicates > 0.0f && MWCRandom.Instance.NextDouble() < m_configuration.m_duplicates)
ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset); // send it again!
return;
}
int num = 1;
- if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
+ if (m_configuration.m_duplicates > 0.0f && MWCRandom.Instance.NextSingle() < m_configuration.m_duplicates)
num++;
float delay = 0;
for (int i = 0; i < num; i++)
{
- delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
+ delay = m_configuration.m_minimumOneWayLatency + (MWCRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
// Enqueue delayed packet
DelayedPacket p = new DelayedPacket();
diff --git a/Lidgren.Network/NetRandom.Implementations.cs b/Lidgren.Network/NetRandom.Implementations.cs
new file mode 100644
index 0000000..5c42957
--- /dev/null
+++ b/Lidgren.Network/NetRandom.Implementations.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Security.Cryptography;
+
+namespace Lidgren.Network
+{
+ ///
+ /// Multiply With Carry random
+ ///
+ public class MWCRandom : NetRandom
+ {
+ public static readonly MWCRandom Instance = new MWCRandom();
+
+ private uint m_w, m_z;
+
+ public MWCRandom()
+ {
+ Initialize(NetRandomSeed.GetUInt64());
+ }
+
+ public override void Initialize(uint seed)
+ {
+ m_w = seed;
+ m_z = seed * 16777619;
+ }
+
+ public void Initialize(ulong seed)
+ {
+ m_w = (uint)seed;
+ m_z = (uint)(seed >> 32);
+ }
+
+ public override uint NextUInt32()
+ {
+ m_z = 36969 * (m_z & 65535) + (m_z >> 16);
+ m_w = 18000 * (m_w & 65535) + (m_w >> 16);
+ return ((m_z << 16) + m_w);
+ }
+ }
+
+ ///
+ /// Xor Shift based random
+ ///
+ public sealed class XorShiftRandom : NetRandom
+ {
+ public static readonly XorShiftRandom Instance = new XorShiftRandom();
+
+ private const uint c_x = 123456789;
+ private const uint c_y = 362436069;
+ private const uint c_z = 521288629;
+ private const uint c_w = 88675123;
+
+ private uint m_x, m_y, m_z, m_w;
+
+ public XorShiftRandom()
+ {
+ Initialize(NetRandomSeed.GetUInt64());
+ }
+
+ public XorShiftRandom(ulong seed)
+ {
+ Initialize(seed);
+ }
+
+ public override void Initialize(uint seed)
+ {
+ m_x = (uint)seed;
+ m_y = c_y;
+ m_z = c_z;
+ m_w = c_w;
+ }
+
+ public void Initialize(ulong seed)
+ {
+ m_x = (uint)seed;
+ m_y = c_y;
+ m_z = (uint)(seed << 32);
+ m_w = c_w;
+ }
+
+ public override uint NextUInt32()
+ {
+ uint t = (m_x ^ (m_x << 11));
+ m_x = m_y; m_y = m_z; m_z = m_w;
+ return (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)));
+ }
+ }
+
+ ///
+ /// Mersenne Twister based random
+ ///
+ public sealed class MersenneTwisterRandom : NetRandom
+ {
+ public static readonly MersenneTwisterRandom Instance = new MersenneTwisterRandom();
+
+ private const int N = 624;
+ private const int M = 397;
+ private const uint MATRIX_A = 0x9908b0dfU;
+ private const uint UPPER_MASK = 0x80000000U;
+ private const uint LOWER_MASK = 0x7fffffffU;
+ private const uint TEMPER1 = 0x9d2c5680U;
+ private const uint TEMPER2 = 0xefc60000U;
+ private const int TEMPER3 = 11;
+ private const int TEMPER4 = 7;
+ private const int TEMPER5 = 15;
+ private const int TEMPER6 = 18;
+
+ private UInt32[] mt;
+ private int mti;
+ private UInt32[] mag01;
+
+ private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
+
+ public MersenneTwisterRandom()
+ {
+ Initialize(NetRandomSeed.GetUInt32());
+ }
+
+ public MersenneTwisterRandom(uint seed)
+ {
+ Initialize(seed);
+ }
+
+ public override void Initialize(uint seed)
+ {
+ mt = new UInt32[N];
+ mti = N + 1;
+ mag01 = new UInt32[] { 0x0U, MATRIX_A };
+ mt[0] = seed;
+ for (int i = 1; i < N; i++)
+ mt[i] = (UInt32)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
+ }
+
+ public override uint NextUInt32()
+ {
+ UInt32 y;
+ if (mti >= N)
+ {
+ GenRandAll();
+ mti = 0;
+ }
+ y = mt[mti++];
+ y ^= (y >> TEMPER3);
+ y ^= (y << TEMPER4) & TEMPER1;
+ y ^= (y << TEMPER5) & TEMPER2;
+ y ^= (y >> TEMPER6);
+ return y;
+ }
+
+ private void GenRandAll()
+ {
+ int kk = 1;
+ UInt32 y;
+ UInt32 p;
+ y = mt[0] & UPPER_MASK;
+ do
+ {
+ p = mt[kk];
+ mt[kk - 1] = mt[kk + (M - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
+ y = p & UPPER_MASK;
+ } while (++kk < N - M + 1);
+ do
+ {
+ p = mt[kk];
+ mt[kk - 1] = mt[kk + (M - N - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
+ y = p & UPPER_MASK;
+ } while (++kk < N);
+ p = mt[0];
+ mt[N - 1] = mt[M - 1] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
+ }
+ }
+
+ ///
+ /// RNGCryptoServiceProvider based random; very slow but cryptographically safe
+ ///
+ public class CryptoRandom : NetRandom
+ {
+ public static readonly CryptoRandom Instance = new CryptoRandom();
+
+ private RandomNumberGenerator m_rnd = new RNGCryptoServiceProvider();
+
+ ///
+ /// Seed in CryptoRandom does not create deterministic sequences
+ ///
+ public override void Initialize(uint seed)
+ {
+ byte[] tmp = new byte[seed % 16];
+ m_rnd.GetBytes(tmp); // just prime it
+ }
+
+ public override uint NextUInt32()
+ {
+ var bytes = new byte[4];
+ m_rnd.GetBytes(bytes);
+ return (uint)bytes[0] | (((uint)bytes[1]) << 8) | (((uint)bytes[2]) << 16) | (((uint)bytes[3]) << 24);
+ }
+
+ public override void NextBytes(byte[] buffer)
+ {
+ m_rnd.GetBytes(buffer);
+ }
+
+ public override void NextBytes(byte[] buffer, int offset, int length)
+ {
+ var bytes = new byte[length];
+ m_rnd.GetBytes(bytes);
+ Array.Copy(bytes, 0, buffer, offset, length);
+ }
+ }
+}
diff --git a/Lidgren.Network/NetRandom.cs b/Lidgren.Network/NetRandom.cs
index a20f349..c14e19f 100644
--- a/Lidgren.Network/NetRandom.cs
+++ b/Lidgren.Network/NetRandom.cs
@@ -5,111 +5,125 @@ using System.Threading;
namespace Lidgren.Network
{
///
- /// Mersenne Twister PRNG
+ /// NetRandom base class
///
- public sealed class NetRandom
+ public abstract class NetRandom : Random
{
- ///
- /// Gets a global NetRandom instance
- ///
- public static readonly NetRandom Instance = new NetRandom();
+ private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
- private const double c_uniformSingleMultiplier = 1.0 / ((double)uint.MaxValue + 1.0);
-
- private static int m_seedIncrement = 997;
-
- private const int N = 624;
- private const int M = 397;
- private const uint MATRIX_A = 0x9908b0dfU;
- private const uint UPPER_MASK = 0x80000000U;
- private const uint LOWER_MASK = 0x7fffffffU;
- private const uint TEMPER1 = 0x9d2c5680U;
- private const uint TEMPER2 = 0xefc60000U;
- private const int TEMPER3 = 11;
- private const int TEMPER4 = 7;
- private const int TEMPER5 = 15;
- private const int TEMPER6 = 18;
-
- private UInt32[] mt;
- private int mti;
- private UInt32[] mag01;
-
- ///
- /// Constructor
- ///
public NetRandom()
{
- // make seed from various numbers
- uint seed = NetHash.Hash(
- (int)Environment.TickCount,
- Guid.NewGuid().GetHashCode(),
- this.GetHashCode(),
- m_seedIncrement
- // can't use Environment.WorkingSet or Stopwatch.GetTimestamp here since it's not available or reliable on all platforms
- );
+ Initialize(NetRandomSeed.GetUInt32());
+ }
- mt = new UInt32[N];
- mti = N + 1;
- mag01 = new UInt32[] { 0x0U, MATRIX_A };
- mt[0] = seed;
- for (int i = 1; i < N; i++)
- mt[i] = (UInt32)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
+ public NetRandom(int seed)
+ {
+ Initialize((uint)seed);
+ }
+
+ public abstract void Initialize(uint seed);
+
+ ///
+ /// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
+ ///
+ public abstract uint NextUInt32();
+
+ ///
+ /// Generates a random value that is >= 0 and < Int32.MaxValue
+ ///
+ public override int Next()
+ {
+ var retval = (int)(0x7FFFFFFF & NextUInt32());
+ if (retval == 0x7FFFFFFF)
+ return NextInt32();
+ return retval;
}
///
- /// Generates a random value from Int32.MinValue to Int32.MaxValue
+ /// Generates a random value >= 0 and <= Int32.MaxValue (inclusively)
///
- [CLSCompliant(false)]
- public uint NextUInt32()
+ public int NextInt32()
{
- UInt32 y;
- if (mti >= N)
+ return (int)(0x7FFFFFFF & NextUInt32());
+ }
+
+ ///
+ /// Returns random value >= 0.0 and < 1.0
+ ///
+ public override double NextDouble()
+ {
+ return c_realUnitInt * NextInt32();
+ }
+
+ ///
+ /// Returns random value >= 0.0 and < 1.0
+ ///
+ protected override double Sample()
+ {
+ return c_realUnitInt * NextInt32();
+ }
+
+ ///
+ /// Returns random value >= 0.0f and < 1.0f
+ ///
+ public float NextSingle()
+ {
+ var retval = (float)(c_realUnitInt * NextInt32());
+ if (retval == 1.0f)
+ return NextSingle();
+ return retval;
+ }
+
+ ///
+ /// Returns a random value >= 0 and < maxValue
+ ///
+ public override int Next(int maxValue)
+ {
+ return (int)(NextDouble() * maxValue);
+ }
+
+ ///
+ /// Returns a random value >= minValue and < maxValue
+ ///
+ public override int Next(int minValue, int maxValue)
+ {
+ return minValue + (int)(NextDouble() * (double)(maxValue - minValue));
+ }
+
+ ///
+ /// Generates a random value between UInt64.MinValue to UInt64.MaxValue
+ ///
+ public ulong NextUInt64()
+ {
+ ulong retval = NextUInt32();
+ retval |= NextUInt32() << 32;
+ return retval;
+ }
+
+ private uint m_boolValues;
+ private int m_nextBoolIndex;
+
+ ///
+ /// Returns true or false, randomly
+ ///
+ public bool NextBool()
+ {
+ if (m_nextBoolIndex >= 32)
{
- GenRandAll();
- mti = 0;
+ m_boolValues = NextUInt32();
+ m_nextBoolIndex = 1;
}
- y = mt[mti++];
- y ^= (y >> TEMPER3);
- y ^= (y << TEMPER4) & TEMPER1;
- y ^= (y << TEMPER5) & TEMPER2;
- y ^= (y >> TEMPER6);
- return y;
- }
- private void GenRandAll()
- {
- int kk = 1;
- UInt32 y;
- UInt32 p;
- y = mt[0] & UPPER_MASK;
- do
- {
- p = mt[kk];
- mt[kk - 1] = mt[kk + (M - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
- y = p & UPPER_MASK;
- } while (++kk < N - M + 1);
- do
- {
- p = mt[kk];
- mt[kk - 1] = mt[kk + (M - N - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
- y = p & UPPER_MASK;
- } while (++kk < N);
- p = mt[0];
- mt[N - 1] = mt[M - 1] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
- }
-
- ///
- /// Fills all bytes in the provided buffer with random values
- ///
- public void NextBytes(byte[] buffer)
- {
- NextBytes(buffer, 0, buffer.Length);
+ var retval = ((m_boolValues >> m_nextBoolIndex) & 1) == 1;
+ m_nextBoolIndex++;
+ return retval;
}
+
///
/// Fills all bytes from offset to offset + length in buffer with random values
///
- public void NextBytes(byte[] buffer, int offset, int length)
+ public virtual void NextBytes(byte[] buffer, int offset, int length)
{
int full = length / 4;
int ptr = offset;
@@ -127,20 +141,9 @@ namespace Lidgren.Network
buffer[ptr++] = (byte)NextUInt32();
}
- ///
- /// Returns a random value >= 0.0f and < 1.0f
- ///
- public float NextSingle()
+ public override void NextBytes(byte[] buffer)
{
- return (float)((double)NextUInt32() * c_uniformSingleMultiplier);
- }
-
- ///
- /// Returns random value that is >= 0.0 and < 1.0
- ///
- public double NextDouble()
- {
- return (double)NextUInt32() * c_uniformSingleMultiplier;
+ NextBytes(buffer, 0, buffer.Length);
}
}
}
diff --git a/Lidgren.Network/NetRandomSeed.cs b/Lidgren.Network/NetRandomSeed.cs
new file mode 100644
index 0000000..57c14fb
--- /dev/null
+++ b/Lidgren.Network/NetRandomSeed.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Threading;
+
+namespace Lidgren.Network
+{
+ public static class NetRandomSeed
+ {
+ private static int m_seedIncrement = -1640531527;
+
+ ///
+ /// Generates a 32 bit random seed
+ ///
+ public static uint GetUInt32()
+ {
+ ulong seed = GetUInt64();
+ uint low = (uint)seed;
+ uint high = (uint)(seed >> 32);
+ return low ^ high;
+ }
+
+ ///
+ /// Generates a 64 bit random seed
+ ///
+ public static ulong GetUInt64()
+ {
+#if !__ANDROID__ && !IOS && !UNITY_WEBPLAYER
+ ulong seed = (ulong)System.Diagnostics.Stopwatch.GetTimestamp();
+ seed ^= (ulong)Environment.WorkingSet;
+ ulong s2 = (ulong)Interlocked.Increment(ref m_seedIncrement);
+ s2 |= (((ulong)Guid.NewGuid().GetHashCode()) << 32);
+ seed ^= s2;
+#else
+ ulong v1 = (ulong)Environment.TickCount;
+ v1 |= (((ulong)(new object().GetHashCode())) << 32);
+ ulong v2 = (ulong)Guid.NewGuid().GetHashCode();
+ v2 |= (((ulong)(Interlocked.Increment(ref m_seedIncrement)) << 32);
+ return v1 ^ v2;
+#endif
+ return seed;
+ }
+ }
+}
diff --git a/Lidgren.Network/NetSRP.cs b/Lidgren.Network/NetSRP.cs
index cd0c140..e9852ff 100644
--- a/Lidgren.Network/NetSRP.cs
+++ b/Lidgren.Network/NetSRP.cs
@@ -48,7 +48,7 @@ namespace Lidgren.Network
public static byte[] CreateRandomSalt()
{
byte[] retval = new byte[16];
- NetRandom.Instance.NextBytes(retval);
+ CryptoRandom.Instance.NextBytes(retval);
return retval;
}
@@ -58,7 +58,7 @@ namespace Lidgren.Network
public static byte[] CreateRandomEphemeral()
{
byte[] retval = new byte[32];
- NetRandom.Instance.NextBytes(retval);
+ CryptoRandom.Instance.NextBytes(retval);
return retval;
}