mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-06-23 14:50:03 -07:00
Fix handling of RBX deployment interfacing
This commit is contained in:
parent
d542efd945
commit
ce82c3faa7
@ -13,11 +13,12 @@
|
|||||||
|
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Shell;
|
||||||
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
using Bloxstrap.AppData;
|
using Bloxstrap.AppData;
|
||||||
using System.Windows.Shell;
|
using Bloxstrap.RobloxInterfaces;
|
||||||
using Bloxstrap.UI.Elements.Bootstrapper.Base;
|
using Bloxstrap.UI.Elements.Bootstrapper.Base;
|
||||||
|
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
@ -77,6 +78,7 @@ namespace Bloxstrap
|
|||||||
_fastZipEvents.ProcessFile += (_, e) => e.ContinueRunning = !_cancelTokenSource.IsCancellationRequested;
|
_fastZipEvents.ProcessFile += (_, e) => e.ContinueRunning = !_cancelTokenSource.IsCancellationRequested;
|
||||||
|
|
||||||
AppData = IsStudioLaunch ? new RobloxStudioData() : new RobloxPlayerData();
|
AppData = IsStudioLaunch ? new RobloxStudioData() : new RobloxPlayerData();
|
||||||
|
Deployment.BinaryType = AppData.BinaryType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStatus(string message)
|
private void SetStatus(string message)
|
||||||
@ -151,7 +153,7 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
SetStatus(Strings.Bootstrapper_Status_Connecting);
|
SetStatus(Strings.Bootstrapper_Status_Connecting);
|
||||||
|
|
||||||
var connectionResult = await RobloxDeployment.InitializeConnectivity();
|
var connectionResult = await Deployment.InitializeConnectivity();
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Connectivity check finished");
|
App.Logger.WriteLine(LOG_IDENT, "Connectivity check finished");
|
||||||
|
|
||||||
@ -244,29 +246,29 @@ namespace Bloxstrap
|
|||||||
// if it's set in the launch uri, we need to use it and set the registry key for it
|
// if it's set in the launch uri, we need to use it and set the registry key for it
|
||||||
// else, check if the registry key for it exists, and use it
|
// else, check if the registry key for it exists, and use it
|
||||||
|
|
||||||
string channel = "production";
|
|
||||||
|
|
||||||
using var key = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\ROBLOX Corporation\\Environments\\{AppData.RegistryName}\\Channel");
|
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)
|
if (match.Groups.Count == 2)
|
||||||
{
|
{
|
||||||
channel = match.Groups[1].Value.ToLowerInvariant();
|
Deployment.Channel = match.Groups[1].Value.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
else if (key.GetValue("www.roblox.com") is string value && !String.IsNullOrEmpty(value))
|
else if (key.GetValue("www.roblox.com") is string value && !String.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
channel = value;
|
Deployment.Channel = value.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel != "production")
|
App.Logger.WriteLine(LOG_IDENT, "Got channel as " + (String.IsNullOrEmpty(Deployment.Channel) ? Deployment.DefaultChannel : Deployment.Channel));
|
||||||
App.SendStat("robloxChannel", channel);
|
|
||||||
|
if (Deployment.Channel != "production")
|
||||||
|
App.SendStat("robloxChannel", Deployment.Channel);
|
||||||
|
|
||||||
ClientVersion clientVersion;
|
ClientVersion clientVersion;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType);
|
clientVersion = await Deployment.GetInfo();
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
@ -275,25 +277,25 @@ namespace Bloxstrap
|
|||||||
and not HttpStatusCode.NotFound)
|
and not HttpStatusCode.NotFound)
|
||||||
throw;
|
throw;
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {channel} to {RobloxDeployment.DefaultChannel} because HTTP {(int)ex.StatusCode}");
|
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {Deployment.Channel} to {Deployment.DefaultChannel} because HTTP {(int)ex.StatusCode}");
|
||||||
|
|
||||||
channel = RobloxDeployment.DefaultChannel;
|
Deployment.Channel = Deployment.DefaultChannel;
|
||||||
clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType);
|
clientVersion = await Deployment.GetInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientVersion.IsBehindDefaultChannel)
|
if (clientVersion.IsBehindDefaultChannel)
|
||||||
{
|
{
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {channel} to {RobloxDeployment.DefaultChannel} because channel is behind production");
|
App.Logger.WriteLine(LOG_IDENT, $"Changing channel from {Deployment.Channel} to {Deployment.DefaultChannel} because channel is behind production");
|
||||||
|
|
||||||
channel = RobloxDeployment.DefaultChannel;
|
Deployment.Channel = Deployment.DefaultChannel;
|
||||||
clientVersion = await RobloxDeployment.GetInfo(channel, AppData.BinaryType);
|
clientVersion = await Deployment.GetInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
key.SetValueSafe("www.roblox.com", channel);
|
key.SetValueSafe("www.roblox.com", Deployment.IsDefaultChannel ? "" : Deployment.Channel);
|
||||||
|
|
||||||
_latestVersionGuid = clientVersion.VersionGuid;
|
_latestVersionGuid = clientVersion.VersionGuid;
|
||||||
|
|
||||||
string pkgManifestUrl = RobloxDeployment.GetLocation($"/{_latestVersionGuid}-rbxPkgManifest.txt");
|
string pkgManifestUrl = Deployment.GetLocation($"/{_latestVersionGuid}-rbxPkgManifest.txt");
|
||||||
var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl);
|
var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl);
|
||||||
|
|
||||||
_versionPackageManifest = new(pkgManifestData);
|
_versionPackageManifest = new(pkgManifestData);
|
||||||
@ -962,7 +964,7 @@ namespace Bloxstrap
|
|||||||
if (_cancelTokenSource.IsCancellationRequested)
|
if (_cancelTokenSource.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string packageUrl = RobloxDeployment.GetLocation($"/{_latestVersionGuid}-{package.Name}");
|
string packageUrl = Deployment.GetLocation($"/{_latestVersionGuid}-{package.Name}");
|
||||||
string robloxPackageLocation = Path.Combine(Paths.LocalAppData, "Roblox", "Downloads", package.Signature);
|
string robloxPackageLocation = Path.Combine(Paths.LocalAppData, "Roblox", "Downloads", package.Signature);
|
||||||
|
|
||||||
if (File.Exists(package.DownloadPath))
|
if (File.Exists(package.DownloadPath))
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace Bloxstrap.Models.Manifest
|
using Bloxstrap.RobloxInterfaces;
|
||||||
|
|
||||||
|
namespace Bloxstrap.Models.Manifest
|
||||||
{
|
{
|
||||||
public class FileManifest : List<ManifestFile>
|
public class FileManifest : List<ManifestFile>
|
||||||
{
|
{
|
||||||
@ -24,7 +26,7 @@
|
|||||||
|
|
||||||
public static async Task<FileManifest> Get(string versionGuid)
|
public static async Task<FileManifest> Get(string versionGuid)
|
||||||
{
|
{
|
||||||
string pkgManifestUrl = RobloxDeployment.GetLocation($"/{versionGuid}-rbxManifest.txt");
|
string pkgManifestUrl = Deployment.GetLocation($"/{versionGuid}-rbxManifest.txt");
|
||||||
var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl);
|
var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl);
|
||||||
|
|
||||||
return new FileManifest(pkgManifestData);
|
return new FileManifest(pkgManifestData);
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace Bloxstrap
|
namespace Bloxstrap.RobloxInterfaces
|
||||||
{
|
{
|
||||||
public class RobloxFastFlags
|
// i am 100% sure there is a much, MUCH better way to handle this
|
||||||
|
// matt wrote this so this is effectively a black box to me right now
|
||||||
|
// i'll likely refactor this at some point
|
||||||
|
public class ApplicationSettings
|
||||||
{
|
{
|
||||||
private string _applicationName;
|
private string _applicationName;
|
||||||
private string _channelName;
|
private string _channelName;
|
||||||
@ -12,7 +15,7 @@ namespace Bloxstrap
|
|||||||
|
|
||||||
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
|
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
private RobloxFastFlags(string applicationName, string channelName)
|
private ApplicationSettings(string applicationName, string channelName)
|
||||||
{
|
{
|
||||||
_applicationName = applicationName;
|
_applicationName = applicationName;
|
||||||
_channelName = channelName;
|
_channelName = channelName;
|
||||||
@ -29,11 +32,11 @@ namespace Bloxstrap
|
|||||||
if (_initialised)
|
if (_initialised)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string logIndent = $"RobloxFastFlags::Fetch.{_applicationName}.{_channelName}";
|
string logIndent = $"ApplicationSettings::Fetch.{_applicationName}.{_channelName}";
|
||||||
App.Logger.WriteLine(logIndent, "Fetching fast flags");
|
App.Logger.WriteLine(logIndent, "Fetching fast flags");
|
||||||
|
|
||||||
string path = $"/v2/settings/application/{_applicationName}";
|
string path = $"/v2/settings/application/{_applicationName}";
|
||||||
if (_channelName != RobloxDeployment.DefaultChannel.ToLowerInvariant())
|
if (_channelName != Deployment.DefaultChannel.ToLowerInvariant())
|
||||||
path += $"/bucket/{_channelName}";
|
path += $"/bucket/{_channelName}";
|
||||||
|
|
||||||
HttpResponseMessage response;
|
HttpResponseMessage response;
|
||||||
@ -100,12 +103,13 @@ namespace Bloxstrap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// _cache[applicationName][channelName]
|
// _cache[applicationName][channelName]
|
||||||
private static Dictionary<string, Dictionary<string, RobloxFastFlags>> _cache = new();
|
private static Dictionary<string, Dictionary<string, ApplicationSettings>> _cache = new();
|
||||||
|
|
||||||
public static RobloxFastFlags PCDesktopClient { get; } = GetSettings("PCDesktopClient");
|
public static ApplicationSettings PCDesktopClient => GetSettings("PCDesktopClient");
|
||||||
public static RobloxFastFlags PCClientBootstrapper { get; } = GetSettings("PCClientBootstrapper");
|
|
||||||
|
|
||||||
public static RobloxFastFlags GetSettings(string applicationName, string channelName = RobloxDeployment.DefaultChannel, bool shouldCache = true)
|
public static ApplicationSettings PCClientBootstrapper => GetSettings("PCClientBootstrapper");
|
||||||
|
|
||||||
|
public static ApplicationSettings GetSettings(string applicationName, string channelName = Deployment.DefaultChannel, bool shouldCache = true)
|
||||||
{
|
{
|
||||||
channelName = channelName.ToLowerInvariant();
|
channelName = channelName.ToLowerInvariant();
|
||||||
|
|
||||||
@ -114,7 +118,7 @@ namespace Bloxstrap
|
|||||||
if (_cache.ContainsKey(applicationName) && _cache[applicationName].ContainsKey(channelName))
|
if (_cache.ContainsKey(applicationName) && _cache[applicationName].ContainsKey(channelName))
|
||||||
return _cache[applicationName][channelName];
|
return _cache[applicationName][channelName];
|
||||||
|
|
||||||
var flags = new RobloxFastFlags(applicationName, channelName);
|
var flags = new ApplicationSettings(applicationName, channelName);
|
||||||
|
|
||||||
if (shouldCache)
|
if (shouldCache)
|
||||||
{
|
{
|
@ -1,13 +1,19 @@
|
|||||||
namespace Bloxstrap
|
namespace Bloxstrap.RobloxInterfaces
|
||||||
{
|
{
|
||||||
public static class RobloxDeployment
|
public static class Deployment
|
||||||
{
|
{
|
||||||
public const string DefaultChannel = "production";
|
public const string DefaultChannel = "production";
|
||||||
|
|
||||||
private const string VersionStudioHash = "version-012732894899482c";
|
private const string VersionStudioHash = "version-012732894899482c";
|
||||||
|
|
||||||
public static string BaseUrl { get; private set; } = null!;
|
public static string Channel = DefaultChannel;
|
||||||
|
|
||||||
|
public static string BinaryType = "WindowsPlayer";
|
||||||
|
|
||||||
|
public static bool IsDefaultChannel => String.Compare(Channel, DefaultChannel, StringComparison.OrdinalIgnoreCase) == 0;
|
||||||
|
|
||||||
|
public static string BaseUrl { get; private set; } = null!;
|
||||||
|
|
||||||
private static readonly Dictionary<string, ClientVersion> ClientVersionCache = new();
|
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
|
// a list of roblox deployment locations that we check for, in case one of them don't work
|
||||||
@ -23,7 +29,7 @@
|
|||||||
|
|
||||||
private static async Task<string?> TestConnection(string url, int priority, CancellationToken token)
|
private static async Task<string?> TestConnection(string url, int priority, CancellationToken token)
|
||||||
{
|
{
|
||||||
string LOG_IDENT = $"RobloxDeployment::TestConnection<{url}>";
|
string LOG_IDENT = $"Deployment::TestConnection<{url}>";
|
||||||
|
|
||||||
await Task.Delay(priority * 1000, token);
|
await Task.Delay(priority * 1000, token);
|
||||||
|
|
||||||
@ -32,7 +38,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await App.HttpClient.GetAsync($"{url}/versionStudio", token);
|
var response = await App.HttpClient.GetAsync($"{url}/versionStudio", token);
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
// versionStudio is the version hash for the last MFC studio to be deployed.
|
// versionStudio is the version hash for the last MFC studio to be deployed.
|
||||||
@ -56,16 +62,14 @@
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This function serves double duty as the setup mirror enumerator, and as our connectivity check.
|
||||||
|
/// Returns null for success.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public static async Task<Exception?> InitializeConnectivity()
|
public static async Task<Exception?> InitializeConnectivity()
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "RobloxDeployment::InitializeConnectivity";
|
const string LOG_IDENT = "Deployment::InitializeConnectivity";
|
||||||
|
|
||||||
// this function serves double duty as the setup mirror enumerator, and as our connectivity check
|
|
||||||
// since we're basically asking four different urls for the exact same thing, if all four fail, then it has to be a user-side problem
|
|
||||||
|
|
||||||
// this should be checked for in the installer and in the bootstrapper
|
|
||||||
|
|
||||||
// returns null for success
|
|
||||||
|
|
||||||
var tokenSource = new CancellationTokenSource();
|
var tokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
@ -74,25 +78,22 @@
|
|||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, "Testing connectivity...");
|
App.Logger.WriteLine(LOG_IDENT, "Testing connectivity...");
|
||||||
|
|
||||||
while (tasks.Any())
|
while (tasks.Any() && String.IsNullOrEmpty(BaseUrl))
|
||||||
{
|
{
|
||||||
var finishedTask = await Task.WhenAny(tasks);
|
var finishedTask = await Task.WhenAny(tasks);
|
||||||
|
|
||||||
if (finishedTask.IsFaulted)
|
tasks.Remove(finishedTask);
|
||||||
{
|
|
||||||
tasks.Remove(finishedTask);
|
|
||||||
exceptions.Add(finishedTask.Exception!.InnerException!);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseUrl = await finishedTask;
|
if (finishedTask.IsFaulted)
|
||||||
break;
|
exceptions.Add(finishedTask.Exception!.InnerException!);
|
||||||
|
else
|
||||||
|
BaseUrl = finishedTask.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop other running connectivity tests
|
// stop other running connectivity tests
|
||||||
tokenSource.Cancel();
|
tokenSource.Cancel();
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(BaseUrl))
|
if (string.IsNullOrEmpty(BaseUrl))
|
||||||
return exceptions[0];
|
return exceptions[0];
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Got {BaseUrl} as the optimal base URL");
|
App.Logger.WriteLine(LOG_IDENT, $"Got {BaseUrl} as the optimal base URL");
|
||||||
@ -100,18 +101,18 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetLocation(string resource, string channel = DefaultChannel)
|
public static string GetLocation(string resource)
|
||||||
{
|
{
|
||||||
string location = BaseUrl;
|
string location = BaseUrl;
|
||||||
|
|
||||||
if (String.Compare(channel, DefaultChannel, StringComparison.InvariantCultureIgnoreCase) != 0)
|
if (!IsDefaultChannel)
|
||||||
{
|
{
|
||||||
string channelName;
|
string channelName;
|
||||||
|
|
||||||
if (RobloxFastFlags.GetSettings(nameof(RobloxFastFlags.PCClientBootstrapper), channel).Get<bool>("FFlagReplaceChannelNameForDownload"))
|
if (ApplicationSettings.GetSettings(nameof(ApplicationSettings.PCClientBootstrapper), Channel).Get<bool>("FFlagReplaceChannelNameForDownload"))
|
||||||
channelName = "common";
|
channelName = "common";
|
||||||
else
|
else
|
||||||
channelName = channel.ToLowerInvariant();
|
channelName = Channel.ToLowerInvariant();
|
||||||
|
|
||||||
location += $"/channel/{channelName}";
|
location += $"/channel/{channelName}";
|
||||||
}
|
}
|
||||||
@ -121,16 +122,18 @@
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ClientVersion> GetInfo(string channel, string binaryType = "WindowsPlayer")
|
public static async Task<ClientVersion> GetInfo(string? channel = null)
|
||||||
{
|
{
|
||||||
const string LOG_IDENT = "RobloxDeployment::GetInfo";
|
const string LOG_IDENT = "Deployment::GetInfo";
|
||||||
|
|
||||||
|
if (String.IsNullOrEmpty(channel))
|
||||||
|
channel = Channel;
|
||||||
|
|
||||||
|
bool isDefaultChannel = String.Compare(channel, DefaultChannel, StringComparison.OrdinalIgnoreCase) == 0;
|
||||||
|
|
||||||
App.Logger.WriteLine(LOG_IDENT, $"Getting deploy info for channel {channel}");
|
App.Logger.WriteLine(LOG_IDENT, $"Getting deploy info for channel {channel}");
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(channel))
|
string cacheKey = $"{channel}-{BinaryType}";
|
||||||
channel = DefaultChannel;
|
|
||||||
|
|
||||||
string cacheKey = $"{channel}-{binaryType}";
|
|
||||||
|
|
||||||
ClientVersion clientVersion;
|
ClientVersion clientVersion;
|
||||||
|
|
||||||
@ -141,12 +144,10 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool isDefaultChannel = String.Compare(channel, DefaultChannel, StringComparison.OrdinalIgnoreCase) == 0;
|
string path = $"/v2/client-version/{BinaryType}";
|
||||||
|
|
||||||
string path = $"/v2/client-version/{binaryType}";
|
|
||||||
|
|
||||||
if (!isDefaultChannel)
|
if (!isDefaultChannel)
|
||||||
path = $"/v2/client-version/{binaryType}/channel/{channel}";
|
path = $"/v2/client-version/{BinaryType}/channel/{channel}";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
Loading…
Reference in New Issue
Block a user