From dd568faf9c6ecacf5f0b39847bcc37a0498d7d8a Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Sun, 8 Sep 2024 23:59:38 +0100 Subject: [PATCH] Implement new bootstrapper error handling --- Bloxstrap/App.xaml.cs | 3 +- Bloxstrap/Bootstrapper.cs | 71 ++++++++------ Bloxstrap/Exceptions/HttpResponseException.cs | 19 ---- Bloxstrap/Resources/Strings.Designer.cs | 92 +++++++++++++------ Bloxstrap/Resources/Strings.resx | 26 ++++-- Bloxstrap/RobloxDeployment.cs | 7 +- Bloxstrap/RobloxFastFlags.cs | 11 +-- .../Elements/Dialogs/ConnectivityDialog.xaml | 3 +- .../Dialogs/ConnectivityDialog.xaml.cs | 37 +++++++- Bloxstrap/UI/Frontend.cs | 10 +- 10 files changed, 178 insertions(+), 101 deletions(-) delete mode 100644 Bloxstrap/Exceptions/HttpResponseException.cs diff --git a/Bloxstrap/App.xaml.cs b/Bloxstrap/App.xaml.cs index 858b85e3..bc333f0e 100644 --- a/Bloxstrap/App.xaml.cs +++ b/Bloxstrap/App.xaml.cs @@ -101,8 +101,7 @@ public static void FinalizeExceptionHandling(Exception ex, bool log = true) _showingExceptionDialog = true; - if (!LaunchSettings.QuietFlag.Active) - Frontend.ShowExceptionDialog(ex); + Frontend.ShowExceptionDialog(ex); Terminate(ErrorCode.ERROR_INSTALL_FAILURE); } diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index 8beaea43..fb23a267 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -82,6 +82,35 @@ private void UpdateProgressBar() Dialog.ProgressValue = progressValue; } + + private void HandleConnectionError(Exception exception) + { + _noConnection = true; + + string message = Strings.Dialog_Connectivity_Preventing; + + if (exception.GetType() == typeof(AggregateException)) + exception = exception.InnerException!; + + if (exception.GetType() == typeof(HttpRequestException)) + message = String.Format(Strings.Dialog_Connectivity_RobloxDown, "[status.roblox.com](https://status.roblox.com)"); + else if (exception.GetType() == typeof(TaskCanceledException)) + message = Strings.Dialog_Connectivity_TimedOut; + + if (_mustUpgrade) + message += $"\n\n{Strings.Dialog_Connectivity_RobloxUpgradeNeeded}\n\n{Strings.Dialog_Connectivity_TryAgainLater}"; + else + message += $"\n\n{Strings.Dialog_Connectivity_RobloxUpgradeSkip}"; + + Frontend.ShowConnectivityDialog( + String.Format(Strings.Dialog_Connectivity_UnableToConnect, "Roblox"), + message, + _mustUpgrade ? MessageBoxImage.Error : MessageBoxImage.Warning, + exception); + + if (_mustUpgrade) + App.Terminate(ErrorCode.ERROR_CANCELLED); + } public async Task Run() { @@ -101,24 +130,7 @@ public async Task Run() if (connectionResult is not null) { - App.Logger.WriteLine(LOG_IDENT, "Connectivity check failed!"); - App.Logger.WriteException(LOG_IDENT, connectionResult); - - string message = Strings.Bootstrapper_Connectivity_Preventing; - - if (connectionResult.GetType() == typeof(HttpResponseException)) - message = Strings.Bootstrapper_Connectivity_RobloxDown; - else if (connectionResult.GetType() == typeof(TaskCanceledException)) - message = Strings.Bootstrapper_Connectivity_TimedOut; - else if (connectionResult.GetType() == typeof(AggregateException)) - connectionResult = connectionResult.InnerException!; - - // TODO: handle update skip - Frontend.ShowConnectivityDialog(Strings.Dialog_Connectivity_UnableToConnect, message, connectionResult); - - App.Terminate(ErrorCode.ERROR_CANCELLED); - - return; + HandleConnectionError(connectionResult); } #if !DEBUG || DEBUG_UPDATER @@ -159,8 +171,17 @@ public async Task Run() App.State.Load(); } - // TODO: handle exception and update skip - await GetLatestVersionInfo(); + if (!_noConnection) + { + try + { + await GetLatestVersionInfo(); + } + catch (Exception ex) + { + HandleConnectionError(ex); + } + } if (!_noConnection) { @@ -172,7 +193,6 @@ public async Task Run() await ApplyModifications(); } - // check if launch uri is set to our bootstrapper // this doesn't go under register, so we check every launch // just in case the stock bootstrapper changes it back @@ -232,15 +252,14 @@ private async Task GetLatestVersionInfo() { clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType); } - catch (HttpResponseException ex) + catch (HttpRequestException ex) { - if (ex.ResponseMessage.StatusCode - is not HttpStatusCode.Unauthorized + if (ex.StatusCode is not HttpStatusCode.Unauthorized and not HttpStatusCode.Forbidden and not HttpStatusCode.NotFound) throw; - App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {channel} to {RobloxDeployment.DefaultChannel} because HTTP {(int)ex.ResponseMessage.StatusCode}"); + App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {channel} to {RobloxDeployment.DefaultChannel} because HTTP {(int)ex.StatusCode}"); channel = RobloxDeployment.DefaultChannel; clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType); @@ -624,7 +643,6 @@ private async Task UpgradeRoblox() SetStatus(Strings.Bootstrapper_Status_Configuring); } - // TODO: handle faulted tasks await Task.WhenAll(extractionTasks); App.Logger.WriteLine(LOG_IDENT, "Writing AppSettings.xml..."); @@ -1011,6 +1029,7 @@ private async Task DownloadPackage(Package package) Frontend.ShowConnectivityDialog( Strings.Dialog_Connectivity_UnableToDownload, String.Format(Strings.Dialog_Connectivity_UnableToDownloadReason, "[https://github.com/pizzaboxer/bloxstrap/wiki/Bloxstrap-is-unable-to-download-Roblox](https://github.com/pizzaboxer/bloxstrap/wiki/Bloxstrap-is-unable-to-download-Roblox)"), + MessageBoxImage.Error, ex ); diff --git a/Bloxstrap/Exceptions/HttpResponseException.cs b/Bloxstrap/Exceptions/HttpResponseException.cs deleted file mode 100644 index 08404b07..00000000 --- a/Bloxstrap/Exceptions/HttpResponseException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Bloxstrap.Exceptions -{ - internal class HttpResponseException : Exception - { - public HttpResponseMessage ResponseMessage { get; } - - public HttpResponseException(HttpResponseMessage responseMessage) - : base($"Could not connect to {responseMessage.RequestMessage!.RequestUri} because it returned HTTP {(int)responseMessage.StatusCode} ({responseMessage.ReasonPhrase})") - { - ResponseMessage = responseMessage; - } - } -} diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 0d863f50..981861c0 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -141,33 +141,6 @@ public static string Bootstrapper_ConfirmLaunch { } } - /// - /// Looks up a localized string similar to It's possible that something is preventing Bloxstrap from connecting to the internet. Please check and try again.. - /// - public static string Bootstrapper_Connectivity_Preventing { - get { - return ResourceManager.GetString("Bootstrapper.Connectivity.Preventing", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Roblox may be down right now. See status.roblox.com for more information. Please try again later.. - /// - public static string Bootstrapper_Connectivity_RobloxDown { - get { - return ResourceManager.GetString("Bootstrapper.Connectivity.RobloxDown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bloxstrap timed out when trying to connect to three different Roblox deployment mirrors, indicating a poor internet connection. Please try again later.. - /// - public static string Bootstrapper_Connectivity_TimedOut { - get { - return ResourceManager.GetString("Bootstrapper.Connectivity.TimedOut", resourceCulture); - } - } - /// /// Looks up a localized string similar to Could not apply the {0} emoji mod preset because of a network error. To try again, please reconfigure the option in the Bloxstrap Menu.. /// @@ -828,6 +801,60 @@ public static string Dialog_Connectivity_MoreInfo { } } + /// + /// Looks up a localized string similar to Something is likely preventing Bloxstrap from connecting to the internet.. + /// + public static string Dialog_Connectivity_Preventing { + get { + return ResourceManager.GetString("Dialog.Connectivity.Preventing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Roblox may be down right now. See {0} for more information.. + /// + public static string Dialog_Connectivity_RobloxDown { + get { + return ResourceManager.GetString("Dialog.Connectivity.RobloxDown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Because Roblox needs to be installed or upgraded, Bloxstrap cannot continue.. + /// + public static string Dialog_Connectivity_RobloxUpgradeNeeded { + get { + return ResourceManager.GetString("Dialog.Connectivity.RobloxUpgradeNeeded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to For this launch, Roblox will not be checked for upgrades, and changes to mods will not be applied.. + /// + public static string Dialog_Connectivity_RobloxUpgradeSkip { + get { + return ResourceManager.GetString("Dialog.Connectivity.RobloxUpgradeSkip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} may be down right now.. + /// + public static string Dialog_Connectivity_ServiceDown { + get { + return ResourceManager.GetString("Dialog.Connectivity.ServiceDown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The connection timed out, which could indicate a poor internet connection or a firewall block.. + /// + public static string Dialog_Connectivity_TimedOut { + get { + return ResourceManager.GetString("Dialog.Connectivity.TimedOut", resourceCulture); + } + } + /// /// Looks up a localized string similar to Connectivity error. /// @@ -838,7 +865,16 @@ public static string Dialog_Connectivity_Title { } /// - /// Looks up a localized string similar to Bloxstrap is unable to connect to Roblox. + /// Looks up a localized string similar to Please try again later.. + /// + public static string Dialog_Connectivity_TryAgainLater { + get { + return ResourceManager.GetString("Dialog.Connectivity.TryAgainLater", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bloxstrap is unable to connect to {0}. /// public static string Dialog_Connectivity_UnableToConnect { get { diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 3301be8c..890ec70f 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -123,14 +123,14 @@ Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching? - - It's possible that something is preventing Bloxstrap from connecting to the internet. Please check and try again. + + Something is likely preventing Bloxstrap from connecting to the internet. - - Roblox may be down right now. See status.roblox.com for more information. Please try again later. + + Roblox may be down right now. See {0} for more information. - - Bloxstrap timed out when trying to connect to three different Roblox deployment mirrors, indicating a poor internet connection. Please try again later. + + The connection timed out, which could indicate a poor internet connection or a firewall block. Could not apply the {0} emoji mod preset because of a network error. To try again, please reconfigure the option in the Bloxstrap Menu. @@ -294,7 +294,7 @@ Click for more information Connectivity error - Bloxstrap is unable to connect to Roblox + Bloxstrap is unable to connect to {0} Copy log contents @@ -1171,4 +1171,16 @@ Are you sure you want to continue? Failed to query server location. + + {0} may be down right now. + + + Please try again later. + + + For this launch, Roblox will not be checked for upgrades, and changes to mods will not be applied. + + + Because Roblox needs to be installed or upgraded, Bloxstrap cannot continue. + \ No newline at end of file diff --git a/Bloxstrap/RobloxDeployment.cs b/Bloxstrap/RobloxDeployment.cs index d23a5aad..e2022c1f 100644 --- a/Bloxstrap/RobloxDeployment.cs +++ b/Bloxstrap/RobloxDeployment.cs @@ -33,8 +33,7 @@ public static class RobloxDeployment { var response = await App.HttpClient.GetAsync($"{url}/versionStudio", token); - if (!response.IsSuccessStatusCode) - throw new HttpResponseException(response); + response.EnsureSuccessStatusCode(); // versionStudio is the version hash for the last MFC studio to be deployed. // the response body should always be "version-012732894899482c". @@ -151,14 +150,14 @@ public static async Task GetInfo(string channel, string binaryTyp try { - clientVersion = await Http.GetJson($"https://clientsettingscdn.roblox.com/{path}"); + clientVersion = await Http.GetJson("https://clientsettingscdn.roblox.com" + path); } catch (Exception ex) { App.Logger.WriteLine(LOG_IDENT, "Failed to contact clientsettingscdn! Falling back to clientsettings..."); App.Logger.WriteException(LOG_IDENT, ex); - clientVersion = await Http.GetJson($"https://clientsettings.roblox.com/{path}"); + clientVersion = await Http.GetJson("https://clientsettings.roblox.com" + path); } // check if channel is behind LIVE diff --git a/Bloxstrap/RobloxFastFlags.cs b/Bloxstrap/RobloxFastFlags.cs index 04e45da8..38799c13 100644 --- a/Bloxstrap/RobloxFastFlags.cs +++ b/Bloxstrap/RobloxFastFlags.cs @@ -52,16 +52,7 @@ private async Task Fetch() string rawResponse = await response.Content.ReadAsStringAsync(); - if (!response.IsSuccessStatusCode) - { - App.Logger.WriteLine(logIndent, - "Failed to fetch client settings!\r\n" + - $"\tStatus code: {response.StatusCode}\r\n" + - $"\tResponse: {rawResponse}" - ); - - throw new HttpResponseException(response); - } + response.EnsureSuccessStatusCode(); var clientSettings = JsonSerializer.Deserialize(rawResponse); diff --git a/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml b/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml index 5ad52995..ca2f96b9 100644 --- a/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml +++ b/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml @@ -12,6 +12,7 @@ Width="480" MinHeight="0" SizeToContent="Height" + Title="{x:Static resources:Strings.Dialog_Connectivity_Title}" Background="{ui:ThemeResource ApplicationBackgroundBrush}" ExtendsContentIntoTitleBar="True" WindowStartupLocation="CenterScreen"> @@ -29,7 +30,7 @@ - + diff --git a/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs b/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs index 21f31bb9..494ee69d 100644 --- a/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs +++ b/Bloxstrap/UI/Elements/Dialogs/ConnectivityDialog.xaml.cs @@ -1,5 +1,7 @@ using System.Media; +using System.Windows; using System.Windows.Interop; +using System.Windows.Media.Imaging; using Windows.Win32; using Windows.Win32.Foundation; @@ -14,10 +16,41 @@ namespace Bloxstrap.UI.Elements.Dialogs /// public partial class ConnectivityDialog { - public ConnectivityDialog(string title, string description, Exception exception) + public ConnectivityDialog(string title, string description, MessageBoxImage image, Exception exception) { InitializeComponent(); + string? iconFilename = null; + SystemSound? sound = null; + + switch (image) + { + case MessageBoxImage.Error: + iconFilename = "Error"; + sound = SystemSounds.Hand; + break; + + case MessageBoxImage.Question: + iconFilename = "Question"; + sound = SystemSounds.Question; + break; + + case MessageBoxImage.Warning: + iconFilename = "Warning"; + sound = SystemSounds.Exclamation; + break; + + case MessageBoxImage.Information: + iconFilename = "Information"; + sound = SystemSounds.Asterisk; + break; + } + + if (iconFilename is null) + IconImage.Visibility = Visibility.Collapsed; + else + IconImage.Source = new BitmapImage(new Uri($"pack://application:,,,/Resources/MessageBox/{iconFilename}.png")); + TitleTextBlock.Text = title; DescriptionTextBlock.MarkdownText = description; @@ -28,7 +61,7 @@ public ConnectivityDialog(string title, string description, Exception exception) Close(); }; - SystemSounds.Hand.Play(); + sound?.Play(); Loaded += delegate { diff --git a/Bloxstrap/UI/Frontend.cs b/Bloxstrap/UI/Frontend.cs index 1699b6b6..f1dfab20 100644 --- a/Bloxstrap/UI/Frontend.cs +++ b/Bloxstrap/UI/Frontend.cs @@ -49,17 +49,23 @@ public static void ShowPlayerErrorDialog(bool crash = false) public static void ShowExceptionDialog(Exception exception) { + if (App.LaunchSettings.QuietFlag.Active) + return; + Application.Current.Dispatcher.Invoke(() => { new ExceptionDialog(exception).ShowDialog(); }); } - public static void ShowConnectivityDialog(string title, string description, Exception exception) + public static void ShowConnectivityDialog(string title, string description, MessageBoxImage image, Exception exception) { + if (App.LaunchSettings.QuietFlag.Active) + return; + Application.Current.Dispatcher.Invoke(() => { - new ConnectivityDialog(title, description, exception).ShowDialog(); + new ConnectivityDialog(title, description, image, exception).ShowDialog(); }); }