using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using RainOpsMini; using RainOpsMini.Models; using RainOpsMini.Helpers.SquadGameLog; using RainOpsMini; // Ensure Program is visible namespace RainOpsMini.Helpers.RCON { public class RconPollingService : BackgroundService { private readonly RconDataCache _cache; public RconPollingService(RconDataCache cache) { _cache = cache; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { RainOpsMini.Helpers.RainOpsLog.Log("RCON Polling Service started."); while (!stoppingToken.IsCancellationRequested) { int delaySeconds = _cache.RefreshIntervalSeconds; try { if (Program.Client != null && Program.Client.IsConnected) { await UpdateRconDataAsync(); } } catch (Exception ex) { RainOpsMini.Helpers.RainOpsLog.Log($"[RconPollingService] Error: {ex.Message}"); } await Task.Delay(TimeSpan.FromSeconds(delaySeconds), stoppingToken); } } private async Task UpdateRconDataAsync() { var client = Program.Client; if (client == null) return; try { // 1. Fetch Server Info string serverInfoRaw = await client.SendCommandAsync("ShowServerInfo"); var serverInfo = RconParser.ParseServerInfo(serverInfoRaw); // 2. Fetch Squads string squadsRaw = await client.SendCommandAsync("ListSquads"); var squads = RconParser.ParseSquads(squadsRaw); // 3. Fetch Players (with retry logic similar to original API) List players = new List(); int retryCount = 0; const int maxRetries = 3; int expectedPlayerCount = 0; if (serverInfo != null && int.TryParse(serverInfo.PlayerCount_I, out int pc)) { expectedPlayerCount = pc; } while (retryCount < maxRetries) { string playersRaw = await client.SendCommandAsync("ListPlayers"); var currentPlayers = RconParser.ParsePlayers(playersRaw); if (currentPlayers.Count > players.Count) { players = currentPlayers; } if (expectedPlayerCount > 0) { if (players.Count >= expectedPlayerCount * 0.8) { break; } } else { if (players.Count > 0) break; } if (retryCount < maxRetries - 1) { await Task.Delay(500); } retryCount++; } // 4. Enrich Data foreach (var squad in squads) { if (!string.IsNullOrEmpty(squad.CreatorSteamId)) { var creationTime = LogStorage.Instance.GetLastSquadCreationTime(squad.CreatorSteamId); if (creationTime.HasValue) { squad.LastCreatedTime = creationTime.Value.ToString("yyyy-MM-dd HH:mm:ss"); } } } DateTime matchStartTime = DateTime.MinValue; if (serverInfo != null && serverInfo.PlayTime_I > 0) { matchStartTime = DateTime.Now.AddSeconds(-serverInfo.PlayTime_I - 60); } foreach (var player in players) { LogStorage.Instance.RegisterPlayer(player.Name, player.SteamId); var stats = LogStorage.Instance.GetPlayerStats(player.SteamId, matchStartTime); player.Kills = stats.Kills; player.Deaths = stats.Deaths; } // 5. Update Cache _cache.UpdateData(serverInfo, squads, players); // 6. Async Update Steam Playtime _ = Task.Run(async () => { try { var cachedTimes = _cache.GetGameTimes(); foreach (var p in players) { if (!string.IsNullOrEmpty(p.SteamId)) { // If not in cache, or value is 0 (maybe failed previously), update it. // You might also want to re-check periodically, but for now let's stick to missing/zero. if (!cachedTimes.ContainsKey(p.SteamId) || cachedTimes[p.SteamId] <= 0) { int minutes = await SteamHelper.GetSquadPlaytimeAsync(p.SteamId); if (minutes > 0) { _cache.UpdateGameTime(p.SteamId, minutes); } await Task.Delay(200); // Rate limit } } } } catch (Exception ex) { RainOpsMini.Helpers.RainOpsLog.Log($"[RconPollingService] Playtime update failed: {ex.Message}"); } }); } catch (Exception ex) { RainOpsMini.Helpers.RainOpsLog.Log($"[RconPollingService] Update failed: {ex.Message}"); } } } }