using System.Threading; using System.Threading.Tasks; using System.Net.Http; using System.Net.Http.Json; using System.Net.Http.Headers; using Content.Shared.CCVar; using Robust.Shared.Configuration; using JetBrains.Annotations; namespace Content.Server._NF.Auth; public sealed class MiniAuthManager { [Dependency] private readonly IConfigurationManager _cfg = default!; private readonly HttpClient _http = new(); /// /// Frontier function to ping a server and check to see if the given player is currently connected to the given server. /// Servers using this function must share an admin_api token as defined in their respective server_config.toml /// /// The address of the server to ping. /// the GUID of the player to check for connection. /// True if the response from the server is successful and the player is connected. False in any case of error, timeout, or failure. public async Task IsPlayerConnected(string address, Guid player) { var connected = false; var statusAddress = "http://" + address + "/admin/info"; var cancel = new CancellationToken(); var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancel); linkedToken.CancelAfter(TimeSpan.FromSeconds(10)); _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SS14Token", _cfg.GetCVar(CCVars.AdminApiToken)); //We need to do a try catch here because theres essentially no way to guarantee our json response is proper. //Throughout all of this, we want it to fail to deny, not fail to allow, so if any step of our auth goes wrong, //people can still connect. try { var status = await _http.GetFromJsonAsync(statusAddress, linkedToken.Token); foreach (var connectedPlayer in status!.Players) { if (connectedPlayer.UserId == player) { connected = true; break; } } } catch (Exception) { } return connected; } /// /// Record used to send the response for the info endpoint. /// Frontier - This is a direct copy of ServerAPI.InfoResponse to match the json format. they kept it private so i just copied it /// [UsedImplicitly] private sealed record InfoResponse { public required int RoundId { get; init; } public required List Players { get; init; } public required List GameRules { get; init; } public required string? GamePreset { get; init; } public required MapInfo? Map { get; init; } public required string? MOTD { get; init; } public required Dictionary PanicBunker { get; init; } public sealed class Player { public required Guid UserId { get; init; } public required string Name { get; init; } public required bool IsAdmin { get; init; } public required bool IsDeadminned { get; init; } } public sealed class MapInfo { public required string Id { get; init; } public required string Name { get; init; } } } }