using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; namespace CommonwealthOnline.Host.Services; // Speaks the server's token-authenticated admin protocol on 127.0.0.1:AdminPort: // read .admin-token, send {..,"adminToken"}\n, read one newline-terminated JSON reply. public sealed class AdminClient { private const int MaxResponseBytes = 1_000_000; private readonly int _port; private readonly string _tokenPath; public AdminClient(int adminPort, string tokenPath) { _port = adminPort; _tokenPath = tokenPath; } public async Task SendAsync(JsonObject request, CancellationToken ct = default) { var token = (await File.ReadAllTextAsync(_tokenPath, ct).ConfigureAwait(false)).Trim(); var authenticated = (JsonObject)request.DeepClone(); authenticated["adminToken"] = token; var payload = JsonSerializer.SerializeToUtf8Bytes(authenticated); using var client = new TcpClient(); await client.ConnectAsync(IPAddress.Loopback, _port, ct).ConfigureAwait(false); var stream = client.GetStream(); await stream.WriteAsync(payload, ct).ConfigureAwait(false); await stream.WriteAsync(new byte[] { (byte)'\n' }, ct).ConfigureAwait(false); using var buffer = new MemoryStream(); var one = new byte[1]; while (buffer.Length < MaxResponseBytes) { var read = await stream.ReadAsync(one, ct).ConfigureAwait(false); if (read == 0 || one[0] == (byte)'\n') { break; } buffer.WriteByte(one[0]); } return JsonNode.Parse(buffer.ToArray()) as JsonObject; } public Task StatusAsync(CancellationToken ct = default) => SendAsync(new JsonObject { ["cmd"] = "status" }, ct); public Task ClientsAsync(CancellationToken ct = default) => SendAsync(new JsonObject { ["cmd"] = "clients" }, ct); public Task KickAsync(uint playerId, string reason, CancellationToken ct = default) => SendAsync(new JsonObject { ["cmd"] = "kick", ["playerId"] = playerId, ["reason"] = reason }, ct); public Task BanAsync(uint playerId, string reason, CancellationToken ct = default) => SendAsync(new JsonObject { ["cmd"] = "ban", ["playerId"] = playerId, ["reason"] = reason }, ct); }