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

View File

@@ -29,7 +29,7 @@
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Windows\Release\</OutputPath>
<OutputPath>..\Subsurface\bin\Windows\Release\</OutputPath>
<DefineConstants>TRACE;WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

View File

@@ -542,7 +542,7 @@ namespace Launcher2
{
if (e.Error!=null)
{
string errorMsg = "Error while downloading: " + e.Error.InnerException.Message;
string errorMsg = "Error while downloading: " + e.Error;
GUITextBlock textBlock = new GUITextBlock(
new Rectangle(0, 0, 0, 0),

View File

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

View File

@@ -1,172 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// AES encryption
/// </summary>
public class NetAESEncryption : INetEncryption
public class NetAESEncryption : NetCryptoProviderBase
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
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])
public NetAESEncryption(NetPeer peer)
: base(peer, new AesCryptoServiceProvider())
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
public NetAESEncryption(NetPeer peer, string key)
: base(peer, new AesCryptoServiceProvider())
{
#if !IOS && !__ANDROID__
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
SetKey(key);
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
public NetAESEncryption(NetPeer peer, byte[] data, int offset, int count)
: base(peer, new AesCryptoServiceProvider())
{
#if !IOS && !__ANDROID__
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
SetKey(data, offset, count);
}
}
}

View File

@@ -6,7 +6,7 @@ namespace Lidgren.Network
/// <summary>
/// Base for a non-threadsafe encryption class
/// </summary>
public abstract class NetBlockEncryptionBase : INetEncryption
public abstract class NetBlockEncryptionBase : NetEncryption
{
// temporary space for one block to avoid reallocating every time
private byte[] m_tmp;
@@ -19,7 +19,8 @@ namespace Lidgren.Network
/// <summary>
/// NetBlockEncryptionBase constructor
/// </summary>
public NetBlockEncryptionBase()
public NetBlockEncryptionBase(NetPeer peer)
: base(peer)
{
m_tmp = new byte[BlockSize];
}
@@ -27,7 +28,7 @@ namespace Lidgren.Network
/// <summary>
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
public override bool Encrypt(NetOutgoingMessage msg)
{
int payloadBitLength = msg.LengthBits;
int numBytes = msg.LengthBytes;
@@ -55,7 +56,7 @@ namespace Lidgren.Network
/// </summary>
/// <param name="msg">message to decrypt</param>
/// <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 blockSize = BlockSize;

View File

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

View File

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

View File

@@ -1,164 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// DES encryption
/// </summary>
public class NetDESEncryption : INetEncryption
public class NetDESEncryption : NetCryptoProviderBase
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
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])
public NetDESEncryption(NetPeer peer)
: base(peer, new DESCryptoServiceProvider())
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
public NetDESEncryption(NetPeer peer, string key)
: base(peer, new DESCryptoServiceProvider())
{
try
{
// 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;
SetKey(key);
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
public NetDESEncryption(NetPeer peer, byte[] data, int offset, int count)
: base(peer, new DESCryptoServiceProvider())
{
try
{
// 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;
SetKey(data, offset, count);
}
}
}
}

View File

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

View File

@@ -1,165 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// RC2 encryption
/// </summary>
public class NetRC2Encryption : INetEncryption
public class NetRC2Encryption : NetCryptoProviderBase
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
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])
public NetRC2Encryption(NetPeer peer)
: base(peer, new RC2CryptoServiceProvider())
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
public NetRC2Encryption(NetPeer peer, string key)
: base(peer, new RC2CryptoServiceProvider())
{
try
{
// 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;
SetKey(key);
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
public NetRC2Encryption(NetPeer peer, byte[] data, int offset, int count)
: base(peer, new RC2CryptoServiceProvider())
{
try
{
// 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;
SetKey(data, offset, count);
}
}
}
}

View File

@@ -1,164 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace Lidgren.Network
{
/// <summary>
/// Triple DES encryption
/// </summary>
public class NetTripleDESEncryption : INetEncryption
public class NetTripleDESEncryption : NetCryptoProviderBase
{
private readonly byte[] m_key;
private readonly byte[] m_iv;
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])
public NetTripleDESEncryption(NetPeer peer)
: base(peer, new TripleDESCryptoServiceProvider())
{
}
/// <summary>
/// Encrypt outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
public NetTripleDESEncryption(NetPeer peer, string key)
: base(peer, new TripleDESCryptoServiceProvider())
{
try
{
// 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;
SetKey(key);
}
/// <summary>
/// Decrypt incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
public NetTripleDESEncryption(NetPeer peer, byte[] data, int offset, int count)
: base(peer, new TripleDESCryptoServiceProvider())
{
try
{
// 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;
SetKey(data, offset, count);
}
}
}
}

View File

@@ -7,22 +7,30 @@ namespace Lidgren.Network
/// <summary>
/// Example class; not very good encryption
/// </summary>
public class NetXorEncryption : INetEncryption
public class NetXorEncryption : NetEncryption
{
private byte[] m_key;
/// <summary>
/// NetXorEncryption constructor
/// </summary>
public NetXorEncryption(byte[] key)
public NetXorEncryption(NetPeer peer, byte[] key)
: base(peer)
{
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>
/// NetXorEncryption constructor
/// </summary>
public NetXorEncryption(string key)
public NetXorEncryption(NetPeer peer, string key)
: base(peer)
{
m_key = Encoding.UTF8.GetBytes(key);
}
@@ -30,7 +38,7 @@ namespace Lidgren.Network
/// <summary>
/// Encrypt an outgoing message
/// </summary>
public bool Encrypt(NetOutgoingMessage msg)
public override bool Encrypt(NetOutgoingMessage msg)
{
int numBytes = msg.LengthBytes;
for (int i = 0; i < numBytes; i++)
@@ -44,7 +52,7 @@ namespace Lidgren.Network
/// <summary>
/// Decrypt an incoming message
/// </summary>
public bool Decrypt(NetIncomingMessage msg)
public override bool Decrypt(NetIncomingMessage msg)
{
int numBytes = msg.LengthBytes;
for (int i = 0; i < numBytes; i++)

View File

@@ -44,7 +44,8 @@ namespace Lidgren.Network
/// <summary>
/// 16 byte key
/// </summary>
public NetXtea(byte[] key, int rounds)
public NetXtea(NetPeer peer, byte[] key, int rounds)
: base(peer)
{
if (key.Length < c_keySize)
throw new NetException("Key too short!");
@@ -73,19 +74,26 @@ namespace Lidgren.Network
/// <summary>
/// 16 byte key
/// </summary>
public NetXtea(byte[] key)
: this(key, 32)
public NetXtea(NetPeer peer, byte[] key)
: this(peer, key, 32)
{
}
/// <summary>
/// String to hash for key
/// </summary>
public NetXtea(string key)
: this(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key)), 32)
public NetXtea(NetPeer peer, string key)
: 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>
/// Encrypts a block of bytes
/// </summary>

View File

@@ -1,5 +1,5 @@
<?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>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Lidgren.Network</RootNamespace>
<AssemblyName>Lidgren.Network</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
@@ -39,6 +39,7 @@
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\Debug\Lidgren.Network.XML</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -48,27 +49,34 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>Lidgren.snk</AssemblyOriginatorKeyFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</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\NetDESEncryption.cs" />
<Compile Include="Encryption\NetRC2Encryption.cs" />
<Compile Include="Encryption\NetTripleDESEncryption.cs" />
<Compile Include="Encryption\NetXorEncryption.cs" />
<Compile Include="Encryption\NetRC2Encryption.cs">
<SubType>Code</SubType>
</Compile>
<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="NamespaceDoc.cs" />
<Compile Include="NetBigInteger.cs" />
@@ -109,6 +117,8 @@
<Compile Include="NetPeerStatus.cs" />
<Compile Include="NetQueue.cs" />
<Compile Include="NetRandom.cs" />
<Compile Include="NetRandom.Implementations.cs" />
<Compile Include="NetRandomSeed.cs" />
<Compile Include="NetReceiverChannelBase.cs" />
<Compile Include="NetReliableOrderedReceiver.cs" />
<Compile Include="NetReliableSenderChannel.cs" />
@@ -126,8 +136,11 @@
<Compile Include="NetUnreliableUnorderedReceiver.cs" />
<Compile Include="NetUPnP.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="SenderChannelBase.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
@@ -151,9 +164,6 @@
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<None Include="Lidgren.snk" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.

Binary file not shown.

View File

@@ -2334,4 +2334,19 @@ namespace Lidgren.Network
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
}

View File

@@ -500,8 +500,7 @@ namespace Lidgren.Network
int num2 = 0;
while (true)
{
if (num2 == 0x23)
throw new FormatException("Bad 7-bit encoded integer");
NetException.Assert(num2 != 0x23, "Bad 7-bit encoded integer");
byte num3 = buffer[offset++];
num1 |= (num3 & 0x7f) << (num2 & 0x1f);

View File

@@ -237,7 +237,7 @@ namespace Lidgren.Network
else
{
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;
}

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
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)
{
if (target == null)
return;
throw new ArgumentNullException("target");
Type tp = target.GetType();
FieldInfo[] fields = tp.GetFields(flags);
@@ -76,8 +77,6 @@ namespace Lidgren.Network
if (target == null)
throw new ArgumentNullException("target");
if (target == null)
return;
Type tp = target.GetType();
PropertyInfo[] fields = tp.GetProperties(flags);
@@ -94,10 +93,11 @@ namespace Lidgren.Network
value = readMethod.Invoke(this, null);
// set the value
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
setMethod.Invoke(target, new object[] { value });
var setMethod = fi.GetSetMethod();
if (setMethod != null)
setMethod.Invoke(target, new object[] { value });
}
}
}
}
}
}

View File

@@ -4,6 +4,10 @@ using System.Text;
using System.Reflection;
using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
/// <summary>
@@ -311,7 +315,7 @@ namespace Lidgren.Network
else
{
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;
return retval;
@@ -406,7 +410,7 @@ namespace Lidgren.Network
{
int num1 = 0;
int num2 = 0;
while (true)
while (m_bitLength - m_readPosition >= 8)
{
byte num3 = this.ReadByte();
num1 |= (num3 & 0x7f) << num2;
@@ -414,6 +418,9 @@ namespace Lidgren.Network
if ((num3 & 0x80) == 0)
return (uint)num1;
}
// ouch; failed to find enough bytes; malformed variable length number?
return (uint)num1;
}
/// <summary>
@@ -424,7 +431,7 @@ namespace Lidgren.Network
{
int num1 = 0;
int num2 = 0;
while (true)
while (m_bitLength - m_readPosition >= 8)
{
byte num3;
if (ReadByte(out num3) == false)
@@ -440,6 +447,8 @@ namespace Lidgren.Network
return true;
}
}
result = (uint)num1;
return false;
}
/// <summary>
@@ -468,7 +477,7 @@ namespace Lidgren.Network
{
UInt64 num1 = 0;
int num2 = 0;
while (true)
while (m_bitLength - m_readPosition >= 8)
{
//if (num2 == 0x23)
// throw new FormatException("Bad 7-bit encoded integer");
@@ -479,6 +488,9 @@ namespace Lidgren.Network
if ((num3 & 0x80) == 0)
return num1;
}
// ouch; failed to find enough bytes; malformed variable length number?
return num1;
}
/// <summary>
@@ -543,10 +555,20 @@ namespace Lidgren.Network
{
int byteLen = (int)ReadVariableUInt32();
if (byteLen == 0)
if (byteLen <= 0)
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)
{
@@ -572,7 +594,7 @@ namespace Lidgren.Network
return false;
}
if (byteLen == 0)
if (byteLen <= 0)
{
result = String.Empty;
return true;
@@ -620,14 +642,14 @@ namespace Lidgren.Network
/// <summary>
/// Reads a stored IPv4 endpoint description
/// </summary>
public IPEndPoint ReadIPEndPoint()
public NetEndPoint ReadIPEndPoint()
{
byte len = ReadByte();
byte[] addressBytes = ReadBytes(len);
int port = (int)ReadUInt16();
IPAddress address = new IPAddress(addressBytes);
return new IPEndPoint(address, port);
var address = NetUtility.CreateAddressFromBytes(addressBytes);
return new NetEndPoint(address, port);
}
/// <summary>

View File

@@ -78,13 +78,16 @@ namespace Lidgren.Network
foreach (PropertyInfo fi in fields)
{
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
object value = getMethod.Invoke(ob, null);
MethodInfo getMethod = fi.GetGetMethod();
if (getMethod != null)
{
object value = getMethod.Invoke(ob, null);
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
// find the appropriate Write method
MethodInfo writeMethod;
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
writeMethod.Invoke(this, new object[] { value });
}
}
}
}

View File

@@ -161,6 +161,18 @@ namespace Lidgren.Network
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>
/// Writes an unsigned integer using 1 to 16 bits
/// </summary>
@@ -183,6 +195,17 @@ namespace Lidgren.Network
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
/// <summary>
/// Writes a 32 bit signed integer
@@ -217,6 +240,17 @@ namespace Lidgren.Network
}
#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
/// <summary>
/// Writes a 32 bit unsigned integer
@@ -253,6 +287,18 @@ namespace Lidgren.Network
}
#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>
/// Writes a 32 bit signed integer
/// </summary>
@@ -299,6 +345,18 @@ namespace Lidgren.Network
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>
/// Writes an unsigned integer using 1 to 64 bits
/// </summary>
@@ -535,7 +593,6 @@ namespace Lidgren.Network
{
if (string.IsNullOrEmpty(source))
{
EnsureBufferSize(m_bitLength + 8);
WriteVariableUInt32(0);
return;
}

View File

@@ -19,6 +19,10 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
/// <summary>
@@ -80,7 +84,7 @@ namespace Lidgren.Network
/// <param name="remoteEndPoint">The remote endpoint to connect to</param>
/// <param name="hailMessage">The hail message to pass</param>
/// <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)
{
@@ -153,6 +157,7 @@ namespace Lidgren.Network
if (serverConnection == null)
{
LogWarning("Cannot send message, no server connection!");
Recycle(msg);
return NetSendResult.FailedNotConnected;
}

View File

@@ -1,6 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
@@ -8,10 +13,11 @@ namespace Lidgren.Network
{
internal bool m_connectRequested;
internal bool m_disconnectRequested;
internal bool m_connectionInitiator;
internal bool m_disconnectReqSendBye;
internal string m_disconnectMessage;
internal bool m_connectionInitiator;
internal NetIncomingMessage m_remoteHailMessage;
internal float m_lastHandshakeSendTime;
internal double m_lastHandshakeSendTime;
internal int m_handshakeAttempts;
/// <summary>
@@ -20,7 +26,7 @@ namespace Lidgren.Network
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
// heartbeat called when connection still is in m_handshakes of NetPeer
internal void UnconnectedHeartbeat(float now)
internal void UnconnectedHeartbeat(double now)
{
m_peer.VerifyNetworkThread();
@@ -43,7 +49,8 @@ namespace Lidgren.Network
break;
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:
// let disconnect finish first
@@ -75,14 +82,12 @@ namespace Lidgren.Network
case NetConnectionStatus.RespondedConnect:
SendConnectResponse(now, true);
break;
case NetConnectionStatus.None:
case NetConnectionStatus.ReceivedInitiation:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
case NetConnectionStatus.RespondedAwaitingApproval:
// awaiting approval
m_lastHandshakeSendTime = now; // postpone handshake resend
break;
case NetConnectionStatus.None:
case NetConnectionStatus.ReceivedInitiation:
default:
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
break;
@@ -94,8 +99,6 @@ namespace Lidgren.Network
{
m_peer.VerifyNetworkThread();
//m_peer.LogDebug("Executing disconnect");
// clear send queues
for (int i = 0; i < m_sendChannels.Length; i++)
{
@@ -107,7 +110,15 @@ namespace Lidgren.Network
if (sendByeMessage)
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
lock (m_peer.m_handshakes)
@@ -118,7 +129,7 @@ namespace Lidgren.Network
m_handshakeAttempts = 0;
}
internal void SendConnect(float now)
internal void SendConnect(double now)
{
m_peer.VerifyNetworkThread();
@@ -129,7 +140,7 @@ namespace Lidgren.Network
om.m_messageType = NetMessageType.Connect;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
om.Write(now);
om.Write((float)now);
WriteLocalHail(om);
@@ -144,7 +155,7 @@ namespace Lidgren.Network
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
}
internal void SendConnectResponse(float now, bool onLibraryThread)
internal void SendConnectResponse(double now, bool onLibraryThread)
{
if (onLibraryThread)
m_peer.VerifyNetworkThread();
@@ -153,14 +164,14 @@ namespace Lidgren.Network
om.m_messageType = NetMessageType.ConnectResponse;
om.Write(m_peerConfiguration.AppIdentifier);
om.Write(m_peer.m_uniqueIdentifier);
om.Write(now);
om.Write((float)now);
Interlocked.Increment(ref om.m_recyclingCount);
WriteLocalHail(om);
if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint);
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_handshakeAttempts++;
@@ -178,10 +189,11 @@ namespace Lidgren.Network
NetOutgoingMessage om = m_peer.CreateMessage(reason);
om.m_messageType = NetMessageType.Disconnect;
Interlocked.Increment(ref om.m_recyclingCount);
if (onLibraryThread)
m_peer.SendLibrary(om, m_remoteEndPoint);
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)
@@ -192,7 +204,7 @@ namespace Lidgren.Network
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
{
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);
}
}
@@ -225,7 +237,7 @@ namespace Lidgren.Network
m_localHailMessage = null;
m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false);
SendConnectResponse(NetTime.Now, false);
}
/// <summary>
@@ -242,7 +254,7 @@ namespace Lidgren.Network
m_localHailMessage = localHail;
m_handshakeAttempts = 0;
SendConnectResponse((float)NetTime.Now, false);
SendConnectResponse(NetTime.Now, false);
}
/// <summary>
@@ -263,7 +275,8 @@ namespace Lidgren.Network
SendDisconnect(reason, false);
// 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)
@@ -322,43 +335,9 @@ namespace Lidgren.Network
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
break;
case NetMessageType.ConnectResponse:
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;
}
HandleConnectResponse(now, tp, ptr, payloadLength);
break;
case NetMessageType.ConnectionEstablished:
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)
{
hail = null;
@@ -439,7 +459,6 @@ namespace Lidgren.Network
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
{
// wrong app identifier
ExecuteDisconnect("Wrong application identifier!", true);
return false;
}
@@ -474,6 +493,7 @@ namespace Lidgren.Network
m_handshakeAttempts = 0;
m_disconnectRequested = true;
m_disconnectReqSendBye = true;
}
}
}

View File

@@ -4,10 +4,10 @@ namespace Lidgren.Network
{
public partial class NetConnection
{
private float m_sentPingTime;
private double m_sentPingTime;
private int m_sentPingNumber;
private float m_averageRoundtripTime;
private float m_timeoutDeadline = float.MaxValue;
private double m_averageRoundtripTime;
private double m_timeoutDeadline = double.MaxValue;
// local time value + m_remoteTimeOffset = remote time value
internal double m_remoteTimeOffset;
@@ -15,7 +15,7 @@ namespace Lidgren.Network
/// <summary>
/// Gets the current average roundtrip time in seconds
/// </summary>
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
public float AverageRoundtripTime { get { return (float)m_averageRoundtripTime; } }
/// <summary>
/// Time offset between this peer and the remote peer
@@ -46,13 +46,13 @@ namespace Lidgren.Network
internal void InitializePing()
{
float now = (float)NetTime.Now;
double now = NetTime.Now;
// randomize ping sent time (0.25 - 1.0 x ping interval)
m_sentPingTime = now;
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while
m_sentPingTime -= (NetRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0f); // initially allow a little more time
m_sentPingTime -= (MWCRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0); // initially allow a little more time
// make it better, quick :-)
SendPing();
@@ -64,7 +64,7 @@ namespace Lidgren.Network
m_sentPingNumber++;
m_sentPingTime = (float)NetTime.Now;
m_sentPingTime = NetTime.Now;
NetOutgoingMessage om = m_peer.CreateMessage(1);
om.Write((byte)m_sentPingNumber); // truncating to 0-255
om.m_messageType = NetMessageType.Ping;
@@ -74,6 +74,7 @@ namespace Lidgren.Network
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
m_statistics.PacketSent(len, 1);
m_peer.Recycle(om);
}
internal void SendPong(int pingNumber)
@@ -91,9 +92,10 @@ namespace Lidgren.Network
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
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)
{
@@ -103,7 +105,7 @@ namespace Lidgren.Network
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
float rtt = now - m_sentPingTime;
double rtt = now - m_sentPingTime;
NetException.Assert(rtt >= 0);
double diff = (remoteSendTime + (rtt / 2.0)) - now;
@@ -116,14 +118,14 @@ namespace Lidgren.Network
}
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_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
float resendDelay = GetResendDelay();
double resendDelay = GetResendDelay();
foreach (var chan in m_sendChannels)
{
var rchan = chan as NetReliableSenderChannel;
@@ -139,7 +141,7 @@ namespace Lidgren.Network
NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4);
update.m_senderConnection = this;
update.m_senderEndPoint = this.m_remoteEndPoint;
update.Write(rtt);
update.Write((float)rtt);
m_peer.ReleaseMessage(update);
}
}

View File

@@ -24,6 +24,11 @@ namespace Lidgren.Network
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)
{
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_statistics.PacketSent(len, 1);
m_peer.Recycle(om);
}
private void FinalizeMTU(int size)
@@ -149,8 +155,9 @@ namespace Lidgren.Network
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
bool 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);
}

View File

@@ -3,12 +3,16 @@ using System.Net;
using System.Threading;
using System.Diagnostics;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
/// <summary>
/// Represents a connection to a remote peer
/// </summary>
[DebuggerDisplay("RemoteUniqueIdentifier={RemoteUniqueIdentifier} RemoteEndPoint={remoteEndPoint}")]
[DebuggerDisplay("RemoteUniqueIdentifier={RemoteUniqueIdentifier} RemoteEndPoint={m_remoteEndPoint}")]
public partial class NetConnection
{
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 NetPeerConfiguration m_peerConfiguration;
internal NetConnectionStatus m_status;
internal NetConnectionStatus m_visibleStatus;
internal IPEndPoint m_remoteEndPoint;
internal NetConnectionStatus m_status; // actual status
internal NetConnectionStatus m_outputtedStatus; // status that has been sent as StatusChanged message
internal NetConnectionStatus m_visibleStatus; // status visible by querying the Status property
internal NetEndPoint m_remoteEndPoint;
internal NetSenderChannelBase[] m_sendChannels;
internal NetReceiverChannelBase[] m_receiveChannels;
internal NetOutgoingMessage m_localHailMessage;
@@ -57,7 +62,7 @@ namespace Lidgren.Network
/// <summary>
/// Gets the remote endpoint for the connection
/// </summary>
public IPEndPoint RemoteEndPoint { get { return m_remoteEndPoint; } }
public NetEndPoint RemoteEndPoint { get { return m_remoteEndPoint; } }
/// <summary>
/// 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; } }
// 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)
avgRtt = 0.1f; // "default" resend is based on 100 ms roundtrip time
return 0.02f + (avgRtt * 2.0f); // 20 ms + double rtt
avgRtt = 0.1; // "default" resend is based on 100 ms roundtrip time
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_peerConfiguration = m_peer.Configuration;
m_status = NetConnectionStatus.None;
m_outputtedStatus = NetConnectionStatus.None;
m_visibleStatus = NetConnectionStatus.None;
m_remoteEndPoint = remoteEndPoint;
m_sendChannels = new NetSenderChannelBase[NetConstants.NumTotalChannels];
@@ -97,45 +103,52 @@ namespace Lidgren.Network
/// <summary>
/// Change the internal endpoint to this new one. Used when, during handshake, a switch in port is detected (due to NAT)
/// </summary>
internal void MutateEndPoint(IPEndPoint endPoint)
internal void MutateEndPoint(NetEndPoint endPoint)
{
m_remoteEndPoint = endPoint;
}
internal void ResetTimeout(double now)
{
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
}
internal void SetStatus(NetConnectionStatus status, string reason)
{
// user or library thread
if (status == m_status)
return;
m_status = status;
if (reason == null)
reason = string.Empty;
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);
}
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.StatusChanged))
{
NetIncomingMessage info = m_peer.CreateIncomingMessage(NetIncomingMessageType.StatusChanged, 4 + reason.Length + (reason.Length > 126 ? 2 : 1));
info.m_senderConnection = this;
info.m_senderEndPoint = m_remoteEndPoint;
info.Write((byte)m_status);
info.Write(reason);
m_peer.ReleaseMessage(info);
if (m_outputtedStatus != status)
{
NetIncomingMessage info = m_peer.CreateIncomingMessage(NetIncomingMessageType.StatusChanged, 4 + reason.Length + (reason.Length > 126 ? 2 : 1));
info.m_senderConnection = this;
info.m_senderEndPoint = m_remoteEndPoint;
info.Write((byte)m_status);
info.Write(reason);
m_peer.ReleaseMessage(info);
m_outputtedStatus = status;
}
}
else
{
// app dont want those messages, update visible status immediately
m_outputtedStatus = m_status;
m_visibleStatus = m_status;
}
}
internal void Heartbeat(float now, uint frameCounter)
internal void Heartbeat(double now, uint frameCounter)
{
m_peer.VerifyNetworkThread();
@@ -150,6 +163,7 @@ namespace Lidgren.Network
//
m_peer.LogVerbose("Connection timed out at " + now + " deadline was " + m_timeoutDeadline);
ExecuteDisconnect("Connection timed out", true);
return;
}
// send ping?
@@ -164,7 +178,7 @@ namespace Lidgren.Network
if (m_disconnectRequested)
{
ExecuteDisconnect(m_disconnectMessage, true);
ExecuteDisconnect(m_disconnectMessage, m_disconnectReqSendBye);
return;
}
}
@@ -233,8 +247,11 @@ namespace Lidgren.Network
{
//m_peer.LogVerbose("Received ack for " + acktp + "#" + seqNr);
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)
chan = CreateSenderChannel(incAck.Item1);
continue;
chan.ReceiveAcknowledge(now, incAck.Item2);
}
}
@@ -249,7 +266,11 @@ namespace Lidgren.Network
var channel = m_sendChannels[i];
NetException.Assert(m_sendBufferWritePtr < 1 || m_sendBufferNumMessages > 0);
if (channel != null)
{
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);
}
}
@@ -275,24 +296,41 @@ namespace Lidgren.Network
m_peer.VerifyNetworkThread();
int sz = om.GetEncodedSize();
if (sz > m_currentMTU)
m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!");
//if (sz > m_currentMTU)
// 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)
{
bool connReset; // TODO: handle connection reset
NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); // or else the message should have been fragmented earlier
if (m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0)
{
// 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_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages);
m_sendBufferWritePtr = 0;
m_sendBufferNumMessages = 0;
}
m_sendBufferWritePtr = om.Encode(m_peer.m_sendBuffer, m_sendBufferWritePtr, seqNr);
m_sendBufferNumMessages++;
if (m_sendBufferWritePtr > 0)
m_peer.m_needFlushSendQueue = true; // flush in heartbeat
NetException.Assert(m_sendBufferWritePtr > 0, "Encoded zero size message?");
NetException.Assert(m_sendBufferNumMessages > 0);
Interlocked.Decrement(ref om.m_recyclingCount);
}
/// <summary>
@@ -321,12 +359,12 @@ namespace Lidgren.Network
if (chan == null)
chan = CreateSenderChannel(tp);
if (msg.GetEncodedSize() > m_currentMTU)
throw new NetException("Message too large! Fragmentation failure?");
if ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.UnreliableSequenced) && msg.GetEncodedSize() > m_currentMTU)
m_peer.ThrowOrLog("Reliable message too large! Fragmentation failure?");
var retval = chan.Enqueue(msg);
if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false)
retval = NetSendResult.Queued; // queued since we're not autoflushing
//if (retval == NetSendResult.Sent && m_peerConfiguration.m_autoFlushSendQueue == false)
// retval = NetSendResult.Queued; // queued since we're not autoflushing
return retval;
}
@@ -347,12 +385,11 @@ namespace Lidgren.Network
}
else
{
switch (method)
{
case NetDeliveryMethod.Unreliable:
case NetDeliveryMethod.UnreliableSequenced:
chan = new NetUnreliableSenderChannel(this, NetUtility.GetWindowSize(method));
chan = new NetUnreliableSenderChannel(this, NetUtility.GetWindowSize(method), method);
break;
case NetDeliveryMethod.ReliableOrdered:
chan = new NetReliableSenderChannel(this, NetUtility.GetWindowSize(method));
@@ -375,13 +412,34 @@ namespace Lidgren.Network
{
m_peer.VerifyNetworkThread();
float now = (float)NetTime.Now;
double now = NetTime.Now;
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:
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;
case NetMessageType.Acknowledge:
for (int i = 0; i < payloadLength; i+=3)
@@ -408,6 +466,11 @@ namespace Lidgren.Network
SendMTUSuccess(payloadLength);
break;
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);
int size = emsg.ReadInt32();
HandleExpandMTUSuccess(now, size);
@@ -492,10 +555,19 @@ namespace Lidgren.Network
}
windowSize = chan.WindowSize;
freeWindowSlots = chan.GetAllowedSends() - chan.m_queuedSends.Count;
freeWindowSlots = chan.GetFreeWindowSlots();
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)
{
ExecuteDisconnect(reason, true);

View File

@@ -40,17 +40,19 @@ namespace Lidgren.Network
{
private readonly NetConnection m_connection;
internal int m_sentPackets;
internal int m_receivedPackets;
internal long m_sentPackets;
internal long m_receivedPackets;
internal int m_sentMessages;
internal int m_receivedMessages;
internal long m_sentMessages;
internal long m_receivedMessages;
internal long m_droppedMessages;
internal long m_receivedFragments;
internal int m_sentBytes;
internal int m_receivedBytes;
internal long m_sentBytes;
internal long m_receivedBytes;
internal int m_resentMessagesDueToDelay;
internal int m_resentMessagesDueToHole;
internal long m_resentMessagesDueToDelay;
internal long m_resentMessagesDueToHole;
internal NetConnectionStatistics(NetConnection conn)
{
@@ -62,38 +64,60 @@ namespace Lidgren.Network
{
m_sentPackets = 0;
m_receivedPackets = 0;
m_sentMessages = 0;
m_receivedMessages = 0;
m_receivedFragments = 0;
m_sentBytes = 0;
m_receivedBytes = 0;
m_resentMessagesDueToDelay = 0;
m_resentMessagesDueToHole = 0;
}
/// <summary>
/// Gets the number of sent packets for this connection
/// </summary>
public int SentPackets { get { return m_sentPackets; } }
public long SentPackets { get { return m_sentPackets; } }
/// <summary>
/// Gets the number of received packets for this connection
/// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } }
public long ReceivedPackets { get { return m_receivedPackets; } }
/// <summary>
/// Gets the number of sent bytes for this connection
/// </summary>
public int SentBytes { get { return m_sentBytes; } }
public long SentBytes { get { return m_sentBytes; } }
/// <summary>
/// Gets the number of received bytes for this connection
/// </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>
/// Gets the number of resent reliable messages for this connection
/// </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; } }
#if USE_RELEASE_STATISTICS
#if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
#endif
internal void PacketSent(int numBytes, int numMessages)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
@@ -101,37 +125,22 @@ namespace Lidgren.Network
m_sentBytes += numBytes;
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
internal void PacketReceived(int numBytes, int numMessages)
#if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
#endif
internal void PacketReceived(int numBytes, int numMessages, int numFragments)
{
NetException.Assert(numBytes > 0 && numMessages > 0);
m_receivedPackets++;
m_receivedBytes += numBytes;
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)
{
if (reason == MessageResendReason.Delay)
@@ -139,16 +148,14 @@ namespace Lidgren.Network
else
m_resentMessagesDueToHole++;
}
#else
#if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
internal void MessageResent(MessageResendReason reason)
{
if (reason == MessageResendReason.Delay)
m_resentMessagesDueToDelay++;
else
m_resentMessagesDueToHole++;
}
#endif
internal void MessageDropped()
{
m_droppedMessages++;
}
/// <summary>
/// Returns a string that represents this object
@@ -157,12 +164,14 @@ namespace Lidgren.Network
{
StringBuilder bdr = new StringBuilder();
//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("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)
bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay);
if (m_resentMessagesDueToDelay > 0)
if (m_resentMessagesDueToHole > 0)
bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole);
int numUnsent = 0;
@@ -171,7 +180,7 @@ namespace Lidgren.Network
{
if (sendChan == null)
continue;
numUnsent += sendChan.m_queuedSends.Count;
numUnsent += sendChan.QueuedSendsCount;
var relSendChan = sendChan as NetReliableSenderChannel;
if (relSendChan != null)

View File

@@ -25,7 +25,6 @@ namespace Lidgren.Network
/// <summary>
/// Exception thrown in the Lidgren Network Library
/// </summary>
[Serializable]
public sealed class NetException : Exception
{
/// <summary>
@@ -52,14 +51,6 @@ namespace Lidgren.Network
{
}
/// <summary>
/// NetException constructor
/// </summary>
private NetException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// <summary>
/// Throws an exception, in DEBUG only, if first parameter is false
/// </summary>

View File

@@ -20,6 +20,10 @@ using System;
using System.Net;
using System.Diagnostics;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
/// <summary>
@@ -29,7 +33,7 @@ namespace Lidgren.Network
public sealed class NetIncomingMessage : NetBuffer
{
internal NetIncomingMessageType m_incomingMessageType;
internal IPEndPoint m_senderEndPoint;
internal NetEndPoint m_senderEndPoint;
internal NetConnection m_senderConnection;
internal int m_sequenceNumber;
internal NetMessageType m_receivedMessageType;
@@ -52,9 +56,9 @@ namespace Lidgren.Network
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
/// <summary>
/// IPEndPoint of sender, if any
/// endpoint of sender, if any
/// </summary>
public IPEndPoint SenderEndPoint { get { return m_senderEndPoint; } }
public NetEndPoint SenderEndPoint { get { return m_senderEndPoint; } }
/// <summary>
/// NetConnection of sender, if any
@@ -90,7 +94,7 @@ namespace Lidgren.Network
/// </summary>
/// <param name="encryption">The encryption algorithm used to encrypt the message</param>
/// <returns>true on success</returns>
public bool Decrypt(INetEncryption encryption)
public bool Decrypt(NetEncryption encryption)
{
return encryption.Decrypt(this);
}

View File

@@ -1,6 +1,11 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
@@ -10,29 +15,31 @@ namespace Lidgren.Network
/// Send NetIntroduction to hostExternal and clientExternal; introducing client to host
/// </summary>
public void Introduce(
IPEndPoint hostInternal,
IPEndPoint hostExternal,
IPEndPoint clientInternal,
IPEndPoint clientExternal,
NetEndPoint hostInternal,
NetEndPoint hostExternal,
NetEndPoint clientInternal,
NetEndPoint clientExternal,
string token)
{
// send message to client
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)0);
msg.Write(hostInternal);
msg.Write(hostExternal);
msg.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg));
NetOutgoingMessage um = CreateMessage(10 + token.Length + 1);
um.m_messageType = NetMessageType.NatIntroduction;
um.Write((byte)0);
um.Write(hostInternal);
um.Write(hostExternal);
um.Write(token);
Interlocked.Increment(ref um.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(clientExternal, um));
// send message to host
msg = CreateMessage(10 + token.Length + 1);
msg.m_messageType = NetMessageType.NatIntroduction;
msg.Write((byte)1);
msg.Write(clientInternal);
msg.Write(clientExternal);
msg.Write(token);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg));
um = CreateMessage(10 + token.Length + 1);
um.m_messageType = NetMessageType.NatIntroduction;
um.Write((byte)1);
um.Write(clientInternal);
um.Write(clientExternal);
um.Write(token);
Interlocked.Increment(ref um.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(hostExternal, um));
}
/// <summary>
@@ -46,8 +53,8 @@ namespace Lidgren.Network
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
byte hostByte = tmp.ReadByte();
IPEndPoint remoteInternal = tmp.ReadIPEndPoint();
IPEndPoint remoteExternal = tmp.ReadIPEndPoint();
NetEndPoint remoteInternal = tmp.ReadIPEndPoint();
NetEndPoint remoteExternal = tmp.ReadIPEndPoint();
string token = tmp.ReadString();
bool isHost = (hostByte != 0);
@@ -63,20 +70,25 @@ namespace Lidgren.Network
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte);
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
punch = CreateMessage(1);
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write(hostByte);
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>
/// Called when receiving a NatPunchMessage from a remote endpoint
/// </summary>
private void HandleNatPunch(int ptr, IPEndPoint senderEndPoint)
private void HandleNatPunch(int ptr, NetEndPoint senderEndPoint)
{
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);
//
// 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);
punchSuccess.m_senderEndPoint = senderEndPoint;
@@ -104,7 +116,8 @@ namespace Lidgren.Network
punch.m_messageType = NetMessageType.NatPunchMessage;
punch.Write((byte)0);
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));
}
}
}

View File

@@ -30,6 +30,13 @@ namespace Lidgren.Network
{
internal NetMessageType m_messageType;
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_fragmentGroup; // which group of fragments ths belongs to
@@ -46,7 +53,7 @@ namespace Lidgren.Network
m_messageType = NetMessageType.LibraryError;
m_bitLength = 0;
m_isSent = false;
m_recyclingCount = 0;
NetException.Assert(m_recyclingCount == 0);
m_fragmentGroup = 0;
}
@@ -116,7 +123,7 @@ namespace Lidgren.Network
/// <summary>
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
/// </summary>
public bool Encrypt(INetEncryption encryption)
public bool Encrypt(NetEncryption encryption)
{
return encryption.Encrypt(this);
}

View File

@@ -1,5 +1,10 @@
using System;
using System.Net;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
@@ -10,9 +15,11 @@ namespace Lidgren.Network
/// </summary>
public void DiscoverLocalPeers(int serverPort)
{
NetOutgoingMessage om = CreateMessage(0);
om.m_messageType = NetMessageType.Discovery;
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om));
NetOutgoingMessage um = CreateMessage(0);
um.m_messageType = NetMessageType.Discovery;
Interlocked.Increment(ref um.m_recyclingCount);
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(new NetEndPoint(NetUtility.GetBroadcastAddress(), serverPort), um));
}
/// <summary>
@@ -20,27 +27,28 @@ namespace Lidgren.Network
/// </summary>
public bool DiscoverKnownPeer(string host, int serverPort)
{
IPAddress address = NetUtility.Resolve(host);
var address = NetUtility.Resolve(host);
if (address == null)
return false;
DiscoverKnownPeer(new IPEndPoint(address, serverPort));
DiscoverKnownPeer(new NetEndPoint(address, serverPort));
return true;
}
/// <summary>
/// Emit a discovery signal to a single known host
/// </summary>
public void DiscoverKnownPeer(IPEndPoint endPoint)
public void DiscoverKnownPeer(NetEndPoint endPoint)
{
NetOutgoingMessage om = CreateMessage(0);
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>
/// Send a discovery response message
/// </summary>
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
public void SendDiscoveryResponse(NetOutgoingMessage msg, NetEndPoint recipient)
{
if (recipient == null)
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)");
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));
}
}
}

View File

@@ -6,7 +6,7 @@ namespace Lidgren.Network
{
internal class ReceivedFragmentGroup
{
public float LastReceived;
//public float LastReceived;
public byte[] Data;
public NetBitVector ReceivedChunks;
}
@@ -18,7 +18,7 @@ namespace Lidgren.Network
private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups;
// 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;
// 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)
numChunks++;
NetSendResult retval = NetSendResult.Sent;
int bitsPerChunk = bytesPerChunk * 8;
int bitsLeft = msg.LengthBits;
for (int i = 0; i < numChunks; i++)
{
NetOutgoingMessage chunk = CreateMessage(mtu);
NetOutgoingMessage chunk = CreateMessage(0);
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
chunk.m_data = msg.m_data;
@@ -63,12 +65,18 @@ namespace Lidgren.Network
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
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;
}
return;
return retval;
}
private void HandleReleasedFragment(NetIncomingMessage im)
@@ -126,7 +134,7 @@ namespace Lidgren.Network
}
info.ReceivedChunks[chunkNumber] = true;
info.LastReceived = (float)NetTime.Now;
//info.LastReceived = (float)NetTime.Now;
// copy to data
int offset = (chunkNumber * chunkByteSize);

View File

@@ -1,8 +1,4 @@
#if !__ANDROID__ && !IOS
#define IS_MAC_AVAILABLE
#endif
using System;
using System;
using System.Net;
using System.Threading;
using System.Diagnostics;
@@ -10,6 +6,10 @@ using System.Security.Cryptography;
using System.Net.Sockets;
using System.Collections.Generic;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
public partial class NetPeer
@@ -24,13 +24,15 @@ namespace Lidgren.Network
private object m_initializeLock = new object();
private uint m_frameCounter;
private double m_lastHeartbeat;
private double m_lastSocketBind = float.MinValue;
private NetUPnP m_upnp;
internal bool m_needFlushSendQueue;
internal readonly NetPeerConfiguration m_configuration;
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 long m_uniqueIdentifier;
@@ -47,13 +49,15 @@ namespace Lidgren.Network
/// <summary>
/// Call this to register a callback for when a new message arrives
/// </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!");
if (m_receiveCallbacks == null)
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>
@@ -63,7 +67,17 @@ namespace Lidgren.Network
{
if (m_receiveCallbacks == null)
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)
m_receiveCallbacks = null;
}
@@ -86,10 +100,59 @@ namespace Lidgren.Network
if (m_receiveCallbacks != null)
{
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()
{
lock (m_initializeLock)
@@ -109,65 +172,21 @@ namespace Lidgren.Network
m_handshakes.Clear();
// bind to socket
IPEndPoint iep = null;
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;
BindSocket(false);
m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize];
m_sendBuffer = new byte[m_configuration.SendBufferSize];
m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error);
m_readHelperMessage.m_data = m_receiveBuffer;
byte[] macBytes = new byte[8];
NetRandom.Instance.NextBytes(macBytes);
byte[] macBytes = NetUtility.GetMacAddressBytes();
#if IS_MAC_AVAILABLE
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
var boundEp = m_socket.LocalEndPoint as NetEndPoint;
byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode());
byte[] combined = new byte[epBytes.Length + macBytes.Length];
Array.Copy(epBytes, 0, combined, 0, epBytes.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;
}
@@ -213,26 +232,26 @@ namespace Lidgren.Network
foreach (var conn in m_connections)
if (conn != null)
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();
// one final heartbeat, will send stuff and do disconnect
Heartbeat();
Thread.Sleep(10);
NetUtility.Sleep(10);
lock (m_initializeLock)
{
try
@@ -243,14 +262,19 @@ namespace Lidgren.Network
{
m_socket.Shutdown(SocketShutdown.Receive);
}
catch { }
m_socket.Close(2); // 2 seconds timeout
}
if (m_messageReceivedEvent != null)
{
m_messageReceivedEvent.Set();
m_messageReceivedEvent.Close();
m_messageReceivedEvent = null;
catch(Exception ex)
{
LogDebug("Socket.Shutdown exception: " + ex.ToString());
}
try
{
m_socket.Close(2); // 2 seconds timeout
}
catch (Exception ex)
{
LogDebug("Socket.Close exception: " + ex.ToString());
}
}
}
finally
@@ -258,8 +282,13 @@ namespace Lidgren.Network
m_socket = null;
m_status = NetPeerStatus.NotRunning;
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_sendBuffer = null;
m_unsentUnconnectedMessages.Clear();
@@ -275,10 +304,8 @@ namespace Lidgren.Network
{
VerifyNetworkThread();
double dnow = NetTime.Now;
float now = (float)dnow;
double delta = dnow - m_lastHeartbeat;
double now = NetTime.Now;
double delta = now - m_lastHeartbeat;
int maxCHBpS = 1250 - m_connections.Count;
if (maxCHBpS < 250)
@@ -286,7 +313,7 @@ namespace Lidgren.Network
if (delta > (1.0 / (double)maxCHBpS) || delta < 0.0) // max connection heartbeats/second max
{
m_frameCounter++;
m_lastHeartbeat = dnow;
m_lastHeartbeat = now;
// do handshake heartbeats
if ((m_frameCounter % 3) == 0)
@@ -320,44 +347,51 @@ namespace Lidgren.Network
#endif
// update m_executeFlushSendQueue
if (m_configuration.m_autoFlushSendQueue)
if (m_configuration.m_autoFlushSendQueue && m_needFlushSendQueue == 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
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);
if (conn.m_status == NetConnectionStatus.Disconnected)
{
//
// remove connection
//
m_connections.Remove(conn);
m_connections.RemoveAt(i);
m_connectionLookup.Remove(conn.RemoteEndPoint);
break; // can't continue iteration here
}
}
}
m_executeFlushSendQueue = false;
// send unsent unconnected messages
NetTuple<IPEndPoint, NetOutgoingMessage> unsent;
NetTuple<NetEndPoint, NetOutgoingMessage> unsent;
while (m_unsentUnconnectedMessages.TryDequeue(out unsent))
{
NetOutgoingMessage om = unsent.Item2;
bool connReset;
int len = om.Encode(m_sendBuffer, 0, 0);
SendPacket(len, unsent.Item1, 1, out connReset);
Interlocked.Decrement(ref om.m_recyclingCount);
if (om.m_recyclingCount <= 0)
Recycle(om);
bool connReset;
SendPacket(len, unsent.Item1, 1, out connReset);
}
}
if (m_upnp != null)
m_upnp.CheckForDiscoveryTimeout();
//
// read from socket
//
@@ -371,8 +405,7 @@ namespace Lidgren.Network
// return;
// update now
dnow = NetTime.Now;
now = (float)dnow;
now = NetTime.Now;
do
{
@@ -383,17 +416,24 @@ namespace Lidgren.Network
}
catch (SocketException sx)
{
if (sx.SocketErrorCode == SocketError.ConnectionReset)
switch (sx.SocketErrorCode)
{
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
// we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
// So, what to do?
LogWarning("ConnectionReset");
return;
}
case SocketError.ConnectionReset:
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
// we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
// So, what to do?
LogWarning("ConnectionReset");
return;
LogWarning(sx.ToString());
return;
case SocketError.NotConnected:
// 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)
@@ -401,23 +441,29 @@ namespace Lidgren.Network
//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?
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);
if (resp.Contains("upnp:rootdevice") || resp.Contains("UPnP/1.0"))
try
{
resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
resp = resp.Substring(0, resp.IndexOf("\r")).Trim();
m_upnp.ExtractServiceUrl(resp);
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;
@@ -427,6 +473,7 @@ namespace Lidgren.Network
// parse packet into messages
//
int numMessages = 0;
int numFragments = 0;
int ptr = 0;
while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize)
{
@@ -446,6 +493,9 @@ namespace Lidgren.Network
bool isFragment = ((low & 1) == 1);
ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7));
if (isFragment)
numFragments++;
ushort payloadBitLength = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
int payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength);
@@ -455,16 +505,20 @@ namespace Lidgren.Network
return;
}
if (tp >= NetMessageType.Unused1 && tp <= NetMessageType.Unused29)
{
ThrowOrLog("Unexpected NetMessageType: " + tp);
return;
}
try
{
NetException.Assert(tp < NetMessageType.Unused1 || tp > NetMessageType.Unused29);
if (tp >= NetMessageType.LibraryError)
{
if (sender != null)
sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
else
ReceivedUnconnectedLibraryMessage(dnow, ipsender, tp, ptr, payloadByteLength);
ReceivedUnconnectedLibraryMessage(now, ipsender, tp, ptr, payloadByteLength);
}
else
{
@@ -473,12 +527,13 @@ namespace Lidgren.Network
NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength);
msg.m_isFragment = isFragment;
msg.m_receiveTime = dnow;
msg.m_receiveTime = now;
msg.m_sequenceNumber = sequenceNumber;
msg.m_receivedMessageType = tp;
msg.m_senderConnection = sender;
msg.m_senderEndPoint = ipsender;
msg.m_bitLength = payloadBitLength;
Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength);
if (sender != null)
{
@@ -510,9 +565,9 @@ namespace Lidgren.Network
ptr += payloadByteLength;
}
m_statistics.PacketReceived(bytesReceived, numMessages);
m_statistics.PacketReceived(bytesReceived, numMessages, numFragments);
if (sender != null)
sender.m_statistics.PacketReceived(bytesReceived, numMessages);
sender.m_statistics.PacketReceived(bytesReceived, numMessages, numFragments);
} while (m_socket.Available > 0);
}
@@ -525,7 +580,7 @@ namespace Lidgren.Network
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))
{
@@ -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))
{
@@ -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;
if (m_handshakes.TryGetValue(senderEndPoint, out shake))
@@ -574,10 +629,12 @@ namespace Lidgren.Network
HandleIncomingDiscoveryResponse(now, senderEndPoint, ptr, payloadByteLength);
return;
case NetMessageType.NatIntroduction:
HandleNatIntroduction(ptr);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess))
HandleNatIntroduction(ptr);
return;
case NetMessageType.NatPunchMessage:
HandleNatPunch(ptr, senderEndPoint);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess))
HandleNatPunch(ptr, senderEndPoint);
return;
case NetMessageType.ConnectResponse:
@@ -688,4 +745,4 @@ namespace Lidgren.Network
return m_readHelperMessage;
}
}
}
}

View File

@@ -25,6 +25,10 @@ using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
public partial class NetPeer
@@ -37,10 +41,10 @@ namespace Lidgren.Network
{
public byte[] Data;
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;
@@ -48,7 +52,7 @@ namespace Lidgren.Network
float loss = m_configuration.m_loss;
if (loss > 0.0f)
{
if ((float)NetRandom.Instance.NextDouble() < loss)
if ((float)MWCRandom.Instance.NextDouble() < loss)
{
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
return; // packet "lost"
@@ -66,17 +70,21 @@ namespace Lidgren.Network
// LogVerbose("Sending packet " + numBytes + " bytes");
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
// 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;
}
int num = 1;
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
if (m_configuration.m_duplicates > 0.0f && MWCRandom.Instance.NextSingle() < m_configuration.m_duplicates)
num++;
float delay = 0;
for (int i = 0; i < num; i++)
{
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
delay = m_configuration.m_minimumOneWayLatency + (MWCRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
// Enqueue delayed packet
DelayedPacket p = new DelayedPacket();
@@ -124,13 +132,16 @@ namespace Lidgren.Network
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;
IPAddress ba = default(IPAddress);
try
{
ba = NetUtility.GetCachedBroadcastAddress();
// TODO: refactor this check outta here
if (target.Address == IPAddress.Broadcast)
if (target.Address == ba)
{
// Some networks do not allow
// a global broadcast so we use the BroadcastAddress from the configuration
@@ -167,13 +178,13 @@ namespace Lidgren.Network
}
finally
{
if (target.Address == IPAddress.Broadcast)
if (target.Address == ba)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
return true;
}
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
internal bool SendMTUPacket(int numBytes, NetEndPoint target)
{
try
{
@@ -209,7 +220,7 @@ namespace Lidgren.Network
return true;
}
#else
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
internal bool SendMTUPacket(int numBytes, NetEndPoint target)
{
try
{
@@ -246,16 +257,18 @@ namespace Lidgren.Network
//
// 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
m_statistics.PacketSent(numBytes, numMessages);
#endif
connectionReset = false;
IPAddress ba = default(IPAddress);
try
{
// 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);
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
@@ -284,7 +297,7 @@ namespace Lidgren.Network
}
finally
{
if (target.Address == IPAddress.Broadcast)
if (target.Address == ba)
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
}
return;
@@ -293,12 +306,6 @@ namespace Lidgren.Network
private void FlushDelayedPackets()
{
}
private void SendCallBack(IAsyncResult res)
{
NetException.Assert(res.IsCompleted == true);
m_socket.EndSendTo(res);
}
#endif
}
}

View File

@@ -28,8 +28,6 @@ namespace Lidgren.Network
#if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Verbose, "", message);
#endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.VerboseDebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.VerboseDebugMessage, message));
}
@@ -40,8 +38,6 @@ namespace Lidgren.Network
#if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Debug, "", message);
#endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DebugMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.DebugMessage, message));
}
@@ -51,8 +47,6 @@ namespace Lidgren.Network
#if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Warn, "", message);
#endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.WarningMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.WarningMessage, message));
}
@@ -62,8 +56,6 @@ namespace Lidgren.Network
#if __ANDROID__
Android.Util.Log.WriteLine(Android.Util.LogPriority.Error, "", message);
#endif
Debug.WriteLine(message);
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ErrorMessage))
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.ErrorMessage, message));
}

View File

@@ -6,14 +6,18 @@ namespace Lidgren.Network
{
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<NetIncomingMessage> m_incomingMessagesPool;
internal int m_storagePoolBytes;
internal int m_storageSlotsUsedCount;
private int m_maxCacheCount;
private void InitializePools()
{
m_storageSlotsUsedCount = 0;
if (m_configuration.UseMessageRecycling)
{
m_storagePool = new List<byte[]>(16);
@@ -26,6 +30,8 @@ namespace Lidgren.Network
m_outgoingMessagesPool = null;
m_incomingMessagesPool = null;
}
m_maxCacheCount = m_configuration.RecycledCacheMaxCount;
}
internal byte[] GetStorage(int minimumCapacityInBytes)
@@ -41,6 +47,7 @@ namespace Lidgren.Network
if (retval != null && retval.Length >= minimumCapacityInBytes)
{
m_storagePool[i] = null;
m_storageSlotsUsedCount--;
m_storagePoolBytes -= retval.Length;
return retval;
}
@@ -52,22 +59,39 @@ namespace Lidgren.Network
internal void Recycle(byte[] storage)
{
if (m_storagePool == null)
if (m_storagePool == null || storage == null)
return;
lock (m_storagePool)
{
m_storagePoolBytes += storage.Length;
int cnt = m_storagePool.Count;
for (int i = 0; i < cnt; i++)
{
if (m_storagePool[i] == null)
{
m_storageSlotsUsedCount++;
m_storagePoolBytes += storage.Length;
m_storagePool[i] = storage;
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>
public NetOutgoingMessage CreateMessage(string content)
{
byte[] bytes = Encoding.UTF8.GetBytes(content);
NetOutgoingMessage om = CreateMessage(2 + bytes.Length);
om.WriteVariableUInt32((uint)bytes.Length);
om.Write(bytes);
var om = CreateMessage(2 + content.Length); // fair guess
om.Write(content);
return om;
}
@@ -101,8 +123,10 @@ namespace Lidgren.Network
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
retval = new NetOutgoingMessage();
byte[] storage = GetStorage(initialCapacity);
retval.m_data = storage;
NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount);
if (initialCapacity > 0)
retval.m_data = GetStorage(initialCapacity);
return retval;
}
@@ -134,17 +158,18 @@ namespace Lidgren.Network
/// </summary>
public void Recycle(NetIncomingMessage msg)
{
if (m_incomingMessagesPool == null)
if (m_incomingMessagesPool == null || msg == null)
return;
#if DEBUG
if (m_incomingMessagesPool.Contains(msg))
throw new NetException("Recyling already recycled message! Thread race?");
#endif
NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled incoming message! Thread race?");
byte[] storage = msg.m_data;
msg.m_data = null;
Recycle(storage);
msg.Reset();
m_incomingMessagesPool.Enqueue(msg);
if (m_incomingMessagesPool.Count < m_maxCacheCount)
m_incomingMessagesPool.Enqueue(msg);
}
/// <summary>
@@ -154,34 +179,8 @@ namespace Lidgren.Network
{
if (m_incomingMessagesPool == null)
return;
// first recycle the storage of each message
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);
foreach (var im in toRecycle)
Recycle(im);
}
internal void Recycle(NetOutgoingMessage msg)
@@ -189,20 +188,25 @@ namespace Lidgren.Network
if (m_outgoingMessagesPool == null)
return;
#if DEBUG
if (m_outgoingMessagesPool.Contains(msg))
throw new NetException("Recyling already recycled message! Thread race?");
NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled outgoing message! Thread race?");
if (msg.m_recyclingCount != 0)
LogWarning("Wrong recycling count! should be zero; found " + msg.m_recyclingCount);
#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;
msg.m_data = null;
// 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
if (msg.m_fragmentGroup == 0)
Recycle(storage);
msg.Reset();
m_outgoingMessagesPool.Enqueue(msg);
if (m_outgoingMessagesPool.Count < m_maxCacheCount)
m_outgoingMessagesPool.Enqueue(msg);
}
/// <summary>

View File

@@ -3,6 +3,10 @@ using System.Collections.Generic;
using System.Threading;
using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
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");
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
if (len <= recipient.m_currentMTU)
if (len <= recipient.m_currentMTU || suppressFragmentation)
{
Interlocked.Increment(ref msg.m_recyclingCount);
return recipient.EnqueueMessage(msg, method, sequenceChannel);
@@ -57,17 +63,25 @@ namespace Lidgren.Network
// message must be fragmented!
if (recipient.m_status != NetConnectionStatus.Connected)
return NetSendResult.FailedNotConnected;
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
return SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
}
}
internal static int GetMTU(IList<NetConnection> recipients)
{
int count = recipients.Count;
NetException.Assert(count > 0);
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++)
{
var conn = recipients[i];
@@ -85,23 +99,30 @@ namespace Lidgren.Network
/// <param name="recipients">The list of recipients to send to</param>
/// <param name="method">How to deliver the message</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)
throw new ArgumentNullException("msg");
if (recipients == null)
{
if (msg.m_isSent == false)
Recycle(msg);
throw new ArgumentNullException("recipients");
}
if (recipients.Count < 1)
{
if (msg.m_isSent == false)
Recycle(msg);
throw new NetException("recipients must contain at least one item");
}
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
if (msg.m_isSent)
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);
msg.m_isSent = true;
int len = msg.GetEncodedSize();
if (len <= mtu)
{
@@ -114,7 +135,7 @@ namespace Lidgren.Network
continue;
}
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
if (res != NetSendResult.Queued && res != NetSendResult.Sent)
if (res == NetSendResult.Dropped)
Interlocked.Decrement(ref msg.m_recyclingCount);
}
}
@@ -141,21 +162,21 @@ namespace Lidgren.Network
if (msg.LengthBytes > 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)
throw new NetException("Failed to resolve " + host);
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
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>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
public void SendUnconnectedMessage(NetOutgoingMessage msg, NetEndPoint recipient)
{
if (msg == null)
throw new ArgumentNullException("msg");
@@ -170,13 +191,13 @@ namespace Lidgren.Network
msg.m_isSent = true;
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>
/// Send a message to an unconnected host
/// </summary>
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients)
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<NetEndPoint> recipients)
{
if (msg == null)
throw new ArgumentNullException("msg");
@@ -193,35 +214,42 @@ namespace Lidgren.Network
msg.m_isSent = true;
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
foreach(IPEndPoint ep in recipients)
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg));
foreach (NetEndPoint ep in recipients)
m_unsentUnconnectedMessages.Enqueue(new NetTuple<NetEndPoint, NetOutgoingMessage>(ep, msg));
}
/// <summary>
/// Send a message to this exact same netpeer (loopback)
/// </summary>
public void SendUnconnectedToSelf(NetOutgoingMessage msg)
public void SendUnconnectedToSelf(NetOutgoingMessage om)
{
if (msg == null)
if (om == null)
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");
msg.m_messageType = NetMessageType.Unconnected;
msg.m_isSent = true;
om.m_messageType = NetMessageType.Unconnected;
om.m_isSent = true;
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
{
Interlocked.Decrement(ref om.m_recyclingCount);
return; // dropping unconnected message since it's not enabled for receiving
}
NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes);
om.Write(msg);
om.m_isFragment = false;
om.m_receiveTime = NetTime.Now;
om.m_senderConnection = null;
om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint;
NetException.Assert(om.m_bitLength == msg.LengthBits);
// convert outgoing to incoming
NetIncomingMessage im = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, om.LengthBytes);
im.Write(om);
im.m_isFragment = false;
im.m_receiveTime = NetTime.Now;
im.m_senderConnection = null;
im.m_senderEndPoint = m_socket.LocalEndPoint as NetEndPoint;
NetException.Assert(im.m_bitLength == om.LengthBits);
ReleaseMessage(om);
// recycle outgoing message
Recycle(om);
ReleaseMessage(im);
}
}
}

View File

@@ -3,6 +3,10 @@ using System.Threading;
using System.Collections.Generic;
using System.Net;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
/// <summary>
@@ -14,9 +18,10 @@ namespace Lidgren.Network
private int m_listenPort;
private object m_tag;
private object m_messageReceivedEventCreationLock = new object();
internal readonly List<NetConnection> m_connections;
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
private readonly Dictionary<NetEndPoint, NetConnection> m_connectionLookup;
private string m_shutdownReason;
@@ -36,7 +41,13 @@ namespace Lidgren.Network
get
{
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;
}
}
@@ -106,11 +117,11 @@ namespace Lidgren.Network
m_configuration = config;
m_statistics = new NetPeerStatistics(this);
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_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
m_connectionLookup = new Dictionary<NetEndPoint, NetConnection>();
m_handshakes = new Dictionary<NetEndPoint, NetConnection>();
m_senderRemote = (EndPoint)new NetEndPoint(IPAddress.Any, 0);
m_status = NetPeerStatus.NotRunning;
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
}
@@ -149,13 +160,13 @@ namespace Lidgren.Network
m_upnp.Discover(this);
// 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>
/// Get the connection, if any, for a certain remote endpoint
/// </summary>
public NetConnection GetConnection(IPEndPoint ep)
public NetConnection GetConnection(NetEndPoint ep)
{
NetConnection retval;
@@ -169,15 +180,24 @@ namespace Lidgren.Network
/// <summary>
/// Read a pending message from any connection, blocking up to maxMillis if needed
/// </summary>
public NetIncomingMessage WaitMessage(int maxMillis)
{
var msg = ReadMessage();
if (msg != null)
return msg; // no need to wait; we already have a message to deliver
if (m_messageReceivedEvent != null)
m_messageReceivedEvent.WaitOne(maxMillis);
return ReadMessage();
}
public NetIncomingMessage WaitMessage(int maxMillis)
{
NetIncomingMessage msg = ReadMessage();
while (msg == null)
{
// This could return true...
if (!MessageReceivedEvent.WaitOne(maxMillis))
{
return null;
}
// ... while this will still returns null. That's why we need to cycle.
msg = ReadMessage();
}
return msg;
}
/// <summary>
/// Read a pending message from any connection, if any
@@ -218,8 +238,8 @@ namespace Lidgren.Network
return added;
}
// send message immediately
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
// send message immediately and recycle it
internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient)
{
VerifyNetworkThread();
NetException.Assert(msg.m_isSent == false);
@@ -227,6 +247,18 @@ namespace Lidgren.Network
bool connReset;
int len = msg.Encode(m_sendBuffer, 0, 0);
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>
@@ -234,7 +266,7 @@ namespace Lidgren.Network
/// </summary>
public NetConnection Connect(string host, int port)
{
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
return Connect(GetNetEndPoint(host, port), null);
}
/// <summary>
@@ -242,13 +274,13 @@ namespace Lidgren.Network
/// </summary>
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>
/// Create a connection to a remote endpoint
/// </summary>
public NetConnection Connect(IPEndPoint remoteEndPoint)
public NetConnection Connect(NetEndPoint remoteEndPoint)
{
return Connect(remoteEndPoint, null);
}
@@ -256,7 +288,7 @@ namespace Lidgren.Network
/// <summary>
/// Create a connection to a remote endpoint
/// </summary>
public virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
{
if (remoteEndPoint == null)
throw new ArgumentNullException("remoteEndPoint");
@@ -281,7 +313,7 @@ namespace Lidgren.Network
break;
case NetConnectionStatus.RespondedConnect:
// send another response
hs.SendConnectResponse((float)NetTime.Now, false);
hs.SendConnectResponse(NetTime.Now, false);
break;
default:
// weird
@@ -308,18 +340,27 @@ namespace Lidgren.Network
/// <summary>
/// Send raw bytes; only used for debugging
/// </summary>
#if DEBUG
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#else
internal void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
#endif
{
public void RawSend(byte[] arr, int offset, int length, NetEndPoint destination)
{
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
Array.Copy(arr, offset, m_sendBuffer, 0, length);
bool 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>
/// Disconnects all active connections and closes the socket
/// </summary>

View File

@@ -27,6 +27,20 @@ namespace Lidgren.Network
/// </summary>
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 bool m_isLocked;
@@ -39,9 +53,12 @@ namespace Lidgren.Network
internal int m_defaultOutgoingMessageCapacity;
internal float m_pingInterval;
internal bool m_useMessageRecycling;
internal int m_recycledCacheMaxCount;
internal float m_connectionTimeout;
internal bool m_enableUPnP;
internal bool m_autoFlushSendQueue;
private NetUnreliableSizeBehaviour m_unreliableSizeBehaviour;
internal bool m_suppressUnreliableUnorderedAcks;
internal NetIncomingMessageType m_disabledTypes;
internal int m_port;
@@ -69,12 +86,12 @@ namespace Lidgren.Network
{
if (string.IsNullOrEmpty(appIdentifier))
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
//
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_localAddress = IPAddress.Any;
m_broadcastAddress = IPAddress.Broadcast;
@@ -92,22 +109,17 @@ namespace Lidgren.Network
m_pingInterval = 4.0f;
m_connectionTimeout = 25.0f;
m_useMessageRecycling = true;
m_recycledCacheMaxCount = 64;
m_resendHandshakeInterval = 3.0f;
m_maximumHandshakeAttempts = 5;
m_autoFlushSendQueue = true;
m_suppressUnreliableUnorderedAcks = false;
// 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"
m_maximumTransmissionUnit = 1408;
m_maximumTransmissionUnit = kDefaultMTU;
m_autoExpandMTU = false;
m_expandMTUFrequency = 2.0f;
m_expandMTUFailAttempts = 5;
m_unreliableSizeBehaviour = NetUnreliableSizeBehaviour.IgnoreMTU;
m_loss = 0.0f;
m_minimumOneWayLatency = 0.0f;
@@ -165,6 +177,15 @@ namespace Lidgren.Network
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>
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
/// </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>
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
/// </summary>
@@ -278,6 +313,20 @@ namespace Lidgren.Network
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>
/// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized.
/// </summary>
@@ -468,4 +517,25 @@ namespace Lidgren.Network
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,
}
}

View File

@@ -39,6 +39,7 @@ namespace Lidgren.Network
internal int m_sentMessages;
internal int m_receivedMessages;
internal int m_receivedFragments;
internal int m_sentBytes;
internal int m_receivedBytes;
@@ -58,6 +59,7 @@ namespace Lidgren.Network
m_sentMessages = 0;
m_receivedMessages = 0;
m_receivedFragments = 0;
m_sentBytes = 0;
m_receivedBytes = 0;
@@ -103,41 +105,35 @@ namespace Lidgren.Network
/// <summary>
/// Gets the number of bytes in the recycled pool
/// </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)
{
m_sentPackets++;
m_sentBytes += numBytes;
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
internal void PacketReceived(int numBytes, int numMessages)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
}
#else
#if !USE_RELEASE_STATISTICS
[Conditional("DEBUG")]
internal void PacketReceived(int numBytes, int numMessages)
#endif
internal void PacketReceived(int numBytes, int numMessages, int numFragments)
{
m_receivedPackets++;
m_receivedBytes += numBytes;
m_receivedMessages += numMessages;
m_receivedFragments += numFragments;
}
#endif
/// <summary>
/// Returns a string that represents this object
@@ -148,13 +144,14 @@ namespace Lidgren.Network
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
#if DEBUG || USE_RELEASE_STATISTICS
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
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");
#endif
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();
}
}

View File

@@ -22,6 +22,10 @@ using System.Diagnostics;
using System.Collections.Generic;
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
{
/// <summary>
@@ -52,12 +56,29 @@ namespace Lidgren.Network
/// <summary>
/// Gets the number of items in the queue
/// </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>
/// Gets the current capacity for the queue
/// </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>
/// NetQueue constructor
@@ -193,6 +214,15 @@ namespace Lidgren.Network
return true;
}
catch
{
#if DEBUG
throw;
#else
item = default(T);
return false;
#endif
}
finally
{
m_lock.ExitWriteLock();
@@ -200,7 +230,7 @@ namespace Lidgren.Network
}
/// <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>
public int TryDrain(IList<T> addTo)
{

View File

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

View File

@@ -1,373 +1,177 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Lidgren.Network
{
/// <summary>
/// A fast random number generator for .NET
/// Colin Green, January 2005
/// NetRandom base class
/// </summary>
/// September 4th 2005
/// 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
public abstract class NetRandom : Random
{
/// <summary>
/// Gets a global NetRandom instance
/// Get global instance of NetRandom (uses MWCRandom)
/// </summary>
public static readonly NetRandom Instance = new NetRandom();
public static NetRandom Instance = new MWCRandom();
// The +1 ensures NextDouble doesn't generate 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
private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
/// <summary>
/// Initialises a new instance using time dependent seed.
/// Constructor with randomized seed
/// </summary>
public NetRandom()
{
// Initialise using the system tick count.
Reinitialise(GetSeed(this));
Initialize(NetRandomSeed.GetUInt32());
}
/// <summary>
/// Initialises a new instance using an int value as seed.
/// This constructor signature is provided to maintain compatibility with
/// System.Random
/// Constructor with provided 32 bit seed
/// </summary>
public NetRandom(int seed)
{
Reinitialise(seed);
Initialize((uint)seed);
}
/// <summary>
/// Create a semi-random seed based on an object
/// (Re)initialize this instance with provided 32 bit seed
/// </summary>
public static int GetSeed(object forObject)
[CLSCompliant(false)]
public virtual void Initialize(uint seed)
{
// mix some semi-random properties
int seed = (int)Environment.TickCount;
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;
// should be abstract, but non-CLS compliant methods can't be abstract!
throw new NotImplementedException("Implement this in inherited classes");
}
#endregion
#region Public Methods [Reinitialisation]
/// <summary>
/// Reinitialises using an int value as a seed.
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
/// </summary>
/// <param name="seed"></param>
public void Reinitialise(int seed)
[CLSCompliant(false)]
public virtual uint NextUInt32()
{
// The only stipulation stated for the xorshift RNG is that at least one of
// the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
// resetting of the x seed
x = (uint)seed;
y = Y;
z = Z;
w = W;
// should be abstract, but non-CLS compliant methods can't be abstract!
throw new NotImplementedException("Implement this in inherited classes");
}
#endregion
#region Public Methods [System.Random functionally equivalent methods]
/// <summary>
/// Generates a random int over the range 0 to int.MaxValue-1.
/// 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.
/// Generates a random value that is greater or equal than 0 and less than Int32.MaxValue
/// </summary>
/// <returns></returns>
public int Next()
public override int Next()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
// 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;
var retval = (int)(0x7FFFFFFF & NextUInt32());
if (retval == 0x7FFFFFFF)
return NextInt32();
return retval;
}
/// <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>
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int upperBound)
public int NextInt32()
{
if (upperBound < 0)
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);
return (int)(0x7FFFFFFF & NextUInt32());
}
/// <summary>
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
/// upperBound must be >= lowerBound. lowerBound may be negative.
/// Returns random value larger or equal to 0.0 and less than 1.0
/// </summary>
/// <param name="lowerBound"></param>
/// <param name="upperBound"></param>
/// <returns></returns>
public int Next(int lowerBound, int upperBound)
public override double NextDouble()
{
if (lowerBound > upperBound)
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);
return c_realUnitInt * NextInt32();
}
/// <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>
/// <returns></returns>
public double NextDouble()
protected override double Sample()
{
uint t = (x ^ (x << 11));
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)))));
return c_realUnitInt * NextInt32();
}
/// <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>
public float NextSingle()
{
return (float)NextDouble();
var retval = (float)(c_realUnitInt * NextInt32());
if (retval == 1.0f)
return NextSingle();
return retval;
}
/// <summary>
/// Fills the provided byte array with random bytes.
/// This method is functionally equivalent to System.Random.NextBytes().
/// Returns a random value is greater or equal than 0 and less than maxValue
/// </summary>
/// <param name="buffer"></param>
public void NextBytes(byte[] buffer)
public override int Next(int maxValue)
{
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
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;
return (int)(NextDouble() * maxValue);
}
// /// <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>
/// Generates a uint. Values returned are over the full range of a uint,
/// uint.MinValue to uint.MaxValue, inclusive.
///
/// 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
/// a uint.
/// Returns a random value is greater or equal than minValue and less than maxValue
/// </summary>
public override int Next(int minValue, int maxValue)
{
return minValue + (int)(NextDouble() * (double)(maxValue - minValue));
}
/// <summary>
/// Generates a random value between UInt64.MinValue to UInt64.MaxValue
/// </summary>
[CLSCompliant(false)]
public uint NextUInt()
public ulong NextUInt64()
{
uint t = (x ^ (x << 11));
x = y; y = z; z = w;
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
ulong retval = NextUInt32();
retval |= NextUInt32() << 32;
return retval;
}
/// <summary>
/// Generates a random int over the range 0 to int.MaxValue, inclusive.
/// 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;
private uint m_boolValues;
private int m_nextBoolIndex;
/// <summary>
/// Generates a single random bit.
/// This method's performance is improved by generating 32 bits in one operation and storing them
/// ready for future calls.
/// Returns true or false, randomly
/// </summary>
/// <returns></returns>
public bool NextBool()
{
if (bitMask == 1)
if (m_nextBoolIndex >= 32)
{
// Generate 32 more bits.
uint t = (x ^ (x << 11));
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;
m_boolValues = NextUInt32();
m_nextBoolIndex = 1;
}
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);
}
}
}

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

View File

@@ -66,8 +66,9 @@ namespace Lidgren.Network
if (relate < 0)
{
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
// duplicate
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return;
}
@@ -75,6 +76,7 @@ namespace Lidgren.Network
if (relate > m_windowSize)
{
// too early message!
m_connection.m_statistics.MessageDropped();
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}

View File

@@ -13,19 +13,27 @@ namespace Lidgren.Network
private int m_windowSize;
private int m_sendStart;
private bool m_anyStoredResends;
private NetBitVector m_receivedAcks;
internal NetStoredReliableMessage[] m_storedMessages;
internal float m_resendDelay;
internal double m_resendDelay;
internal override int WindowSize { get { return m_windowSize; } }
internal override bool NeedToSendMessages()
{
return base.NeedToSendMessages() || m_anyStoredResends;
}
internal NetReliableSenderChannel(NetConnection connection, int windowSize)
{
m_connection = connection;
m_windowSize = windowSize;
m_windowStart = 0;
m_sendStart = 0;
m_anyStoredResends = false;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
m_storedMessages = new NetStoredReliableMessage[m_windowSize];
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
@@ -44,6 +52,7 @@ namespace Lidgren.Network
m_receivedAcks.Clear();
for (int i = 0; i < m_storedMessages.Length; i++)
m_storedMessages[i].Reset();
m_anyStoredResends = false;
m_queuedSends.Clear();
m_windowStart = 0;
m_sendStart = 0;
@@ -52,27 +61,29 @@ namespace Lidgren.Network
internal override NetSendResult Enqueue(NetOutgoingMessage message)
{
m_queuedSends.Enqueue(message);
int queueLen = m_queuedSends.Count;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen <= left)
m_connection.m_peer.m_needFlushSendQueue = true; // a race condition to this variable will simply result in a single superflous call to FlushSendQueue()
if (m_queuedSends.Count <= GetAllowedSends())
return NetSendResult.Sent;
return NetSendResult.Queued;
}
// call this regularely
internal override void SendQueuedMessages(float now)
internal override void SendQueuedMessages(double now)
{
//
// resends
//
m_anyStoredResends = false;
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)
continue;
float t = m_storedMessages[i].LastSent;
m_anyStoredResends = true;
double t = storedMsg.LastSent;
if (t > 0 && (now - t) > m_resendDelay)
{
// 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_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].NumSent++;
@@ -103,7 +115,7 @@ namespace Lidgren.Network
return;
// queued sends
while (m_queuedSends.Count > 0 && num > 0)
while (num > 0 && m_queuedSends.Count > 0)
{
NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om))
@@ -112,12 +124,16 @@ namespace Lidgren.Network
NetException.Assert(num == GetAllowedSends());
}
}
private void ExecuteSend(float now, NetOutgoingMessage message)
private void ExecuteSend(double now, NetOutgoingMessage message)
{
int seqNr = m_sendStart;
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);
int storeIndex = seqNr % m_windowSize;
@@ -127,13 +143,22 @@ namespace Lidgren.Network
m_storedMessages[storeIndex].Message = message;
m_storedMessages[storeIndex].LastSent = now;
m_storedMessages[storeIndex].SequenceNumber = seqNr;
m_anyStoredResends = true;
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 (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");
@@ -141,7 +166,6 @@ namespace Lidgren.Network
if (storedMessage != null)
{
#endif
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
if (storedMessage.m_recyclingCount <= 0)
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
// 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?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
@@ -171,8 +195,9 @@ namespace Lidgren.Network
// ack arrived right on time
NetException.Assert(seqNr == m_windowStart);
bool resetTimeout;
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;
// 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_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
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
//m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
}
if (resetTimeout)
m_connection.ResetTimeout(now);
return;
}
@@ -241,7 +269,7 @@ namespace Lidgren.Network
NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
//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
}
@@ -250,6 +278,7 @@ namespace Lidgren.Network
m_storedMessages[slot].LastSent = now;
m_storedMessages[slot].NumSent++;
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);
}
}

View File

@@ -42,6 +42,7 @@ namespace Lidgren.Network
if (relate < 0)
{
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
return;
}
@@ -50,6 +51,7 @@ namespace Lidgren.Network
if (relate > m_windowSize)
{
// too early message!
m_connection.m_statistics.MessageDropped();
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
return;
}

View File

@@ -65,6 +65,7 @@ namespace Lidgren.Network
if (relate < 0)
{
// duplicate
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
return;
}
@@ -73,10 +74,19 @@ namespace Lidgren.Network
if (relate > m_windowSize)
{
// too early message!
m_connection.m_statistics.MessageDropped();
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
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_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
//m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;

View File

@@ -14,17 +14,7 @@ namespace Lidgren.Network
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
private static readonly NetBigInteger g = NetBigInteger.Two;
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>
/// Compute multiplier (k)
/// </summary>
@@ -36,9 +26,7 @@ namespace Lidgren.Network
string ccstr = one + two.PadLeft(one.Length, '0');
byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm();
var ccHashed = sha.ComputeHash(cc);
var ccHashed = NetUtility.ComputeSHAHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16);
}
@@ -48,7 +36,7 @@ namespace Lidgren.Network
public static byte[] CreateRandomSalt()
{
byte[] retval = new byte[16];
NetRandom.Instance.NextBytes(retval);
CryptoRandom.Instance.NextBytes(retval);
return retval;
}
@@ -58,7 +46,7 @@ namespace Lidgren.Network
public static byte[] CreateRandomEphemeral()
{
byte[] retval = new byte[32];
NetRandom.Instance.NextBytes(retval);
CryptoRandom.Instance.NextBytes(retval);
return retval;
}
@@ -67,17 +55,15 @@ namespace Lidgren.Network
/// </summary>
public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
{
var sha = GetHashAlgorithm();
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];
Buffer.BlockCopy(salt, 0, total, 0, salt.Length);
Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length);
// 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>
@@ -93,15 +79,6 @@ namespace Lidgren.Network
return serverVerifier.ToByteArrayUnsigned();
}
/// <summary>
/// SHA hash data
/// </summary>
public static byte[] Hash(byte[] data)
{
var sha = GetHashAlgorithm();
return sha.ComputeHash(data);
}
/// <summary>
/// Compute client public ephemeral value (A)
/// </summary>
@@ -144,8 +121,7 @@ namespace Lidgren.Network
byte[] cc = NetUtility.ToByteArray(ccstr);
var sha = GetHashAlgorithm();
var ccHashed = sha.ComputeHash(cc);
var ccHashed = NetUtility.ComputeSHAHash(cc);
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned();
}
@@ -185,10 +161,9 @@ namespace Lidgren.Network
/// <summary>
/// Create XTEA symmetrical encryption object from sessionValue
/// </summary>
public static NetXtea CreateEncryption(byte[] sessionValue)
public static NetXtea CreateEncryption(NetPeer peer, byte[] sessionValue)
{
var sha = GetHashAlgorithm();
var hash = sha.ComputeHash(sessionValue);
var hash = NetUtility.ComputeSHAHash(sessionValue);
var key = new byte[16];
for(int i=0;i<16;i++)
@@ -198,7 +173,7 @@ namespace Lidgren.Network
key[i] ^= hash[i + (j * 16)];
}
return new NetXtea(key);
return new NetXtea(peer, key);
}
}
}

View File

@@ -5,15 +5,24 @@ namespace Lidgren.Network
internal abstract class NetSenderChannelBase
{
// 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 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 void SendQueuedMessages(float now);
internal abstract void SendQueuedMessages(double now);
internal abstract void Reset();
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber);
internal abstract void ReceiveAcknowledge(double now, int sequenceNumber);
}
}

View File

@@ -5,14 +5,14 @@ namespace Lidgren.Network
internal struct NetStoredReliableMessage
{
public int NumSent;
public float LastSent;
public double LastSent;
public NetOutgoingMessage Message;
public int SequenceNumber;
public void Reset()
{
NumSent = 0;
LastSent = 0;
LastSent = 0.0;
Message = null;
}
}

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.
*/
#define IS_STOPWATCH_AVAILABLE
using System;
using System.Collections.Generic;
using System.Text;
@@ -29,25 +27,8 @@ namespace Lidgren.Network
/// <summary>
/// Time service
/// </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>
/// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
/// </summary>

View File

@@ -5,6 +5,10 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
#if !__NOIPENDPOINT__
using NetEndPoint = System.Net.IPEndPoint;
#endif
namespace Lidgren.Network
{
/// <summary>
@@ -40,7 +44,7 @@ namespace Lidgren.Network
private NetPeer m_peer;
private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false);
internal float m_discoveryResponseDeadline;
internal double m_discoveryResponseDeadline;
private UPnPStatus m_status;
@@ -55,7 +59,7 @@ namespace Lidgren.Network
public NetUPnP(NetPeer peer)
{
m_peer = peer;
m_discoveryResponseDeadline = float.MinValue;
m_discoveryResponseDeadline = double.MinValue;
}
internal void Discover(NetPeer peer)
@@ -67,22 +71,25 @@ namespace Lidgren.Network
"MAN:\"ssdp:discover\"\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;
byte[] arr = System.Text.Encoding.UTF8.GetBytes(str);
m_peer.LogDebug("Attempting UPnP discovery");
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);
// 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)
{
#if !DEBUG
@@ -90,7 +97,9 @@ namespace Lidgren.Network
{
#endif
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);
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
@@ -168,11 +177,11 @@ namespace Lidgren.Network
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
SOAPRequest(m_serviceUrl,
"<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" +
"<NewRemoteHost></NewRemoteHost>" +
"<NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture) + "</NewProtocol>" +
"<NewInternalPort>" + port.ToString() + "</NewInternalPort>" +
"<NewInternalClient>" + client.ToString() + "</NewInternalClient>" +
"<NewEnabled>1</NewEnabled>" +
@@ -182,7 +191,7 @@ namespace Lidgren.Network
"AddPortMapping");
m_peer.LogDebug("Sent UPnP port forward request");
System.Threading.Thread.Sleep(50);
NetUtility.Sleep(50);
}
catch (Exception ex)
{
@@ -202,12 +211,12 @@ namespace Lidgren.Network
try
{
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
SOAPRequest(m_serviceUrl,
"<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" +
"<NewRemoteHost>" +
"</NewRemoteHost>" +
"<NewExternalPort>" + port + "</NewExternalPort>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture) + "</NewProtocol>" +
"</u:DeletePortMapping>", "DeletePortMapping");
return true;
}
@@ -256,11 +265,12 @@ namespace Lidgren.Network
r.ContentType = "text/xml; charset=\"utf-8\"";
r.ContentLength = b.Length;
r.GetRequestStream().Write(b, 0, b.Length);
XmlDocument resp = new XmlDocument();
WebResponse wres = r.GetResponse();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
using (WebResponse wres = r.GetResponse()) {
XmlDocument resp = new XmlDocument();
Stream ress = wres.GetResponseStream();
resp.Load(ress);
return resp;
}
}
}
}

View File

@@ -12,12 +12,13 @@ namespace Lidgren.Network
private int m_windowStart;
private int m_windowSize;
private int m_sendStart;
private bool m_doFlowControl;
private NetBitVector m_receivedAcks;
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_windowSize = windowSize;
@@ -25,10 +26,16 @@ namespace Lidgren.Network
m_sendStart = 0;
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
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()
{
if (!m_doFlowControl)
return 2; // always allowed to send without flow control!
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
NetException.Assert(retval >= 0 && retval <= m_windowSize);
return retval;
@@ -45,23 +52,27 @@ namespace Lidgren.Network
internal override NetSendResult Enqueue(NetOutgoingMessage message)
{
int queueLen = m_queuedSends.Count + 1;
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
if (queueLen > left)
int left = GetAllowedSends();
if (queueLen > left || (message.LengthBytes > m_connection.m_currentMTU && m_connection.m_peerConfiguration.UnreliableSizeBehaviour == NetUnreliableSizeBehaviour.DropAboveMTU))
{
// drop message
return NetSendResult.Dropped;
}
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;
}
// call this regularely
internal override void SendQueuedMessages(float now)
internal override void SendQueuedMessages(double now)
{
int num = GetAllowedSends();
if (num < 1)
return;
// queued sends
while (m_queuedSends.Count > 0 && num > 0)
while (num > 0 && m_queuedSends.Count > 0)
{
NetOutgoingMessage om;
if (m_queuedSends.TryDequeue(out om))
@@ -79,7 +90,6 @@ namespace Lidgren.Network
m_connection.QueueSendMessage(message, seqNr);
Interlocked.Decrement(ref message.m_recyclingCount);
if (message.m_recyclingCount <= 0)
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
// 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?
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);

View File

@@ -4,7 +4,7 @@ namespace Lidgren.Network
{
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
{
private int m_lastReceivedSequenceNumber;
private int m_lastReceivedSequenceNumber = -1;
public NetUnreliableSequencedReceiver(NetConnection connection)
: base(connection)
@@ -18,9 +18,13 @@ namespace Lidgren.Network
// ack no matter what
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)
{
m_connection.m_statistics.MessageDropped();
m_peer.LogVerbose("Received message #" + nr + " DROPPING DUPLICATE");
return; // drop if late
}
m_lastReceivedSequenceNumber = nr;
m_peer.ReleaseMessage(msg);

View File

@@ -4,15 +4,18 @@ namespace Lidgren.Network
{
internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase
{
private bool m_doFlowControl;
public NetUnreliableUnorderedReceiver(NetConnection connection)
: base(connection)
{
m_doFlowControl = connection.Peer.Configuration.SuppressUnreliableUnorderedAcks == false;
}
internal override void ReceiveMessage(NetIncomingMessage msg)
{
// ack no matter what
m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber);
if (m_doFlowControl)
m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber);
m_peer.ReleaseMessage(msg);
}

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
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
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Security.Cryptography;
namespace Lidgren.Network
{
/// <summary>
/// Utility methods
/// </summary>
public static class NetUtility
public static partial class NetUtility
{
private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null;
/// <summary>
/// Resolve endpoint callback
/// </summary>
public delegate void ResolveEndPointCallback(IPEndPoint endPoint);
public delegate void ResolveEndPointCallback(NetEndPoint endPoint);
/// <summary>
/// Resolve address callback
/// </summary>
public delegate void ResolveAddressCallback(IPAddress adr);
public delegate void ResolveAddressCallback(NetAddress adr);
/// <summary>
/// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number (asynchronous version)
/// </summary>
public static void ResolveAsync(string ipOrHost, int port, ResolveEndPointCallback callback)
{
ResolveAsync(ipOrHost, delegate(IPAddress adr)
ResolveAsync(ipOrHost, delegate(NetAddress adr)
{
if (adr == null)
{
@@ -58,7 +63,7 @@ namespace Lidgren.Network
}
else
{
callback(new IPEndPoint(adr, port));
callback(new NetEndPoint(adr, port));
}
});
}
@@ -66,10 +71,18 @@ namespace Lidgren.Network
/// <summary>
/// Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number
/// </summary>
public static IPEndPoint Resolve(string ipOrHost, int port)
public static NetEndPoint Resolve(string ipOrHost, int port)
{
IPAddress adr = Resolve(ipOrHost);
return new IPEndPoint(adr, port);
var adr = Resolve(ipOrHost);
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>
@@ -82,8 +95,8 @@ namespace Lidgren.Network
ipOrHost = ipOrHost.Trim();
IPAddress ipAddress = null;
if (IPAddress.TryParse(ipOrHost, out ipAddress))
NetAddress ipAddress = null;
if (NetAddress.TryParse(ipOrHost, out ipAddress))
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
@@ -124,7 +137,7 @@ namespace Lidgren.Network
}
// 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)
{
@@ -153,15 +166,15 @@ namespace Lidgren.Network
/// <summary>
/// Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname
/// </summary>
public static IPAddress Resolve(string ipOrHost)
public static NetAddress Resolve(string ipOrHost)
{
if (string.IsNullOrEmpty(ipOrHost))
throw new ArgumentException("Supplied string must not be empty", "ipOrHost");
ipOrHost = ipOrHost.Trim();
IPAddress ipAddress = null;
if (IPAddress.TryParse(ipOrHost, out ipAddress))
NetAddress ipAddress = null;
if (NetAddress.TryParse(ipOrHost, out ipAddress))
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
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>
/// Create a hex string from an Int64 value
/// </summary>
@@ -258,134 +221,30 @@ namespace Lidgren.Network
/// </summary>
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;
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);
b = ((byte)(data[i] & 0xF));
b = ((byte)(data[offset + i] & 0xF));
c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
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>
/// 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>
public static IPAddress GetMyAddress(out IPAddress mask)
{
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)
public static bool IsLocal(NetEndPoint endPoint)
{
if (endPoint == null)
return false;
@@ -395,10 +254,10 @@ namespace Lidgren.Network
/// <summary>
/// Returns true if the IPAddress supplied is on the same subnet as this host
/// </summary>
public static bool IsLocal(IPAddress remote)
public static bool IsLocal(NetAddress remote)
{
IPAddress mask;
IPAddress local = GetMyAddress(out mask);
NetAddress mask;
var local = GetMyAddress(out mask);
if (mask == null)
return false;
@@ -588,5 +447,11 @@ namespace Lidgren.Network
}
return bdr.ToString();
}
public static byte[] ComputeSHAHash(byte[] bytes)
{
// this is defined in the platform specific files
return ComputeSHAHash(bytes, 0, bytes.Length);
}
}
}

View File

@@ -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

View File

@@ -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

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

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

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

View File

@@ -99,6 +99,7 @@
<Compile Include="Source\Map\LocationType.cs" />
<Compile Include="Source\Map\SubmarineHull.cs" />
<Compile Include="Source\Networking\NetConfig.cs" />
<Compile Include="Source\Networking\NetStats.cs" />
<Compile Include="Source\Particles\ParticleEmitter.cs" />
<Compile Include="Source\Screens\ServerListScreen.cs" />
<Compile Include="Source\Utils\Rand.cs" />

View File

@@ -61,7 +61,8 @@
<Item
name="Railgun Shell"
pickdistance="200">
pickdistance="200"
price="100">
<Sprite texture ="railgunshell.png" depth ="0.5"/>

View File

@@ -299,7 +299,7 @@ namespace Barotrauma
//the collision is ignored if the lowest limb is under the platform
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)
{

View File

@@ -21,6 +21,8 @@ namespace Barotrauma
public bool Wrap;
private float textDepth;
public override Vector4 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
{
get;
@@ -221,7 +229,7 @@ namespace Barotrauma
new Vector2(rect.X, rect.Y) + textPos,
textColor * (textColor.A / 255.0f),
0.0f, origin, 1.0f,
SpriteEffects.None, 0.0f);
SpriteEffects.None, textDepth);
}
DrawChildren(spriteBatch);

View File

@@ -9,6 +9,8 @@ namespace Barotrauma
Client traitor;
Client target;
private Character traitorCharacter, targetCharacter;
public TraitorMode(GameModePreset preset)
: base(preset)
{
@@ -47,34 +49,38 @@ namespace Barotrauma
}
target = GameMain.Server.connectedClients[targetIndex];
traitorCharacter = traitor.character;
targetCharacter = target.character;
GameMain.Server.NewTraitor(traitor, target);
}
else
{
if (target.character.IsDead)
if (target.character == null || target.character.IsDead)
{
string endMessage = traitor.character.Info.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ". The task was successful.";
string endMessage = traitorCharacter.Name + " was a traitor! ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + targetCharacter.Name + ". The task was successful.";
End(endMessage);
}
else if (traitor.character.IsDead)
else if (traitor.character == null || traitor.character.IsDead)
{
string endMessage = traitor.character.Info.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ", but ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "he" : "she";
endMessage += " got " + ((traitor.character.Info.Gender == Gender.Male) ? "himself" : "herself");
string endMessage = traitorCharacter.Name + " was a traitor! ";
//TODO: remove references to traitor.character
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + targetCharacter.Name + ", but ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "he" : "she";
endMessage += " got " + ((traitorCharacter.Info.Gender == Gender.Male) ? "himself" : "herself");
endMessage += " killed before completing it.";
End(endMessage);
return;
}
else if (Level.Loaded.AtEndPosition)
{
string endMessage = traitor.character.Info.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ". ";
string endMessage = traitorCharacter.Name + " was a traitor! ";
endMessage += (traitorCharacter.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + targetCharacter.Name + ". ";
endMessage += "The task was unsuccessful - the has submarine reached its destination.";
End(endMessage);
return;

View File

@@ -21,7 +21,7 @@ namespace Barotrauma
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();
@@ -164,6 +164,7 @@ namespace Barotrauma
while (Math.Abs(reactor.Temperature-3000.0f) > 100.0f)
{
reactor.AutoTemp = false;
reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f);
yield return CoroutineStatus.Running;
}

View File

@@ -131,6 +131,18 @@ namespace Barotrauma.Items.Components
{
picker = character;
if (item.body == null)
{
if (body!=null)
{
item.body = body;
}
else
{
return;
}
}
if (!item.body.Enabled)
{
Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand);

View File

@@ -41,6 +41,7 @@ namespace Barotrauma.Items.Components
null, null, true);
textBlock.Font = GUI.SmallFont;
textBlock.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
textBlock.TextDepth = item.Sprite.Depth - 0.0001f;
}
return textBlock;
}

View File

@@ -103,7 +103,11 @@ namespace Barotrauma.Items.Components
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);
stickJoint = null;
}
@@ -111,25 +115,19 @@ namespace Barotrauma.Items.Components
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);
}
GameMain.World.RemoveJoint(stickJoint);
stickJoint = null;
IsActive = false;
item.body.FarseerBody.RestoreCollisionWith(stickTarget);
stickTarget = null;
}
}
else
{
IsActive = false;
}
GameMain.World.RemoveJoint(stickJoint);
stickJoint = null;
IsActive = false;
}
}
private bool OnProjectileCollision(Fixture f1, Fixture f2, Contact contact)

View File

@@ -230,6 +230,13 @@ namespace Barotrauma
int itemCapacity = item.Capacity;
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,
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)
{
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;

View File

@@ -1163,11 +1163,19 @@ namespace Barotrauma
switch (type)
{
case NetworkEventType.DropItem:
if (body != null) body.FillNetworkData(type, message);
if (body != null)
{
message.Write(body.SimPosition.X);
message.Write(body.SimPosition.Y);
}
break;
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;
case NetworkEventType.UpdateProperty:
var allProperties = GetProperties<InGameEditable>();
@@ -1215,11 +1223,16 @@ namespace Barotrauma
switch (type)
{
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);
break;
case NetworkEventType.UpdateComponent:
int componentIndex = message.ReadInt32();
int componentIndex = message.ReadByte();
if (componentIndex < 0 || componentIndex > components.Count - 1) return;
components[componentIndex].ReadNetworkData(type, message);
break;
@@ -1272,7 +1285,7 @@ namespace Barotrauma
base.Remove();
//sprite.Remove();
if (body != null) body.Remove();
//if (body != null) body.Remove();
foreach (ItemComponent ic in components)
{

View File

@@ -255,7 +255,15 @@ namespace Barotrauma
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;
}

View File

@@ -244,7 +244,7 @@ namespace Barotrauma.Networking
NetConnectionStatus connectionStatus = (NetConnectionStatus)inc.ReadByte();
Debug.WriteLine(connectionStatus);
if (connectionStatus != NetConnectionStatus.Connected)
if (connectionStatus == NetConnectionStatus.Disconnected)
{
string denyMessage = inc.ReadString();

View File

@@ -16,6 +16,8 @@ namespace Barotrauma.Networking
//for keeping track of disconnected clients in case the reconnect shortly after
private List<Client> disconnectedClients = new List<Client>();
private NetStats netStats;
//is the server running
bool started;
@@ -34,6 +36,20 @@ namespace Barotrauma.Networking
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)
{
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");
netStats = new NetStats();
#if DEBUG
config.SimulatedLoss = 0.2f;
config.SimulatedMinimumLatency = 0.3f;
config.SimulatedDuplicatesChance = 0.05f;
config.SimulatedMinimumLatency = 0.1f;
#endif
config.Port = port;
Port = port;
@@ -61,9 +81,9 @@ namespace Barotrauma.Networking
config.DisableMessageType(NetIncomingMessageType.DebugMessage |
NetIncomingMessageType.WarningMessage | NetIncomingMessageType.Receipt |
NetIncomingMessageType.ErrorMessage | NetIncomingMessageType.Error);
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
CoroutineManager.StartCoroutine(StartServer(isPublic));
}
@@ -190,21 +210,23 @@ namespace Barotrauma.Networking
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;
return;
}
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
DebugConsole.ThrowError("Error while connecting to master server (" + response.StatusCode + ": " + response.StatusDescription + ")");
registeredToMaster = false;
DebugConsole.NewMessage("Error while reporting to master server (" + response.StatusCode + ": " + response.StatusDescription + ")", Color.Red);
//registeredToMaster = false;
return;
}
}
public override void Update(float deltaTime)
{
if (GameMain.DebugDraw) netStats.Update(deltaTime);
if (!started) return;
base.Update(deltaTime);
@@ -212,6 +234,24 @@ namespace Barotrauma.Networking
if (gameStarted)
{
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-- )
@@ -628,7 +668,7 @@ namespace Barotrauma.Networking
List<Character> crew = new List<Character>();
WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos);
for (int i = 0; i < connectedClients.Count; i++)
{
connectedClients[i].character = new Character(
@@ -817,7 +857,7 @@ namespace Barotrauma.Networking
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();
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 packets: " + server.Statistics.SentPackets, new Vector2(x + 10, y + 90), Color.White);
int resentMessages = 0;
y += 110;
foreach (Client c in connectedClients)
{
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);
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)

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

View File

@@ -124,8 +124,15 @@ namespace Barotrauma.Networking
System.Diagnostics.Debug.WriteLine("Networkevent entity: "+e.ToString());
//System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e);
e.ReadNetworkData(eventType, message);
try
{
e.ReadNetworkData(eventType, message);
}
catch
{
DebugConsole.ThrowError("Received invalid network message");
return false;
}
return true;
}

View File

@@ -28,19 +28,19 @@ namespace Barotrauma
{
using (var game = new GameMain())
{
//#if !DEBUG
#if !DEBUG
try
{
//#endif
#endif
game.Run();
//#if !DEBUG
#if !DEBUG
}
catch (Exception e)
{
CrashDump(game, "crashreport.txt", e);
}
//#endif
#endif
}
}

View File

@@ -18,7 +18,7 @@ namespace Barotrauma
private GUIListBox playerList;
private GUIListBox subList, modeList, chatBox;
private GUIListBox jobList;
private GUITextBox textBox, seedBox;
@@ -29,6 +29,8 @@ namespace Barotrauma
private GUIFrame playerFrame;
private GUITickBox autoRestartBox;
private float camAngle;
public bool IsServer;
@@ -91,10 +93,18 @@ namespace Barotrauma
}
}
//public string DurationText()
//{
// return "Duration: " + GameDuration.TotalMinutes + " min";
//}
private float autoRestartTimer;
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()
{
@@ -194,7 +204,6 @@ namespace Barotrauma
//gamemode description ------------------------------------------------------------------
var modeDescription = new GUITextBlock(
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);
@@ -214,6 +223,14 @@ namespace Barotrauma
seedBox.OnTextChanged = SelectSeed;
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 ------------------------------------------------------------------
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()
{
textBox.Deselect();
seedBox.Text = ToolBox.RandomSeed(8);
}
public override void Select()
@@ -372,6 +391,20 @@ namespace Barotrauma
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)
{
if (GameMain.Server != null) GameMain.Server.UpdateNetLobby(obj);
@@ -491,6 +524,11 @@ namespace Barotrauma
if (jobInfoFrame != null) jobInfoFrame.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);
}
@@ -722,13 +760,15 @@ namespace Barotrauma
//msg.Write(durationBar.BarScroll);
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));
for (int i = 0; i < playerList.CountChildren; i++)
{
string clientName = playerList.children[i].UserData as string;
msg.Write(clientName==null ? "" : clientName);
}
}
@@ -741,6 +781,10 @@ namespace Barotrauma
//float durationScroll = 0.0f;
string levelSeed = "";
bool autoRestart = false;
float restartTimer = 0.0f;
try
{
mapName = msg.ReadString();
@@ -755,6 +799,9 @@ namespace Barotrauma
levelSeed = msg.ReadString();
autoRestart = msg.ReadBoolean();
restartTimer = msg.ReadFloat();
int playerCount = msg.ReadByte();
playerList.ClearChildren();
@@ -773,6 +820,9 @@ namespace Barotrauma
modeList.Select(modeIndex);
autoRestartBox.Selected = autoRestart;
autoRestartTimer = restartTimer;
//durationBar.BarScroll = durationScroll;
LevelSeed = levelSeed;

View File

@@ -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[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.OnClicked = RefreshServers;
@@ -90,8 +90,7 @@ namespace Barotrauma
base.Select();
//RefreshServers(null, null);
//UpdateServerList();
RefreshServers(null, null);
}
private bool SelectServer(GUIComponent component, object obj)

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

Binary file not shown.