2024-02-07 21:41:39 +00:00
|
|
|
|
using System.Buffers;
|
|
|
|
|
using System.Buffers.Binary;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
2024-02-10 19:11:16 +00:00
|
|
|
|
using GameServer.Settings;
|
2024-02-07 21:41:39 +00:00
|
|
|
|
using KcpSharp;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2024-02-10 19:11:16 +00:00
|
|
|
|
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;
|
2024-02-10 19:11:16 +00:00
|
|
|
|
private readonly IOptions<GatewaySettings> _options;
|
2024-02-07 21:41:39 +00:00
|
|
|
|
private readonly SessionManager _sessionManager;
|
|
|
|
|
|
|
|
|
|
private IKcpTransport<IKcpMultiplexConnection>? _kcpTransport;
|
|
|
|
|
private int _convCounter;
|
|
|
|
|
|
2024-02-10 19:11:16 +00:00
|
|
|
|
public KcpGateway(ILogger<KcpGateway> logger, IOptions<GatewaySettings> options, SessionManager sessionManager)
|
2024-02-07 21:41:39 +00:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
2024-02-10 19:11:16 +00:00
|
|
|
|
_options = options;
|
2024-02-07 21:41:39 +00:00
|
|
|
|
_sessionManager = sessionManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
2024-02-10 19:11:16 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|