using System.Text.RegularExpressions; using RainOpsMini.Models; using System.Text.Json; using System.Text.Json.Serialization; using RainOpsMini.Helpers.RCON; namespace RainOpsMini { public static class RconParser { // Team ID: 1 (1st Infantry Division) private static readonly Regex TeamHeaderRegex = new Regex(@"Team ID:\s*(\d+)\s*\(", RegexOptions.Compiled); // ID: 1 | Name: Squad 1 | Size: 5 | Locked: False | Creator Name: lanyu | Creator Online IDs: EOS: 00020148e4944a0a9ca3ddd4c703b5e0 steam: 76561198296558085 private static readonly Regex SquadRegex = new Regex(@"ID:\s*(\d+)\s*\|\s*Name:\s*(.+?)\s*\|\s*Size:\s*(\d+)\s*\|\s*Locked:\s*(\w+)\s*\|\s*Creator Name:\s*(.+?)\s*\|\s*Creator Online IDs:\s*EOS:\s*(\w+)\s*steam:\s*(\d+)", RegexOptions.Compiled); // ID: 40 | Online IDs: EOS: 00026c77c7fc494fa0ab3a337382826f steam: 76561199065911668 | Name: ANDRUHA | Team ID: 1 | Squad ID: N/A | Is Leader: False | Role: USA_Rifleman_01 private static readonly Regex PlayerRegex = new Regex(@"ID:\s*(\d+)\s*\|\s*Online IDs:\s*EOS:\s*(\w+)\s*steam:\s*(\d+)\s*\|\s*Name:\s*(.+?)\s*\|\s*Team ID:\s*(\d+)\s*\|\s*Squad ID:\s*(.+?)\s*\|\s*Is Leader:\s*(\w+)\s*\|\s*Role:\s*(.+)", RegexOptions.Compiled); // [ChatAll] [Online IDs:EOS: 0002236691c54d00b82bcf1b04a27761 steam: 76561199735605536] [hina] 澶ф : cj // [ChatAll] [Online IDs:EOS: 00026937662e43e593caf18bebdbeb2a steam: 76561199402993315] Rain鍦ㄤ笅鐝?: 00 // Improved Regex to handle both [Name] Message and Name : Message formats private static readonly Regex ChatRegex = new Regex(@"\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin|ChatGame)\]\s*\[Online IDs:?\s*EOS:\s*(\w+)\s*steam:\s*(\d+)\]\s*(?:\[(.+?)\]\s*(.*)|(.+?)\s*:\s*(.*))", RegexOptions.Compiled); public static ChatMessage? ParseChatMessage(string rconOutput) { if (string.IsNullOrWhiteSpace(rconOutput)) return null; var match = ChatRegex.Match(rconOutput); if (match.Success) { // Group 4/5 are for [Name] Message format // Group 6/7 are for Name : Message format string name = match.Groups[4].Success ? match.Groups[4].Value : match.Groups[6].Value; string message = match.Groups[4].Success ? match.Groups[5].Value : match.Groups[7].Value; return new ChatMessage { Type = match.Groups[1].Value, EosId = match.Groups[2].Value, SteamId = match.Groups[3].Value, Name = name?.Trim() ?? "Unknown", Message = message?.Trim() ?? "", Timestamp = DateTime.Now }; } return null; } public static ServerInfoResponse? ParseServerInfo(string rconOutput) { if (string.IsNullOrWhiteSpace(rconOutput)) return null; // ShowServerInfo might return raw JSON string, sometimes wrapped in quotes or with other text // We'll try to find the JSON object part: starts with { and ends with } int startIndex = rconOutput.IndexOf('{'); int endIndex = rconOutput.LastIndexOf('}'); if (startIndex >= 0 && endIndex > startIndex) { string json = rconOutput.Substring(startIndex, endIndex - startIndex + 1); try { var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, NumberHandling = JsonNumberHandling.AllowReadingFromString }; var info = JsonSerializer.Deserialize(json, options); // Fallback for PlayTime_I if not in JSON (it is usually there, but just in case) // Actually standard ShowServerInfo returns JSON with PLAYTIME_I field. return info; } catch (Exception ex) { RainOpsMini.Helpers.RainOpsLog.Log($"[Error] Failed to parse ServerInfo JSON: {ex.Message}"); // Optional: Print json snippet for debugging // RainOpsMini.Helpers.RainOpsLog.Log($"[Debug] JSON: {json}"); return null; } } return null; } public static List ParseSquads(string rconOutput) { var squads = new List(); if (string.IsNullOrWhiteSpace(rconOutput)) return squads; var lines = rconOutput.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); int currentTeamId = 0; foreach (var line in lines) { var teamMatch = TeamHeaderRegex.Match(line); if (teamMatch.Success) { if (int.TryParse(teamMatch.Groups[1].Value, out int tid)) { currentTeamId = tid; } continue; } var squadMatch = SquadRegex.Match(line); if (squadMatch.Success) { var squad = new SquadInfo { TeamId = currentTeamId, Id = int.Parse(squadMatch.Groups[1].Value), Name = squadMatch.Groups[2].Value.Trim(), Size = int.Parse(squadMatch.Groups[3].Value), Locked = bool.Parse(squadMatch.Groups[4].Value), CreatorName = squadMatch.Groups[5].Value.Trim(), CreatorEosId = squadMatch.Groups[6].Value, CreatorSteamId = squadMatch.Groups[7].Value }; squads.Add(squad); } } return squads; } public static List ParsePlayers(string rconOutput) { var players = new List(); if (string.IsNullOrWhiteSpace(rconOutput)) return players; var lines = rconOutput.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var match = PlayerRegex.Match(line); if (match.Success) { var player = new PlayerInfo { Id = int.Parse(match.Groups[1].Value), EosId = match.Groups[2].Value, SteamId = match.Groups[3].Value, Name = match.Groups[4].Value.Trim(), TeamId = int.Parse(match.Groups[5].Value), IsLeader = bool.Parse(match.Groups[7].Value), Role = match.Groups[8].Value.Trim() }; string squadIdStr = match.Groups[6].Value.Trim(); if (squadIdStr != "N/A" && int.TryParse(squadIdStr, out int sid)) { player.SquadId = sid; } players.Add(player); } } return players; } } }