using System.Management.Automation.Language; using LANCommander.SDK; using System.Timers; using Microsoft.Extensions.Logging; using Timer = System.Timers.Timer; namespace LANCommander.Launcher.Services; public class KeepAliveService : BaseService { private readonly AuthenticationService AuthenticationService; private Timer? CheckConnectionTimer; private Timer? RetryConnectionTimer; private int PingInterval = 2000; private int RetryInterval = 1000; private int RetryCount; private int MaxRetries = 10; private bool ConnectionLost = false; private bool IsCheckConnectionActive = false; private bool IsRetryConnectionActive = false; private Models.ConnectionState ConnectionState = new(); public event EventHandler ConnectionSevered; public event EventHandler ConnectionRetryNext; public event EventHandler ConnectionLostPermanently; public event EventHandler ConnectionEstablished; public KeepAliveService( Client client, ILogger logger, AuthenticationService authenticationService) : base(client, logger) { AuthenticationService = authenticationService; AuthenticationService.OnLogin += (sender, args) => StartMonitoring(); AuthenticationService.OnLogout += (sender, args) => StopMonitoring(); AuthenticationService.OnRegister += (sender, args) => StartMonitoring(); AuthenticationService.OnOfflineModeChanged += (state) => { if (state) StopMonitoring(); else StartMonitoring(); }; ConnectionEstablished += (sender, args) => StartMonitoring(); } public Models.ConnectionState GetConnectionState() { return ConnectionState; } public (int current, int total) GetRetryCount() { return (RetryCount, MaxRetries); } public void StartMonitoring() { RetryCount = 0; ConnectionLost = false; CheckConnectionTimer?.Stop(); CheckConnectionTimer?.Dispose(); CheckConnectionTimer = new Timer(PingInterval); CheckConnectionTimer.Elapsed += CheckConnection!; CheckConnectionTimer.AutoReset = true; CheckConnectionTimer.Start(); } public void StopMonitoring() { RetryCount = 0; CheckConnectionTimer?.Stop(); CheckConnectionTimer?.Dispose(); RetryConnectionTimer?.Stop(); RetryConnectionTimer?.Dispose(); } private async void CheckConnection(object sender, ElapsedEventArgs e) { if (IsCheckConnectionActive) return; IsCheckConnectionActive = true; try { var serverOnline = await AuthenticationService.IsServerOnlineAsync(); if (!serverOnline && !ConnectionLost) { CheckConnectionTimer?.Stop(); CheckConnectionTimer?.Dispose(); CheckConnectionTimer = null; ConnectionLost = true; ConnectionSevered?.Invoke(this, EventArgs.Empty); RetryConnectionTimer = new Timer(RetryInterval); RetryConnectionTimer.Elapsed += RetryConnection!; RetryConnectionTimer.AutoReset = false; RetryConnectionTimer.Start(); } } finally { IsCheckConnectionActive = false; } } private async void RetryConnection(object sender, ElapsedEventArgs e) { if (IsRetryConnectionActive) return; IsRetryConnectionActive = true; try { var serverOnline = await AuthenticationService.IsServerOnlineAsync(); if (serverOnline && ConnectionLost) { ConnectionLost = false; RetryConnectionTimer?.Stop(); RetryConnectionTimer?.Dispose(); RetryConnectionTimer = null; ConnectionEstablished?.Invoke(this, EventArgs.Empty); AuthenticationService.SetOfflineMode(false); } else { RetryCount++; RetryConnectionTimer?.Start(); ConnectionRetryNext?.Invoke(this, EventArgs.Empty); if (RetryCount == MaxRetries) { RetryConnectionTimer?.Stop(); RetryConnectionTimer?.Dispose(); RetryConnectionTimer = null; ConnectionLostPermanently?.Invoke(this, EventArgs.Empty); AuthenticationService.SetOfflineMode(true); } } } finally { IsRetryConnectionActive = false; } } }