Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
108
Server/Network/BufferPool.cs
Normal file
108
Server/Network/BufferPool.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class BufferPool
|
||||
{
|
||||
public static List<BufferPool> Pools { get; private set; }
|
||||
|
||||
static BufferPool()
|
||||
{
|
||||
Pools = new List<BufferPool>();
|
||||
}
|
||||
|
||||
private readonly string m_Name;
|
||||
|
||||
private readonly int m_InitialCapacity;
|
||||
private readonly int m_BufferSize;
|
||||
|
||||
private int m_Misses;
|
||||
|
||||
private readonly Queue<byte[]> m_FreeBuffers;
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
return m_FreeBuffers.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetInfo(
|
||||
out string name,
|
||||
out int freeCount,
|
||||
out int initialCapacity,
|
||||
out int currentCapacity,
|
||||
out int bufferSize,
|
||||
out int misses)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
name = m_Name;
|
||||
freeCount = m_FreeBuffers.Count;
|
||||
initialCapacity = m_InitialCapacity;
|
||||
currentCapacity = m_InitialCapacity * (1 + m_Misses);
|
||||
bufferSize = m_BufferSize;
|
||||
misses = m_Misses;
|
||||
}
|
||||
}
|
||||
|
||||
public BufferPool(string name, int initialCapacity, int bufferSize)
|
||||
{
|
||||
m_Name = name;
|
||||
|
||||
m_InitialCapacity = initialCapacity;
|
||||
m_BufferSize = bufferSize;
|
||||
|
||||
m_FreeBuffers = new Queue<byte[]>(initialCapacity);
|
||||
|
||||
for (int i = 0; i < initialCapacity; ++i)
|
||||
{
|
||||
m_FreeBuffers.Enqueue(new byte[bufferSize]);
|
||||
}
|
||||
|
||||
lock (Pools)
|
||||
Pools.Add(this);
|
||||
}
|
||||
|
||||
public byte[] AcquireBuffer()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (m_FreeBuffers.Count > 0)
|
||||
{
|
||||
return m_FreeBuffers.Dequeue();
|
||||
}
|
||||
|
||||
++m_Misses;
|
||||
|
||||
for (int i = 0; i < m_InitialCapacity; ++i)
|
||||
{
|
||||
m_FreeBuffers.Enqueue(new byte[m_BufferSize]);
|
||||
}
|
||||
|
||||
return m_FreeBuffers.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseBuffer(byte[] buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (this)
|
||||
m_FreeBuffers.Enqueue(buffer);
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
lock (Pools)
|
||||
Pools.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
147
Server/Network/ByteQueue.cs
Normal file
147
Server/Network/ByteQueue.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
#region References
|
||||
using System;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class ByteQueue
|
||||
{
|
||||
private int m_Head;
|
||||
private int m_Tail;
|
||||
private int m_Size;
|
||||
|
||||
private byte[] m_Buffer;
|
||||
|
||||
public int Length { get { return m_Size; } }
|
||||
|
||||
public ByteQueue()
|
||||
{
|
||||
m_Buffer = new byte[2048];
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Head = 0;
|
||||
m_Tail = 0;
|
||||
m_Size = 0;
|
||||
}
|
||||
|
||||
private void SetCapacity(int capacity)
|
||||
{
|
||||
var newBuffer = new byte[capacity];
|
||||
|
||||
if (m_Size > 0)
|
||||
{
|
||||
if (m_Head < m_Tail)
|
||||
{
|
||||
Buffer.BlockCopy(m_Buffer, m_Head, newBuffer, 0, m_Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(m_Buffer, m_Head, newBuffer, 0, m_Buffer.Length - m_Head);
|
||||
Buffer.BlockCopy(m_Buffer, 0, newBuffer, m_Buffer.Length - m_Head, m_Tail);
|
||||
}
|
||||
}
|
||||
|
||||
m_Head = 0;
|
||||
m_Tail = m_Size;
|
||||
m_Buffer = newBuffer;
|
||||
}
|
||||
|
||||
public byte GetPacketID()
|
||||
{
|
||||
if (m_Size >= 1)
|
||||
{
|
||||
return m_Buffer[m_Head];
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public int GetPacketLength()
|
||||
{
|
||||
if (m_Size >= 3)
|
||||
{
|
||||
return (m_Buffer[(m_Head + 1) % m_Buffer.Length] << 8) | m_Buffer[(m_Head + 2) % m_Buffer.Length];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int Dequeue(byte[] buffer, int offset, int size)
|
||||
{
|
||||
if (size > m_Size)
|
||||
{
|
||||
size = m_Size;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buffer != null)
|
||||
{
|
||||
if (m_Head < m_Tail)
|
||||
{
|
||||
Buffer.BlockCopy(m_Buffer, m_Head, buffer, offset, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
int rightLength = (m_Buffer.Length - m_Head);
|
||||
|
||||
if (rightLength >= size)
|
||||
{
|
||||
Buffer.BlockCopy(m_Buffer, m_Head, buffer, offset, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(m_Buffer, m_Head, buffer, offset, rightLength);
|
||||
Buffer.BlockCopy(m_Buffer, 0, buffer, offset + rightLength, size - rightLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Head = (m_Head + size) % m_Buffer.Length;
|
||||
m_Size -= size;
|
||||
|
||||
if (m_Size == 0)
|
||||
{
|
||||
m_Head = 0;
|
||||
m_Tail = 0;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public void Enqueue(byte[] buffer, int offset, int size)
|
||||
{
|
||||
if ((m_Size + size) > m_Buffer.Length)
|
||||
{
|
||||
SetCapacity((m_Size + size + 2047) & ~2047);
|
||||
}
|
||||
|
||||
if (m_Head < m_Tail)
|
||||
{
|
||||
int rightLength = (m_Buffer.Length - m_Tail);
|
||||
|
||||
if (rightLength >= size)
|
||||
{
|
||||
Buffer.BlockCopy(buffer, offset, m_Buffer, m_Tail, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(buffer, offset, m_Buffer, m_Tail, rightLength);
|
||||
Buffer.BlockCopy(buffer, offset + rightLength, m_Buffer, 0, size - rightLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(buffer, offset, m_Buffer, m_Tail, size);
|
||||
}
|
||||
|
||||
m_Tail = (m_Tail + size) % m_Buffer.Length;
|
||||
m_Size += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
394
Server/Network/Compression.cs
Normal file
394
Server/Network/Compression.cs
Normal file
@@ -0,0 +1,394 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles outgoing packet compression for the network.
|
||||
/// </summary>
|
||||
public static class Compression
|
||||
{
|
||||
private static readonly int[] _huffmanTable = new int[514]
|
||||
{
|
||||
0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032, 0x8, 0x0E0, 0x8,
|
||||
0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057, 0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC,
|
||||
0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D, 0x9, 0x191, 0x9, 0x1CE, 0x7, 0x03F, 0x9, 0x090, 0x8,
|
||||
0x059, 0x8, 0x07B, 0x8, 0x091, 0x8, 0x0C6, 0x6, 0x02D, 0x9, 0x186, 0x8, 0x06F, 0x9, 0x093, 0xA, 0x1CC, 0x8, 0x05A,
|
||||
0xA, 0x1AE, 0xA, 0x1C0, 0x9, 0x148, 0x9, 0x14A, 0x9, 0x082, 0xA, 0x19F, 0x9, 0x171, 0x9, 0x120, 0x9, 0x0E7, 0xA,
|
||||
0x1F3, 0x9, 0x14B, 0x9, 0x100, 0x9, 0x190, 0x6, 0x013, 0x9, 0x161, 0x9, 0x125, 0x9, 0x133, 0x9, 0x195, 0x9, 0x173,
|
||||
0x9, 0x1CA, 0x9, 0x086, 0x9, 0x1E9, 0x9, 0x0DB, 0x9, 0x1EC, 0x9, 0x08B, 0x9, 0x085, 0x5, 0x00A, 0x8, 0x096, 0x8,
|
||||
0x09C, 0x9, 0x1C3, 0x9, 0x19C, 0x9, 0x08F, 0x9, 0x18F, 0x9, 0x091, 0x9, 0x087, 0x9, 0x0C6, 0x9, 0x177, 0x9, 0x089,
|
||||
0x9, 0x0D6, 0x9, 0x08C, 0x9, 0x1EE, 0x9, 0x1EB, 0x9, 0x084, 0x9, 0x164, 0x9, 0x175, 0x9, 0x1CD, 0x8, 0x05E, 0x9,
|
||||
0x088, 0x9, 0x12B, 0x9, 0x172, 0x9, 0x10A, 0x9, 0x08D, 0x9, 0x13A, 0x9, 0x11C, 0xA, 0x1E1, 0xA, 0x1E0, 0x9, 0x187,
|
||||
0xA, 0x1DC, 0xA, 0x1DF, 0x7, 0x074, 0x9, 0x19F, 0x8, 0x08D, 0x8, 0x0E4, 0x7, 0x079, 0x9, 0x0EA, 0x9, 0x0E1, 0x8,
|
||||
0x040, 0x7, 0x041, 0x9, 0x10B, 0x9, 0x0B0, 0x8, 0x06A, 0x8, 0x0C1, 0x7, 0x071, 0x7, 0x078, 0x8, 0x0B1, 0x9, 0x14C,
|
||||
0x7, 0x043, 0x8, 0x076, 0x7, 0x066, 0x7, 0x04D, 0x9, 0x08A, 0x6, 0x02F, 0x8, 0x0C9, 0x9, 0x0CE, 0x9, 0x149, 0x9,
|
||||
0x160, 0xA, 0x1BA, 0xA, 0x19E, 0xA, 0x39F, 0x9, 0x0E5, 0x9, 0x194, 0x9, 0x184, 0x9, 0x126, 0x7, 0x030, 0x8, 0x06C,
|
||||
0x9, 0x121, 0x9, 0x1E8, 0xA, 0x1C1, 0xA, 0x11D, 0xA, 0x163, 0xA, 0x385, 0xA, 0x3DB, 0xA, 0x17D, 0xA, 0x106, 0xA,
|
||||
0x397, 0xA, 0x24E, 0x7, 0x02E, 0x8, 0x098, 0xA, 0x33C, 0xA, 0x32E, 0xA, 0x1E9, 0x9, 0x0BF, 0xA, 0x3DF, 0xA, 0x1DD,
|
||||
0xA, 0x32D, 0xA, 0x2ED, 0xA, 0x30B, 0xA, 0x107, 0xA, 0x2E8, 0xA, 0x3DE, 0xA, 0x125, 0xA, 0x1E8, 0x9, 0x0E9, 0xA,
|
||||
0x1CD, 0xA, 0x1B5, 0x9, 0x165, 0xA, 0x232, 0xA, 0x2E1, 0xB, 0x3AE, 0xB, 0x3C6, 0xB, 0x3E2, 0xA, 0x205, 0xA, 0x29A,
|
||||
0xA, 0x248, 0xA, 0x2CD, 0xA, 0x23B, 0xB, 0x3C5, 0xA, 0x251, 0xA, 0x2E9, 0xA, 0x252, 0x9, 0x1EA, 0xB, 0x3A0, 0xB,
|
||||
0x391, 0xA, 0x23C, 0xB, 0x392, 0xB, 0x3D5, 0xA, 0x233, 0xA, 0x2CC, 0xB, 0x390, 0xA, 0x1BB, 0xB, 0x3A1, 0xB, 0x3C4,
|
||||
0xA, 0x211, 0xA, 0x203, 0x9, 0x12A, 0xA, 0x231, 0xB, 0x3E0, 0xA, 0x29B, 0xB, 0x3D7, 0xA, 0x202, 0xB, 0x3AD, 0xA,
|
||||
0x213, 0xA, 0x253, 0xA, 0x32C, 0xA, 0x23D, 0xA, 0x23F, 0xA, 0x32F, 0xA, 0x11C, 0xA, 0x384, 0xA, 0x31C, 0xA, 0x17C,
|
||||
0xA, 0x30A, 0xA, 0x2E0, 0xA, 0x276, 0xA, 0x250, 0xB, 0x3E3, 0xA, 0x396, 0xA, 0x18F, 0xA, 0x204, 0xA, 0x206, 0xA,
|
||||
0x230, 0xA, 0x265, 0xA, 0x212, 0xA, 0x23E, 0xB, 0x3AC, 0xB, 0x393, 0xB, 0x3E1, 0xA, 0x1DE, 0xB, 0x3D6, 0xA, 0x31D,
|
||||
0xB, 0x3E5, 0xB, 0x3E4, 0xA, 0x207, 0xB, 0x3C7, 0xA, 0x277, 0xB, 0x3D4, 0x8, 0x0C0, 0xA, 0x162, 0xA, 0x3DA, 0xA,
|
||||
0x124, 0xA, 0x1B4, 0xA, 0x264, 0xA, 0x33D, 0xA, 0x1D1, 0xA, 0x1AF, 0xA, 0x39E, 0xA, 0x24F, 0xB, 0x373, 0xA, 0x249,
|
||||
0xB, 0x372, 0x9, 0x167, 0xA, 0x210, 0xA, 0x23A, 0xA, 0x1B8, 0xB, 0x3AF, 0xA, 0x18E, 0xA, 0x2EC, 0x7, 0x062, 0x4,
|
||||
0x00D
|
||||
};
|
||||
|
||||
private const int CountIndex = 0;
|
||||
private const int ValueIndex = 1;
|
||||
|
||||
// UO packets may not exceed 64kb in length
|
||||
private const int BufferSize = 0x10000;
|
||||
|
||||
// Optimal compression ratio is 2 / 8; worst compression ratio is 11 / 8
|
||||
private const int MinimalCodeLength = 2;
|
||||
private const int MaximalCodeLength = 11;
|
||||
|
||||
// Fixed overhead, in bits, per compression call
|
||||
private const int TerminalCodeLength = 4;
|
||||
|
||||
// If our input exceeds this length, we cannot possibly compress it within the buffer
|
||||
private const int DefiniteOverflow = ((BufferSize * 8) - TerminalCodeLength) / MinimalCodeLength;
|
||||
|
||||
// If our input exceeds this length, we may potentially overflow the buffer
|
||||
private const int PossibleOverflow = ((BufferSize * 8) - TerminalCodeLength) / MaximalCodeLength;
|
||||
|
||||
public static unsafe void Compress(byte[] input, int offset, int count, byte[] output, ref int length)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
else if (offset < 0 || offset >= input.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
}
|
||||
else if (count < 0 || count > input.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
}
|
||||
else if ((input.Length - offset) < count)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
length = 0;
|
||||
|
||||
if (count > DefiniteOverflow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int bitCount = 0;
|
||||
int bitValue = 0;
|
||||
|
||||
fixed (int* pTable = _huffmanTable)
|
||||
{
|
||||
int* pEntry;
|
||||
|
||||
fixed (byte* pInputBuffer = input)
|
||||
{
|
||||
byte* pInput = pInputBuffer + offset, pInputEnd = pInput + count;
|
||||
|
||||
fixed (byte* pOutputBuffer = output)
|
||||
{
|
||||
byte* pOutput = pOutputBuffer, pOutputEnd = pOutput + BufferSize;
|
||||
|
||||
while (pInput < pInputEnd)
|
||||
{
|
||||
pEntry = &pTable[*pInput++ << 1];
|
||||
|
||||
bitCount += pEntry[CountIndex];
|
||||
|
||||
bitValue <<= pEntry[CountIndex];
|
||||
bitValue |= pEntry[ValueIndex];
|
||||
|
||||
while (bitCount >= 8)
|
||||
{
|
||||
bitCount -= 8;
|
||||
|
||||
if (pOutput < pOutputEnd)
|
||||
{
|
||||
*pOutput++ = (byte)(bitValue >> bitCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// terminal code
|
||||
pEntry = &pTable[0x200];
|
||||
|
||||
bitCount += pEntry[CountIndex];
|
||||
|
||||
bitValue <<= pEntry[CountIndex];
|
||||
bitValue |= pEntry[ValueIndex];
|
||||
|
||||
// align on byte boundary
|
||||
if ((bitCount & 7) != 0)
|
||||
{
|
||||
bitValue <<= (8 - (bitCount & 7));
|
||||
bitCount += (8 - (bitCount & 7));
|
||||
}
|
||||
|
||||
while (bitCount >= 8)
|
||||
{
|
||||
bitCount -= 8;
|
||||
|
||||
if (pOutput < pOutputEnd)
|
||||
{
|
||||
*pOutput++ = (byte)(bitValue >> bitCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
length = (int)(pOutput - pOutputBuffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly ICompressor Compressor;
|
||||
|
||||
static Compression()
|
||||
{
|
||||
if (Core.Unix)
|
||||
{
|
||||
if (Core.Is64Bit)
|
||||
{
|
||||
Compressor = new CompressorUnix64();
|
||||
}
|
||||
else
|
||||
{
|
||||
Compressor = new CompressorUnix32();
|
||||
}
|
||||
}
|
||||
else if (Core.Is64Bit)
|
||||
{
|
||||
Compressor = new Compressor64();
|
||||
}
|
||||
else
|
||||
{
|
||||
Compressor = new Compressor32();
|
||||
}
|
||||
}
|
||||
|
||||
public static ZLibError Pack(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return Compressor.Compress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
|
||||
public static ZLibError Pack(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
|
||||
{
|
||||
return Compressor.Compress(dest, ref destLength, source, sourceLength, quality);
|
||||
}
|
||||
|
||||
public static ZLibError Unpack(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return Compressor.Decompress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICompressor
|
||||
{
|
||||
string Version { get; }
|
||||
|
||||
ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
|
||||
ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality);
|
||||
|
||||
ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
|
||||
}
|
||||
|
||||
public sealed class Compressor32 : ICompressor
|
||||
{
|
||||
internal class SafeNativeMethods
|
||||
{
|
||||
[DllImport("zlibwapi32")]
|
||||
internal static extern string zlibVersion();
|
||||
|
||||
[DllImport("zlibwapi32")]
|
||||
internal static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
|
||||
|
||||
[DllImport("zlibwapi32")]
|
||||
internal static extern ZLibError compress2(
|
||||
byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality);
|
||||
|
||||
[DllImport("zlibwapi32")]
|
||||
internal static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
|
||||
}
|
||||
|
||||
public string Version { get { return SafeNativeMethods.zlibVersion(); } }
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
|
||||
{
|
||||
return SafeNativeMethods.compress2(dest, ref destLength, source, sourceLength, quality);
|
||||
}
|
||||
|
||||
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Compressor64 : ICompressor
|
||||
{
|
||||
internal class SafeNativeMethods
|
||||
{
|
||||
[DllImport("zlibwapi64")]
|
||||
internal static extern string zlibVersion();
|
||||
|
||||
[DllImport("zlibwapi64")]
|
||||
internal static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
|
||||
|
||||
[DllImport("zlibwapi64")]
|
||||
internal static extern ZLibError compress2(
|
||||
byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality);
|
||||
|
||||
[DllImport("zlibwapi64")]
|
||||
internal static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
|
||||
}
|
||||
|
||||
public string Version { get { return SafeNativeMethods.zlibVersion(); } }
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
|
||||
{
|
||||
return SafeNativeMethods.compress2(dest, ref destLength, source, sourceLength, quality);
|
||||
}
|
||||
|
||||
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CompressorUnix32 : ICompressor
|
||||
{
|
||||
internal class SafeNativeMethods
|
||||
{
|
||||
[DllImport("libz")]
|
||||
internal static extern string zlibVersion();
|
||||
|
||||
[DllImport("libz")]
|
||||
internal static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
|
||||
|
||||
[DllImport("libz")]
|
||||
internal static extern ZLibError compress2(
|
||||
byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality);
|
||||
|
||||
[DllImport("libz")]
|
||||
internal static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
|
||||
}
|
||||
|
||||
public string Version { get { return SafeNativeMethods.zlibVersion(); } }
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
|
||||
{
|
||||
return SafeNativeMethods.compress2(dest, ref destLength, source, sourceLength, quality);
|
||||
}
|
||||
|
||||
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CompressorUnix64 : ICompressor
|
||||
{
|
||||
internal class SafeNativeMethods
|
||||
{
|
||||
[DllImport("libz")]
|
||||
internal static extern string zlibVersion();
|
||||
|
||||
[DllImport("libz")]
|
||||
internal static extern ZLibError compress(byte[] dest, ref long destLength, byte[] source, long sourceLength);
|
||||
|
||||
[DllImport("libz")]
|
||||
internal static extern ZLibError compress2(byte[] dest, ref long destLength, byte[] source, long sourceLength, ZLibQuality quality);
|
||||
|
||||
[DllImport("libz")]
|
||||
internal static extern ZLibError uncompress(byte[] dest, ref long destLen, byte[] source, long sourceLen);
|
||||
}
|
||||
|
||||
public string Version { get { return SafeNativeMethods.zlibVersion(); } }
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
long destLengthLong = destLength;
|
||||
ZLibError z = SafeNativeMethods.compress(dest, ref destLengthLong, source, sourceLength);
|
||||
destLength = (int)destLengthLong;
|
||||
return z;
|
||||
}
|
||||
|
||||
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
|
||||
{
|
||||
long destLengthLong = destLength;
|
||||
ZLibError z = SafeNativeMethods.compress2(dest, ref destLengthLong, source, sourceLength, quality);
|
||||
destLength = (int)destLengthLong;
|
||||
return z;
|
||||
}
|
||||
|
||||
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
|
||||
{
|
||||
long destLengthLong = destLength;
|
||||
ZLibError z = SafeNativeMethods.uncompress(dest, ref destLengthLong, source, sourceLength);
|
||||
destLength = (int)destLengthLong;
|
||||
return z;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ZLibError
|
||||
{
|
||||
VersionError = -6,
|
||||
BufferError = -5,
|
||||
MemoryError = -4,
|
||||
DataError = -3,
|
||||
StreamError = -2,
|
||||
FileError = -1,
|
||||
|
||||
Okay = 0,
|
||||
|
||||
StreamEnd = 1,
|
||||
NeedDictionary = 2
|
||||
}
|
||||
|
||||
public enum ZLibQuality
|
||||
{
|
||||
Default = -1,
|
||||
|
||||
None = 0,
|
||||
|
||||
Speed = 1,
|
||||
Size = 9
|
||||
}
|
||||
}
|
||||
24
Server/Network/EncodedPacketHandler.cs
Normal file
24
Server/Network/EncodedPacketHandler.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Server.Network
|
||||
{
|
||||
public delegate void OnEncodedPacketReceive(NetState state, IEntity ent, EncodedReader pvSrc);
|
||||
|
||||
public class EncodedPacketHandler
|
||||
{
|
||||
private readonly int m_PacketID;
|
||||
private readonly bool m_Ingame;
|
||||
private readonly OnEncodedPacketReceive m_OnReceive;
|
||||
|
||||
public EncodedPacketHandler(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
|
||||
{
|
||||
m_PacketID = packetID;
|
||||
m_Ingame = ingame;
|
||||
m_OnReceive = onReceive;
|
||||
}
|
||||
|
||||
public int PacketID { get { return m_PacketID; } }
|
||||
|
||||
public OnEncodedPacketReceive OnReceive { get { return m_OnReceive; } }
|
||||
|
||||
public bool Ingame { get { return m_Ingame; } }
|
||||
}
|
||||
}
|
||||
63
Server/Network/EncodedReader.cs
Normal file
63
Server/Network/EncodedReader.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace Server.Network
|
||||
{
|
||||
public class EncodedReader
|
||||
{
|
||||
private readonly PacketReader m_Reader;
|
||||
|
||||
public EncodedReader(PacketReader reader)
|
||||
{
|
||||
m_Reader = reader;
|
||||
}
|
||||
|
||||
public byte[] Buffer { get { return m_Reader.Buffer; } }
|
||||
|
||||
public void Trace(NetState state)
|
||||
{
|
||||
m_Reader.Trace(state);
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
if (m_Reader.ReadByte() != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_Reader.ReadInt32();
|
||||
}
|
||||
|
||||
public Point3D ReadPoint3D()
|
||||
{
|
||||
if (m_Reader.ReadByte() != 3)
|
||||
{
|
||||
return Point3D.Zero;
|
||||
}
|
||||
|
||||
return new Point3D(m_Reader.ReadInt16(), m_Reader.ReadInt16(), m_Reader.ReadByte());
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringSafe()
|
||||
{
|
||||
if (m_Reader.ReadByte() != 2)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int length = m_Reader.ReadUInt16();
|
||||
|
||||
return m_Reader.ReadUnicodeStringSafe(length);
|
||||
}
|
||||
|
||||
public string ReadUnicodeString()
|
||||
{
|
||||
if (m_Reader.ReadByte() != 2)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int length = m_Reader.ReadUInt16();
|
||||
|
||||
return m_Reader.ReadUnicodeString(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
280
Server/Network/Listener.cs
Normal file
280
Server/Network/Listener.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class Listener : IDisposable
|
||||
{
|
||||
private Socket m_Listener;
|
||||
private PingListener _PingListener;
|
||||
|
||||
private readonly Queue<Socket> m_Accepted;
|
||||
private readonly object m_AcceptedSyncRoot;
|
||||
|
||||
private readonly AsyncCallback m_OnAccept;
|
||||
|
||||
private static readonly Socket[] m_EmptySockets = new Socket[0];
|
||||
|
||||
public static IPEndPoint[] EndPoints { get; set; }
|
||||
|
||||
public Listener(IPEndPoint ipep)
|
||||
{
|
||||
m_Accepted = new Queue<Socket>();
|
||||
m_AcceptedSyncRoot = ((ICollection)m_Accepted).SyncRoot;
|
||||
|
||||
m_Listener = Bind(ipep);
|
||||
|
||||
if (m_Listener == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayListener();
|
||||
_PingListener = new PingListener(ipep);
|
||||
|
||||
m_OnAccept = OnAccept;
|
||||
try
|
||||
{
|
||||
IAsyncResult res = m_Listener.BeginAccept(m_OnAccept, m_Listener);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetState.TraceException(ex);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{ }
|
||||
}
|
||||
|
||||
private Socket Bind(IPEndPoint ipep)
|
||||
{
|
||||
Socket s = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
try
|
||||
{
|
||||
s.LingerState.Enabled = false;
|
||||
|
||||
// Default is 'false' starting Windows Vista and Server 2008. Source: https://msdn.microsoft.com/en-us/library/system.net.sockets.socket.exclusiveaddressuse(v=vs.110).aspx?f=255&MSPPError=-2147217396
|
||||
s.ExclusiveAddressUse = false;
|
||||
|
||||
s.Bind(ipep);
|
||||
s.Listen(8);
|
||||
|
||||
return s;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is SocketException)
|
||||
{
|
||||
SocketException se = (SocketException)e;
|
||||
|
||||
if (se.ErrorCode == 10048)
|
||||
{
|
||||
// WSAEADDRINUSE
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Listener Failed: {0}:{1} (In Use)", ipep.Address, ipep.Port);
|
||||
Utility.PopColor();
|
||||
}
|
||||
else if (se.ErrorCode == 10049)
|
||||
{
|
||||
// WSAEADDRNOTAVAIL
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Listener Failed: {0}:{1} (Unavailable)", ipep.Address, ipep.Port);
|
||||
Utility.PopColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Listener Exception:");
|
||||
Console.WriteLine(e);
|
||||
Utility.PopColor();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayListener()
|
||||
{
|
||||
IPEndPoint ipep = m_Listener.LocalEndPoint as IPEndPoint;
|
||||
|
||||
if (ipep == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any))
|
||||
{
|
||||
var adapters = NetworkInterface.GetAllNetworkInterfaces();
|
||||
foreach (NetworkInterface adapter in adapters)
|
||||
{
|
||||
IPInterfaceProperties properties = adapter.GetIPProperties();
|
||||
foreach (IPAddressInformation unicast in properties.UnicastAddresses)
|
||||
{
|
||||
if (ipep.AddressFamily == unicast.Address.AddressFamily)
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Green);
|
||||
Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port);
|
||||
Utility.PopColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
try {
|
||||
Console.WriteLine( "Listening: {0}:{1}", IPAddress.Loopback, ipep.Port );
|
||||
IPHostEntry iphe = Dns.GetHostEntry( Dns.GetHostName() );
|
||||
IPAddress[] ip = iphe.AddressList;
|
||||
for ( int i = 0; i < ip.Length; ++i )
|
||||
Console.WriteLine( "Listening: {0}:{1}", ip[i], ipep.Port );
|
||||
}
|
||||
catch { }
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Green);
|
||||
Console.WriteLine("Listening: {0}:{1}", ipep.Address, ipep.Port);
|
||||
Utility.PopColor();
|
||||
}
|
||||
|
||||
Utility.PushColor(ConsoleColor.DarkGreen);
|
||||
Console.WriteLine(@"----------------------------------------------------------------------");
|
||||
Utility.PopColor();
|
||||
}
|
||||
|
||||
private void OnAccept(IAsyncResult asyncResult)
|
||||
{
|
||||
Socket listener = (Socket)asyncResult.AsyncState;
|
||||
|
||||
Socket accepted = null;
|
||||
|
||||
try
|
||||
{
|
||||
accepted = listener.EndAccept(asyncResult);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetState.TraceException(ex);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (accepted != null)
|
||||
{
|
||||
if (VerifySocket(accepted))
|
||||
{
|
||||
Enqueue(accepted);
|
||||
}
|
||||
else
|
||||
{
|
||||
Release(accepted);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
listener.BeginAccept(m_OnAccept, listener);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetState.TraceException(ex);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{ }
|
||||
}
|
||||
|
||||
private bool VerifySocket(Socket socket)
|
||||
{
|
||||
try
|
||||
{
|
||||
SocketConnectEventArgs args = new SocketConnectEventArgs(socket);
|
||||
|
||||
EventSink.InvokeSocketConnect(args);
|
||||
|
||||
return args.AllowConnection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetState.TraceException(ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Enqueue(Socket socket)
|
||||
{
|
||||
lock (m_AcceptedSyncRoot)
|
||||
{
|
||||
m_Accepted.Enqueue(socket);
|
||||
}
|
||||
|
||||
Core.Set();
|
||||
}
|
||||
|
||||
private void Release(Socket socket)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetState.TraceException(ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetState.TraceException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Socket[] Slice()
|
||||
{
|
||||
Socket[] array;
|
||||
|
||||
lock (m_AcceptedSyncRoot)
|
||||
{
|
||||
if (m_Accepted.Count == 0)
|
||||
{
|
||||
return m_EmptySockets;
|
||||
}
|
||||
|
||||
array = m_Accepted.ToArray();
|
||||
m_Accepted.Clear();
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Socket socket = Interlocked.Exchange(ref m_Listener, null);
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
|
||||
if (_PingListener == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_PingListener.Dispose();
|
||||
_PingListener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
366
Server/Network/MessagePump.cs
Normal file
366
Server/Network/MessagePump.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
using Server.Diagnostics;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class MessagePump
|
||||
{
|
||||
private Queue<NetState> m_Queue;
|
||||
private Queue<NetState> m_WorkingQueue;
|
||||
private readonly Queue<NetState> m_Throttled;
|
||||
|
||||
public Listener[] Listeners { get; set; }
|
||||
|
||||
public MessagePump()
|
||||
{
|
||||
var ipep = Listener.EndPoints;
|
||||
|
||||
Listeners = new Listener[ipep.Length];
|
||||
|
||||
bool success = false;
|
||||
|
||||
do
|
||||
{
|
||||
for (int i = 0; i < ipep.Length; i++)
|
||||
{
|
||||
Listeners[i] = new Listener(ipep[i]);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Yellow);
|
||||
Console.WriteLine("Retrying...");
|
||||
Utility.PopColor();
|
||||
|
||||
Thread.Sleep(10000);
|
||||
}
|
||||
}
|
||||
while (!success);
|
||||
|
||||
m_Queue = new Queue<NetState>();
|
||||
m_WorkingQueue = new Queue<NetState>();
|
||||
m_Throttled = new Queue<NetState>();
|
||||
}
|
||||
|
||||
public void AddListener(Listener l)
|
||||
{
|
||||
var old = Listeners;
|
||||
|
||||
Listeners = new Listener[old.Length + 1];
|
||||
|
||||
for (int i = 0; i < old.Length; ++i)
|
||||
{
|
||||
Listeners[i] = old[i];
|
||||
}
|
||||
|
||||
Listeners[old.Length] = l;
|
||||
}
|
||||
|
||||
private void CheckListener()
|
||||
{
|
||||
foreach (Listener l in Listeners)
|
||||
{
|
||||
var accepted = l.Slice();
|
||||
|
||||
foreach (Socket s in accepted)
|
||||
{
|
||||
NetState ns = new NetState(s, this);
|
||||
|
||||
ns.Start();
|
||||
|
||||
if (ns.Running && Display(ns))
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Green);
|
||||
Console.WriteLine("Client: {0}: Connected. [{1} Online]", ns, NetState.Instances.Count);
|
||||
Utility.PopColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Display(NetState ns)
|
||||
{
|
||||
if (ns == null)
|
||||
return false;
|
||||
|
||||
string state = ns.ToString();
|
||||
|
||||
foreach (var str in _NoDisplay)
|
||||
{
|
||||
if (str == state)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string[] _NoDisplay =
|
||||
{
|
||||
"192.99.10.155",
|
||||
"192.99.69.21",
|
||||
};
|
||||
|
||||
public void OnReceive(NetState ns)
|
||||
{
|
||||
lock (this)
|
||||
m_Queue.Enqueue(ns);
|
||||
|
||||
Core.Set();
|
||||
}
|
||||
|
||||
public void Slice()
|
||||
{
|
||||
CheckListener();
|
||||
|
||||
lock (this)
|
||||
{
|
||||
var temp = m_WorkingQueue;
|
||||
m_WorkingQueue = m_Queue;
|
||||
m_Queue = temp;
|
||||
}
|
||||
|
||||
while (m_WorkingQueue.Count > 0)
|
||||
{
|
||||
NetState ns = m_WorkingQueue.Dequeue();
|
||||
|
||||
if (ns.Running)
|
||||
{
|
||||
HandleReceive(ns);
|
||||
}
|
||||
}
|
||||
|
||||
lock (this)
|
||||
{
|
||||
while (m_Throttled.Count > 0)
|
||||
{
|
||||
m_Queue.Enqueue(m_Throttled.Dequeue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const int BufferSize = 4096;
|
||||
private readonly BufferPool m_Buffers = new BufferPool("Processor", 4, BufferSize);
|
||||
|
||||
public static bool HandleSeed(NetState ns, ByteQueue buffer)
|
||||
{
|
||||
if (buffer.GetPacketID() == 0xEF)
|
||||
{
|
||||
// new packet in client 6.0.5.0 replaces the traditional seed method with a seed packet
|
||||
// 0xEF = 239 = multicast IP, so this should never appear in a normal seed. So this is backwards compatible with older clients.
|
||||
ns.Seeded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.Length >= 4)
|
||||
{
|
||||
var m_Peek = new byte[4];
|
||||
|
||||
buffer.Dequeue(m_Peek, 0, 4);
|
||||
|
||||
uint seed = (uint)((m_Peek[0] << 24) | (m_Peek[1] << 16) | (m_Peek[2] << 8) | m_Peek[3]);
|
||||
|
||||
if (seed == 0)
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Login: {0}: Invalid Client", ns);
|
||||
Utility.PopColor();
|
||||
|
||||
ns.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ns.Seed = seed;
|
||||
ns.Seeded = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool CheckEncrypted(NetState ns, int packetID)
|
||||
{
|
||||
if (!ns.SentFirstPacket && packetID != 0xF0 && packetID != 0xF1 && packetID != 0xCF && packetID != 0x80 &&
|
||||
packetID != 0x91 && packetID != 0xA4 && packetID != 0xEF && packetID != 0xE4 && packetID != 0xFF)
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Client: {0}: Encrypted Client Unsupported", ns);
|
||||
Utility.PopColor();
|
||||
|
||||
ns.Dispose();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void HandleReceive(NetState ns)
|
||||
{
|
||||
ByteQueue buffer = ns.Buffer;
|
||||
|
||||
if (buffer == null || buffer.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (buffer)
|
||||
{
|
||||
if (!ns.Seeded && !HandleSeed(ns, buffer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int length = buffer.Length;
|
||||
|
||||
while (length > 0 && ns.Running)
|
||||
{
|
||||
int packetID = buffer.GetPacketID();
|
||||
|
||||
if (CheckEncrypted(ns, packetID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PacketHandler handler = ns.GetHandler(packetID);
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
#if DEBUG
|
||||
var data = new byte[length];
|
||||
length = buffer.Dequeue(data, 0, length);
|
||||
new PacketReader(data, length, false).Trace(ns);
|
||||
#else
|
||||
buffer.Dequeue(null, 0, length);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
int packetLength = handler.Length;
|
||||
|
||||
if (packetLength <= 0)
|
||||
{
|
||||
if (length >= 3)
|
||||
{
|
||||
packetLength = buffer.GetPacketLength();
|
||||
|
||||
if (packetLength < 3)
|
||||
{
|
||||
ns.Dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (length < packetLength)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler.Ingame)
|
||||
{
|
||||
if (ns.Mobile == null)
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Client: {0}: Packet (0x{1:X2}) Requires State Mobile", ns, packetID);
|
||||
Utility.PopColor();
|
||||
|
||||
ns.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ns.Mobile.Deleted)
|
||||
{
|
||||
Utility.PushColor(ConsoleColor.Red);
|
||||
Console.WriteLine("Client: {0}: Packet (0x{1:X2}) Ivalid State Mobile", ns, packetID);
|
||||
Utility.PopColor();
|
||||
|
||||
ns.Dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ThrottlePacketCallback throttler = handler.ThrottleCallback;
|
||||
|
||||
if (throttler != null)
|
||||
{
|
||||
bool drop;
|
||||
|
||||
if (!throttler((byte)packetID, ns, out drop))
|
||||
{
|
||||
if (!drop)
|
||||
{
|
||||
m_Throttled.Enqueue(ns);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.Dequeue(null, 0, packetLength);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PacketReceiveProfile prof = null;
|
||||
|
||||
if (Core.Profiling)
|
||||
{
|
||||
prof = PacketReceiveProfile.Acquire(packetID);
|
||||
}
|
||||
|
||||
if (prof != null)
|
||||
{
|
||||
prof.Start();
|
||||
}
|
||||
|
||||
byte[] packetBuffer;
|
||||
|
||||
if (BufferSize >= packetLength)
|
||||
{
|
||||
packetBuffer = m_Buffers.AcquireBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
packetBuffer = new byte[packetLength];
|
||||
}
|
||||
|
||||
packetLength = buffer.Dequeue(packetBuffer, 0, packetLength);
|
||||
|
||||
if (packetBuffer != null && packetBuffer.Length > 0 && packetLength > 0)
|
||||
{
|
||||
PacketReader r = new PacketReader(packetBuffer, packetLength, handler.Length != 0);
|
||||
|
||||
handler.OnReceive(ns, r);
|
||||
|
||||
ns.SetPacketTime((byte)packetID);
|
||||
|
||||
if (BufferSize >= packetLength)
|
||||
{
|
||||
m_Buffers.ReleaseBuffer(packetBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (prof != null)
|
||||
{
|
||||
prof.Finish(packetLength);
|
||||
}
|
||||
|
||||
length = buffer.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1335
Server/Network/NetState.cs
Normal file
1335
Server/Network/NetState.cs
Normal file
File diff suppressed because it is too large
Load Diff
32
Server/Network/PacketHandler.cs
Normal file
32
Server/Network/PacketHandler.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Server.Network
|
||||
{
|
||||
public delegate void OnPacketReceive(NetState state, PacketReader pvSrc);
|
||||
|
||||
public delegate bool ThrottlePacketCallback(byte packetID, NetState state, out bool drop);
|
||||
|
||||
public class PacketHandler
|
||||
{
|
||||
private readonly int m_PacketID;
|
||||
private readonly int m_Length;
|
||||
private readonly bool m_Ingame;
|
||||
private readonly OnPacketReceive m_OnReceive;
|
||||
|
||||
public PacketHandler(int packetID, int length, bool ingame, OnPacketReceive onReceive)
|
||||
{
|
||||
m_PacketID = packetID;
|
||||
m_Length = length;
|
||||
m_Ingame = ingame;
|
||||
m_OnReceive = onReceive;
|
||||
}
|
||||
|
||||
public int PacketID { get { return m_PacketID; } }
|
||||
|
||||
public int Length { get { return m_Length; } }
|
||||
|
||||
public OnPacketReceive OnReceive { get { return m_OnReceive; } }
|
||||
|
||||
public ThrottlePacketCallback ThrottleCallback { get; set; }
|
||||
|
||||
public bool Ingame { get { return m_Ingame; } }
|
||||
}
|
||||
}
|
||||
3367
Server/Network/PacketHandlers.cs
Normal file
3367
Server/Network/PacketHandlers.cs
Normal file
File diff suppressed because it is too large
Load Diff
511
Server/Network/PacketReader.cs
Normal file
511
Server/Network/PacketReader.cs
Normal file
@@ -0,0 +1,511 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class PacketReader
|
||||
{
|
||||
private readonly byte[] m_Data;
|
||||
private readonly int m_Size;
|
||||
private int m_Index;
|
||||
|
||||
public PacketReader(byte[] data, int size, bool fixedSize)
|
||||
{
|
||||
m_Data = data;
|
||||
m_Size = size;
|
||||
m_Index = fixedSize ? 1 : 3;
|
||||
}
|
||||
|
||||
public byte[] Buffer { get { return m_Data; } }
|
||||
|
||||
public int Size { get { return m_Size; } }
|
||||
|
||||
public void Trace(NetState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter("Packets.log", true))
|
||||
{
|
||||
var buffer = m_Data;
|
||||
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
sw.WriteLine("Client: {0}: Unhandled packet 0x{1:X2}", state, buffer[0]);
|
||||
}
|
||||
|
||||
using (MemoryStream ms = new MemoryStream(buffer))
|
||||
{
|
||||
Utility.FormatBuffer(sw, ms, buffer.Length);
|
||||
}
|
||||
|
||||
sw.WriteLine();
|
||||
sw.WriteLine();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
|
||||
public int Seek(int offset, SeekOrigin origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
m_Index = offset;
|
||||
break;
|
||||
case SeekOrigin.Current:
|
||||
m_Index += offset;
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
m_Index = m_Size - offset;
|
||||
break;
|
||||
}
|
||||
|
||||
return m_Index;
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
if ((m_Index + 4) > m_Size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (m_Data[m_Index++] << 24) | (m_Data[m_Index++] << 16) | (m_Data[m_Index++] << 8) | m_Data[m_Index++];
|
||||
}
|
||||
|
||||
public short ReadInt16()
|
||||
{
|
||||
if ((m_Index + 2) > m_Size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (short)((m_Data[m_Index++] << 8) | m_Data[m_Index++]);
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
if ((m_Index + 1) > m_Size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_Data[m_Index++];
|
||||
}
|
||||
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
if ((m_Index + 4) > m_Size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint)((m_Data[m_Index++] << 24) | (m_Data[m_Index++] << 16) | (m_Data[m_Index++] << 8) | m_Data[m_Index++]);
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
if ((m_Index + 2) > m_Size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ushort)((m_Data[m_Index++] << 8) | m_Data[m_Index++]);
|
||||
}
|
||||
|
||||
public sbyte ReadSByte()
|
||||
{
|
||||
if ((m_Index + 1) > m_Size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (sbyte)m_Data[m_Index++];
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
if ((m_Index + 1) > m_Size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (m_Data[m_Index++] != 0);
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringLE()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < m_Size && (c = (m_Data[m_Index++] | (m_Data[m_Index++] << 8))) != 0)
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringLESafe(int fixedLength)
|
||||
{
|
||||
int bound = m_Index + (fixedLength << 1);
|
||||
int end = bound;
|
||||
|
||||
if (bound > m_Size)
|
||||
{
|
||||
bound = m_Size;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < bound && (c = (m_Data[m_Index++] | (m_Data[m_Index++] << 8))) != 0)
|
||||
{
|
||||
if (IsSafeChar(c))
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
m_Index = end;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringLESafe()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < m_Size && (c = (m_Data[m_Index++] | (m_Data[m_Index++] << 8))) != 0)
|
||||
{
|
||||
if (IsSafeChar(c))
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringSafe()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < m_Size && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0)
|
||||
{
|
||||
if (IsSafeChar(c))
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < m_Size && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0)
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public bool IsSafeChar(int c)
|
||||
{
|
||||
return (c >= 0x20 && c < 0xFFFE);
|
||||
}
|
||||
|
||||
public string ReadUTF8StringSafe(int fixedLength)
|
||||
{
|
||||
if (m_Index >= m_Size)
|
||||
{
|
||||
m_Index += fixedLength;
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
int bound = m_Index + fixedLength;
|
||||
//int end = bound;
|
||||
|
||||
if (bound > m_Size)
|
||||
{
|
||||
bound = m_Size;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int index = m_Index;
|
||||
int start = m_Index;
|
||||
|
||||
while (index < bound && m_Data[index++] != 0)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
|
||||
var buffer = new byte[count];
|
||||
int value = 0;
|
||||
|
||||
while (m_Index < bound && (value = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
buffer[index++] = (byte)value;
|
||||
}
|
||||
|
||||
string s = Utility.UTF8.GetString(buffer);
|
||||
|
||||
bool isSafe = true;
|
||||
|
||||
for (int i = 0; isSafe && i < s.Length; ++i)
|
||||
{
|
||||
isSafe = IsSafeChar(s[i]);
|
||||
}
|
||||
|
||||
m_Index = start + fixedLength;
|
||||
|
||||
if (isSafe)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(s.Length);
|
||||
|
||||
for (int i = 0; i < s.Length; ++i)
|
||||
{
|
||||
if (IsSafeChar(s[i]))
|
||||
{
|
||||
sb.Append(s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUTF8StringSafe()
|
||||
{
|
||||
if (m_Index >= m_Size)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int index = m_Index;
|
||||
|
||||
while (index < m_Size && m_Data[index++] != 0)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
|
||||
var buffer = new byte[count];
|
||||
int value = 0;
|
||||
|
||||
while (m_Index < m_Size && (value = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
buffer[index++] = (byte)value;
|
||||
}
|
||||
|
||||
string s = Utility.UTF8.GetString(buffer);
|
||||
|
||||
bool isSafe = true;
|
||||
|
||||
for (int i = 0; isSafe && i < s.Length; ++i)
|
||||
{
|
||||
isSafe = IsSafeChar(s[i]);
|
||||
}
|
||||
|
||||
if (isSafe)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(s.Length);
|
||||
|
||||
for (int i = 0; i < s.Length; ++i)
|
||||
{
|
||||
if (IsSafeChar(s[i]))
|
||||
{
|
||||
sb.Append(s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUTF8String()
|
||||
{
|
||||
if (m_Index >= m_Size)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
int index = m_Index;
|
||||
|
||||
while (index < m_Size && m_Data[index++] != 0)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
|
||||
var buffer = new byte[count];
|
||||
int value = 0;
|
||||
|
||||
while (m_Index < m_Size && (value = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
buffer[index++] = (byte)value;
|
||||
}
|
||||
|
||||
return Utility.UTF8.GetString(buffer);
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while (m_Index < m_Size && (c = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadStringSafe()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while (m_Index < m_Size && (c = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
if (IsSafeChar(c))
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringSafe(int fixedLength)
|
||||
{
|
||||
int bound = m_Index + (fixedLength << 1);
|
||||
int end = bound;
|
||||
|
||||
if (bound > m_Size)
|
||||
{
|
||||
bound = m_Size;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < bound && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0)
|
||||
{
|
||||
if (IsSafeChar(c))
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
m_Index = end;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeString(int fixedLength)
|
||||
{
|
||||
int bound = m_Index + (fixedLength << 1);
|
||||
int end = bound;
|
||||
|
||||
if (bound > m_Size)
|
||||
{
|
||||
bound = m_Size;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ((m_Index + 1) < bound && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0)
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
|
||||
m_Index = end;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadStringSafe(int fixedLength)
|
||||
{
|
||||
int bound = m_Index + fixedLength;
|
||||
int end = bound;
|
||||
|
||||
if (bound > m_Size)
|
||||
{
|
||||
bound = m_Size;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while (m_Index < bound && (c = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
if (IsSafeChar(c))
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
m_Index = end;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadString(int fixedLength)
|
||||
{
|
||||
int bound = m_Index + fixedLength;
|
||||
int end = bound;
|
||||
|
||||
if (bound > m_Size)
|
||||
{
|
||||
bound = m_Size;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while (m_Index < bound && (c = m_Data[m_Index++]) != 0)
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
|
||||
m_Index = end;
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
202
Server/Network/PacketThrottles.cs
Normal file
202
Server/Network/PacketThrottles.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
|
||||
using Server.Commands;
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public static class PacketThrottles
|
||||
{
|
||||
private static readonly int[] _Delays = new int[Byte.MaxValue];
|
||||
|
||||
private static readonly bool[] _Reserved = new bool[Byte.MaxValue];
|
||||
|
||||
static PacketThrottles()
|
||||
{
|
||||
_Delays[0x03] = 5; // speech
|
||||
_Delays[0xAD] = 5; // speech
|
||||
|
||||
_Delays[0x32] = 5; // fly toggle
|
||||
_Delays[0x72] = 5; // war toggle
|
||||
|
||||
_Delays[0x3B] = 100; // vendor buy response
|
||||
_Delays[0x9F] = 100; // vendor sell response
|
||||
|
||||
_Delays[0xB8] = 100; // profile request
|
||||
_Delays[0x6F] = 100; // trade request
|
||||
_Delays[0x75] = 100; // rename request
|
||||
_Delays[0x9B] = 100; // help request
|
||||
|
||||
_Delays[0xEC] = 100; // equip macro
|
||||
_Delays[0xED] = 100; // unequip macro
|
||||
|
||||
#region Reserved
|
||||
|
||||
// Reserved packets cannot be overridden by this throttle system
|
||||
|
||||
_Reserved[0x02] = true; // movement request, see: PlayerMobile
|
||||
_Reserved[0x80] = true; // login request, see: AccountAttackLimiter
|
||||
_Reserved[0x91] = true; // login request, see: AccountAttackLimiter
|
||||
_Reserved[0xCF] = true; // login request, see: AccountAttackLimiter
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
EventSink.WorldLoad += () => Persistence.Deserialize("Saves/PacketThrottles.bin", Load);
|
||||
EventSink.WorldSave += e => Persistence.Serialize("Saves/PacketThrottles.bin", Save);
|
||||
|
||||
CommandSystem.Register("GetThrottle", AccessLevel.Administrator, new CommandEventHandler(GetThrottle));
|
||||
CommandSystem.Register("SetThrottle", AccessLevel.Administrator, new CommandEventHandler(SetThrottle));
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
for (byte i = 0; i < Byte.MaxValue; i++)
|
||||
{
|
||||
if (!_Reserved[i] && _Delays[i] > 0)
|
||||
{
|
||||
PacketHandlers.RegisterThrottler(i, HandleThrottle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetThrottle(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length != 1)
|
||||
{
|
||||
e.Mobile.SendMessage("Usage: {0}GetThrottle <packetID>", CommandSystem.Prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
var packetID = e.GetInt32(0);
|
||||
|
||||
if (packetID < Byte.MinValue || packetID > Byte.MaxValue)
|
||||
{
|
||||
e.Mobile.SendMessage("Usage: PacketID must be between {0} and {1} inclusive", Byte.MinValue, Byte.MaxValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Reserved[packetID])
|
||||
{
|
||||
e.Mobile.SendMessage("Packet 0x{0:X2} throttle is protected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("Packet 0x{0:X2} throttle is {1}ms", packetID, _Delays[packetID]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetThrottle(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length < 2)
|
||||
{
|
||||
e.Mobile.SendMessage("Usage: {0}SetThrottle <packetID> <delayMS>", CommandSystem.Prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
var packetID = e.GetInt32(0);
|
||||
var delay = e.GetInt32(1);
|
||||
|
||||
if (packetID < Byte.MinValue || packetID > Byte.MaxValue)
|
||||
{
|
||||
e.Mobile.SendMessage("Usage: PacketID must be between {0} and {1} inclusive", Byte.MinValue, Byte.MaxValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Reserved[packetID])
|
||||
{
|
||||
e.Mobile.SendMessage("Packet 0x{0:X2} throttle is protected and can not be set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay < 0 || delay > 5000)
|
||||
{
|
||||
e.Mobile.SendMessage("Usage: Delay must be between 0 and 5000 inclusive");
|
||||
return;
|
||||
}
|
||||
|
||||
SetThrottle((byte)packetID, delay);
|
||||
|
||||
if (delay > 0)
|
||||
{
|
||||
e.Mobile.SendMessage("Packet 0x{0:X} throttle is {1}ms", packetID, _Delays[packetID]);
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage("Packet 0x{0:X} throttle has been removed", packetID, _Delays[packetID]);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetThrottle(byte packetID)
|
||||
{
|
||||
if (!_Reserved[packetID])
|
||||
{
|
||||
return _Delays[packetID];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetThrottle(byte packetID, int delay)
|
||||
{
|
||||
if (_Reserved[packetID])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
delay = Math.Max(0, Math.Min(5000, delay));
|
||||
|
||||
var oldDelay = _Delays[packetID];
|
||||
|
||||
if (oldDelay <= 0 && delay > 0)
|
||||
{
|
||||
PacketHandlers.RegisterThrottler(packetID, HandleThrottle);
|
||||
}
|
||||
else if (oldDelay > 0 && delay <= 0)
|
||||
{
|
||||
PacketHandlers.RegisterThrottler(packetID, null);
|
||||
}
|
||||
|
||||
_Delays[packetID] = delay;
|
||||
}
|
||||
|
||||
private static bool HandleThrottle(byte packetID, NetState ns, out bool drop)
|
||||
{
|
||||
drop = false;
|
||||
|
||||
if (ns.Mobile == null || ns.Mobile.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ns.IsThrottled(packetID, _Delays[packetID]))
|
||||
{
|
||||
drop = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void Save(GenericWriter writer)
|
||||
{
|
||||
writer.WriteEncodedInt(0);
|
||||
|
||||
for (var i = 0; i < _Delays.Length; i++)
|
||||
{
|
||||
writer.WriteEncodedInt(_Delays[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Load(GenericReader reader)
|
||||
{
|
||||
reader.ReadEncodedInt();
|
||||
|
||||
for (var i = 0; i < _Delays.Length; i++)
|
||||
{
|
||||
_Delays[i] = reader.ReadEncodedInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
439
Server/Network/PacketWriter.cs
Normal file
439
Server/Network/PacketWriter.cs
Normal file
@@ -0,0 +1,439 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality for writing primitive binary data.
|
||||
/// </summary>
|
||||
public class PacketWriter
|
||||
{
|
||||
private static readonly Stack<PacketWriter> m_Pool = new Stack<PacketWriter>();
|
||||
|
||||
public static PacketWriter CreateInstance()
|
||||
{
|
||||
return CreateInstance(32);
|
||||
}
|
||||
|
||||
public static PacketWriter CreateInstance(int capacity)
|
||||
{
|
||||
PacketWriter pw = null;
|
||||
|
||||
lock (m_Pool)
|
||||
{
|
||||
if (m_Pool.Count > 0)
|
||||
{
|
||||
pw = m_Pool.Pop();
|
||||
|
||||
if (pw != null)
|
||||
{
|
||||
pw.m_Capacity = capacity;
|
||||
pw.m_Stream.SetLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pw == null)
|
||||
{
|
||||
pw = new PacketWriter(capacity);
|
||||
}
|
||||
|
||||
return pw;
|
||||
}
|
||||
|
||||
public static void ReleaseInstance(PacketWriter pw)
|
||||
{
|
||||
lock (m_Pool)
|
||||
{
|
||||
if (!m_Pool.Contains(pw))
|
||||
{
|
||||
m_Pool.Push(pw);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
using (StreamWriter op = new StreamWriter("neterr.log"))
|
||||
{
|
||||
op.WriteLine("{0}\tInstance pool contains writer", DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("net error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal stream which holds the entire packet.
|
||||
/// </summary>
|
||||
private readonly MemoryStream m_Stream;
|
||||
|
||||
private int m_Capacity;
|
||||
|
||||
/// <summary>
|
||||
/// Internal format buffer.
|
||||
/// </summary>
|
||||
private readonly byte[] m_Buffer = new byte[4];
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new PacketWriter instance with the default capacity of 4 bytes.
|
||||
/// </summary>
|
||||
public PacketWriter()
|
||||
: this(32)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new PacketWriter instance with a given capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity">Initial capacity for the internal stream.</param>
|
||||
public PacketWriter(int capacity)
|
||||
{
|
||||
m_Stream = new MemoryStream(capacity);
|
||||
m_Capacity = capacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1.
|
||||
/// </summary>
|
||||
public void Write(bool value)
|
||||
{
|
||||
m_Stream.WriteByte((byte)(value ? 1 : 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 1-byte unsigned integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write(byte value)
|
||||
{
|
||||
m_Stream.WriteByte(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 1-byte signed integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write(sbyte value)
|
||||
{
|
||||
m_Stream.WriteByte((byte)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 2-byte signed integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write(short value)
|
||||
{
|
||||
m_Buffer[0] = (byte)(value >> 8);
|
||||
m_Buffer[1] = (byte)value;
|
||||
|
||||
m_Stream.Write(m_Buffer, 0, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 2-byte unsigned integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write(ushort value)
|
||||
{
|
||||
m_Buffer[0] = (byte)(value >> 8);
|
||||
m_Buffer[1] = (byte)value;
|
||||
|
||||
m_Stream.Write(m_Buffer, 0, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 4-byte signed integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write(int value)
|
||||
{
|
||||
m_Buffer[0] = (byte)(value >> 24);
|
||||
m_Buffer[1] = (byte)(value >> 16);
|
||||
m_Buffer[2] = (byte)(value >> 8);
|
||||
m_Buffer[3] = (byte)value;
|
||||
|
||||
m_Stream.Write(m_Buffer, 0, 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 4-byte unsigned integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write(uint value)
|
||||
{
|
||||
m_Buffer[0] = (byte)(value >> 24);
|
||||
m_Buffer[1] = (byte)(value >> 16);
|
||||
m_Buffer[2] = (byte)(value >> 8);
|
||||
m_Buffer[3] = (byte)value;
|
||||
|
||||
m_Stream.Write(m_Buffer, 0, 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the underlying stream
|
||||
/// </summary>
|
||||
public void Write(byte[] buffer, int offset, int size)
|
||||
{
|
||||
m_Stream.Write(buffer, offset, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters.
|
||||
/// </summary>
|
||||
public void WriteAsciiFixed(string value, int size)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine("Network: Attempted to WriteAsciiFixed() with null value");
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
m_Stream.SetLength(m_Stream.Length + size);
|
||||
|
||||
if (length >= size)
|
||||
{
|
||||
m_Stream.Position += Encoding.ASCII.GetBytes(value, 0, size, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding.ASCII.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
m_Stream.Position += size;
|
||||
}
|
||||
|
||||
/*byte[] buffer = Encoding.ASCII.GetBytes( value );
|
||||
|
||||
if ( buffer.Length >= size )
|
||||
{
|
||||
m_Stream.Write( buffer, 0, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
Fill( size - buffer.Length );
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character.
|
||||
/// </summary>
|
||||
public void WriteAsciiNull(string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine("Network: Attempted to WriteAsciiNull() with null value");
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
m_Stream.SetLength(m_Stream.Length + length + 1);
|
||||
|
||||
Encoding.ASCII.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
m_Stream.Position += length + 1;
|
||||
|
||||
/*byte[] buffer = Encoding.ASCII.GetBytes( value );
|
||||
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
m_Stream.WriteByte( 0 );*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null character.
|
||||
/// </summary>
|
||||
public void WriteLittleUniNull(string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine("Network: Attempted to WriteLittleUniNull() with null value");
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
m_Stream.SetLength(m_Stream.Length + ((length + 1) * 2));
|
||||
|
||||
m_Stream.Position += Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
m_Stream.Position += 2;
|
||||
|
||||
/*byte[] buffer = Encoding.Unicode.GetBytes( value );
|
||||
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
|
||||
m_Buffer[0] = 0;
|
||||
m_Buffer[1] = 0;
|
||||
m_Stream.Write( m_Buffer, 0, 2 );*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters.
|
||||
/// </summary>
|
||||
public void WriteLittleUniFixed(string value, int size)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine("Network: Attempted to WriteLittleUniFixed() with null value");
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
size *= 2;
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
m_Stream.SetLength(m_Stream.Length + size);
|
||||
|
||||
if ((length * 2) >= size)
|
||||
{
|
||||
m_Stream.Position += Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
m_Stream.Position += size;
|
||||
}
|
||||
|
||||
/*size *= 2;
|
||||
|
||||
byte[] buffer = Encoding.Unicode.GetBytes( value );
|
||||
|
||||
if ( buffer.Length >= size )
|
||||
{
|
||||
m_Stream.Write( buffer, 0, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
Fill( size - buffer.Length );
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null character.
|
||||
/// </summary>
|
||||
public void WriteBigUniNull(string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine("Network: Attempted to WriteBigUniNull() with null value");
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
m_Stream.SetLength(m_Stream.Length + ((length + 1) * 2));
|
||||
|
||||
m_Stream.Position += Encoding.BigEndianUnicode.GetBytes(
|
||||
value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
m_Stream.Position += 2;
|
||||
|
||||
/*byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
|
||||
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
|
||||
m_Buffer[0] = 0;
|
||||
m_Buffer[1] = 0;
|
||||
m_Stream.Write( m_Buffer, 0, 2 );*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters.
|
||||
/// </summary>
|
||||
public void WriteBigUniFixed(string value, int size)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Console.WriteLine("Network: Attempted to WriteBigUniFixed() with null value");
|
||||
value = String.Empty;
|
||||
}
|
||||
|
||||
size *= 2;
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
m_Stream.SetLength(m_Stream.Length + size);
|
||||
|
||||
if ((length * 2) >= size)
|
||||
{
|
||||
m_Stream.Position += Encoding.BigEndianUnicode.GetBytes(
|
||||
value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding.BigEndianUnicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
|
||||
m_Stream.Position += size;
|
||||
}
|
||||
|
||||
/*size *= 2;
|
||||
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
|
||||
|
||||
if ( buffer.Length >= size )
|
||||
{
|
||||
m_Stream.Write( buffer, 0, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
Fill( size - buffer.Length );
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the stream from the current position up to (capacity) with 0x00's
|
||||
/// </summary>
|
||||
public void Fill()
|
||||
{
|
||||
Fill((int)(m_Capacity - m_Stream.Length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a number of 0x00 byte values to the underlying stream.
|
||||
/// </summary>
|
||||
public void Fill(int length)
|
||||
{
|
||||
if (m_Stream.Position == m_Stream.Length)
|
||||
{
|
||||
m_Stream.SetLength(m_Stream.Length + length);
|
||||
m_Stream.Seek(0, SeekOrigin.End);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write(new byte[length], 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total stream length.
|
||||
/// </summary>
|
||||
public long Length { get { return m_Stream.Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current stream position.
|
||||
/// </summary>
|
||||
public long Position { get { return m_Stream.Position; } set { m_Stream.Position = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The internal stream used by this PacketWriter instance.
|
||||
/// </summary>
|
||||
public MemoryStream UnderlyingStream { get { return m_Stream; } }
|
||||
|
||||
/// <summary>
|
||||
/// Offsets the current position from an origin.
|
||||
/// </summary>
|
||||
public long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return m_Stream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entire stream content as a byte array.
|
||||
/// </summary>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
return m_Stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
5492
Server/Network/Packets.cs
Normal file
5492
Server/Network/Packets.cs
Normal file
File diff suppressed because it is too large
Load Diff
98
Server/Network/PingListener.cs
Normal file
98
Server/Network/PingListener.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class PingListener : IDisposable
|
||||
{
|
||||
private UdpClient _Listener;
|
||||
const int Port = 12000;
|
||||
|
||||
private static UdpClient Bind(IPEndPoint ipep)
|
||||
{
|
||||
ipep = new IPEndPoint(ipep.Address, Port);
|
||||
|
||||
var s = new UdpClient
|
||||
{
|
||||
Client = new Socket(ipep.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
s.Client.ExclusiveAddressUse = false;
|
||||
s.Client.Bind(ipep);
|
||||
|
||||
return s;
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
switch (e.ErrorCode)
|
||||
{
|
||||
case 10048: // WSAEADDRINUSE
|
||||
Console.WriteLine("Ping Listener Failed: {0}:{1} (In Use)", ipep.Address, Port);
|
||||
break;
|
||||
case 10049: // WSAEADDRNOTAVAIL
|
||||
Console.WriteLine("Ping Listener Failed: {0}:{1} (Unavailable)", ipep.Address, Port);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
Console.WriteLine("Ping Listener Exception:");
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public PingListener(IPEndPoint ipep)
|
||||
{
|
||||
_Listener = Bind(ipep);
|
||||
|
||||
BeginReceive();
|
||||
}
|
||||
|
||||
private void BeginReceive()
|
||||
{
|
||||
if (_Listener != null)
|
||||
{
|
||||
_Listener.BeginReceive(EndReceive, _Listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void EndReceive(IAsyncResult r)
|
||||
{
|
||||
var ripep = new IPEndPoint(IPAddress.Any, Port);
|
||||
var recvd = _Listener.EndReceive(r, ref ripep);
|
||||
|
||||
//Console.WriteLine("[PING]: \"{0}\" Received from {1}", Encoding.UTF8.GetString(recvd), ripep);
|
||||
|
||||
BeginSend(recvd, ripep);
|
||||
|
||||
BeginReceive();
|
||||
}
|
||||
|
||||
private void BeginSend(byte[] data, IPEndPoint ipep)
|
||||
{
|
||||
//Console.WriteLine("[PONG]: \"{0}\" Sent to {1}", Encoding.UTF8.GetString(data), ipep);
|
||||
|
||||
_Listener.BeginSend(data, data.Length, ipep, EndSend, _Listener);
|
||||
}
|
||||
|
||||
private void EndSend(IAsyncResult asyncResult)
|
||||
{
|
||||
_Listener.EndSend(asyncResult);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Listener.Close();
|
||||
_Listener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
239
Server/Network/SendQueue.cs
Normal file
239
Server/Network/SendQueue.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public class SendQueue
|
||||
{
|
||||
public class Gram
|
||||
{
|
||||
private static readonly Stack<Gram> _pool = new Stack<Gram>();
|
||||
|
||||
public static Gram Acquire()
|
||||
{
|
||||
lock (_pool)
|
||||
{
|
||||
Gram gram;
|
||||
|
||||
if (_pool.Count > 0)
|
||||
{
|
||||
gram = _pool.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
gram = new Gram();
|
||||
}
|
||||
|
||||
gram._buffer = AcquireBuffer();
|
||||
gram._length = 0;
|
||||
|
||||
return gram;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _length;
|
||||
|
||||
public byte[] Buffer { get { return _buffer; } }
|
||||
|
||||
public int Length { get { return _length; } }
|
||||
|
||||
public int Available { get { return (_buffer.Length - _length); } }
|
||||
|
||||
public bool IsFull { get { return (_length == _buffer.Length); } }
|
||||
|
||||
private Gram()
|
||||
{ }
|
||||
|
||||
public int Write(byte[] buffer, int offset, int length)
|
||||
{
|
||||
int write = Math.Min(length, Available);
|
||||
|
||||
System.Buffer.BlockCopy(buffer, offset, _buffer, _length, write);
|
||||
|
||||
_length += write;
|
||||
|
||||
return write;
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
lock (_pool)
|
||||
{
|
||||
_pool.Push(this);
|
||||
ReleaseBuffer(_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int m_CoalesceBufferSize = 512;
|
||||
private static BufferPool m_UnusedBuffers = new BufferPool("Coalesced", 2048, m_CoalesceBufferSize);
|
||||
|
||||
public static int CoalesceBufferSize
|
||||
{
|
||||
get { return m_CoalesceBufferSize; }
|
||||
set
|
||||
{
|
||||
if (m_CoalesceBufferSize == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BufferPool old = m_UnusedBuffers;
|
||||
|
||||
lock (old)
|
||||
{
|
||||
if (m_UnusedBuffers != null)
|
||||
{
|
||||
m_UnusedBuffers.Free();
|
||||
}
|
||||
|
||||
m_CoalesceBufferSize = value;
|
||||
m_UnusedBuffers = new BufferPool("Coalesced", 2048, m_CoalesceBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] AcquireBuffer()
|
||||
{
|
||||
lock (m_UnusedBuffers)
|
||||
return m_UnusedBuffers.AcquireBuffer();
|
||||
}
|
||||
|
||||
public static void ReleaseBuffer(byte[] buffer)
|
||||
{
|
||||
lock (m_UnusedBuffers)
|
||||
if (buffer != null && buffer.Length == m_CoalesceBufferSize)
|
||||
{
|
||||
m_UnusedBuffers.ReleaseBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Queue<Gram> _pending;
|
||||
|
||||
private Gram _buffered;
|
||||
|
||||
public bool IsFlushReady { get { return (_pending.Count == 0 && _buffered != null); } }
|
||||
|
||||
public bool IsEmpty { get { return (_pending.Count == 0 && _buffered == null); } }
|
||||
|
||||
public SendQueue()
|
||||
{
|
||||
_pending = new Queue<Gram>();
|
||||
}
|
||||
|
||||
public Gram CheckFlushReady()
|
||||
{
|
||||
Gram gram = _buffered;
|
||||
_pending.Enqueue(_buffered);
|
||||
_buffered = null;
|
||||
return gram;
|
||||
}
|
||||
|
||||
public Gram Dequeue()
|
||||
{
|
||||
Gram gram = null;
|
||||
|
||||
if (_pending.Count > 0)
|
||||
{
|
||||
_pending.Dequeue().Release();
|
||||
|
||||
if (_pending.Count > 0)
|
||||
{
|
||||
gram = _pending.Peek();
|
||||
}
|
||||
}
|
||||
|
||||
return gram;
|
||||
}
|
||||
|
||||
private const int PendingCap = 0x200000;
|
||||
|
||||
public Gram Enqueue(byte[] buffer, int length)
|
||||
{
|
||||
return Enqueue(buffer, 0, length);
|
||||
}
|
||||
|
||||
public Gram Enqueue(byte[] buffer, int offset, int length)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException("buffer");
|
||||
}
|
||||
else if (!(offset >= 0 && offset < buffer.Length))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
"offset", offset, "Offset must be greater than or equal to zero and less than the size of the buffer.");
|
||||
}
|
||||
else if (length < 0 || length > buffer.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
"length", length, "Length cannot be less than zero or greater than the size of the buffer.");
|
||||
}
|
||||
else if ((buffer.Length - offset) < length)
|
||||
{
|
||||
throw new ArgumentException("Offset and length do not point to a valid segment within the buffer.");
|
||||
}
|
||||
|
||||
int existingBytes = (_pending.Count * m_CoalesceBufferSize) + (_buffered == null ? 0 : _buffered.Length);
|
||||
|
||||
if ((existingBytes + length) > PendingCap)
|
||||
{
|
||||
throw new CapacityExceededException();
|
||||
}
|
||||
|
||||
Gram gram = null;
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
if (_buffered == null)
|
||||
{
|
||||
// nothing yet buffered
|
||||
_buffered = Gram.Acquire();
|
||||
}
|
||||
|
||||
int bytesWritten = _buffered.Write(buffer, offset, length);
|
||||
|
||||
offset += bytesWritten;
|
||||
length -= bytesWritten;
|
||||
|
||||
if (_buffered.IsFull)
|
||||
{
|
||||
if (_pending.Count == 0)
|
||||
{
|
||||
gram = _buffered;
|
||||
}
|
||||
|
||||
_pending.Enqueue(_buffered);
|
||||
_buffered = null;
|
||||
}
|
||||
}
|
||||
|
||||
return gram;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_buffered != null)
|
||||
{
|
||||
_buffered.Release();
|
||||
_buffered = null;
|
||||
}
|
||||
|
||||
while (_pending.Count > 0)
|
||||
{
|
||||
_pending.Dequeue().Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CapacityExceededException : Exception
|
||||
{
|
||||
public CapacityExceededException()
|
||||
: base("Too much data pending.")
|
||||
{ }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user