From 6a93624040fb98fee0decb11d1168c5731b6807a Mon Sep 17 00:00:00 2001 From: pizzaboxer Date: Fri, 25 Oct 2024 23:44:12 +0100 Subject: [PATCH] Improve Roblox upgrade reliability Addresses #3408 --- Bloxstrap/AppData/IAppData.cs | 2 ++ Bloxstrap/AppData/RobloxPlayerData.cs | 2 ++ Bloxstrap/AppData/RobloxStudioData.cs | 4 ++- Bloxstrap/Bootstrapper.cs | 33 ++++++++++++++++++------ Bloxstrap/Logger.cs | 4 ++- Bloxstrap/Resources/Strings.Designer.cs | 11 ++++++++ Bloxstrap/Resources/Strings.resx | 6 +++++ Bloxstrap/RobloxInterfaces/Deployment.cs | 5 ++++ 8 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Bloxstrap/AppData/IAppData.cs b/Bloxstrap/AppData/IAppData.cs index 7af04f7..63a6b48 100644 --- a/Bloxstrap/AppData/IAppData.cs +++ b/Bloxstrap/AppData/IAppData.cs @@ -12,6 +12,8 @@ string Directory { get; } + string OldDirectory { get; } + string LockFilePath { get; } string ExecutablePath { get; } diff --git a/Bloxstrap/AppData/RobloxPlayerData.cs b/Bloxstrap/AppData/RobloxPlayerData.cs index 1477b8c..6ec09a1 100644 --- a/Bloxstrap/AppData/RobloxPlayerData.cs +++ b/Bloxstrap/AppData/RobloxPlayerData.cs @@ -18,6 +18,8 @@ namespace Bloxstrap.AppData 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 IReadOnlyDictionary PackageDirectoryMap { get; set; } = new Dictionary() diff --git a/Bloxstrap/AppData/RobloxStudioData.cs b/Bloxstrap/AppData/RobloxStudioData.cs index 4bc2369..886e630 100644 --- a/Bloxstrap/AppData/RobloxStudioData.cs +++ b/Bloxstrap/AppData/RobloxStudioData.cs @@ -11,7 +11,9 @@ 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 IReadOnlyDictionary PackageDirectoryMap { get; set; } = new Dictionary() diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs index ab8bf25..381965a 100644 --- a/Bloxstrap/Bootstrapper.cs +++ b/Bloxstrap/Bootstrapper.cs @@ -452,8 +452,8 @@ namespace Bloxstrap Process.Start(Paths.Process, args); } - // average grace time between log being created and the window being shown - Thread.Sleep(2000); + // allow for window to show, since the log is created pretty far beforehand + Thread.Sleep(1000); } public void Cancel() @@ -631,19 +631,36 @@ namespace Bloxstrap { try { - // gross hack to see if roblox is still running - // i don't want to rely on mutexes because they can change, and will false flag for - // running installations that are not by bloxstrap - File.Delete(AppData.ExecutablePath); + // 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) { - App.Logger.WriteLine(LOG_IDENT, "Could not delete executable/folder, Roblox may still be running. Aborting update."); + 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) + { + // ensure no files are marked as read-only for good measure + foreach (var file in Directory.GetFiles(AppData.Directory, "*", SearchOption.AllDirectories)) + Filesystem.AssertReadOnly(file); + + Frontend.ShowMessageBox( + Strings.Bootstrapper_FilesInUse, + _mustUpgrade ? MessageBoxImage.Error : MessageBoxImage.Warning + ); + + if (_mustUpgrade) + App.Terminate(ErrorCode.ERROR_CANCELLED); + } + return; } - Directory.Delete(AppData.Directory, true); + Directory.Delete(AppData.OldDirectory, true); } _isInstalling = true; diff --git a/Bloxstrap/Logger.cs b/Bloxstrap/Logger.cs index 4d4ee69..ff6db1a 100644 --- a/Bloxstrap/Logger.cs +++ b/Bloxstrap/Logger.cs @@ -115,7 +115,9 @@ { Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; - WriteLine($"[{identifier}] {ex}"); + string hresult = "0x" + ex.HResult.ToString("X8"); + + WriteLine($"[{identifier}] ({hresult}) {ex}"); Thread.CurrentThread.CurrentUICulture = Locale.CurrentCulture; } diff --git a/Bloxstrap/Resources/Strings.Designer.cs b/Bloxstrap/Resources/Strings.Designer.cs index 115ee7e..41b4c8f 100644 --- a/Bloxstrap/Resources/Strings.Designer.cs +++ b/Bloxstrap/Resources/Strings.Designer.cs @@ -168,6 +168,17 @@ namespace Bloxstrap.Resources { } } + /// + /// Looks up a localized string similar to Bloxstrap tried to upgrade Roblox but can't because Roblox's files are still in use. + /// + ///Please close any applications that may be using Roblox's files, and relaunch.. + /// + public static string Bootstrapper_FilesInUse { + get { + return ResourceManager.GetString("Bootstrapper.FilesInUse", resourceCulture); + } + } + /// /// Looks up a localized string similar to You must first install Bloxstrap before uninstalling.. /// diff --git a/Bloxstrap/Resources/Strings.resx b/Bloxstrap/Resources/Strings.resx index 1771f15..2b9ca6e 100644 --- a/Bloxstrap/Resources/Strings.resx +++ b/Bloxstrap/Resources/Strings.resx @@ -1241,4 +1241,10 @@ Would you like to enable test mode? Version {0} + + Bloxstrap tried to upgrade Roblox but can't because Roblox's files are still in use. + +Please close any applications that may be using Roblox's files, and relaunch. + This is *not* for when Roblox is still running when trying to upgrade. This applies to files being open (i.e. image assets) + \ No newline at end of file diff --git a/Bloxstrap/RobloxInterfaces/Deployment.cs b/Bloxstrap/RobloxInterfaces/Deployment.cs index 698be79..f6ee997 100644 --- a/Bloxstrap/RobloxInterfaces/Deployment.cs +++ b/Bloxstrap/RobloxInterfaces/Deployment.cs @@ -157,6 +157,11 @@ { clientVersion = await Http.GetJson("https://clientsettingscdn.roblox.com" + path); } + catch (HttpRequestException) + { + // throw up the exception handler chain, as we shouldn't be the one handling it + throw; + } catch (Exception ex) { App.Logger.WriteLine(LOG_IDENT, "Failed to contact clientsettingscdn! Falling back to clientsettings...");