diff --git a/Projects/Server/Buffers/CircularBuffer.cs b/Projects/Server/Buffers/CircularBuffer.cs
new file mode 100644
index 000000000..b34240490
--- /dev/null
+++ b/Projects/Server/Buffers/CircularBuffer.cs
@@ -0,0 +1,130 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: CircularBuffer.cs *
+ * *
+ * This program is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+namespace System.Buffers
+{
+ public readonly ref struct CircularBuffer where T : struct
+ {
+ private readonly Span _first;
+ private readonly Span _second;
+
+ public int Length { get; }
+
+ public CircularBuffer(ArraySegment[] buffers) : this(buffers[0], buffers[1])
+ {
+ }
+
+ public CircularBuffer(Span first, Span second)
+ {
+ _first = first;
+ _second = second;
+ Length = first.Length + second.Length;
+ }
+
+ public T this[int index]
+ {
+ get
+ {
+ if (index < 0 || index > Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ return index < _first.Length ? _first[index] : _second[_first.Length - index];
+ }
+ set
+ {
+ if (index < 0 || index > Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ if (index < _first.Length)
+ {
+ _first[index] = value;
+ }
+ else
+ {
+ _second[_first.Length - index] = value;
+ }
+ }
+ }
+
+ public void CopyFrom(ReadOnlySpan bytes)
+ {
+ var remaining = bytes.Length;
+ var offset = 0;
+
+ if (remaining == 0)
+ {
+ return;
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ var buffer = i == 0 ? _first : _second;
+
+ if (buffer.Length == 0)
+ {
+ continue;
+ }
+
+ var sz = Math.Min(remaining, buffer.Length);
+ bytes.Slice(offset, sz).CopyTo(buffer);
+
+ remaining -= sz;
+ offset += sz;
+
+ if (remaining == 0)
+ {
+ return;
+ }
+ }
+
+ throw new OutOfMemoryException();
+ }
+
+ public void CopyTo(Span bytes)
+ {
+ if (bytes.Length < Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bytes.Length));
+ }
+
+ if (_first.Length > 0)
+ {
+ _first.CopyTo(bytes);
+ }
+
+ if (_second.Length > 0)
+ {
+ _second.CopyTo(bytes.Slice(_first.Length));
+ }
+ }
+
+ public CircularBuffer Slice(int offset, int count)
+ {
+ var firstCount = Math.Min(count, _first.Length - offset);
+ var first = offset < _first.Length
+ ? _first.Slice(offset, firstCount)
+ : Span.Empty;
+
+ var secondCount = offset > _first.Length ? count : count - firstCount;
+ var second = secondCount > 0 ? _second.Slice(Math.Max(0, offset - _first.Length), secondCount) : Span.Empty;
+
+ return new CircularBuffer(first, second);
+ }
+ }
+}
diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs
index 66b246a12..1899b80d3 100644
--- a/Projects/Server/Buffers/SpanReader.cs
+++ b/Projects/Server/Buffers/SpanReader.cs
@@ -13,13 +13,13 @@
* along with this program. If not, see . *
*************************************************************************/
-using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
+using Server;
-namespace Server.Buffers
+namespace System.Buffers
{
ref struct SpanReader
{
diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs
index d61b2d352..ac88a392b 100644
--- a/Projects/Server/Buffers/SpanWriter.cs
+++ b/Projects/Server/Buffers/SpanWriter.cs
@@ -13,14 +13,14 @@
* along with this program. If not, see . *
*************************************************************************/
-using System;
using System.Buffers.Binary;
using System.Data;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
+using Server;
-namespace Server.Buffers
+namespace System.Buffers
{
public ref struct SpanWriter
{
diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs
index 0f3f2f681..da985d191 100644
--- a/Projects/Server/Network/NetState/NetState.cs
+++ b/Projects/Server/Network/NetState/NetState.cs
@@ -33,12 +33,12 @@ namespace Server.Network
{
public delegate void NetStateCreatedCallback(NetState ns);
- public delegate void EncodePacket(ReadOnlySpan inputBuffer, CircularBufferWriter outputBuffer);
+ public delegate void EncodePacket(CircularBuffer buffer, ref int length);
public partial class NetState : IComparable
{
- private static int IncomingPipeSize = 1024 * 64;
- private static int OutgoingPipeSize = 1024 * 256;
+ private static int RecvPipeSize = 1024 * 64;
+ private static int SendPipeSize = 1024 * 256;
private static int GumpCap = 512;
private static int HuePickerCap = 512;
private static int MenuCap = 512;
@@ -51,13 +51,13 @@ namespace Server.Network
private readonly string m_ToString;
private int m_Disposing;
private ClientVersion m_Version;
- private byte[] m_IncomingBuffer;
- private Pipe m_IncomingPipe;
- private byte[] m_OutgoingBuffer;
- private Pipe m_OutgoingPipe;
+ private byte[] _recvBuffer;
+ private Pipe _recvPipe;
+ private byte[] _sendBuffer;
+ private Pipe _sendPipe;
private long m_NextCheckActivity;
private volatile bool m_Running;
- private Thread _sendThread;
+ private readonly Thread _sendThread;
private volatile EncodePacket _packetDecoder;
private volatile EncodePacket _packetEncoder;
@@ -66,8 +66,8 @@ namespace Server.Network
public static void Configure()
{
- IncomingPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.incomingPipeSize", IncomingPipeSize);
- OutgoingPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.outgoingPipeSize", OutgoingPipeSize);
+ RecvPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.recvPipeSize", RecvPipeSize);
+ SendPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.sendPipeSize", SendPipeSize);
GumpCap = ServerConfiguration.GetOrUpdateSetting("netstate.gumpCap", GumpCap);
HuePickerCap = ServerConfiguration.GetOrUpdateSetting("netstate.huePickerCap", HuePickerCap);
MenuCap = ServerConfiguration.GetOrUpdateSetting("netstate.menuCap", MenuCap);
@@ -88,10 +88,10 @@ namespace Server.Network
HuePickers = new List();
Menus = new List();
Trades = new List();
- m_IncomingBuffer = new byte[IncomingPipeSize];
- m_IncomingPipe = new Pipe(m_IncomingBuffer);
- m_OutgoingBuffer = new byte[OutgoingPipeSize];
- m_OutgoingPipe = new Pipe(m_OutgoingBuffer);
+ _recvBuffer = new byte[RecvPipeSize];
+ _recvPipe = new Pipe(_recvBuffer);
+ _sendBuffer = new byte[SendPipeSize];
+ _sendPipe = new Pipe(_sendBuffer);
m_NextCheckActivity = Core.TickCount + 30000;
_sendThread = sendThread ?? Core.Thread;
@@ -370,7 +370,9 @@ namespace Server.Network
NetworkState.Resume(ref m_NetworkState);
}
- public virtual void Send(Span buffer)
+ public Pipe.Result GetAvailableSendPipe() => _recvPipe.Writer.GetAvailable();
+
+ public virtual void Send(CircularBuffer buffer, int length)
{
if (Connection == null || BlockAllPackets || buffer.Length == 0)
{
@@ -387,25 +389,10 @@ namespace Server.Network
#endif
}
- var writer = m_OutgoingPipe.Writer;
-
try
{
- var result = writer.GetAvailable();
- int length;
- if (PacketEncoder != null)
- {
- var bufferWriter = new CircularBufferWriter(result.Buffer);
- PacketEncoder?.Invoke(buffer, bufferWriter);
- length = bufferWriter.Position;
- }
- else
- {
- result.CopyFrom(buffer);
- length = buffer.Length;
- }
-
- writer.Advance((uint)length);
+ _packetEncoder?.Invoke(buffer, ref length);
+ _sendPipe.Writer.Advance((uint)length);
}
catch (Exception ex)
{
@@ -435,7 +422,7 @@ namespace Server.Network
#endif
}
- var writer = m_OutgoingPipe.Writer;
+ var writer = _sendPipe.Writer;
try
{
@@ -490,7 +477,7 @@ namespace Server.Network
private async void SendTask(object state)
{
- var reader = m_OutgoingPipe.Reader;
+ var reader = _sendPipe.Reader;
try
{
@@ -527,19 +514,16 @@ namespace Server.Network
}
}
- private int DecodePacket(ReadOnlySpan input, ArraySegment[] output)
+ private void DecodePacket(ArraySegment[] buffer, ref int length)
{
- var writer = new CircularBufferWriter(output);
- PacketDecoder(input, writer);
- return writer.Position;
+ CircularBuffer cBuffer = new CircularBuffer(buffer);
+ _packetDecoder?.Invoke(cBuffer, ref length);
}
private async void RecvTask(object state)
{
var socket = Connection;
- var writer = m_IncomingPipe.Writer;
-
- byte[] encodingBuffer = null;
+ var writer = _recvPipe.Writer;
try
{
@@ -557,33 +541,13 @@ namespace Server.Network
continue;
}
- int bytesWritten;
-
- if (PacketDecoder != null)
+ var bytesWritten = await socket.ReceiveAsync(result.Buffer, SocketFlags.None);
+ if (bytesWritten <= 0)
{
- encodingBuffer ??= ArrayPool.Shared.Rent(0x10000);
- bytesWritten = await socket.ReceiveAsync(encodingBuffer, SocketFlags.None);
- if (bytesWritten <= 0)
- {
- break;
- }
- bytesWritten = DecodePacket(encodingBuffer.AsSpan(0, bytesWritten), result.Buffer);
+ break;
}
- else
- {
- if (encodingBuffer != null)
- {
- var returnBuffer = encodingBuffer;
- encodingBuffer = null;
- ArrayPool.Shared.Return(returnBuffer);
- }
- bytesWritten = await socket.ReceiveAsync(result.Buffer, SocketFlags.None);
- if (bytesWritten <= 0)
- {
- break;
- }
- }
+ DecodePacket(result.Buffer, ref bytesWritten);
writer.Advance((uint)bytesWritten);
m_NextCheckActivity = Core.TickCount + 90000;
@@ -600,10 +564,6 @@ namespace Server.Network
}
finally
{
- if (encodingBuffer != null)
- {
- ArrayPool.Shared.Return(encodingBuffer);
- }
Dispose();
}
}
@@ -627,7 +587,7 @@ namespace Server.Network
try
{
- var reader = m_IncomingPipe.Reader;
+ var reader = _recvPipe.Reader;
// Process as many packets as we can synchronously
while (true)
@@ -670,7 +630,7 @@ namespace Server.Network
{
if (Connection != null)
{
- m_OutgoingPipe.Writer.Flush();
+ _sendPipe.Writer.Flush();
}
}
@@ -755,7 +715,7 @@ namespace Server.Network
return;
}
- m_OutgoingPipe.Writer.Close();
+ _sendPipe.Writer.Close();
try
{
@@ -793,10 +753,10 @@ namespace Server.Network
ns.m_Running = false;
ns.Connection = null;
- ns.m_IncomingBuffer = null;
- ns.m_IncomingPipe = null;
- ns.m_OutgoingBuffer = null;
- ns.m_OutgoingPipe = null;
+ ns._recvBuffer = null;
+ ns._recvPipe = null;
+ ns._sendBuffer = null;
+ ns._sendPipe = null;
ns.Gumps.Clear();
ns.Menus.Clear();
ns.HuePickers.Clear();
diff --git a/Projects/Server/Network/NetworkCompression.cs b/Projects/Server/Network/NetworkCompression.cs
index 946df2b4e..7c78d0d2c 100644
--- a/Projects/Server/Network/NetworkCompression.cs
+++ b/Projects/Server/Network/NetworkCompression.cs
@@ -12,7 +12,7 @@ namespace Server.Network
private const int ValueIndex = 1;
// UO packets may not exceed 64kb in length
- private const int BufferSize = 0x10000;
+ public const int BufferSize = 0x10000;
// Optimal compression ratio is 2 / 8; worst compression ratio is 11 / 8
private const int MinimalCodeLength = 2;
@@ -61,170 +61,127 @@ namespace Server.Network
0x4, 0x00D
};
- public static void Compress(ReadOnlySpan input, CircularBufferWriter output)
+ public static void Compress(CircularBuffer buffer, ref int length)
{
- int inputCapacity = input.Length;
+ length = Compress(buffer, length, buffer);
+ }
- if (inputCapacity > DefiniteOverflow)
+ public static int Compress(CircularBuffer input, int inputLength, CircularBuffer output)
+ {
+ if (inputLength > DefiniteOverflow)
{
- return;
+ return 0;
}
int bitCount = 0;
int bitValue = 0;
int inputIdx = 0;
+ int outputIdx = 0;
- while (inputIdx < inputCapacity)
+ while (inputIdx < inputLength)
{
int i = input[inputIdx++] << 1;
bitCount += _huffmanTable[i];
- bitValue <<= _huffmanTable[i];
- bitValue |= _huffmanTable[i + 1];
+ bitValue = (bitValue << _huffmanTable[i]) | _huffmanTable[i + 1];
while (bitCount >= 8)
{
bitCount -= 8;
- if (output.Length < output.Position + 1)
+ if (output.Length < outputIdx + 1)
{
- return;
+ return 0;
}
- output.Write((byte)(bitValue >> bitCount));
+ output[outputIdx++] = (byte)(bitValue >> bitCount);
}
}
// terminal code
bitCount += _huffmanTable[0x200];
- bitValue <<= _huffmanTable[0x200];
- bitValue |= _huffmanTable[0x201];
+ bitValue = (bitValue << _huffmanTable[0x200]) | _huffmanTable[0x201];
// align on byte boundary
if ((bitCount & 7) != 0)
{
- bitValue <<= (8 - (bitCount & 7));
- bitCount += (8 - (bitCount & 7));
+ bitValue <<= 8 - (bitCount & 7);
+ bitCount += 8 - (bitCount & 7);
}
while (bitCount >= 8)
{
bitCount -= 8;
- if (output.Length < output.Position + 1)
+ if (output.Length < outputIdx + 1)
{
- return;
+ return 0;
}
- output.Write((byte)(bitValue >> bitCount));
+ output[outputIdx++] = (byte)(bitValue >> bitCount);
}
+
+ return outputIdx;
}
- public static unsafe void Compress(
- ReadOnlySpan input, int offset, int count, Span output, out int length
- )
+ public static int Compress(ReadOnlySpan input, Span output)
{
- if (input == null)
+ if (input.Length > DefiniteOverflow)
{
- throw new ArgumentNullException(nameof(input));
+ return 0;
}
- if (offset < 0 || offset >= input.Length)
+ int bitCount = 0;
+ int bitValue = 0;
+
+ int inputIdx = 0;
+ int outputIdx = 0;
+
+ while (inputIdx < input.Length)
{
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
+ int i = input[inputIdx++] << 1;
- if (count < 0 || count > input.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(count));
- }
+ bitCount += _huffmanTable[i];
+ bitValue = (bitValue << _huffmanTable[i]) | _huffmanTable[i + 1];
- if (input.Length - offset < count)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
-
- length = 0;
-
- if (count > DefiniteOverflow)
- {
- return;
- }
-
- var bitCount = 0;
- var bitValue = 0;
-
- fixed (int* pTable = _huffmanTable)
- {
- fixed (byte* pInputBuffer = input)
+ while (bitCount >= 8)
{
- byte* pInput = pInputBuffer + offset, pInputEnd = pInput + count;
+ bitCount -= 8;
- fixed (byte* pOutputBuffer = output)
+ if (output.Length < outputIdx + 1)
{
- byte* pOutput = pOutputBuffer, pOutputEnd = pOutput + BufferSize;
-
- int* pEntry;
- 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 0;
}
+
+ output[outputIdx++] = (byte)(bitValue >> bitCount);
}
}
+
+ // terminal code
+ bitCount += _huffmanTable[0x200];
+ bitValue = (bitValue << _huffmanTable[0x200]) | _huffmanTable[0x201];
+
+ // align on byte boundary
+ if ((bitCount & 7) != 0)
+ {
+ bitValue <<= 8 - (bitCount & 7);
+ bitCount += 8 - (bitCount & 7);
+ }
+
+ while (bitCount >= 8)
+ {
+ bitCount -= 8;
+
+ if (output.Length < outputIdx + 1)
+ {
+ return 0;
+ }
+
+ output[outputIdx++] = (byte)(bitValue >> bitCount);
+ }
+
+ return outputIdx;
}
}
}
diff --git a/Projects/Server/Network/Packet.cs b/Projects/Server/Network/Packet.cs
index 3a7b0177d..9676e6cc8 100644
--- a/Projects/Server/Network/Packet.cs
+++ b/Projects/Server/Network/Packet.cs
@@ -194,7 +194,7 @@ namespace Server.Network
if (compress)
{
var compressorBuffer = ArrayPool.Shared.Rent(CompressorBufferSize);
- NetworkCompression.Compress(m_CompiledBuffer, 0, length, compressorBuffer, out var compressedLength);
+ var compressedLength = NetworkCompression.Compress(m_CompiledBuffer.AsSpan(0, length), compressorBuffer);
if (length <= 0)
{