1
0
mirror of synced 2024-11-12 00:40:51 +01:00
GC-local-server-rewrite/GCRelayServer/RelayServer.cs
2022-08-01 21:39:24 +08:00

160 lines
5.0 KiB
C#

using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using BinarySerialization;
using Swan.Logging;
namespace GCRelayServer;
public class RelayServer : NetCoreServer.UdpServer
{
private ConcurrentDictionary<uint, DictEntry> matchingDictionary = new();
public RelayServer(IPAddress address, int port) : base(address, port) {}
protected override void OnStarted()
{
// Start receive datagrams
ReceiveAsync();
}
protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size)
{
var serializer = new BinarySerializer();
var inputStream = new MemoryStream(buffer, (int)offset, (int)size, false);
var packet = serializer.Deserialize<RelayPacket>(inputStream);
if (!IsValidPacket(packet))
{
"Received malformed packet!".Warn();
return;
}
$"Received packet from {endpoint}, type is 0x{packet.RequestSubType:X2}".Info();
switch (packet.RequestSubType)
{
case RelayPacketTypes.HEART_BEAT:
{
for (var i = 0; i < 2; i++)
{
SendPacketSingle(endpoint, packet, RelayPacketTypes.HEART_BEAT_RESPONSE);
}
break;
}
case RelayPacketTypes.START_MATCHING:
{
AddEntry(packet.MatchingId, endpoint);
for (var i = 0; i < 2; i++)
{
SendPacketSingle(endpoint, packet, RelayPacketTypes.START_MATCHING_RESPONSE);
}
break;
}
case RelayPacketTypes.REGISTER_MATCHING:
{
var entry = AddEntry(packet.MatchingId, endpoint);
SendPacketToOthers(entry.EndPoints, packet, endpoint);
break;
}
default:
{
var entry = GetEntry(packet.MatchingId);
if (entry is null)
{
break;
}
SendPacketToOthers(entry.EndPoints, packet, endpoint);
break;
}
}
ReceiveAsync();
}
private void SendPacketSingle(EndPoint endpoint, RelayPacket packet, ushort subType)
{
packet.RequestSubType = subType;
var serializer = new BinarySerializer();
var sendStream = new MemoryStream(1024);
serializer.Serialize(sendStream, packet);
$"Send packet to {endpoint}, type is 0x{packet.RequestSubType:X2}".Info();
SendAsync(endpoint, sendStream.GetBuffer(), 0, sendStream.Length);
}
private void SendPacketToOthers(IEnumerable<EndPoint> endPoints, RelayPacket packet, EndPoint owner)
{
if (owner is not IPEndPoint ipEndPoint)
{
"Endpoint is not IP endpoint! This should not happen!".Fatal();
throw new ApplicationException();
}
foreach (var endPoint in endPoints.Where(endPoint => !ipEndPoint.Equals(endPoint)))
{
SendPacketSingle(endPoint, packet, packet.RequestSubType);
}
}
protected override void OnError(SocketError error)
{
$"Relay server caught an error with code {error}".Error();
}
private static bool IsValidPacket(RelayPacket packet)
{
var totalSize = packet.Magic + packet.RemainingSize;
var actualSize = 36 + packet.Data.Length;
if (totalSize != actualSize)
{
return false;
}
return packet.Data.Length == packet.DataSize;
}
private DictEntry AddEntry(uint matchingId, EndPoint endPoint)
{
var now = DateTime.Now;
var entry = matchingDictionary.GetValueOrDefault(matchingId, new DictEntry());
var shouldClear = false;
if (entry.LastAccessTime <= now && now - entry.LastAccessTime >= TimeSpan.FromMinutes(10))
{
$"Entry for matching id {matchingId:X8} has expired! Clients will be cleared!".Info();
shouldClear = true;
}
if (entry.EndPoints.Count >= 4)
{
$"Entry for matching id {matchingId:X8} contains more than 4 clients! Clients will be cleared!".Warn();
shouldClear = true;
}
entry.AddEndpoint(endPoint, shouldClear);
entry.LastAccessTime = DateTime.Now;
matchingDictionary[matchingId] = entry;
return entry;
}
private DictEntry? GetEntry(uint matchingId)
{
var now = DateTime.Now;
if (!matchingDictionary.ContainsKey(matchingId))
{
$"Entry for matching id {matchingId:X8} does not exist!".Warn();
return null;
}
var entry = matchingDictionary[matchingId];
if (entry.LastAccessTime <= now && now - entry.LastAccessTime >= TimeSpan.FromMinutes(10))
{
$"Entry for matching id {matchingId:X8} has expired!".Warn();
return null;
}
entry.LastAccessTime = DateTime.Now;
return entry;
}
}