WutheringWaves/GameServer/Network/Kcp/KcpGateway.cs

73 lines
2.4 KiB
C#
Raw Permalink Normal View History

2024-02-07 21:41:39 +00:00
using System.Buffers;
using System.Buffers.Binary;
using System.Net;
using System.Net.Sockets;
using GameServer.Settings;
2024-02-07 21:41:39 +00:00
using KcpSharp;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
2024-02-07 21:41:39 +00:00
2024-02-09 09:44:42 +00:00
namespace GameServer.Network.Kcp;
2024-02-07 21:41:39 +00:00
internal class KcpGateway
{
private const int KcpSynPacketSize = 1;
private const int KcpAckPacketSize = 5;
private const byte NetFlagSyn = 0xED;
private const byte NetFlagAck = 0xEE;
private readonly ILogger _logger;
private readonly IOptions<GatewaySettings> _options;
2024-02-07 21:41:39 +00:00
private readonly SessionManager _sessionManager;
private IKcpTransport<IKcpMultiplexConnection>? _kcpTransport;
private int _convCounter;
public KcpGateway(ILogger<KcpGateway> logger, IOptions<GatewaySettings> options, SessionManager sessionManager)
2024-02-07 21:41:39 +00:00
{
_logger = logger;
_options = options;
2024-02-07 21:41:39 +00:00
_sessionManager = sessionManager;
}
public void Start()
{
IPEndPoint endPoint = _options.Value.EndPoint;
2024-02-07 21:41:39 +00:00
_kcpTransport = KcpSocketTransport.CreateMultiplexConnection(new(endPoint), 1400);
_kcpTransport.SetHandshakeHandler(KcpSynPacketSize, HandleKcpSynPacket);
_kcpTransport.Start();
_logger.LogInformation("Listening for incoming connections at {endPoint}", endPoint);
}
private async ValueTask HandleKcpSynPacket(UdpReceiveResult recvResult)
{
if (recvResult.Buffer[0] != NetFlagSyn) return;
_logger.LogInformation("Received SYN from {remoteEndPoint}", recvResult.RemoteEndPoint);
int conv = Interlocked.Increment(ref _convCounter);
_ = _sessionManager.RunSessionAsync(_kcpTransport!.Connection.CreateConversation(conv, recvResult.RemoteEndPoint));
await SendAckAsync(conv, recvResult.RemoteEndPoint);
}
private async ValueTask SendAckAsync(int conv, IPEndPoint remoteEndPoint)
{
_logger.LogInformation("Sending ACK to {remoteEndPoint}, convID: {conv}", remoteEndPoint, conv);
byte[] buffer = ArrayPool<byte>.Shared.Rent(KcpAckPacketSize);
try
{
buffer[0] = NetFlagAck;
BinaryPrimitives.WriteInt32LittleEndian(buffer.AsSpan(1), conv);
await _kcpTransport!.SendPacketAsync(buffer.AsMemory(0, KcpAckPacketSize), remoteEndPoint, CancellationToken.None);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
}