Adds PacketDecoder & Makes NetState testable (#286)
This commit is contained in:
parent
e0da7209f6
commit
3337cfa4e2
5 changed files with 177 additions and 35 deletions
|
|
@ -161,7 +161,17 @@ namespace Server.Buffers
|
||||||
throw new OutOfMemoryException();
|
throw new OutOfMemoryException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Position += encoding.GetBytes(src, _buffer.Slice(Position));
|
var bytesWritten = encoding.GetBytes(src, _buffer.Slice(Position));
|
||||||
|
Position += bytesWritten;
|
||||||
|
|
||||||
|
if (fixedLength > -1)
|
||||||
|
{
|
||||||
|
var extra = fixedLength * sizeT - bytesWritten;
|
||||||
|
if (extra > 0)
|
||||||
|
{
|
||||||
|
Clear(extra);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
/*************************************************************************
|
|
||||||
* ModernUO *
|
|
||||||
* Copyright 2019-2020 - ModernUO Development Team *
|
|
||||||
* Email: hi@modernuo.com *
|
|
||||||
* File: IPacketEncoder.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/>. *
|
|
||||||
*************************************************************************/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Server.Network
|
|
||||||
{
|
|
||||||
public interface IPacketEncoder
|
|
||||||
{
|
|
||||||
void EncodeOutgoingPacket(NetState to, ref Memory<byte> seq);
|
|
||||||
void DecodeIncomingPacket(NetState from, ref Memory<byte> seq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -33,6 +33,8 @@ namespace Server.Network
|
||||||
{
|
{
|
||||||
public delegate void NetStateCreatedCallback(NetState ns);
|
public delegate void NetStateCreatedCallback(NetState ns);
|
||||||
|
|
||||||
|
public delegate void EncodePacket(ReadOnlySpan<byte> inputBuffer, CircularBufferWriter outputBuffer);
|
||||||
|
|
||||||
public partial class NetState : IComparable<NetState>
|
public partial class NetState : IComparable<NetState>
|
||||||
{
|
{
|
||||||
private static int IncomingPipeSize = 1024 * 64;
|
private static int IncomingPipeSize = 1024 * 64;
|
||||||
|
|
@ -55,6 +57,9 @@ namespace Server.Network
|
||||||
private Pipe<byte> m_OutgoingPipe;
|
private Pipe<byte> m_OutgoingPipe;
|
||||||
private long m_NextCheckActivity;
|
private long m_NextCheckActivity;
|
||||||
private volatile bool m_Running;
|
private volatile bool m_Running;
|
||||||
|
private Thread _sendThread;
|
||||||
|
private volatile EncodePacket _packetDecoder;
|
||||||
|
private volatile EncodePacket _packetEncoder;
|
||||||
|
|
||||||
internal int m_AuthID;
|
internal int m_AuthID;
|
||||||
internal int m_Seed;
|
internal int m_Seed;
|
||||||
|
|
@ -74,7 +79,7 @@ namespace Server.Network
|
||||||
Timer.DelayCall(checkAliveDuration, checkAliveDuration, CheckAllAlive);
|
Timer.DelayCall(checkAliveDuration, checkAliveDuration, CheckAllAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetState(Socket connection)
|
public NetState(Socket connection, Thread sendThread = null)
|
||||||
{
|
{
|
||||||
m_Running = false;
|
m_Running = false;
|
||||||
Connection = connection;
|
Connection = connection;
|
||||||
|
|
@ -88,6 +93,7 @@ namespace Server.Network
|
||||||
m_OutgoingBuffer = new byte[OutgoingPipeSize];
|
m_OutgoingBuffer = new byte[OutgoingPipeSize];
|
||||||
m_OutgoingPipe = new Pipe<byte>(m_OutgoingBuffer);
|
m_OutgoingPipe = new Pipe<byte>(m_OutgoingBuffer);
|
||||||
m_NextCheckActivity = Core.TickCount + 30000;
|
m_NextCheckActivity = Core.TickCount + 30000;
|
||||||
|
_sendThread = sendThread ?? Core.Thread;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +120,17 @@ namespace Server.Network
|
||||||
|
|
||||||
public IPAddress Address { get; }
|
public IPAddress Address { get; }
|
||||||
|
|
||||||
public IPacketEncoder PacketEncoder { get; set; }
|
public EncodePacket PacketDecoder
|
||||||
|
{
|
||||||
|
get => _packetDecoder;
|
||||||
|
set => _packetDecoder = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EncodePacket PacketEncoder
|
||||||
|
{
|
||||||
|
get => _packetEncoder;
|
||||||
|
set => _packetEncoder = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool SentFirstPacket { get; set; }
|
public bool SentFirstPacket { get; set; }
|
||||||
|
|
||||||
|
|
@ -354,6 +370,53 @@ namespace Server.Network
|
||||||
NetworkState.Resume(ref m_NetworkState);
|
NetworkState.Resume(ref m_NetworkState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void Send(Span<byte> buffer)
|
||||||
|
{
|
||||||
|
if (Connection == null || BlockAllPackets || buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentThread = Thread.CurrentThread;
|
||||||
|
|
||||||
|
if (currentThread != _sendThread)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Core: Attempted to send packet outside core thread! [{0}]", currentThread.ManagedThreadId);
|
||||||
|
#if DEBUG
|
||||||
|
throw new InvalidThreadException(nameof(Send));
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
TraceException(ex);
|
||||||
|
#endif
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Send(Packet p)
|
public virtual void Send(Packet p)
|
||||||
{
|
{
|
||||||
if (Connection == null || BlockAllPackets)
|
if (Connection == null || BlockAllPackets)
|
||||||
|
|
@ -364,9 +427,9 @@ namespace Server.Network
|
||||||
|
|
||||||
var currentThread = Thread.CurrentThread;
|
var currentThread = Thread.CurrentThread;
|
||||||
|
|
||||||
if (currentThread != Core.Thread)
|
if (currentThread != _sendThread)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Core: Attempted to send packet outside core thread! [{0}]", currentThread.ManagedThreadId);
|
Console.Error.WriteLine("Core: Attempted to send packet outside send thread! [{0}]", currentThread.ManagedThreadId);
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
throw new InvalidThreadException(nameof(Send));
|
throw new InvalidThreadException(nameof(Send));
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -464,11 +527,20 @@ namespace Server.Network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int DecodePacket(ReadOnlySpan<byte> input, ArraySegment<byte>[] output)
|
||||||
|
{
|
||||||
|
var writer = new CircularBufferWriter(output);
|
||||||
|
PacketDecoder(input, writer);
|
||||||
|
return writer.Position;
|
||||||
|
}
|
||||||
|
|
||||||
private async void RecvTask(object state)
|
private async void RecvTask(object state)
|
||||||
{
|
{
|
||||||
var socket = Connection;
|
var socket = Connection;
|
||||||
var writer = m_IncomingPipe.Writer;
|
var writer = m_IncomingPipe.Writer;
|
||||||
|
|
||||||
|
byte[] encodingBuffer = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (m_Running)
|
while (m_Running)
|
||||||
|
|
@ -485,13 +557,32 @@ namespace Server.Network
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = result.Buffer;
|
int bytesWritten;
|
||||||
|
|
||||||
var bytesWritten = await socket.ReceiveAsync(buffer, SocketFlags.None);
|
if (PacketDecoder != null)
|
||||||
|
|
||||||
if (bytesWritten <= 0)
|
|
||||||
{
|
{
|
||||||
break;
|
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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Advance((uint)bytesWritten);
|
writer.Advance((uint)bytesWritten);
|
||||||
|
|
@ -509,6 +600,10 @@ namespace Server.Network
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
if (encodingBuffer != null)
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(encodingBuffer);
|
||||||
|
}
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Server.Network
|
namespace Server.Network
|
||||||
{
|
{
|
||||||
|
|
@ -60,6 +61,66 @@ namespace Server.Network
|
||||||
0x4, 0x00D
|
0x4, 0x00D
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static void Compress(ReadOnlySpan<byte> input, CircularBufferWriter output)
|
||||||
|
{
|
||||||
|
int inputCapacity = input.Length;
|
||||||
|
|
||||||
|
if (inputCapacity > DefiniteOverflow)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitCount = 0;
|
||||||
|
int bitValue = 0;
|
||||||
|
|
||||||
|
int inputIdx = 0;
|
||||||
|
|
||||||
|
while (inputIdx < inputCapacity)
|
||||||
|
{
|
||||||
|
int i = input[inputIdx++] << 1;
|
||||||
|
|
||||||
|
bitCount += _huffmanTable[i];
|
||||||
|
bitValue <<= _huffmanTable[i];
|
||||||
|
bitValue |= _huffmanTable[i + 1];
|
||||||
|
|
||||||
|
while (bitCount >= 8)
|
||||||
|
{
|
||||||
|
bitCount -= 8;
|
||||||
|
|
||||||
|
if (output.Length < output.Position + 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Write((byte)(bitValue >> bitCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminal code
|
||||||
|
bitCount += _huffmanTable[0x200];
|
||||||
|
bitValue <<= _huffmanTable[0x200];
|
||||||
|
bitValue |= _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 < output.Position + 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Write((byte)(bitValue >> bitCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static unsafe void Compress(
|
public static unsafe void Compress(
|
||||||
ReadOnlySpan<byte> input, int offset, int count, Span<byte> output, out int length
|
ReadOnlySpan<byte> input, int offset, int count, Span<byte> output, out int length
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2549,6 +2549,7 @@ namespace Server.Network
|
||||||
{
|
{
|
||||||
state.CityInfo = e.CityInfo;
|
state.CityInfo = e.CityInfo;
|
||||||
state.CompressionEnabled = true;
|
state.CompressionEnabled = true;
|
||||||
|
state.PacketEncoder = NetworkCompression.Compress;
|
||||||
|
|
||||||
state.Send(SupportedFeatures.Instantiate(state));
|
state.Send(SupportedFeatures.Instantiate(state));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue