add private channel support

This commit is contained in:
bluepilledgreat 2025-04-05 18:52:55 +01:00
parent 1f21e8ce0b
commit de9ff5ab74
6 changed files with 107 additions and 5 deletions

View File

@ -1,4 +1,5 @@
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Security.Cryptography;
using System.Windows;
using System.Windows.Shell;
@ -232,6 +233,37 @@ namespace Bloxstrap
}
}
/// <summary>
/// This should only ever return true if we're launched from the Roblox installer path, created by the private channel launch
/// </summary>
private static bool IsRobloxInstallerPath(string path, [NotNullWhen(true)] out string? truePath)
{
truePath = null;
DirectoryInfo info = new DirectoryInfo(path);
if (info.Name != "RobloxPlayerInstaller.exe" && info.Name != "RobloxStudioInstaller.exe")
return false;
DirectoryInfo? versionDir = info.Parent;
if (versionDir == null || !versionDir.Name.StartsWith("version-", StringComparison.Ordinal))
return false;
DirectoryInfo? versionsDir = versionDir.Parent;
if (versionsDir == null || versionsDir.Name != "Versions")
return false;
DirectoryInfo? bloxstrapDir = versionsDir.Parent;
if (bloxstrapDir == null)
return false;
string bloxstrapPath = Path.Combine(bloxstrapDir.FullName, "Bloxstrap.exe");
if (!File.Exists(bloxstrapPath))
return false;
truePath = bloxstrapPath;
return true;
}
protected override void OnStartup(StartupEventArgs e)
{
const string LOG_IDENT = "App::OnStartup";
@ -277,6 +309,13 @@ namespace Bloxstrap
HttpClient.Timeout = TimeSpan.FromSeconds(30);
HttpClient.DefaultRequestHeaders.Add("User-Agent", userAgent);
// make sure we're in the correct place
if (IsRobloxInstallerPath(Paths.Process, out string? truePath))
{
Process.Start(truePath, e.Args);
return;
}
LaunchSettings = new LaunchSettings(e.Args);
// installation check begins here

View File

@ -14,6 +14,8 @@
string ExecutablePath { get; }
string RobloxInstallerExecutableName { get; }
AppState State { get; }
IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; }

View File

@ -18,6 +18,8 @@ namespace Bloxstrap.AppData
public override AppState State => App.RobloxState.Prop.Player;
public string RobloxInstallerExecutableName => "RobloxPlayerInstaller.exe";
public override IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } = new Dictionary<string, string>()
{
{ "RobloxApp.zip", @"" }

View File

@ -12,6 +12,8 @@
public override AppState State => App.RobloxState.Prop.Studio;
public string RobloxInstallerExecutableName => "RobloxStudioInstaller.exe";
public override IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } = new Dictionary<string, string>()
{
{ "RobloxStudio.zip", @"" },

View File

@ -168,6 +168,42 @@ namespace Bloxstrap
App.Terminate(ErrorCode.ERROR_CANCELLED);
}
private void HandlePrivateChannelLaunch()
{
const string LOG_IDENT = "Bootstrapper::HandlePrivateChannelLaunch";
string path = Path.Combine(_latestVersionDirectory, AppData.RobloxInstallerExecutableName);
if (Deployment.PrivateChannel)
{
if (File.Exists(path))
{
string versionCopyHash = MD5Hash.FromFile(path);
string processHash = MD5Hash.FromFile(Paths.Process);
if (versionCopyHash != processHash)
{
App.Logger.WriteLine(LOG_IDENT, $"Installer in version directory is not the same as the current process ({versionCopyHash} =/= {processHash})");
App.Logger.WriteLine(LOG_IDENT, "Copying...");
File.Copy(Paths.Process, path, true);
}
}
else
{
App.Logger.WriteLine(LOG_IDENT, $"Copying Bloxstrap into the installation folder as {AppData.RobloxInstallerExecutableName}");
App.Logger.WriteLine(LOG_IDENT, "There is a spy among us...");
File.Copy(Paths.Process, path);
}
}
else
{
if (File.Exists(path))
{
App.Logger.WriteLine(LOG_IDENT, $"Deleting {AppData.RobloxInstallerExecutableName} from the version directory");
File.Delete(path);
}
}
}
public async Task Run()
{
const string LOG_IDENT = "Bootstrapper::Run";
@ -250,7 +286,9 @@ namespace Bloxstrap
if (!_noConnection)
{
if (AppData.State.VersionGuid != _latestVersionGuid || _mustUpgrade)
// for private channels, the roblox client will relaunch us with the proper arguments
// so avoid an unnecessary update if roblox is already installed
if ((!Deployment.PrivateChannel && AppData.State.VersionGuid != _latestVersionGuid) || _mustUpgrade)
{
bool backgroundUpdaterMutexOpen = Utilities.DoesMutexExist("Bloxstrap-BackgroundUpdater");
if (App.LaunchSettings.BackgroundUpdaterFlag.Active)
@ -282,6 +320,8 @@ namespace Bloxstrap
allModificationsApplied = await ApplyModifications();
}
HandlePrivateChannelLaunch();
// check registry entries for every launch, just in case the stock bootstrapper changes it back
if (IsStudioLaunch)
@ -365,6 +405,12 @@ namespace Bloxstrap
{
App.Logger.WriteLine(LOG_IDENT, $"Resetting channel from {Deployment.Channel} because {ex.StatusCode}");
if (ex.StatusCode == HttpStatusCode.Unauthorized)
{
App.Logger.WriteLine(LOG_IDENT, "Enabling private channel launch mode");
Deployment.PrivateChannel = true;
}
Deployment.Channel = Deployment.DefaultChannel;
clientVersion = await Deployment.GetInfo();
}
@ -429,6 +475,12 @@ namespace Bloxstrap
return false;
}
if (Deployment.PrivateChannel)
{
App.Logger.WriteLine(LOG_IDENT, "Not eligible: Private channel launch");
return false;
}
// at least 3GB of free space
const long minimumFreeSpace = 3_000_000_000;
long space = Filesystem.GetFreeDiskSpace(Paths.Base);

View File

@ -6,9 +6,14 @@
private const string VersionStudioHash = "version-012732894899482c";
public static string Channel = DefaultChannel;
public static string Channel { get; set; } = DefaultChannel;
public static string BinaryType = "WindowsPlayer";
/// <summary>
/// Copies Bloxstrap into the Roblox installation folder to allow for private channel support.
/// </summary>
public static bool PrivateChannel { get; set; } = false;
public static string BinaryType { get; set; } = "WindowsPlayer";
public static bool IsDefaultChannel => Channel.Equals(DefaultChannel, StringComparison.OrdinalIgnoreCase);