Initial commit: SpdUp Windows Gaming Booster v1.0.0
- .NET 8 WPF app with full MVVM architecture - One-click boost: CPU, RAM, GPU, network, services, timer resolution - Real-time system monitor (LibreHardwareMonitor) - Auto game detection (30+ games) - RAM optimizer with kernel-level cleanup - Network optimizer (TCP registry tweaks) - Service manager, process manager, shader cache cleaner - Desktop widget overlay - Debug log viewer - Custom dark gaming theme with trans pride colors - 84 unit tests (xUnit) - Inno Setup installer script - Landing page website
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
using System.Diagnostics;
|
||||
using SpdUp.Core;
|
||||
using SpdUp.Models;
|
||||
|
||||
namespace SpdUp.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Detects running games and known launchers.
|
||||
/// </summary>
|
||||
public sealed class GameDetectionService : IDisposable
|
||||
{
|
||||
private readonly Timer _pollTimer;
|
||||
private readonly AppSettings _settings;
|
||||
private readonly HashSet<string> _knownGameProcesses = new(StringComparer.OrdinalIgnoreCase);
|
||||
private string? _detectedGameProcess;
|
||||
|
||||
public event Action<string>? GameStarted;
|
||||
public event Action<string>? GameStopped;
|
||||
|
||||
/// <summary>
|
||||
/// Well-known launcher processes that indicate a game may launch.
|
||||
/// </summary>
|
||||
private static readonly HashSet<string> LauncherProcesses = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"steam", "steamwebhelper",
|
||||
"EpicGamesLauncher",
|
||||
"Battle.net", "Agent"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Heuristic: processes that look like games (high-memory GPU-using apps not in system dirs).
|
||||
/// </summary>
|
||||
private static readonly HashSet<string> ExcludedProcessNames = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"explorer", "svchost", "System", "Idle", "csrss", "smss", "lsass",
|
||||
"services", "wininit", "winlogon", "dwm", "taskhostw", "RuntimeBroker",
|
||||
"ShellExperienceHost", "SearchHost", "SearchIndexer", "fontdrvhost",
|
||||
"conhost", "dllhost", "sihost", "ctfmon", "TextInputHost",
|
||||
"SecurityHealthSystray", "SecurityHealthService",
|
||||
"devenv", "Code", "chrome", "msedge", "firefox", "Discord",
|
||||
"Spotify", "slack", "Teams"
|
||||
};
|
||||
|
||||
public GameDetectionService(AppSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
|
||||
// Build known game process name set from settings.
|
||||
foreach (var g in _settings.Games)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(g.ProcessName))
|
||||
_knownGameProcesses.Add(g.ProcessName);
|
||||
}
|
||||
|
||||
_pollTimer = new Timer(PollForGames, null, Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
|
||||
public string? CurrentGameProcess => _detectedGameProcess;
|
||||
|
||||
public void Start(int intervalMs = 3000)
|
||||
{
|
||||
_pollTimer.Change(0, intervalMs);
|
||||
Logger.Info("Game detection started.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_pollTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
Logger.Info("Game detection stopped.");
|
||||
}
|
||||
|
||||
public void AddGameProcess(string processName)
|
||||
{
|
||||
_knownGameProcesses.Add(processName);
|
||||
}
|
||||
|
||||
private void PollForGames(object? state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var running = Process.GetProcesses();
|
||||
string? foundGame = null;
|
||||
|
||||
// First: check known game processes
|
||||
foreach (var proc in running)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_knownGameProcesses.Contains(proc.ProcessName))
|
||||
{
|
||||
foundGame = proc.ProcessName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { /* access denied */ }
|
||||
finally { proc.Dispose(); }
|
||||
}
|
||||
|
||||
// Second: heuristic – detect GPU-heavy, non-system, non-launcher processes
|
||||
if (foundGame is null)
|
||||
{
|
||||
foreach (var proc in Process.GetProcesses())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ExcludedProcessNames.Contains(proc.ProcessName)) continue;
|
||||
if (LauncherProcesses.Contains(proc.ProcessName)) continue;
|
||||
if (proc.Id <= 4) continue;
|
||||
|
||||
// Heuristic: >300 MB working set and has a main window => likely a game
|
||||
if (proc.WorkingSet64 > 300_000_000 && proc.MainWindowHandle != IntPtr.Zero)
|
||||
{
|
||||
// Check it's not from system folders
|
||||
try
|
||||
{
|
||||
var path = proc.MainModule?.FileName;
|
||||
if (path is not null &&
|
||||
!path.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.Windows), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foundGame = proc.ProcessName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { /* access denied */ }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
finally { proc.Dispose(); }
|
||||
}
|
||||
}
|
||||
|
||||
// State machine: detect start / stop
|
||||
if (foundGame is not null && _detectedGameProcess is null)
|
||||
{
|
||||
_detectedGameProcess = foundGame;
|
||||
Logger.Info($"Game detected: {foundGame}");
|
||||
GameStarted?.Invoke(foundGame);
|
||||
}
|
||||
else if (foundGame is null && _detectedGameProcess is not null)
|
||||
{
|
||||
var prev = _detectedGameProcess;
|
||||
_detectedGameProcess = null;
|
||||
Logger.Info($"Game stopped: {prev}");
|
||||
GameStopped?.Invoke(prev);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("Game detection poll error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pollTimer.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user