Skip to content

Commit

Permalink
Improve handling of bootstrapper connection errors
Browse files Browse the repository at this point in the history
  • Loading branch information
pizzaboxer committed Oct 29, 2024
1 parent ba561a2 commit bd506ae
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 42 deletions.
37 changes: 21 additions & 16 deletions Bloxstrap/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,14 @@ private void HandleConnectionError(Exception exception)
App.Logger.WriteLine(LOG_IDENT, "Connectivity check failed");
App.Logger.WriteException(LOG_IDENT, exception);

string message = Strings.Dialog_Connectivity_Preventing;
string message = Strings.Dialog_Connectivity_BadConnection;

if (exception.GetType() == typeof(AggregateException))
if (exception is AggregateException)
exception = exception.InnerException!;

if (exception.GetType() == typeof(HttpRequestException))
// https://gist.github.com/pizzaboxer/4b58303589ee5b14cc64397460a8f386
if (exception is HttpRequestException && exception.InnerException is null)
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}";
Expand Down Expand Up @@ -252,6 +251,10 @@ public async Task Run()
Dialog?.CloseBootstrapper();
}

/// <summary>
/// Will throw whatever HttpClient can throw
/// </summary>
/// <returns></returns>
private async Task GetLatestVersionInfo()
{
const string LOG_IDENT = "Bootstrapper::GetLatestVersionInfo";
Expand All @@ -262,7 +265,11 @@ private async Task GetLatestVersionInfo()

using var key = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\ROBLOX Corporation\\Environments\\{AppData.RegistryName}\\Channel");

var match = Regex.Match(App.LaunchSettings.RobloxLaunchArgs, "channel:([a-zA-Z0-9-_]+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
var match = Regex.Match(
App.LaunchSettings.RobloxLaunchArgs,
"channel:([a-zA-Z0-9-_]+)",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant
);

if (match.Groups.Count == 2)
{
Expand All @@ -273,9 +280,12 @@ private async Task GetLatestVersionInfo()
Deployment.Channel = value.ToLowerInvariant();
}

App.Logger.WriteLine(LOG_IDENT, "Got channel as " + (String.IsNullOrEmpty(Deployment.Channel) ? Deployment.DefaultChannel : Deployment.Channel));
if (String.IsNullOrEmpty(Deployment.Channel))
Deployment.Channel = Deployment.DefaultChannel;

App.Logger.WriteLine(LOG_IDENT, $"Got channel as {Deployment.DefaultChannel}");

if (Deployment.Channel != "production")
if (!Deployment.IsDefaultChannel)
App.SendStat("robloxChannel", Deployment.Channel);

ClientVersion clientVersion;
Expand All @@ -284,22 +294,17 @@ private async Task GetLatestVersionInfo()
{
clientVersion = await Deployment.GetInfo();
}
catch (HttpRequestException ex)
catch (InvalidChannelException ex)
{
if (ex.StatusCode is not HttpStatusCode.Unauthorized
and not HttpStatusCode.Forbidden
and not HttpStatusCode.NotFound)
throw;

App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {Deployment.Channel} to {Deployment.DefaultChannel} because HTTP {(int)ex.StatusCode}");
App.Logger.WriteLine(LOG_IDENT, $"Resetting channel from {Deployment.Channel} because {ex.StatusCode}");

Deployment.Channel = Deployment.DefaultChannel;
clientVersion = await Deployment.GetInfo();
}

if (clientVersion.IsBehindDefaultChannel)
{
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {Deployment.Channel} to {Deployment.DefaultChannel} because channel is behind production");
App.Logger.WriteLine(LOG_IDENT, $"Resetting channel from {Deployment.Channel} because it's behind production");

Deployment.Channel = Deployment.DefaultChannel;
clientVersion = await Deployment.GetInfo();
Expand Down
10 changes: 10 additions & 0 deletions Bloxstrap/Exceptions/InvalidChannelException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Bloxstrap.Exceptions
{
public class InvalidChannelException : Exception
{
public HttpStatusCode? StatusCode;

public InvalidChannelException(HttpStatusCode? statusCode) : base()
=> StatusCode = statusCode;
}
}
21 changes: 6 additions & 15 deletions Bloxstrap/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions Bloxstrap/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,11 @@
<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="Dialog.Connectivity.Preventing" xml:space="preserve">
<value>Something is likely preventing Bloxstrap from connecting to the internet.</value>
</data>
<data name="Dialog.Connectivity.RobloxDown" xml:space="preserve">
<value>Roblox may be down right now. See {0} for more information.</value>
</data>
<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 name="Dialog.Connectivity.BadConnection" xml:space="preserve">
<value>A connection could not be made, which likely indicates a poor internet connection or a firewall block. If your connection is fine, please ensure that your antivirus isn't blocking Bloxstrap.</value>
</data>
<data name="Bootstrapper.FirstRunUninstall" xml:space="preserve">
<value>You must first install Bloxstrap before uninstalling.</value>
Expand Down
21 changes: 15 additions & 6 deletions Bloxstrap/RobloxInterfaces/Deployment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ public static class Deployment

public static string BinaryType = "WindowsPlayer";

public static bool IsDefaultChannel => String.Compare(Channel, DefaultChannel, StringComparison.OrdinalIgnoreCase) == 0;
public static bool IsDefaultChannel => Channel.Equals(DefaultChannel, StringComparison.OrdinalIgnoreCase);

public static string BaseUrl { get; private set; } = null!;


public static readonly List<HttpStatusCode?> BadChannelCodes = new()
{
HttpStatusCode.Unauthorized,
HttpStatusCode.Forbidden,
HttpStatusCode.NotFound
};

private static readonly Dictionary<string, ClientVersion> ClientVersionCache = new();

// a list of roblox deployment locations that we check for, in case one of them don't work
Expand Down Expand Up @@ -97,7 +104,9 @@ public static class Deployment
{
if (exceptions.Any())
return exceptions[0];
return new TaskCanceledException("All tasks have been cancelled"); // we can't add TaskCanceledExceptions to the list

// task cancellation exceptions don't get added to the list
return new TaskCanceledException("All connection attempts timed out.");
}

App.Logger.WriteLine(LOG_IDENT, $"Got {BaseUrl} as the optimal base URL");
Expand Down Expand Up @@ -157,10 +166,10 @@ public static async Task<ClientVersion> GetInfo(string? channel = null)
{
clientVersion = await Http.GetJson<ClientVersion>("https://clientsettingscdn.roblox.com" + path);
}
catch (HttpRequestException)
catch (HttpRequestException httpEx)
when (!isDefaultChannel && BadChannelCodes.Contains(httpEx.StatusCode))
{
// throw up the exception handler chain, as we shouldn't be the one handling it
throw;
throw new InvalidChannelException(httpEx.StatusCode);
}
catch (Exception ex)
{
Expand Down

0 comments on commit bd506ae

Please sign in to comment.