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
This commit is contained in:
pizzaboxer 2023-03-12 14:14:10 +00:00
parent 558fc4e983
commit 961d21cff9
6 changed files with 191 additions and 177 deletions

View File

@ -167,139 +167,153 @@ namespace Bloxstrap
Settings.Load(); Settings.Load();
State.Load(); State.Load();
} }
#if !DEBUG #if !DEBUG
if (!IsUninstall && !IsFirstRun) try
Updater.CheckInstalledVersion(); {
if (!IsUninstall && !IsFirstRun)
Updater.CheckInstalledVersion();
#endif #endif
string commandLine = ""; string commandLine = "";
if (IsMenuLaunch) if (IsMenuLaunch)
{
Mutex mutex;
try
{ {
mutex = Mutex.OpenExisting("Bloxstrap_MenuMutex"); Mutex mutex;
Logger.WriteLine("[App::OnStartup] Bloxstrap_MenuMutex mutex exists, aborting menu launch...");
Terminate(); 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 if (LaunchArgs[0].StartsWith("roblox-player:"))
mutex = new(true, "Bloxstrap_MenuMutex"); {
} commandLine = Protocol.ParseUri(LaunchArgs[0]);
}
if (Utilities.GetProcessCount(ProjectName) > 1) else if (LaunchArgs[0].StartsWith("roblox:"))
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); {
commandLine = $"--app --deeplink {LaunchArgs[0]}";
new MainWindow().ShowDialog(); }
App.FastFlags.Save(); else
} {
else if (LaunchArgs.Length > 0) commandLine = "--app";
{ }
if (LaunchArgs[0].StartsWith("roblox-player:"))
{
commandLine = Protocol.ParseUri(LaunchArgs[0]);
}
else if (LaunchArgs[0].StartsWith("roblox:"))
{
commandLine = $"--app --deeplink {LaunchArgs[0]}";
} }
else else
{ {
commandLine = "--app"; commandLine = "--app";
} }
}
else
{
commandLine = "--app";
}
if (!String.IsNullOrEmpty(commandLine)) 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)
{ {
Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper dialog"); if (!IsFirstRun)
dialog = Settings.Prop.BootstrapperStyle.GetNew(); ShouldSaveConfigs = true;
bootstrapper.Dialog = dialog;
dialog.Bootstrapper = bootstrapper;
}
// handle roblox singleton mutex for multi-instance launching DeployManager.SetChannel(Settings.Prop.Channel);
// 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; // 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) if (!IsQuiet)
{
Logger.WriteLine("[App::OnStartup] Creating singleton mutex");
try
{ {
Mutex.OpenExisting("ROBLOX_singletonMutex"); Logger.WriteLine($"[App::OnStartup] Initializing bootstrapper dialog");
Logger.WriteLine("[App::OnStartup] Warning - singleton mutex already exists!"); 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 Logger.WriteLine("[App::OnStartup] Creating singleton mutex");
singletonMutex = new Mutex(true, "ROBLOX_singletonMutex");
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! // there's a bug here that i have yet to fix!
// sometimes the task just terminates when the bootstrapper hasn't // sometimes the task just terminates when the bootstrapper hasn't
// actually finished, causing the bootstrapper to hang indefinitely // actually finished, causing the bootstrapper to hang indefinitely
// i have no idea how the fuck this happens, but it happens like VERY // i have no idea how the fuck this happens, but it happens like VERY
// rarely so i'm not too concerned by it // rarely so i'm not too concerned by it
// maybe one day ill find out why it happens // maybe one day ill find out why it happens
Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t => Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t =>
{ {
Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished"); Logger.WriteLine("[App::OnStartup] Bootstrapper task has finished");
if (t.Exception is null) if (t.IsFaulted)
return; Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper");
Logger.WriteLine("[App::OnStartup] An exception occurred when running the bootstrapper"); if (t.Exception is null)
Logger.WriteLine($"[App::OnStartup] {t.Exception}"); return;
Logger.WriteLine($"[App::OnStartup] {t.Exception}");
#if DEBUG #if DEBUG
throw t.Exception; throw t.Exception;
#else #else
var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception; var exception = t.Exception.InnerExceptions.Count >= 1 ? t.Exception.InnerExceptions[0] : t.Exception;
dialog?.ShowError($"{exception.GetType()}: {exception.Message}"); dialog?.ShowError($"{exception.GetType()}: {exception.Message}");
#endif #endif
}); }, TaskScheduler.FromCurrentSynchronizationContext());
dialog?.ShowBootstrapper(); dialog?.ShowBootstrapper();
bootstrapperTask.Wait(); bootstrapperTask.Wait();
if (singletonMutex is not null) 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)
{ {
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();
} }
} }
} }

View File

@ -14,73 +14,73 @@ namespace Bloxstrap.Helpers
// to delete a fastflag, set the value to null // to delete a fastflag, set the value to null
public Dictionary<string, object?> Changes = new(); public Dictionary<string, object?> Changes = new();
// only one missing here is Metal because lol // only one missing here is Metal because lol
public static IReadOnlyDictionary<string, string> RenderingModes { get; set; } = new Dictionary<string, string>() public static IReadOnlyDictionary<string, string> RenderingModes { get; set; } = new Dictionary<string, string>()
{ {
{ "Automatic", "" }, { "Automatic", "" },
{ "Direct3D 11", "FFlagDebugGraphicsPreferD3D11" }, { "Direct3D 11", "FFlagDebugGraphicsPreferD3D11" },
{ "OpenGL", "FFlagDebugGraphicsPreferOpenGL" }, { "OpenGL", "FFlagDebugGraphicsPreferOpenGL" },
{ "Vulkan", "FFlagDebugGraphicsPreferVulkan" } { "Vulkan", "FFlagDebugGraphicsPreferVulkan" }
}; };
// this returns null if the fflag doesn't exist // this returns null if the fflag doesn't exist
// this also returns as a string because deserializing an object doesn't // this also returns as a string because deserializing an object doesn't
// deserialize back into the original object type, it instead deserializes // deserialize back into the original object type, it instead deserializes
// as a "JsonElement" which is annoying // as a "JsonElement" which is annoying
public string? GetValue(string key) public string? GetValue(string key)
{ {
// check if we have an updated change for it pushed first // check if we have an updated change for it pushed first
if (Changes.TryGetValue(key, out object? changedValue)) if (Changes.TryGetValue(key, out object? changedValue))
return changedValue?.ToString(); return changedValue?.ToString();
if (Prop.TryGetValue(key, out object? value) && value is not null) if (Prop.TryGetValue(key, out object? value) && value is not null)
return value.ToString(); return value.ToString();
return null; return null;
} }
public void SetRenderingMode(string value) public void SetRenderingMode(string value)
{ {
foreach (var mode in RenderingModes) foreach (var mode in RenderingModes)
{ {
if (value != "Automatic") if (value != "Automatic")
App.FastFlags.Changes[mode.Value] = null; App.FastFlags.Changes[mode.Value] = null;
} }
if (value != "Automatic") if (value != "Automatic")
App.FastFlags.Changes[RenderingModes[value]] = true; App.FastFlags.Changes[RenderingModes[value]] = true;
} }
public override void Save() public override void Save()
{ {
App.Logger.WriteLine($"[FastFlagManager::Save] Attempting to save JSON to {FileLocation}..."); App.Logger.WriteLine($"[FastFlagManager::Save] Attempting to save JSON to {FileLocation}...");
if (Changes.Count == 0) if (Changes.Count == 0)
{ {
App.Logger.WriteLine($"[FastFlagManager::Save] No changes to apply, aborting."); App.Logger.WriteLine($"[FastFlagManager::Save] No changes to apply, aborting.");
return; return;
} }
// reload for any changes made while the menu was open // reload for any changes made while the menu was open
Load(); Load();
foreach (var change in Changes) foreach (var change in Changes)
{ {
if (change.Value is null) if (change.Value is null)
{ {
App.Logger.WriteLine($"[FastFlagManager::Save] Removing '{change.Key}'"); App.Logger.WriteLine($"[FastFlagManager::Save] Removing '{change.Key}'");
Prop.Remove(change.Key); Prop.Remove(change.Key);
continue; continue;
} }
App.Logger.WriteLine($"[FastFlagManager::Save] Setting '{change.Key}' to {change.Value}"); App.Logger.WriteLine($"[FastFlagManager::Save] Setting '{change.Key}' to {change.Value}");
Prop[change.Key] = change.Value; Prop[change.Key] = change.Value;
} }
Directory.CreateDirectory(Path.GetDirectoryName(FileLocation)!); Directory.CreateDirectory(Path.GetDirectoryName(FileLocation)!);
File.WriteAllText(FileLocation, JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true })); File.WriteAllText(FileLocation, JsonSerializer.Serialize(Prop, new JsonSerializerOptions { WriteIndented = true }));
App.Logger.WriteLine($"[FastFlagManager::Save] JSON saved!"); App.Logger.WriteLine($"[FastFlagManager::Save] JSON saved!");
} }
} }
} }

View File

@ -106,9 +106,9 @@ namespace Bloxstrap.Helpers
MessageBoxButton.OK MessageBoxButton.OK
); );
new MainWindow().ShowDialog(); new MainWindow().ShowDialog();
App.Terminate(); App.Terminate();
} }
} }
} }
} }

View File

@ -6,16 +6,16 @@ using System.Threading.Tasks;
namespace Bloxstrap.Models namespace Bloxstrap.Models
{ {
public class ReShadeShaderConfig public class ReShadeShaderConfig
{ {
// it's assumed that the BaseFolder has a "Textures" folder and a "Shaders" folder // it's assumed that the BaseFolder has a "Textures" folder and a "Shaders" folder
// the files listed in ExcludedFiles are relative to the BaseFolder // the files listed in ExcludedFiles are relative to the BaseFolder
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
public string DownloadLocation { get; set; } = null!; public string DownloadLocation { get; set; } = null!;
public string BaseFolder { get; set; } = "/"; public string BaseFolder { get; set; } = "/";
public List<string> ExcludedFiles { get; set; } = new List<string>(); public List<string> ExcludedFiles { get; set; } = new List<string>();
public override string ToString() => Name; public override string ToString() => Name;
} }
} }

View File

@ -85,9 +85,9 @@ namespace Bloxstrap.ViewModels
ReShadePresetsEnabled = value; ReShadePresetsEnabled = value;
if (value) if (value)
App.FastFlags.SetRenderingMode("Direct3D 11"); App.FastFlags.SetRenderingMode("Direct3D 11");
OnPropertyChanged(nameof(ReShadePresetsEnabled)); OnPropertyChanged(nameof(ReShadePresetsEnabled));
} }
} }

View File

@ -8,11 +8,11 @@ using CommunityToolkit.Mvvm.Input;
namespace Bloxstrap.ViewModels namespace Bloxstrap.ViewModels
{ {
public class ModsViewModel : INotifyPropertyChanged public class ModsViewModel : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 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() private void OpenModsFolder()
{ {
@ -49,9 +49,9 @@ namespace Bloxstrap.ViewModels
if (value) if (value)
{ {
App.FastFlags.SetRenderingMode("Direct3D 11"); App.FastFlags.SetRenderingMode("Direct3D 11");
OnPropertyChanged(nameof(SelectedRenderingMode)); OnPropertyChanged(nameof(SelectedRenderingMode));
} }
} }
} }