using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using RainOpsMini.Helpers; using RainOpsMini.Helpers.RCON; using RainOpsMini.Models; namespace RainOpsMini.Services { public class OnlineUserService { // Panel Users: Username -> ConnectionCount private ConcurrentDictionary _panelUsers = new ConcurrentDictionary(); private readonly RconDataCache _rconCache; public OnlineUserService(RconDataCache rconCache) { _rconCache = rconCache; } public void UserConnected(string username) { if (string.IsNullOrEmpty(username)) return; _panelUsers.AddOrUpdate(username, 1, (k, v) => v + 1); } public void UserDisconnected(string username) { if (string.IsNullOrEmpty(username)) return; _panelUsers.AddOrUpdate(username, 0, (k, v) => v > 1 ? v - 1 : 0); // Clean up zeros if (_panelUsers.TryGetValue(username, out int count) && count <= 0) { _panelUsers.TryRemove(username, out _); } } public List GetOnlinePanelUsers() { return _panelUsers.Where(kvp => kvp.Value > 0).Select(kvp => kvp.Key).ToList(); } public List GetOnlineCameramen() { // Priority 1: Check Current Directory (likely Project Root in Dev, or App Root in Prod) var configPath = Path.Combine(Directory.GetCurrentDirectory(), "SquadGame", "ServerConfig", "Admins.cfg"); // Priority 2: Check Base Directory (Bin folder) if (!File.Exists(configPath)) { configPath = Path.Combine(Environment.CurrentDirectory, "SquadGame", "ServerConfig", "Admins.cfg"); } if (!File.Exists(configPath)) return new List(); // RainOpsMini.Helpers.RainOpsLog.Log($"Loading Admins.cfg from: {configPath}"); var groupsWithCameraman = new HashSet(StringComparer.OrdinalIgnoreCase); var onlineCameramen = new List(); try { var lines = File.ReadAllLines(configPath); // 1. Identify Groups with 'cameraman' permission foreach (var line in lines) { var trimLine = line.Trim(); if (string.IsNullOrWhiteSpace(trimLine) || trimLine.StartsWith("//") || trimLine.StartsWith("#")) continue; if (trimLine.StartsWith("Group=", StringComparison.OrdinalIgnoreCase)) { // Format: Group=GroupName:perm1,perm2... var parts = trimLine.Substring(6).Split(':'); if (parts.Length >= 2) { var groupName = parts[0].Trim(); var perms = parts[1].Split(',').Select(p => p.Trim().ToLower()).ToHashSet(); if (perms.Contains("cameraman")) { groupsWithCameraman.Add(groupName); } } } } // 2. Identify Admins in those groups var definedCameramen = new List(); foreach (var line in lines) { var trimLine = line.Trim(); if (string.IsNullOrWhiteSpace(trimLine) || trimLine.StartsWith("//") || trimLine.StartsWith("#")) continue; if (trimLine.StartsWith("Admin=", StringComparison.OrdinalIgnoreCase)) { // Format: Admin=SteamID:GroupName//Comment var content = trimLine.Substring(6); var parts = content.Split(':'); if (parts.Length >= 2) { var steamId = parts[0].Trim(); var rest = parts[1]; // Split GroupName and Comment string groupName; string comment = ""; int commentIdx = rest.IndexOf("//"); if (commentIdx >= 0) { groupName = rest.Substring(0, commentIdx).Trim(); comment = rest.Substring(commentIdx + 2).Trim(); } else { groupName = rest.Trim(); } if (groupsWithCameraman.Contains(groupName)) { definedCameramen.Add(new CameramanInfo { SteamId = steamId, Name = comment, // Use comment as name GroupName = groupName }); } } } } // 3. Check online status // We use cached player list var currentPlayers = _rconCache.Players ?? new List(); // Safe dictionary creation to avoid duplicate keys exception var onlineSteamIds = new Dictionary(); foreach (var p in currentPlayers) { if (!string.IsNullOrEmpty(p.SteamId) && !onlineSteamIds.ContainsKey(p.SteamId)) { onlineSteamIds[p.SteamId] = p.Name; } } foreach (var cam in definedCameramen) { if (onlineSteamIds.ContainsKey(cam.SteamId)) { cam.IsOnline = true; // Optionally update name from actual player name if available // cam.Name = $"{cam.Name} ({onlineSteamIds[cam.SteamId]})"; onlineCameramen.Add(cam); } } } catch (Exception ex) { RainOpsLog.Log($"Error parsing Admins.cfg: {ex.Message}"); } return onlineCameramen; } } public class CameramanInfo { public string SteamId { get; set; } = ""; public string Name { get; set; } = ""; public string GroupName { get; set; } = ""; public bool IsOnline { get; set; } } }