using System.Diagnostics; using System.Net.Sockets; using System.Text; namespace BSO.Launcher; /// /// Fragt einen BSO-Server direkt ab wie die Mod (wie ServerBrowser.cs bei MSCO): Info senden, InfoReply lesen. Liefert die /// Laufzeit (Ping) und den aktuellen Stand (Spieler) - die Liste der Website ist bis zu einer Minute alt. /// Paketaufbau: [int Laenge][byte Typ][Daten], Info = 9, InfoReply = 10 (string Name, string Version, int Spieler, int max). /// public static class ServerPing { public sealed record Info(int Ping, string Name, string Version, int Players, int MaxPlayers); public static async Task Query(string host, int port, int timeoutMs = 2500) { try { using var tcp = new TcpClient { NoDelay = true }; using var cts = new CancellationTokenSource(timeoutMs); var clock = Stopwatch.StartNew(); await tcp.ConnectAsync(host, port, cts.Token); var stream = tcp.GetStream(); await stream.WriteAsync(new byte[] { 1, 0, 0, 0, 9 }, cts.Token); var head = new byte[5]; await stream.ReadExactlyAsync(head, cts.Token); int ping = (int)clock.ElapsedMilliseconds; int length = BitConverter.ToInt32(head, 0); if (head[4] != 10 || length < 1 || length > 4096) return null; var payload = new byte[length - 1]; await stream.ReadExactlyAsync(payload, cts.Token); using var r = new BinaryReader(new MemoryStream(payload), Encoding.UTF8); return new Info(ping, r.ReadString(), r.ReadString(), r.ReadInt32(), r.ReadInt32()); } catch { return null; } } }