Implement new bootstrapper error handling

This commit is contained in:
pizzaboxer 2024-09-08 23:59:38 +01:00
parent cd411e33ff
commit dd568faf9c
No known key found for this signature in database
GPG Key ID: 59D4A1DBAD0F2BA8
10 changed files with 178 additions and 101 deletions

View File

@ -101,8 +101,7 @@ namespace Bloxstrap
_showingExceptionDialog = true;
if (!LaunchSettings.QuietFlag.Active)
Frontend.ShowExceptionDialog(ex);
Frontend.ShowExceptionDialog(ex);
Terminate(ErrorCode.ERROR_INSTALL_FAILURE);
}

View File

@ -82,6 +82,35 @@ namespace Bloxstrap
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 @@ namespace Bloxstrap
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 @@ namespace Bloxstrap
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 @@ namespace Bloxstrap
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 @@ namespace Bloxstrap
{
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 @@ namespace Bloxstrap
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 @@ namespace Bloxstrap
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
);

View File

@ -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;
}
}
}

View File

@ -141,33 +141,6 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to It&apos;s possible that something is preventing Bloxstrap from connecting to the internet. Please check and try again..
/// </summary>
public static string Bootstrapper_Connectivity_Preventing {
get {
return ResourceManager.GetString("Bootstrapper.Connectivity.Preventing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Roblox may be down right now. See status.roblox.com for more information. Please try again later..
/// </summary>
public static string Bootstrapper_Connectivity_RobloxDown {
get {
return ResourceManager.GetString("Bootstrapper.Connectivity.RobloxDown", resourceCulture);
}
}
/// <summary>
/// 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..
/// </summary>
public static string Bootstrapper_Connectivity_TimedOut {
get {
return ResourceManager.GetString("Bootstrapper.Connectivity.TimedOut", resourceCulture);
}
}
/// <summary>
/// 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..
/// </summary>
@ -828,6 +801,60 @@ namespace Bloxstrap.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Something is likely preventing Bloxstrap from connecting to the internet..
/// </summary>
public static string Dialog_Connectivity_Preventing {
get {
return ResourceManager.GetString("Dialog.Connectivity.Preventing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Roblox may be down right now. See {0} for more information..
/// </summary>
public static string Dialog_Connectivity_RobloxDown {
get {
return ResourceManager.GetString("Dialog.Connectivity.RobloxDown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Because Roblox needs to be installed or upgraded, Bloxstrap cannot continue..
/// </summary>
public static string Dialog_Connectivity_RobloxUpgradeNeeded {
get {
return ResourceManager.GetString("Dialog.Connectivity.RobloxUpgradeNeeded", resourceCulture);
}
}
/// <summary>
/// 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..
/// </summary>
public static string Dialog_Connectivity_RobloxUpgradeSkip {
get {
return ResourceManager.GetString("Dialog.Connectivity.RobloxUpgradeSkip", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} may be down right now..
/// </summary>
public static string Dialog_Connectivity_ServiceDown {
get {
return ResourceManager.GetString("Dialog.Connectivity.ServiceDown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The connection timed out, which could indicate a poor internet connection or a firewall block..
/// </summary>
public static string Dialog_Connectivity_TimedOut {
get {
return ResourceManager.GetString("Dialog.Connectivity.TimedOut", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connectivity error.
/// </summary>
@ -838,7 +865,16 @@ namespace Bloxstrap.Resources {
}
/// <summary>
/// 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..
/// </summary>
public static string Dialog_Connectivity_TryAgainLater {
get {
return ResourceManager.GetString("Dialog.Connectivity.TryAgainLater", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bloxstrap is unable to connect to {0}.
/// </summary>
public static string Dialog_Connectivity_UnableToConnect {
get {

View File

@ -123,14 +123,14 @@
<data name="Bootstrapper.ConfirmLaunch" xml:space="preserve">
<value>Roblox is currently running, and launching another instance will close it. Are you sure you want to continue launching?</value>
</data>
<data name="Bootstrapper.Connectivity.Preventing" xml:space="preserve">
<value>It's possible that something is preventing Bloxstrap from connecting to the internet. Please check and try again.</value>
<data name="Dialog.Connectivity.Preventing" xml:space="preserve">
<value>Something is likely preventing Bloxstrap from connecting to the internet.</value>
</data>
<data name="Bootstrapper.Connectivity.RobloxDown" xml:space="preserve">
<value>Roblox may be down right now. See status.roblox.com for more information. Please try again later.</value>
<data name="Dialog.Connectivity.RobloxDown" xml:space="preserve">
<value>Roblox may be down right now. See {0} for more information.</value>
</data>
<data name="Bootstrapper.Connectivity.TimedOut" xml:space="preserve">
<value>Bloxstrap timed out when trying to connect to three different Roblox deployment mirrors, indicating a poor internet connection. Please try again later.</value>
<data name="Dialog.Connectivity.TimedOut" xml:space="preserve">
<value>The connection timed out, which could indicate a poor internet connection or a firewall block.</value>
</data>
<data name="Bootstrapper.EmojiPresetFetchFailed" xml:space="preserve">
<value>Could not apply the {0} emoji mod preset because of a network error. To try again, please reconfigure the option in the Bloxstrap Menu.</value>
@ -294,7 +294,7 @@ Click for more information</value>
<value>Connectivity error</value>
</data>
<data name="Dialog.Connectivity.UnableToConnect" xml:space="preserve">
<value>Bloxstrap is unable to connect to Roblox</value>
<value>Bloxstrap is unable to connect to {0}</value>
</data>
<data name="Dialog.Exception.CopyLogContents" xml:space="preserve">
<value>Copy log contents</value>
@ -1171,4 +1171,16 @@ Are you sure you want to continue?</value>
<data name="ActivityWatcher.LocationQueryFailed" xml:space="preserve">
<value>Failed to query server location.</value>
</data>
<data name="Dialog.Connectivity.ServiceDown" xml:space="preserve">
<value>{0} may be down right now.</value>
</data>
<data name="Dialog.Connectivity.TryAgainLater" xml:space="preserve">
<value>Please try again later.</value>
</data>
<data name="Dialog.Connectivity.RobloxUpgradeSkip" xml:space="preserve">
<value>For this launch, Roblox will not be checked for upgrades, and changes to mods will not be applied.</value>
</data>
<data name="Dialog.Connectivity.RobloxUpgradeNeeded" xml:space="preserve">
<value>Because Roblox needs to be installed or upgraded, Bloxstrap cannot continue.</value>
</data>
</root>

View File

@ -33,8 +33,7 @@
{
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 @@
try
{
clientVersion = await Http.GetJson<ClientVersion>($"https://clientsettingscdn.roblox.com/{path}");
clientVersion = await Http.GetJson<ClientVersion>("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<ClientVersion>($"https://clientsettings.roblox.com/{path}");
clientVersion = await Http.GetJson<ClientVersion>("https://clientsettings.roblox.com" + path);
}
// check if channel is behind LIVE

View File

@ -52,16 +52,7 @@ namespace Bloxstrap
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<ClientFlagSettings>(rawResponse);

View File

@ -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 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/Resources/MessageBox/Error.png" />
<Image x:Name="IconImage" Grid.Column="0" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality" Source="pack://application:,,,/Resources/MessageBox/Error.png" />
<StackPanel Grid.Column="1">
<TextBlock x:Name="TitleTextBlock" Text="? is unable to connect to ?" FontSize="18" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
<controls:MarkdownTextBlock x:Name="DescriptionTextBlock" MarkdownText="?" Margin="0,16,0,0" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />

View File

@ -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
/// </summary>
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 @@ namespace Bloxstrap.UI.Elements.Dialogs
Close();
};
SystemSounds.Hand.Play();
sound?.Play();
Loaded += delegate
{

View File

@ -49,17 +49,23 @@ namespace Bloxstrap.UI
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();
});
}