using System.Diagnostics; using System.ServiceProcess; using SpdUp.Core; using SpdUp.Models; namespace SpdUp.Services; /// /// Manages the full boost lifecycle: activate → monitor → restore. /// public sealed class OptimizationService { private readonly RamOptimizationService _ramService = new(); private readonly AppSettings _settings; private readonly List _stoppedServices = new(); private uint _previousTimerResolution; private bool _timerResolutionChanged; private string? _boostedProcessName; private bool _isBoosted; public bool IsBoosted => _isBoosted; public string? BoostedProcessName => _boostedProcessName; public event Action? BoostActivated; public event Action? BoostDeactivated; public OptimizationService(AppSettings settings) { _settings = settings; } /// /// Activate all boost optimizations. Thread-safe. /// public async Task ActivateBoostAsync(string? gameProcessName = null) { if (_isBoosted) return new BoostResult(false, "Boost is already active."); var messages = new List(); try { // 1. Enable required privileges NativeMethods.EnablePrivilege(NativeMethods.SE_DEBUG_NAME); NativeMethods.EnablePrivilege(NativeMethods.SE_INC_BASE_PRIORITY_NAME); NativeMethods.EnablePrivilege(NativeMethods.SE_PROF_SINGLE_PROCESS_NAME); // 2. RAM Cleanup await Task.Run(() => { var result = _ramService.FullCleanup(); messages.Add($"RAM: trimmed {result.TrimmedCount} processes, standby purge {(result.StandbyPurged > 0 ? "OK" : "skipped")}."); }); // 3. Stop non-critical services await Task.Run(() => { foreach (var svcName in _settings.ServicesToStop) { try { using var sc = new ServiceController(svcName); if (sc.Status == ServiceControllerStatus.Running) { sc.Stop(); _stoppedServices.Add(svcName); messages.Add($"Stopped service: {sc.DisplayName}"); } } catch (Exception ex) { Logger.Warn($"Could not stop {svcName}: {ex.Message}"); } } }); // 4. Set game to HIGH priority if (!string.IsNullOrEmpty(gameProcessName)) { _boostedProcessName = gameProcessName; await Task.Run(() => SetProcessPriority(gameProcessName, ProcessPriorityClass.High)); messages.Add($"Set {gameProcessName} to HIGH priority."); } // 5. Reduce timer resolution (0.5 ms) await Task.Run(() => { NativeMethods.NtQueryTimerResolution(out _, out _, out _previousTimerResolution); int status = NativeMethods.NtSetTimerResolution(5000, true, out _); _timerResolutionChanged = status == 0; if (_timerResolutionChanged) messages.Add("Timer resolution set to 0.5 ms."); }); _isBoosted = true; BoostActivated?.Invoke(string.Join("\n", messages)); Logger.Info("Boost activated: " + string.Join(" | ", messages)); return new BoostResult(true, string.Join("\n", messages)); } catch (Exception ex) { Logger.Error("Boost activation failed", ex); return new BoostResult(false, $"Error: {ex.Message}"); } } /// /// Restore all changes made during boost. /// public async Task DeactivateBoostAsync() { if (!_isBoosted) return; try { // 1. Restore timer resolution if (_timerResolutionChanged) { NativeMethods.NtSetTimerResolution(_previousTimerResolution, true, out _); _timerResolutionChanged = false; Logger.Info("Timer resolution restored."); } // 2. Restart stopped services await Task.Run(() => { foreach (var svcName in _stoppedServices) { try { using var sc = new ServiceController(svcName); if (sc.Status == ServiceControllerStatus.Stopped) { sc.Start(); Logger.Info($"Restarted service: {sc.DisplayName}"); } } catch (Exception ex) { Logger.Warn($"Could not restart {svcName}: {ex.Message}"); } } _stoppedServices.Clear(); }); // 3. Restore process priority if (_boostedProcessName is not null) { await Task.Run(() => SetProcessPriority(_boostedProcessName, ProcessPriorityClass.Normal)); _boostedProcessName = null; } _isBoosted = false; BoostDeactivated?.Invoke(); Logger.Info("Boost deactivated – all settings restored."); } catch (Exception ex) { Logger.Error("Boost deactivation error", ex); } } /// /// Set priority class for a process by name. /// public static void SetProcessPriority(string processName, ProcessPriorityClass priority) { foreach (var proc in Process.GetProcessesByName(processName)) { try { proc.PriorityClass = priority; Logger.Info($"Set {processName} (PID {proc.Id}) priority to {priority}."); } catch (Exception ex) { Logger.Warn($"Cannot set priority for {processName}: {ex.Message}"); } finally { proc.Dispose(); } } } /// /// Kill a process by PID. /// public static bool KillProcess(int pid) { try { using var proc = Process.GetProcessById(pid); proc.Kill(entireProcessTree: true); Logger.Info($"Killed process {proc.ProcessName} (PID {pid})."); return true; } catch (Exception ex) { Logger.Error($"Failed to kill PID {pid}", ex); return false; } } } public record BoostResult(bool Success, string Message);