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:
@@ -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>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
77
Lidgren.Network/Encryption/NetCryptoProviderBase.cs
Normal file
77
Lidgren.Network/Encryption/NetCryptoProviderBase.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs
Normal file
59
Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
Lidgren.Network/Encryption/NetEncryption.cs
Normal file
45
Lidgren.Network/Encryption/NetEncryption.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
@@ -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
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
281
Lidgren.Network/NetRandom.Implementations.cs
Normal file
281
Lidgren.Network/NetRandom.Implementations.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
Lidgren.Network/NetRandomSeed.cs
Normal file
45
Lidgren.Network/NetRandomSeed.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for generating random seeds
|
||||
/// </summary>
|
||||
public static class NetRandomSeed
|
||||
{
|
||||
private static int m_seedIncrement = -1640531527;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a 32 bit random seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public static uint GetUInt32()
|
||||
{
|
||||
ulong seed = GetUInt64();
|
||||
uint low = (uint)seed;
|
||||
uint high = (uint)(seed >> 32);
|
||||
return low ^ high;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a 64 bit random seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetUInt64()
|
||||
{
|
||||
var guidBytes = Guid.NewGuid().ToByteArray();
|
||||
ulong seed =
|
||||
((ulong)guidBytes[0] << (8 * 0)) |
|
||||
((ulong)guidBytes[1] << (8 * 1)) |
|
||||
((ulong)guidBytes[2] << (8 * 2)) |
|
||||
((ulong)guidBytes[3] << (8 * 3)) |
|
||||
((ulong)guidBytes[4] << (8 * 4)) |
|
||||
((ulong)guidBytes[5] << (8 * 5)) |
|
||||
((ulong)guidBytes[6] << (8 * 6)) |
|
||||
((ulong)guidBytes[7] << (8 * 7));
|
||||
|
||||
return seed ^ NetUtility.GetPlatformSeed(m_seedIncrement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,9 @@ namespace Lidgren.Network
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Lidgren.Network/Platform/PlatformAndroid.cs
Normal file
88
Lidgren.Network/Platform/PlatformAndroid.cs
Normal 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
|
||||
86
Lidgren.Network/Platform/PlatformConstrained.cs
Normal file
86
Lidgren.Network/Platform/PlatformConstrained.cs
Normal 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
|
||||
156
Lidgren.Network/Platform/PlatformWin32.cs
Normal file
156
Lidgren.Network/Platform/PlatformWin32.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
#if !__ANDROID__ && !__CONSTRAINED__ && !WINDOWS_RUNTIME && !UNITY_STANDALONE_LINUX
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public static partial class NetUtility
|
||||
{
|
||||
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetPlatformSeed(int seedInc)
|
||||
{
|
||||
ulong seed = (ulong)System.Diagnostics.Stopwatch.GetTimestamp();
|
||||
return seed ^ ((ulong)Environment.WorkingSet + (ulong)seedInc);
|
||||
}
|
||||
|
||||
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
|
||||
|
||||
private static NetworkInterface GetNetworkInterface()
|
||||
{
|
||||
var computerProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
if (computerProperties == null)
|
||||
return null;
|
||||
|
||||
var nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
if (nics == null || nics.Length < 1)
|
||||
return null;
|
||||
|
||||
NetworkInterface best = null;
|
||||
foreach (NetworkInterface adapter in nics)
|
||||
{
|
||||
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback || adapter.NetworkInterfaceType == NetworkInterfaceType.Unknown)
|
||||
continue;
|
||||
if (!adapter.Supports(NetworkInterfaceComponent.IPv4))
|
||||
continue;
|
||||
if (best == null)
|
||||
best = adapter;
|
||||
if (adapter.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
|
||||
// make sure this adapter has any ipv4 addresses
|
||||
IPInterfaceProperties properties = adapter.GetIPProperties();
|
||||
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
// Yes it does, return this network interface.
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If available, returns the bytes of the physical (MAC) address for the first usable network interface
|
||||
/// </summary>
|
||||
public static byte[] GetMacAddressBytes()
|
||||
{
|
||||
var ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
return null;
|
||||
return ni.GetPhysicalAddress().GetAddressBytes();
|
||||
}
|
||||
|
||||
public static IPAddress GetBroadcastAddress()
|
||||
{
|
||||
var ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
return null;
|
||||
|
||||
var properties = ni.GetIPProperties();
|
||||
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
var mask = unicastAddress.IPv4Mask;
|
||||
byte[] ipAdressBytes = unicastAddress.Address.GetAddressBytes();
|
||||
byte[] subnetMaskBytes = mask.GetAddressBytes();
|
||||
|
||||
if (ipAdressBytes.Length != subnetMaskBytes.Length)
|
||||
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
|
||||
|
||||
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
|
||||
for (int i = 0; i < broadcastAddress.Length; i++)
|
||||
{
|
||||
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
|
||||
}
|
||||
return new IPAddress(broadcastAddress);
|
||||
}
|
||||
}
|
||||
return IPAddress.Broadcast;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets my local IPv4 address (not necessarily external) and subnet mask
|
||||
/// </summary>
|
||||
public static IPAddress GetMyAddress(out IPAddress mask)
|
||||
{
|
||||
var ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
{
|
||||
mask = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
IPInterfaceProperties properties = ni.GetIPProperties();
|
||||
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
mask = unicastAddress.IPv4Mask;
|
||||
return unicastAddress.Address;
|
||||
}
|
||||
}
|
||||
|
||||
mask = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void Sleep(int milliseconds)
|
||||
{
|
||||
System.Threading.Thread.Sleep(milliseconds);
|
||||
}
|
||||
|
||||
public static IPAddress CreateAddressFromBytes(byte[] bytes)
|
||||
{
|
||||
return new IPAddress(bytes);
|
||||
}
|
||||
|
||||
private static readonly SHA256 s_sha = SHA256.Create();
|
||||
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
|
||||
{
|
||||
return s_sha.ComputeHash(bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class NetTime
|
||||
{
|
||||
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
102
Lidgren.Network/Platform/PlatformWinRT.cs
Normal file
102
Lidgren.Network/Platform/PlatformWinRT.cs
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -61,7 +61,8 @@
|
||||
|
||||
<Item
|
||||
name="Railgun Shell"
|
||||
pickdistance="200">
|
||||
pickdistance="200"
|
||||
price="100">
|
||||
|
||||
<Sprite texture ="railgunshell.png" depth ="0.5"/>
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
163
Subsurface/Source/Networking/NetStats.cs
Normal file
163
Subsurface/Source/Networking/NetStats.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
Reference in New Issue
Block a user