- Seite Server: Liste der Website, Ping/Spieler direkt abgefragt, BEITRETEN und Direkt verbinden - Holt beim Spielen ein Spiel-Ticket und schreibt es in BSO.json Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
39 lines
1.7 KiB
C#
39 lines
1.7 KiB
C#
using System.Diagnostics;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
|
|
namespace BSO.Launcher;
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
public static class ServerPing
|
|
{
|
|
public sealed record Info(int Ping, string Name, string Version, int Players, int MaxPlayers);
|
|
|
|
public static async Task<Info?> 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; }
|
|
}
|
|
}
|