From 56016e97232efd75c4c4dd9404948c480f1d77ca Mon Sep 17 00:00:00 2001 From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:35:22 +0100 Subject: [PATCH 01/19] use cancellation tokens for connectivity check --- Bloxstrap/RobloxDeployment.cs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/Bloxstrap/RobloxDeployment.cs b/Bloxstrap/RobloxDeployment.cs index b350eea..fd33de7 100644 --- a/Bloxstrap/RobloxDeployment.cs +++ b/Bloxstrap/RobloxDeployment.cs @@ -18,24 +18,26 @@ { "https://s3.amazonaws.com/setup.roblox.com", 4 } }; - private static async Task TestConnection(string url, int priority) + private static async Task TestConnection(string url, int priority, CancellationToken token) { string LOG_IDENT = $"RobloxDeployment::TestConnection.{url}"; - await Task.Delay(priority * 1000); - - if (BaseUrl is not null) - return null; + await Task.Delay(priority * 1000, token); App.Logger.WriteLine(LOG_IDENT, "Connecting..."); try { - var response = await App.HttpClient.GetAsync($"{url}/version"); + var response = await App.HttpClient.GetAsync($"{url}/version", token); if (!response.IsSuccessStatusCode) throw new HttpResponseException(response); } + catch (TaskCanceledException) + { + App.Logger.WriteLine(LOG_IDENT, "Connectivity test cancelled."); + throw; + } catch (Exception ex) { App.Logger.WriteException(LOG_IDENT, ex); @@ -59,8 +61,11 @@ if (!String.IsNullOrEmpty(BaseUrl)) return null; + CancellationTokenSource tokenSource = new CancellationTokenSource(); + CancellationToken token = tokenSource.Token; + var exceptions = new List(); - var tasks = (from entry in BaseUrls select TestConnection(entry.Key, entry.Value)).ToList(); + var tasks = (from entry in BaseUrls select TestConnection(entry.Key, entry.Value, token)).ToList(); App.Logger.WriteLine(LOG_IDENT, "Testing connectivity..."); @@ -79,6 +84,9 @@ break; } + // stop other running connectivity tests + tokenSource.Cancel(); + if (String.IsNullOrEmpty(BaseUrl)) return exceptions[0]; From 7123aa79517efc05a54e83ad0ad6bca4f5a197bd Mon Sep 17 00:00:00 2001 From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:37:01 +0100 Subject: [PATCH 02/19] remove pointless baseurl check InitializeConnectivity is an async function. if it is somehow ran twice and the previous task hasn't completed, the connectivity check will still run. --- Bloxstrap/RobloxDeployment.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Bloxstrap/RobloxDeployment.cs b/Bloxstrap/RobloxDeployment.cs index fd33de7..38d12f2 100644 --- a/Bloxstrap/RobloxDeployment.cs +++ b/Bloxstrap/RobloxDeployment.cs @@ -58,9 +58,6 @@ // returns null for success - if (!String.IsNullOrEmpty(BaseUrl)) - return null; - CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; From 2e8cb16ad4a095af61bf4b4a66003e39b4fad828 Mon Sep 17 00:00:00 2001 From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:41:12 +0100 Subject: [PATCH 03/19] check versionStudio instead of version --- Bloxstrap/RobloxDeployment.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Bloxstrap/RobloxDeployment.cs b/Bloxstrap/RobloxDeployment.cs index 38d12f2..8315a60 100644 --- a/Bloxstrap/RobloxDeployment.cs +++ b/Bloxstrap/RobloxDeployment.cs @@ -4,6 +4,8 @@ { public const string DefaultChannel = "production"; + private const string VersionStudioHash = "version-012732894899482c"; + public static string BaseUrl { get; private set; } = null!; private static readonly Dictionary ClientVersionCache = new(); @@ -28,10 +30,16 @@ try { - var response = await App.HttpClient.GetAsync($"{url}/version", token); + var response = await App.HttpClient.GetAsync($"{url}/versionStudio", token); if (!response.IsSuccessStatusCode) throw new HttpResponseException(response); + + // versionStudio is the version hash for the last MFC studio to be deployed. + // the response body should always be "version-012732894899482c". + string content = await response.Content.ReadAsStringAsync(token); + if (content != VersionStudioHash) + throw new Exception($"versionStudio response does not match (expected {VersionStudioHash}, got {content})"); } catch (TaskCanceledException) { From f3dd87db04481c947fdae25db7fac6ceb18fe566 Mon Sep 17 00:00:00 2001 From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:44:05 +0100 Subject: [PATCH 04/19] put responses around quotes --- Bloxstrap/RobloxDeployment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bloxstrap/RobloxDeployment.cs b/Bloxstrap/RobloxDeployment.cs index 8315a60..37784a5 100644 --- a/Bloxstrap/RobloxDeployment.cs +++ b/Bloxstrap/RobloxDeployment.cs @@ -39,7 +39,7 @@ // the response body should always be "version-012732894899482c". string content = await response.Content.ReadAsStringAsync(token); if (content != VersionStudioHash) - throw new Exception($"versionStudio response does not match (expected {VersionStudioHash}, got {content})"); + throw new Exception($"versionStudio response does not match (expected \"{VersionStudioHash}\", got \"{content}\")"); } catch (TaskCanceledException) { From 3dafe969bb0dcb6e11e5d02f437e729b3d2ab50a Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sat, 24 Aug 2024 16:08:19 +0100 Subject: [PATCH 05/19] Update README.md --- README.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ee67957..8461f55 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,21 @@ [![Discord](https://img.shields.io/discord/1099468797410283540?logo=discord&logoColor=white&label=discord&color=4d3dff)](https://discord.gg/nKjV3mGq6R) [![lol](https://img.shields.io/badge/mom%20made-pizza%20rolls-orange)](https://media.tenor.com/FIkSGbGycmAAAAAd/manly-roblox.gif) -This is a drop-in replacement for the standard Roblox bootstrapper, providing additional useful features and improvements. Nothing more, nothing less. +Bloxstrap is a third-party replacement for the standard Roblox bootstrapper, providing additional useful features and improvements such as hassle-free Discord Rich Presence and easy content file replacement. -This does not touch or modify the game client itself, it's really just a launcher. So don't worry, there's [no risk of being banned](https://github.com/pizzaboxer/bloxstrap/wiki/Why-it%27s-not-reasonably-possible-for-you-to-be-banned-by-Bloxstrap) for using this. - -Running into a problem or need help with something? [Check out the Wiki](https://github.com/pizzaboxer/bloxstrap/wiki). If you can't find anything, or would like to suggest something, please [submit an issue](https://github.com/pizzaboxer/bloxstrap/issues) or report it in our [Discord server](https://discord.gg/nKjV3mGq6R). +Running into a problem or need help with something? [Check out the Wiki](https://github.com/pizzaboxer/bloxstrap/wiki). If you can't find anything, or would like to suggest something, please [submit an issue](https://github.com/pizzaboxer/bloxstrap/issues). Bloxstrap is only supported for PCs running Windows. + +## Frequently Asked Questions + +**Q: Is this malware?** + +**A:** No. The source code here is viewable to all, and it'd be impossible for us to slip anything malicious into the downloads without anyone noticing. Just be sure you're downloading it from an official source. The only two official sources are this GitHub repository and [bloxstrap.pizzaboxer.xyz](https://bloxstrap.pizzaboxer.xyz). + +**Q: Can using this get me banned?** + +**A:** No, it shouldn't. Bloxstrap doesn't interact with the Roblox client in the same way that exploits do. [Read more about that here.](https://github.com/pizzaboxer/bloxstrap/wiki/Why-it's-not-reasonably-possible-for-you-to-be-banned-by-Bloxstrap) ## Installing Download the [latest release of Bloxstrap](https://github.com/pizzaboxer/bloxstrap/releases/latest), and run it. Configure your preferences if needed, and install. That's about it! @@ -31,16 +39,6 @@ You will also need the [.NET 6 Desktop Runtime](https://aka.ms/dotnet-core-appla It's not unlikely that Windows Smartscreen will show a popup when you run Bloxstrap for the first time. This happens because it's an unknown program, not because it's actually detected as being malicious. To dismiss it, just click on "More info" and then "Run anyway". Once installed, Bloxstrap is added to your Start Menu, where you can access the menu and reconfigure your preferences if needed. - -## Features -Here's some of the features that Bloxstrap provides over the stock Roblox bootstrapper: - -* Persistent file modifications, includes re-adding the old death sound! -* Painless and seamless support for Discord Rich Presence -* A customizable launcher look -* Lets you see what region your current server is located in - -All the available features are browsable through the Bloxstrap menu. ## Screenshots From b8c69e1f4edde13848a807260b2da4f2dfc30a6f Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sat, 24 Aug 2024 16:11:57 +0100 Subject: [PATCH 06/19] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8461f55..0b47eb9 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ Bloxstrap is only supported for PCs running Windows. **Q: Can using this get me banned?** **A:** No, it shouldn't. Bloxstrap doesn't interact with the Roblox client in the same way that exploits do. [Read more about that here.](https://github.com/pizzaboxer/bloxstrap/wiki/Why-it's-not-reasonably-possible-for-you-to-be-banned-by-Bloxstrap) + +**Q: Why was multi-instance launching removed?** + +**A:** It was removed starting with v2.6.0 for the [reasons stated here](https://github.com/pizzaboxer/bloxstrap/wiki/Plans-to-remove-multi%E2%80%90instance-launching-from-Bloxstrap). It may be added back in the future when there are less issues with doing so. ## Installing Download the [latest release of Bloxstrap](https://github.com/pizzaboxer/bloxstrap/releases/latest), and run it. Configure your preferences if needed, and install. That's about it! From 0e743442a1d633d5f5e4cda9550f336f84e26cdc Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sat, 24 Aug 2024 16:28:31 +0100 Subject: [PATCH 07/19] give me money --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3d2e4f6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: boxerpizza From 5f791e3c6a50df56cb5f9706f5a7696798408196 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sat, 24 Aug 2024 19:38:41 +0100 Subject: [PATCH 08/19] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index f2d9a6e..56f0166 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -11,6 +11,7 @@ body: - If your problem is with Roblox itself (i.e. it crashes or doesn't launch), [check to see if it happens without Bloxstrap](https://github.com/pizzaboxer/bloxstrap/wiki/Roblox-crashes-or-does-not-launch). - Please only open an issue if your problem happens only with Bloxstrap, and state clearly that this is the case, as anything else is out of my control. - If you are getting a Bloxstrap Exception error, please attach a copy of the provided log file. There is a button on the dialog that locates it for you. + - Also, please ensure you are using the [latest version of Bloxstrap](https://github.com/pizzaboxer/bloxstrap/releases/latest). Your report will be null and void if you are not. - If more clarification on the issue is needed, and you don't respond after a month, then your issue will be closed as stale. - type: checkboxes id: terms @@ -19,7 +20,11 @@ body: options: - label: I have read the preliminary instructions, and I am certain that my problem has not already been addressed. required: true - - label: My answer given in the checkbox above is a lie. + - label: I have thoroughly looked through the available Wiki articles and could not find a solution to my problem. + required: true + - label: I am using the latest version of Bloxstrap. + required: true + - label: I did not answer truthfully to all the above checkboxes. - type: textarea id: what-happened attributes: From 1eac672a22289a0cccadc14f80cbbed9c3b64ea7 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Mon, 26 Aug 2024 14:43:41 +0100 Subject: [PATCH 09/19] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b47eb9..6cb3581 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Discord](https://img.shields.io/discord/1099468797410283540?logo=discord&logoColor=white&label=discord&color=4d3dff)](https://discord.gg/nKjV3mGq6R) [![lol](https://img.shields.io/badge/mom%20made-pizza%20rolls-orange)](https://media.tenor.com/FIkSGbGycmAAAAAd/manly-roblox.gif) -Bloxstrap is a third-party replacement for the standard Roblox bootstrapper, providing additional useful features and improvements such as hassle-free Discord Rich Presence and easy content file replacement. +Bloxstrap is a third-party replacement for the standard Roblox bootstrapper, providing additional useful features and improvements. Running into a problem or need help with something? [Check out the Wiki](https://github.com/pizzaboxer/bloxstrap/wiki). If you can't find anything, or would like to suggest something, please [submit an issue](https://github.com/pizzaboxer/bloxstrap/issues). @@ -29,6 +29,13 @@ Bloxstrap is only supported for PCs running Windows. **Q: Why was multi-instance launching removed?** **A:** It was removed starting with v2.6.0 for the [reasons stated here](https://github.com/pizzaboxer/bloxstrap/wiki/Plans-to-remove-multi%E2%80%90instance-launching-from-Bloxstrap). It may be added back in the future when there are less issues with doing so. + +## Features + +- Hassle-free Discord Rich Presence to let your friends know what you're playing at a glance +- Simple support for modding of content files for customizability (death sound, mouse cursor, etc) +- See where your server is geographically located (courtesy of [ipinfo.io](https://ipinfo.io)) +- Ability to configure graphics fidelity and UI experience ## Installing Download the [latest release of Bloxstrap](https://github.com/pizzaboxer/bloxstrap/releases/latest), and run it. Configure your preferences if needed, and install. That's about it! From e96c17a3d509e7c2c443553e094a624eda0721cb Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Mon, 26 Aug 2024 19:26:05 +0100 Subject: [PATCH 10/19] Move About page to its own separate window --- Bloxstrap/App.xaml.cs | 2 + Bloxstrap/Resources/Strings.Designer.cs | 27 + Bloxstrap/Resources/Strings.resx | 9 + Bloxstrap/UI/Elements/About/MainWindow.xaml | 48 ++ .../UI/Elements/About/MainWindow.xaml.cs | 35 + .../UI/Elements/About/Pages/AboutPage.xaml | 134 ++++ .../Pages/AboutPage.xaml.cs | 2 +- .../UI/Elements/About/Pages/LicensesPage.xaml | 72 ++ .../Elements/About/Pages/LicensesPage.xaml.cs | 13 + .../Elements/About/Pages/TranslatorsPage.xaml | 484 +++++++++++++ .../About/Pages/TranslatorsPage.xaml.cs | 13 + .../UI/Elements/Dialogs/LaunchMenuDialog.xaml | 8 + .../UI/Elements/Settings/MainWindow.xaml | 2 +- .../UI/Elements/Settings/Pages/AboutPage.xaml | 665 ------------------ .../ViewModels/Dialogs/LaunchMenuViewModel.cs | 6 +- .../Settings/MainWindowViewModel.cs | 6 +- 16 files changed, 857 insertions(+), 669 deletions(-) create mode 100644 Bloxstrap/UI/Elements/About/MainWindow.xaml create mode 100644 Bloxstrap/UI/Elements/About/MainWindow.xaml.cs create mode 100644 Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml rename Bloxstrap/UI/Elements/{Settings => About}/Pages/AboutPage.xaml.cs (86%) create mode 100644 Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml create mode 100644 Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml.cs create mode 100644 Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml create mode 100644 Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml.cs delete mode 100644 Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index db72422..a644408 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -6,6 +6,8 @@ using System.Windows.Threading; using Microsoft.Win32; using Bloxstrap.Models.SettingTasks.Base; +using Bloxstrap.UI.Elements.About.Pages; +using Bloxstrap.UI.Elements.About; namespace Bloxstrap { diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 10e9947..cbfba62 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -60,6 +60,33 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Licenses. + /// + public static string About_Licenses_Title { + get { + return ResourceManager.GetString("About.Licenses.Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to About Bloxstrap. + /// + public static string About_Title { + get { + return ResourceManager.GetString("About.Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Translators. + /// + public static string About_Translators_Title { + get { + return ResourceManager.GetString("About.Translators.Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to left game. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 0fb0885..09587cb 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -1130,4 +1130,13 @@ If not, then please report this exception to the maintainers of this fork. Do NO Please wait for uninstallation to finish. + + About Bloxstrap + + + Licenses + + + Translators + \ No newline at end of file diff --git a/Bloxstrap/UI/Elements/About/MainWindow.xaml b/Bloxstrap/UI/Elements/About/MainWindow.xaml new file mode 100644 index 0000000..a334f3a --- /dev/null +++ b/Bloxstrap/UI/Elements/About/MainWindow.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/About/MainWindow.xaml.cs b/Bloxstrap/UI/Elements/About/MainWindow.xaml.cs new file mode 100644 index 0000000..425b995 --- /dev/null +++ b/Bloxstrap/UI/Elements/About/MainWindow.xaml.cs @@ -0,0 +1,35 @@ +using System.Windows.Controls; +using Wpf.Ui.Controls.Interfaces; +using Wpf.Ui.Mvvm.Contracts; + +namespace Bloxstrap.UI.Elements.About +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : INavigationWindow + { + public MainWindow() + { + InitializeComponent(); + + App.Logger.WriteLine("MainWindow::MainWindow", "Initializing menu"); + } + + #region INavigationWindow methods + + public Frame GetFrame() => RootFrame; + + public INavigation GetNavigation() => RootNavigation; + + public bool Navigate(Type pageType) => RootNavigation.Navigate(pageType); + + public void SetPageService(IPageService pageService) => RootNavigation.PageService = pageService; + + public void ShowWindow() => Show(); + + public void CloseWindow() => Close(); + + #endregion INavigationWindow methods + } +} diff --git a/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml new file mode 100644 index 0000000..8e16999 --- /dev/null +++ b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml.cs b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs similarity index 86% rename from Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml.cs rename to Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs index 75cbd32..3294258 100644 --- a/Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml.cs +++ b/Bloxstrap/UI/Elements/About/Pages/AboutPage.xaml.cs @@ -1,6 +1,6 @@ using Bloxstrap.UI.ViewModels.Settings; -namespace Bloxstrap.UI.Elements.Settings.Pages +namespace Bloxstrap.UI.Elements.About.Pages { /// /// Interaction logic for AboutPage.xaml diff --git a/Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml b/Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml new file mode 100644 index 0000000..8a4f984 --- /dev/null +++ b/Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml.cs b/Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml.cs new file mode 100644 index 0000000..7ce2e61 --- /dev/null +++ b/Bloxstrap/UI/Elements/About/Pages/LicensesPage.xaml.cs @@ -0,0 +1,13 @@ +namespace Bloxstrap.UI.Elements.About.Pages +{ + /// + /// Interaction logic for LicensesPage.xaml + /// + public partial class LicensesPage + { + public LicensesPage() + { + InitializeComponent(); + } + } +} diff --git a/Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml b/Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml new file mode 100644 index 0000000..2ad3738 --- /dev/null +++ b/Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml.cs b/Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml.cs new file mode 100644 index 0000000..fb41f0a --- /dev/null +++ b/Bloxstrap/UI/Elements/About/Pages/TranslatorsPage.xaml.cs @@ -0,0 +1,13 @@ +namespace Bloxstrap.UI.Elements.About.Pages +{ + /// + /// Interaction logic for TranslatorsPage.xaml + /// + public partial class TranslatorsPage + { + public TranslatorsPage() + { + InitializeComponent(); + } + } +} diff --git a/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml index 446b34c..b7bce3b 100644 --- a/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml +++ b/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml @@ -41,6 +41,8 @@ + + @@ -48,6 +50,12 @@ + + + + + + diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml index 49f508a..4d6fc63 100644 --- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml +++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml @@ -60,7 +60,7 @@ - + diff --git a/Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml b/Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml deleted file mode 100644 index 830e14b..0000000 --- a/Bloxstrap/UI/Elements/Settings/Pages/AboutPage.xaml +++ /dev/null @@ -1,665 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs b/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs index 821bd02..991ae8d 100644 --- a/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs @@ -1,7 +1,7 @@ using System.Windows.Input; using CommunityToolkit.Mvvm.Input; -using Bloxstrap.Resources; +using Bloxstrap.UI.Elements.About; namespace Bloxstrap.UI.ViewModels.Installer { @@ -14,10 +14,14 @@ namespace Bloxstrap.UI.ViewModels.Installer public ICommand LaunchRobloxCommand => new RelayCommand(LaunchRoblox); + public ICommand LaunchAboutCommand => new RelayCommand(LaunchAbout); + public event EventHandler? CloseWindowRequest; private void LaunchSettings() => CloseWindowRequest?.Invoke(this, NextAction.LaunchSettings); private void LaunchRoblox() => CloseWindowRequest?.Invoke(this, NextAction.LaunchRoblox); + + private void LaunchAbout() => new MainWindow().Show(); } } diff --git a/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs index df279e5..767bb0b 100644 --- a/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs @@ -1,15 +1,19 @@ using System.Windows.Input; - +using Bloxstrap.UI.Elements.About; using CommunityToolkit.Mvvm.Input; namespace Bloxstrap.UI.ViewModels.Settings { public class MainWindowViewModel : NotifyPropertyChangedViewModel { + public ICommand OpenAboutCommand => new RelayCommand(OpenAbout); + public ICommand SaveSettingsCommand => new RelayCommand(SaveSettings); public EventHandler? RequestSaveNoticeEvent; + private void OpenAbout() => new MainWindow().ShowDialog(); + private void SaveSettings() { const string LOG_IDENT = "MainWindowViewModel::SaveSettings"; From b8722f24298d75515eb66ae0b7aed769c14a660a Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Tue, 27 Aug 2024 09:33:16 +0100 Subject: [PATCH 11/19] Restyle launch menu --- .../UI/Elements/Dialogs/LaunchMenuDialog.xaml | 78 ++++++++++++------- .../UI/ViewModels/Settings/AboutViewModel.cs | 2 +- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml index b7bce3b..ea14175 100644 --- a/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml +++ b/Bloxstrap/UI/Elements/Dialogs/LaunchMenuDialog.xaml @@ -12,7 +12,7 @@ Title="Bloxstrap" MinWidth="0" MinHeight="0" - Width="320" + Width="580" SizeToContent="Height" ResizeMode="NoResize" Background="{ui:ThemeResource ApplicationBackgroundBrush}" @@ -24,39 +24,61 @@ - + - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + + + + + - + + + + + - - - - - - + - - - - - - - - + + + + + + + + diff --git a/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs index 9f99d63..00cb254 100644 --- a/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs @@ -4,7 +4,7 @@ namespace Bloxstrap.UI.ViewModels.Settings { public class AboutViewModel { - public string Version => string.Format(Resources.Strings.Menu_About_Version, App.Version); + public string Version => string.Format(Strings.Menu_About_Version, App.Version); public BuildMetadataAttribute BuildMetadata => App.BuildMetadata; From 91e2c45f0c79df064eee5a3dd7b21c5440e12723 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Tue, 27 Aug 2024 11:25:03 +0100 Subject: [PATCH 12/19] Issue reporting autofill + shorthand build checks --- Bloxstrap/App.xaml.cs | 10 ++++++--- Bloxstrap/Bootstrapper.cs | 2 +- Bloxstrap/Logger.cs | 15 ++++++------- .../Elements/Dialogs/ExceptionDialog.xaml.cs | 22 +++++++++++++------ .../UI/ViewModels/Settings/AboutViewModel.cs | 4 ++-- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index a644408..1ad9b69 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -31,6 +31,10 @@ namespace Bloxstrap public static string Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2]; + public static bool IsActionBuild => !String.IsNullOrEmpty(BuildMetadata.CommitRef); + + public static bool IsProductionBuild => IsActionBuild && BuildMetadata.CommitRef.StartsWith("tag", StringComparison.Ordinal); + public static readonly MD5 MD5Provider = MD5.Create(); public static NotifyIconWrapper? NotifyIcon { get; set; } @@ -105,10 +109,10 @@ namespace Bloxstrap Logger.WriteLine(LOG_IDENT, $"Starting {ProjectName} v{Version}"); - if (String.IsNullOrEmpty(BuildMetadata.CommitHash)) - Logger.WriteLine(LOG_IDENT, $"Compiled {BuildMetadata.Timestamp.ToFriendlyString()} from {BuildMetadata.Machine}"); - else + if (IsActionBuild) Logger.WriteLine(LOG_IDENT, $"Compiled {BuildMetadata.Timestamp.ToFriendlyString()} from commit {BuildMetadata.CommitHash} ({BuildMetadata.CommitRef})"); + else + Logger.WriteLine(LOG_IDENT, $"Compiled {BuildMetadata.Timestamp.ToFriendlyString()} from {BuildMetadata.Machine}"); Logger.WriteLine(LOG_IDENT, $"Loaded from {Paths.Process}"); diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index c23d17d..db5b7e0 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -528,7 +528,7 @@ namespace Bloxstrap var versionComparison = Utilities.CompareVersions(App.Version, releaseInfo.TagName); // check if we aren't using a deployed build, so we can update to one if a new version comes out - if (versionComparison == VersionComparison.Equal && App.BuildMetadata.CommitRef.StartsWith("tag") || versionComparison == VersionComparison.GreaterThan) + if (versionComparison == VersionComparison.Equal && App.IsProductionBuild || versionComparison == VersionComparison.GreaterThan) { App.Logger.WriteLine(LOG_IDENT, $"No updates found"); return; diff --git a/Bloxstrap/Logger.cs b/Bloxstrap/Logger.cs index 2eea25d..bbaa40a 100644 --- a/Bloxstrap/Logger.cs +++ b/Bloxstrap/Logger.cs @@ -7,7 +7,7 @@ private readonly SemaphoreSlim _semaphore = new(1, 1); private FileStream? _filestream; - public readonly List Backlog = new(); + public readonly List History = new(); public bool Initialized = false; public bool NoWriteMode = false; public string? FileLocation; @@ -55,7 +55,7 @@ WriteLine(LOG_IDENT, $"Failed to initialize because Bloxstrap cannot write to {directory}"); Frontend.ShowMessageBox( - String.Format(Resources.Strings.Logger_NoWriteMode, directory), + String.Format(Strings.Logger_NoWriteMode, directory), System.Windows.MessageBoxImage.Warning, System.Windows.MessageBoxButton.OK ); @@ -68,8 +68,8 @@ Initialized = true; - if (Backlog.Count > 0) - WriteToLog(string.Join("\r\n", Backlog)); + if (History.Count > 0) + WriteToLog(string.Join("\r\n", History)); WriteLine(LOG_IDENT, "Finished initializing!"); @@ -102,10 +102,12 @@ { string timestamp = DateTime.UtcNow.ToString("s") + "Z"; string outcon = $"{timestamp} {message}"; - string outlog = outcon.Replace(Paths.UserProfile, "%UserProfile%"); + string outlog = outcon.Replace(Paths.UserProfile, "%UserProfile%", StringComparison.InvariantCultureIgnoreCase); Debug.WriteLine(outcon); WriteToLog(outlog); + + History.Add(outlog); } public void WriteLine(string identifier, string message) => WriteLine($"[{identifier}] {message}"); @@ -122,10 +124,7 @@ private async void WriteToLog(string message) { if (!Initialized) - { - Backlog.Add(message); return; - } try { diff --git a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs index 7205b37..dea2842 100644 --- a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs +++ b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml.cs @@ -1,13 +1,11 @@ using System.Media; +using System.Web; using System.Windows; -using System.Windows.Controls; using System.Windows.Interop; using Windows.Win32; using Windows.Win32.Foundation; -using Bloxstrap.Resources; - namespace Bloxstrap.UI.Elements.Dialogs { // hmm... do i use MVVM for this? @@ -26,10 +24,20 @@ namespace Bloxstrap.UI.Elements.Dialogs if (!App.Logger.Initialized) LocateLogFileButton.Content = Strings.Dialog_Exception_CopyLogContents; - string helpMessage = String.Format(Strings.Dialog_Exception_Info_2, "https://github.com/pizzaboxer/bloxstrap/wiki", "https://github.com/pizzaboxer/bloxstrap/issues/new?template=bug_report.yaml"); + string repoUrl = $"https://github.com/{App.ProjectRepository}"; + string wikiUrl = $"{repoUrl}/wiki"; - if (String.IsNullOrEmpty(App.BuildMetadata.CommitHash)) - helpMessage = String.Format(Strings.Dialog_Exception_Info_2_Alt, "https://github.com/pizzaboxer/bloxstrap/wiki"); + string issueUrl = String.Format( + "{0}/issues/new?template=bug_report.yaml&title={1}&what-happened={2}", + repoUrl, + HttpUtility.UrlEncode($"[BUG] {exception.GetType()}: {exception.Message}"), + HttpUtility.UrlEncode($"Log:\n```\n{String.Join('\n', App.Logger.History)}\n```") + ); + + string helpMessage = String.Format(Strings.Dialog_Exception_Info_2, wikiUrl, issueUrl); + + if (!App.IsActionBuild && !App.BuildMetadata.Machine.Contains("pizzaboxer", StringComparison.Ordinal)) + helpMessage = String.Format(Strings.Dialog_Exception_Info_2_Alt, wikiUrl); HelpMessageMDTextBlock.MarkdownText = helpMessage; @@ -38,7 +46,7 @@ namespace Bloxstrap.UI.Elements.Dialogs if (App.Logger.Initialized) Process.Start("explorer.exe", $"/select,\"{App.Logger.FileLocation}\""); else - Clipboard.SetDataObject(String.Join("\r\n", App.Logger.Backlog)); + Clipboard.SetDataObject(String.Join("\r\n", App.Logger.History)); }; CloseButton.Click += delegate diff --git a/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs index 00cb254..5cfeb0d 100644 --- a/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Settings/AboutViewModel.cs @@ -11,7 +11,7 @@ namespace Bloxstrap.UI.ViewModels.Settings public string BuildTimestamp => BuildMetadata.Timestamp.ToFriendlyString(); public string BuildCommitHashUrl => $"https://github.com/{App.ProjectRepository}/commit/{BuildMetadata.CommitHash}"; - public Visibility BuildInformationVisibility => BuildMetadata.CommitRef.StartsWith("tag") ? Visibility.Collapsed : Visibility.Visible; - public Visibility BuildCommitVisibility => string.IsNullOrEmpty(BuildMetadata.CommitHash) ? Visibility.Collapsed : Visibility.Visible; + public Visibility BuildInformationVisibility => App.IsProductionBuild ? Visibility.Collapsed : Visibility.Visible; + public Visibility BuildCommitVisibility => App.IsActionBuild ? Visibility.Visible : Visibility.Collapsed; } } From 0544f137d000f285e483a216e4e08a246a4a5861 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Tue, 27 Aug 2024 11:37:59 +0100 Subject: [PATCH 13/19] Remove preset registration for Vulkan/OpenGL --- Bloxstrap/Enums/FlagPresets/RenderingMode.cs | 2 -- Bloxstrap/FastFlagManager.cs | 15 --------------- .../UI/ViewModels/Settings/FastFlagsViewModel.cs | 6 +----- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/Bloxstrap/Enums/FlagPresets/RenderingMode.cs b/Bloxstrap/Enums/FlagPresets/RenderingMode.cs index 769a430..082d301 100644 --- a/Bloxstrap/Enums/FlagPresets/RenderingMode.cs +++ b/Bloxstrap/Enums/FlagPresets/RenderingMode.cs @@ -4,9 +4,7 @@ { [EnumName(FromTranslation = "Common.Automatic")] Default, - // Vulkan, D3D11, D3D10, - // OpenGL } } diff --git a/Bloxstrap/FastFlagManager.cs b/Bloxstrap/FastFlagManager.cs index 492bb68..d702c22 100644 --- a/Bloxstrap/FastFlagManager.cs +++ b/Bloxstrap/FastFlagManager.cs @@ -28,9 +28,6 @@ namespace Bloxstrap { "Rendering.Mode.D3D11", "FFlagDebugGraphicsPreferD3D11" }, { "Rendering.Mode.D3D10", "FFlagDebugGraphicsPreferD3D11FL10" }, - { "Rendering.Mode.Vulkan", "FFlagDebugGraphicsPreferVulkan" }, - { "Rendering.Mode.Vulkan.Fix", "FFlagRenderVulkanFixMinimizeWindow" }, - { "Rendering.Mode.OpenGL", "FFlagDebugGraphicsPreferOpenGL" }, { "Rendering.Lighting.Voxel", "DFFlagDebugRenderForceTechnologyVoxel" }, { "Rendering.Lighting.ShadowMap", "FFlagDebugForceFutureIsBrightPhase2" }, @@ -63,10 +60,8 @@ namespace Bloxstrap public static IReadOnlyDictionary RenderingModes => new Dictionary { { RenderingMode.Default, "None" }, - // { RenderingMode.Vulkan, "Vulkan" }, { RenderingMode.D3D11, "D3D11" }, { RenderingMode.D3D10, "D3D10" }, - // { RenderingMode.OpenGL, "OpenGL" } }; public static IReadOnlyDictionary LightingModes => new Dictionary @@ -228,14 +223,6 @@ namespace Bloxstrap return mapping.First().Key; } - public void CheckManualFullscreenPreset() - { - if (GetPreset("Rendering.Mode.Vulkan") == "True" || GetPreset("Rendering.Mode.OpenGL") == "True") - SetPreset("Rendering.ManualFullscreen", null); - else - SetPreset("Rendering.ManualFullscreen", "False"); - } - public override void Save() { // convert all flag values to strings before saving @@ -250,8 +237,6 @@ namespace Bloxstrap { base.Load(); - CheckManualFullscreenPreset(); - // TODO - remove when activity tracking has been revamped if (GetPreset("Network.Log") != "7") SetPreset("Network.Log", "7"); diff --git a/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs index d9902b7..9d0a35e 100644 --- a/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Settings/FastFlagsViewModel.cs @@ -87,11 +87,7 @@ namespace Bloxstrap.UI.ViewModels.Settings public RenderingMode SelectedRenderingMode { get => App.FastFlags.GetPresetEnum(RenderingModes, "Rendering.Mode", "True"); - set - { - App.FastFlags.SetPresetEnum("Rendering.Mode", RenderingModes[value], "True"); - App.FastFlags.CheckManualFullscreenPreset(); - } + set => App.FastFlags.SetPresetEnum("Rendering.Mode", RenderingModes[value], "True"); } public bool FixDisplayScaling From bac13eb507961e52124f8df541bd2463e56f732e Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Tue, 27 Aug 2024 12:42:16 +0100 Subject: [PATCH 14/19] Warn about unsaved changes when closing settings --- Bloxstrap/FastFlagManager.cs | 8 ++++++++ Bloxstrap/JsonManager.cs | 2 ++ .../Models/SettingTasks/Base/BoolBaseTask.cs | 8 ++++++-- .../Models/SettingTasks/Base/EnumBaseTask.cs | 8 ++++++-- .../SettingTasks/Base/StringBaseTask.cs | 8 ++++++-- Bloxstrap/Resources/Strings.Designer.cs | 9 +++++++++ Bloxstrap/Resources/Strings.resx | 3 +++ .../UI/Elements/Settings/MainWindow.xaml | 5 +++-- .../UI/Elements/Settings/MainWindow.xaml.cs | 19 +++++++++++++++++-- 9 files changed, 60 insertions(+), 10 deletions(-) diff --git a/Bloxstrap/FastFlagManager.cs b/Bloxstrap/FastFlagManager.cs index d702c22..f211752 100644 --- a/Bloxstrap/FastFlagManager.cs +++ b/Bloxstrap/FastFlagManager.cs @@ -6,6 +6,8 @@ namespace Bloxstrap { public override string FileLocation => Path.Combine(Paths.Modifications, "ClientSettings\\ClientAppSettings.json"); + public bool Changed => !OriginalProp.SequenceEqual(Prop); + public static IReadOnlyDictionary PresetFlags = new Dictionary { { "Network.Log", "FLogNetwork" }, @@ -231,12 +233,18 @@ namespace Bloxstrap Prop[pair.Key] = pair.Value.ToString()!; base.Save(); + + // clone the dictionary + OriginalProp = new(Prop); } public override void Load() { base.Load(); + // clone the dictionary + OriginalProp = new(Prop); + // TODO - remove when activity tracking has been revamped if (GetPreset("Network.Log") != "7") SetPreset("Network.Log", "7"); diff --git a/Bloxstrap/JsonManager.cs b/Bloxstrap/JsonManager.cs index 1a91949..a8e8c3e 100644 --- a/Bloxstrap/JsonManager.cs +++ b/Bloxstrap/JsonManager.cs @@ -4,6 +4,8 @@ namespace Bloxstrap { public class JsonManager where T : class, new() { + public T OriginalProp { get; set; } = new(); + public T Prop { get; set; } = new(); public virtual string FileLocation => Path.Combine(Paths.Base, $"{typeof(T).Name}.json"); diff --git a/Bloxstrap/Models/SettingTasks/Base/BoolBaseTask.cs b/Bloxstrap/Models/SettingTasks/Base/BoolBaseTask.cs index b22b067..5b5adce 100644 --- a/Bloxstrap/Models/SettingTasks/Base/BoolBaseTask.cs +++ b/Bloxstrap/Models/SettingTasks/Base/BoolBaseTask.cs @@ -29,12 +29,16 @@ namespace Bloxstrap.Models.SettingTasks.Base set { - App.PendingSettingTasks[Name] = this; _newState = value; + + if (Changed) + App.PendingSettingTasks[Name] = this; + else + App.PendingSettingTasks.Remove(Name); } } - public override bool Changed => NewState != OriginalState; + public override bool Changed => _newState != OriginalState; public BoolBaseTask(string prefix, string name) : base(prefix, name) { } } diff --git a/Bloxstrap/Models/SettingTasks/Base/EnumBaseTask.cs b/Bloxstrap/Models/SettingTasks/Base/EnumBaseTask.cs index 56a8c47..c64e0a0 100644 --- a/Bloxstrap/Models/SettingTasks/Base/EnumBaseTask.cs +++ b/Bloxstrap/Models/SettingTasks/Base/EnumBaseTask.cs @@ -23,12 +23,16 @@ set { - App.PendingSettingTasks[Name] = this; _newState = value; + + if (Changed) + App.PendingSettingTasks[Name] = this; + else + App.PendingSettingTasks.Remove(Name); } } - public override bool Changed => !NewState.Equals(OriginalState); + public override bool Changed => !_newState.Equals(OriginalState); public IEnumerable Selections { get; private set; } = Enum.GetValues(typeof(T)).Cast().OrderBy(x => diff --git a/Bloxstrap/Models/SettingTasks/Base/StringBaseTask.cs b/Bloxstrap/Models/SettingTasks/Base/StringBaseTask.cs index dd4a07f..6b3ce13 100644 --- a/Bloxstrap/Models/SettingTasks/Base/StringBaseTask.cs +++ b/Bloxstrap/Models/SettingTasks/Base/StringBaseTask.cs @@ -29,12 +29,16 @@ namespace Bloxstrap.Models.SettingTasks.Base set { - App.PendingSettingTasks[Name] = this; _newState = value; + + if (Changed) + App.PendingSettingTasks[Name] = this; + else + App.PendingSettingTasks.Remove(Name); } } - public override bool Changed => NewState != OriginalState; + public override bool Changed => _newState != OriginalState; public StringBaseTask(string prefix, string name) : base(prefix, name) { } } diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index cbfba62..8a77c15 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -2969,6 +2969,15 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to You have unsaved changes. Are you sure you want to close without saving?. + /// + public static string Menu_UnsavedChanges { + get { + return ResourceManager.GetString("Menu.UnsavedChanges", resourceCulture); + } + } + /// /// Looks up a localized string similar to They'll be kept where Bloxstrap was installed, and will automatically be restored on a reinstall.. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 09587cb..0b01859 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -1139,4 +1139,7 @@ If not, then please report this exception to the maintainers of this fork. Do NO Translators + + You have unsaved changes. Are you sure you want to close without saving? + \ No newline at end of file diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml index 4d6fc63..7012567 100644 --- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml +++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml @@ -15,7 +15,8 @@ Background="{ui:ThemeResource ApplicationBackgroundBrush}" ExtendsContentIntoTitleBar="True" WindowBackdropType="Mica" - WindowStartupLocation="CenterScreen"> + WindowStartupLocation="CenterScreen" + Closing="WpfUiWindow_Closing"> @@ -93,7 +94,7 @@ - + diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs index c877bae..c22a732 100644 --- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs +++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs @@ -1,6 +1,10 @@ -using System.Windows.Controls; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; + using Wpf.Ui.Controls.Interfaces; using Wpf.Ui.Mvvm.Contracts; + using Bloxstrap.UI.ViewModels.Settings; namespace Bloxstrap.UI.Elements.Settings @@ -22,7 +26,7 @@ namespace Bloxstrap.UI.Elements.Settings App.Logger.WriteLine("MainWindow::MainWindow", "Initializing menu"); #if DEBUG // easier access - EditorWarningNavItem.Visibility = System.Windows.Visibility.Visible; + EditorWarningNavItem.Visibility = Visibility.Visible; #endif if (showAlreadyRunningWarning) @@ -50,5 +54,16 @@ namespace Bloxstrap.UI.Elements.Settings public void CloseWindow() => Close(); #endregion INavigationWindow methods + + private void WpfUiWindow_Closing(object sender, CancelEventArgs e) + { + if (App.FastFlags.Changed || App.PendingSettingTasks.Any()) + { + var result = Frontend.ShowMessageBox(Strings.Menu_UnsavedChanges, MessageBoxImage.Warning, MessageBoxButton.YesNo); + + if (result != MessageBoxResult.Yes) + e.Cancel = true; + } + } } } From 9a412ea17bf944f309d2ee33c6d286c9f005615b Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Tue, 27 Aug 2024 12:53:03 +0100 Subject: [PATCH 15/19] Warn about in-place downgrades --- Bloxstrap/App.xaml.cs | 2 ++ Bloxstrap/Installer.cs | 15 +++++++++++++++ Bloxstrap/Resources/Strings.Designer.cs | 11 +++++++++++ Bloxstrap/Resources/Strings.resx | 5 +++++ 4 files changed, 33 insertions(+) diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 1ad9b69..f244c7c 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -220,8 +220,10 @@ namespace Bloxstrap Locale.Set(Settings.Prop.Locale); +#if !DEBUG if (!LaunchSettings.UninstallFlag.Active) Installer.HandleUpgrade(); +#endif LaunchHandler.ProcessLaunchArgs(); } diff --git a/Bloxstrap/Installer.cs b/Bloxstrap/Installer.cs index ccb458c..849a834 100644 --- a/Bloxstrap/Installer.cs +++ b/Bloxstrap/Installer.cs @@ -340,6 +340,18 @@ namespace Bloxstrap if (MD5Hash.FromFile(Paths.Process) == MD5Hash.FromFile(Paths.Application)) return; + if (currentVer is not null && existingVer is not null && Utilities.CompareVersions(currentVer, existingVer) == VersionComparison.LessThan) + { + var result = Frontend.ShowMessageBox( + Strings.InstallChecker_VersionLessThanInstalled, + MessageBoxImage.Question, + MessageBoxButton.YesNo + ); + + if (result != MessageBoxResult.Yes) + return; + } + // silently upgrade version if the command line flag is set or if we're launching from an auto update if (!App.LaunchSettings.UpgradeFlag.Active && !isAutoUpgrade) { @@ -466,6 +478,9 @@ namespace Bloxstrap App.FastFlags.Save(); } + if (currentVer is null) + return; + if (isAutoUpgrade) { Utilities.ShellExecute($"https://github.com/{App.ProjectRepository}/wiki/Release-notes-for-Bloxstrap-v{currentVer}"); diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 8a77c15..61592fe 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -1276,6 +1276,17 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to The version of Bloxstrap you've launched is older than the version you currently have installed. + ///Issues may occur and your settings may be altered. A reinstall is recommended. + ///Are you sure you want to continue?. + /// + public static string InstallChecker_VersionLessThanInstalled { + get { + return ResourceManager.GetString("InstallChecker.VersionLessThanInstalled", resourceCulture); + } + } + /// /// Looks up a localized string similar to Will drop you into the desktop app once everything's done. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 0b01859..70f23a2 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -1142,4 +1142,9 @@ If not, then please report this exception to the maintainers of this fork. Do NO You have unsaved changes. Are you sure you want to close without saving? + + The version of Bloxstrap you've launched is older than the version you currently have installed. +Issues may occur and your settings may be altered. A reinstall is recommended. +Are you sure you want to continue? + \ No newline at end of file From cf45d9c80862f9d871891676955341f524a34fc4 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Tue, 27 Aug 2024 14:09:56 +0100 Subject: [PATCH 16/19] More JSON autocorrecting (#2733) --- .../Settings/Pages/FastFlagEditorPage.xaml.cs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs b/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs index e6efa2a..b9bc747 100644 --- a/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs +++ b/Bloxstrap/UI/Elements/Settings/Pages/FastFlagEditorPage.xaml.cs @@ -137,7 +137,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages } else { - Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information); + Frontend.ShowMessageBox(Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information); bool refresh = false; @@ -175,7 +175,14 @@ namespace Bloxstrap.UI.Elements.Settings.Pages json = '{' + json; if (!json.EndsWith('}')) - json += '}'; + { + int lastIndex = json.LastIndexOf('}'); + + if (lastIndex == -1) + json += '}'; + else + json = json.Substring(0, lastIndex+1); + } try { @@ -193,7 +200,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages catch (Exception ex) { Frontend.ShowMessageBox( - String.Format(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidJSON, ex.Message), + String.Format(Strings.Menu_FastFlagEditor_InvalidJSON, ex.Message), MessageBoxImage.Error ); @@ -205,7 +212,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages if (list.Count > 16) { var result = Frontend.ShowMessageBox( - Bloxstrap.Resources.Strings.Menu_FastFlagEditor_LargeConfig, + Strings.Menu_FastFlagEditor_LargeConfig, MessageBoxImage.Warning, MessageBoxButton.YesNo ); @@ -222,7 +229,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages int count = conflictingFlags.Count(); string message = String.Format( - Bloxstrap.Resources.Strings.Menu_FastFlagEditor_ConflictingImport, + Strings.Menu_FastFlagEditor_ConflictingImport, count, String.Join(", ", conflictingFlags.Take(25)) ); @@ -263,16 +270,16 @@ namespace Bloxstrap.UI.Elements.Settings.Pages string errorMessage = ""; if (!_validPrefixes.Any(name.StartsWith)) - errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPrefix; + errorMessage = Strings.Menu_FastFlagEditor_InvalidPrefix; else if (!name.All(x => char.IsLetterOrDigit(x) || x == '_')) - errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidCharacter; + errorMessage = Strings.Menu_FastFlagEditor_InvalidCharacter; if (name.EndsWith("_PlaceFilter") || name.EndsWith("_DataCenterFilter")) - errorMessage = !ValidateFilter(name, value) ? Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidPlaceFilter : ""; + errorMessage = !ValidateFilter(name, value) ? Strings.Menu_FastFlagEditor_InvalidPlaceFilter : ""; else if ((name.StartsWith("FInt") || name.StartsWith("DFInt")) && !Int32.TryParse(value, out _)) - errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidNumberValue; + errorMessage = Strings.Menu_FastFlagEditor_InvalidNumberValue; else if ((name.StartsWith("FFlag") || name.StartsWith("DFFlag")) && lowerValue != "true" && lowerValue != "false") - errorMessage = Bloxstrap.Resources.Strings.Menu_FastFlagEditor_InvalidBoolValue; + errorMessage = Strings.Menu_FastFlagEditor_InvalidBoolValue; if (!String.IsNullOrEmpty(errorMessage)) { @@ -319,7 +326,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages if (App.FastFlags.GetValue(newName) is not null) { - Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information); + Frontend.ShowMessageBox(Strings.Menu_FastFlagEditor_AlreadyExists, MessageBoxImage.Information); e.Cancel = true; textbox.Text = oldName; return; @@ -387,7 +394,7 @@ namespace Bloxstrap.UI.Elements.Settings.Pages { string json = JsonSerializer.Serialize(App.FastFlags.Prop, new JsonSerializerOptions { WriteIndented = true }); Clipboard.SetDataObject(json); - Frontend.ShowMessageBox(Bloxstrap.Resources.Strings.Menu_FastFlagEditor_JsonCopiedToClipboard, MessageBoxImage.Information); + Frontend.ShowMessageBox(Strings.Menu_FastFlagEditor_JsonCopiedToClipboard, MessageBoxImage.Information); } private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) From fd290f9ff79ba76474f0c3fb2db51199f4ba3c06 Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Wed, 28 Aug 2024 22:39:49 +0100 Subject: [PATCH 17/19] Move activity watcher to separate process (#810) this was done to: - ensure robloxplayerbeta launches as an orphaned process - help alleviate problems with multiple instances - alleviate problems with the notifyicon causing blocking conflicts on the bootstrapper ui thread - help reduce functional dependency on the bootstrapper, makes it less monolithic and more maintainable ive always wanted to do this for a long while, but have always put it off because of how painful it would be this may genuinely be the most painful refactoring i've ever had to do, but after 2 days, i managed to do it, and it works great! --- Bloxstrap/App.xaml.cs | 39 ++--- Bloxstrap/Bootstrapper.cs | 90 +++-------- Bloxstrap/Integrations/ActivityWatcher.cs | 34 ++-- Bloxstrap/Integrations/DiscordRichPresence.cs | 3 + Bloxstrap/LaunchHandler.cs | 72 +++++---- Bloxstrap/LaunchSettings.cs | 2 +- Bloxstrap/Properties/launchSettings.json | 4 + .../Elements/ContextMenu/MenuContainer.xaml | 4 +- .../ContextMenu/MenuContainer.xaml.cs | 47 +++--- .../ContextMenu/ServerInformation.xaml.cs | 8 +- .../UI/Elements/Settings/MainWindow.xaml | 2 +- .../UI/Elements/Settings/MainWindow.xaml.cs | 5 + Bloxstrap/UI/NotifyIconWrapper.cs | 70 +++------ .../ContextMenu/ServerInformationViewModel.cs | 19 ++- .../ViewModels/Dialogs/LaunchMenuViewModel.cs | 2 +- .../Settings/MainWindowViewModel.cs | 8 +- Bloxstrap/Watcher.cs | 147 ++++++++++++++++++ 17 files changed, 329 insertions(+), 227 deletions(-) create mode 100644 Bloxstrap/Watcher.cs diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index f244c7c..651806d 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -8,6 +8,7 @@ using Microsoft.Win32; using Bloxstrap.Models.SettingTasks.Base; using Bloxstrap.UI.Elements.About.Pages; using Bloxstrap.UI.Elements.About; +using System; namespace Bloxstrap { @@ -37,8 +38,6 @@ namespace Bloxstrap public static readonly MD5 MD5Provider = MD5.Create(); - public static NotifyIconWrapper? NotifyIcon { get; set; } - public static readonly Logger Logger = new(); public static readonly Dictionary PendingSettingTasks = new(); @@ -55,19 +54,23 @@ namespace Bloxstrap ) ); -#if RELEASE private static bool _showingExceptionDialog = false; -#endif + + private static bool _terminating = false; public static void Terminate(ErrorCode exitCode = ErrorCode.ERROR_SUCCESS) { + if (_terminating) + return; + int exitCodeNum = (int)exitCode; Logger.WriteLine("App::Terminate", $"Terminating with exit code {exitCodeNum} ({exitCode})"); - NotifyIcon?.Dispose(); + Current.Dispatcher.Invoke(() => Current.Shutdown(exitCodeNum)); + // Environment.Exit(exitCodeNum); - Environment.Exit(exitCodeNum); + _terminating = true; } void GlobalExceptionHandler(object sender, DispatcherUnhandledExceptionEventArgs e) @@ -79,24 +82,28 @@ namespace Bloxstrap FinalizeExceptionHandling(e.Exception); } - public static void FinalizeExceptionHandling(Exception exception, bool log = true) + public static void FinalizeExceptionHandling(AggregateException ex) + { + foreach (var innerEx in ex.InnerExceptions) + Logger.WriteException("App::FinalizeExceptionHandling", innerEx); + + FinalizeExceptionHandling(ex.GetBaseException(), false); + } + + public static void FinalizeExceptionHandling(Exception ex, bool log = true) { if (log) - Logger.WriteException("App::FinalizeExceptionHandling", exception); + Logger.WriteException("App::FinalizeExceptionHandling", ex); -#if DEBUG - throw exception; -#else if (_showingExceptionDialog) return; _showingExceptionDialog = true; if (!LaunchSettings.QuietFlag.Active) - Frontend.ShowExceptionDialog(exception); + Frontend.ShowExceptionDialog(ex); Terminate(ErrorCode.ERROR_INSTALL_FAILURE); -#endif } protected override void OnStartup(StartupEventArgs e) @@ -208,10 +215,6 @@ namespace Bloxstrap State.Load(); FastFlags.Load(); - // we can only parse them now as settings need - // to be loaded first to know what our channel is - // LaunchSettings.ParseRoblox(); - if (!Locale.SupportedLocales.ContainsKey(Settings.Prop.Locale)) { Settings.Prop.Locale = "nil"; @@ -228,7 +231,7 @@ namespace Bloxstrap LaunchHandler.ProcessLaunchArgs(); } - Terminate(); + // you must *explicitly* call terminate when everything is done, it won't be called implicitly } } } diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index db5b7e0..0fc6171 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -3,8 +3,6 @@ using System.Windows.Forms; using Microsoft.Win32; -using Bloxstrap.Integrations; -using Bloxstrap.Resources; using Bloxstrap.AppData; namespace Bloxstrap @@ -289,9 +287,6 @@ namespace Bloxstrap _launchCommandLine = _launchCommandLine.Replace("robloxLocale:en_us", $"robloxLocale:{match.Groups[1].Value}", StringComparison.InvariantCultureIgnoreCase); } - // whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes - bool shouldWait = false; - var startInfo = new ProcessStartInfo() { FileName = _playerLocation, @@ -308,19 +303,16 @@ namespace Bloxstrap // v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now int gameClientPid; - using (Process gameClient = Process.Start(startInfo)!) + using (var gameClient = Process.Start(startInfo)!) { gameClientPid = gameClient.Id; } - List autocloseProcesses = new(); - ActivityWatcher? activityWatcher = null; - DiscordRichPresence? richPresence = null; - App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid})"); using (var startEvent = new SystemEvent(AppData.StartEvent)) { + // TODO: get rid of this bool startEventFired = await startEvent.WaitForEvent(); startEvent.Close(); @@ -330,40 +322,14 @@ namespace Bloxstrap return; } - if (App.Settings.Prop.EnableActivityTracking && _launchMode == LaunchMode.Player) - App.NotifyIcon?.SetProcessId(gameClientPid); - - if (App.Settings.Prop.EnableActivityTracking) - { - activityWatcher = new(gameClientPid); - shouldWait = true; - - App.NotifyIcon?.SetActivityWatcher(activityWatcher); - - if (App.Settings.Prop.UseDisableAppPatch) - { - activityWatcher.OnAppClose += (_, _) => - { - App.Logger.WriteLine(LOG_IDENT, "Received desktop app exit, closing Roblox"); - using var process = Process.GetProcessById(gameClientPid); - process.CloseMainWindow(); - }; - } - - if (App.Settings.Prop.UseDiscordRichPresence) - { - App.Logger.WriteLine(LOG_IDENT, "Using Discord Rich Presence"); - richPresence = new(activityWatcher); - - App.NotifyIcon?.SetRichPresenceHandler(richPresence); - } - } + var autoclosePids = new List(); // launch custom integrations now - foreach (CustomIntegration integration in App.Settings.Prop.CustomIntegrations) + foreach (var integration in App.Settings.Prop.CustomIntegrations) { App.Logger.WriteLine(LOG_IDENT, $"Launching custom integration '{integration.Name}' ({integration.Location} {integration.LaunchArgs} - autoclose is {integration.AutoClose})"); + int pid = 0; try { var process = Process.Start(new ProcessStartInfo @@ -372,48 +338,34 @@ namespace Bloxstrap Arguments = integration.LaunchArgs.Replace("\r\n", " "), WorkingDirectory = Path.GetDirectoryName(integration.Location), UseShellExecute = true - }); + })!; - if (integration.AutoClose) - { - shouldWait = true; - autocloseProcesses.Add(process); - } + pid = process.Id; } catch (Exception ex) { App.Logger.WriteLine(LOG_IDENT, $"Failed to launch integration '{integration.Name}'!"); App.Logger.WriteLine(LOG_IDENT, $"{ex.Message}"); } + + if (integration.AutoClose && pid != 0) + autoclosePids.Add(pid); + } + + using (var proclock = new InterProcessLock("Watcher")) + { + string args = gameClientPid.ToString(); + + if (autoclosePids.Any()) + args += $";{String.Join(',', autoclosePids)}"; + + if (proclock.IsAcquired) + Process.Start(Paths.Process, $"-watcher \"{args}\""); } // event fired, wait for 3 seconds then close await Task.Delay(3000); Dialog?.CloseBootstrapper(); - - // keep bloxstrap open in the background if needed - if (!shouldWait) - return; - - activityWatcher?.StartWatcher(); - - App.Logger.WriteLine(LOG_IDENT, "Waiting for Roblox to close"); - - while (Utilities.GetProcessesSafe().Any(x => x.Id == gameClientPid)) - await Task.Delay(1000); - - App.Logger.WriteLine(LOG_IDENT, $"Roblox has exited"); - - richPresence?.Dispose(); - - foreach (var process in autocloseProcesses) - { - if (process is null || process.HasExited) - continue; - - App.Logger.WriteLine(LOG_IDENT, $"Autoclosing process '{process.ProcessName}' (PID {process.Id})"); - process.Kill(); - } } public void CancelInstall() diff --git a/Bloxstrap/Integrations/ActivityWatcher.cs b/Bloxstrap/Integrations/ActivityWatcher.cs index e15cd2d..fbca8d1 100644 --- a/Bloxstrap/Integrations/ActivityWatcher.cs +++ b/Bloxstrap/Integrations/ActivityWatcher.cs @@ -19,7 +19,6 @@ private const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|[0-9]+"; private const string GameMessageEntryPattern = @"\[BloxstrapRPC\] (.*)"; - private int _gameClientPid; private int _logEntriesRead = 0; private bool _teleportMarker = false; private bool _reservedTeleportMarker = false; @@ -27,6 +26,7 @@ public event EventHandler? OnLogEntry; public event EventHandler? OnGameJoin; public event EventHandler? OnGameLeave; + public event EventHandler? OnLogOpen; public event EventHandler? OnAppClose; public event EventHandler? OnRPCMessage; @@ -47,14 +47,9 @@ public bool IsDisposed = false; - public ActivityWatcher(int gameClientPid) + public async void Start() { - _gameClientPid = gameClientPid; - } - - public async void StartWatcher() - { - const string LOG_IDENT = "ActivityWatcher::StartWatcher"; + const string LOG_IDENT = "ActivityWatcher::Start"; // okay, here's the process: // @@ -84,23 +79,26 @@ { logFileInfo = new DirectoryInfo(logDirectory) .GetFiles() - .Where(x => x.CreationTime <= DateTime.Now) + .Where(x => x.Name.Contains("Player", StringComparison.OrdinalIgnoreCase) && x.CreationTime <= DateTime.Now) .OrderByDescending(x => x.CreationTime) .First(); if (logFileInfo.CreationTime.AddSeconds(15) > DateTime.Now) break; + // TODO: report failure after 10 seconds of no log file App.Logger.WriteLine(LOG_IDENT, $"Could not find recent enough log file, waiting... (newest is {logFileInfo.Name})"); await Task.Delay(1000); } + OnLogOpen?.Invoke(this, EventArgs.Empty); + LogLocation = logFileInfo.FullName; FileStream logFileStream = logFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); App.Logger.WriteLine(LOG_IDENT, $"Opened {LogLocation}"); - AutoResetEvent logUpdatedEvent = new(false); - FileSystemWatcher logWatcher = new() + var logUpdatedEvent = new AutoResetEvent(false); + var logWatcher = new FileSystemWatcher() { Path = logDirectory, Filter = Path.GetFileName(logFileInfo.FullName), @@ -108,7 +106,7 @@ }; logWatcher.Changed += (s, e) => logUpdatedEvent.Set(); - using StreamReader sr = new(logFileStream); + using var sr = new StreamReader(logFileStream); while (!IsDisposed) { @@ -117,13 +115,13 @@ if (log is null) logUpdatedEvent.WaitOne(250); else - ExamineLogEntry(log); + ReadLogEntry(log); } } - private void ExamineLogEntry(string entry) + private void ReadLogEntry(string entry) { - const string LOG_IDENT = "ActivityWatcher::ExamineLogEntry"; + const string LOG_IDENT = "ActivityWatcher::ReadLogEntry"; OnLogEntry?.Invoke(this, entry); @@ -302,7 +300,7 @@ var ipInfo = await Http.GetJson($"https://ipinfo.io/{ActivityMachineAddress}/json"); if (ipInfo is null) - return $"? ({Resources.Strings.ActivityTracker_LookupFailed})"; + return $"? ({Strings.ActivityTracker_LookupFailed})"; if (string.IsNullOrEmpty(ipInfo.Country)) location = "?"; @@ -312,7 +310,7 @@ location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}"; if (!ActivityInGame) - return $"? ({Resources.Strings.ActivityTracker_LeftGame})"; + return $"? ({Strings.ActivityTracker_LeftGame})"; GeolocationCache[ActivityMachineAddress] = location; @@ -323,7 +321,7 @@ App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {ActivityMachineAddress}"); App.Logger.WriteException(LOG_IDENT, ex); - return $"? ({Resources.Strings.ActivityTracker_LookupFailed})"; + return $"? ({Strings.ActivityTracker_LookupFailed})"; } } diff --git a/Bloxstrap/Integrations/DiscordRichPresence.cs b/Bloxstrap/Integrations/DiscordRichPresence.cs index ab920d1..c79fe04 100644 --- a/Bloxstrap/Integrations/DiscordRichPresence.cs +++ b/Bloxstrap/Integrations/DiscordRichPresence.cs @@ -194,6 +194,8 @@ namespace Bloxstrap.Integrations App.Logger.WriteLine(LOG_IDENT, $"Setting presence for Place ID {placeId}"); + // TODO: move this to its own function under the activity watcher? + // TODO: show error if information cannot be queried instead of silently failing var universeIdResponse = await Http.GetJson($"https://apis.roblox.com/universes/v1/places/{placeId}/universe"); if (universeIdResponse is null) { @@ -282,6 +284,7 @@ namespace Bloxstrap.Integrations // this is used for configuration from BloxstrapRPC _currentPresenceCopy = _currentPresence.Clone(); + // TODO: use queue for stashing messages if (_stashedRPCMessage is not null) { App.Logger.WriteLine(LOG_IDENT, "Found stashed RPC message, invoking presence set command now"); diff --git a/Bloxstrap/LaunchHandler.cs b/Bloxstrap/LaunchHandler.cs index 58ef421..a7f5406 100644 --- a/Bloxstrap/LaunchHandler.cs +++ b/Bloxstrap/LaunchHandler.cs @@ -1,11 +1,11 @@ using System.Windows; -using Bloxstrap.UI.Elements.Dialogs; - using Microsoft.Win32; using Windows.Win32; using Windows.Win32.Foundation; +using Bloxstrap.UI.Elements.Dialogs; + namespace Bloxstrap { public static class LaunchHandler @@ -19,6 +19,7 @@ namespace Bloxstrap break; case NextAction.LaunchRoblox: + App.LaunchSettings.RobloxLaunchMode = LaunchMode.Player; LaunchRoblox(); break; @@ -85,7 +86,7 @@ namespace Bloxstrap ProcessNextAction(installer.CloseAction, !installer.Finished); } - + } public static void LaunchUninstaller() @@ -120,6 +121,8 @@ namespace Bloxstrap Installer.DoUninstall(keepData); Frontend.ShowMessageBox(Strings.Bootstrapper_SuccessfullyUninstalled, MessageBoxImage.Information); + + App.Terminate(); } public static void LaunchSettings() @@ -131,12 +134,12 @@ namespace Bloxstrap if (interlock.IsAcquired) { bool showAlreadyRunningWarning = Process.GetProcessesByName(App.ProjectName).Length > 1; - new UI.Elements.Settings.MainWindow(showAlreadyRunningWarning).ShowDialog(); + new UI.Elements.Settings.MainWindow(showAlreadyRunningWarning).Show(); } else { App.Logger.WriteLine(LOG_IDENT, $"Found an already existing menu window"); - + var process = Utilities.GetProcessesSafe().Where(x => x.MainWindowTitle == Strings.Menu_Title).FirstOrDefault(); if (process is not null) @@ -156,7 +159,6 @@ namespace Bloxstrap { const string LOG_IDENT = "LaunchHandler::LaunchRoblox"; - if (!File.Exists(Path.Combine(Paths.System, "mfplat.dll"))) { Frontend.ShowMessageBox(Strings.Bootstrapper_WMFNotFound, MessageBoxImage.Error); @@ -191,8 +193,6 @@ namespace Bloxstrap } } - App.NotifyIcon = new(); - // start bootstrapper and show the bootstrapper modal if we're not running silently App.Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper"); var bootstrapper = new Bootstrapper(installWebView2); @@ -206,45 +206,53 @@ namespace Bloxstrap dialog.Bootstrapper = bootstrapper; } - Task bootstrapperTask = Task.Run(async () => await bootstrapper.Run()).ContinueWith(t => + Task.Run(bootstrapper.Run).ContinueWith(t => { App.Logger.WriteLine(LOG_IDENT, "Bootstrapper task has finished"); - // notifyicon is blocking main thread, must be disposed here - App.NotifyIcon?.Dispose(); - if (t.IsFaulted) + { App.Logger.WriteLine(LOG_IDENT, "An exception occurred when running the bootstrapper"); - if (t.Exception is null) - return; + if (t.Exception is not null) + App.FinalizeExceptionHandling(t.Exception, false); + } - App.Logger.WriteException(LOG_IDENT, t.Exception); - - Exception exception = t.Exception; - -#if !DEBUG - if (t.Exception.GetType().ToString() == "System.AggregateException") - exception = t.Exception.InnerException!; -#endif - - App.FinalizeExceptionHandling(exception, false); + App.Terminate(); }); - // this ordering is very important as all wpf windows are shown as modal dialogs, mess it up and you'll end up blocking input to one of them dialog?.ShowBootstrapper(); - - if (!App.LaunchSettings.NoLaunchFlag.Active && App.Settings.Prop.EnableActivityTracking) - App.NotifyIcon?.InitializeContextMenu(); - - App.Logger.WriteLine(LOG_IDENT, "Waiting for bootstrapper task to finish"); - - bootstrapperTask.Wait(); } public static void LaunchWatcher() { + const string LOG_IDENT = "LaunchHandler::LaunchWatcher"; + // this whole topology is a bit confusing, bear with me: + // main thread: strictly UI only, handles showing of the notification area icon, context menu, server details dialog + // - server information task: queries server location, invoked if either the explorer notification is shown or the server details dialog is opened + // - discord rpc thread: handles rpc connection with discord + // - discord rich presence tasks: handles querying and displaying of game information, invoked on activity watcher events + // - watcher task: runs activity watcher + waiting for roblox to close, terminates when it has + + var watcher = new Watcher(); + + Task.Run(watcher.Run).ContinueWith(t => + { + App.Logger.WriteLine(LOG_IDENT, "Watcher task has finished"); + + watcher.Dispose(); + + if (t.IsFaulted) + { + App.Logger.WriteLine(LOG_IDENT, "An exception occurred when running the watcher"); + + if (t.Exception is not null) + App.FinalizeExceptionHandling(t.Exception); + } + + App.Terminate(); + }); } } } diff --git a/Bloxstrap/LaunchSettings.cs b/Bloxstrap/LaunchSettings.cs index a217d09..046fd9a 100644 --- a/Bloxstrap/LaunchSettings.cs +++ b/Bloxstrap/LaunchSettings.cs @@ -28,7 +28,7 @@ namespace Bloxstrap public LaunchFlag StudioFlag { get; } = new("studio"); - public LaunchMode RobloxLaunchMode { get; private set; } = LaunchMode.None; + public LaunchMode RobloxLaunchMode { get; set; } = LaunchMode.None; public string RobloxLaunchArgs { get; private set; } = ""; diff --git a/Bloxstrap/Properties/launchSettings.json b/Bloxstrap/Properties/launchSettings.json index e3f8ff7..d07acf2 100644 --- a/Bloxstrap/Properties/launchSettings.json +++ b/Bloxstrap/Properties/launchSettings.json @@ -26,6 +26,10 @@ "Bloxstrap (Studio Launch)": { "commandName": "Project", "commandLineArgs": "-studio" + }, + "Bloxstrap (Watcher)": { + "commandName": "Project", + "commandLineArgs": "-watcher" } } } \ No newline at end of file diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml index 8bf796c..a926eb4 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml @@ -61,7 +61,7 @@ - + @@ -73,7 +73,7 @@ - + diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs index 9e34a29..c4412ba 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml.cs @@ -22,32 +22,28 @@ namespace Bloxstrap.UI.Elements.ContextMenu { // i wouldve gladly done this as mvvm but turns out that data binding just does not work with menuitems for some reason so idk this sucks - private readonly ActivityWatcher? _activityWatcher; - private readonly DiscordRichPresence? _richPresenceHandler; + private readonly Watcher _watcher; + + private ActivityWatcher? _activityWatcher => _watcher.ActivityWatcher; private ServerInformation? _serverInformationWindow; - private int? _processId; - public MenuContainer(ActivityWatcher? activityWatcher, DiscordRichPresence? richPresenceHandler, int? processId) + public MenuContainer(Watcher watcher) { InitializeComponent(); - _activityWatcher = activityWatcher; - _richPresenceHandler = richPresenceHandler; - _processId = processId; + _watcher = watcher; if (_activityWatcher is not null) { + _activityWatcher.OnLogOpen += ActivityWatcher_OnLogOpen; _activityWatcher.OnGameJoin += ActivityWatcher_OnGameJoin; _activityWatcher.OnGameLeave += ActivityWatcher_OnGameLeave; } - if (_richPresenceHandler is not null) + if (_watcher.RichPresence is not null) RichPresenceMenuItem.Visibility = Visibility.Visible; - if (_processId is not null) - CloseRobloxMenuItem.Visibility = Visibility.Visible; - VersionTextBlock.Text = $"{App.ProjectName} v{App.Version}"; } @@ -55,7 +51,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu { if (_serverInformationWindow is null) { - _serverInformationWindow = new ServerInformation(_activityWatcher!); + _serverInformationWindow = new ServerInformation(_watcher); _serverInformationWindow.Closed += (_, _) => _serverInformationWindow = null; } @@ -65,17 +61,23 @@ namespace Bloxstrap.UI.Elements.ContextMenu _serverInformationWindow.Activate(); } - private void ActivityWatcher_OnGameJoin(object? sender, EventArgs e) + public void ActivityWatcher_OnLogOpen(object? sender, EventArgs e) => + Dispatcher.Invoke(() => LogTracerMenuItem.Visibility = Visibility.Visible); + + public void ActivityWatcher_OnGameJoin(object? sender, EventArgs e) { + if (_activityWatcher is null) + return; + Dispatcher.Invoke(() => { - if (_activityWatcher?.ActivityServerType == ServerType.Public) + if (_activityWatcher.ActivityServerType == ServerType.Public) InviteDeeplinkMenuItem.Visibility = Visibility.Visible; ServerDetailsMenuItem.Visibility = Visibility.Visible; }); } - private void ActivityWatcher_OnGameLeave(object? sender, EventArgs e) + public void ActivityWatcher_OnGameLeave(object? sender, EventArgs e) { Dispatcher.Invoke(() => { InviteDeeplinkMenuItem.Visibility = Visibility.Collapsed; @@ -100,7 +102,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu private void Window_Closed(object sender, EventArgs e) => App.Logger.WriteLine("MenuContainer::Window_Closed", "Context menu container closed"); - private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _richPresenceHandler?.SetVisibility(((MenuItem)sender).IsChecked); + private void RichPresenceMenuItem_Click(object sender, RoutedEventArgs e) => _watcher.RichPresence?.SetVisibility(((MenuItem)sender).IsChecked); private void InviteDeeplinkMenuItem_Click(object sender, RoutedEventArgs e) => Clipboard.SetDataObject($"roblox://experiences/start?placeId={_activityWatcher?.ActivityPlaceId}&gameInstanceId={_activityWatcher?.ActivityJobId}"); @@ -110,13 +112,8 @@ namespace Bloxstrap.UI.Elements.ContextMenu { string? location = _activityWatcher?.LogLocation; - if (location is null) - { - Frontend.ShowMessageBox(Strings.ContextMenu_RobloxNotRunning, MessageBoxImage.Information); - return; - } - - Utilities.ShellExecute(location); + if (location is not null) + Utilities.ShellExecute(location); } private void CloseRobloxMenuItem_Click(object sender, RoutedEventArgs e) @@ -130,9 +127,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu if (result != MessageBoxResult.Yes) return; - using Process process = Process.GetProcessById((int)_processId!); - process.Kill(); - process.Close(); + _watcher.KillRobloxProcess(); } } } diff --git a/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml.cs b/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml.cs index 5d46d85..65b8f1e 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml.cs +++ b/Bloxstrap/UI/Elements/ContextMenu/ServerInformation.xaml.cs @@ -22,9 +22,13 @@ namespace Bloxstrap.UI.Elements.ContextMenu /// public partial class ServerInformation { - public ServerInformation(ActivityWatcher activityWatcher) + public ServerInformation(Watcher watcher) { - DataContext = new ServerInformationViewModel(this, activityWatcher); + var viewModel = new ServerInformationViewModel(watcher); + + viewModel.RequestCloseEvent += (_, _) => Close(); + + DataContext = viewModel; InitializeComponent(); } } diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml index 7012567..b63b1a7 100644 --- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml +++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml @@ -94,7 +94,7 @@ - + diff --git a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs index c22a732..59bd1db 100644 --- a/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs +++ b/Bloxstrap/UI/Elements/Settings/MainWindow.xaml.cs @@ -17,7 +17,9 @@ namespace Bloxstrap.UI.Elements.Settings public MainWindow(bool showAlreadyRunningWarning) { var viewModel = new MainWindowViewModel(); + viewModel.RequestSaveNoticeEvent += (_, _) => SettingsSavedSnackbar.Show(); + viewModel.RequestCloseWindowEvent += (_, _) => Close(); DataContext = viewModel; @@ -64,6 +66,9 @@ namespace Bloxstrap.UI.Elements.Settings if (result != MessageBoxResult.Yes) e.Cancel = true; } + + if (!e.Cancel) + App.Terminate(); } } } diff --git a/Bloxstrap/UI/NotifyIconWrapper.cs b/Bloxstrap/UI/NotifyIconWrapper.cs index 6b32da3..ae0b042 100644 --- a/Bloxstrap/UI/NotifyIconWrapper.cs +++ b/Bloxstrap/UI/NotifyIconWrapper.cs @@ -1,4 +1,5 @@ using Bloxstrap.Integrations; +using Bloxstrap.UI.Elements.About; using Bloxstrap.UI.Elements.ContextMenu; namespace Bloxstrap.UI @@ -10,18 +11,21 @@ namespace Bloxstrap.UI private bool _disposing = false; private readonly System.Windows.Forms.NotifyIcon _notifyIcon; - private MenuContainer? _menuContainer; - private ActivityWatcher? _activityWatcher; - private DiscordRichPresence? _richPresenceHandler; - private int? _processId; + private readonly MenuContainer _menuContainer; + + private readonly Watcher _watcher; + + private ActivityWatcher? _activityWatcher => _watcher.ActivityWatcher; EventHandler? _alertClickHandler; - public NotifyIconWrapper() + public NotifyIconWrapper(Watcher watcher) { App.Logger.WriteLine("NotifyIconWrapper::NotifyIconWrapper", "Initializing notification area icon"); + _watcher = watcher; + _notifyIcon = new() { Icon = Properties.Resources.IconBloxstrap, @@ -30,52 +34,18 @@ namespace Bloxstrap.UI }; _notifyIcon.MouseClick += MouseClickEventHandler; - } - #region Handler registers - public void SetRichPresenceHandler(DiscordRichPresence richPresenceHandler) - { - if (_richPresenceHandler is not null) - return; - - _richPresenceHandler = richPresenceHandler; - } - - public void SetActivityWatcher(ActivityWatcher activityWatcher) - { if (_activityWatcher is not null) - return; + _activityWatcher.OnGameJoin += OnGameJoin; - _activityWatcher = activityWatcher; - - if (App.Settings.Prop.ShowServerDetails) - _activityWatcher.OnGameJoin += (_, _) => Task.Run(OnGameJoin); + _menuContainer = new(_watcher); + _menuContainer.Show(); } - public void SetProcessId(int processId) - { - if (_processId is not null) - return; - - _processId = processId; - } - #endregion - #region Context menu - public void InitializeContextMenu() - { - if (_menuContainer is not null || _disposing) - return; - - App.Logger.WriteLine("NotifyIconWrapper::InitializeContextMenu", "Initializing context menu"); - - _menuContainer = new(_activityWatcher, _richPresenceHandler, _processId); - _menuContainer.ShowDialog(); - } - public void MouseClickEventHandler(object? sender, System.Windows.Forms.MouseEventArgs e) { - if (e.Button != System.Windows.Forms.MouseButtons.Right || _menuContainer is null) + if (e.Button != System.Windows.Forms.MouseButtons.Right) return; _menuContainer.Activate(); @@ -84,9 +54,12 @@ namespace Bloxstrap.UI #endregion #region Activity handlers - public async void OnGameJoin() + public async void OnGameJoin(object? sender, EventArgs e) { - string serverLocation = await _activityWatcher!.GetServerLocation(); + if (_activityWatcher is null) + return; + + string serverLocation = await _activityWatcher.GetServerLocation(); string title = _activityWatcher.ActivityServerType switch { ServerType.Public => Strings.ContextMenu_ServerInformation_Notification_Title_Public, @@ -99,7 +72,7 @@ namespace Bloxstrap.UI title, String.Format(Strings.ContextMenu_ServerInformation_Notification_Text, serverLocation), 10, - (_, _) => _menuContainer?.ShowServerInformationWindow() + (_, _) => _menuContainer.ShowServerInformationWindow() ); } #endregion @@ -151,9 +124,8 @@ namespace Bloxstrap.UI App.Logger.WriteLine("NotifyIconWrapper::Dispose", "Disposing NotifyIcon"); - _menuContainer?.Dispatcher.Invoke(_menuContainer.Close); - _notifyIcon?.Dispose(); - + _menuContainer.Dispatcher.Invoke(_menuContainer.Close); + _notifyIcon.Dispose(); GC.SuppressFinalize(this); } diff --git a/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs b/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs index 71d50c3..9d1b875 100644 --- a/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs +++ b/Bloxstrap/UI/ViewModels/ContextMenu/ServerInformationViewModel.cs @@ -7,20 +7,23 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu { internal class ServerInformationViewModel : NotifyPropertyChangedViewModel { - private readonly Window _window; private readonly ActivityWatcher _activityWatcher; public string InstanceId => _activityWatcher.ActivityJobId; - public string ServerType => Resources.Strings.ResourceManager.GetStringSafe($"Enums.ServerType.{_activityWatcher.ActivityServerType}"); - public string ServerLocation { get; private set; } = Resources.Strings.ContextMenu_ServerInformation_Loading; + + public string ServerType => Strings.ResourceManager.GetStringSafe($"Enums.ServerType.{_activityWatcher.ActivityServerType}"); + + public string ServerLocation { get; private set; } = Strings.ContextMenu_ServerInformation_Loading; public ICommand CopyInstanceIdCommand => new RelayCommand(CopyInstanceId); - public ICommand CloseWindowCommand => new RelayCommand(_window.Close); - public ServerInformationViewModel(Window window, ActivityWatcher activityWatcher) + public ICommand CloseWindowCommand => new RelayCommand(RequestClose); + + public EventHandler? RequestCloseEvent; + + public ServerInformationViewModel(Watcher watcher) { - _window = window; - _activityWatcher = activityWatcher; + _activityWatcher = watcher.ActivityWatcher!; Task.Run(async () => { @@ -30,5 +33,7 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu } private void CopyInstanceId() => Clipboard.SetDataObject(InstanceId); + + private void RequestClose() => RequestCloseEvent?.Invoke(this, EventArgs.Empty); } } diff --git a/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs b/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs index 991ae8d..0680998 100644 --- a/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Dialogs/LaunchMenuViewModel.cs @@ -22,6 +22,6 @@ namespace Bloxstrap.UI.ViewModels.Installer private void LaunchRoblox() => CloseWindowRequest?.Invoke(this, NextAction.LaunchRoblox); - private void LaunchAbout() => new MainWindow().Show(); + private void LaunchAbout() => new MainWindow().ShowDialog(); } } diff --git a/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs b/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs index 767bb0b..5c04e50 100644 --- a/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs +++ b/Bloxstrap/UI/ViewModels/Settings/MainWindowViewModel.cs @@ -9,11 +9,17 @@ namespace Bloxstrap.UI.ViewModels.Settings public ICommand OpenAboutCommand => new RelayCommand(OpenAbout); public ICommand SaveSettingsCommand => new RelayCommand(SaveSettings); + + public ICommand CloseWindowCommand => new RelayCommand(CloseWindow); public EventHandler? RequestSaveNoticeEvent; + + public EventHandler? RequestCloseWindowEvent; private void OpenAbout() => new MainWindow().ShowDialog(); + private void CloseWindow() => RequestCloseWindowEvent?.Invoke(this, EventArgs.Empty); + private void SaveSettings() { const string LOG_IDENT = "MainWindowViewModel::SaveSettings"; @@ -35,7 +41,7 @@ namespace Bloxstrap.UI.ViewModels.Settings App.PendingSettingTasks.Clear(); - RequestSaveNoticeEvent?.Invoke(this, new EventArgs()); + RequestSaveNoticeEvent?.Invoke(this, EventArgs.Empty); } } } diff --git a/Bloxstrap/Watcher.cs b/Bloxstrap/Watcher.cs new file mode 100644 index 0000000..652675e --- /dev/null +++ b/Bloxstrap/Watcher.cs @@ -0,0 +1,147 @@ +using Bloxstrap.Integrations; +using System.CodeDom; +using System.Security.Permissions; + +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.Versions, App.State.Prop.PlayerVersionGuid, "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[0].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 += (_, _) => + { + 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() => KillProcess(_gameClientPid); + + public void KillProcess(int pid) + { + using var process = Process.GetProcessById(pid); + + App.Logger.WriteLine("Watcher::KillProcess", $"Killing process '{process.ProcessName}' (PID {process.Id})"); + + if (process.HasExited) + { + App.Logger.WriteLine("Watcher::KillProcess", $"PID {process.Id} has already exited"); + return; + } + + process.Kill(); + process.Close(); + } + + public void CloseProcess(int pid) + { + using var process = Process.GetProcessById(pid); + + App.Logger.WriteLine("Watcher::CloseProcess", $"Closing process '{process.ProcessName}' (PID {process.Id})"); + + if (process.HasExited) + { + App.Logger.WriteLine("Watcher::CloseProcess", $"PID {process.Id} has already exited"); + return; + } + + process.CloseMainWindow(); + process.Close(); + } + + 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); + } + + public void Dispose() + { + App.Logger.WriteLine("Watcher::Dispose", "Disposing Watcher"); + + _notifyIcon?.Dispose(); + RichPresence?.Dispose(); + + GC.SuppressFinalize(this); + } + } +} From 6738e09443cac005e46305337f2e824ade853f6b Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Thu, 29 Aug 2024 11:01:00 +0100 Subject: [PATCH 18/19] Alterations to how bug reports are handled - New field for log files in issue templates (which the exception dialog will directly fill out) - Exception dialog lets you open the log file directly now that locating it isn't necessary anymore --- .github/ISSUE_TEMPLATE/bug_report.yaml | 7 +++++ Bloxstrap/Resources/Strings.Designer.cs | 27 +++++++------------ Bloxstrap/Resources/Strings.resx | 7 ++--- .../Elements/ContextMenu/MenuContainer.xaml | 2 +- .../UI/Elements/Dialogs/ExceptionDialog.xaml | 2 +- .../Elements/Dialogs/ExceptionDialog.xaml.cs | 8 +++--- 6 files changed, 24 insertions(+), 29 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 56f0166..e76f232 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -32,3 +32,10 @@ body: description: Provide a comprehensive description of the problem you're facing. Don't forget to attach any additional resources you may have, such as log files and screenshots. validations: required: true + - type: textarea + id: log + attributes: + label: Bloxstrap Log + description: If you're getting a Bloxstrap Exception error, upload your log file here. Otherwise, just leave it empty. + value: "N/A" + render: text \ No newline at end of file diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 61592fe..1ce1035 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -449,15 +449,6 @@ namespace Bloxstrap.Resources { } } - /// - /// Looks up a localized string similar to Locate log file. - /// - public static string Common_LocateLogFile { - get { - return ResourceManager.GetString("Common.LocateLogFile", resourceCulture); - } - } - /// /// Looks up a localized string similar to Miscellaneous. /// @@ -521,6 +512,15 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Open log file. + /// + public static string Common_OpenLogFile { + get { + return ResourceManager.GetString("Common.OpenLogFile", resourceCulture); + } + } + /// /// Looks up a localized string similar to Presets. /// @@ -629,15 +629,6 @@ namespace Bloxstrap.Resources { } } - /// - /// Looks up a localized string similar to Open log file. - /// - public static string ContextMenu_OpenLogFile { - get { - return ResourceManager.GetString("ContextMenu.OpenLogFile", resourceCulture); - } - } - /// /// Looks up a localized string similar to Roblox is still launching. A log file will only be available once Roblox launches.. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 70f23a2..52c7f52 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -226,8 +226,8 @@ Your ReShade configuration files will still be saved, and you can locate them by Import JSON - - Locate log file + + Open log file Miscellaneous @@ -268,9 +268,6 @@ Your ReShade configuration files will still be saved, and you can locate them by Copy invite deeplink - - Open log file - See server details diff --git a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml index a926eb4..88a7380 100644 --- a/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml +++ b/Bloxstrap/UI/Elements/ContextMenu/MenuContainer.xaml @@ -81,7 +81,7 @@ - + diff --git a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml index 4c1dd33..1fa1c01 100644 --- a/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml +++ b/Bloxstrap/UI/Elements/Dialogs/ExceptionDialog.xaml @@ -41,7 +41,7 @@ -