v0.2.2: updated Lidgren, railgun shells can be bought, autorestart server, netstats, tutorial moloch spawning in a wall fix, misc error checks

This commit is contained in:
Regalis
2015-10-18 22:44:30 +03:00
parent aa3882a815
commit 0e5e86e363
85 changed files with 2763 additions and 1866 deletions
+1 -1
View File
@@ -29,7 +29,7 @@
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Windows\Release\</OutputPath> <OutputPath>..\Subsurface\bin\Windows\Release\</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants> <DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
+1 -1
View File
@@ -542,7 +542,7 @@ namespace Launcher2
{ {
if (e.Error!=null) if (e.Error!=null)
{ {
string errorMsg = "Error while downloading: " + e.Error.InnerException.Message; string errorMsg = "Error while downloading: " + e.Error;
GUITextBlock textBlock = new GUITextBlock( GUITextBlock textBlock = new GUITextBlock(
new Rectangle(0, 0, 0, 0), new Rectangle(0, 0, 0, 0),
@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
namespace Lidgren.Network
{
/// <summary>
/// Interface for an encryption algorithm
/// </summary>
public interface INetEncryption
{
/// <summary>
/// Encrypt an outgoing message in place
/// </summary>
bool Encrypt(NetOutgoingMessage msg);
/// <summary>
/// Decrypt an incoming message in place
/// </summary>
bool Decrypt(NetIncomingMessage msg);
}
}
+9 -155
View File
@@ -1,172 +1,26 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> public class NetAESEncryption : NetCryptoProviderBase
/// AES encryption
/// </summary>
public class NetAESEncryption : INetEncryption
{ {
private readonly byte[] m_key; public NetAESEncryption(NetPeer peer)
private readonly byte[] m_iv; : base(peer, new AesCryptoServiceProvider())
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetAESEncryption()
{
#if !IOS && !__ANDROID__
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in aes.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in aes.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
#endif
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetAESEncryption constructor
/// </summary>
public NetAESEncryption(string key)
: this(key, m_keysizes[0])
{ {
} }
/// <summary> public NetAESEncryption(NetPeer peer, string key)
/// Encrypt outgoing message : base(peer, new AesCryptoServiceProvider())
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{ {
#if !IOS && !__ANDROID__ SetKey(key);
try
{
// nested usings are fun!
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
#else
return false;
#endif
} }
/// <summary> public NetAESEncryption(NetPeer peer, byte[] data, int offset, int count)
/// Decrypt incoming message : base(peer, new AesCryptoServiceProvider())
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{ {
#if !IOS && !__ANDROID__ SetKey(data, offset, count);
try
{
// nested usings are fun!
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
#else
return false;
#endif
} }
} }
} }
@@ -6,7 +6,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Base for a non-threadsafe encryption class /// Base for a non-threadsafe encryption class
/// </summary> /// </summary>
public abstract class NetBlockEncryptionBase : INetEncryption public abstract class NetBlockEncryptionBase : NetEncryption
{ {
// temporary space for one block to avoid reallocating every time // temporary space for one block to avoid reallocating every time
private byte[] m_tmp; private byte[] m_tmp;
@@ -19,7 +19,8 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// NetBlockEncryptionBase constructor /// NetBlockEncryptionBase constructor
/// </summary> /// </summary>
public NetBlockEncryptionBase() public NetBlockEncryptionBase(NetPeer peer)
: base(peer)
{ {
m_tmp = new byte[BlockSize]; m_tmp = new byte[BlockSize];
} }
@@ -27,7 +28,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted /// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
/// </summary> /// </summary>
public bool Encrypt(NetOutgoingMessage msg) public override bool Encrypt(NetOutgoingMessage msg)
{ {
int payloadBitLength = msg.LengthBits; int payloadBitLength = msg.LengthBits;
int numBytes = msg.LengthBytes; int numBytes = msg.LengthBytes;
@@ -55,7 +56,7 @@ namespace Lidgren.Network
/// </summary> /// </summary>
/// <param name="msg">message to decrypt</param> /// <param name="msg">message to decrypt</param>
/// <returns>true if successful; false if failed</returns> /// <returns>true if successful; false if failed</returns>
public bool Decrypt(NetIncomingMessage msg) public override bool Decrypt(NetIncomingMessage msg)
{ {
int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length
int blockSize = BlockSize; int blockSize = BlockSize;
@@ -0,0 +1,77 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace Lidgren.Network
{
public abstract class NetCryptoProviderBase : NetEncryption
{
protected SymmetricAlgorithm m_algorithm;
public NetCryptoProviderBase(NetPeer peer, SymmetricAlgorithm algo)
: base(peer)
{
m_algorithm = algo;
m_algorithm.GenerateKey();
m_algorithm.GenerateIV();
}
public override void SetKey(byte[] data, int offset, int count)
{
int len = m_algorithm.Key.Length;
var key = new byte[len];
for (int i = 0; i < len; i++)
key[i] = data[offset + (i % count)];
m_algorithm.Key = key;
len = m_algorithm.IV.Length;
key = new byte[len];
for (int i = 0; i < len; i++)
key[len - 1 - i] = data[offset + (i % count)];
m_algorithm.IV = key;
}
public override bool Encrypt(NetOutgoingMessage msg)
{
int unEncLenBits = msg.LengthBits;
var ms = new MemoryStream();
var cs = new CryptoStream(ms, m_algorithm.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(msg.m_data, 0, msg.LengthBytes);
cs.Close();
// get results
var arr = ms.ToArray();
ms.Close();
msg.EnsureBufferSize((arr.Length + 4) * 8);
msg.LengthBits = 0; // reset write pointer
msg.Write((uint)unEncLenBits);
msg.Write(arr);
msg.LengthBits = (arr.Length + 4) * 8;
return true;
}
public override bool Decrypt(NetIncomingMessage msg)
{
int unEncLenBits = (int)msg.ReadUInt32();
var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
var cs = new CryptoStream(ms, m_algorithm.CreateDecryptor(), CryptoStreamMode.Read);
var byteLen = NetUtility.BytesToHoldBits(unEncLenBits);
var result = m_peer.GetStorage(byteLen);
cs.Read(result, 0, byteLen);
cs.Close();
// TODO: recycle existing msg
msg.m_data = result;
msg.m_bitLength = unEncLenBits;
msg.m_readPosition = 0;
return true;
}
}
}
@@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace Lidgren.Network
{
public abstract class NetCryptoProviderEncryption : NetEncryption
{
public NetCryptoProviderEncryption(NetPeer peer)
: base(peer)
{
}
protected abstract CryptoStream GetEncryptStream(MemoryStream ms);
protected abstract CryptoStream GetDecryptStream(MemoryStream ms);
public override bool Encrypt(NetOutgoingMessage msg)
{
int unEncLenBits = msg.LengthBits;
var ms = new MemoryStream();
var cs = GetEncryptStream(ms);
cs.Write(msg.m_data, 0, msg.LengthBytes);
cs.Close();
// get results
var arr = ms.ToArray();
ms.Close();
msg.EnsureBufferSize((arr.Length + 4) * 8);
msg.LengthBits = 0; // reset write pointer
msg.Write((uint)unEncLenBits);
msg.Write(arr);
msg.LengthBits = (arr.Length + 4) * 8;
return true;
}
public override bool Decrypt(NetIncomingMessage msg)
{
int unEncLenBits = (int)msg.ReadUInt32();
var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
var cs = GetDecryptStream(ms);
var result = m_peer.GetStorage(unEncLenBits);
cs.Read(result, 0, NetUtility.BytesToHoldBits(unEncLenBits));
cs.Close();
// TODO: recycle existing msg
msg.m_data = result;
msg.m_bitLength = unEncLenBits;
return true;
}
}
}
+10 -148
View File
@@ -1,164 +1,26 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> public class NetDESEncryption : NetCryptoProviderBase
/// DES encryption
/// </summary>
public class NetDESEncryption : INetEncryption
{ {
private readonly byte[] m_key; public NetDESEncryption(NetPeer peer)
private readonly byte[] m_iv; : base(peer, new DESCryptoServiceProvider())
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetDESEncryption()
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in des.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in des.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetDESEncryption constructor
/// </summary>
public NetDESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetDESEncryption constructor
/// </summary>
public NetDESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetDESEncryption constructor
/// </summary>
public NetDESEncryption(string key)
: this(key, m_keysizes[0])
{ {
} }
/// <summary> public NetDESEncryption(NetPeer peer, string key)
/// Encrypt outgoing message : base(peer, new DESCryptoServiceProvider())
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{ {
try SetKey(key);
{
// nested usings are fun!
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
} }
/// <summary> public NetDESEncryption(NetPeer peer, byte[] data, int offset, int count)
/// Decrypt incoming message : base(peer, new DESCryptoServiceProvider())
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{ {
try SetKey(data, offset, count);
{
// nested usings are fun!
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
} }
} }
} }
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace Lidgren.Network
{
/// <summary>
/// Interface for an encryption algorithm
/// </summary>
public abstract class NetEncryption
{
/// <summary>
/// NetPeer
/// </summary>
protected NetPeer m_peer;
/// <summary>
/// Constructor
/// </summary>
public NetEncryption(NetPeer peer)
{
if (peer == null)
throw new NetException("Peer must not be null");
m_peer = peer;
}
public void SetKey(string str)
{
var bytes = System.Text.Encoding.ASCII.GetBytes(str);
SetKey(bytes, 0, bytes.Length);
}
public abstract void SetKey(byte[] data, int offset, int count);
/// <summary>
/// Encrypt an outgoing message in place
/// </summary>
public abstract bool Encrypt(NetOutgoingMessage msg);
/// <summary>
/// Decrypt an incoming message in place
/// </summary>
public abstract bool Decrypt(NetIncomingMessage msg);
}
}
+10 -149
View File
@@ -1,165 +1,26 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> public class NetRC2Encryption : NetCryptoProviderBase
/// RC2 encryption
/// </summary>
public class NetRC2Encryption : INetEncryption
{ {
private readonly byte[] m_key; public NetRC2Encryption(NetPeer peer)
private readonly byte[] m_iv; : base(peer, new RC2CryptoServiceProvider())
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetRC2Encryption()
{
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in rc2.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in rc2.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetRC2Encryption constructor
/// </summary>
public NetRC2Encryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetRC2Encryption constructor
/// </summary>
public NetRC2Encryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetRC2Encryption constructor
/// </summary>
/// <param name="key"></param>
public NetRC2Encryption(string key)
: this(key, m_keysizes[0])
{ {
} }
/// <summary> public NetRC2Encryption(NetPeer peer, string key)
/// Encrypt outgoing message : base(peer, new RC2CryptoServiceProvider())
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{ {
try SetKey(key);
{
// nested usings are fun!
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
} }
/// <summary> public NetRC2Encryption(NetPeer peer, byte[] data, int offset, int count)
/// Decrypt incoming message : base(peer, new RC2CryptoServiceProvider())
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{ {
try SetKey(data, offset, count);
{
// nested usings are fun!
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
} }
} }
} }
@@ -1,164 +1,26 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> public class NetTripleDESEncryption : NetCryptoProviderBase
/// Triple DES encryption
/// </summary>
public class NetTripleDESEncryption : INetEncryption
{ {
private readonly byte[] m_key; public NetTripleDESEncryption(NetPeer peer)
private readonly byte[] m_iv; : base(peer, new TripleDESCryptoServiceProvider())
private readonly int m_bitSize;
private static readonly List<int> m_keysizes;
private static readonly List<int> m_blocksizes;
static NetTripleDESEncryption()
{
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
List<int> temp = new List<int>();
foreach (KeySizes keysize in tripleDES.LegalKeySizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_keysizes = temp;
temp = new List<int>();
foreach (KeySizes keysize in tripleDES.LegalBlockSizes)
{
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
{
if (!temp.Contains(i))
temp.Add(i);
if (i == keysize.MaxSize)
break;
}
}
m_blocksizes = temp;
}
/// <summary>
/// NetTriplsDESEncryption constructor
/// </summary>
public NetTripleDESEncryption(byte[] key, byte[] iv)
{
if (!m_keysizes.Contains(key.Length * 8))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
if (!m_blocksizes.Contains(iv.Length * 8))
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
m_key = key;
m_iv = iv;
m_bitSize = m_key.Length * 8;
}
/// <summary>
/// NetTriplsDESEncryption constructor
/// </summary>
public NetTripleDESEncryption(string key, int bitsize)
{
if (!m_keysizes.Contains(bitsize))
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
byte[] entropy = Encoding.UTF32.GetBytes(key);
// I know hardcoding salts is bad, but in this case I think it is acceptable.
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
hmacsha512.Initialize();
for (int i = 0; i < 1000; i++)
{
entropy = hmacsha512.ComputeHash(entropy);
}
int keylen = bitsize / 8;
m_key = new byte[keylen];
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
m_iv = new byte[m_blocksizes[0] / 8];
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
m_bitSize = bitsize;
}
/// <summary>
/// NetTriplsDESEncryption constructor
/// </summary>
public NetTripleDESEncryption(string key)
: this(key, m_keysizes[0])
{ {
} }
/// <summary> public NetTripleDESEncryption(NetPeer peer, string key)
/// Encrypt outgoing message : base(peer, new TripleDESCryptoServiceProvider())
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
{ {
try SetKey(key);
{
// nested usings are fun!
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
} }
/// <summary> public NetTripleDESEncryption(NetPeer peer, byte[] data, int offset, int count)
/// Decrypt incoming message : base(peer, new TripleDESCryptoServiceProvider())
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
{ {
try SetKey(data, offset, count);
{
// nested usings are fun!
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
{
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
CryptoStreamMode.Write))
{
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
}
msg.m_data = memoryStream.ToArray();
}
}
}
}
catch
{
return false;
}
return true;
} }
} }
} }
+13 -5
View File
@@ -7,22 +7,30 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Example class; not very good encryption /// Example class; not very good encryption
/// </summary> /// </summary>
public class NetXorEncryption : INetEncryption public class NetXorEncryption : NetEncryption
{ {
private byte[] m_key; private byte[] m_key;
/// <summary> /// <summary>
/// NetXorEncryption constructor /// NetXorEncryption constructor
/// </summary> /// </summary>
public NetXorEncryption(byte[] key) public NetXorEncryption(NetPeer peer, byte[] key)
: base(peer)
{ {
m_key = key; m_key = key;
} }
public override void SetKey(byte[] data, int offset, int count)
{
m_key = new byte[count];
Array.Copy(data, offset, m_key, 0, count);
}
/// <summary> /// <summary>
/// NetXorEncryption constructor /// NetXorEncryption constructor
/// </summary> /// </summary>
public NetXorEncryption(string key) public NetXorEncryption(NetPeer peer, string key)
: base(peer)
{ {
m_key = Encoding.UTF8.GetBytes(key); m_key = Encoding.UTF8.GetBytes(key);
} }
@@ -30,7 +38,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Encrypt an outgoing message /// Encrypt an outgoing message
/// </summary> /// </summary>
public bool Encrypt(NetOutgoingMessage msg) public override bool Encrypt(NetOutgoingMessage msg)
{ {
int numBytes = msg.LengthBytes; int numBytes = msg.LengthBytes;
for (int i = 0; i < numBytes; i++) for (int i = 0; i < numBytes; i++)
@@ -44,7 +52,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Decrypt an incoming message /// Decrypt an incoming message
/// </summary> /// </summary>
public bool Decrypt(NetIncomingMessage msg) public override bool Decrypt(NetIncomingMessage msg)
{ {
int numBytes = msg.LengthBytes; int numBytes = msg.LengthBytes;
for (int i = 0; i < numBytes; i++) for (int i = 0; i < numBytes; i++)
@@ -44,7 +44,8 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// 16 byte key /// 16 byte key
/// </summary> /// </summary>
public NetXtea(byte[] key, int rounds) public NetXtea(NetPeer peer, byte[] key, int rounds)
: base(peer)
{ {
if (key.Length < c_keySize) if (key.Length < c_keySize)
throw new NetException("Key too short!"); throw new NetException("Key too short!");
@@ -73,19 +74,26 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// 16 byte key /// 16 byte key
/// </summary> /// </summary>
public NetXtea(byte[] key) public NetXtea(NetPeer peer, byte[] key)
: this(key, 32) : this(peer, key, 32)
{ {
} }
/// <summary> /// <summary>
/// String to hash for key /// String to hash for key
/// </summary> /// </summary>
public NetXtea(string key) public NetXtea(NetPeer peer, string key)
: this(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key)), 32) : this(peer, NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(key)), 32)
{ {
} }
public override void SetKey(byte[] data, int offset, int length)
{
var key = NetUtility.ComputeSHAHash(data, offset, length);
NetException.Assert(key.Length >= 16);
SetKey(key, 0, 16);
}
/// <summary> /// <summary>
/// Encrypts a block of bytes /// Encrypts a block of bytes
/// </summary> /// </summary>
+28 -18
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Lidgren.Network</RootNamespace> <RootNamespace>Lidgren.Network</RootNamespace>
<AssemblyName>Lidgren.Network</AssemblyName> <AssemblyName>Lidgren.Network</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install> <Install>true</Install>
@@ -39,6 +39,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\Debug\Lidgren.Network.XML</DocumentationFile> <DocumentationFile>bin\Debug\Lidgren.Network.XML</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@@ -48,27 +49,34 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> <Prefer32Bit>false</Prefer32Bit>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>Lidgren.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Encryption\INetEncryption.cs" /> <Compile Include="Encryption\NetAESEncryption.cs">
<Compile Include="Encryption\NetAESEncryption.cs" /> <SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetCryptoProviderBase.cs" />
<Compile Include="Encryption\NetDESEncryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetEncryption.cs" />
<Compile Include="Encryption\NetBlockEncryptionBase.cs" /> <Compile Include="Encryption\NetBlockEncryptionBase.cs" />
<Compile Include="Encryption\NetDESEncryption.cs" /> <Compile Include="Encryption\NetRC2Encryption.cs">
<Compile Include="Encryption\NetRC2Encryption.cs" /> <SubType>Code</SubType>
<Compile Include="Encryption\NetTripleDESEncryption.cs" /> </Compile>
<Compile Include="Encryption\NetXorEncryption.cs" /> <Compile Include="Encryption\NetTripleDESEncryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetXorEncryption.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Encryption\NetXteaEncryption.cs" /> <Compile Include="Encryption\NetXteaEncryption.cs" />
<Compile Include="NamespaceDoc.cs" /> <Compile Include="NamespaceDoc.cs" />
<Compile Include="NetBigInteger.cs" /> <Compile Include="NetBigInteger.cs" />
@@ -109,6 +117,8 @@
<Compile Include="NetPeerStatus.cs" /> <Compile Include="NetPeerStatus.cs" />
<Compile Include="NetQueue.cs" /> <Compile Include="NetQueue.cs" />
<Compile Include="NetRandom.cs" /> <Compile Include="NetRandom.cs" />
<Compile Include="NetRandom.Implementations.cs" />
<Compile Include="NetRandomSeed.cs" />
<Compile Include="NetReceiverChannelBase.cs" /> <Compile Include="NetReceiverChannelBase.cs" />
<Compile Include="NetReliableOrderedReceiver.cs" /> <Compile Include="NetReliableOrderedReceiver.cs" />
<Compile Include="NetReliableSenderChannel.cs" /> <Compile Include="NetReliableSenderChannel.cs" />
@@ -126,8 +136,11 @@
<Compile Include="NetUnreliableUnorderedReceiver.cs" /> <Compile Include="NetUnreliableUnorderedReceiver.cs" />
<Compile Include="NetUPnP.cs" /> <Compile Include="NetUPnP.cs" />
<Compile Include="NetUtility.cs" /> <Compile Include="NetUtility.cs" />
<Compile Include="Platform\PlatformAndroid.cs" />
<Compile Include="Platform\PlatformConstrained.cs" />
<Compile Include="Platform\PlatformWin32.cs" />
<Compile Include="Platform\PlatformWinRT.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SenderChannelBase.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0"> <BootstrapperPackage Include=".NETFramework,Version=v4.0">
@@ -151,9 +164,6 @@
<Install>true</Install> <Install>true</Install>
</BootstrapperPackage> </BootstrapperPackage>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="Lidgren.snk" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
Binary file not shown.
+15
View File
@@ -2334,4 +2334,19 @@ namespace Lidgren.Network
return ((word >> (n % 32)) & 1) > 0; return ((word >> (n % 32)) & 1) > 0;
} }
} }
#if WINDOWS_RUNTIME
internal sealed class Stack
{
private System.Collections.Generic.List<object> m_list = new System.Collections.Generic.List<object>();
public int Count { get { return m_list.Count; } }
public void Push(object item) { m_list.Add(item); }
public object Pop()
{
var item = m_list[m_list.Count - 1];
m_list.RemoveAt(m_list.Count - 1);
return item;
}
}
#endif
} }
+1 -2
View File
@@ -500,8 +500,7 @@ namespace Lidgren.Network
int num2 = 0; int num2 = 0;
while (true) while (true)
{ {
if (num2 == 0x23) NetException.Assert(num2 != 0x23, "Bad 7-bit encoded integer");
throw new FormatException("Bad 7-bit encoded integer");
byte num3 = buffer[offset++]; byte num3 = buffer[offset++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f); num1 |= (num3 & 0x7f) << (num2 & 0x1f);
+1 -1
View File
@@ -237,7 +237,7 @@ namespace Lidgren.Network
else else
{ {
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32; retval |= (UInt64)NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition + 32) << 32;
} }
return retval; return retval;
} }
+7 -7
View File
@@ -1,4 +1,4 @@
/* Copyright (c) 2010 Michael Lidgren /* Copyright (c) 2010 Michael Lidgren
Permission is hereby granted, free of charge, to any person obtaining a copy of this software Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without and associated documentation files (the "Software"), to deal in the Software without
@@ -37,7 +37,8 @@ namespace Lidgren.Network
public void ReadAllFields(object target, BindingFlags flags) public void ReadAllFields(object target, BindingFlags flags)
{ {
if (target == null) if (target == null)
return; throw new ArgumentNullException("target");
Type tp = target.GetType(); Type tp = target.GetType();
FieldInfo[] fields = tp.GetFields(flags); FieldInfo[] fields = tp.GetFields(flags);
@@ -76,8 +77,6 @@ namespace Lidgren.Network
if (target == null) if (target == null)
throw new ArgumentNullException("target"); throw new ArgumentNullException("target");
if (target == null)
return;
Type tp = target.GetType(); Type tp = target.GetType();
PropertyInfo[] fields = tp.GetProperties(flags); PropertyInfo[] fields = tp.GetProperties(flags);
@@ -94,10 +93,11 @@ namespace Lidgren.Network
value = readMethod.Invoke(this, null); value = readMethod.Invoke(this, null);
// set the value // set the value
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic); var setMethod = fi.GetSetMethod();
setMethod.Invoke(target, new object[] { value }); if (setMethod != null)
setMethod.Invoke(target, new object[] { value });
} }
} }
} }
} }
} }
+32 -10
View File
@@ -4,6 +4,10 @@ using System.Text;
using System.Reflection; using System.Reflection;
using System.Net; using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
@@ -311,7 +315,7 @@ namespace Lidgren.Network
else else
{ {
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32; retval |= (UInt64)NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition + 32) << 32;
} }
m_readPosition += numberOfBits; m_readPosition += numberOfBits;
return retval; return retval;
@@ -406,7 +410,7 @@ namespace Lidgren.Network
{ {
int num1 = 0; int num1 = 0;
int num2 = 0; int num2 = 0;
while (true) while (m_bitLength - m_readPosition >= 8)
{ {
byte num3 = this.ReadByte(); byte num3 = this.ReadByte();
num1 |= (num3 & 0x7f) << num2; num1 |= (num3 & 0x7f) << num2;
@@ -414,6 +418,9 @@ namespace Lidgren.Network
if ((num3 & 0x80) == 0) if ((num3 & 0x80) == 0)
return (uint)num1; return (uint)num1;
} }
// ouch; failed to find enough bytes; malformed variable length number?
return (uint)num1;
} }
/// <summary> /// <summary>
@@ -424,7 +431,7 @@ namespace Lidgren.Network
{ {
int num1 = 0; int num1 = 0;
int num2 = 0; int num2 = 0;
while (true) while (m_bitLength - m_readPosition >= 8)
{ {
byte num3; byte num3;
if (ReadByte(out num3) == false) if (ReadByte(out num3) == false)
@@ -440,6 +447,8 @@ namespace Lidgren.Network
return true; return true;
} }
} }
result = (uint)num1;
return false;
} }
/// <summary> /// <summary>
@@ -468,7 +477,7 @@ namespace Lidgren.Network
{ {
UInt64 num1 = 0; UInt64 num1 = 0;
int num2 = 0; int num2 = 0;
while (true) while (m_bitLength - m_readPosition >= 8)
{ {
//if (num2 == 0x23) //if (num2 == 0x23)
// throw new FormatException("Bad 7-bit encoded integer"); // throw new FormatException("Bad 7-bit encoded integer");
@@ -479,6 +488,9 @@ namespace Lidgren.Network
if ((num3 & 0x80) == 0) if ((num3 & 0x80) == 0)
return num1; return num1;
} }
// ouch; failed to find enough bytes; malformed variable length number?
return num1;
} }
/// <summary> /// <summary>
@@ -543,10 +555,20 @@ namespace Lidgren.Network
{ {
int byteLen = (int)ReadVariableUInt32(); int byteLen = (int)ReadVariableUInt32();
if (byteLen == 0) if (byteLen <= 0)
return String.Empty; return String.Empty;
NetException.Assert(m_bitLength - m_readPosition >= (byteLen * 8), c_readOverflowError); if ((ulong)(m_bitLength - m_readPosition) < ((ulong)byteLen * 8))
{
// not enough data
#if DEBUG
throw new NetException(c_readOverflowError);
#else
m_readPosition = m_bitLength;
return null; // unfortunate; but we need to protect against DDOS
#endif
}
if ((m_readPosition & 7) == 0) if ((m_readPosition & 7) == 0)
{ {
@@ -572,7 +594,7 @@ namespace Lidgren.Network
return false; return false;
} }
if (byteLen == 0) if (byteLen <= 0)
{ {
result = String.Empty; result = String.Empty;
return true; return true;
@@ -620,14 +642,14 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Reads a stored IPv4 endpoint description /// Reads a stored IPv4 endpoint description
/// </summary> /// </summary>
public IPEndPoint ReadIPEndPoint() public NetEndPoint ReadIPEndPoint()
{ {
byte len = ReadByte(); byte len = ReadByte();
byte[] addressBytes = ReadBytes(len); byte[] addressBytes = ReadBytes(len);
int port = (int)ReadUInt16(); int port = (int)ReadUInt16();
IPAddress address = new IPAddress(addressBytes); var address = NetUtility.CreateAddressFromBytes(addressBytes);
return new IPEndPoint(address, port); return new NetEndPoint(address, port);
} }
/// <summary> /// <summary>
@@ -78,13 +78,16 @@ namespace Lidgren.Network
foreach (PropertyInfo fi in fields) foreach (PropertyInfo fi in fields)
{ {
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic); MethodInfo getMethod = fi.GetGetMethod();
object value = getMethod.Invoke(ob, null); if (getMethod != null)
{
object value = getMethod.Invoke(ob, null);
// find the appropriate Write method // find the appropriate Write method
MethodInfo writeMethod; MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod)) if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value }); writeMethod.Invoke(this, new object[] { value });
}
} }
} }
} }
+58 -1
View File
@@ -161,6 +161,18 @@ namespace Lidgren.Network
m_bitLength += 16; m_bitLength += 16;
} }
/// <summary>
/// Writes a 16 bit unsigned integer at a given offset in the buffer
/// </summary>
[CLSCompliant(false)]
public void WriteAt(Int32 offset, UInt16 source)
{
int newBitLength = Math.Max(m_bitLength, offset + 16);
EnsureBufferSize(newBitLength);
NetBitWriter.WriteUInt16(source, 16, m_data, offset);
m_bitLength = newBitLength;
}
/// <summary> /// <summary>
/// Writes an unsigned integer using 1 to 16 bits /// Writes an unsigned integer using 1 to 16 bits
/// </summary> /// </summary>
@@ -183,6 +195,17 @@ namespace Lidgren.Network
m_bitLength += 16; m_bitLength += 16;
} }
/// <summary>
/// Writes a 16 bit signed integer at a given offset in the buffer
/// </summary>
public void WriteAt(Int32 offset, Int16 source)
{
int newBitLength = Math.Max(m_bitLength, offset + 16);
EnsureBufferSize(newBitLength);
NetBitWriter.WriteUInt16((ushort)source, 16, m_data, offset);
m_bitLength = newBitLength;
}
#if UNSAFE #if UNSAFE
/// <summary> /// <summary>
/// Writes a 32 bit signed integer /// Writes a 32 bit signed integer
@@ -217,6 +240,17 @@ namespace Lidgren.Network
} }
#endif #endif
/// <summary>
/// Writes a 32 bit signed integer at a given offset in the buffer
/// </summary>
public void WriteAt(Int32 offset, Int32 source)
{
int newBitLength = Math.Max(m_bitLength, offset + 32);
EnsureBufferSize(newBitLength);
NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, offset);
m_bitLength = newBitLength;
}
#if UNSAFE #if UNSAFE
/// <summary> /// <summary>
/// Writes a 32 bit unsigned integer /// Writes a 32 bit unsigned integer
@@ -253,6 +287,18 @@ namespace Lidgren.Network
} }
#endif #endif
/// <summary>
/// Writes a 32 bit unsigned integer at a given offset in the buffer
/// </summary>
[CLSCompliant(false)]
public void WriteAt(Int32 offset, UInt32 source)
{
int newBitLength = Math.Max(m_bitLength, offset + 32);
EnsureBufferSize(newBitLength);
NetBitWriter.WriteUInt32(source, 32, m_data, offset);
m_bitLength = newBitLength;
}
/// <summary> /// <summary>
/// Writes a 32 bit signed integer /// Writes a 32 bit signed integer
/// </summary> /// </summary>
@@ -299,6 +345,18 @@ namespace Lidgren.Network
m_bitLength += 64; m_bitLength += 64;
} }
/// <summary>
/// Writes a 64 bit unsigned integer at a given offset in the buffer
/// </summary>
[CLSCompliant(false)]
public void WriteAt(Int32 offset, UInt64 source)
{
int newBitLength = Math.Max(m_bitLength, offset + 64);
EnsureBufferSize(newBitLength);
NetBitWriter.WriteUInt64(source, 64, m_data, offset);
m_bitLength = newBitLength;
}
/// <summary> /// <summary>
/// Writes an unsigned integer using 1 to 64 bits /// Writes an unsigned integer using 1 to 64 bits
/// </summary> /// </summary>
@@ -535,7 +593,6 @@ namespace Lidgren.Network
{ {
if (string.IsNullOrEmpty(source)) if (string.IsNullOrEmpty(source))
{ {
EnsureBufferSize(m_bitLength + 8);
WriteVariableUInt32(0); WriteVariableUInt32(0);
return; return;
} }
+6 -1
View File
@@ -19,6 +19,10 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Net; using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
@@ -80,7 +84,7 @@ namespace Lidgren.Network
/// <param name="remoteEndPoint">The remote endpoint to connect to</param> /// <param name="remoteEndPoint">The remote endpoint to connect to</param>
/// <param name="hailMessage">The hail message to pass</param> /// <param name="hailMessage">The hail message to pass</param>
/// <returns>server connection, or null if already connected</returns> /// <returns>server connection, or null if already connected</returns>
public override NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage) public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{ {
lock (m_connections) lock (m_connections)
{ {
@@ -153,6 +157,7 @@ namespace Lidgren.Network
if (serverConnection == null) if (serverConnection == null)
{ {
LogWarning("Cannot send message, no server connection!"); LogWarning("Cannot send message, no server connection!");
Recycle(msg);
return NetSendResult.FailedNotConnected; return NetSendResult.FailedNotConnected;
} }
+79 -59
View File
@@ -1,6 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
@@ -8,10 +13,11 @@ namespace Lidgren.Network
{ {
internal bool m_connectRequested; internal bool m_connectRequested;
internal bool m_disconnectRequested; internal bool m_disconnectRequested;
internal bool m_connectionInitiator; internal bool m_disconnectReqSendBye;
internal string m_disconnectMessage; internal string m_disconnectMessage;
internal bool m_connectionInitiator;
internal NetIncomingMessage m_remoteHailMessage; internal NetIncomingMessage m_remoteHailMessage;
internal float m_lastHandshakeSendTime; internal double m_lastHandshakeSendTime;
internal int m_handshakeAttempts; internal int m_handshakeAttempts;
/// <summary> /// <summary>
@@ -20,7 +26,7 @@ namespace Lidgren.Network
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } } public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
// heartbeat called when connection still is in m_handshakes of NetPeer // heartbeat called when connection still is in m_handshakes of NetPeer
internal void UnconnectedHeartbeat(float now) internal void UnconnectedHeartbeat(double now)
{ {
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
@@ -43,7 +49,8 @@ namespace Lidgren.Network
break; break;
case NetConnectionStatus.Disconnected: case NetConnectionStatus.Disconnected:
throw new NetException("This connection is Disconnected; spent. A new one should have been created"); m_peer.ThrowOrLog("This connection is Disconnected; spent. A new one should have been created");
break;
case NetConnectionStatus.Disconnecting: case NetConnectionStatus.Disconnecting:
// let disconnect finish first // let disconnect finish first
@@ -75,14 +82,12 @@ namespace Lidgren.Network
case NetConnectionStatus.RespondedConnect: case NetConnectionStatus.RespondedConnect:
SendConnectResponse(now, true); SendConnectResponse(now, true);
break; break;
case NetConnectionStatus.None:
case NetConnectionStatus.ReceivedInitiation:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
case NetConnectionStatus.RespondedAwaitingApproval: case NetConnectionStatus.RespondedAwaitingApproval:
// awaiting approval // awaiting approval
m_lastHandshakeSendTime = now; // postpone handshake resend m_lastHandshakeSendTime = now; // postpone handshake resend
break; break;
case NetConnectionStatus.None:
case NetConnectionStatus.ReceivedInitiation:
default: default:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status); m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break; break;
@@ -94,8 +99,6 @@ namespace Lidgren.Network
{ {
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
//m_peer.LogDebug("Executing disconnect");
// clear send queues // clear send queues
for (int i = 0; i < m_sendChannels.Length; i++) for (int i = 0; i < m_sendChannels.Length; i++)
{ {
@@ -107,7 +110,15 @@ namespace Lidgren.Network
if (sendByeMessage) if (sendByeMessage)
SendDisconnect(reason, true); SendDisconnect(reason, true);
SetStatus(NetConnectionStatus.Disconnected, reason); if (m_status == NetConnectionStatus.ReceivedInitiation)
{
// nothing much has happened yet; no need to send disconnected status message
m_status = NetConnectionStatus.Disconnected;
}
else
{
SetStatus(NetConnectionStatus.Disconnected, reason);
}
// in case we're still in handshake // in case we're still in handshake
lock (m_peer.m_handshakes) lock (m_peer.m_handshakes)
@@ -118,7 +129,7 @@ namespace Lidgren.Network
m_handshakeAttempts = 0; m_handshakeAttempts = 0;
} }
internal void SendConnect(float now) internal void SendConnect(double now)
{ {
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
@@ -129,7 +140,7 @@ namespace Lidgren.Network
om.m_messageType = NetMessageType.Connect; om.m_messageType = NetMessageType.Connect;
om.Write(m_peerConfiguration.AppIdentifier); om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier); om.Write(m_peer.m_uniqueIdentifier);
om.Write(now); om.Write((float)now);
WriteLocalHail(om); WriteLocalHail(om);
@@ -144,7 +155,7 @@ namespace Lidgren.Network
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect"); SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
} }
internal void SendConnectResponse(float now, bool onLibraryThread) internal void SendConnectResponse(double now, bool onLibraryThread)
{ {
if (onLibraryThread) if (onLibraryThread)
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
@@ -153,14 +164,14 @@ namespace Lidgren.Network
om.m_messageType = NetMessageType.ConnectResponse; om.m_messageType = NetMessageType.ConnectResponse;
om.Write(m_peerConfiguration.AppIdentifier); om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier); om.Write(m_peer.m_uniqueIdentifier);
om.Write(now); om.Write((float)now);
Interlocked.Increment(ref om.m_recyclingCount);
WriteLocalHail(om); WriteLocalHail(om);
if (onLibraryThread) if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint); m_peer.SendLibrary(om, m_remoteEndPoint);
else else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om)); m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
m_lastHandshakeSendTime = now; m_lastHandshakeSendTime = now;
m_handshakeAttempts++; m_handshakeAttempts++;
@@ -178,10 +189,11 @@ namespace Lidgren.Network
NetOutgoingMessage om = m_peer.CreateMessage(reason); NetOutgoingMessage om = m_peer.CreateMessage(reason);
om.m_messageType = NetMessageType.Disconnect; om.m_messageType = NetMessageType.Disconnect;
Interlocked.Increment(ref om.m_recyclingCount);
if (onLibraryThread) if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint); m_peer.SendLibrary(om, m_remoteEndPoint);
else else
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om)); m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
} }
private void WriteLocalHail(NetOutgoingMessage om) private void WriteLocalHail(NetOutgoingMessage om)
@@ -192,7 +204,7 @@ namespace Lidgren.Network
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes) if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
{ {
if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10) if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes)); m_peer.ThrowOrLog("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes); om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes);
} }
} }
@@ -225,7 +237,7 @@ namespace Lidgren.Network
m_localHailMessage = null; m_localHailMessage = null;
m_handshakeAttempts = 0; m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false); SendConnectResponse(NetTime.Now, false);
} }
/// <summary> /// <summary>
@@ -242,7 +254,7 @@ namespace Lidgren.Network
m_localHailMessage = localHail; m_localHailMessage = localHail;
m_handshakeAttempts = 0; m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false); SendConnectResponse(NetTime.Now, false);
} }
/// <summary> /// <summary>
@@ -263,7 +275,8 @@ namespace Lidgren.Network
SendDisconnect(reason, false); SendDisconnect(reason, false);
// remove from handshakes // remove from handshakes
m_peer.m_handshakes.Remove(m_remoteEndPoint); // TODO: make this more thread safe? we're on user thread lock (m_peer.m_handshakes)
m_peer.m_handshakes.Remove(m_remoteEndPoint);
} }
internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength) internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength)
@@ -322,43 +335,9 @@ namespace Lidgren.Network
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength); m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
break; break;
case NetMessageType.ConnectResponse: case NetMessageType.ConnectResponse:
switch (m_status) HandleConnectResponse(now, tp, ptr, payloadLength);
{
case NetConnectionStatus.InitiatedConnect:
// awesome
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
if (ok)
{
if (hail != null)
{
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
m_remoteHailMessage.LengthBits = (hail.Length * 8);
}
else
{
m_remoteHailMessage = null;
}
m_peer.AcceptConnection(this);
SendConnectionEstablished();
return;
}
break;
case NetConnectionStatus.RespondedConnect:
// hello, wtf?
break;
case NetConnectionStatus.Disconnecting:
case NetConnectionStatus.Disconnected:
case NetConnectionStatus.ReceivedInitiation:
case NetConnectionStatus.None:
// wtf? anyway, bye!
break;
case NetConnectionStatus.Connected:
// my ConnectionEstablished must have been lost, send another one
SendConnectionEstablished();
return;
}
break; break;
case NetMessageType.ConnectionEstablished: case NetMessageType.ConnectionEstablished:
switch (m_status) switch (m_status)
{ {
@@ -421,6 +400,47 @@ namespace Lidgren.Network
} }
} }
private void HandleConnectResponse(double now, NetMessageType tp, int ptr, int payloadLength)
{
byte[] hail;
switch (m_status)
{
case NetConnectionStatus.InitiatedConnect:
// awesome
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
if (ok)
{
if (hail != null)
{
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
m_remoteHailMessage.LengthBits = (hail.Length * 8);
}
else
{
m_remoteHailMessage = null;
}
m_peer.AcceptConnection(this);
SendConnectionEstablished();
return;
}
break;
case NetConnectionStatus.RespondedConnect:
// hello, wtf?
break;
case NetConnectionStatus.Disconnecting:
case NetConnectionStatus.Disconnected:
case NetConnectionStatus.ReceivedInitiation:
case NetConnectionStatus.None:
// wtf? anyway, bye!
break;
case NetConnectionStatus.Connected:
// my ConnectionEstablished must have been lost, send another one
SendConnectionEstablished();
return;
}
}
private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail) private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail)
{ {
hail = null; hail = null;
@@ -439,7 +459,6 @@ namespace Lidgren.Network
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier) if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
{ {
// wrong app identifier
ExecuteDisconnect("Wrong application identifier!", true); ExecuteDisconnect("Wrong application identifier!", true);
return false; return false;
} }
@@ -474,6 +493,7 @@ namespace Lidgren.Network
m_handshakeAttempts = 0; m_handshakeAttempts = 0;
m_disconnectRequested = true; m_disconnectRequested = true;
m_disconnectReqSendBye = true;
} }
} }
} }
+15 -13
View File
@@ -4,10 +4,10 @@ namespace Lidgren.Network
{ {
public partial class NetConnection public partial class NetConnection
{ {
private float m_sentPingTime; private double m_sentPingTime;
private int m_sentPingNumber; private int m_sentPingNumber;
private float m_averageRoundtripTime; private double m_averageRoundtripTime;
private float m_timeoutDeadline = float.MaxValue; private double m_timeoutDeadline = double.MaxValue;
// local time value + m_remoteTimeOffset = remote time value // local time value + m_remoteTimeOffset = remote time value
internal double m_remoteTimeOffset; internal double m_remoteTimeOffset;
@@ -15,7 +15,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Gets the current average roundtrip time in seconds /// Gets the current average roundtrip time in seconds
/// </summary> /// </summary>
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } } public float AverageRoundtripTime { get { return (float)m_averageRoundtripTime; } }
/// <summary> /// <summary>
/// Time offset between this peer and the remote peer /// Time offset between this peer and the remote peer
@@ -46,13 +46,13 @@ namespace Lidgren.Network
internal void InitializePing() internal void InitializePing()
{ {
float now = (float)NetTime.Now; double now = NetTime.Now;
// randomize ping sent time (0.25 - 1.0 x ping interval) // randomize ping sent time (0.25 - 1.0 x ping interval)
m_sentPingTime = now; m_sentPingTime = now;
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while 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 m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0); // initially allow a little more time
// make it better, quick :-) // make it better, quick :-)
SendPing(); SendPing();
@@ -64,7 +64,7 @@ namespace Lidgren.Network
m_sentPingNumber++; m_sentPingNumber++;
m_sentPingTime = (float)NetTime.Now; m_sentPingTime = NetTime.Now;
NetOutgoingMessage om = m_peer.CreateMessage(1); NetOutgoingMessage om = m_peer.CreateMessage(1);
om.Write((byte)m_sentPingNumber); // truncating to 0-255 om.Write((byte)m_sentPingNumber); // truncating to 0-255
om.m_messageType = NetMessageType.Ping; om.m_messageType = NetMessageType.Ping;
@@ -74,6 +74,7 @@ namespace Lidgren.Network
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset); m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1); m_statistics.PacketSent(len, 1);
m_peer.Recycle(om);
} }
internal void SendPong(int pingNumber) internal void SendPong(int pingNumber)
@@ -91,9 +92,10 @@ namespace Lidgren.Network
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset); m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1); m_statistics.PacketSent(len, 1);
m_peer.Recycle(om);
} }
internal void ReceivedPong(float now, int pongNumber, float remoteSendTime) internal void ReceivedPong(double now, int pongNumber, float remoteSendTime)
{ {
if ((byte)pongNumber != (byte)m_sentPingNumber) if ((byte)pongNumber != (byte)m_sentPingNumber)
{ {
@@ -103,7 +105,7 @@ namespace Lidgren.Network
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout; m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
float rtt = now - m_sentPingTime; double rtt = now - m_sentPingTime;
NetException.Assert(rtt >= 0); NetException.Assert(rtt >= 0);
double diff = (remoteSendTime + (rtt / 2.0)) - now; double diff = (remoteSendTime + (rtt / 2.0)) - now;
@@ -116,14 +118,14 @@ namespace Lidgren.Network
} }
else else
{ {
m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f); m_averageRoundtripTime = (m_averageRoundtripTime * 0.7) + (rtt * 0.3);
m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber; m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber;
m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")"); m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")");
} }
// update resend delay for all channels // update resend delay for all channels
float resendDelay = GetResendDelay(); double resendDelay = GetResendDelay();
foreach (var chan in m_sendChannels) foreach (var chan in m_sendChannels)
{ {
var rchan = chan as NetReliableSenderChannel; var rchan = chan as NetReliableSenderChannel;
@@ -139,7 +141,7 @@ namespace Lidgren.Network
NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4); NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4);
update.m_senderConnection = this; update.m_senderConnection = this;
update.m_senderEndPoint = this.m_remoteEndPoint; update.m_senderEndPoint = this.m_remoteEndPoint;
update.Write(rtt); update.Write((float)rtt);
m_peer.ReleaseMessage(update); m_peer.ReleaseMessage(update);
} }
} }
+8 -1
View File
@@ -24,6 +24,11 @@ namespace Lidgren.Network
internal int m_currentMTU; internal int m_currentMTU;
/// <summary>
/// Gets the current MTU in bytes. If PeerConfiguration.AutoExpandMTU is false, this will be PeerConfiguration.MaximumTransmissionUnit.
/// </summary>
public int CurrentMTU { get { return m_currentMTU; } }
internal void InitExpandMTU(double now) internal void InitExpandMTU(double now)
{ {
m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.5f + m_averageRoundtripTime; // wait a tiny bit before starting to expand mtu m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.5f + m_averageRoundtripTime; // wait a tiny bit before starting to expand mtu
@@ -128,6 +133,7 @@ namespace Lidgren.Network
m_lastSentMTUAttemptTime = now; m_lastSentMTUAttemptTime = now;
m_statistics.PacketSent(len, 1); m_statistics.PacketSent(len, 1);
m_peer.Recycle(om);
} }
private void FinalizeMTU(int size) private void FinalizeMTU(int size)
@@ -149,8 +155,9 @@ namespace Lidgren.Network
int len = om.Encode(m_peer.m_sendBuffer, 0, 0); int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool connectionReset; bool connectionReset;
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset); m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_peer.Recycle(om);
// m_peer.LogDebug("Received MTU expand request for " + size + " bytes"); //m_peer.LogDebug("Received MTU expand request for " + size + " bytes");
m_statistics.PacketSent(len, 1); m_statistics.PacketSent(len, 1);
} }
+113 -41
View File
@@ -3,12 +3,16 @@ using System.Net;
using System.Threading; using System.Threading;
using System.Diagnostics; using System.Diagnostics;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
/// Represents a connection to a remote peer /// Represents a connection to a remote peer
/// </summary> /// </summary>
[DebuggerDisplay("RemoteUniqueIdentifier={RemoteUniqueIdentifier} RemoteEndPoint={remoteEndPoint}")] [DebuggerDisplay("RemoteUniqueIdentifier={RemoteUniqueIdentifier} RemoteEndPoint={m_remoteEndPoint}")]
public partial class NetConnection public partial class NetConnection
{ {
private const int m_infrequentEventsSkipFrames = 8; // number of heartbeats to skip checking for infrequent events (ping, timeout etc) private const int m_infrequentEventsSkipFrames = 8; // number of heartbeats to skip checking for infrequent events (ping, timeout etc)
@@ -16,9 +20,10 @@ namespace Lidgren.Network
internal NetPeer m_peer; internal NetPeer m_peer;
internal NetPeerConfiguration m_peerConfiguration; internal NetPeerConfiguration m_peerConfiguration;
internal NetConnectionStatus m_status; internal NetConnectionStatus m_status; // actual status
internal NetConnectionStatus m_visibleStatus; internal NetConnectionStatus m_outputtedStatus; // status that has been sent as StatusChanged message
internal IPEndPoint m_remoteEndPoint; internal NetConnectionStatus m_visibleStatus; // status visible by querying the Status property
internal NetEndPoint m_remoteEndPoint;
internal NetSenderChannelBase[] m_sendChannels; internal NetSenderChannelBase[] m_sendChannels;
internal NetReceiverChannelBase[] m_receiveChannels; internal NetReceiverChannelBase[] m_receiveChannels;
internal NetOutgoingMessage m_localHailMessage; internal NetOutgoingMessage m_localHailMessage;
@@ -57,7 +62,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Gets the remote endpoint for the connection /// Gets the remote endpoint for the connection
/// </summary> /// </summary>
public IPEndPoint RemoteEndPoint { get { return m_remoteEndPoint; } } public NetEndPoint RemoteEndPoint { get { return m_remoteEndPoint; } }
/// <summary> /// <summary>
/// Gets the unique identifier of the remote NetPeer for this connection /// Gets the unique identifier of the remote NetPeer for this connection
@@ -70,19 +75,20 @@ namespace Lidgren.Network
public NetOutgoingMessage LocalHailMessage { get { return m_localHailMessage; } } public NetOutgoingMessage LocalHailMessage { get { return m_localHailMessage; } }
// gets the time before automatically resending an unacked message // gets the time before automatically resending an unacked message
internal float GetResendDelay() internal double GetResendDelay()
{ {
float avgRtt = m_averageRoundtripTime; double avgRtt = m_averageRoundtripTime;
if (avgRtt <= 0) if (avgRtt <= 0)
avgRtt = 0.1f; // "default" resend is based on 100 ms roundtrip time avgRtt = 0.1; // "default" resend is based on 100 ms roundtrip time
return 0.02f + (avgRtt * 2.0f); // 20 ms + double rtt return 0.025 + (avgRtt * 2.1); // 25 ms + double rtt
} }
internal NetConnection(NetPeer peer, IPEndPoint remoteEndPoint) internal NetConnection(NetPeer peer, NetEndPoint remoteEndPoint)
{ {
m_peer = peer; m_peer = peer;
m_peerConfiguration = m_peer.Configuration; m_peerConfiguration = m_peer.Configuration;
m_status = NetConnectionStatus.None; m_status = NetConnectionStatus.None;
m_outputtedStatus = NetConnectionStatus.None;
m_visibleStatus = NetConnectionStatus.None; m_visibleStatus = NetConnectionStatus.None;
m_remoteEndPoint = remoteEndPoint; m_remoteEndPoint = remoteEndPoint;
m_sendChannels = new NetSenderChannelBase[NetConstants.NumTotalChannels]; m_sendChannels = new NetSenderChannelBase[NetConstants.NumTotalChannels];
@@ -97,45 +103,52 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Change the internal endpoint to this new one. Used when, during handshake, a switch in port is detected (due to NAT) /// Change the internal endpoint to this new one. Used when, during handshake, a switch in port is detected (due to NAT)
/// </summary> /// </summary>
internal void MutateEndPoint(IPEndPoint endPoint) internal void MutateEndPoint(NetEndPoint endPoint)
{ {
m_remoteEndPoint = endPoint; m_remoteEndPoint = endPoint;
} }
internal void ResetTimeout(double now)
{
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
}
internal void SetStatus(NetConnectionStatus status, string reason) internal void SetStatus(NetConnectionStatus status, string reason)
{ {
// user or library thread // user or library thread
if (status == m_status)
return;
m_status = status; m_status = status;
if (reason == null) if (reason == null)
reason = string.Empty; reason = string.Empty;
if (m_status == NetConnectionStatus.Connected) if (m_status == NetConnectionStatus.Connected)
{ {
m_timeoutDeadline = (float)NetTime.Now + m_peerConfiguration.m_connectionTimeout; m_timeoutDeadline = NetTime.Now + m_peerConfiguration.m_connectionTimeout;
m_peer.LogVerbose("Timeout deadline initialized to " + m_timeoutDeadline); m_peer.LogVerbose("Timeout deadline initialized to " + m_timeoutDeadline);
} }
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.StatusChanged)) if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.StatusChanged))
{ {
NetIncomingMessage info = m_peer.CreateIncomingMessage(NetIncomingMessageType.StatusChanged, 4 + reason.Length + (reason.Length > 126 ? 2 : 1)); if (m_outputtedStatus != status)
info.m_senderConnection = this; {
info.m_senderEndPoint = m_remoteEndPoint; NetIncomingMessage info = m_peer.CreateIncomingMessage(NetIncomingMessageType.StatusChanged, 4 + reason.Length + (reason.Length > 126 ? 2 : 1));
info.Write((byte)m_status); info.m_senderConnection = this;
info.Write(reason); info.m_senderEndPoint = m_remoteEndPoint;
m_peer.ReleaseMessage(info); info.Write((byte)m_status);
info.Write(reason);
m_peer.ReleaseMessage(info);
m_outputtedStatus = status;
}
} }
else else
{ {
// app dont want those messages, update visible status immediately // app dont want those messages, update visible status immediately
m_outputtedStatus = m_status;
m_visibleStatus = m_status; m_visibleStatus = m_status;
} }
} }
internal void Heartbeat(float now, uint frameCounter) internal void Heartbeat(double now, uint frameCounter)
{ {
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
@@ -150,6 +163,7 @@ namespace Lidgren.Network
// //
m_peer.LogVerbose("Connection timed out at " + now + " deadline was " + m_timeoutDeadline); m_peer.LogVerbose("Connection timed out at " + now + " deadline was " + m_timeoutDeadline);
ExecuteDisconnect("Connection timed out", true); ExecuteDisconnect("Connection timed out", true);
return;
} }
// send ping? // send ping?
@@ -164,7 +178,7 @@ namespace Lidgren.Network
if (m_disconnectRequested) if (m_disconnectRequested)
{ {
ExecuteDisconnect(m_disconnectMessage, true); ExecuteDisconnect(m_disconnectMessage, m_disconnectReqSendBye);
return; return;
} }
} }
@@ -233,8 +247,11 @@ namespace Lidgren.Network
{ {
//m_peer.LogVerbose("Received ack for " + acktp + "#" + seqNr); //m_peer.LogVerbose("Received ack for " + acktp + "#" + seqNr);
NetSenderChannelBase chan = m_sendChannels[(int)incAck.Item1 - 1]; NetSenderChannelBase chan = m_sendChannels[(int)incAck.Item1 - 1];
// If we haven't sent a message on this channel there is no reason to ack it
if (chan == null) if (chan == null)
chan = CreateSenderChannel(incAck.Item1); continue;
chan.ReceiveAcknowledge(now, incAck.Item2); chan.ReceiveAcknowledge(now, incAck.Item2);
} }
} }
@@ -249,7 +266,11 @@ namespace Lidgren.Network
var channel = m_sendChannels[i]; var channel = m_sendChannels[i];
NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0); NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
if (channel != null) if (channel != null)
{
channel.SendQueuedMessages(now); channel.SendQueuedMessages(now);
if (channel.NeedToSendMessages())
m_peer.m_needFlushSendQueue = true; // failed to send all queued sends; likely a full window - need to try again
}
NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0); NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
} }
} }
@@ -275,24 +296,41 @@ namespace Lidgren.Network
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
int sz = om.GetEncodedSize(); int sz = om.GetEncodedSize();
if (sz > m_currentMTU) //if (sz > m_currentMTU)
m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!"); // m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");
bool connReset; // TODO: handle connection reset
// can fit this message together with previously written to buffer?
if (m_sendBufferWritePtr + sz > m_currentMTU) if (m_sendBufferWritePtr + sz > m_currentMTU)
{ {
bool connReset; // TODO: handle connection reset if (m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0)
NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); // or else the message should have been fragmented earlier {
// previous message in buffer; send these first
m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
m_sendBufferWritePtr = 0;
m_sendBufferNumMessages = 0;
}
}
// encode it into buffer regardless if it (now) fits within MTU or not
m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
m_sendBufferNumMessages++;
if (m_sendBufferWritePtr > m_currentMTU)
{
// send immediately; we're already over MTU
m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset); m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset);
m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages); m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
m_sendBufferWritePtr = 0; m_sendBufferWritePtr = 0;
m_sendBufferNumMessages = 0; m_sendBufferNumMessages = 0;
} }
m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr); if (m_sendBufferWritePtr > 0)
m_sendBufferNumMessages++; m_peer.m_needFlushSendQueue = true; // flush in heartbeat
NetException.Assert(m_sendBufferWritePtr > 0, "Encoded zero size message?"); Interlocked.Decrement(ref om.m_recyclingCount);
NetException.Assert(m_sendBufferNumMessages > 0);
} }
/// <summary> /// <summary>
@@ -321,12 +359,12 @@ namespace Lidgren.Network
if (chan == null) if (chan == null)
chan = CreateSenderChannel(tp); chan = CreateSenderChannel(tp);
if (msg.GetEncodedSize() > m_currentMTU) if ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.UnreliableSequenced) && msg.GetEncodedSize() > m_currentMTU)
throw new NetException("Message too large! Fragmentation failure?"); m_peer.ThrowOrLog("Reliable message too large! Fragmentation failure?");
var retval = chan.Enqueue(msg); var retval = chan.Enqueue(msg);
if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false) //if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false)
retval = NetSendResult.Queued; // queued since we're not autoflushing // retval = NetSendResult.Queued; // queued since we're not autoflushing
return retval; return retval;
} }
@@ -347,12 +385,11 @@ namespace Lidgren.Network
} }
else else
{ {
switch (method) switch (method)
{ {
case NetDeliveryMethod.Unreliable: case NetDeliveryMethod.Unreliable:
case NetDeliveryMethod.UnreliableSequenced: case NetDeliveryMethod.UnreliableSequenced:
chan = new NetUnreliableSenderChannel(this, NetUtility.GetWindowSize(method)); chan = new NetUnreliableSenderChannel(this, NetUtility.GetWindowSize(method), method);
break; break;
case NetDeliveryMethod.ReliableOrdered: case NetDeliveryMethod.ReliableOrdered:
chan = new NetReliableSenderChannel(this, NetUtility.GetWindowSize(method)); chan = new NetReliableSenderChannel(this, NetUtility.GetWindowSize(method));
@@ -375,13 +412,34 @@ namespace Lidgren.Network
{ {
m_peer.VerifyNetworkThread(); m_peer.VerifyNetworkThread();
float now = (float)NetTime.Now; double now = NetTime.Now;
switch (tp) switch (tp)
{ {
case NetMessageType.Connect:
m_peer.LogDebug("Received handshake message (" + tp + ") despite connection being in place");
break;
case NetMessageType.ConnectResponse:
// handshake message must have been lost
HandleConnectResponse(now, tp, ptr, payloadLength);
break;
case NetMessageType.ConnectionEstablished:
// do nothing, all's well
break;
case NetMessageType.LibraryError:
m_peer.ThrowOrLog("LibraryError received by ReceivedLibraryMessage; this usually indicates a malformed message");
break;
case NetMessageType.Disconnect: case NetMessageType.Disconnect:
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength); NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
ExecuteDisconnect(msg.ReadString(), false);
m_disconnectRequested = true;
m_disconnectMessage = msg.ReadString();
m_disconnectReqSendBye = false;
//ExecuteDisconnect(msg.ReadString(), false);
break; break;
case NetMessageType.Acknowledge: case NetMessageType.Acknowledge:
for (int i = 0; i < payloadLength; i+=3) for (int i = 0; i < payloadLength; i+=3)
@@ -408,6 +466,11 @@ namespace Lidgren.Network
SendMTUSuccess(payloadLength); SendMTUSuccess(payloadLength);
break; break;
case NetMessageType.ExpandMTUSuccess: case NetMessageType.ExpandMTUSuccess:
if (m_peer.Configuration.AutoExpandMTU == false)
{
m_peer.LogDebug("Received ExpandMTURequest altho AutoExpandMTU is turned off!");
break;
}
NetIncomingMessage emsg = m_peer.SetupReadHelperMessage(ptr, payloadLength); NetIncomingMessage emsg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
int size = emsg.ReadInt32(); int size = emsg.ReadInt32();
HandleExpandMTUSuccess(now, size); HandleExpandMTUSuccess(now, size);
@@ -492,10 +555,19 @@ namespace Lidgren.Network
} }
windowSize = chan.WindowSize; windowSize = chan.WindowSize;
freeWindowSlots = chan.GetAllowedSends() - chan.m_queuedSends.Count; freeWindowSlots = chan.GetFreeWindowSlots();
return; return;
} }
public bool CanSendImmediately(NetDeliveryMethod method, int sequenceChannel)
{
int channelSlot = (int)method - 1 + sequenceChannel;
var chan = m_sendChannels[channelSlot];
if (chan == null)
return true;
return chan.GetFreeWindowSlots() > 0;
}
internal void Shutdown(string reason) internal void Shutdown(string reason)
{ {
ExecuteDisconnect(reason, true); ExecuteDisconnect(reason, true);
+57 -48
View File
@@ -40,17 +40,19 @@ namespace Lidgren.Network
{ {
private readonly NetConnection m_connection; private readonly NetConnection m_connection;
internal int m_sentPackets; internal long m_sentPackets;
internal int m_receivedPackets; internal long m_receivedPackets;
internal int m_sentMessages; internal long m_sentMessages;
internal int m_receivedMessages; internal long m_receivedMessages;
internal long m_droppedMessages;
internal long m_receivedFragments;
internal int m_sentBytes; internal long m_sentBytes;
internal int m_receivedBytes; internal long m_receivedBytes;
internal int m_resentMessagesDueToDelay; internal long m_resentMessagesDueToDelay;
internal int m_resentMessagesDueToHole; internal long m_resentMessagesDueToHole;
internal NetConnectionStatistics(NetConnection conn) internal NetConnectionStatistics(NetConnection conn)
{ {
@@ -62,38 +64,60 @@ namespace Lidgren.Network
{ {
m_sentPackets = 0; m_sentPackets = 0;
m_receivedPackets = 0; m_receivedPackets = 0;
m_sentMessages = 0;
m_receivedMessages = 0;
m_receivedFragments = 0;
m_sentBytes = 0; m_sentBytes = 0;
m_receivedBytes = 0; m_receivedBytes = 0;
m_resentMessagesDueToDelay = 0;
m_resentMessagesDueToHole = 0;
} }
/// <summary> /// <summary>
/// Gets the number of sent packets for this connection /// Gets the number of sent packets for this connection
/// </summary> /// </summary>
public int SentPackets { get { return m_sentPackets; } } public long SentPackets { get { return m_sentPackets; } }
/// <summary> /// <summary>
/// Gets the number of received packets for this connection /// Gets the number of received packets for this connection
/// </summary> /// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } } public long ReceivedPackets { get { return m_receivedPackets; } }
/// <summary> /// <summary>
/// Gets the number of sent bytes for this connection /// Gets the number of sent bytes for this connection
/// </summary> /// </summary>
public int SentBytes { get { return m_sentBytes; } } public long SentBytes { get { return m_sentBytes; } }
/// <summary> /// <summary>
/// Gets the number of received bytes for this connection /// Gets the number of received bytes for this connection
/// </summary> /// </summary>
public int ReceivedBytes { get { return m_receivedBytes; } } public long ReceivedBytes { get { return m_receivedBytes; } }
/// <summary>
/// Gets the number of sent messages for this connection
/// </summary>
public long SentMessages { get { return m_sentMessages; } }
/// <summary>
/// Gets the number of received messages for this connection
/// </summary>
public long ReceivedMessages { get { return m_receivedMessages; } }
/// <summary> /// <summary>
/// Gets the number of resent reliable messages for this connection /// Gets the number of resent reliable messages for this connection
/// </summary> /// </summary>
public int ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } } public long ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } }
/// <summary>
/// Gets the number of dropped messages for this connection
/// </summary>
public long DroppedMessages { get { return m_droppedMessages; } }
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } } // public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
#if USE_RELEASE_STATISTICS #if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
#endif
internal void PacketSent(int numBytes, int numMessages) internal void PacketSent(int numBytes, int numMessages)
{ {
NetException.Assert(numBytes > 0 && numMessages > 0); NetException.Assert(numBytes > 0 && numMessages > 0);
@@ -101,37 +125,22 @@ namespace Lidgren.Network
m_sentBytes += numBytes; m_sentBytes += numBytes;
m_sentMessages += numMessages; m_sentMessages += numMessages;
} }
#else
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS #if !USE_RELEASE_STATISTICS
internal void PacketReceived(int numBytes, int numMessages) [Conditional("DEBUG")]
#endif
internal void PacketReceived(int numBytes, int numMessages, int numFragments)
{ {
NetException.Assert(numBytes > 0 && numMessages > 0); NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++; m_receivedPackets++;
m_receivedBytes += numBytes; m_receivedBytes += numBytes;
m_receivedMessages += numMessages; m_receivedMessages += numMessages;
m_receivedFragments += numFragments;
} }
#else
[Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS #if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
#endif
internal void MessageResent(MessageResendReason reason) internal void MessageResent(MessageResendReason reason)
{ {
if (reason == MessageResendReason.Delay) if (reason == MessageResendReason.Delay)
@@ -139,16 +148,14 @@ namespace Lidgren.Network
else else
m_resentMessagesDueToHole++; m_resentMessagesDueToHole++;
} }
#else
#if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")] [Conditional("DEBUG")]
internal void MessageResent(MessageResendReason reason)
{
if (reason == MessageResendReason.Delay)
m_resentMessagesDueToDelay++;
else
m_resentMessagesDueToHole++;
}
#endif #endif
internal void MessageDropped()
{
m_droppedMessages++;
}
/// <summary> /// <summary>
/// Returns a string that represents this object /// Returns a string that represents this object
@@ -157,12 +164,14 @@ namespace Lidgren.Network
{ {
StringBuilder bdr = new StringBuilder(); StringBuilder bdr = new StringBuilder();
//bdr.AppendLine("Average roundtrip time: " + NetTime.ToReadable(m_connection.m_averageRoundtripTime)); //bdr.AppendLine("Average roundtrip time: " + NetTime.ToReadable(m_connection.m_averageRoundtripTime));
bdr.AppendLine("Current MTU: " + m_connection.m_currentMTU);
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets"); bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets"); bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages (of which " + m_receivedFragments + " fragments) in " + m_receivedPackets + " packets");
bdr.AppendLine("Dropped " + m_droppedMessages + " messages (dupes/late/early)");
if (m_resentMessagesDueToDelay > 0) if (m_resentMessagesDueToDelay > 0)
bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay); bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay);
if (m_resentMessagesDueToDelay > 0) if (m_resentMessagesDueToHole > 0)
bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole); bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole);
int numUnsent = 0; int numUnsent = 0;
@@ -171,7 +180,7 @@ namespace Lidgren.Network
{ {
if (sendChan == null) if (sendChan == null)
continue; continue;
numUnsent += sendChan.m_queuedSends.Count; numUnsent += sendChan.QueuedSendsCount;
var relSendChan = sendChan as NetReliableSenderChannel; var relSendChan = sendChan as NetReliableSenderChannel;
if (relSendChan != null) if (relSendChan != null)
-9
View File
@@ -25,7 +25,6 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Exception thrown in the Lidgren Network Library /// Exception thrown in the Lidgren Network Library
/// </summary> /// </summary>
[Serializable]
public sealed class NetException : Exception public sealed class NetException : Exception
{ {
/// <summary> /// <summary>
@@ -52,14 +51,6 @@ namespace Lidgren.Network
{ {
} }
/// <summary>
/// NetException constructor
/// </summary>
private NetException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary> /// <summary>
/// Throws an exception, in DEBUG only, if first parameter is false /// Throws an exception, in DEBUG only, if first parameter is false
/// </summary> /// </summary>
+8 -4
View File
@@ -20,6 +20,10 @@ using System;
using System.Net; using System.Net;
using System.Diagnostics; using System.Diagnostics;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
@@ -29,7 +33,7 @@ namespace Lidgren.Network
public sealed class NetIncomingMessage : NetBuffer public sealed class NetIncomingMessage : NetBuffer
{ {
internal NetIncomingMessageType m_incomingMessageType; internal NetIncomingMessageType m_incomingMessageType;
internal IPEndPoint m_senderEndPoint; internal NetEndPoint m_senderEndPoint;
internal NetConnection m_senderConnection; internal NetConnection m_senderConnection;
internal int m_sequenceNumber; internal int m_sequenceNumber;
internal NetMessageType m_receivedMessageType; internal NetMessageType m_receivedMessageType;
@@ -52,9 +56,9 @@ namespace Lidgren.Network
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } } public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
/// <summary> /// <summary>
/// IPEndPoint of sender, if any /// endpoint of sender, if any
/// </summary> /// </summary>
public IPEndPoint SenderEndPoint { get { return m_senderEndPoint; } } public NetEndPoint SenderEndPoint { get { return m_senderEndPoint; } }
/// <summary> /// <summary>
/// NetConnection of sender, if any /// NetConnection of sender, if any
@@ -90,7 +94,7 @@ namespace Lidgren.Network
/// </summary> /// </summary>
/// <param name="encryption">The encryption algorithm used to encrypt the message</param> /// <param name="encryption">The encryption algorithm used to encrypt the message</param>
/// <returns>true on success</returns> /// <returns>true on success</returns>
public bool Decrypt(INetEncryption encryption) public bool Decrypt(NetEncryption encryption)
{ {
return encryption.Decrypt(this); return encryption.Decrypt(this);
} }
+38 -25
View File
@@ -1,6 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
@@ -10,29 +15,31 @@ namespace Lidgren.Network
/// Send NetIntroduction to hostExternal and clientExternal; introducing client to host /// Send NetIntroduction to hostExternal and clientExternal; introducing client to host
/// </summary> /// </summary>
public void Introduce( public void Introduce(
IPEndPoint hostInternal, NetEndPoint hostInternal,
IPEndPoint hostExternal, NetEndPoint hostExternal,
IPEndPoint clientInternal, NetEndPoint clientInternal,
IPEndPoint clientExternal, NetEndPoint clientExternal,
string token) string token)
{ {
// send message to client // send message to client
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1); NetOutgoingMessage um = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction; um.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)0); um.Write((byte)0);
msg.Write(hostInternal); um.Write(hostInternal);
msg.Write(hostExternal); um.Write(hostExternal);
msg.Write(token); um.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg)); Interlocked.Increment(ref um.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(clientExternal, um));
// send message to host // send message to host
msg = CreateMessage(10 + token.Length + 1); um = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction; um.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)1); um.Write((byte)1);
msg.Write(clientInternal); um.Write(clientInternal);
msg.Write(clientExternal); um.Write(clientExternal);
msg.Write(token); um.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg)); Interlocked.Increment(ref um.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(hostExternal, um));
} }
/// <summary> /// <summary>
@@ -46,8 +53,8 @@ namespace Lidgren.Network
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
byte hostByte = tmp.ReadByte(); byte hostByte = tmp.ReadByte();
IPEndPoint remoteInternal = tmp.ReadIPEndPoint(); NetEndPoint remoteInternal = tmp.ReadIPEndPoint();
IPEndPoint remoteExternal = tmp.ReadIPEndPoint(); NetEndPoint remoteExternal = tmp.ReadIPEndPoint();
string token = tmp.ReadString(); string token = tmp.ReadString();
bool isHost = (hostByte != 0); bool isHost = (hostByte != 0);
@@ -63,20 +70,25 @@ namespace Lidgren.Network
punch.m_messageType = NetMessageType.NatPunchMessage; punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte); punch.Write(hostByte);
punch.Write(token); punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteInternal, punch)); Interlocked.Increment(ref punch.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(remoteInternal, punch));
LogDebug("NAT punch sent to " + remoteInternal);
// send external punch // send external punch
punch = CreateMessage(1); punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage; punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte); punch.Write(hostByte);
punch.Write(token); punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteExternal, punch)); Interlocked.Increment(ref punch.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(remoteExternal, punch));
LogDebug("NAT punch sent to " + remoteExternal);
} }
/// <summary> /// <summary>
/// Called when receiving a NatPunchMessage from a remote endpoint /// Called when receiving a NatPunchMessage from a remote endpoint
/// </summary> /// </summary>
private void HandleNatPunch(int ptr, IPEndPoint senderEndPoint) private void HandleNatPunch(int ptr, NetEndPoint senderEndPoint)
{ {
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
@@ -92,7 +104,7 @@ namespace Lidgren.Network
LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token); LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token);
// //
// Release punch success to client; enabling him to Connect() to msg.SenderIPEndPoint if token is ok // Release punch success to client; enabling him to Connect() to msg.Sender if token is ok
// //
NetIncomingMessage punchSuccess = CreateIncomingMessage(NetIncomingMessageType.NatIntroductionSuccess, 10); NetIncomingMessage punchSuccess = CreateIncomingMessage(NetIncomingMessageType.NatIntroductionSuccess, 10);
punchSuccess.m_senderEndPoint = senderEndPoint; punchSuccess.m_senderEndPoint = senderEndPoint;
@@ -104,7 +116,8 @@ namespace Lidgren.Network
punch.m_messageType = NetMessageType.NatPunchMessage; punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write((byte)0); punch.Write((byte)0);
punch.Write(token); punch.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(senderEndPoint, punch)); Interlocked.Increment(ref punch.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(senderEndPoint, punch));
} }
} }
} }
+9 -2
View File
@@ -30,6 +30,13 @@ namespace Lidgren.Network
{ {
internal NetMessageType m_messageType; internal NetMessageType m_messageType;
internal bool m_isSent; internal bool m_isSent;
// Recycling count is:
// * incremented for each recipient on send
// * incremented, when reliable, in SenderChannel.ExecuteSend()
// * decremented (both reliable and unreliable) in NetConnection.QueueSendMessage()
// * decremented, when reliable, in SenderChannel.DestoreMessage()
// ... when it reaches zero it can be recycled
internal int m_recyclingCount; internal int m_recyclingCount;
internal int m_fragmentGroup; // which group of fragments ths belongs to internal int m_fragmentGroup; // which group of fragments ths belongs to
@@ -46,7 +53,7 @@ namespace Lidgren.Network
m_messageType = NetMessageType.LibraryError; m_messageType = NetMessageType.LibraryError;
m_bitLength = 0; m_bitLength = 0;
m_isSent = false; m_isSent = false;
m_recyclingCount = 0; NetException.Assert(m_recyclingCount == 0);
m_fragmentGroup = 0; m_fragmentGroup = 0;
} }
@@ -116,7 +123,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt! /// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
/// </summary> /// </summary>
public bool Encrypt(INetEncryption encryption) public bool Encrypt(NetEncryption encryption)
{ {
return encryption.Encrypt(this); return encryption.Encrypt(this);
} }
+18 -9
View File
@@ -1,5 +1,10 @@
using System; using System;
using System.Net; using System.Net;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
@@ -10,9 +15,11 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public void DiscoverLocalPeers(int serverPort) public void DiscoverLocalPeers(int serverPort)
{ {
NetOutgoingMessage om = CreateMessage(0); NetOutgoingMessage um = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery; um.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om)); Interlocked.Increment(ref um.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(new NetEndPoint(NetUtility.GetBroadcastAddress(), serverPort), um));
} }
/// <summary> /// <summary>
@@ -20,27 +27,28 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public bool DiscoverKnownPeer(string host, int serverPort) public bool DiscoverKnownPeer(string host, int serverPort)
{ {
IPAddress address = NetUtility.Resolve(host); var address = NetUtility.Resolve(host);
if (address == null) if (address == null)
return false; return false;
DiscoverKnownPeer(new IPEndPoint(address, serverPort)); DiscoverKnownPeer(new NetEndPoint(address, serverPort));
return true; return true;
} }
/// <summary> /// <summary>
/// Emit a discovery signal to a single known host /// Emit a discovery signal to a single known host
/// </summary> /// </summary>
public void DiscoverKnownPeer(IPEndPoint endPoint) public void DiscoverKnownPeer(NetEndPoint endPoint)
{ {
NetOutgoingMessage om = CreateMessage(0); NetOutgoingMessage om = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery; om.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(endPoint, om)); om.m_recyclingCount = 1;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(endPoint, om));
} }
/// <summary> /// <summary>
/// Send a discovery response message /// Send a discovery response message
/// </summary> /// </summary>
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient) public void SendDiscoveryResponse(NetOutgoingMessage msg, NetEndPoint recipient)
{ {
if (recipient == null) if (recipient == null)
throw new ArgumentNullException("recipient"); throw new ArgumentNullException("recipient");
@@ -54,7 +62,8 @@ namespace Lidgren.Network
throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)"); throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)");
msg.m_messageType = NetMessageType.DiscoveryResponse; msg.m_messageType = NetMessageType.DiscoveryResponse;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg)); Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(recipient, msg));
} }
} }
} }
+14 -6
View File
@@ -6,7 +6,7 @@ namespace Lidgren.Network
{ {
internal class ReceivedFragmentGroup internal class ReceivedFragmentGroup
{ {
public float LastReceived; //public float LastReceived;
public byte[] Data; public byte[] Data;
public NetBitVector ReceivedChunks; public NetBitVector ReceivedChunks;
} }
@@ -18,7 +18,7 @@ namespace Lidgren.Network
private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups; private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups;
// on user thread // on user thread
private void SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel) private NetSendResult SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{ {
// Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients; // Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients;
// this should be ok however; as long as recipients differentiate between same id but different sender // this should be ok however; as long as recipients differentiate between same id but different sender
@@ -44,11 +44,13 @@ namespace Lidgren.Network
if (numChunks * bytesPerChunk < totalBytes) if (numChunks * bytesPerChunk < totalBytes)
numChunks++; numChunks++;
NetSendResult retval = NetSendResult.Sent;
int bitsPerChunk = bytesPerChunk * 8; int bitsPerChunk = bytesPerChunk * 8;
int bitsLeft = msg.LengthBits; int bitsLeft = msg.LengthBits;
for (int i = 0; i < numChunks; i++) for (int i = 0; i < numChunks; i++)
{ {
NetOutgoingMessage chunk = CreateMessage(mtu); NetOutgoingMessage chunk = CreateMessage(0);
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft); chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
chunk.m_data = msg.m_data; chunk.m_data = msg.m_data;
@@ -63,12 +65,18 @@ namespace Lidgren.Network
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count); Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
foreach (NetConnection recipient in recipients) foreach (NetConnection recipient in recipients)
recipient.EnqueueMessage(chunk, method, sequenceChannel); {
var res = recipient.EnqueueMessage(chunk, method, sequenceChannel);
if (res == NetSendResult.Dropped)
Interlocked.Decrement(ref chunk.m_recyclingCount);
if ((int)res > (int)retval)
retval = res; // return "worst" result
}
bitsLeft -= bitsPerChunk; bitsLeft -= bitsPerChunk;
} }
return; return retval;
} }
private void HandleReleasedFragment(NetIncomingMessage im) private void HandleReleasedFragment(NetIncomingMessage im)
@@ -126,7 +134,7 @@ namespace Lidgren.Network
} }
info.ReceivedChunks[chunkNumber] = true; info.ReceivedChunks[chunkNumber] = true;
info.LastReceived = (float)NetTime.Now; //info.LastReceived = (float)NetTime.Now;
// copy to data // copy to data
int offset = (chunkNumber * chunkByteSize); int offset = (chunkNumber * chunkByteSize);
+179 -122
View File
@@ -1,8 +1,4 @@
#if !__ANDROID__ && !IOS using System;
#define IS_MAC_AVAILABLE
#endif
using System;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Diagnostics; using System.Diagnostics;
@@ -10,6 +6,10 @@ using System.Security.Cryptography;
using System.Net.Sockets; using System.Net.Sockets;
using System.Collections.Generic; using System.Collections.Generic;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
public partial class NetPeer public partial class NetPeer
@@ -24,13 +24,15 @@ namespace Lidgren.Network
private object m_initializeLock = new object(); private object m_initializeLock = new object();
private uint m_frameCounter; private uint m_frameCounter;
private double m_lastHeartbeat; private double m_lastHeartbeat;
private double m_lastSocketBind = float.MinValue;
private NetUPnP m_upnp; private NetUPnP m_upnp;
internal bool m_needFlushSendQueue;
internal readonly NetPeerConfiguration m_configuration; internal readonly NetPeerConfiguration m_configuration;
private readonly NetQueue<NetIncomingMessage> m_releasedIncomingMessages; private readonly NetQueue<NetIncomingMessage> m_releasedIncomingMessages;
internal readonly NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>> m_unsentUnconnectedMessages; internal readonly NetQueue<NetTuple<NetEndPoint, NetOutgoingMessage>> m_unsentUnconnectedMessages;
internal Dictionary<IPEndPoint, NetConnection> m_handshakes; internal Dictionary<NetEndPoint, NetConnection> m_handshakes;
internal readonly NetPeerStatistics m_statistics; internal readonly NetPeerStatistics m_statistics;
internal long m_uniqueIdentifier; internal long m_uniqueIdentifier;
@@ -47,13 +49,15 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Call this to register a callback for when a new message arrives /// Call this to register a callback for when a new message arrives
/// </summary> /// </summary>
public void RegisterReceivedCallback(SendOrPostCallback callback) public void RegisterReceivedCallback(SendOrPostCallback callback, SynchronizationContext syncContext = null)
{ {
if (SynchronizationContext.Current == null) if (syncContext == null)
syncContext = SynchronizationContext.Current;
if (syncContext == null)
throw new NetException("Need a SynchronizationContext to register callback on correct thread!"); throw new NetException("Need a SynchronizationContext to register callback on correct thread!");
if (m_receiveCallbacks == null) if (m_receiveCallbacks == null)
m_receiveCallbacks = new List<NetTuple<SynchronizationContext, SendOrPostCallback>>(); m_receiveCallbacks = new List<NetTuple<SynchronizationContext, SendOrPostCallback>>();
m_receiveCallbacks.Add(new NetTuple<SynchronizationContext, SendOrPostCallback>(SynchronizationContext.Current, callback)); m_receiveCallbacks.Add(new NetTuple<SynchronizationContext, SendOrPostCallback>(syncContext, callback));
} }
/// <summary> /// <summary>
@@ -63,7 +67,17 @@ namespace Lidgren.Network
{ {
if (m_receiveCallbacks == null) if (m_receiveCallbacks == null)
return; return;
m_receiveCallbacks.Remove(new NetTuple<SynchronizationContext, SendOrPostCallback>(SynchronizationContext.Current, callback));
// remove all callbacks regardless of sync context
RestartRemoveCallbacks:
for (int i = 0; i < m_receiveCallbacks.Count; i++)
{
if (m_receiveCallbacks[i].Item2.Equals(callback))
{
m_receiveCallbacks.RemoveAt(i);
goto RestartRemoveCallbacks;
}
}
if (m_receiveCallbacks.Count < 1) if (m_receiveCallbacks.Count < 1)
m_receiveCallbacks = null; m_receiveCallbacks = null;
} }
@@ -86,10 +100,59 @@ namespace Lidgren.Network
if (m_receiveCallbacks != null) if (m_receiveCallbacks != null)
{ {
foreach (var tuple in m_receiveCallbacks) foreach (var tuple in m_receiveCallbacks)
tuple.Item1.Post(tuple.Item2, this); {
try
{
tuple.Item1.Post(tuple.Item2, this);
}
catch (Exception ex)
{
LogWarning("Receive callback exception:" + ex);
}
}
} }
} }
private void BindSocket(bool reBind)
{
double now = NetTime.Now;
if (now - m_lastSocketBind < 1.0)
{
LogDebug("Suppressed socket rebind; last bound " + (now - m_lastSocketBind) + " seconds ago");
return; // only allow rebind once every second
}
m_lastSocketBind = now;
if (m_socket == null)
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
if (reBind)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1);
m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize;
m_socket.SendBufferSize = m_configuration.SendBufferSize;
m_socket.Blocking = false;
var ep = (EndPoint)new NetEndPoint(m_configuration.LocalAddress, reBind ? m_listenPort : m_configuration.Port);
m_socket.Bind(ep);
try
{
const uint IOC_IN = 0x80000000;
const uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
m_socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
}
catch
{
// ignore; SIO_UDP_CONNRESET not supported on this platform
}
var boundEp = m_socket.LocalEndPoint as NetEndPoint;
LogDebug("Socket bound to " + boundEp + ": " + m_socket.IsBound);
m_listenPort = boundEp.Port;
}
private void InitializeNetwork() private void InitializeNetwork()
{ {
lock (m_initializeLock) lock (m_initializeLock)
@@ -109,65 +172,21 @@ namespace Lidgren.Network
m_handshakes.Clear(); m_handshakes.Clear();
// bind to socket // bind to socket
IPEndPoint iep = null; BindSocket(false);
iep = new IPEndPoint(m_configuration.LocalAddress, m_configuration.Port);
EndPoint ep = (EndPoint)iep;
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize;
m_socket.SendBufferSize = m_configuration.SendBufferSize;
m_socket.Blocking = false;
m_socket.Bind(ep);
try
{
const uint IOC_IN = 0x80000000;
const uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
m_socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
}
catch
{
// ignore; SIO_UDP_CONNRESET not supported on this platform
}
IPEndPoint boundEp = m_socket.LocalEndPoint as IPEndPoint;
LogDebug("Socket bound to " + boundEp + ": " + m_socket.IsBound);
m_listenPort = boundEp.Port;
m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize]; m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize];
m_sendBuffer = new byte[m_configuration.SendBufferSize]; m_sendBuffer = new byte[m_configuration.SendBufferSize];
m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error); m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error);
m_readHelperMessage.m_data = m_receiveBuffer; m_readHelperMessage.m_data = m_receiveBuffer;
byte[] macBytes = new byte[8]; byte[] macBytes = NetUtility.GetMacAddressBytes();
NetRandom.Instance.NextBytes(macBytes);
#if IS_MAC_AVAILABLE var boundEp = m_socket.LocalEndPoint as NetEndPoint;
try
{
System.Net.NetworkInformation.PhysicalAddress pa = NetUtility.GetMacAddress();
if (pa != null)
{
macBytes = pa.GetAddressBytes();
LogVerbose("Mac address is " + NetUtility.ToHexString(macBytes));
}
else
{
LogWarning("Failed to get Mac address");
}
}
catch (NotSupportedException)
{
// not supported; lets just keep the random bytes set above
}
#endif
byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode()); byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode());
byte[] combined = new byte[epBytes.Length + macBytes.Length]; byte[] combined = new byte[epBytes.Length + macBytes.Length];
Array.Copy(epBytes, 0, combined, 0, epBytes.Length); Array.Copy(epBytes, 0, combined, 0, epBytes.Length);
Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length); Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length);
m_uniqueIdentifier = BitConverter.ToInt64(SHA1.Create().ComputeHash(combined), 0); m_uniqueIdentifier = BitConverter.ToInt64(NetUtility.ComputeSHAHash(combined), 0);
m_status = NetPeerStatus.Running; m_status = NetPeerStatus.Running;
} }
@@ -213,26 +232,26 @@ namespace Lidgren.Network
foreach (var conn in m_connections) foreach (var conn in m_connections)
if (conn != null) if (conn != null)
list.Add(conn); list.Add(conn);
lock (m_handshakes)
{
foreach (var hs in m_handshakes.Values)
if (hs != null)
list.Add(hs);
// shut down connections
foreach (NetConnection conn in list)
conn.Shutdown(m_shutdownReason);
}
} }
lock (m_handshakes)
{
foreach (var hs in m_handshakes.Values)
if (hs != null && list.Contains(hs) == false)
list.Add(hs);
}
// shut down connections
foreach (NetConnection conn in list)
conn.Shutdown(m_shutdownReason);
FlushDelayedPackets(); FlushDelayedPackets();
// one final heartbeat, will send stuff and do disconnect // one final heartbeat, will send stuff and do disconnect
Heartbeat(); Heartbeat();
Thread.Sleep(10); NetUtility.Sleep(10);
lock (m_initializeLock) lock (m_initializeLock)
{ {
try try
@@ -243,14 +262,19 @@ namespace Lidgren.Network
{ {
m_socket.Shutdown(SocketShutdown.Receive); m_socket.Shutdown(SocketShutdown.Receive);
} }
catch { } catch(Exception ex)
m_socket.Close(2); // 2 seconds timeout {
} LogDebug("Socket.Shutdown exception: " + ex.ToString());
if (m_messageReceivedEvent != null) }
{
m_messageReceivedEvent.Set(); try
m_messageReceivedEvent.Close(); {
m_messageReceivedEvent = null; m_socket.Close(2); // 2 seconds timeout
}
catch (Exception ex)
{
LogDebug("Socket.Close exception: " + ex.ToString());
}
} }
} }
finally finally
@@ -258,8 +282,13 @@ namespace Lidgren.Network
m_socket = null; m_socket = null;
m_status = NetPeerStatus.NotRunning; m_status = NetPeerStatus.NotRunning;
LogDebug("Shutdown complete"); LogDebug("Shutdown complete");
// wake up any threads waiting for server shutdown
if (m_messageReceivedEvent != null)
m_messageReceivedEvent.Set();
} }
m_lastSocketBind = float.MinValue;
m_receiveBuffer = null; m_receiveBuffer = null;
m_sendBuffer = null; m_sendBuffer = null;
m_unsentUnconnectedMessages.Clear(); m_unsentUnconnectedMessages.Clear();
@@ -275,10 +304,8 @@ namespace Lidgren.Network
{ {
VerifyNetworkThread(); VerifyNetworkThread();
double dnow = NetTime.Now; double now = NetTime.Now;
float now = (float)dnow; double delta = now - m_lastHeartbeat;
double delta = dnow - m_lastHeartbeat;
int maxCHBpS = 1250 - m_connections.Count; int maxCHBpS = 1250 - m_connections.Count;
if (maxCHBpS < 250) if (maxCHBpS < 250)
@@ -286,7 +313,7 @@ namespace Lidgren.Network
if (delta > (1.0 / (double)maxCHBpS) || delta < 0.0) // max connection heartbeats/second max if (delta > (1.0 / (double)maxCHBpS) || delta < 0.0) // max connection heartbeats/second max
{ {
m_frameCounter++; m_frameCounter++;
m_lastHeartbeat = dnow; m_lastHeartbeat = now;
// do handshake heartbeats // do handshake heartbeats
if ((m_frameCounter % 3) == 0) if ((m_frameCounter % 3) == 0)
@@ -320,44 +347,51 @@ namespace Lidgren.Network
#endif #endif
// update m_executeFlushSendQueue // update m_executeFlushSendQueue
if (m_configuration.m_autoFlushSendQueue) if (m_configuration.m_autoFlushSendQueue && m_needFlushSendQueue == true)
{
m_executeFlushSendQueue = true; m_executeFlushSendQueue = true;
m_needFlushSendQueue = false; // a race condition to this variable will simply result in a single superfluous call to FlushSendQueue()
}
// do connection heartbeats // do connection heartbeats
lock (m_connections) lock (m_connections)
{ {
foreach (NetConnection conn in m_connections) for (int i = m_connections.Count - 1; i >= 0; i--)
{ {
var conn = m_connections[i];
conn.Heartbeat(now, m_frameCounter); conn.Heartbeat(now, m_frameCounter);
if (conn.m_status == NetConnectionStatus.Disconnected) if (conn.m_status == NetConnectionStatus.Disconnected)
{ {
// //
// remove connection // remove connection
// //
m_connections.Remove(conn); m_connections.RemoveAt(i);
m_connectionLookup.Remove(conn.RemoteEndPoint); m_connectionLookup.Remove(conn.RemoteEndPoint);
break; // can't continue iteration here
} }
} }
} }
m_executeFlushSendQueue = false; m_executeFlushSendQueue = false;
// send unsent unconnected messages // send unsent unconnected messages
NetTuple<IPEndPoint, NetOutgoingMessage> unsent; NetTuple<NetEndPoint, NetOutgoingMessage> unsent;
while (m_unsentUnconnectedMessages.TryDequeue(out unsent)) while (m_unsentUnconnectedMessages.TryDequeue(out unsent))
{ {
NetOutgoingMessage om = unsent.Item2; NetOutgoingMessage om = unsent.Item2;
bool connReset;
int len = om.Encode(m_sendBuffer, 0, 0); int len = om.Encode(m_sendBuffer, 0, 0);
SendPacket(len, unsent.Item1, 1, out connReset);
Interlocked.Decrement(ref om.m_recyclingCount); Interlocked.Decrement(ref om.m_recyclingCount);
if (om.m_recyclingCount <= 0) if (om.m_recyclingCount <= 0)
Recycle(om); Recycle(om);
bool connReset;
SendPacket(len, unsent.Item1, 1, out connReset);
} }
} }
if (m_upnp != null)
m_upnp.CheckForDiscoveryTimeout();
// //
// read from socket // read from socket
// //
@@ -371,8 +405,7 @@ namespace Lidgren.Network
// return; // return;
// update now // update now
dnow = NetTime.Now; now = NetTime.Now;
now = (float)dnow;
do do
{ {
@@ -383,17 +416,24 @@ namespace Lidgren.Network
} }
catch (SocketException sx) catch (SocketException sx)
{ {
if (sx.SocketErrorCode == SocketError.ConnectionReset) switch (sx.SocketErrorCode)
{ {
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" case SocketError.ConnectionReset:
// we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?! // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
// So, what to do? // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
LogWarning("ConnectionReset"); // So, what to do?
return; LogWarning("ConnectionReset");
} return;
LogWarning(sx.ToString()); case SocketError.NotConnected:
return; // socket is unbound; try to rebind it (happens on mobile when process goes to sleep)
BindSocket(true);
return;
default:
LogWarning("Socket exception: " + sx.ToString());
return;
}
} }
if (bytesReceived < NetConstants.HeaderByteSize) if (bytesReceived < NetConstants.HeaderByteSize)
@@ -401,23 +441,29 @@ namespace Lidgren.Network
//LogVerbose("Received " + bytesReceived + " bytes"); //LogVerbose("Received " + bytesReceived + " bytes");
IPEndPoint ipsender = (IPEndPoint)m_senderRemote; var ipsender = (NetEndPoint)m_senderRemote;
if (m_upnp != null && now < m_upnp.m_discoveryResponseDeadline) if (m_upnp != null && now < m_upnp.m_discoveryResponseDeadline && bytesReceived > 32)
{ {
// is this an UPnP response? // is this an UPnP response?
try string resp = System.Text.Encoding.UTF8.GetString(m_receiveBuffer, 0, bytesReceived);
if (resp.Contains("upnp:rootdevice") || resp.Contains("UPnP/1.0"))
{ {
string resp = System.Text.Encoding.ASCII.GetString(m_receiveBuffer, 0, bytesReceived); try
if (resp.Contains("upnp:rootdevice") || resp.Contains("UPnP/1.0"))
{ {
resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); resp = resp.Substring(0, resp.IndexOf("\r")).Trim();
m_upnp.ExtractServiceUrl(resp); m_upnp.ExtractServiceUrl(resp);
return; return;
} }
catch (Exception ex)
{
LogDebug("Failed to parse UPnP response: " + ex.ToString());
// don't try to parse this packet further
return;
}
} }
catch { }
} }
NetConnection sender = null; NetConnection sender = null;
@@ -427,6 +473,7 @@ namespace Lidgren.Network
// parse packet into messages // parse packet into messages
// //
int numMessages = 0; int numMessages = 0;
int numFragments = 0;
int ptr = 0; int ptr = 0;
while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize) while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize)
{ {
@@ -446,6 +493,9 @@ namespace Lidgren.Network
bool isFragment = ((low & 1) == 1); bool isFragment = ((low & 1) == 1);
ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7)); ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7));
if (isFragment)
numFragments++;
ushort payloadBitLength = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8)); ushort payloadBitLength = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
int payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength); int payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength);
@@ -455,16 +505,20 @@ namespace Lidgren.Network
return; return;
} }
if (tp >= NetMessageType.Unused1 && tp <= NetMessageType.Unused29)
{
ThrowOrLog("Unexpected NetMessageType: " + tp);
return;
}
try try
{ {
NetException.Assert(tp < NetMessageType.Unused1 || tp > NetMessageType.Unused29);
if (tp >= NetMessageType.LibraryError) if (tp >= NetMessageType.LibraryError)
{ {
if (sender != null) if (sender != null)
sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength); sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
else else
ReceivedUnconnectedLibraryMessage(dnow, ipsender, tp, ptr, payloadByteLength); ReceivedUnconnectedLibraryMessage(now, ipsender, tp, ptr, payloadByteLength);
} }
else else
{ {
@@ -473,12 +527,13 @@ namespace Lidgren.Network
NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength); NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength);
msg.m_isFragment = isFragment; msg.m_isFragment = isFragment;
msg.m_receiveTime = dnow; msg.m_receiveTime = now;
msg.m_sequenceNumber = sequenceNumber; msg.m_sequenceNumber = sequenceNumber;
msg.m_receivedMessageType = tp; msg.m_receivedMessageType = tp;
msg.m_senderConnection = sender; msg.m_senderConnection = sender;
msg.m_senderEndPoint = ipsender; msg.m_senderEndPoint = ipsender;
msg.m_bitLength = payloadBitLength; msg.m_bitLength = payloadBitLength;
Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength); Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength);
if (sender != null) if (sender != null)
{ {
@@ -510,9 +565,9 @@ namespace Lidgren.Network
ptr += payloadByteLength; ptr += payloadByteLength;
} }
m_statistics.PacketReceived(bytesReceived, numMessages); m_statistics.PacketReceived(bytesReceived, numMessages, numFragments);
if (sender != null) if (sender != null)
sender.m_statistics.PacketReceived(bytesReceived, numMessages); sender.m_statistics.PacketReceived(bytesReceived, numMessages, numFragments);
} while (m_socket.Available > 0); } while (m_socket.Available > 0);
} }
@@ -525,7 +580,7 @@ namespace Lidgren.Network
m_executeFlushSendQueue = true; m_executeFlushSendQueue = true;
} }
internal void HandleIncomingDiscoveryRequest(double now, IPEndPoint senderEndPoint, int ptr, int payloadByteLength) internal void HandleIncomingDiscoveryRequest(double now, NetEndPoint senderEndPoint, int ptr, int payloadByteLength)
{ {
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryRequest)) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryRequest))
{ {
@@ -539,7 +594,7 @@ namespace Lidgren.Network
} }
} }
internal void HandleIncomingDiscoveryResponse(double now, IPEndPoint senderEndPoint, int ptr, int payloadByteLength) internal void HandleIncomingDiscoveryResponse(double now, NetEndPoint senderEndPoint, int ptr, int payloadByteLength)
{ {
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryResponse)) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryResponse))
{ {
@@ -553,7 +608,7 @@ namespace Lidgren.Network
} }
} }
private void ReceivedUnconnectedLibraryMessage(double now, IPEndPoint senderEndPoint, NetMessageType tp, int ptr, int payloadByteLength) private void ReceivedUnconnectedLibraryMessage(double now, NetEndPoint senderEndPoint, NetMessageType tp, int ptr, int payloadByteLength)
{ {
NetConnection shake; NetConnection shake;
if (m_handshakes.TryGetValue(senderEndPoint, out shake)) if (m_handshakes.TryGetValue(senderEndPoint, out shake))
@@ -574,10 +629,12 @@ namespace Lidgren.Network
HandleIncomingDiscoveryResponse(now, senderEndPoint, ptr, payloadByteLength); HandleIncomingDiscoveryResponse(now, senderEndPoint, ptr, payloadByteLength);
return; return;
case NetMessageType.NatIntroduction: case NetMessageType.NatIntroduction:
HandleNatIntroduction(ptr); if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess))
HandleNatIntroduction(ptr);
return; return;
case NetMessageType.NatPunchMessage: case NetMessageType.NatPunchMessage:
HandleNatPunch(ptr, senderEndPoint); if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess))
HandleNatPunch(ptr, senderEndPoint);
return; return;
case NetMessageType.ConnectResponse: case NetMessageType.ConnectResponse:
@@ -688,4 +745,4 @@ namespace Lidgren.Network
return m_readHelperMessage; return m_readHelperMessage;
} }
} }
} }
+26 -19
View File
@@ -25,6 +25,10 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Diagnostics; using System.Diagnostics;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
public partial class NetPeer public partial class NetPeer
@@ -37,10 +41,10 @@ namespace Lidgren.Network
{ {
public byte[] Data; public byte[] Data;
public double DelayedUntil; public double DelayedUntil;
public IPEndPoint Target; public NetEndPoint Target;
} }
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset) internal void SendPacket(int numBytes, NetEndPoint target, int numMessages, out bool connectionReset)
{ {
connectionReset = false; connectionReset = false;
@@ -48,7 +52,7 @@ namespace Lidgren.Network
float loss = m_configuration.m_loss; float loss = m_configuration.m_loss;
if (loss > 0.0f) if (loss > 0.0f)
{ {
if ((float)NetRandom.Instance.NextDouble() < loss) if ((float)MWCRandom.Instance.NextDouble() < loss)
{ {
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!"); LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
return; // packet "lost" return; // packet "lost"
@@ -66,17 +70,21 @@ namespace Lidgren.Network
// LogVerbose("Sending packet " + numBytes + " bytes"); // LogVerbose("Sending packet " + numBytes + " bytes");
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset); bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
// TODO: handle wasSent == false? // TODO: handle wasSent == false?
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; return;
} }
int num = 1; 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++; num++;
float delay = 0; float delay = 0;
for (int i = 0; i < num; i++) 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 // Enqueue delayed packet
DelayedPacket p = new DelayedPacket(); DelayedPacket p = new DelayedPacket();
@@ -124,13 +132,16 @@ namespace Lidgren.Network
catch { } catch { }
} }
internal bool ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset) internal bool ActuallySendPacket(byte[] data, int numBytes, NetEndPoint target, out bool connectionReset)
{ {
connectionReset = false; connectionReset = false;
IPAddress ba = default(IPAddress);
try try
{ {
ba = NetUtility.GetCachedBroadcastAddress();
// TODO: refactor this check outta here // TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast) if (target.Address == ba)
{ {
// Some networks do not allow // Some networks do not allow
// a global broadcast so we use the BroadcastAddress from the configuration // a global broadcast so we use the BroadcastAddress from the configuration
@@ -167,13 +178,13 @@ namespace Lidgren.Network
} }
finally finally
{ {
if (target.Address == IPAddress.Broadcast) if (target.Address == ba)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
} }
return true; return true;
} }
internal bool SendMTUPacket(int numBytes, IPEndPoint target) internal bool SendMTUPacket(int numBytes, NetEndPoint target)
{ {
try try
{ {
@@ -209,7 +220,7 @@ namespace Lidgren.Network
return true; return true;
} }
#else #else
internal bool SendMTUPacket(int numBytes, IPEndPoint target) internal bool SendMTUPacket(int numBytes, NetEndPoint target)
{ {
try try
{ {
@@ -246,16 +257,18 @@ namespace Lidgren.Network
// //
// Release - just send the packet straight away // Release - just send the packet straight away
// //
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset) internal void SendPacket(int numBytes, NetEndPoint target, int numMessages, out bool connectionReset)
{ {
#if USE_RELEASE_STATISTICS #if USE_RELEASE_STATISTICS
m_statistics.PacketSent(numBytes, numMessages); m_statistics.PacketSent(numBytes, numMessages);
#endif #endif
connectionReset = false; connectionReset = false;
IPAddress ba = default(IPAddress);
try try
{ {
// TODO: refactor this check outta here // TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast) ba = NetUtility.GetCachedBroadcastAddress();
if (target.Address == ba)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target); int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
@@ -284,7 +297,7 @@ namespace Lidgren.Network
} }
finally finally
{ {
if (target.Address == IPAddress.Broadcast) if (target.Address == ba)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
} }
return; return;
@@ -293,12 +306,6 @@ namespace Lidgren.Network
private void FlushDelayedPackets() private void FlushDelayedPackets()
{ {
} }
private void SendCallBack(IAsyncResult res)
{
NetException.Assert(res.IsCompleted == true);
m_socket.EndSendTo(res);
}
#endif #endif
} }
} }
-8
View File
@@ -28,8 +28,6 @@ namespace Lidgren.Network
#if __ANDROID__ #if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Verbose, "", message); Android.Util.Log.WriteLine(Android.Util.LogPriority.Verbose, "", message);
#endif #endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.VerboseDebugMessage)) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.VerboseDebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.VerboseDebugMessage, message)); ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.VerboseDebugMessage, message));
} }
@@ -40,8 +38,6 @@ namespace Lidgren.Network
#if __ANDROID__ #if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Debug, "", message); Android.Util.Log.WriteLine(Android.Util.LogPriority.Debug, "", message);
#endif #endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DebugMessage)) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.DebugMessage, message)); ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.DebugMessage, message));
} }
@@ -51,8 +47,6 @@ namespace Lidgren.Network
#if __ANDROID__ #if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Warn, "", message); Android.Util.Log.WriteLine(Android.Util.LogPriority.Warn, "", message);
#endif #endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.WarningMessage)) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.WarningMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.WarningMessage, message)); ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.WarningMessage, message));
} }
@@ -62,8 +56,6 @@ namespace Lidgren.Network
#if __ANDROID__ #if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Error, "", message); Android.Util.Log.WriteLine(Android.Util.LogPriority.Error, "", message);
#endif #endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ErrorMessage)) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ErrorMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.ErrorMessage, message)); ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.ErrorMessage, message));
} }
+53 -49
View File
@@ -6,14 +6,18 @@ namespace Lidgren.Network
{ {
public partial class NetPeer public partial class NetPeer
{ {
private List<byte[]> m_storagePool; // sorted smallest to largest internal List<byte[]> m_storagePool;
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool; private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
private NetQueue<NetIncomingMessage> m_incomingMessagesPool; private NetQueue<NetIncomingMessage> m_incomingMessagesPool;
internal int m_storagePoolBytes; internal int m_storagePoolBytes;
internal int m_storageSlotsUsedCount;
private int m_maxCacheCount;
private void InitializePools() private void InitializePools()
{ {
m_storageSlotsUsedCount = 0;
if (m_configuration.UseMessageRecycling) if (m_configuration.UseMessageRecycling)
{ {
m_storagePool = new List<byte[]>(16); m_storagePool = new List<byte[]>(16);
@@ -26,6 +30,8 @@ namespace Lidgren.Network
m_outgoingMessagesPool = null; m_outgoingMessagesPool = null;
m_incomingMessagesPool = null; m_incomingMessagesPool = null;
} }
m_maxCacheCount = m_configuration.RecycledCacheMaxCount;
} }
internal byte[] GetStorage(int minimumCapacityInBytes) internal byte[] GetStorage(int minimumCapacityInBytes)
@@ -41,6 +47,7 @@ namespace Lidgren.Network
if (retval != null && retval.Length >= minimumCapacityInBytes) if (retval != null && retval.Length >= minimumCapacityInBytes)
{ {
m_storagePool[i] = null; m_storagePool[i] = null;
m_storageSlotsUsedCount--;
m_storagePoolBytes -= retval.Length; m_storagePoolBytes -= retval.Length;
return retval; return retval;
} }
@@ -52,22 +59,39 @@ namespace Lidgren.Network
internal void Recycle(byte[] storage) internal void Recycle(byte[] storage)
{ {
if (m_storagePool == null) if (m_storagePool == null || storage == null)
return; return;
lock (m_storagePool) lock (m_storagePool)
{ {
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count; int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
{ {
if (m_storagePool[i] == null) if (m_storagePool[i] == null)
{ {
m_storageSlotsUsedCount++;
m_storagePoolBytes += storage.Length;
m_storagePool[i] = storage; m_storagePool[i] = storage;
return; return;
} }
} }
m_storagePool.Add(storage);
if (m_storagePool.Count >= m_maxCacheCount)
{
// pool is full; replace randomly chosen entry to keep size distribution
var idx = NetRandom.Instance.Next(m_storagePool.Count);
m_storagePoolBytes -= m_storagePool[idx].Length;
m_storagePoolBytes += storage.Length;
m_storagePool[idx] = storage; // replace
}
else
{
m_storageSlotsUsedCount++;
m_storagePoolBytes += storage.Length;
m_storagePool.Add(storage);
}
} }
} }
@@ -84,10 +108,8 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public NetOutgoingMessage CreateMessage(string content) public NetOutgoingMessage CreateMessage(string content)
{ {
byte[] bytes = Encoding.UTF8.GetBytes(content); var om = CreateMessage(2 + content.Length); // fair guess
NetOutgoingMessage om = CreateMessage(2 + bytes.Length); om.Write(content);
om.WriteVariableUInt32((uint)bytes.Length);
om.Write(bytes);
return om; return om;
} }
@@ -101,8 +123,10 @@ namespace Lidgren.Network
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval)) if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
retval = new NetOutgoingMessage(); retval = new NetOutgoingMessage();
byte[] storage = GetStorage(initialCapacity); NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount);
retval.m_data = storage;
if (initialCapacity > 0)
retval.m_data = GetStorage(initialCapacity);
return retval; return retval;
} }
@@ -134,17 +158,18 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public void Recycle(NetIncomingMessage msg) public void Recycle(NetIncomingMessage msg)
{ {
if (m_incomingMessagesPool == null) if (m_incomingMessagesPool == null || msg == null)
return; return;
#if DEBUG
if (m_incomingMessagesPool.Contains(msg)) NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled incoming message! Thread race?");
throw new NetException("Recyling already recycled message! Thread race?");
#endif
byte[] storage = msg.m_data; byte[] storage = msg.m_data;
msg.m_data = null; msg.m_data = null;
Recycle(storage); Recycle(storage);
msg.Reset(); msg.Reset();
m_incomingMessagesPool.Enqueue(msg);
if (m_incomingMessagesPool.Count < m_maxCacheCount)
m_incomingMessagesPool.Enqueue(msg);
} }
/// <summary> /// <summary>
@@ -154,34 +179,8 @@ namespace Lidgren.Network
{ {
if (m_incomingMessagesPool == null) if (m_incomingMessagesPool == null)
return; return;
foreach (var im in toRecycle)
// first recycle the storage of each message Recycle(im);
if (m_storagePool != null)
{
lock (m_storagePool)
{
foreach (var msg in toRecycle)
{
var storage = msg.m_data;
msg.m_data = null;
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++)
{
if (m_storagePool[i] == null)
{
m_storagePool[i] = storage;
return;
}
}
msg.Reset();
m_storagePool.Add(storage);
}
}
}
// then recycle the message objects
m_incomingMessagesPool.Enqueue(toRecycle);
} }
internal void Recycle(NetOutgoingMessage msg) internal void Recycle(NetOutgoingMessage msg)
@@ -189,20 +188,25 @@ namespace Lidgren.Network
if (m_outgoingMessagesPool == null) if (m_outgoingMessagesPool == null)
return; return;
#if DEBUG #if DEBUG
if (m_outgoingMessagesPool.Contains(msg)) NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled outgoing message! Thread race?");
throw new NetException("Recyling already recycled message! Thread race?"); if (msg.m_recyclingCount != 0)
LogWarning("Wrong recycling count! should be zero; found " + msg.m_recyclingCount);
#endif #endif
// setting m_recyclingCount to zero SHOULD be an unnecessary maneuver, if it's not zero something is wrong
// however, in RELEASE, we'll just have to accept this and move on with life
msg.m_recyclingCount = 0;
byte[] storage = msg.m_data; byte[] storage = msg.m_data;
msg.m_data = null; msg.m_data = null;
// message fragments cannot be recycled // message fragments cannot be recycled
// TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them // TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them
if (msg.m_fragmentGroup == 0) if (msg.m_fragmentGroup == 0)
Recycle(storage); Recycle(storage);
msg.Reset(); msg.Reset();
m_outgoingMessagesPool.Enqueue(msg); if (m_outgoingMessagesPool.Count < m_maxCacheCount)
m_outgoingMessagesPool.Enqueue(msg);
} }
/// <summary> /// <summary>
+59 -31
View File
@@ -3,6 +3,10 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Net; using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
public partial class NetPeer public partial class NetPeer
@@ -46,8 +50,10 @@ namespace Lidgren.Network
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_isSent = true; msg.m_isSent = true;
bool suppressFragmentation = (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.UnreliableSequenced) && m_configuration.UnreliableSizeBehaviour != NetUnreliableSizeBehaviour.NormalFragmentation;
int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize
if (len <= recipient.m_currentMTU) if (len <= recipient.m_currentMTU || suppressFragmentation)
{ {
Interlocked.Increment(ref msg.m_recyclingCount); Interlocked.Increment(ref msg.m_recyclingCount);
return recipient.EnqueueMessage(msg, method, sequenceChannel); return recipient.EnqueueMessage(msg, method, sequenceChannel);
@@ -57,17 +63,25 @@ namespace Lidgren.Network
// message must be fragmented! // message must be fragmented!
if (recipient.m_status != NetConnectionStatus.Connected) if (recipient.m_status != NetConnectionStatus.Connected)
return NetSendResult.FailedNotConnected; return NetSendResult.FailedNotConnected;
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
} }
} }
internal static int GetMTU(IList<NetConnection> recipients) internal static int GetMTU(IList<NetConnection> recipients)
{ {
int count = recipients.Count; int count = recipients.Count;
NetException.Assert(count > 0);
int mtu = int.MaxValue; int mtu = int.MaxValue;
if (count < 1)
{
#if DEBUG
throw new NetException("GetMTU called with no recipients");
#else
// we don't have access to the particular peer, so just use default MTU
return NetPeerConfiguration.kDefaultMTU;
#endif
}
for(int i=0;i<count;i++) for(int i=0;i<count;i++)
{ {
var conn = recipients[i]; var conn = recipients[i];
@@ -85,23 +99,30 @@ namespace Lidgren.Network
/// <param name="recipients">The list of recipients to send to</param> /// <param name="recipients">The list of recipients to send to</param>
/// <param name="method">How to deliver the message</param> /// <param name="method">How to deliver the message</param>
/// <param name="sequenceChannel">Sequence channel within the delivery method</param> /// <param name="sequenceChannel">Sequence channel within the delivery method</param>
public void SendMessage(NetOutgoingMessage msg, List<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel) public void SendMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
{ {
if (msg == null) if (msg == null)
throw new ArgumentNullException("msg"); throw new ArgumentNullException("msg");
if (recipients == null) if (recipients == null)
{
if (msg.m_isSent == false)
Recycle(msg);
throw new ArgumentNullException("recipients"); throw new ArgumentNullException("recipients");
}
if (recipients.Count < 1) if (recipients.Count < 1)
{
if (msg.m_isSent == false)
Recycle(msg);
throw new NetException("recipients must contain at least one item"); throw new NetException("recipients must contain at least one item");
}
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!"); NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
if (msg.m_isSent) if (msg.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_isSent = true;
int mtu = GetMTU(recipients); int mtu = GetMTU(recipients);
msg.m_isSent = true;
int len = msg.GetEncodedSize(); int len = msg.GetEncodedSize();
if (len <= mtu) if (len <= mtu)
{ {
@@ -114,7 +135,7 @@ namespace Lidgren.Network
continue; continue;
} }
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel); NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
if (res != NetSendResult.Queued && res != NetSendResult.Sent) if (res == NetSendResult.Dropped)
Interlocked.Decrement(ref msg.m_recyclingCount); Interlocked.Decrement(ref msg.m_recyclingCount);
} }
} }
@@ -141,21 +162,21 @@ namespace Lidgren.Network
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit) if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")"); throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
IPAddress adr = NetUtility.Resolve(host); msg.m_isSent = true;
msg.m_messageType = NetMessageType.Unconnected;
var adr = NetUtility.Resolve(host);
if (adr == null) if (adr == null)
throw new NetException("Failed to resolve " + host); throw new NetException("Failed to resolve " + host);
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
Interlocked.Increment(ref msg.m_recyclingCount); Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg)); m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(new NetEndPoint(adr, port), msg));
} }
/// <summary> /// <summary>
/// Send a message to an unconnected host /// Send a message to an unconnected host
/// </summary> /// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient) public void SendUnconnectedMessage(NetOutgoingMessage msg, NetEndPoint recipient)
{ {
if (msg == null) if (msg == null)
throw new ArgumentNullException("msg"); throw new ArgumentNullException("msg");
@@ -170,13 +191,13 @@ namespace Lidgren.Network
msg.m_isSent = true; msg.m_isSent = true;
Interlocked.Increment(ref msg.m_recyclingCount); Interlocked.Increment(ref msg.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg)); m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(recipient, msg));
} }
/// <summary> /// <summary>
/// Send a message to an unconnected host /// Send a message to an unconnected host
/// </summary> /// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients) public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<NetEndPoint> recipients)
{ {
if (msg == null) if (msg == null)
throw new ArgumentNullException("msg"); throw new ArgumentNullException("msg");
@@ -193,35 +214,42 @@ namespace Lidgren.Network
msg.m_isSent = true; msg.m_isSent = true;
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count); Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
foreach(IPEndPoint ep in recipients) foreach (NetEndPoint ep in recipients)
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg)); m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(ep, msg));
} }
/// <summary> /// <summary>
/// Send a message to this exact same netpeer (loopback) /// Send a message to this exact same netpeer (loopback)
/// </summary> /// </summary>
public void SendUnconnectedToSelf(NetOutgoingMessage msg) public void SendUnconnectedToSelf(NetOutgoingMessage om)
{ {
if (msg == null) if (om == null)
throw new ArgumentNullException("msg"); throw new ArgumentNullException("msg");
if (msg.m_isSent) if (om.m_isSent)
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
msg.m_messageType = NetMessageType.Unconnected; om.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true; om.m_isSent = true;
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false) if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
{
Interlocked.Decrement(ref om.m_recyclingCount);
return; // dropping unconnected message since it's not enabled for receiving return; // dropping unconnected message since it's not enabled for receiving
}
NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes); // convert outgoing to incoming
om.Write(msg); NetIncomingMessage im = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, om.LengthBytes);
om.m_isFragment = false; im.Write(om);
om.m_receiveTime = NetTime.Now; im.m_isFragment = false;
om.m_senderConnection = null; im.m_receiveTime = NetTime.Now;
om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint; im.m_senderConnection = null;
NetException.Assert(om.m_bitLength == msg.LengthBits); im.m_senderEndPoint = m_socket.LocalEndPoint as NetEndPoint;
NetException.Assert(im.m_bitLength == om.LengthBits);
ReleaseMessage(om); // recycle outgoing message
Recycle(om);
ReleaseMessage(im);
} }
} }
} }
+71 -30
View File
@@ -3,6 +3,10 @@ using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
@@ -14,9 +18,10 @@ namespace Lidgren.Network
private int m_listenPort; private int m_listenPort;
private object m_tag; private object m_tag;
private object m_messageReceivedEventCreationLock = new object();
internal readonly List<NetConnection> m_connections; internal readonly List<NetConnection> m_connections;
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup; private readonly Dictionary<NetEndPoint, NetConnection> m_connectionLookup;
private string m_shutdownReason; private string m_shutdownReason;
@@ -36,7 +41,13 @@ namespace Lidgren.Network
get get
{ {
if (m_messageReceivedEvent == null) if (m_messageReceivedEvent == null)
m_messageReceivedEvent = new AutoResetEvent(false); {
lock (m_messageReceivedEventCreationLock) // make sure we don't create more than one event object
{
if (m_messageReceivedEvent == null)
m_messageReceivedEvent = new AutoResetEvent(false);
}
}
return m_messageReceivedEvent; return m_messageReceivedEvent;
} }
} }
@@ -106,11 +117,11 @@ namespace Lidgren.Network
m_configuration = config; m_configuration = config;
m_statistics = new NetPeerStatistics(this); m_statistics = new NetPeerStatistics(this);
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4); m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2); m_unsentUnconnectedMessages = new NetQueue<NetTuple<NetEndPoint, NetOutgoingMessage>>(2);
m_connections = new List<NetConnection>(); m_connections = new List<NetConnection>();
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>(); m_connectionLookup = new Dictionary<NetEndPoint, NetConnection>();
m_handshakes = new Dictionary<IPEndPoint, NetConnection>(); m_handshakes = new Dictionary<NetEndPoint, NetConnection>();
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0); m_senderRemote = (EndPoint)new NetEndPoint(IPAddress.Any, 0);
m_status = NetPeerStatus.NotRunning; m_status = NetPeerStatus.NotRunning;
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>(); m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
} }
@@ -149,13 +160,13 @@ namespace Lidgren.Network
m_upnp.Discover(this); m_upnp.Discover(this);
// allow some time for network thread to start up in case they call Connect() or UPnP calls immediately // allow some time for network thread to start up in case they call Connect() or UPnP calls immediately
Thread.Sleep(50); NetUtility.Sleep(50);
} }
/// <summary> /// <summary>
/// Get the connection, if any, for a certain remote endpoint /// Get the connection, if any, for a certain remote endpoint
/// </summary> /// </summary>
public NetConnection GetConnection(IPEndPoint ep) public NetConnection GetConnection(NetEndPoint ep)
{ {
NetConnection retval; NetConnection retval;
@@ -169,15 +180,24 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Read a pending message from any connection, blocking up to maxMillis if needed /// Read a pending message from any connection, blocking up to maxMillis if needed
/// </summary> /// </summary>
public NetIncomingMessage WaitMessage(int maxMillis) public NetIncomingMessage WaitMessage(int maxMillis)
{ {
var msg = ReadMessage(); NetIncomingMessage msg = ReadMessage();
if (msg != null)
return msg; // no need to wait; we already have a message to deliver while (msg == null)
if (m_messageReceivedEvent != null) {
m_messageReceivedEvent.WaitOne(maxMillis); // This could return true...
return ReadMessage(); if (!MessageReceivedEvent.WaitOne(maxMillis))
} {
return null;
}
// ... while this will still returns null. That's why we need to cycle.
msg = ReadMessage();
}
return msg;
}
/// <summary> /// <summary>
/// Read a pending message from any connection, if any /// Read a pending message from any connection, if any
@@ -218,8 +238,8 @@ namespace Lidgren.Network
return added; return added;
} }
// send message immediately // send message immediately and recycle it
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient) internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient)
{ {
VerifyNetworkThread(); VerifyNetworkThread();
NetException.Assert(msg.m_isSent == false); NetException.Assert(msg.m_isSent == false);
@@ -227,6 +247,18 @@ namespace Lidgren.Network
bool connReset; bool connReset;
int len = msg.Encode(m_sendBuffer, 0, 0); int len = msg.Encode(m_sendBuffer, 0, 0);
SendPacket(len, recipient, 1, out connReset); SendPacket(len, recipient, 1, out connReset);
// no reliability, no multiple recipients - we can just recycle this message immediately
msg.m_recyclingCount = 0;
Recycle(msg);
}
static NetEndPoint GetNetEndPoint(string host, int port)
{
IPAddress address = NetUtility.Resolve(host);
if (address == null)
throw new NetException("Could not resolve host");
return new NetEndPoint(address, port);
} }
/// <summary> /// <summary>
@@ -234,7 +266,7 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public NetConnection Connect(string host, int port) public NetConnection Connect(string host, int port)
{ {
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null); return Connect(GetNetEndPoint(host, port), null);
} }
/// <summary> /// <summary>
@@ -242,13 +274,13 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage) public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage)
{ {
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage); return Connect(GetNetEndPoint(host, port), hailMessage);
} }
/// <summary> /// <summary>
/// Create a connection to a remote endpoint /// Create a connection to a remote endpoint
/// </summary> /// </summary>
public NetConnection Connect(IPEndPoint remoteEndPoint) public NetConnection Connect(NetEndPoint remoteEndPoint)
{ {
return Connect(remoteEndPoint, null); return Connect(remoteEndPoint, null);
} }
@@ -256,7 +288,7 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Create a connection to a remote endpoint /// Create a connection to a remote endpoint
/// </summary> /// </summary>
public virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage) public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{ {
if (remoteEndPoint == null) if (remoteEndPoint == null)
throw new ArgumentNullException("remoteEndPoint"); throw new ArgumentNullException("remoteEndPoint");
@@ -281,7 +313,7 @@ namespace Lidgren.Network
break; break;
case NetConnectionStatus.RespondedConnect: case NetConnectionStatus.RespondedConnect:
// send another response // send another response
hs.SendConnectResponse((float)NetTime.Now, false); hs.SendConnectResponse(NetTime.Now, false);
break; break;
default: default:
// weird // weird
@@ -308,18 +340,27 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Send raw bytes; only used for debugging /// Send raw bytes; only used for debugging
/// </summary> /// </summary>
#if DEBUG public void RawSend(byte[] arr, int offset, int length, NetEndPoint destination)
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination) {
#else
internal void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#endif
{
// wrong thread - this miiiight crash with network thread... but what's a boy to do. // wrong thread - this miiiight crash with network thread... but what's a boy to do.
Array.Copy(arr, offset, m_sendBuffer, 0, length); Array.Copy(arr, offset, m_sendBuffer, 0, length);
bool unused; bool unused;
SendPacket(length, destination, 1, out unused); SendPacket(length, destination, 1, out unused);
} }
/// <summary>
/// In DEBUG, throws an exception, in RELEASE logs an error message
/// </summary>
/// <param name="message"></param>
internal void ThrowOrLog(string message)
{
#if DEBUG
throw new NetException(message);
#else
LogError(message);
#endif
}
/// <summary> /// <summary>
/// Disconnects all active connections and closes the socket /// Disconnects all active connections and closes the socket
/// </summary> /// </summary>
+81 -11
View File
@@ -27,6 +27,20 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public sealed class NetPeerConfiguration public sealed class NetPeerConfiguration
{ {
// Maximum transmission unit
// Ethernet can take 1500 bytes of payload, so lets stay below that.
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
// -20 bytes IP header
// -8 bytes UDP header
// -4 bytes to be on the safe side and align to 8-byte boundary
// Total 1408 bytes
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
/// <summary>
/// Default MTU value in bytes
/// </summary>
public const int kDefaultMTU = 1408;
private const string c_isLockedMessage = "You may not modify the NetPeerConfiguration after it has been used to initialize a NetPeer"; private const string c_isLockedMessage = "You may not modify the NetPeerConfiguration after it has been used to initialize a NetPeer";
private bool m_isLocked; private bool m_isLocked;
@@ -39,9 +53,12 @@ namespace Lidgren.Network
internal int m_defaultOutgoingMessageCapacity; internal int m_defaultOutgoingMessageCapacity;
internal float m_pingInterval; internal float m_pingInterval;
internal bool m_useMessageRecycling; internal bool m_useMessageRecycling;
internal int m_recycledCacheMaxCount;
internal float m_connectionTimeout; internal float m_connectionTimeout;
internal bool m_enableUPnP; internal bool m_enableUPnP;
internal bool m_autoFlushSendQueue; internal bool m_autoFlushSendQueue;
private NetUnreliableSizeBehaviour m_unreliableSizeBehaviour;
internal bool m_suppressUnreliableUnorderedAcks;
internal NetIncomingMessageType m_disabledTypes; internal NetIncomingMessageType m_disabledTypes;
internal int m_port; internal int m_port;
@@ -69,12 +86,12 @@ namespace Lidgren.Network
{ {
if (string.IsNullOrEmpty(appIdentifier)) if (string.IsNullOrEmpty(appIdentifier))
throw new NetException("App identifier must be at least one character long"); throw new NetException("App identifier must be at least one character long");
m_appIdentifier = appIdentifier.ToString(System.Globalization.CultureInfo.InvariantCulture); m_appIdentifier = appIdentifier;
// //
// default values // default values
// //
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage | NetIncomingMessageType.ConnectionLatencyUpdated; m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage | NetIncomingMessageType.ConnectionLatencyUpdated | NetIncomingMessageType.NatIntroductionSuccess;
m_networkThreadName = "Lidgren network thread"; m_networkThreadName = "Lidgren network thread";
m_localAddress = IPAddress.Any; m_localAddress = IPAddress.Any;
m_broadcastAddress = IPAddress.Broadcast; m_broadcastAddress = IPAddress.Broadcast;
@@ -92,22 +109,17 @@ namespace Lidgren.Network
m_pingInterval = 4.0f; m_pingInterval = 4.0f;
m_connectionTimeout = 25.0f; m_connectionTimeout = 25.0f;
m_useMessageRecycling = true; m_useMessageRecycling = true;
m_recycledCacheMaxCount = 64;
m_resendHandshakeInterval = 3.0f; m_resendHandshakeInterval = 3.0f;
m_maximumHandshakeAttempts = 5; m_maximumHandshakeAttempts = 5;
m_autoFlushSendQueue = true; m_autoFlushSendQueue = true;
m_suppressUnreliableUnorderedAcks = false;
// Maximum transmission unit m_maximumTransmissionUnit = kDefaultMTU;
// Ethernet can take 1500 bytes of payload, so lets stay below that.
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
// -20 bytes IP header
// -8 bytes UDP header
// -4 bytes to be on the safe side and align to 8-byte boundary
// Total 1408 bytes
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
m_maximumTransmissionUnit = 1408;
m_autoExpandMTU = false; m_autoExpandMTU = false;
m_expandMTUFrequency = 2.0f; m_expandMTUFrequency = 2.0f;
m_expandMTUFailAttempts = 5; m_expandMTUFailAttempts = 5;
m_unreliableSizeBehaviour = NetUnreliableSizeBehaviour.IgnoreMTU;
m_loss = 0.0f; m_loss = 0.0f;
m_minimumOneWayLatency = 0.0f; m_minimumOneWayLatency = 0.0f;
@@ -165,6 +177,15 @@ namespace Lidgren.Network
return !((m_disabledTypes & type) == type); return !((m_disabledTypes & type) == type);
} }
/// <summary>
/// Gets or sets the behaviour of unreliable sends above MTU
/// </summary>
public NetUnreliableSizeBehaviour UnreliableSizeBehaviour
{
get { return m_unreliableSizeBehaviour; }
set { m_unreliableSizeBehaviour = value; }
}
/// <summary> /// <summary>
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized. /// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
/// </summary> /// </summary>
@@ -241,6 +262,20 @@ namespace Lidgren.Network
} }
} }
/// <summary>
/// Gets or sets the maximum number of incoming/outgoing messages to keep in the recycle cache.
/// </summary>
public int RecycledCacheMaxCount
{
get { return m_recycledCacheMaxCount; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_recycledCacheMaxCount = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong /// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
/// </summary> /// </summary>
@@ -278,6 +313,20 @@ namespace Lidgren.Network
set { m_autoFlushSendQueue = value; } set { m_autoFlushSendQueue = value; }
} }
/// <summary>
/// If true, will not send acks for unreliable unordered messages. This will save bandwidth, but disable flow control and duplicate detection for this type of messages.
/// </summary>
public bool SuppressUnreliableUnorderedAcks
{
get { return m_suppressUnreliableUnorderedAcks; }
set
{
if (m_isLocked)
throw new NetException(c_isLockedMessage);
m_suppressUnreliableUnorderedAcks = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized. /// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized.
/// </summary> /// </summary>
@@ -468,4 +517,25 @@ namespace Lidgren.Network
return retval; return retval;
} }
} }
/// <summary>
/// Behaviour of unreliable sends above MTU
/// </summary>
public enum NetUnreliableSizeBehaviour
{
/// <summary>
/// Sending an unreliable message will ignore MTU and send everything in a single packet; this is the new default
/// </summary>
IgnoreMTU = 0,
/// <summary>
/// Old behaviour; use normal fragmentation for unreliable messages - if a fragment is dropped, memory for received fragments are never reclaimed!
/// </summary>
NormalFragmentation = 1,
/// <summary>
/// Alternate behaviour; just drops unreliable messages above MTU
/// </summary>
DropAboveMTU = 2,
}
} }
+20 -23
View File
@@ -39,6 +39,7 @@ namespace Lidgren.Network
internal int m_sentMessages; internal int m_sentMessages;
internal int m_receivedMessages; internal int m_receivedMessages;
internal int m_receivedFragments;
internal int m_sentBytes; internal int m_sentBytes;
internal int m_receivedBytes; internal int m_receivedBytes;
@@ -58,6 +59,7 @@ namespace Lidgren.Network
m_sentMessages = 0; m_sentMessages = 0;
m_receivedMessages = 0; m_receivedMessages = 0;
m_receivedFragments = 0;
m_sentBytes = 0; m_sentBytes = 0;
m_receivedBytes = 0; m_receivedBytes = 0;
@@ -103,41 +105,35 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Gets the number of bytes in the recycled pool /// Gets the number of bytes in the recycled pool
/// </summary> /// </summary>
public int BytesInRecyclePool { get { return m_peer.m_storagePoolBytes; } } public int BytesInRecyclePool
{
get
{
lock (m_peer.m_storagePool)
return m_peer.m_storagePoolBytes;
}
}
#if USE_RELEASE_STATISTICS #if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
#endif
internal void PacketSent(int numBytes, int numMessages) internal void PacketSent(int numBytes, int numMessages)
{ {
m_sentPackets++; m_sentPackets++;
m_sentBytes += numBytes; m_sentBytes += numBytes;
m_sentMessages += numMessages; m_sentMessages += numMessages;
} }
#else
[Conditional("DEBUG")]
internal void PacketSent(int numBytes, int numMessages)
{
m_sentPackets++;
m_sentBytes += numBytes;
m_sentMessages += numMessages;
}
#endif
#if USE_RELEASE_STATISTICS #if !USE_RELEASE_STATISTICS
internal void PacketReceived(int numBytes, int numMessages)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#else
[Conditional("DEBUG")] [Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages) #endif
internal void PacketReceived(int numBytes, int numMessages, int numFragments)
{ {
m_receivedPackets++; m_receivedPackets++;
m_receivedBytes += numBytes; m_receivedBytes += numBytes;
m_receivedMessages += numMessages; m_receivedMessages += numMessages;
m_receivedFragments += numFragments;
} }
#endif
/// <summary> /// <summary>
/// Returns a string that represents this object /// Returns a string that represents this object
@@ -148,13 +144,14 @@ namespace Lidgren.Network
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections"); bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
#if DEBUG || USE_RELEASE_STATISTICS #if DEBUG || USE_RELEASE_STATISTICS
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets"); bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets"); bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages (of which " + m_receivedFragments + " fragments) in " + m_receivedPackets + " packets");
#else #else
bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets"); bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets");
bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets"); bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets");
#endif #endif
bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes"); bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes");
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes"); if (m_peer.m_storagePool != null)
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes (" + m_peer.m_storageSlotsUsedCount + " entries)");
return bdr.ToString(); return bdr.ToString();
} }
} }
+33 -3
View File
@@ -22,6 +22,10 @@ using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
//
// Comment for Linux Mono users: reports of library thread hangs on EnterReadLock() suggests switching to plain lock() works better
//
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
@@ -52,12 +56,29 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Gets the number of items in the queue /// Gets the number of items in the queue
/// </summary> /// </summary>
public int Count { get { return m_size; } } public int Count {
get
{
m_lock.EnterReadLock();
int count = m_size;
m_lock.ExitReadLock();
return count;
}
}
/// <summary> /// <summary>
/// Gets the current capacity for the queue /// Gets the current capacity for the queue
/// </summary> /// </summary>
public int Capacity { get { return m_items.Length; } } public int Capacity
{
get
{
m_lock.EnterReadLock();
int capacity = m_items.Length;
m_lock.ExitReadLock();
return capacity;
}
}
/// <summary> /// <summary>
/// NetQueue constructor /// NetQueue constructor
@@ -193,6 +214,15 @@ namespace Lidgren.Network
return true; return true;
} }
catch
{
#if DEBUG
throw;
#else
item = default(T);
return false;
#endif
}
finally finally
{ {
m_lock.ExitWriteLock(); m_lock.ExitWriteLock();
@@ -200,7 +230,7 @@ namespace Lidgren.Network
} }
/// <summary> /// <summary>
/// Gets an item from the head of the queue, or returns default(T) if empty /// Gets all items from the head of the queue, or returns number of items popped
/// </summary> /// </summary>
public int TryDrain(IList<T> addTo) public int TryDrain(IList<T> addTo)
{ {
@@ -0,0 +1,281 @@
using System;
using System.Security.Cryptography;
namespace Lidgren.Network
{
/// <summary>
/// Multiply With Carry random
/// </summary>
public class MWCRandom : NetRandom
{
/// <summary>
/// Get global instance of MWCRandom
/// </summary>
public static new readonly MWCRandom Instance = new MWCRandom();
private uint m_w, m_z;
/// <summary>
/// Constructor with randomized seed
/// </summary>
public MWCRandom()
{
Initialize(NetRandomSeed.GetUInt64());
}
/// <summary>
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
m_w = seed;
m_z = seed * 16777619;
}
/// <summary>
/// (Re)initialize this instance with provided 64 bit seed
/// </summary>
[CLSCompliant(false)]
public void Initialize(ulong seed)
{
m_w = (uint)seed;
m_z = (uint)(seed >> 32);
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
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);
}
}
/// <summary>
/// Xor Shift based random
/// </summary>
public sealed class XorShiftRandom : NetRandom
{
/// <summary>
/// Get global instance of XorShiftRandom
/// </summary>
public static new 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;
/// <summary>
/// Constructor with randomized seed
/// </summary>
public XorShiftRandom()
{
Initialize(NetRandomSeed.GetUInt64());
}
/// <summary>
/// Constructor with provided 64 bit seed
/// </summary>
[CLSCompliant(false)]
public XorShiftRandom(ulong seed)
{
Initialize(seed);
}
/// <summary>
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
m_x = (uint)seed;
m_y = c_y;
m_z = c_z;
m_w = c_w;
}
/// <summary>
/// (Re)initialize this instance with provided 64 bit seed
/// </summary>
[CLSCompliant(false)]
public void Initialize(ulong seed)
{
m_x = (uint)seed;
m_y = c_y;
m_z = (uint)(seed << 32);
m_w = c_w;
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
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)));
}
}
/// <summary>
/// Mersenne Twister based random
/// </summary>
public sealed class MersenneTwisterRandom : NetRandom
{
/// <summary>
/// Get global instance of MersenneTwisterRandom
/// </summary>
public static new 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);
/// <summary>
/// Constructor with randomized seed
/// </summary>
public MersenneTwisterRandom()
{
Initialize(NetRandomSeed.GetUInt32());
}
/// <summary>
/// Constructor with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
public MersenneTwisterRandom(uint seed)
{
Initialize(seed);
}
/// <summary>
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
[CLSCompliant(false)]
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);
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
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];
}
}
/// <summary>
/// RNGCryptoServiceProvider based random; very slow but cryptographically safe
/// </summary>
public class CryptoRandom : NetRandom
{
/// <summary>
/// Global instance of CryptoRandom
/// </summary>
public static new readonly CryptoRandom Instance = new CryptoRandom();
private RandomNumberGenerator m_rnd = new RNGCryptoServiceProvider();
/// <summary>
/// Seed in CryptoRandom does not create deterministic sequences
/// </summary>
[CLSCompliant(false)]
public override void Initialize(uint seed)
{
byte[] tmp = new byte[seed % 16];
m_rnd.GetBytes(tmp); // just prime it
}
/// <summary>
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
[CLSCompliant(false)]
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);
}
/// <summary>
/// Fill the specified buffer with random values
/// </summary>
public override void NextBytes(byte[] buffer)
{
m_rnd.GetBytes(buffer);
}
/// <summary>
/// Fills all bytes from offset to offset + length in buffer with random values
/// </summary>
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);
}
}
}
+95 -291
View File
@@ -1,373 +1,177 @@
using System; using System;
using System.Collections.Generic;
using System.Threading;
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
/// A fast random number generator for .NET /// NetRandom base class
/// Colin Green, January 2005
/// </summary> /// </summary>
/// September 4th 2005 public abstract class NetRandom : Random
/// Added NextBytesUnsafe() - commented out by default.
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
///
/// Key points:
/// 1) Based on a simple and fast xor-shift pseudo random number generator (RNG) specified in:
/// Marsaglia, George. (2003). Xorshift RNGs.
/// http://www.jstatsoft.org/v08/i14/xorshift.pdf
///
/// This particular implementation of xorshift has a period of 2^128-1. See the above paper to see
/// how this can be easily extened if you need a longer period. At the time of writing I could find no
/// information on the period of System.Random for comparison.
///
/// 2) Faster than System.Random. Up to 8x faster, depending on which methods are called.
///
/// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random
/// does plus some additional methods. The like named methods are functionally equivalent.
///
/// 4) Allows fast re-initialisation with a seed, unlike System.Random which accepts a seed at construction
/// time which then executes a relatively expensive initialisation routine. This provides a vast speed improvement
/// if you need to reset the pseudo-random number sequence many times, e.g. if you want to re-generate the same
/// sequence many times. An alternative might be to cache random numbers in an array, but that approach is limited
/// by memory capacity and the fact that you may also want a large number of different sequences cached. Each sequence
/// can each be represented by a single seed value (int) when using FastRandom.
///
/// Notes.
/// A further performance improvement can be obtained by declaring local variables as static, thus avoiding
/// re-allocation of variables on each call. However care should be taken if multiple instances of
/// FastRandom are in use or if being used in a multi-threaded environment.
public class NetRandom
{ {
/// <summary> /// <summary>
/// Gets a global NetRandom instance /// Get global instance of NetRandom (uses MWCRandom)
/// </summary> /// </summary>
public static readonly NetRandom Instance = new NetRandom(); public static NetRandom Instance = new MWCRandom();
// The +1 ensures NextDouble doesn't generate 1.0 private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
const uint Y = 842502087, Z = 3579807591, W = 273326509;
private static int s_extraSeed = 42;
uint x, y, z, w;
#region Constructors
/// <summary> /// <summary>
/// Initialises a new instance using time dependent seed. /// Constructor with randomized seed
/// </summary> /// </summary>
public NetRandom() public NetRandom()
{ {
// Initialise using the system tick count. Initialize(NetRandomSeed.GetUInt32());
Reinitialise(GetSeed(this));
} }
/// <summary> /// <summary>
/// Initialises a new instance using an int value as seed. /// Constructor with provided 32 bit seed
/// This constructor signature is provided to maintain compatibility with
/// System.Random
/// </summary> /// </summary>
public NetRandom(int seed) public NetRandom(int seed)
{ {
Reinitialise(seed); Initialize((uint)seed);
} }
/// <summary> /// <summary>
/// Create a semi-random seed based on an object /// (Re)initialize this instance with provided 32 bit seed
/// </summary> /// </summary>
public static int GetSeed(object forObject) [CLSCompliant(false)]
public virtual void Initialize(uint seed)
{ {
// mix some semi-random properties // should be abstract, but non-CLS compliant methods can't be abstract!
int seed = (int)Environment.TickCount; throw new NotImplementedException("Implement this in inherited classes");
seed ^= forObject.GetHashCode();
//seed ^= (int)(Stopwatch.GetTimestamp());
//seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
int extraSeed = System.Threading.Interlocked.Increment(ref s_extraSeed);
return seed + extraSeed;
} }
#endregion
#region Public Methods [Reinitialisation]
/// <summary> /// <summary>
/// Reinitialises using an int value as a seed. /// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary> /// </summary>
/// <param name="seed"></param> [CLSCompliant(false)]
public void Reinitialise(int seed) public virtual uint NextUInt32()
{ {
// The only stipulation stated for the xorshift RNG is that at least one of // should be abstract, but non-CLS compliant methods can't be abstract!
// the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing throw new NotImplementedException("Implement this in inherited classes");
// resetting of the x seed
x = (uint)seed;
y = Y;
z = Z;
w = W;
} }
#endregion
#region Public Methods [System.Random functionally equivalent methods]
/// <summary> /// <summary>
/// Generates a random int over the range 0 to int.MaxValue-1. /// Generates a random value that is greater or equal than 0 and less than Int32.MaxValue
/// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
/// This does slightly eat into some of the performance gain over System.Random, but not much.
/// For better performance see:
///
/// Call NextInt() for an int over the range 0 to int.MaxValue.
///
/// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
/// including negative values.
/// </summary> /// </summary>
/// <returns></returns> public override int Next()
public int Next()
{ {
uint t = (x ^ (x << 11)); var retval = (int)(0x7FFFFFFF & NextUInt32());
x = y; y = z; z = w; if (retval == 0x7FFFFFFF)
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); return NextInt32();
return retval;
// Handle the special case where the value int.MaxValue is generated. This is outside of
// the range of permitted values, so we therefore call Next() to try again.
uint rtn = w & 0x7FFFFFFF;
if (rtn == 0x7FFFFFFF)
return Next();
return (int)rtn;
} }
/// <summary> /// <summary>
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound. /// Generates a random value greater or equal than 0 and less or equal than Int32.MaxValue (inclusively)
/// </summary> /// </summary>
/// <param name="upperBound"></param> public int NextInt32()
/// <returns></returns>
public int Next(int upperBound)
{ {
if (upperBound < 0) return (int)(0x7FFFFFFF & NextUInt32());
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// The explicit int cast before the first multiplication gives better performance.
// See comments in NextDouble.
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
} }
/// <summary> /// <summary>
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound. /// Returns random value larger or equal to 0.0 and less than 1.0
/// upperBound must be >= lowerBound. lowerBound may be negative.
/// </summary> /// </summary>
/// <param name="lowerBound"></param> public override double NextDouble()
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int lowerBound, int upperBound)
{ {
if (lowerBound > upperBound) return c_realUnitInt * NextInt32();
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
// The explicit int cast before the first multiplication gives better performance.
// See comments in NextDouble.
int range = upperBound - lowerBound;
if (range < 0)
{ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
}
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
// a little more performance.
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
} }
/// <summary> /// <summary>
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0. /// Returns random value is greater or equal than 0.0 and less than 1.0
/// </summary> /// </summary>
/// <returns></returns> protected override double Sample()
public double NextDouble()
{ {
uint t = (x ^ (x << 11)); return c_realUnitInt * NextInt32();
x = y; y = z; z = w;
// Here we can gain a 2x speed improvement by generating a value that can be cast to
// an int instead of the more easily available uint. If we then explicitly cast to an
// int the compiler will then cast the int to a double to perform the multiplication,
// this final cast is a lot faster than casting from a uint to a double. The extra cast
// to an int is very fast (the allocated bits remain the same) and so the overall effect
// of the extra cast is a significant performance improvement.
//
// Also note that the loss of one bit of precision is equivalent to what occurs within
// System.Random.
return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
} }
/// <summary> /// <summary>
/// Generates a random single. Values returned are from 0.0 up to but not including 1.0. /// Returns random value is greater or equal than 0.0f and less than 1.0f
/// </summary> /// </summary>
public float NextSingle() public float NextSingle()
{ {
return (float)NextDouble(); var retval = (float)(c_realUnitInt * NextInt32());
if (retval == 1.0f)
return NextSingle();
return retval;
} }
/// <summary> /// <summary>
/// Fills the provided byte array with random bytes. /// Returns a random value is greater or equal than 0 and less than maxValue
/// This method is functionally equivalent to System.Random.NextBytes().
/// </summary> /// </summary>
/// <param name="buffer"></param> public override int Next(int maxValue)
public void NextBytes(byte[] buffer)
{ {
// Fill up the bulk of the buffer in chunks of 4 bytes at a time. return (int)(NextDouble() * maxValue);
uint x = this.x, y = this.y, z = this.z, w = this.w;
int i = 0;
uint t;
for (int bound = buffer.Length - 3; i < bound; )
{
// Generate 4 bytes.
// Increased performance is achieved by generating 4 random bytes per loop.
// Also note that no mask needs to be applied to zero out the higher order bytes before
// casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out.
t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
buffer[i++] = (byte)w;
buffer[i++] = (byte)(w >> 8);
buffer[i++] = (byte)(w >> 16);
buffer[i++] = (byte)(w >> 24);
}
// Fill up any remaining bytes in the buffer.
if (i < buffer.Length)
{
// Generate 4 bytes.
t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
buffer[i++] = (byte)w;
if (i < buffer.Length)
{
buffer[i++] = (byte)(w >> 8);
if (i < buffer.Length)
{
buffer[i++] = (byte)(w >> 16);
if (i < buffer.Length)
{
buffer[i] = (byte)(w >> 24);
}
}
}
}
this.x = x; this.y = y; this.z = z; this.w = w;
} }
// /// <summary>
// /// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
// /// thus providing a nice speedup. The loop is also partially unrolled to allow out-of-order-execution,
// /// this results in about a x2 speedup on an AMD Athlon. Thus performance may vary wildly on different CPUs
// /// depending on the number of execution units available.
// ///
// /// Another significant speedup is obtained by setting the 4 bytes by indexing pDWord (e.g. pDWord[i++]=w)
// /// instead of adjusting it dereferencing it (e.g. *pDWord++=w).
// ///
// /// Note that this routine requires the unsafe compilation flag to be specified and so is commented out by default.
// /// </summary>
// /// <param name="buffer"></param>
// public unsafe void NextBytesUnsafe(byte[] buffer)
// {
// if(buffer.Length % 8 != 0)
// throw new ArgumentException("Buffer length must be divisible by 8", "buffer");
//
// uint x=this.x, y=this.y, z=this.z, w=this.w;
//
// fixed(byte* pByte0 = buffer)
// {
// uint* pDWord = (uint*)pByte0;
// for(int i=0, len=buffer.Length>>2; i < len; i+=2)
// {
// uint t=(x^(x<<11));
// x=y; y=z; z=w;
// pDWord[i] = w = (w^(w>>19))^(t^(t>>8));
//
// t=(x^(x<<11));
// x=y; y=z; z=w;
// pDWord[i+1] = w = (w^(w>>19))^(t^(t>>8));
// }
// }
//
// this.x=x; this.y=y; this.z=z; this.w=w;
// }
#endregion
#region Public Methods [Methods not present on System.Random]
/// <summary> /// <summary>
/// Generates a uint. Values returned are over the full range of a uint, /// Returns a random value is greater or equal than minValue and less than maxValue
/// uint.MinValue to uint.MaxValue, inclusive. /// </summary>
/// public override int Next(int minValue, int maxValue)
/// This is the fastest method for generating a single random number because the underlying {
/// random number generator algorithm generates 32 random bits that can be cast directly to return minValue + (int)(NextDouble() * (double)(maxValue - minValue));
/// a uint. }
/// <summary>
/// Generates a random value between UInt64.MinValue to UInt64.MaxValue
/// </summary> /// </summary>
[CLSCompliant(false)] [CLSCompliant(false)]
public uint NextUInt() public ulong NextUInt64()
{ {
uint t = (x ^ (x << 11)); ulong retval = NextUInt32();
x = y; y = z; z = w; retval |= NextUInt32() << 32;
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))); return retval;
} }
/// <summary> private uint m_boolValues;
/// Generates a random int over the range 0 to int.MaxValue, inclusive. private int m_nextBoolIndex;
/// This method differs from Next() only in that the range is 0 to int.MaxValue
/// and not 0 to int.MaxValue-1.
///
/// The slight difference in range means this method is slightly faster than Next()
/// but is not functionally equivalent to System.Random.Next().
/// </summary>
/// <returns></returns>
public int NextInt()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
}
// Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
// with bitBufferIdx.
uint bitBuffer;
uint bitMask = 1;
/// <summary> /// <summary>
/// Generates a single random bit. /// Returns true or false, randomly
/// This method's performance is improved by generating 32 bits in one operation and storing them
/// ready for future calls.
/// </summary> /// </summary>
/// <returns></returns>
public bool NextBool() public bool NextBool()
{ {
if (bitMask == 1) if (m_nextBoolIndex >= 32)
{ {
// Generate 32 more bits. m_boolValues = NextUInt32();
uint t = (x ^ (x << 11)); m_nextBoolIndex = 1;
x = y; y = z; z = w;
bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
// Reset the bitMask that tells us which bit to read next.
bitMask = 0x80000000;
return (bitBuffer & bitMask) == 0;
} }
return (bitBuffer & (bitMask >>= 1)) == 0; var retval = ((m_boolValues >> m_nextBoolIndex) & 1) == 1;
m_nextBoolIndex++;
return retval;
}
/// <summary>
/// Fills all bytes from offset to offset + length in buffer with random values
/// </summary>
public virtual void NextBytes(byte[] buffer, int offset, int length)
{
int full = length / 4;
int ptr = offset;
for (int i = 0; i < full; i++)
{
uint r = NextUInt32();
buffer[ptr++] = (byte)r;
buffer[ptr++] = (byte)(r >> 8);
buffer[ptr++] = (byte)(r >> 16);
buffer[ptr++] = (byte)(r >> 24);
}
int rest = length - (full * 4);
for (int i = 0; i < rest; i++)
buffer[ptr++] = (byte)NextUInt32();
} }
#endregion /// <summary>
/// Fill the specified buffer with random values
/// </summary>
public override void NextBytes(byte[] buffer)
{
NextBytes(buffer, 0, buffer.Length);
}
} }
} }
+45
View File
@@ -0,0 +1,45 @@
using System;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// Class for generating random seeds
/// </summary>
public static class NetRandomSeed
{
private static int m_seedIncrement = -1640531527;
/// <summary>
/// Generates a 32 bit random seed
/// </summary>
[CLSCompliant(false)]
public static uint GetUInt32()
{
ulong seed = GetUInt64();
uint low = (uint)seed;
uint high = (uint)(seed >> 32);
return low ^ high;
}
/// <summary>
/// Generates a 64 bit random seed
/// </summary>
[CLSCompliant(false)]
public static ulong GetUInt64()
{
var guidBytes = Guid.NewGuid().ToByteArray();
ulong seed =
((ulong)guidBytes[0] << (8 * 0)) |
((ulong)guidBytes[1] << (8 * 1)) |
((ulong)guidBytes[2] << (8 * 2)) |
((ulong)guidBytes[3] << (8 * 3)) |
((ulong)guidBytes[4] << (8 * 4)) |
((ulong)guidBytes[5] << (8 * 5)) |
((ulong)guidBytes[6] << (8 * 6)) |
((ulong)guidBytes[7] << (8 * 7));
return seed ^ NetUtility.GetPlatformSeed(m_seedIncrement);
}
}
}
@@ -66,8 +66,9 @@ namespace Lidgren.Network
if (relate < 0) if (relate < 0)
{ {
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
// duplicate // duplicate
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return; return;
} }
@@ -75,6 +76,7 @@ namespace Lidgren.Network
if (relate > m_windowSize) if (relate > m_windowSize)
{ {
// too early message! // too early message!
m_connection.m_statistics.MessageDropped();
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart); m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return; return;
} }
+49 -20
View File
@@ -13,19 +13,27 @@ namespace Lidgren.Network
private int m_windowSize; private int m_windowSize;
private int m_sendStart; private int m_sendStart;
private bool m_anyStoredResends;
private NetBitVector m_receivedAcks; private NetBitVector m_receivedAcks;
internal NetStoredReliableMessage[] m_storedMessages; internal NetStoredReliableMessage[] m_storedMessages;
internal float m_resendDelay; internal double m_resendDelay;
internal override int WindowSize { get { return m_windowSize; } } internal override int WindowSize { get { return m_windowSize; } }
internal override bool NeedToSendMessages()
{
return base.NeedToSendMessages() || m_anyStoredResends;
}
internal NetReliableSenderChannel(NetConnection connection, int windowSize) internal NetReliableSenderChannel(NetConnection connection, int windowSize)
{ {
m_connection = connection; m_connection = connection;
m_windowSize = windowSize; m_windowSize = windowSize;
m_windowStart = 0; m_windowStart = 0;
m_sendStart = 0; m_sendStart = 0;
m_anyStoredResends = false;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers); m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_storedMessages = new NetStoredReliableMessage[m_windowSize]; m_storedMessages = new NetStoredReliableMessage[m_windowSize];
m_queuedSends = new NetQueue<NetOutgoingMessage>(8); m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
@@ -44,6 +52,7 @@ namespace Lidgren.Network
m_receivedAcks.Clear(); m_receivedAcks.Clear();
for (int i = 0; i < m_storedMessages.Length; i++) for (int i = 0; i < m_storedMessages.Length; i++)
m_storedMessages[i].Reset(); m_storedMessages[i].Reset();
m_anyStoredResends = false;
m_queuedSends.Clear(); m_queuedSends.Clear();
m_windowStart = 0; m_windowStart = 0;
m_sendStart = 0; m_sendStart = 0;
@@ -52,27 +61,29 @@ namespace Lidgren.Network
internal override NetSendResult Enqueue(NetOutgoingMessage message) internal override NetSendResult Enqueue(NetOutgoingMessage message)
{ {
m_queuedSends.Enqueue(message); m_queuedSends.Enqueue(message);
m_connection.m_peer.m_needFlushSendQueue = true; // a race condition to this variable will simply result in a single superflous call to FlushSendQueue()
int queueLen = m_queuedSends.Count; if (m_queuedSends.Count <= GetAllowedSends())
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen <= left)
return NetSendResult.Sent; return NetSendResult.Sent;
return NetSendResult.Queued; return NetSendResult.Queued;
} }
// call this regularely // call this regularely
internal override void SendQueuedMessages(float now) internal override void SendQueuedMessages(double now)
{ {
// //
// resends // resends
// //
m_anyStoredResends = false;
for (int i = 0; i < m_storedMessages.Length; i++) for (int i = 0; i < m_storedMessages.Length; i++)
{ {
NetOutgoingMessage om = m_storedMessages[i].Message; var storedMsg = m_storedMessages[i];
NetOutgoingMessage om = storedMsg.Message;
if (om == null) if (om == null)
continue; continue;
float t = m_storedMessages[i].LastSent; m_anyStoredResends = true;
double t = storedMsg.LastSent;
if (t > 0 && (now - t) > m_resendDelay) if (t > 0 && (now - t) > m_resendDelay)
{ {
// deduce sequence number // deduce sequence number
@@ -91,7 +102,8 @@ namespace Lidgren.Network
//m_connection.m_peer.LogVerbose("Resending due to delay #" + m_storedMessages[i].SequenceNumber + " " + om.ToString()); //m_connection.m_peer.LogVerbose("Resending due to delay #" + m_storedMessages[i].SequenceNumber + " " + om.ToString());
m_connection.m_statistics.MessageResent(MessageResendReason.Delay); m_connection.m_statistics.MessageResent(MessageResendReason.Delay);
m_connection.QueueSendMessage(om, m_storedMessages[i].SequenceNumber); Interlocked.Increment(ref om.m_recyclingCount); // increment this since it's being decremented in QueueSendMessage
m_connection.QueueSendMessage(om, storedMsg.SequenceNumber);
m_storedMessages[i].LastSent = now; m_storedMessages[i].LastSent = now;
m_storedMessages[i].NumSent++; m_storedMessages[i].NumSent++;
@@ -103,7 +115,7 @@ namespace Lidgren.Network
return; return;
// queued sends // queued sends
while (m_queuedSends.Count > 0 && num > 0) while (num > 0 && m_queuedSends.Count > 0)
{ {
NetOutgoingMessage om; NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om)) if (m_queuedSends.TryDequeue(out om))
@@ -112,12 +124,16 @@ namespace Lidgren.Network
NetException.Assert(num == GetAllowedSends()); NetException.Assert(num == GetAllowedSends());
} }
} }
private void ExecuteSend(float now, NetOutgoingMessage message) private void ExecuteSend(double now, NetOutgoingMessage message)
{ {
int seqNr = m_sendStart; int seqNr = m_sendStart;
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
// must increment recycle count here, since it's decremented in QueueSendMessage and we want to keep it for the future in case or resends
// we will decrement once more in DestoreMessage for final recycling
Interlocked.Increment(ref message.m_recyclingCount);
m_connection.QueueSendMessage(message, seqNr); m_connection.QueueSendMessage(message, seqNr);
int storeIndex = seqNr % m_windowSize; int storeIndex = seqNr % m_windowSize;
@@ -127,13 +143,22 @@ namespace Lidgren.Network
m_storedMessages[storeIndex].Message = message; m_storedMessages[storeIndex].Message = message;
m_storedMessages[storeIndex].LastSent = now; m_storedMessages[storeIndex].LastSent = now;
m_storedMessages[storeIndex].SequenceNumber = seqNr; m_storedMessages[storeIndex].SequenceNumber = seqNr;
m_anyStoredResends = true;
return; return;
} }
private void DestoreMessage(int storeIndex) private void DestoreMessage(double now, int storeIndex, out bool resetTimeout)
{ {
NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message; // reset timeout if we receive ack within kThreshold of sending it
const double kThreshold = 2.0;
var srm = m_storedMessages[storeIndex];
resetTimeout = (srm.NumSent == 1) && (now - srm.LastSent < kThreshold);
var storedMessage = srm.Message;
// on each destore; reduce recyclingcount so that when all instances are destored, the outgoing message can be recycled
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
#if DEBUG #if DEBUG
if (storedMessage == null) if (storedMessage == null)
throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago"); throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago");
@@ -141,7 +166,6 @@ namespace Lidgren.Network
if (storedMessage != null) if (storedMessage != null)
{ {
#endif #endif
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
if (storedMessage.m_recyclingCount <= 0) if (storedMessage.m_recyclingCount <= 0)
m_connection.m_peer.Recycle(storedMessage); m_connection.m_peer.Recycle(storedMessage);
@@ -153,7 +177,7 @@ namespace Lidgren.Network
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // remoteWindowStart is remote expected sequence number; everything below this has arrived properly
// seqNr is the actual nr received // seqNr is the actual nr received
internal override void ReceiveAcknowledge(float now, int seqNr) internal override void ReceiveAcknowledge(double now, int seqNr)
{ {
// late (dupe), on time or early ack? // late (dupe), on time or early ack?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
@@ -171,8 +195,9 @@ namespace Lidgren.Network
// ack arrived right on time // ack arrived right on time
NetException.Assert(seqNr == m_windowStart); NetException.Assert(seqNr == m_windowStart);
bool resetTimeout;
m_receivedAcks[m_windowStart] = false; m_receivedAcks[m_windowStart] = false;
DestoreMessage(m_windowStart % m_windowSize); DestoreMessage(now, m_windowStart % m_windowSize, out resetTimeout);
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
// advance window if we already have early acks // advance window if we already have early acks
@@ -180,13 +205,16 @@ namespace Lidgren.Network
{ {
//m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "..."); //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "...");
m_receivedAcks[m_windowStart] = false; m_receivedAcks[m_windowStart] = false;
DestoreMessage(m_windowStart % m_windowSize); bool rt;
DestoreMessage(now, m_windowStart % m_windowSize, out rt);
resetTimeout |= rt;
NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
//m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart); //m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
} }
if (resetTimeout)
m_connection.ResetTimeout(now);
return; return;
} }
@@ -241,7 +269,7 @@ namespace Lidgren.Network
NetOutgoingMessage rmsg = m_storedMessages[slot].Message; NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
//m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")"); //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")");
if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f)) if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35))
{ {
// already resent recently // already resent recently
} }
@@ -250,6 +278,7 @@ namespace Lidgren.Network
m_storedMessages[slot].LastSent = now; m_storedMessages[slot].LastSent = now;
m_storedMessages[slot].NumSent++; m_storedMessages[slot].NumSent++;
m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence); m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
Interlocked.Increment(ref rmsg.m_recyclingCount); // increment this since it's being decremented in QueueSendMessage
m_connection.QueueSendMessage(rmsg, rnr); m_connection.QueueSendMessage(rmsg, rnr);
} }
} }
@@ -42,6 +42,7 @@ namespace Lidgren.Network
if (relate < 0) if (relate < 0)
{ {
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE"); m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
return; return;
} }
@@ -50,6 +51,7 @@ namespace Lidgren.Network
if (relate > m_windowSize) if (relate > m_windowSize)
{ {
// too early message! // too early message!
m_connection.m_statistics.MessageDropped();
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart); m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return; return;
} }
@@ -65,6 +65,7 @@ namespace Lidgren.Network
if (relate < 0) if (relate < 0)
{ {
// duplicate // duplicate
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE"); m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return; return;
} }
@@ -73,10 +74,19 @@ namespace Lidgren.Network
if (relate > m_windowSize) if (relate > m_windowSize)
{ {
// too early message! // too early message!
m_connection.m_statistics.MessageDropped();
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart); m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return; return;
} }
if (m_earlyReceived.Get(message.m_sequenceNumber % m_windowSize))
{
// duplicate
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return;
}
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true); m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
//m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart); //m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
//m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message; //m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
+10 -35
View File
@@ -14,17 +14,7 @@ namespace Lidgren.Network
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16); private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
private static readonly NetBigInteger g = NetBigInteger.Two; private static readonly NetBigInteger g = NetBigInteger.Two;
private static readonly NetBigInteger k = ComputeMultiplier(); private static readonly NetBigInteger k = ComputeMultiplier();
private static HashAlgorithm GetHashAlgorithm()
{
#if USE_SHA256
// this does not seem to work as of yet
return SHA256.Create();
#else
return SHA1.Create();
#endif
}
/// <summary> /// <summary>
/// Compute multiplier (k) /// Compute multiplier (k)
/// </summary> /// </summary>
@@ -36,9 +26,7 @@ namespace Lidgren.Network
string ccstr = one + two.PadLeft(one.Length, '0'); string ccstr = one + two.PadLeft(one.Length, '0');
byte[] cc = NetUtility.ToByteArray(ccstr); byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm(); var ccHashed = NetUtility.ComputeSHAHash(cc);
var ccHashed = sha.ComputeHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16); return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16);
} }
@@ -48,7 +36,7 @@ namespace Lidgren.Network
public static byte[] CreateRandomSalt() public static byte[] CreateRandomSalt()
{ {
byte[] retval = new byte[16]; byte[] retval = new byte[16];
NetRandom.Instance.NextBytes(retval); CryptoRandom.Instance.NextBytes(retval);
return retval; return retval;
} }
@@ -58,7 +46,7 @@ namespace Lidgren.Network
public static byte[] CreateRandomEphemeral() public static byte[] CreateRandomEphemeral()
{ {
byte[] retval = new byte[32]; byte[] retval = new byte[32];
NetRandom.Instance.NextBytes(retval); CryptoRandom.Instance.NextBytes(retval);
return retval; return retval;
} }
@@ -67,17 +55,15 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public static byte[] ComputePrivateKey(string username, string password, byte[] salt) public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
{ {
var sha = GetHashAlgorithm();
byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password); byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password);
byte[] innerHash = sha.ComputeHash(tmp); byte[] innerHash = NetUtility.ComputeSHAHash(tmp);
byte[] total = new byte[innerHash.Length + salt.Length]; byte[] total = new byte[innerHash.Length + salt.Length];
Buffer.BlockCopy(salt, 0, total, 0, salt.Length); Buffer.BlockCopy(salt, 0, total, 0, salt.Length);
Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length); Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length);
// x ie. H(salt || H(username || ":" || password)) // x ie. H(salt || H(username || ":" || password))
return new NetBigInteger(NetUtility.ToHexString(sha.ComputeHash(total)), 16).ToByteArrayUnsigned(); return new NetBigInteger(NetUtility.ToHexString(NetUtility.ComputeSHAHash(total)), 16).ToByteArrayUnsigned();
} }
/// <summary> /// <summary>
@@ -93,15 +79,6 @@ namespace Lidgren.Network
return serverVerifier.ToByteArrayUnsigned(); return serverVerifier.ToByteArrayUnsigned();
} }
/// <summary>
/// SHA hash data
/// </summary>
public static byte[] Hash(byte[] data)
{
var sha = GetHashAlgorithm();
return sha.ComputeHash(data);
}
/// <summary> /// <summary>
/// Compute client public ephemeral value (A) /// Compute client public ephemeral value (A)
/// </summary> /// </summary>
@@ -144,8 +121,7 @@ namespace Lidgren.Network
byte[] cc = NetUtility.ToByteArray(ccstr); byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm(); var ccHashed = NetUtility.ComputeSHAHash(cc);
var ccHashed = sha.ComputeHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned(); return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned();
} }
@@ -185,10 +161,9 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Create XTEA symmetrical encryption object from sessionValue /// Create XTEA symmetrical encryption object from sessionValue
/// </summary> /// </summary>
public static NetXtea CreateEncryption(byte[] sessionValue) public static NetXtea CreateEncryption(NetPeer peer, byte[] sessionValue)
{ {
var sha = GetHashAlgorithm(); var hash = NetUtility.ComputeSHAHash(sessionValue);
var hash = sha.ComputeHash(sessionValue);
var key = new byte[16]; var key = new byte[16];
for(int i=0;i<16;i++) for(int i=0;i<16;i++)
@@ -198,7 +173,7 @@ namespace Lidgren.Network
key[i] ^= hash[i + (j * 16)]; key[i] ^= hash[i + (j * 16)];
} }
return new NetXtea(key); return new NetXtea(peer, key);
} }
} }
} }
+12 -3
View File
@@ -5,15 +5,24 @@ namespace Lidgren.Network
internal abstract class NetSenderChannelBase internal abstract class NetSenderChannelBase
{ {
// access this directly to queue things in this channel // access this directly to queue things in this channel
internal NetQueue<NetOutgoingMessage> m_queuedSends; protected NetQueue<NetOutgoingMessage> m_queuedSends;
internal abstract int WindowSize { get; } internal abstract int WindowSize { get; }
internal abstract int GetAllowedSends(); internal abstract int GetAllowedSends();
internal int QueuedSendsCount { get { return m_queuedSends.Count; } }
internal virtual bool NeedToSendMessages() { return m_queuedSends.Count > 0; }
public int GetFreeWindowSlots()
{
return GetAllowedSends() - m_queuedSends.Count;
}
internal abstract NetSendResult Enqueue(NetOutgoingMessage message); internal abstract NetSendResult Enqueue(NetOutgoingMessage message);
internal abstract void SendQueuedMessages(float now); internal abstract void SendQueuedMessages(double now);
internal abstract void Reset(); internal abstract void Reset();
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber); internal abstract void ReceiveAcknowledge(double now, int sequenceNumber);
} }
} }
+2 -2
View File
@@ -5,14 +5,14 @@ namespace Lidgren.Network
internal struct NetStoredReliableMessage internal struct NetStoredReliableMessage
{ {
public int NumSent; public int NumSent;
public float LastSent; public double LastSent;
public NetOutgoingMessage Message; public NetOutgoingMessage Message;
public int SequenceNumber; public int SequenceNumber;
public void Reset() public void Reset()
{ {
NumSent = 0; NumSent = 0;
LastSent = 0; LastSent = 0.0;
Message = null; Message = null;
} }
} }
+1 -20
View File
@@ -17,8 +17,6 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR TH
USE OR OTHER DEALINGS IN THE SOFTWARE. USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#define IS_STOPWATCH_AVAILABLE
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
@@ -29,25 +27,8 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Time service /// Time service
/// </summary> /// </summary>
public static class NetTime public static partial class NetTime
{ {
#if IS_STOPWATCH_AVAILABLE
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
#else
private static readonly uint s_timeInitialized = (uint)Environment.TickCount;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
#endif
/// <summary> /// <summary>
/// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds) /// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
/// </summary> /// </summary>
+30 -20
View File
@@ -5,6 +5,10 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
@@ -40,7 +44,7 @@ namespace Lidgren.Network
private NetPeer m_peer; private NetPeer m_peer;
private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false); private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false);
internal float m_discoveryResponseDeadline; internal double m_discoveryResponseDeadline;
private UPnPStatus m_status; private UPnPStatus m_status;
@@ -55,7 +59,7 @@ namespace Lidgren.Network
public NetUPnP(NetPeer peer) public NetUPnP(NetPeer peer)
{ {
m_peer = peer; m_peer = peer;
m_discoveryResponseDeadline = float.MinValue; m_discoveryResponseDeadline = double.MinValue;
} }
internal void Discover(NetPeer peer) internal void Discover(NetPeer peer)
@@ -67,22 +71,25 @@ namespace Lidgren.Network
"MAN:\"ssdp:discover\"\r\n" + "MAN:\"ssdp:discover\"\r\n" +
"MX:3\r\n\r\n"; "MX:3\r\n\r\n";
m_discoveryResponseDeadline = NetTime.Now + 6.0; // arbitrarily chosen number, router gets 6 seconds to respond
m_status = UPnPStatus.Discovering; m_status = UPnPStatus.Discovering;
byte[] arr = System.Text.Encoding.UTF8.GetBytes(str); byte[] arr = System.Text.Encoding.UTF8.GetBytes(str);
m_peer.LogDebug("Attempting UPnP discovery"); m_peer.LogDebug("Attempting UPnP discovery");
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
peer.RawSend(arr, 0, arr.Length, new IPEndPoint(IPAddress.Broadcast, 1900)); peer.RawSend(arr, 0, arr.Length, new NetEndPoint(NetUtility.GetBroadcastAddress(), 1900));
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
// allow some extra time for router to respond
// System.Threading.Thread.Sleep(50);
m_discoveryResponseDeadline = (float)NetTime.Now + 6.0f; // arbitrarily chosen number, router gets 6 seconds to respond
m_status = UPnPStatus.Discovering;
} }
internal void CheckForDiscoveryTimeout()
{
if ((m_status != UPnPStatus.Discovering) || (NetTime.Now < m_discoveryResponseDeadline))
return;
m_peer.LogDebug("UPnP discovery timed out");
m_status = UPnPStatus.NotAvailable;
}
internal void ExtractServiceUrl(string resp) internal void ExtractServiceUrl(string resp)
{ {
#if !DEBUG #if !DEBUG
@@ -90,7 +97,9 @@ namespace Lidgren.Network
{ {
#endif #endif
XmlDocument desc = new XmlDocument(); XmlDocument desc = new XmlDocument();
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream()); using (var response = WebRequest.Create(resp).GetResponse())
desc.Load(response.GetResponseStream());
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
@@ -168,11 +177,11 @@ namespace Lidgren.Network
try try
{ {
XmlDocument xdoc = SOAPRequest(m_serviceUrl, SOAPRequest(m_serviceUrl,
"<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" + "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" +
"<NewRemoteHost></NewRemoteHost>" + "<NewRemoteHost></NewRemoteHost>" +
"<NewExternalPort>" + port.ToString() + "</NewExternalPort>" + "<NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" + "<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture) + "</NewProtocol>" +
"<NewInternalPort>" + port.ToString() + "</NewInternalPort>" + "<NewInternalPort>" + port.ToString() + "</NewInternalPort>" +
"<NewInternalClient>" + client.ToString() + "</NewInternalClient>" + "<NewInternalClient>" + client.ToString() + "</NewInternalClient>" +
"<NewEnabled>1</NewEnabled>" + "<NewEnabled>1</NewEnabled>" +
@@ -182,7 +191,7 @@ namespace Lidgren.Network
"AddPortMapping"); "AddPortMapping");
m_peer.LogDebug("Sent UPnP port forward request"); m_peer.LogDebug("Sent UPnP port forward request");
System.Threading.Thread.Sleep(50); NetUtility.Sleep(50);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -202,12 +211,12 @@ namespace Lidgren.Network
try try
{ {
XmlDocument xdoc = SOAPRequest(m_serviceUrl, SOAPRequest(m_serviceUrl,
"<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" + "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" +
"<NewRemoteHost>" + "<NewRemoteHost>" +
"</NewRemoteHost>" + "</NewRemoteHost>" +
"<NewExternalPort>" + port + "</NewExternalPort>" + "<NewExternalPort>" + port + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" + "<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture) + "</NewProtocol>" +
"</u:DeletePortMapping>", "DeletePortMapping"); "</u:DeletePortMapping>", "DeletePortMapping");
return true; return true;
} }
@@ -256,11 +265,12 @@ namespace Lidgren.Network
r.ContentType = "text/xml; charset=\"utf-8\""; r.ContentType = "text/xml; charset=\"utf-8\"";
r.ContentLength = b.Length; r.ContentLength = b.Length;
r.GetRequestStream().Write(b, 0, b.Length); r.GetRequestStream().Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument(); using (WebResponse wres = r.GetResponse()) {
WebResponse wres = r.GetResponse(); XmlDocument resp = new XmlDocument();
Stream ress = wres.GetResponseStream(); Stream ress = wres.GetResponseStream();
resp.Load(ress); resp.Load(ress);
return resp; return resp;
}
} }
} }
} }
+24 -7
View File
@@ -12,12 +12,13 @@ namespace Lidgren.Network
private int m_windowStart; private int m_windowStart;
private int m_windowSize; private int m_windowSize;
private int m_sendStart; private int m_sendStart;
private bool m_doFlowControl;
private NetBitVector m_receivedAcks; private NetBitVector m_receivedAcks;
internal override int WindowSize { get { return m_windowSize; } } internal override int WindowSize { get { return m_windowSize; } }
internal NetUnreliableSenderChannel(NetConnection connection, int windowSize) internal NetUnreliableSenderChannel(NetConnection connection, int windowSize, NetDeliveryMethod method)
{ {
m_connection = connection; m_connection = connection;
m_windowSize = windowSize; m_windowSize = windowSize;
@@ -25,10 +26,16 @@ namespace Lidgren.Network
m_sendStart = 0; m_sendStart = 0;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers); m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_queuedSends = new NetQueue<NetOutgoingMessage>(8); m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
m_doFlowControl = true;
if (method == NetDeliveryMethod.Unreliable && connection.Peer.Configuration.SuppressUnreliableUnorderedAcks == true)
m_doFlowControl = false;
} }
internal override int GetAllowedSends() internal override int GetAllowedSends()
{ {
if (!m_doFlowControl)
return 2; // always allowed to send without flow control!
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize; int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
NetException.Assert(retval >= 0 && retval <= m_windowSize); NetException.Assert(retval >= 0 && retval <= m_windowSize);
return retval; return retval;
@@ -45,23 +52,27 @@ namespace Lidgren.Network
internal override NetSendResult Enqueue(NetOutgoingMessage message) internal override NetSendResult Enqueue(NetOutgoingMessage message)
{ {
int queueLen = m_queuedSends.Count + 1; int queueLen = m_queuedSends.Count + 1;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers; int left = GetAllowedSends();
if (queueLen > left) if (queueLen > left || (message.LengthBytes > m_connection.m_currentMTU && m_connection.m_peerConfiguration.UnreliableSizeBehaviour == NetUnreliableSizeBehaviour.DropAboveMTU))
{
// drop message
return NetSendResult.Dropped; return NetSendResult.Dropped;
}
m_queuedSends.Enqueue(message); m_queuedSends.Enqueue(message);
m_connection.m_peer.m_needFlushSendQueue = true; // a race condition to this variable will simply result in a single superflous call to FlushSendQueue()
return NetSendResult.Sent; return NetSendResult.Sent;
} }
// call this regularely // call this regularely
internal override void SendQueuedMessages(float now) internal override void SendQueuedMessages(double now)
{ {
int num = GetAllowedSends(); int num = GetAllowedSends();
if (num < 1) if (num < 1)
return; return;
// queued sends // queued sends
while (m_queuedSends.Count > 0 && num > 0) while (num > 0 && m_queuedSends.Count > 0)
{ {
NetOutgoingMessage om; NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om)) if (m_queuedSends.TryDequeue(out om))
@@ -79,7 +90,6 @@ namespace Lidgren.Network
m_connection.QueueSendMessage(message, seqNr); m_connection.QueueSendMessage(message, seqNr);
Interlocked.Decrement(ref message.m_recyclingCount);
if (message.m_recyclingCount <= 0) if (message.m_recyclingCount <= 0)
m_connection.m_peer.Recycle(message); m_connection.m_peer.Recycle(message);
@@ -88,8 +98,15 @@ namespace Lidgren.Network
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // remoteWindowStart is remote expected sequence number; everything below this has arrived properly
// seqNr is the actual nr received // seqNr is the actual nr received
internal override void ReceiveAcknowledge(float now, int seqNr) internal override void ReceiveAcknowledge(double now, int seqNr)
{ {
if (m_doFlowControl == false)
{
// we have no use for acks on this channel since we don't respect the window anyway
m_connection.m_peer.LogWarning("SuppressUnreliableUnorderedAcks sender/receiver mismatch!");
return;
}
// late (dupe), on time or early ack? // late (dupe), on time or early ack?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
@@ -4,7 +4,7 @@ namespace Lidgren.Network
{ {
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
{ {
private int m_lastReceivedSequenceNumber; private int m_lastReceivedSequenceNumber = -1;
public NetUnreliableSequencedReceiver(NetConnection connection) public NetUnreliableSequencedReceiver(NetConnection connection)
: base(connection) : base(connection)
@@ -18,9 +18,13 @@ namespace Lidgren.Network
// ack no matter what // ack no matter what
m_connection.QueueAck(msg.m_receivedMessageType, nr); m_connection.QueueAck(msg.m_receivedMessageType, nr);
int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber); int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber + 1);
if (relate < 0) if (relate < 0)
{
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + nr + " DROPPING DUPLICATE");
return; // drop if late return; // drop if late
}
m_lastReceivedSequenceNumber = nr; m_lastReceivedSequenceNumber = nr;
m_peer.ReleaseMessage(msg); m_peer.ReleaseMessage(msg);
@@ -4,15 +4,18 @@ namespace Lidgren.Network
{ {
internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase
{ {
private bool m_doFlowControl;
public NetUnreliableUnorderedReceiver(NetConnection connection) public NetUnreliableUnorderedReceiver(NetConnection connection)
: base(connection) : base(connection)
{ {
m_doFlowControl = connection.Peer.Configuration.SuppressUnreliableUnorderedAcks == false;
} }
internal override void ReceiveMessage(NetIncomingMessage msg) internal override void ReceiveMessage(NetIncomingMessage msg)
{ {
// ack no matter what if (m_doFlowControl)
m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber); m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber);
m_peer.ReleaseMessage(msg); m_peer.ReleaseMessage(msg);
} }
+53 -188
View File
@@ -16,41 +16,46 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRA
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE. USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#if !__ANDROID__ && !IOS
#define IS_FULL_NET_AVAILABLE #if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
using NetAddress = System.Net.IPAddress;
#endif #endif
using System; using System;
using System.Net; using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography;
namespace Lidgren.Network namespace Lidgren.Network
{ {
/// <summary> /// <summary>
/// Utility methods /// Utility methods
/// </summary> /// </summary>
public static class NetUtility public static partial class NetUtility
{ {
private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null;
/// <summary> /// <summary>
/// Resolve endpoint callback /// Resolve endpoint callback
/// </summary> /// </summary>
public delegate void ResolveEndPointCallback(IPEndPoint endPoint); public delegate void ResolveEndPointCallback(NetEndPoint endPoint);
/// <summary> /// <summary>
/// Resolve address callback /// Resolve address callback
/// </summary> /// </summary>
public delegate void ResolveAddressCallback(IPAddress adr); public delegate void ResolveAddressCallback(NetAddress adr);
/// <summary> /// <summary>
/// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number (asynchronous version) /// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number (asynchronous version)
/// </summary> /// </summary>
public static void ResolveAsync(string ipOrHost, int port, ResolveEndPointCallback callback) public static void ResolveAsync(string ipOrHost, int port, ResolveEndPointCallback callback)
{ {
ResolveAsync(ipOrHost, delegate(IPAddress adr) ResolveAsync(ipOrHost, delegate(NetAddress adr)
{ {
if (adr == null) if (adr == null)
{ {
@@ -58,7 +63,7 @@ namespace Lidgren.Network
} }
else else
{ {
callback(new IPEndPoint(adr, port)); callback(new NetEndPoint(adr, port));
} }
}); });
} }
@@ -66,10 +71,18 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number /// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number
/// </summary> /// </summary>
public static IPEndPoint Resolve(string ipOrHost, int port) public static NetEndPoint Resolve(string ipOrHost, int port)
{ {
IPAddress adr = Resolve(ipOrHost); var adr = Resolve(ipOrHost);
return new IPEndPoint(adr, port); return adr == null ? null : new NetEndPoint(adr, port);
}
private static IPAddress s_broadcastAddress;
public static IPAddress GetCachedBroadcastAddress()
{
if (s_broadcastAddress == null)
s_broadcastAddress = GetBroadcastAddress();
return s_broadcastAddress;
} }
/// <summary> /// <summary>
@@ -82,8 +95,8 @@ namespace Lidgren.Network
ipOrHost = ipOrHost.Trim(); ipOrHost = ipOrHost.Trim();
IPAddress ipAddress = null; NetAddress ipAddress = null;
if (IPAddress.TryParse(ipOrHost, out ipAddress)) if (NetAddress.TryParse(ipOrHost, out ipAddress))
{ {
if (ipAddress.AddressFamily == AddressFamily.InterNetwork) if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{ {
@@ -124,7 +137,7 @@ namespace Lidgren.Network
} }
// check each entry for a valid IP address // check each entry for a valid IP address
foreach (IPAddress ipCurrent in entry.AddressList) foreach (var ipCurrent in entry.AddressList)
{ {
if (ipCurrent.AddressFamily == AddressFamily.InterNetwork) if (ipCurrent.AddressFamily == AddressFamily.InterNetwork)
{ {
@@ -153,15 +166,15 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname /// Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname
/// </summary> /// </summary>
public static IPAddress Resolve(string ipOrHost) public static NetAddress Resolve(string ipOrHost)
{ {
if (string.IsNullOrEmpty(ipOrHost)) if (string.IsNullOrEmpty(ipOrHost))
throw new ArgumentException("Supplied string must not be empty", "ipOrHost"); throw new ArgumentException("Supplied string must not be empty", "ipOrHost");
ipOrHost = ipOrHost.Trim(); ipOrHost = ipOrHost.Trim();
IPAddress ipAddress = null; NetAddress ipAddress = null;
if (IPAddress.TryParse(ipOrHost, out ipAddress)) if (NetAddress.TryParse(ipOrHost, out ipAddress))
{ {
if (ipAddress.AddressFamily == AddressFamily.InterNetwork) if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
return ipAddress; return ipAddress;
@@ -195,56 +208,6 @@ namespace Lidgren.Network
} }
} }
#if IS_FULL_NET_AVAILABLE
private static NetworkInterface GetNetworkInterface()
{
IPGlobalProperties computerProperties = IPGlobalProperties.GetIPGlobalProperties();
if (computerProperties == null)
return null;
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
if (nics == null || nics.Length < 1)
return null;
NetworkInterface best = null;
foreach (NetworkInterface adapter in nics)
{
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback || adapter.NetworkInterfaceType == NetworkInterfaceType.Unknown)
continue;
if (!adapter.Supports(NetworkInterfaceComponent.IPv4))
continue;
if (best == null)
best = adapter;
if (adapter.OperationalStatus != OperationalStatus.Up)
continue;
// make sure this adapter has any ipv4 addresses
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
{
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
// Yes it does, return this network interface.
return adapter;
}
}
}
return best;
}
/// <summary>
/// Returns the physical (MAC) address for the first usable network interface
/// </summary>
public static PhysicalAddress GetMacAddress()
{
NetworkInterface ni = GetNetworkInterface();
if (ni == null)
return null;
return ni.GetPhysicalAddress();
}
#endif
/// <summary> /// <summary>
/// Create a hex string from an Int64 value /// Create a hex string from an Int64 value
/// </summary> /// </summary>
@@ -258,134 +221,30 @@ namespace Lidgren.Network
/// </summary> /// </summary>
public static string ToHexString(byte[] data) public static string ToHexString(byte[] data)
{ {
char[] c = new char[data.Length * 2]; return ToHexString(data, 0, data.Length);
}
/// <summary>
/// Create a hex string from an array of bytes
/// </summary>
public static string ToHexString(byte[] data, int offset, int length)
{
char[] c = new char[length * 2];
byte b; byte b;
for (int i = 0; i < data.Length; ++i) for (int i = 0; i < length; ++i)
{ {
b = ((byte)(data[i] >> 4)); b = ((byte)(data[offset + i] >> 4));
c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30); c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(data[i] & 0xF)); b = ((byte)(data[offset + i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30); c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
} }
return new string(c); return new string(c);
} }
/// <summary>
/// Gets the local broadcast address
/// </summary>
public static IPAddress GetBroadcastAddress()
{
#if __ANDROID__
try{
Android.Net.Wifi.WifiManager wifi = (Android.Net.Wifi.WifiManager)Android.App.Application.Context.GetSystemService(Android.App.Activity.WifiService);
if (wifi.IsWifiEnabled)
{
var dhcp = wifi.DhcpInfo;
int broadcast = (dhcp.IpAddress & dhcp.Netmask) | ~dhcp.Netmask;
byte[] quads = new byte[4];
for (int k = 0; k < 4; k++)
{
quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
}
return new IPAddress(quads);
}
}
catch // Catch Access Denied Errors
{
return IPAddress.Broadcast;
}
#endif
#if IS_FULL_NET_AVAILABLE
try
{
NetworkInterface ni = GetNetworkInterface();
if (ni == null)
{
return null;
}
IPInterfaceProperties properties = ni.GetIPProperties();
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
{
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
var mask = unicastAddress.IPv4Mask;
byte[] ipAdressBytes = unicastAddress.Address.GetAddressBytes();
byte[] subnetMaskBytes = mask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress);
}
}
}
catch // Catch any errors
{
return IPAddress.Broadcast;
}
#endif
return IPAddress.Broadcast;
}
/// <summary> /// <summary>
/// Gets my local IPv4 address (not necessarily external) and subnet mask /// Returns true if the endpoint supplied is on the same subnet as this host
/// </summary> /// </summary>
public static IPAddress GetMyAddress(out IPAddress mask) public static bool IsLocal(NetEndPoint endPoint)
{
mask = null;
#if __ANDROID__
try
{
Android.Net.Wifi.WifiManager wifi = (Android.Net.Wifi.WifiManager)Android.App.Application.Context.GetSystemService(Android.App.Activity.WifiService);
if (!wifi.IsWifiEnabled) return null;
var dhcp = wifi.DhcpInfo;
int addr = dhcp.IpAddress;
byte[] quads = new byte[4];
for (int k = 0; k < 4; k++)
{
quads[k] = (byte) ((addr >> k * 8) & 0xFF);
}
return new IPAddress(quads);
}
catch // Catch Access Denied errors
{
return null;
}
#endif
#if IS_FULL_NET_AVAILABLE
NetworkInterface ni = GetNetworkInterface();
if (ni == null)
{
mask = null;
return null;
}
IPInterfaceProperties properties = ni.GetIPProperties();
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
{
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
mask = unicastAddress.IPv4Mask;
return unicastAddress.Address;
}
}
#endif
return null;
}
/// <summary>
/// Returns true if the IPEndPoint supplied is on the same subnet as this host
/// </summary>
public static bool IsLocal(IPEndPoint endPoint)
{ {
if (endPoint == null) if (endPoint == null)
return false; return false;
@@ -395,10 +254,10 @@ namespace Lidgren.Network
/// <summary> /// <summary>
/// Returns true if the IPAddress supplied is on the same subnet as this host /// Returns true if the IPAddress supplied is on the same subnet as this host
/// </summary> /// </summary>
public static bool IsLocal(IPAddress remote) public static bool IsLocal(NetAddress remote)
{ {
IPAddress mask; NetAddress mask;
IPAddress local = GetMyAddress(out mask); var local = GetMyAddress(out mask);
if (mask == null) if (mask == null)
return false; return false;
@@ -588,5 +447,11 @@ namespace Lidgren.Network
} }
return bdr.ToString(); return bdr.ToString();
} }
public static byte[] ComputeSHAHash(byte[] bytes)
{
// this is defined in the platform specific files
return ComputeSHAHash(bytes, 0, bytes.Length);
}
} }
} }
@@ -0,0 +1,88 @@
#if __ANDROID__
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Net;
namespace Lidgren.Network
{
public static partial class NetUtility
{
private static byte[] s_randomMacBytes;
static NetUtility()
{
s_randomMacBytes = new byte[8];
MWCRandom.Instance.NextBytes(s_randomMacBytes);
}
[CLSCompliant(false)]
public static ulong GetPlatformSeed(int seedInc)
{
ulong seed = (ulong)Environment.TickCount + (ulong)seedInc;
return seed ^ ((ulong)(new object().GetHashCode()) << 32);
}
/// <summary>
/// Gets my local IPv4 address (not necessarily external) and subnet mask
/// </summary>
public static IPAddress GetMyAddress(out IPAddress mask)
{
mask = null;
try
{
Android.Net.Wifi.WifiManager wifi = (Android.Net.Wifi.WifiManager)Android.App.Application.Context.GetSystemService(Android.App.Activity.WifiService);
if (!wifi.IsWifiEnabled)
return null;
var dhcp = wifi.DhcpInfo;
int addr = dhcp.IpAddress;
byte[] quads = new byte[4];
for (int k = 0; k < 4; k++)
quads[k] = (byte)((addr >> k * 8) & 0xFF);
return new IPAddress(quads);
}
catch // Catch Access Denied errors
{
return null;
}
}
public static byte[] GetMacAddressBytes()
{
return s_randomMacBytes;
}
public static void Sleep(int milliseconds)
{
System.Threading.Thread.Sleep(milliseconds);
}
public static IPAddress GetBroadcastAddress()
{
return IPAddress.Broadcast;
}
public static IPAddress CreateAddressFromBytes(byte[] bytes)
{
return new IPAddress(bytes);
}
private static readonly SHA1 s_sha = SHA1.Create();
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
{
return s_sha.ComputeHash(bytes, offset, count);
}
}
public static partial class NetTime
{
private static readonly long s_timeInitialized = Environment.TickCount;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
}
}
#endif
@@ -0,0 +1,86 @@
#if __CONSTRAINED__ || UNITY_STANDALONE_LINUX
using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography;
namespace Lidgren.Network
{
public static partial class NetUtility
{
private static byte[] s_randomMacBytes;
static NetUtility()
{
s_randomMacBytes = new byte[8];
MWCRandom.Instance.NextBytes(s_randomMacBytes);
}
[CLSCompliant(false)]
public static ulong GetPlatformSeed(int seedInc)
{
ulong seed = (ulong)Environment.TickCount + (ulong)seedInc;
return seed ^ ((ulong)(new object().GetHashCode()) << 32);
}
/// <summary>
/// Gets my local IPv4 address (not necessarily external) and subnet mask
/// </summary>
public static IPAddress GetMyAddress(out IPAddress mask)
{
mask = null;
#if UNITY_ANDROID || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_IOS
try
{
if (!(UnityEngine.Application.internetReachability == UnityEngine.NetworkReachability.NotReachable))
{
return null;
}
return IPAddress.Parse(UnityEngine.Network.player.externalIP);
}
catch // Catch Access Denied errors
{
return null;
}
#endif
return null;
}
public static byte[] GetMacAddressBytes()
{
return s_randomMacBytes;
}
public static IPAddress GetBroadcastAddress()
{
return IPAddress.Broadcast;
}
public static void Sleep(int milliseconds)
{
System.Threading.Thread.Sleep(milliseconds);
}
public static IPAddress CreateAddressFromBytes(byte[] bytes)
{
return new IPAddress(bytes);
}
private static readonly SHA1 s_sha = SHA1.Create();
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
{
return s_sha.ComputeHash(bytes, offset, count);
}
}
public static partial class NetTime
{
private static readonly long s_timeInitialized = Environment.TickCount;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
}
}
#endif
+156
View File
@@ -0,0 +1,156 @@
#if !__ANDROID__ && !__CONSTRAINED__ && !WINDOWS_RUNTIME && !UNITY_STANDALONE_LINUX
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Security.Cryptography;
namespace Lidgren.Network
{
public static partial class NetUtility
{
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
[CLSCompliant(false)]
public static ulong GetPlatformSeed(int seedInc)
{
ulong seed = (ulong)System.Diagnostics.Stopwatch.GetTimestamp();
return seed ^ ((ulong)Environment.WorkingSet + (ulong)seedInc);
}
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
private static NetworkInterface GetNetworkInterface()
{
var computerProperties = IPGlobalProperties.GetIPGlobalProperties();
if (computerProperties == null)
return null;
var nics = NetworkInterface.GetAllNetworkInterfaces();
if (nics == null || nics.Length < 1)
return null;
NetworkInterface best = null;
foreach (NetworkInterface adapter in nics)
{
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback || adapter.NetworkInterfaceType == NetworkInterfaceType.Unknown)
continue;
if (!adapter.Supports(NetworkInterfaceComponent.IPv4))
continue;
if (best == null)
best = adapter;
if (adapter.OperationalStatus != OperationalStatus.Up)
continue;
// make sure this adapter has any ipv4 addresses
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
{
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
// Yes it does, return this network interface.
return adapter;
}
}
}
return best;
}
/// <summary>
/// If available, returns the bytes of the physical (MAC) address for the first usable network interface
/// </summary>
public static byte[] GetMacAddressBytes()
{
var ni = GetNetworkInterface();
if (ni == null)
return null;
return ni.GetPhysicalAddress().GetAddressBytes();
}
public static IPAddress GetBroadcastAddress()
{
var ni = GetNetworkInterface();
if (ni == null)
return null;
var properties = ni.GetIPProperties();
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
{
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
var mask = unicastAddress.IPv4Mask;
byte[] ipAdressBytes = unicastAddress.Address.GetAddressBytes();
byte[] subnetMaskBytes = mask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++)
{
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress);
}
}
return IPAddress.Broadcast;
}
/// <summary>
/// Gets my local IPv4 address (not necessarily external) and subnet mask
/// </summary>
public static IPAddress GetMyAddress(out IPAddress mask)
{
var ni = GetNetworkInterface();
if (ni == null)
{
mask = null;
return null;
}
IPInterfaceProperties properties = ni.GetIPProperties();
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
{
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
mask = unicastAddress.IPv4Mask;
return unicastAddress.Address;
}
}
mask = null;
return null;
}
public static void Sleep(int milliseconds)
{
System.Threading.Thread.Sleep(milliseconds);
}
public static IPAddress CreateAddressFromBytes(byte[] bytes)
{
return new IPAddress(bytes);
}
private static readonly SHA256 s_sha = SHA256.Create();
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
{
return s_sha.ComputeHash(bytes, offset, count);
}
}
public static partial class NetTime
{
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
}
}
#endif
+102
View File
@@ -0,0 +1,102 @@
#if WINDOWS_RUNTIME
//
//
//
// Completely broken right now
//
//
//
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace Lidgren.Network
{
public class NetAddress
{
public static readonly HostName Any = null;
}
public class NetEndPoint
{
public NetEndPoint(HostName hostname, int port) { HostName = hostname; Port = port; }
public NetEndPoint(HostName hostname, string port) { HostName = hostname; Port = int.Parse(port); }
public NetEndPoint(string hostname, int port) { HostName = (hostname == null) ? null : new HostName(hostname); Port = port; }
public HostName HostName;
public int Port;
public override string ToString() { return string.Format("{0}:{1}", HostName, Port); }
public override int GetHashCode()
{
return HostName.RawName.GetHashCode() + Port;
}
public override bool Equals(object obj)
{
var ep = obj as NetEndPoint;
if (ep == null) return false;
if (Port != ep.Port) return false;
return HostName.RawName.Equals(ep.HostName.RawName);
}
};
public static partial class NetUtility
{
[CLSCompliant(false)]
public static ulong GetPlatformSeed(int seedInc)
{
ulong seed = (ulong)Environment.TickCount + (ulong)seedInc;
return seed ^ ((ulong)(new object().GetHashCode()) << 32);
}
/// <summary>
/// Returns the physical (MAC) address for the first usable network interface
/// </summary>
public static PhysicalAddress GetMacAddress()
{
throw new NotImplementedException();
}
public static IPAddress GetBroadcastAddress()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets my local IPv4 address (not necessarily external) and subnet mask
/// </summary>
public static IPAddress GetMyAddress(out IPAddress mask)
{
throw new NotImplementedException();
}
public static void Sleep(int milliseconds)
{
Task.Delay(50).Wait();
}
public static NetAddress CreateAddressFromBytes(byte[] bytes)
{
throw new NotImplementedException();
}
private static readonly SHA1CryptoServiceProvider s_sha = new SHA1CryptoServiceProvider();
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
{
return s_sha.ComputeHash(bytes, offset, count);
}
}
public static partial class NetTime
{
private static readonly long s_timeInitialized = Environment.TickCount;
/// <summary>
/// Get number of seconds since the application started
/// </summary>
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
}
}
#endif
-11
View File
@@ -1,11 +0,0 @@
using System;
namespace Lidgren.Network
{
internal abstract class SenderChannelBase
{
internal abstract NetSendResult Send(float now, NetOutgoingMessage message);
internal abstract void SendQueuedMessages(float now);
internal abstract void Reset();
}
}
+1
View File
@@ -99,6 +99,7 @@
<Compile Include="Source\Map\LocationType.cs" /> <Compile Include="Source\Map\LocationType.cs" />
<Compile Include="Source\Map\SubmarineHull.cs" /> <Compile Include="Source\Map\SubmarineHull.cs" />
<Compile Include="Source\Networking\NetConfig.cs" /> <Compile Include="Source\Networking\NetConfig.cs" />
<Compile Include="Source\Networking\NetStats.cs" />
<Compile Include="Source\Particles\ParticleEmitter.cs" /> <Compile Include="Source\Particles\ParticleEmitter.cs" />
<Compile Include="Source\Screens\ServerListScreen.cs" /> <Compile Include="Source\Screens\ServerListScreen.cs" />
<Compile Include="Source\Utils\Rand.cs" /> <Compile Include="Source\Utils\Rand.cs" />
+2 -1
View File
@@ -61,7 +61,8 @@
<Item <Item
name="Railgun Shell" name="Railgun Shell"
pickdistance="200"> pickdistance="200"
price="100">
<Sprite texture ="railgunshell.png" depth ="0.5"/> <Sprite texture ="railgunshell.png" depth ="0.5"/>
+1 -1
View File
@@ -299,7 +299,7 @@ namespace Barotrauma
//the collision is ignored if the lowest limb is under the platform //the collision is ignored if the lowest limb is under the platform
if (lowestLimb==null || lowestLimb.Position.Y < structure.Rect.Y) return false; if (lowestLimb==null || lowestLimb.Position.Y < structure.Rect.Y) return false;
} }
else if (structure.StairDirection!=Direction.None) else if (structure.StairDirection!=Direction.None && lowestLimb != null)
{ {
if (targetMovement.Y < 0.5f) if (targetMovement.Y < 0.5f)
{ {
+9 -1
View File
@@ -21,6 +21,8 @@ namespace Barotrauma
public bool Wrap; public bool Wrap;
private float textDepth;
public override Vector4 Padding public override Vector4 Padding
{ {
get { return padding; } get { return padding; }
@@ -41,6 +43,12 @@ namespace Barotrauma
} }
} }
public float TextDepth
{
get { return textDepth; }
set { textDepth = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
public bool LimitText public bool LimitText
{ {
get; get;
@@ -221,7 +229,7 @@ namespace Barotrauma
new Vector2(rect.X, rect.Y) + textPos, new Vector2(rect.X, rect.Y) + textPos,
textColor * (textColor.A / 255.0f), textColor * (textColor.A / 255.0f),
0.0f, origin, 1.0f, 0.0f, origin, 1.0f,
SpriteEffects.None, 0.0f); SpriteEffects.None, textDepth);
} }
DrawChildren(spriteBatch); DrawChildren(spriteBatch);
@@ -9,6 +9,8 @@ namespace Barotrauma
Client traitor; Client traitor;
Client target; Client target;
private Character traitorCharacter, targetCharacter;
public TraitorMode(GameModePreset preset) public TraitorMode(GameModePreset preset)
: base(preset) : base(preset)
{ {
@@ -47,34 +49,38 @@ namespace Barotrauma
} }
target = GameMain.Server.connectedClients[targetIndex]; target = GameMain.Server.connectedClients[targetIndex];
traitorCharacter = traitor.character;
targetCharacter = target.character;
GameMain.Server.NewTraitor(traitor, target); GameMain.Server.NewTraitor(traitor, target);
} }
else else
{ {
if (target.character.IsDead) if (target.character == null || target.character.IsDead)
{ {
string endMessage = traitor.character.Info.Name + " was a traitor! "; string endMessage = traitorCharacter.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her"; endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ". The task was successful."; endMessage += " task was to assassinate " + targetCharacter.Name + ". The task was successful.";
End(endMessage); End(endMessage);
} }
else if (traitor.character.IsDead) else if (traitor.character == null || traitor.character.IsDead)
{ {
string endMessage = traitor.character.Info.Name + " was a traitor! "; string endMessage = traitorCharacter.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her"; //TODO: remove references to traitor.character
endMessage += " task was to assassinate " + target.character.Info.Name + ", but "; endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "he" : "she"; endMessage += " task was to assassinate " + targetCharacter.Name + ", but ";
endMessage += " got " + ((traitor.character.Info.Gender == Gender.Male) ? "himself" : "herself"); endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
endMessage += " got " + ((traitorCharacter.Info.Gender == Gender.Male) ? "himself" : "herself");
endMessage += " killed before completing it."; endMessage += " killed before completing it.";
End(endMessage); End(endMessage);
return; return;
} }
else if (Level.Loaded.AtEndPosition) else if (Level.Loaded.AtEndPosition)
{ {
string endMessage = traitor.character.Info.Name + " was a traitor! "; string endMessage = traitorCharacter.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her"; endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ". "; endMessage += " task was to assassinate " + targetCharacter.Name + ". ";
endMessage += "The task was unsuccessful - the has submarine reached its destination."; endMessage += "The task was unsuccessful - the has submarine reached its destination.";
End(endMessage); End(endMessage);
return; return;
@@ -21,7 +21,7 @@ namespace Barotrauma
GameMain.GameSession = new GameSession(Submarine.Loaded, "", GameModePreset.list.Find(gm => gm.Name.ToLower()=="tutorial")); GameMain.GameSession = new GameSession(Submarine.Loaded, "", GameModePreset.list.Find(gm => gm.Name.ToLower()=="tutorial"));
GameMain.GameSession.StartShift("tutorial"); GameMain.GameSession.StartShift("tuto");
GameMain.GameSession.taskManager.Tasks.Clear(); GameMain.GameSession.taskManager.Tasks.Clear();
@@ -164,6 +164,7 @@ namespace Barotrauma
while (Math.Abs(reactor.Temperature-3000.0f) > 100.0f) while (Math.Abs(reactor.Temperature-3000.0f) > 100.0f)
{ {
reactor.AutoTemp = false;
reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f); reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -131,6 +131,18 @@ namespace Barotrauma.Items.Components
{ {
picker = character; picker = character;
if (item.body == null)
{
if (body!=null)
{
item.body = body;
}
else
{
return;
}
}
if (!item.body.Enabled) if (!item.body.Enabled)
{ {
Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand); Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand);
@@ -41,6 +41,7 @@ namespace Barotrauma.Items.Components
null, null, true); null, null, true);
textBlock.Font = GUI.SmallFont; textBlock.Font = GUI.SmallFont;
textBlock.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); textBlock.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
textBlock.TextDepth = item.Sprite.Depth - 0.0001f;
} }
return textBlock; return textBlock;
} }
@@ -103,7 +103,11 @@ namespace Barotrauma.Items.Components
if (stickJoint != null && doesStick) if (stickJoint != null && doesStick)
{ {
if (stickTarget!=null) item.body.FarseerBody.RestoreCollisionWith(stickTarget); if (stickTarget != null)
{
item.body.FarseerBody.RestoreCollisionWith(stickTarget);
stickTarget = null;
}
GameMain.World.RemoveJoint(stickJoint); GameMain.World.RemoveJoint(stickJoint);
stickJoint = null; stickJoint = null;
} }
@@ -111,25 +115,19 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam) public override void Update(float deltaTime, Camera cam)
{ {
if (stickJoint != null) if (stickJoint != null && stickJoint.JointTranslation < 0.01f)
{ {
if (stickJoint.JointTranslation < 0.01f) if (stickTarget!=null)
{ {
if (stickTarget!=null) item.body.FarseerBody.RestoreCollisionWith(stickTarget);
{ stickTarget = null;
item.body.FarseerBody.RestoreCollisionWith(stickTarget);
}
GameMain.World.RemoveJoint(stickJoint);
stickJoint = null;
IsActive = false;
} }
}
else GameMain.World.RemoveJoint(stickJoint);
{ stickJoint = null;
IsActive = false;
} IsActive = false;
}
} }
private bool OnProjectileCollision(Fixture f1, Fixture f2, Contact contact) private bool OnProjectileCollision(Fixture f1, Fixture f2, Contact contact)
+8
View File
@@ -230,6 +230,13 @@ namespace Barotrauma
int itemCapacity = item.Capacity; int itemCapacity = item.Capacity;
if (itemCapacity == 0) return; if (itemCapacity == 0) return;
#if DEBUG
System.Diagnostics.Debug.Assert(slotIndex>=0 && slotIndex<items.Length);
#else
if (slotIndex<0 || slotIndex>=items.Length) return;
#endif
Rectangle containerRect = new Rectangle(rect.X - 5, rect.Y - (rect.Height + 10) * itemCapacity - 5, Rectangle containerRect = new Rectangle(rect.X - 5, rect.Y - (rect.Height + 10) * itemCapacity - 5,
rect.Width + 10, rect.Height + (rect.Height + 10) * itemCapacity + 10); rect.Width + 10, rect.Height + (rect.Height + 10) * itemCapacity + 10);
@@ -253,6 +260,7 @@ namespace Barotrauma
protected void DrawSlot(SpriteBatch spriteBatch, Rectangle rect, Item item, bool isHighLighted, bool isSubSlot) protected void DrawSlot(SpriteBatch spriteBatch, Rectangle rect, Item item, bool isHighLighted, bool isSubSlot)
{ {
GUI.DrawRectangle(spriteBatch, rect, (isHighLighted ? Color.Red : Color.White) * ((isSubSlot) ? 0.1f : 0.3f), true); GUI.DrawRectangle(spriteBatch, rect, (isHighLighted ? Color.Red : Color.White) * ((isSubSlot) ? 0.1f : 0.3f), true);
GUI.DrawRectangle(spriteBatch, rect, (isHighLighted ? Color.Red : Color.White) * ((isSubSlot) ? 0.2f : 0.4f), false);
if (item == null) return; if (item == null) return;
+19 -6
View File
@@ -1163,11 +1163,19 @@ namespace Barotrauma
switch (type) switch (type)
{ {
case NetworkEventType.DropItem: case NetworkEventType.DropItem:
if (body != null) body.FillNetworkData(type, message); if (body != null)
{
message.Write(body.SimPosition.X);
message.Write(body.SimPosition.Y);
}
break; break;
case NetworkEventType.UpdateComponent: case NetworkEventType.UpdateComponent:
message.Write((int)data);
components[(int)data].FillNetworkData(type, message); int componentIndex = (int)data;
if (componentIndex < 0 || componentIndex >= components.Count) return;
message.Write((byte)componentIndex);
components[componentIndex].FillNetworkData(type, message);
break; break;
case NetworkEventType.UpdateProperty: case NetworkEventType.UpdateProperty:
var allProperties = GetProperties<InGameEditable>(); var allProperties = GetProperties<InGameEditable>();
@@ -1215,11 +1223,16 @@ namespace Barotrauma
switch (type) switch (type)
{ {
case NetworkEventType.DropItem: case NetworkEventType.DropItem:
if (body != null) body.ReadNetworkData(type, message); Vector2 newSimPos = Vector2.Zero;
if (body != null)
{
newSimPos = new Vector2(message.ReadFloat(), message.ReadFloat());
}
SetTransform(newSimPos, body.Rotation);
Drop(null, false); Drop(null, false);
break; break;
case NetworkEventType.UpdateComponent: case NetworkEventType.UpdateComponent:
int componentIndex = message.ReadInt32(); int componentIndex = message.ReadByte();
if (componentIndex < 0 || componentIndex > components.Count - 1) return; if (componentIndex < 0 || componentIndex > components.Count - 1) return;
components[componentIndex].ReadNetworkData(type, message); components[componentIndex].ReadNetworkData(type, message);
break; break;
@@ -1272,7 +1285,7 @@ namespace Barotrauma
base.Remove(); base.Remove();
//sprite.Remove(); //sprite.Remove();
if (body != null) body.Remove(); //if (body != null) body.Remove();
foreach (ItemComponent ic in components) foreach (ItemComponent ic in components)
{ {
+9 -1
View File
@@ -255,7 +255,15 @@ namespace Barotrauma
assignedWayPoints[i] = GetRandom(SpawnType.Human); assignedWayPoints[i] = GetRandom(SpawnType.Human);
} }
for (int i = 0; i < assignedWayPoints.Length; i++ )
{
if (assignedWayPoints[i]==null)
{
DebugConsole.ThrowError("Couldn't find a waypoint for " + crew[i].Name + "!");
assignedWayPoints[i] = WayPointList[0];
}
}
return assignedWayPoints; return assignedWayPoints;
} }
+1 -1
View File
@@ -244,7 +244,7 @@ namespace Barotrauma.Networking
NetConnectionStatus connectionStatus = (NetConnectionStatus)inc.ReadByte(); NetConnectionStatus connectionStatus = (NetConnectionStatus)inc.ReadByte();
Debug.WriteLine(connectionStatus); Debug.WriteLine(connectionStatus);
if (connectionStatus != NetConnectionStatus.Connected) if (connectionStatus == NetConnectionStatus.Disconnected)
{ {
string denyMessage = inc.ReadString(); string denyMessage = inc.ReadString();
+62 -10
View File
@@ -16,6 +16,8 @@ namespace Barotrauma.Networking
//for keeping track of disconnected clients in case the reconnect shortly after //for keeping track of disconnected clients in case the reconnect shortly after
private List<Client> disconnectedClients = new List<Client>(); private List<Client> disconnectedClients = new List<Client>();
private NetStats netStats;
//is the server running //is the server running
bool started; bool started;
@@ -34,6 +36,20 @@ namespace Barotrauma.Networking
private string password; private string password;
private bool autoRestart;
public bool AutoRestart
{
get { return (connectedClients.Count==0) ? false : autoRestart; }
set
{
autoRestart = value;
AutoRestartTimer = autoRestart ? 20.0f : 0.0f;
}
}
public float AutoRestartTimer;
public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10) public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10)
{ {
var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 290, 20, 150, 25), "End round", Alignment.TopLeft, GUI.Style, inGameHUD); var endRoundButton = new GUIButton(new Rectangle(GameMain.GraphicsWidth - 290, 20, 150, 25), "End round", Alignment.TopLeft, GUI.Style, inGameHUD);
@@ -44,9 +60,13 @@ namespace Barotrauma.Networking
config = new NetPeerConfiguration("barotrauma"); config = new NetPeerConfiguration("barotrauma");
netStats = new NetStats();
#if DEBUG #if DEBUG
config.SimulatedLoss = 0.2f; config.SimulatedLoss = 0.2f;
config.SimulatedMinimumLatency = 0.3f; config.SimulatedMinimumLatency = 0.3f;
config.SimulatedDuplicatesChance = 0.05f;
config.SimulatedMinimumLatency = 0.1f;
#endif #endif
config.Port = port; config.Port = port;
Port = port; Port = port;
@@ -61,9 +81,9 @@ namespace Barotrauma.Networking
config.DisableMessageType(NetIncomingMessageType.DebugMessage | config.DisableMessageType(NetIncomingMessageType.DebugMessage |
NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt | NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt |
NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error); NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error);
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval); config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
CoroutineManager.StartCoroutine(StartServer(isPublic)); CoroutineManager.StartCoroutine(StartServer(isPublic));
} }
@@ -190,21 +210,23 @@ namespace Barotrauma.Networking
if (response.ErrorException != null) if (response.ErrorException != null)
{ {
DebugConsole.ThrowError("Error while connecting to master server", response.ErrorException); DebugConsole.ThrowError("Error while registering to master server", response.ErrorException);
registeredToMaster = false; registeredToMaster = false;
return; return;
} }
if (response.StatusCode != System.Net.HttpStatusCode.OK) if (response.StatusCode != System.Net.HttpStatusCode.OK)
{ {
DebugConsole.ThrowError("Error while connecting to master server (" + response.StatusCode + ": " + response.StatusDescription + ")"); DebugConsole.NewMessage("Error while reporting to master server (" + response.StatusCode + ": " + response.StatusDescription + ")", Color.Red);
registeredToMaster = false; //registeredToMaster = false;
return; return;
} }
} }
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {
if (GameMain.DebugDraw) netStats.Update(deltaTime);
if (!started) return; if (!started) return;
base.Update(deltaTime); base.Update(deltaTime);
@@ -212,6 +234,24 @@ namespace Barotrauma.Networking
if (gameStarted) if (gameStarted)
{ {
inGameHUD.Update((float)Physics.step); inGameHUD.Update((float)Physics.step);
//if all characters dead
if (AutoRestart &&
connectedClients.Find(c => c.character != null && !c.character.IsDead)==null &&
(myCharacter == null || myCharacter.IsDead))
{
EndButtonHit(null, null);
AutoRestartTimer = 20.0f;
return;
}
}
else if (autoRestart && Screen.Selected == GameMain.NetLobbyScreen && connectedClients.Count>0)
{
AutoRestartTimer -= deltaTime;
if (AutoRestartTimer < 0.0f)
{
StartGameClicked(null,null);
}
} }
for (int i = disconnectedClients.Count - 1; i >= 0; i-- ) for (int i = disconnectedClients.Count - 1; i >= 0; i-- )
@@ -628,7 +668,7 @@ namespace Barotrauma.Networking
List<Character> crew = new List<Character>(); List<Character> crew = new List<Character>();
WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos); WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos);
for (int i = 0; i < connectedClients.Count; i++) for (int i = 0; i < connectedClients.Count; i++)
{ {
connectedClients[i].character = new Character( connectedClients[i].character = new Character(
@@ -817,7 +857,7 @@ namespace Barotrauma.Networking
public void NewTraitor(Client traitor, Client target) public void NewTraitor(Client traitor, Client target)
{ {
new GUIMessageBox("New traitor", traitor.name + " is the traitor and the target is " + target.name+"."); //new GUIMessageBox("New traitor", traitor.name + " is the traitor and the target is " + target.name+".");
NetOutgoingMessage msg = server.CreateMessage(); NetOutgoingMessage msg = server.CreateMessage();
msg.Write((byte)PacketTypes.Traitor); msg.Write((byte)PacketTypes.Traitor);
@@ -846,15 +886,27 @@ namespace Barotrauma.Networking
spriteBatch.DrawString(GUI.SmallFont, "Sent bytes: " + server.Statistics.SentBytes, new Vector2(x + 10, y + 75), Color.White); spriteBatch.DrawString(GUI.SmallFont, "Sent bytes: " + server.Statistics.SentBytes, new Vector2(x + 10, y + 75), Color.White);
spriteBatch.DrawString(GUI.SmallFont, "Sent packets: " + server.Statistics.SentPackets, new Vector2(x + 10, y + 90), Color.White); spriteBatch.DrawString(GUI.SmallFont, "Sent packets: " + server.Statistics.SentPackets, new Vector2(x + 10, y + 90), Color.White);
int resentMessages = 0;
y += 110; y += 110;
foreach (Client c in connectedClients) foreach (Client c in connectedClients)
{ {
spriteBatch.DrawString(GUI.SmallFont, c.name + ":", new Vector2(x + 10, y), Color.White); spriteBatch.DrawString(GUI.SmallFont, c.name + ":", new Vector2(x + 10, y), Color.White);
spriteBatch.DrawString(GUI.SmallFont, "- avg roundtrip " + c.Connection.AverageRoundtripTime+" s", new Vector2(x + 20, y + 15), Color.White); spriteBatch.DrawString(GUI.SmallFont, "- avg roundtrip " + c.Connection.AverageRoundtripTime+" s", new Vector2(x + 20, y + 15), Color.White);
y += 50; spriteBatch.DrawString(GUI.SmallFont, "- resent messages " + c.Connection.Statistics.ResentMessages, new Vector2(x + 20, y + 30), Color.White);
resentMessages += (int)c.Connection.Statistics.ResentMessages;
y += 50;
} }
netStats.AddValue(NetStats.NetStatType.ResentMessages, resentMessages);
netStats.AddValue(NetStats.NetStatType.SentBytes, server.Statistics.SentBytes);
netStats.AddValue(NetStats.NetStatType.ReceivedBytes, server.Statistics.ReceivedBytes);
netStats.Draw(spriteBatch, new Rectangle(200,0,800,200));
} }
public bool UpdateNetLobby(object obj) public bool UpdateNetLobby(object obj)
+163
View File
@@ -0,0 +1,163 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Barotrauma.Networking
{
class NetStats
{
public enum NetStatType
{
SentBytes = 0,
ReceivedBytes = 1,
ResentMessages = 2
}
private Graph[] graphs;
private Queue<float>[] valueQueue;
private float[] lastValues;
const float UpdateInterval = 1.0f;
float updateTimer;
public NetStats()
{
graphs = new Graph[3];
valueQueue = new Queue<float>[3];
for (int i = 0; i < 3; i++ )
{
valueQueue[i] = new Queue<float>();
graphs[i] = new Graph();
}
lastValues = new float[3];
}
public void AddValue(NetStatType statType, float value)
{
float valueChange = value - lastValues[(int)statType];
valueQueue[(int)statType].Enqueue(valueChange);
lastValues[(int)statType] = value;
}
public void Update(float deltaTime)
{
updateTimer -= deltaTime;
if (updateTimer > 0.0f) return;
for (int i = 0; i<3; i++)
{
int valueCount = valueQueue[i].Count;
float totalValue = 0.0f;
while (valueQueue[i].Count>1)
{
totalValue += valueQueue[i].Dequeue();
}
graphs[i].Update(valueCount==0 ? 0.0f : totalValue/valueCount);
}
updateTimer = UpdateInterval;
}
public void Draw(SpriteBatch spriteBatch, Rectangle rect)
{
GUI.DrawRectangle(spriteBatch, rect, Color.Black*0.4f, true);
graphs[(int)NetStatType.ReceivedBytes].Draw(spriteBatch, rect, null, 0.0f, Color.Cyan);
graphs[(int)NetStatType.SentBytes].Draw(spriteBatch, rect, null, 0.0f, Color.Orange);
graphs[(int)NetStatType.ResentMessages].Draw(spriteBatch, rect, null, 0.0f, Color.Red);
spriteBatch.DrawString(GUI.SmallFont, "Max received: "+graphs[(int)NetStatType.ReceivedBytes].LargestValue()+" bytes/s",
new Vector2(rect.X + 10, rect.Y+10), Color.Cyan);
spriteBatch.DrawString(GUI.SmallFont, "Max sent: " + graphs[(int)NetStatType.SentBytes].LargestValue() + " bytes/s",
new Vector2(rect.X + 10, rect.Y + 30), Color.Orange);
spriteBatch.DrawString(GUI.SmallFont, "Max resent: " + graphs[(int)NetStatType.ResentMessages].LargestValue() + " messages/s",
new Vector2(rect.X + 10, rect.Y + 50), Color.Red);
}
}
class Graph
{
const int ArraySize = 100;
private float[] values;
public Graph()
{
values = new float[ArraySize];
}
public float LargestValue()
{
float maxValue = 0.0f;
for (int i = 0; i < values.Length; i++)
{
if (values[i] > maxValue) maxValue = values[i];
}
return maxValue;
}
public void Update(float newValue)
{
for (int i = values.Length-1; i > 0; i--)
{
values[i] = values[i - 1];
}
values[0] = newValue;
}
public void Draw(SpriteBatch spriteBatch, Rectangle rect, float? maxVal, float xOffset, Color color)
{
float graphMaxVal = 1.0f;
if (maxVal == null)
{
graphMaxVal = LargestValue();
}
else if (maxVal > 0.0f)
{
graphMaxVal = (float)maxVal;
}
float lineWidth = (float)rect.Width / (float)(values.Length - 2);
float yScale = (float)rect.Height / graphMaxVal;
GUI.DrawRectangle(spriteBatch, rect, Color.White);
Vector2 prevPoint = new Vector2(rect.Right, rect.Bottom - (values[1] + (values[0] - values[1]) * xOffset) * yScale);
float currX = rect.Right - ((xOffset - 1.0f) * lineWidth);
for (int i = 1; i < values.Length - 1; i++)
{
currX -= lineWidth;
Vector2 newPoint = new Vector2(currX, rect.Bottom - values[i] * yScale);
GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color);
prevPoint = newPoint;
}
Vector2 lastPoint = new Vector2(rect.X,
rect.Bottom - (values[values.Length - 1] + (values[values.Length - 2] - values[values.Length - 1]) * xOffset) * yScale);
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color);
}
}
}
+9 -2
View File
@@ -124,8 +124,15 @@ namespace Barotrauma.Networking
System.Diagnostics.Debug.WriteLine("Networkevent entity: "+e.ToString()); System.Diagnostics.Debug.WriteLine("Networkevent entity: "+e.ToString());
//System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e); //System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e);
try
e.ReadNetworkData(eventType, message); {
e.ReadNetworkData(eventType, message);
}
catch
{
DebugConsole.ThrowError("Received invalid network message");
return false;
}
return true; return true;
} }
+4 -4
View File
@@ -28,19 +28,19 @@ namespace Barotrauma
{ {
using (var game = new GameMain()) using (var game = new GameMain())
{ {
//#if !DEBUG #if !DEBUG
try try
{ {
//#endif #endif
game.Run(); game.Run();
//#if !DEBUG #if !DEBUG
} }
catch (Exception e) catch (Exception e)
{ {
CrashDump(game, "crashreport.txt", e); CrashDump(game, "crashreport.txt", e);
} }
//#endif #endif
} }
} }
+57 -7
View File
@@ -18,7 +18,7 @@ namespace Barotrauma
private GUIListBox playerList; private GUIListBox playerList;
private GUIListBox subList, modeList, chatBox; private GUIListBox subList, modeList, chatBox;
private GUIListBox jobList; private GUIListBox jobList;
private GUITextBox textBox, seedBox; private GUITextBox textBox, seedBox;
@@ -29,6 +29,8 @@ namespace Barotrauma
private GUIFrame playerFrame; private GUIFrame playerFrame;
private GUITickBox autoRestartBox;
private float camAngle; private float camAngle;
public bool IsServer; public bool IsServer;
@@ -91,10 +93,18 @@ namespace Barotrauma
} }
} }
//public string DurationText() private float autoRestartTimer;
//{
// return "Duration: " + GameDuration.TotalMinutes + " min"; public string AutoRestartText()
//} {
if (GameMain.Server != null)
{
if (!GameMain.Server.AutoRestart) return "";
return "Restarting in " + (int)GameMain.Server.AutoRestartTimer;
}
if (autoRestartTimer == 0.0f) return "";
return "Restarting in " + (int)autoRestartTimer;
}
public NetLobbyScreen() public NetLobbyScreen()
{ {
@@ -194,7 +204,6 @@ namespace Barotrauma
//gamemode description ------------------------------------------------------------------ //gamemode description ------------------------------------------------------------------
var modeDescription = new GUITextBlock( var modeDescription = new GUITextBlock(
new Rectangle(columnX, 150, (int)(columnWidth * 1.5f), infoFrame.Rect.Height - 150 - 80), new Rectangle(columnX, 150, (int)(columnWidth * 1.5f), infoFrame.Rect.Height - 150 - 80),
"", GUI.Style, Alignment.TopLeft, Alignment.TopLeft, infoFrame, true, GameMain.GraphicsWidth>1024 ? GUI.Font : GUI.SmallFont); "", GUI.Style, Alignment.TopLeft, Alignment.TopLeft, infoFrame, true, GameMain.GraphicsWidth>1024 ? GUI.Font : GUI.SmallFont);
@@ -214,6 +223,14 @@ namespace Barotrauma
seedBox.OnTextChanged = SelectSeed; seedBox.OnTextChanged = SelectSeed;
LevelSeed = ToolBox.RandomSeed(8); LevelSeed = ToolBox.RandomSeed(8);
//automatic restart ------------------------------------------------------------------
autoRestartBox = new GUITickBox(new Rectangle(columnX, 190, 20, 20), "Automatic restart", Alignment.TopLeft, infoFrame);
autoRestartBox.OnSelected = ToggleAutoRestart;
var restartText = new GUITextBlock(new Rectangle(columnX, 210, 20, 20), "", GUI.Style, infoFrame);
restartText.TextGetter = AutoRestartText;
//server info ------------------------------------------------------------------ //server info ------------------------------------------------------------------
var serverName = new GUITextBox(new Rectangle(0, 0, 200, 20), null, null, Alignment.TopLeft, Alignment.TopLeft, GUI.Style, infoFrame); var serverName = new GUITextBox(new Rectangle(0, 0, 200, 20), null, null, Alignment.TopLeft, Alignment.TopLeft, GUI.Style, infoFrame);
@@ -230,6 +247,8 @@ namespace Barotrauma
public override void Deselect() public override void Deselect()
{ {
textBox.Deselect(); textBox.Deselect();
seedBox.Text = ToolBox.RandomSeed(8);
} }
public override void Select() public override void Select()
@@ -372,6 +391,20 @@ namespace Barotrauma
return false; return false;
} }
private bool ToggleAutoRestart(object obj)
{
if (GameMain.Server == null) return false;
GUITickBox tickBox = obj as GUITickBox;
if (tickBox==null) return false;
GameMain.Server.AutoRestart = tickBox.Selected;
GameMain.Server.UpdateNetLobby(obj);
return true;
}
private bool SelectMap(GUIComponent component, object obj) private bool SelectMap(GUIComponent component, object obj)
{ {
if (GameMain.Server != null) GameMain.Server.UpdateNetLobby(obj); if (GameMain.Server != null) GameMain.Server.UpdateNetLobby(obj);
@@ -491,6 +524,11 @@ namespace Barotrauma
if (jobInfoFrame != null) jobInfoFrame.Update((float)deltaTime); if (jobInfoFrame != null) jobInfoFrame.Update((float)deltaTime);
if (playerFrame != null) playerFrame.Update((float)deltaTime); if (playerFrame != null) playerFrame.Update((float)deltaTime);
if (autoRestartTimer != 0.0f && autoRestartBox.Selected)
{
autoRestartTimer = Math.Max(autoRestartTimer - (float)deltaTime, 0.0f);
}
//durationBar.BarScroll = Math.Max(durationBar.BarScroll, 1.0f / 60.0f); //durationBar.BarScroll = Math.Max(durationBar.BarScroll, 1.0f / 60.0f);
} }
@@ -722,13 +760,15 @@ namespace Barotrauma
//msg.Write(durationBar.BarScroll); //msg.Write(durationBar.BarScroll);
msg.Write(LevelSeed); msg.Write(LevelSeed);
msg.Write(GameMain.Server==null ? false : GameMain.Server.AutoRestart);
msg.Write(GameMain.Server == null ? 0.0f : GameMain.Server.AutoRestartTimer);
msg.Write((byte)(playerList.CountChildren)); msg.Write((byte)(playerList.CountChildren));
for (int i = 0; i < playerList.CountChildren; i++) for (int i = 0; i < playerList.CountChildren; i++)
{ {
string clientName = playerList.children[i].UserData as string; string clientName = playerList.children[i].UserData as string;
msg.Write(clientName==null ? "" : clientName); msg.Write(clientName==null ? "" : clientName);
} }
} }
@@ -741,6 +781,10 @@ namespace Barotrauma
//float durationScroll = 0.0f; //float durationScroll = 0.0f;
string levelSeed = ""; string levelSeed = "";
bool autoRestart = false;
float restartTimer = 0.0f;
try try
{ {
mapName = msg.ReadString(); mapName = msg.ReadString();
@@ -755,6 +799,9 @@ namespace Barotrauma
levelSeed = msg.ReadString(); levelSeed = msg.ReadString();
autoRestart = msg.ReadBoolean();
restartTimer = msg.ReadFloat();
int playerCount = msg.ReadByte(); int playerCount = msg.ReadByte();
playerList.ClearChildren(); playerList.ClearChildren();
@@ -773,6 +820,9 @@ namespace Barotrauma
modeList.Select(modeIndex); modeList.Select(modeIndex);
autoRestartBox.Selected = autoRestart;
autoRestartTimer = restartTimer;
//durationBar.BarScroll = durationScroll; //durationBar.BarScroll = durationScroll;
LevelSeed = levelSeed; LevelSeed = levelSeed;
@@ -69,7 +69,7 @@ namespace Barotrauma
new GUITextBlock(new Rectangle(middleX + columnX[0], 30, 0, 30), "Name", GUI.Style, menu); new GUITextBlock(new Rectangle(middleX + columnX[0], 30, 0, 30), "Name", GUI.Style, menu);
new GUITextBlock(new Rectangle(middleX + columnX[1], 30, 0, 30), "Players", GUI.Style, menu); new GUITextBlock(new Rectangle(middleX + columnX[1], 30, 0, 30), "Players", GUI.Style, menu);
new GUITextBlock(new Rectangle(middleX + columnX[2], 30, 0, 30), "Running", GUI.Style, menu); new GUITextBlock(new Rectangle(middleX + columnX[2], 30, 0, 30), "Round started", GUI.Style, menu);
joinButton = new GUIButton(new Rectangle(-170, 0, 150, 30), "Refresh", Alignment.BottomRight, GUI.Style, menu); joinButton = new GUIButton(new Rectangle(-170, 0, 150, 30), "Refresh", Alignment.BottomRight, GUI.Style, menu);
joinButton.OnClicked = RefreshServers; joinButton.OnClicked = RefreshServers;
@@ -90,8 +90,7 @@ namespace Barotrauma
base.Select(); base.Select();
//RefreshServers(null, null); RefreshServers(null, null);
//UpdateServerList();
} }
private bool SelectServer(GUIComponent component, object obj) private bool SelectServer(GUIComponent component, object obj)
+16
View File
@@ -1,4 +1,20 @@
---------------------------------------------------------------------------------------------------------
v0.2.1
---------------------------------------------------------------------------------------------------------
Multiplayer:
- fixed a bug that crashed the game after successfully retrieving an artifact in quest mode
- fixed client not disconnecting when going back to the main menu
- fixed a bug that caused some map seeds to throw an error
- players can be kicked out of the server by selecting them in the player list, not just through
the debug console
Items:
- all wires can be used for electrical repairs, not just ones named ''Wire''
- broken doors can only be fixed by mechanics
- fixed a bug that sometimes made it impossible to pick/select items after reattaching them on a wall
- wires are disconnected and dropped if the item at either end is removed
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.2 v0.2
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
Binary file not shown.