using System.Text.Json; using System.Text.Json.Serialization; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Cvars; using CounterStrikeSharp.API.Modules.Utils; namespace WebPanelBridge; // Reports live match state to simpleadmin-web over RCON. The server console command prints one // JSON object per line, each prefixed with "wpb ", so the web panel can pick them out of whatever // else lands in the RCON response. Nothing here changes game state. public class WebPanelBridge : BasePlugin { public override string ModuleName => "WebPanelBridge"; public override string ModuleVersion => "0.1.0"; public override string ModuleAuthor => "astra"; public override string ModuleDescription => "Live server status for simpleadmin-web, over RCON"; private const string Prefix = "wpb "; private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.Never, }; private record ServerLine(int V, string Hostname, string Map, int MaxPlayers, int ScoreT, int ScoreCt, bool Warmup); private record PlayerLine(int Userid, string Name, string Steamid, int Team, int Kills, int Deaths, uint Ping, bool Bot); [ConsoleCommand("css_webpanel_status", "Prints live server status as JSON lines for simpleadmin-web")] [CommandHelper(whoCanExecute: CommandUsage.SERVER_ONLY)] public void OnStatus(CCSPlayerController? caller, CommandInfo command) { int scoreT = 0, scoreCt = 0; foreach (var team in Utilities.FindAllEntitiesByDesignerName("cs_team_manager")) { if (team.TeamNum == (byte)CsTeam.Terrorist) scoreT = team.Score; else if (team.TeamNum == (byte)CsTeam.CounterTerrorist) scoreCt = team.Score; } var warmup = Utilities.FindAllEntitiesByDesignerName("cs_gamerules") .FirstOrDefault()?.GameRules?.WarmupPeriod ?? false; var maxPlayers = Server.MaxPlayers; var visible = ConVar.Find("sv_visiblemaxplayers")?.GetPrimitiveValue() ?? -1; if (visible > 0) maxPlayers = visible; var server = new ServerLine(1, ConVar.Find("hostname")?.StringValue ?? "", Server.MapName, maxPlayers, scoreT, scoreCt, warmup); command.ReplyToCommand(Prefix + JsonSerializer.Serialize(server, JsonOptions)); foreach (var player in Utilities.GetPlayers()) { if (player is not { IsValid: true, IsHLTV: false } || player.Connected != PlayerConnectedState.Connected) continue; var stats = player.ActionTrackingServices?.MatchStats; var line = new PlayerLine( player.UserId ?? -1, player.PlayerName, player.IsBot ? "0" : player.SteamID.ToString(), player.TeamNum, stats?.Kills ?? 0, stats?.Deaths ?? 0, player.Ping, player.IsBot); command.ReplyToCommand(Prefix + JsonSerializer.Serialize(line, JsonOptions)); } command.ReplyToCommand(Prefix + "end"); } }