Changes Huffman to in-place & Adds CircularBuffer (#287)
- [X] Adds CircularBuffer - [X] Updates Packet Encoding/Decoding - [X] Changes Huffman to in-place Bumps release version
This commit is contained in:
parent
3337cfa4e2
commit
293e691539
6 changed files with 237 additions and 190 deletions
130
Projects/Server/Buffers/CircularBuffer.cs
Normal file
130
Projects/Server/Buffers/CircularBuffer.cs
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public readonly ref struct CircularBuffer<T> where T : struct
|
||||
{
|
||||
private readonly Span<T> _first;
|
||||
private readonly Span<T> _second;
|
||||
|
||||
public int Length { get; }
|
||||
|
||||
public CircularBuffer(ArraySegment<T>[] buffers) : this(buffers[0], buffers[1])
|
||||
{
|
||||
}
|
||||
|
||||
public CircularBuffer(Span<T> first, Span<T> 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<T> 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<T> 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<T> Slice(int offset, int count)
|
||||
{
|
||||
var firstCount = Math.Min(count, _first.Length - offset);
|
||||
var first = offset < _first.Length
|
||||
? _first.Slice(offset, firstCount)
|
||||
: Span<T>.Empty;
|
||||
|
||||
var secondCount = offset > _first.Length ? count : count - firstCount;
|
||||
var second = secondCount > 0 ? _second.Slice(Math.Max(0, offset - _first.Length), secondCount) : Span<T>.Empty;
|
||||
|
||||
return new CircularBuffer<T>(first, second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,13 +13,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ namespace Server.Network
|
|||
{
|
||||
public delegate void NetStateCreatedCallback(NetState ns);
|
||||
|
||||
public delegate void EncodePacket(ReadOnlySpan<byte> inputBuffer, CircularBufferWriter outputBuffer);
|
||||
public delegate void EncodePacket(CircularBuffer<byte> buffer, ref int length);
|
||||
|
||||
public partial class NetState : IComparable<NetState>
|
||||
{
|
||||
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<byte> m_IncomingPipe;
|
||||
private byte[] m_OutgoingBuffer;
|
||||
private Pipe<byte> m_OutgoingPipe;
|
||||
private byte[] _recvBuffer;
|
||||
private Pipe<byte> _recvPipe;
|
||||
private byte[] _sendBuffer;
|
||||
private Pipe<byte> _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<HuePicker>();
|
||||
Menus = new List<IMenu>();
|
||||
Trades = new List<SecureTrade>();
|
||||
m_IncomingBuffer = new byte[IncomingPipeSize];
|
||||
m_IncomingPipe = new Pipe<byte>(m_IncomingBuffer);
|
||||
m_OutgoingBuffer = new byte[OutgoingPipeSize];
|
||||
m_OutgoingPipe = new Pipe<byte>(m_OutgoingBuffer);
|
||||
_recvBuffer = new byte[RecvPipeSize];
|
||||
_recvPipe = new Pipe<byte>(_recvBuffer);
|
||||
_sendBuffer = new byte[SendPipeSize];
|
||||
_sendPipe = new Pipe<byte>(_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<byte> buffer)
|
||||
public Pipe<byte>.Result<byte> GetAvailableSendPipe() => _recvPipe.Writer.GetAvailable();
|
||||
|
||||
public virtual void Send(CircularBuffer<byte> 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<byte> input, ArraySegment<byte>[] output)
|
||||
private void DecodePacket(ArraySegment<byte>[] buffer, ref int length)
|
||||
{
|
||||
var writer = new CircularBufferWriter(output);
|
||||
PacketDecoder(input, writer);
|
||||
return writer.Position;
|
||||
CircularBuffer<byte> cBuffer = new CircularBuffer<byte>(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<byte>.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<byte>.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<byte>.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();
|
||||
|
|
|
|||
|
|
@ -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<byte> input, CircularBufferWriter output)
|
||||
public static void Compress(CircularBuffer<byte> buffer, ref int length)
|
||||
{
|
||||
int inputCapacity = input.Length;
|
||||
length = Compress(buffer, length, buffer);
|
||||
}
|
||||
|
||||
if (inputCapacity > DefiniteOverflow)
|
||||
public static int Compress(CircularBuffer<byte> input, int inputLength, CircularBuffer<byte> 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<byte> input, int offset, int count, Span<byte> output, out int length
|
||||
)
|
||||
public static int Compress(ReadOnlySpan<byte> input, Span<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ namespace Server.Network
|
|||
if (compress)
|
||||
{
|
||||
var compressorBuffer = ArrayPool<byte>.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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue