namespace Supercell.Laser.Server.Networking { using Supercell.Laser.Server.Networking.UDP.Game; using Supercell.Laser.Titan.DataStream; using System.Net; using System.Net.Sockets; public static class UDPGateway { private const int SioUdpConnReset = -1744830452; private const int UdpReceiveBufferSize = 1500; private const int UdpHeaderLength = 10; private static Socket Socket; private static Thread Thread; private static long SessionCounter; private static Dictionary Sockets; private static ManualResetEvent ReceiveEvent; public static void Init(string host, int port) { Sockets = new Dictionary(); Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); ConfigureSocket(); Socket.Bind(new IPEndPoint(IPAddress.Parse(host), port)); ReceiveEvent = new ManualResetEvent(false); Thread = new Thread(Update); Thread.Start(); Logger.Print($"UDP Server started at {host}:{port}"); } private static void ReceiveCallback(IAsyncResult result) { ReceiveEvent.Set(); State state = (State)result.AsyncState; byte[] buffer = state.Buffer; EndPoint remoteEndPoint = state.RemoteEndPoint; int r = 0; try { r = Socket.EndReceiveFrom(result, ref remoteEndPoint); } catch (SocketException exception) when (IsExpectedUdpReset(exception)) { Logger.Warning($"UDP peer reset ignored from {remoteEndPoint}: {exception.SocketErrorCode}"); return; } catch (Exception exception) { Logger.Exception("UDP receive failed", exception); return; } if (r < UdpHeaderLength) return; try { ByteStream stream = new ByteStream(buffer, r); long sessionId = stream.ReadLong(); stream.ReadShort(); if (!Sockets.ContainsKey(sessionId)) return; UDPSocket client = Sockets[sessionId]; if (client.IsConnected && !Equals(client.EndPoint, remoteEndPoint)) { Logger.Warning($"UDP endpoint mismatch for session {sessionId}: expected={client.EndPoint}, got={remoteEndPoint}"); return; } if (!client.IsConnected) { client.SetEndPoint(remoteEndPoint); if (client.Battle == null) return; if (!client.IsSpectator) { client.Battle.GetPlayerBySessionId(client.SessionId).GameListener = new UDPGameListener(client, client.TCPConnection); } } client.ProcessReceive(stream); } catch (Exception exception) { Logger.Exception("UDP packet processing failed", exception); } } private class State { public byte[] Buffer; public EndPoint RemoteEndPoint; } private static void Update() { while (true) { try { ReceiveEvent.Reset(); EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); byte[] buffer = new byte[UdpReceiveBufferSize]; State state = new State() { RemoteEndPoint = remoteEndPoint, Buffer = buffer }; Socket.BeginReceiveFrom(buffer, 0, UdpReceiveBufferSize, SocketFlags.None, ref remoteEndPoint, new AsyncCallback(ReceiveCallback), state); ReceiveEvent.WaitOne(); } catch (Exception exception) { Logger.Exception("UDP gateway update loop failed", exception); } } } public static UDPSocket CreateSocket() { UDPSocket socket = new UDPSocket(++SessionCounter); Sockets.Add(socket.SessionId, socket); return socket; } public static void SendTo(byte[] data, int startIndex, int count, EndPoint remoteEndPoint) { try { if (remoteEndPoint == null) return; Socket.SendTo(data, startIndex, count, SocketFlags.None, remoteEndPoint); } catch (SocketException) { } } private static void ConfigureSocket() { if (!OperatingSystem.IsWindows()) { return; } try { Socket.IOControl((IOControlCode)SioUdpConnReset, new byte[] { 0 }, null); } catch (NotSupportedException) { Logger.Warning("Failed to disable UDP_CONNRESET: operation is not supported on this platform."); } catch (SocketException exception) { Logger.Warning($"Failed to disable UDP_CONNRESET: {exception.SocketErrorCode}"); } } private static bool IsExpectedUdpReset(SocketException exception) { return OperatingSystem.IsWindows() && (exception.SocketErrorCode == SocketError.ConnectionReset || exception.ErrorCode == 10054); } } }