From 961d21cff91fe78aaf313ae5972541f5afcba1ce Mon Sep 17 00:00:00 2001 From: pizzaboxer <41478239+pizzaboxer@users.noreply.github.com> Date: Sun, 12 Mar 2023 14:14:10 +0000 Subject: [PATCH] Add exception handling for main thread wdym you're not supposed to just throw everything into a try catch block i have no idea what youre talking about --- Bloxstrap/App.xaml.cs | 210 ++++++++++-------- Bloxstrap/Helpers/FastFlagManager.cs | 114 +++++----- Bloxstrap/Helpers/Updater.cs | 6 +- Bloxstrap/Models/ReShadeShaderConfig.cs | 20 +- Bloxstrap/ViewModels/IntegrationsViewModel.cs | 4 +- Bloxstrap/ViewModels/ModsViewModel.cs | 14 +- 6 files changed, 191 insertions(+), 177 deletions(-) diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index fb5ae85..1d589c5 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -167,139 +167,153 @@ namespace Bloxstrap Settings.Load(); State.Load(); } - #if !DEBUG - if (!IsUninstall && !IsFirstRun) - Updater.CheckInstalledVersion(); + try + { + if (!IsUninstall && !IsFirstRun) + Updater.CheckInstalledVersion(); #endif - string commandLine = ""; + string commandLine = ""; - if (IsMenuLaunch) - { - Mutex mutex; - - try + if (IsMenuLaunch) { - mutex = Mutex.OpenExisting("Bloxstrap_MenuMutex"); - Logger.WriteLine("[App::OnStartup] Bloxstrap_MenuMutex mutex exists, aborting menu launch..."); - Terminate(); + Mutex mutex; + + try + { + mutex = Mutex.OpenExisting("Bloxstrap_MenuMutex"); + Logger.WriteLine("[App::OnStartup] Bloxstrap_MenuMutex mutex exists, aborting menu launch..."); + Terminate(); + } + catch + { + // no mutex exists, continue to opening preferences menu + mutex = new(true, "Bloxstrap_MenuMutex"); + } + + if (Utilities.GetProcessCount(ProjectName) > 1) + ShowMessageBox($"{ProjectName} is currently running, likely as a background Roblox process. Please note that not all your changes will immediately apply until you close all currently open Roblox instances.", MessageBoxImage.Information); + + new MainWindow().ShowDialog(); + FastFlags.Save(); } - catch + else if (LaunchArgs.Length > 0) { - // no mutex exists, continue to opening preferences menu - mutex = new(true, "Bloxstrap_MenuMutex"); - } - - if (Utilities.GetProcessCount(ProjectName) > 1) - ShowMessageBox($"{ProjectName} is currently running, likely as a background Roblox process. Please note that not all your changes will immediately apply until you close all currently open Roblox instances.", MessageBoxImage.Information); - - new MainWindow().ShowDialog(); - App.FastFlags.Save(); - } - else if (LaunchArgs.Length > 0) - { - if (LaunchArgs[0].StartsWith("roblox-player:")) - { - commandLine = Protocol.ParseUri(LaunchArgs[0]); - } - else if (LaunchArgs[0].StartsWith("roblox:")) - { - commandLine = $"--app --deeplink {LaunchArgs[0]}"; + if (LaunchArgs[0].StartsWith("roblox-player:")) + { + commandLine = Protocol.ParseUri(LaunchArgs[0]); + } + else if (LaunchArgs[0].StartsWith("roblox:")) + { + commandLine = $"--app --deeplink {LaunchArgs[0]}"; + } + else + { + commandLine = "--app"; + } } else { commandLine = "--app"; } - } - else - { - commandLine = "--app"; - } - if (!String.IsNullOrEmpty(commandLine)) - { - if (!IsFirstRun) - ShouldSaveConfigs = true; - - DeployManager.SetChannel(Settings.Prop.Channel); - - // start bootstrapper and show the bootstrapper modal if we're not running silently - Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper"); - Bootstrapper bootstrapper = new(commandLine); - IBootstrapperDialog? dialog = null; - - if (!IsQuiet) + if (!String.IsNullOrEmpty(commandLine)) { - Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper dialog"); - dialog = Settings.Prop.BootstrapperStyle.GetNew(); - bootstrapper.Dialog = dialog; - dialog.Bootstrapper = bootstrapper; - } + if (!IsFirstRun) + ShouldSaveConfigs = true; - // handle roblox singleton mutex for multi-instance launching - // note we're handling it here in the main thread and NOT in the - // bootstrapper as handling mutexes in async contexts suuuuuucks + DeployManager.SetChannel(Settings.Prop.Channel); - Mutex? singletonMutex = null; + // start bootstrapper and show the bootstrapper modal if we're not running silently + Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper"); + Bootstrapper bootstrapper = new(commandLine); + IBootstrapperDialog? dialog = null; - if (Settings.Prop.MultiInstanceLaunching) - { - Logger.WriteLine("[App::OnStartup] Creating singleton mutex"); - - try + if (!IsQuiet) { - Mutex.OpenExisting("ROBLOX_singletonMutex"); - Logger.WriteLine("[App::OnStartup] Warning - singleton mutex already exists!"); + Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper dialog"); + dialog = Settings.Prop.BootstrapperStyle.GetNew(); + bootstrapper.Dialog = dialog; + dialog.Bootstrapper = bootstrapper; } - catch + + // handle roblox singleton mutex for multi-instance launching + // note we're handling it here in the main thread and NOT in the + // bootstrapper as handling mutexes in async contexts suuuuuucks + + Mutex? singletonMutex = null; + + if (Settings.Prop.MultiInstanceLaunching) { - // create the singleton mutex before the game client does - singletonMutex = new Mutex(true, "ROBLOX_singletonMutex"); + Logger.WriteLine("[App::OnStartup] Creating singleton mutex"); + + try + { + Mutex.OpenExisting("ROBLOX_singletonMutex"); + Logger.WriteLine("[App::OnStartup] Warning - singleton mutex already exists!"); + } + catch + { + // create the singleton mutex before the game client does + singletonMutex = new Mutex(true, "ROBLOX_singletonMutex"); + } } - } - // there's a bug here that i have yet to fix! - // sometimes the task just terminates when the bootstrapper hasn't - // actually finished, causing the bootstrapper to hang indefinitely - // i have no idea how the fuck this happens, but it happens like VERY - // rarely so i'm not too concerned by it - // maybe one day ill find out why it happens - Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t => - { - Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished"); + // there's a bug here that i have yet to fix! + // sometimes the task just terminates when the bootstrapper hasn't + // actually finished, causing the bootstrapper to hang indefinitely + // i have no idea how the fuck this happens, but it happens like VERY + // rarely so i'm not too concerned by it + // maybe one day ill find out why it happens + Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t => + { + Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished"); - if (t.Exception is null) - return; + if (t.IsFaulted) + Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper"); - Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper"); - Logger.WriteLine($"[App::OnStartup] {t.Exception}"); + if (t.Exception is null) + return; + + Logger.WriteLine($"[App::OnStartup] {t.Exception}"); #if DEBUG - throw t.Exception; + throw t.Exception; #else - var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception; - dialog?.ShowError($"{exception.GetType()}: {exception.Message}"); + var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception; + dialog?.ShowError($"{exception.GetType()}: {exception.Message}"); #endif - }); + }, TaskScheduler.FromCurrentSynchronizationContext()); - dialog?.ShowBootstrapper(); - bootstrapperTask.Wait(); + dialog?.ShowBootstrapper(); + bootstrapperTask.Wait(); - if (singletonMutex is not null) - { - Logger.WriteLine($"[App::OnStartup] We have singleton mutex ownership! Running in background until all Roblox processes are closed"); - - // we've got ownership of the roblox singleton mutex! - // if we stop running, everything will screw up once any more roblox instances launched - while (Utilities.GetProcessCount("RobloxPlayerBeta", false) != 0) + if (singletonMutex is not null) { - Thread.Sleep(5000); + Logger.WriteLine($"[App::OnStartup] We have singleton mutex ownership! Running in background until all Roblox processes are closed"); + + // we've got ownership of the roblox singleton mutex! + // if we stop running, everything will screw up once any more roblox instances launched + while (Utilities.GetProcessCount("RobloxPlayerBeta", false) != 0) + { + Thread.Sleep(5000); + } } } +#if !DEBUG } + catch (Exception ex) + { + Logger.WriteLine("[App::OnStartup] An exception occurred when running the main thread"); + Logger.WriteLine($"[App::OnStartup] {ex}"); - Terminate(); + if (!IsQuiet) + Settings.Prop.BootstrapperStyle.GetNew().ShowError($"{ex.GetType()}: {ex.Message}"); + } +#endif + + Terminate(); } } } diff --git a/Bloxstrap/Helpers/FastFlagManager.cs b/Bloxstrap/Helpers/FastFlagManager.cs index 990eac8..8a8bb2d 100644 --- a/Bloxstrap/Helpers/FastFlagManager.cs +++ b/Bloxstrap/Helpers/FastFlagManager.cs @@ -14,73 +14,73 @@ namespace Bloxstrap.Helpers // to delete a fastflag, set the value to null public Dictionary Changes = new(); - // only one missing here is Metal because lol - public static IReadOnlyDictionary RenderingModes { get; set; } = new Dictionary() - { - { "Automatic", "" }, - { "Direct3D 11", "FFlagDebugGraphicsPreferD3D11" }, - { "OpenGL", "FFlagDebugGraphicsPreferOpenGL" }, - { "Vulkan", "FFlagDebugGraphicsPreferVulkan" } - }; + // only one missing here is Metal because lol + public static IReadOnlyDictionary RenderingModes { get; set; } = new Dictionary() + { + { "Automatic", "" }, + { "Direct3D 11", "FFlagDebugGraphicsPreferD3D11" }, + { "OpenGL", "FFlagDebugGraphicsPreferOpenGL" }, + { "Vulkan", "FFlagDebugGraphicsPreferVulkan" } + }; - // this returns null if the fflag doesn't exist - // this also returns as a string because deserializing an object doesn't - // deserialize back into the original object type, it instead deserializes - // as a "JsonElement" which is annoying - public string? GetValue(string key) - { - // check if we have an updated change for it pushed first - if (Changes.TryGetValue(key, out object? changedValue)) - return changedValue?.ToString(); + // this returns null if the fflag doesn't exist + // this also returns as a string because deserializing an object doesn't + // deserialize back into the original object type, it instead deserializes + // as a "JsonElement" which is annoying + public string? GetValue(string key) + { + // check if we have an updated change for it pushed first + if (Changes.TryGetValue(key, out object? changedValue)) + return changedValue?.ToString(); - if (Prop.TryGetValue(key, out object? value) && value is not null) - return value.ToString(); + if (Prop.TryGetValue(key, out object? value) && value is not null) + return value.ToString(); - return null; - } + return null; + } - public void SetRenderingMode(string value) - { - foreach (var mode in RenderingModes) - { - if (value != "Automatic") - App.FastFlags.Changes[mode.Value] = null; - } + public void SetRenderingMode(string value) + { + foreach (var mode in RenderingModes) + { + if (value != "Automatic") + App.FastFlags.Changes[mode.Value] = null; + } - if (value != "Automatic") - App.FastFlags.Changes[RenderingModes[value]] = true; - } + if (value != "Automatic") + App.FastFlags.Changes[RenderingModes[value]] = true; + } - public override void Save() - { - App.Logger.WriteLine($"[FastFlagManager::Save] Attempting to save JSON to {FileLocation}..."); + public override void Save() + { + App.Logger.WriteLine($"[FastFlagManager::Save] Attempting to save JSON to {FileLocation}..."); - if (Changes.Count == 0) - { - App.Logger.WriteLine($"[FastFlagManager::Save] No changes to apply, aborting."); - return; - } + if (Changes.Count == 0) + { + App.Logger.WriteLine($"[FastFlagManager::Save] No changes to apply, aborting."); + return; + } - // reload for any changes made while the menu was open - Load(); + // reload for any changes made while the menu was open + Load(); - foreach (var change in Changes) - { - if (change.Value is null) - { - App.Logger.WriteLine($"[FastFlagManager::Save] Removing '{change.Key}'"); - Prop.Remove(change.Key); - continue; - } + foreach (var change in Changes) + { + if (change.Value is null) + { + App.Logger.WriteLine($"[FastFlagManager::Save] Removing '{change.Key}'"); + Prop.Remove(change.Key); + continue; + } - App.Logger.WriteLine($"[FastFlagManager::Save] Setting '{change.Key}' to {change.Value}"); - Prop[change.Key] = change.Value; - } + App.Logger.WriteLine($"[FastFlagManager::Save] Setting '{change.Key}' to {change.Value}"); + Prop[change.Key] = change.Value; + } - Directory.CreateDirectory(Path.GetDirectoryName(FileLocation)!); - File.WriteAllText(FileLocation, JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true })); + Directory.CreateDirectory(Path.GetDirectoryName(FileLocation)!); + File.WriteAllText(FileLocation, JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true })); - App.Logger.WriteLine($"[FastFlagManager::Save] JSON saved!"); - } - } + App.Logger.WriteLine($"[FastFlagManager::Save] JSON saved!"); + } + } } diff --git a/Bloxstrap/Helpers/Updater.cs b/Bloxstrap/Helpers/Updater.cs index 0c467c9..c9c1ebe 100644 --- a/Bloxstrap/Helpers/Updater.cs +++ b/Bloxstrap/Helpers/Updater.cs @@ -106,9 +106,9 @@ namespace Bloxstrap.Helpers MessageBoxButton.OK ); - new MainWindow().ShowDialog(); - App.Terminate(); - } + new MainWindow().ShowDialog(); + App.Terminate(); + } } } } \ No newline at end of file diff --git a/Bloxstrap/Models/ReShadeShaderConfig.cs b/Bloxstrap/Models/ReShadeShaderConfig.cs index 7993374..5ecda89 100644 --- a/Bloxstrap/Models/ReShadeShaderConfig.cs +++ b/Bloxstrap/Models/ReShadeShaderConfig.cs @@ -6,16 +6,16 @@ using System.Threading.Tasks; namespace Bloxstrap.Models { - public class ReShadeShaderConfig - { - // it's assumed that the BaseFolder has a "Textures" folder and a "Shaders" folder - // the files listed in ExcludedFiles are relative to the BaseFolder + public class ReShadeShaderConfig + { + // it's assumed that the BaseFolder has a "Textures" folder and a "Shaders" folder + // the files listed in ExcludedFiles are relative to the BaseFolder - public string Name { get; set; } = null!; - public string DownloadLocation { get; set; } = null!; - public string BaseFolder { get; set; } = "/"; - public List ExcludedFiles { get; set; } = new List(); + public string Name { get; set; } = null!; + public string DownloadLocation { get; set; } = null!; + public string BaseFolder { get; set; } = "/"; + public List ExcludedFiles { get; set; } = new List(); - public override string ToString() => Name; - } + public override string ToString() => Name; + } } diff --git a/Bloxstrap/ViewModels/IntegrationsViewModel.cs b/Bloxstrap/ViewModels/IntegrationsViewModel.cs index e4951b1..599c797 100644 --- a/Bloxstrap/ViewModels/IntegrationsViewModel.cs +++ b/Bloxstrap/ViewModels/IntegrationsViewModel.cs @@ -85,9 +85,9 @@ namespace Bloxstrap.ViewModels ReShadePresetsEnabled = value; if (value) - App.FastFlags.SetRenderingMode("Direct3D 11"); + App.FastFlags.SetRenderingMode("Direct3D 11"); - OnPropertyChanged(nameof(ReShadePresetsEnabled)); + OnPropertyChanged(nameof(ReShadePresetsEnabled)); } } diff --git a/Bloxstrap/ViewModels/ModsViewModel.cs b/Bloxstrap/ViewModels/ModsViewModel.cs index ca1fdce..7467c80 100644 --- a/Bloxstrap/ViewModels/ModsViewModel.cs +++ b/Bloxstrap/ViewModels/ModsViewModel.cs @@ -8,11 +8,11 @@ using CommunityToolkit.Mvvm.Input; namespace Bloxstrap.ViewModels { public class ModsViewModel : INotifyPropertyChanged - { - public event PropertyChangedEventHandler? PropertyChanged; - public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + { + public event PropertyChangedEventHandler? PropertyChanged; + public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - public ICommand OpenModsFolderCommand => new RelayCommand(OpenModsFolder); + public ICommand OpenModsFolderCommand => new RelayCommand(OpenModsFolder); private void OpenModsFolder() { @@ -49,9 +49,9 @@ namespace Bloxstrap.ViewModels if (value) { - App.FastFlags.SetRenderingMode("Direct3D 11"); - OnPropertyChanged(nameof(SelectedRenderingMode)); - } + App.FastFlags.SetRenderingMode("Direct3D 11"); + OnPropertyChanged(nameof(SelectedRenderingMode)); + } } }