From 78882ec1ae85b71508fc74da9eea21da3a0a1373 Mon Sep 17 00:00:00 2001 From: Matt <97983689+bluepilledgreat@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:59:26 +0000 Subject: [PATCH] bring back the old roblox version directory (#3908) --- Bloxstrap/AppData/CommonAppData.cs | 10 +- Bloxstrap/AppData/IAppData.cs | 4 - Bloxstrap/AppData/RobloxPlayerData.cs | 6 +- Bloxstrap/AppData/RobloxStudioData.cs | 6 +- Bloxstrap/Bloxstrap.csproj | 4 +- Bloxstrap/Bootstrapper.cs | 123 +++++++++++----------- Bloxstrap/Extensions/RegistryKeyEx.cs | 14 +++ Bloxstrap/Installer.cs | 22 ++-- Bloxstrap/Models/Entities/ActivityData.cs | 3 +- Bloxstrap/Paths.cs | 4 +- Bloxstrap/Utilities.cs | 7 +- Bloxstrap/Watcher.cs | 5 +- 12 files changed, 106 insertions(+), 102 deletions(-) diff --git a/Bloxstrap/AppData/CommonAppData.cs b/Bloxstrap/AppData/CommonAppData.cs index 54aaaa01..1e1b4253 100644 --- a/Bloxstrap/AppData/CommonAppData.cs +++ b/Bloxstrap/AppData/CommonAppData.cs @@ -42,13 +42,13 @@ public abstract class CommonAppData }; public virtual string ExecutableName { get; } = null!; - - public virtual string Directory { get; } = null!; - public string LockFilePath => Path.Combine(Directory, "Bloxstrap.lock"); - + public string Directory => Path.Combine(Paths.Versions, State.VersionGuid); + public string ExecutablePath => Path.Combine(Directory, ExecutableName); - + + public virtual AppState State { get; } = null!; + public virtual IReadOnlyDictionary PackageDirectoryMap { get; set; } diff --git a/Bloxstrap/AppData/IAppData.cs b/Bloxstrap/AppData/IAppData.cs index 63a6b482..b19aa959 100644 --- a/Bloxstrap/AppData/IAppData.cs +++ b/Bloxstrap/AppData/IAppData.cs @@ -12,10 +12,6 @@ internal interface IAppData string Directory { get; } - string OldDirectory { get; } - - string LockFilePath { get; } - string ExecutablePath { get; } AppState State { get; } diff --git a/Bloxstrap/AppData/RobloxPlayerData.cs b/Bloxstrap/AppData/RobloxPlayerData.cs index 6ec09a1d..3c4f7285 100644 --- a/Bloxstrap/AppData/RobloxPlayerData.cs +++ b/Bloxstrap/AppData/RobloxPlayerData.cs @@ -16,11 +16,7 @@ public class RobloxPlayerData : CommonAppData, IAppData public override string ExecutableName => "RobloxPlayerBeta.exe"; - public override string Directory => Path.Combine(Paths.Roblox, "Player"); - - public string OldDirectory => Path.Combine(Paths.Roblox, "Player.old"); - - public AppState State => App.State.Prop.Player; + public override AppState State => App.State.Prop.Player; public override IReadOnlyDictionary PackageDirectoryMap { get; set; } = new Dictionary() { diff --git a/Bloxstrap/AppData/RobloxStudioData.cs b/Bloxstrap/AppData/RobloxStudioData.cs index 886e630a..2ada1c2e 100644 --- a/Bloxstrap/AppData/RobloxStudioData.cs +++ b/Bloxstrap/AppData/RobloxStudioData.cs @@ -10,11 +10,7 @@ public class RobloxStudioData : CommonAppData, IAppData public override string ExecutableName => "RobloxStudioBeta.exe"; - public override string Directory => Path.Combine(Paths.Roblox, "Studio"); - - public string OldDirectory => Path.Combine(Paths.Roblox, "Studio.old"); - - public AppState State => App.State.Prop.Studio; + public override AppState State => App.State.Prop.Studio; public override IReadOnlyDictionary PackageDirectoryMap { get; set; } = new Dictionary() { diff --git a/Bloxstrap/Bloxstrap.csproj b/Bloxstrap/Bloxstrap.csproj index 9f1b69a1..9ca54f8d 100644 --- a/Bloxstrap/Bloxstrap.csproj +++ b/Bloxstrap/Bloxstrap.csproj @@ -7,8 +7,8 @@ true True Bloxstrap.ico - 2.8.1 - 2.8.1 + 2.8.2 + 2.8.2 app.manifest true false diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index cb04b7d7..660d02f8 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -50,6 +50,7 @@ public class Bootstrapper private string _launchCommandLine = App.LaunchSettings.RobloxLaunchArgs; private string _latestVersionGuid = null!; + private string _latestVersionDirectory = null!; private PackageManifest _versionPackageManifest = null!; private bool _isInstalling = false; @@ -58,7 +59,7 @@ public class Bootstrapper private double _taskbarProgressMaximum; private long _totalDownloadedBytes = 0; - private bool _mustUpgrade => String.IsNullOrEmpty(AppData.State.VersionGuid) || File.Exists(AppData.LockFilePath) || !File.Exists(AppData.ExecutablePath); + private bool _mustUpgrade => String.IsNullOrEmpty(AppData.State.VersionGuid) || !File.Exists(AppData.ExecutablePath); private bool _noConnection = false; private AsyncMutex? _mutex; @@ -313,6 +314,7 @@ private async Task GetLatestVersionInfo() key.SetValueSafe("www.roblox.com", Deployment.IsDefaultChannel ? "" : Deployment.Channel); _latestVersionGuid = clientVersion.VersionGuid; + _latestVersionDirectory = Path.Combine(Paths.Versions, _latestVersionGuid); string pkgManifestUrl = Deployment.GetLocation($"/{_latestVersionGuid}-rbxPkgManifest.txt"); var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl); @@ -513,18 +515,13 @@ public void Cancel() try { // clean up install - if (Directory.Exists(AppData.Directory)) - Directory.Delete(AppData.Directory, true); + if (Directory.Exists(_latestVersionDirectory)) + Directory.Delete(_latestVersionDirectory, true); } catch (Exception ex) { App.Logger.WriteLine(LOG_IDENT, "Could not fully clean up installation!"); App.Logger.WriteException(LOG_IDENT, ex); - - // assurance to make sure the next launch does a fresh install - // we probably shouldn't be using the lockfile to do this, but meh - var lockFile = new FileInfo(AppData.LockFilePath); - lockFile.Create().Dispose(); } } else if (_appPid != 0) @@ -649,69 +646,67 @@ private async Task CheckForUpdates() return false; } -#endregion + #endregion #region Roblox Install - private async Task UpgradeRoblox() + private void CleanupVersionsFolder() { - const string LOG_IDENT = "Bootstrapper::UpgradeRoblox"; - - if (String.IsNullOrEmpty(AppData.State.VersionGuid)) - SetStatus(Strings.Bootstrapper_Status_Installing); - else - SetStatus(Strings.Bootstrapper_Status_Upgrading); - - Directory.CreateDirectory(Paths.Base); - Directory.CreateDirectory(Paths.Downloads); - Directory.CreateDirectory(Paths.Roblox); + const string LOG_IDENT = "Bootstrapper::CleanupVersionsFolder"; - if (Directory.Exists(AppData.Directory)) + foreach (string dir in Directory.GetDirectories(Paths.Versions)) { - if (Directory.Exists(AppData.OldDirectory)) - Directory.Delete(AppData.OldDirectory, true); + string dirName = Path.GetFileName(dir); - try - { - // test to see if any files are in use - // if you have a better way to check for this, please let me know! - Directory.Move(AppData.Directory, AppData.OldDirectory); - } - catch (Exception ex) + if (dirName != App.State.Prop.Player.VersionGuid && dirName != App.State.Prop.Studio.VersionGuid) { - App.Logger.WriteLine(LOG_IDENT, "Could not clear old files, aborting update."); - App.Logger.WriteException(LOG_IDENT, ex); - - // 0x80070020 is the HRESULT that indicates that a process is still running - // (either RobloxPlayerBeta or RobloxCrashHandler), so we'll silently ignore it - if ((uint)ex.HResult != 0x80070020) + try + { + Directory.Delete(dir, true); + } + catch (IOException ex) { - // ensure no files are marked as read-only for good measure - foreach (var file in Directory.GetFiles(AppData.Directory, "*", SearchOption.AllDirectories)) - Filesystem.AssertReadOnly(file); + App.Logger.WriteLine(LOG_IDENT, $"Failed to delete {dir}"); + App.Logger.WriteException(LOG_IDENT, ex); + } + } + } + } - Frontend.ShowMessageBox( - Strings.Bootstrapper_FilesInUse, - _mustUpgrade ? MessageBoxImage.Error : MessageBoxImage.Warning - ); + private void MigrateCompatibilityFlags() + { + const string LOG_IDENT = "Bootstrapper::MigrateCompatibilityFlags"; - if (_mustUpgrade) - App.Terminate(ErrorCode.ERROR_CANCELLED); - } + string oldClientLocation = Path.Combine(Paths.Versions, AppData.State.VersionGuid, AppData.ExecutableName); + string newClientLocation = Path.Combine(_latestVersionDirectory, AppData.ExecutableName); - return; - } + // move old compatibility flags for the old location + using RegistryKey appFlagsKey = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"); + string? appFlags = appFlagsKey.GetValue(oldClientLocation) as string; - Directory.Delete(AppData.OldDirectory, true); + if (appFlags is not null) + { + App.Logger.WriteLine(LOG_IDENT, $"Migrating app compatibility flags from {oldClientLocation} to {newClientLocation}..."); + appFlagsKey.SetValueSafe(newClientLocation, appFlags); + appFlagsKey.DeleteValueSafe(oldClientLocation); } + } - _isInstalling = true; + private async Task UpgradeRoblox() + { + const string LOG_IDENT = "Bootstrapper::UpgradeRoblox"; - Directory.CreateDirectory(AppData.Directory); + if (String.IsNullOrEmpty(AppData.State.VersionGuid)) + SetStatus(Strings.Bootstrapper_Status_Installing); + else + SetStatus(Strings.Bootstrapper_Status_Upgrading); + + Directory.CreateDirectory(Paths.Base); + Directory.CreateDirectory(Paths.Downloads); + Directory.CreateDirectory(Paths.Versions); + + _isInstalling = true; - // installer lock, it should only be present while roblox is in the process of upgrading - // if it's present while we're launching, then it's an unfinished install and must be reinstalled - var lockFile = new FileInfo(AppData.LockFilePath); - lockFile.Create().Dispose(); + Directory.CreateDirectory(_latestVersionDirectory); var cachedPackageHashes = Directory.GetFiles(Paths.Downloads).Select(x => Path.GetFileName(x)); @@ -779,7 +774,7 @@ private async Task UpgradeRoblox() await Task.WhenAll(extractionTasks); App.Logger.WriteLine(LOG_IDENT, "Writing AppSettings.xml..."); - await File.WriteAllTextAsync(Path.Combine(AppData.Directory, "AppSettings.xml"), AppSettings); + await File.WriteAllTextAsync(Path.Combine(_latestVersionDirectory, "AppSettings.xml"), AppSettings); if (_cancelTokenSource.IsCancellationRequested) return; @@ -814,7 +809,7 @@ private async Task UpgradeRoblox() return; } - string baseDirectory = Path.Combine(AppData.Directory, AppData.PackageDirectoryMap[package.Name]); + string baseDirectory = Path.Combine(_latestVersionDirectory, AppData.PackageDirectoryMap[package.Name]); ExtractPackage(package); @@ -838,6 +833,8 @@ private async Task UpgradeRoblox() // finishing and cleanup + MigrateCompatibilityFlags(); + AppData.State.VersionGuid = _latestVersionGuid; AppData.State.PackageHashes.Clear(); @@ -845,6 +842,8 @@ private async Task UpgradeRoblox() foreach (var package in _versionPackageManifest) AppData.State.PackageHashes.Add(package.Name, package.Signature); + CleanupVersionsFolder(); + var allPackageHashes = new List(); allPackageHashes.AddRange(App.State.Prop.Player.PackageHashes.Values); @@ -885,8 +884,6 @@ private async Task UpgradeRoblox() App.State.Save(); - lockFile.Delete(); - _isInstalling = false; } @@ -919,7 +916,7 @@ private async Task ApplyModifications() const string path = "rbxasset://fonts/CustomFont.ttf"; - foreach (string jsonFilePath in Directory.GetFiles(Path.Combine(AppData.Directory, "content\\fonts\\families"))) + foreach (string jsonFilePath in Directory.GetFiles(Path.Combine(_latestVersionDirectory, "content\\fonts\\families"))) { string jsonFilename = Path.GetFileName(jsonFilePath); string modFilepath = Path.Combine(modFontFamiliesFolder, jsonFilename); @@ -980,7 +977,7 @@ private async Task ApplyModifications() modFolderFiles.Add(relativeFile); string fileModFolder = Path.Combine(Paths.Modifications, relativeFile); - string fileVersionFolder = Path.Combine(AppData.Directory, relativeFile); + string fileVersionFolder = Path.Combine(_latestVersionDirectory, relativeFile); if (File.Exists(fileVersionFolder) && MD5Hash.FromFile(fileModFolder) == MD5Hash.FromFile(fileVersionFolder)) { @@ -1016,7 +1013,7 @@ private async Task ApplyModifications() { App.Logger.WriteLine(LOG_IDENT, $"{fileLocation} was removed as a mod but does not belong to a package"); - string versionFileLocation = Path.Combine(AppData.Directory, fileLocation); + string versionFileLocation = Path.Combine(_latestVersionDirectory, fileLocation); if (File.Exists(versionFileLocation)) File.Delete(versionFileLocation); @@ -1204,7 +1201,7 @@ private void ExtractPackage(Package package, List? files = null) return; } - string packageFolder = Path.Combine(AppData.Directory, packageDir); + string packageFolder = Path.Combine(_latestVersionDirectory, packageDir); string? fileFilter = null; // for sharpziplib, each file in the filter needs to be a regex diff --git a/Bloxstrap/Extensions/RegistryKeyEx.cs b/Bloxstrap/Extensions/RegistryKeyEx.cs index dd556909..19efd58d 100644 --- a/Bloxstrap/Extensions/RegistryKeyEx.cs +++ b/Bloxstrap/Extensions/RegistryKeyEx.cs @@ -17,5 +17,19 @@ public static void SetValueSafe(this RegistryKey registryKey, string? name, obje App.Terminate(ErrorCode.ERROR_INSTALL_FAILURE); } } + + public static void DeleteValueSafe(this RegistryKey registryKey, string name) + { + try + { + App.Logger.WriteLine("RegistryKeyEx::DeleteValueSafe", $"Deleting {registryKey}\\{name}"); + registryKey.DeleteValue(name); + } + catch (UnauthorizedAccessException) + { + Frontend.ShowMessageBox(Strings.Dialog_RegistryWriteError, System.Windows.MessageBoxImage.Error); + App.Terminate(ErrorCode.ERROR_INSTALL_FAILURE); + } + } } } diff --git a/Bloxstrap/Installer.cs b/Bloxstrap/Installer.cs index 4eaeacf1..dbd5be80 100644 --- a/Bloxstrap/Installer.cs +++ b/Bloxstrap/Installer.cs @@ -287,8 +287,8 @@ public static void DoUninstall(bool keepData) () => File.Delete(StartMenuShortcut), + () => Directory.Delete(Paths.Versions, true), () => Directory.Delete(Paths.Downloads, true), - () => Directory.Delete(Paths.Roblox, true), () => File.Delete(App.State.FileLocation) }; @@ -547,15 +547,6 @@ public static void HandleUpgrade() App.FastFlags.SetValue("FFlagDisableNewIGMinDUA", null); App.FastFlags.SetValue("FFlagFixGraphicsQuality", null); - - try - { - Directory.Delete(Path.Combine(Paths.Base, "Versions"), true); - } - catch (Exception ex) - { - App.Logger.WriteException(LOG_IDENT, ex); - } } if (Utilities.CompareVersions(existingVer, "2.8.1") == VersionComparison.LessThan) @@ -572,6 +563,17 @@ public static void HandleUpgrade() App.FastFlags.SetValue("FFlagEnableInGameMenuChromeABTest4", null); } + if (Utilities.CompareVersions(existingVer, "2.8.2") == VersionComparison.LessThan) + { + try + { + Directory.Delete(Path.Combine(Paths.Base, "Roblox"), true); + } + catch (Exception ex) + { + App.Logger.WriteException(LOG_IDENT, ex); + } + } App.Settings.Save(); App.FastFlags.Save(); diff --git a/Bloxstrap/Models/Entities/ActivityData.cs b/Bloxstrap/Models/Entities/ActivityData.cs index 0afd1a89..039d06a6 100644 --- a/Bloxstrap/Models/Entities/ActivityData.cs +++ b/Bloxstrap/Models/Entities/ActivityData.cs @@ -1,6 +1,7 @@ using System.Web; using System.Windows; using System.Windows.Input; +using Bloxstrap.AppData; using Bloxstrap.Models.APIs; using CommunityToolkit.Mvvm.Input; @@ -149,7 +150,7 @@ public string GetInviteDeeplink(bool launchData = true) private void RejoinServer() { - string playerPath = Path.Combine(Paths.Roblox, "Player", "RobloxPlayerBeta.exe"); + string playerPath = new RobloxPlayerData().ExecutablePath; Process.Start(playerPath, GetInviteDeeplink(false)); } diff --git a/Bloxstrap/Paths.cs b/Bloxstrap/Paths.cs index 36f81361..43d4a1c0 100644 --- a/Bloxstrap/Paths.cs +++ b/Bloxstrap/Paths.cs @@ -20,8 +20,8 @@ static class Paths public static string Downloads { get; private set; } = ""; public static string Logs { get; private set; } = ""; public static string Integrations { get; private set; } = ""; + public static string Versions { get; private set; } = ""; public static string Modifications { get; private set; } = ""; - public static string Roblox { get; private set; } = ""; public static string Application { get; private set; } = ""; @@ -35,8 +35,8 @@ public static void Initialize(string baseDirectory) Downloads = Path.Combine(Base, "Downloads"); Logs = Path.Combine(Base, "Logs"); Integrations = Path.Combine(Base, "Integrations"); + Versions = Path.Combine(Base, "Versions"); Modifications = Path.Combine(Base, "Modifications"); - Roblox = Path.Combine(Base, "Roblox"); Application = Path.Combine(Base, $"{App.ProjectName}.exe"); } diff --git a/Bloxstrap/Utilities.cs b/Bloxstrap/Utilities.cs index b1cd93d0..35c11ac6 100644 --- a/Bloxstrap/Utilities.cs +++ b/Bloxstrap/Utilities.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using Bloxstrap.AppData; +using System.ComponentModel; namespace Bloxstrap { @@ -64,9 +65,9 @@ public static VersionComparison CompareVersions(string versionStr1, string versi public static string GetRobloxVersion(bool studio) { - string fileName = studio ? "Studio/RobloxStudioBeta.exe" : "Player/RobloxPlayerBeta.exe"; + IAppData data = studio ? new RobloxStudioData() : new RobloxPlayerData(); - string playerLocation = Path.Combine(Paths.Roblox, fileName); + string playerLocation = data.ExecutablePath; if (!File.Exists(playerLocation)) return ""; diff --git a/Bloxstrap/Watcher.cs b/Bloxstrap/Watcher.cs index 660d5d41..eef397e8 100644 --- a/Bloxstrap/Watcher.cs +++ b/Bloxstrap/Watcher.cs @@ -1,4 +1,5 @@ -using Bloxstrap.Integrations; +using Bloxstrap.AppData; +using Bloxstrap.Integrations; using Bloxstrap.Models; namespace Bloxstrap @@ -30,7 +31,7 @@ public Watcher() #if DEBUG if (String.IsNullOrEmpty(watcherDataArg)) { - string path = Path.Combine(Paths.Roblox, "Player", "RobloxPlayerBeta.exe"); + string path = new RobloxPlayerData().ExecutablePath; using var gameClientProcess = Process.Start(path); _watcherData = new() { ProcessId = gameClientProcess.Id };