From e96c17a3d509e7c2c443553e094a624eda0721cb Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Mon, 26 Aug 2024 19:26:05 +0100 Subject: [PATCH 01/67] 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 02/67] 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 03/67] 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 04/67] 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 05/67] 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 06/67] 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 07/67] 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 08/67] 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 09/67] 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 @@ -