using Bloxstrap.Integrations; namespace Bloxstrap { public class Watcher : IDisposable { private int _gameClientPid = 0; private readonly InterProcessLock _lock = new("Watcher"); private readonly List _autoclosePids = new(); private readonly NotifyIconWrapper? _notifyIcon; public readonly ActivityWatcher? ActivityWatcher; public readonly DiscordRichPresence? RichPresence; public Watcher() { const string LOG_IDENT = "Watcher"; if (!_lock.IsAcquired) { App.Logger.WriteLine(LOG_IDENT, "Watcher instance already exists"); return; } string? watcherData = App.LaunchSettings.WatcherFlag.Data; #if DEBUG if (String.IsNullOrEmpty(watcherData)) { string path = Path.Combine(Paths.Roblox, "Player", "RobloxPlayerBeta.exe"); using var gameClientProcess = Process.Start(path); _gameClientPid = gameClientProcess.Id; } #else if (String.IsNullOrEmpty(watcherData)) throw new Exception("Watcher data not specified"); #endif if (!String.IsNullOrEmpty(watcherData) && _gameClientPid == 0) { var split = watcherData.Split(';'); if (split.Length == 0) _ = int.TryParse(watcherData, out _gameClientPid); if (split.Length >= 1) _ = int.TryParse(split[0], out _gameClientPid); if (split.Length >= 2) { foreach (string strPid in split[1].Split(',')) { if (int.TryParse(strPid, out int pid) && pid != 0) _autoclosePids.Add(pid); } } } if (_gameClientPid == 0) throw new Exception("Watcher data is invalid"); if (App.Settings.Prop.EnableActivityTracking) { ActivityWatcher = new(); if (App.Settings.Prop.UseDisableAppPatch) { ActivityWatcher.OnAppClose += delegate { App.Logger.WriteLine(LOG_IDENT, "Received desktop app exit, closing Roblox"); using var process = Process.GetProcessById(_gameClientPid); process.CloseMainWindow(); }; } if (App.Settings.Prop.UseDiscordRichPresence) RichPresence = new(ActivityWatcher); } _notifyIcon = new(this); } public void KillRobloxProcess() => CloseProcess(_gameClientPid, true); public void CloseProcess(int pid, bool force = false) { const string LOG_IDENT = "Watcher::CloseProcess"; try { using var process = Process.GetProcessById(pid); App.Logger.WriteLine(LOG_IDENT, $"Killing process '{process.ProcessName}' (pid={pid}, force={force})"); if (process.HasExited) { App.Logger.WriteLine(LOG_IDENT, $"PID {pid} has already exited"); return; } if (force) process.Kill(); else process.CloseMainWindow(); } catch (Exception ex) { App.Logger.WriteLine(LOG_IDENT, $"PID {pid} could not be closed"); App.Logger.WriteException(LOG_IDENT, ex); } } public async Task Run() { if (!_lock.IsAcquired) return; ActivityWatcher?.Start(); while (Utilities.GetProcessesSafe().Any(x => x.Id == _gameClientPid)) await Task.Delay(1000); foreach (int pid in _autoclosePids) CloseProcess(pid); if (App.LaunchSettings.TestModeFlag.Active) Process.Start(Paths.Process, "-settings -testmode"); } public void Dispose() { App.Logger.WriteLine("Watcher::Dispose", "Disposing Watcher"); _notifyIcon?.Dispose(); RichPresence?.Dispose(); GC.SuppressFinalize(this); } } }