Files
SpdUp/Services/GameDetectionService.cs
T
Phil-icyou 19ba425e79 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
2026-03-08 17:34:45 +01:00

158 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
}
}