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; }