Abandon staging folder idea, use lockfile instead

thank you matt for helping me see sense through this one
This commit is contained in:
pizzaboxer 2024-09-07 22:50:45 +01:00
parent 72df157eb8
commit 99ff002121
No known key found for this signature in database
GPG Key ID: 59D4A1DBAD0F2BA8
10 changed files with 79 additions and 69 deletions

View File

@ -39,13 +39,13 @@ namespace Bloxstrap.AppData
{ "extracontent-places.zip", @"ExtraContent\places\" }, { "extracontent-places.zip", @"ExtraContent\places\" },
}; };
public virtual string FinalDirectory { get; } = null!;
public string StagingDirectory => $"{FinalDirectory}.staging";
public virtual string ExecutableName { get; } = null!; public virtual string ExecutableName { get; } = null!;
public string ExecutablePath => Path.Combine(FinalDirectory, ExecutableName); public virtual string Directory { get; } = null!;
public string LockFilePath => Path.Combine(Directory, "Bloxstrap.lock");
public string ExecutablePath => Path.Combine(Directory, ExecutableName);
public virtual IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; } public virtual IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; }

View File

@ -12,9 +12,9 @@
string StartEvent { get; } string StartEvent { get; }
string FinalDirectory { get; } string Directory { get; }
string StagingDirectory { get; } string LockFilePath { get; }
string ExecutablePath { get; } string ExecutablePath { get; }

View File

@ -18,7 +18,7 @@ namespace Bloxstrap.AppData
public string StartEvent => "www.roblox.com/robloxStartedEvent"; public string StartEvent => "www.roblox.com/robloxStartedEvent";
public override string FinalDirectory => Path.Combine(Paths.Roblox, "Player"); public override string Directory => Path.Combine(Paths.Roblox, "Player");
public AppState State => App.State.Prop.Player; public AppState State => App.State.Prop.Player;

View File

@ -12,7 +12,7 @@
public string StartEvent => "www.roblox.com/robloxStudioStartedEvent"; public string StartEvent => "www.roblox.com/robloxStudioStartedEvent";
public override string FinalDirectory => Path.Combine(Paths.Roblox, "Studio"); public override string Directory => Path.Combine(Paths.Roblox, "Studio");
public AppState State => App.State.Prop.Studio; public AppState State => App.State.Prop.Studio;

View File

@ -47,6 +47,9 @@ namespace Bloxstrap
private double _progressIncrement; private double _progressIncrement;
private long _totalDownloadedBytes = 0; private long _totalDownloadedBytes = 0;
private bool _mustUpgrade => File.Exists(AppData.LockFilePath) || !File.Exists(AppData.ExecutablePath);
private bool _skipUpgrade = false;
public IBootstrapperDialog? Dialog = null; public IBootstrapperDialog? Dialog = null;
public bool IsStudioLaunch => _launchMode != LaunchMode.Player; public bool IsStudioLaunch => _launchMode != LaunchMode.Player;
@ -162,8 +165,8 @@ namespace Bloxstrap
await GetLatestVersionInfo(); await GetLatestVersionInfo();
// install/update roblox if we're running for the first time, needs updating, or the player location doesn't exist // install/update roblox if we're running for the first time, needs updating, or the player location doesn't exist
if (!File.Exists(AppData.ExecutablePath) || AppData.State.VersionGuid != _latestVersionGuid) if (!_skipUpgrade && (AppData.State.VersionGuid != _latestVersionGuid || _mustUpgrade))
await InstallLatestVersion(); await UpgradeRoblox();
//await ApplyModifications(); //await ApplyModifications();
@ -284,7 +287,7 @@ namespace Bloxstrap
{ {
FileName = AppData.ExecutablePath, FileName = AppData.ExecutablePath,
Arguments = _launchCommandLine, Arguments = _launchCommandLine,
WorkingDirectory = AppData.FinalDirectory WorkingDirectory = AppData.Directory
}; };
if (_launchMode == LaunchMode.StudioAuth) if (_launchMode == LaunchMode.StudioAuth)
@ -362,14 +365,17 @@ namespace Bloxstrap
{ {
using var ipl = new InterProcessLock("Watcher", TimeSpan.FromSeconds(5)); using var ipl = new InterProcessLock("Watcher", TimeSpan.FromSeconds(5));
// TODO: look into if this needs to be launched *before* roblox starts
if (ipl.IsAcquired) if (ipl.IsAcquired)
Process.Start(Paths.Process, $"-watcher \"{args}\""); Process.Start(Paths.Process, $"-watcher \"{args}\"");
} }
} }
public void CancelInstall() // TODO: the bootstrapper dialogs call this function directly.
// this should probably be behind an event invocation.
public void Cancel()
{ {
const string LOG_IDENT = "Bootstrapper::CancelInstall"; const string LOG_IDENT = "Bootstrapper::Cancel";
if (!_isInstalling) if (!_isInstalling)
{ {
@ -381,21 +387,24 @@ namespace Bloxstrap
if (_cancelTokenSource.IsCancellationRequested) if (_cancelTokenSource.IsCancellationRequested)
return; return;
App.Logger.WriteLine(LOG_IDENT, "Cancelling install..."); App.Logger.WriteLine(LOG_IDENT, "Cancelling launch...");
_cancelTokenSource.Cancel(); _cancelTokenSource.Cancel();
if (_isInstalling)
{
try try
{ {
// clean up install // clean up install
if (Directory.Exists(AppData.StagingDirectory)) if (Directory.Exists(AppData.Directory))
Directory.Delete(AppData.StagingDirectory, true); Directory.Delete(AppData.Directory, true);
} }
catch (Exception ex) catch (Exception ex)
{ {
App.Logger.WriteLine(LOG_IDENT, "Could not fully clean up installation!"); App.Logger.WriteLine(LOG_IDENT, "Could not fully clean up installation!");
App.Logger.WriteException(LOG_IDENT, ex); App.Logger.WriteException(LOG_IDENT, ex);
} }
}
Dialog?.CloseBootstrapper(); Dialog?.CloseBootstrapper();
@ -509,11 +518,9 @@ namespace Bloxstrap
#endregion #endregion
#region Roblox Install #region Roblox Install
private async Task InstallLatestVersion() private async Task UpgradeRoblox()
{ {
const string LOG_IDENT = "Bootstrapper::InstallLatestVersion"; const string LOG_IDENT = "Bootstrapper::UpgradeRoblox";
_isInstalling = true;
SetStatus(FreshInstall ? Strings.Bootstrapper_Status_Installing : Strings.Bootstrapper_Status_Upgrading); SetStatus(FreshInstall ? Strings.Bootstrapper_Status_Installing : Strings.Bootstrapper_Status_Upgrading);
@ -521,10 +528,36 @@ namespace Bloxstrap
Directory.CreateDirectory(Paths.Downloads); Directory.CreateDirectory(Paths.Downloads);
Directory.CreateDirectory(Paths.Roblox); Directory.CreateDirectory(Paths.Roblox);
if (Directory.Exists(AppData.StagingDirectory)) if (Directory.Exists(AppData.Directory))
Directory.Delete(AppData.StagingDirectory, true); {
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);
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not delete executable/folder, Roblox may still be running. Aborting update.");
App.Logger.WriteException(LOG_IDENT, ex);
Directory.CreateDirectory(AppData.StagingDirectory); Directory.Delete(AppData.Directory);
return;
}
Directory.Delete(AppData.Directory, true);
}
_isInstalling = true;
Directory.CreateDirectory(AppData.Directory);
// 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();
// package manifest states packed size and uncompressed size in exact bytes // package manifest states packed size and uncompressed size in exact bytes
// packed size only matters if we don't already have the package cached on disk // packed size only matters if we don't already have the package cached on disk
@ -564,8 +597,7 @@ namespace Bloxstrap
if (package.Name == "WebView2RuntimeInstaller.zip") if (package.Name == "WebView2RuntimeInstaller.zip")
continue; continue;
// extract the package immediately after download asynchronously // extract the package async immediately after download
// discard is just used to suppress the warning
extractionTasks.Add(Task.Run(() => ExtractPackage(package), _cancelTokenSource.Token)); extractionTasks.Add(Task.Run(() => ExtractPackage(package), _cancelTokenSource.Token));
} }
@ -586,11 +618,13 @@ namespace Bloxstrap
await Task.WhenAll(extractionTasks); await Task.WhenAll(extractionTasks);
App.Logger.WriteLine(LOG_IDENT, "Writing AppSettings.xml..."); App.Logger.WriteLine(LOG_IDENT, "Writing AppSettings.xml...");
await File.WriteAllTextAsync(Path.Combine(AppData.StagingDirectory, "AppSettings.xml"), AppSettings); await File.WriteAllTextAsync(Path.Combine(AppData.Directory, "AppSettings.xml"), AppSettings);
if (_cancelTokenSource.IsCancellationRequested) if (_cancelTokenSource.IsCancellationRequested)
return; return;
// only prompt on fresh install, since we don't want to be prompting them for every single launch
// TODO: state entry?
if (FreshInstall) if (FreshInstall)
{ {
using var hklmKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"); using var hklmKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}");
@ -612,7 +646,7 @@ namespace Bloxstrap
return; return;
} }
string baseDirectory = Path.Combine(AppData.StagingDirectory, AppData.PackageDirectoryMap[package.Name]); string baseDirectory = Path.Combine(AppData.Directory, AppData.PackageDirectoryMap[package.Name]);
ExtractPackage(package); ExtractPackage(package);
@ -681,37 +715,13 @@ namespace Bloxstrap
App.Logger.WriteLine(LOG_IDENT, $"Registered as {totalSize} KB"); App.Logger.WriteLine(LOG_IDENT, $"Registered as {totalSize} KB");
App.State.Save();
lockFile.Delete();
if (Dialog is not null) if (Dialog is not null)
Dialog.CancelEnabled = false; Dialog.CancelEnabled = false;
if (Directory.Exists(AppData.FinalDirectory))
{
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);
Directory.Delete(AppData.FinalDirectory, true);
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not delete executable/folder, Roblox may still be running. Aborting update.");
App.Logger.WriteException(LOG_IDENT, ex);
Directory.Delete(AppData.StagingDirectory);
_isInstalling = false;
return;
}
}
Directory.Move(AppData.StagingDirectory, AppData.FinalDirectory);
App.State.Save();
_isInstalling = false; _isInstalling = false;
} }
@ -996,7 +1006,7 @@ namespace Bloxstrap
const string LOG_IDENT = "Bootstrapper::ExtractPackage"; const string LOG_IDENT = "Bootstrapper::ExtractPackage";
string packageLocation = Path.Combine(Paths.Downloads, package.Signature); string packageLocation = Path.Combine(Paths.Downloads, package.Signature);
string packageFolder = Path.Combine(AppData.StagingDirectory, AppData.PackageDirectoryMap[package.Name]); string packageFolder = Path.Combine(AppData.Directory, AppData.PackageDirectoryMap[package.Name]);
App.Logger.WriteLine(LOG_IDENT, $"Extracting {package.Name}..."); App.Logger.WriteLine(LOG_IDENT, $"Extracting {package.Name}...");

View File

@ -108,7 +108,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper.Base
public void Dialog_FormClosing(object sender, FormClosingEventArgs e) public void Dialog_FormClosing(object sender, FormClosingEventArgs e)
{ {
if (!_isClosing) if (!_isClosing)
Bootstrapper?.CancelInstall(); Bootstrapper?.Cancel();
} }
#endregion #endregion

View File

@ -105,7 +105,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
private void Window_Closing(object sender, CancelEventArgs e) private void Window_Closing(object sender, CancelEventArgs e)
{ {
if (!_isClosing) if (!_isClosing)
Bootstrapper?.CancelInstall(); Bootstrapper?.Cancel();
} }
#region IBootstrapperDialog Methods #region IBootstrapperDialog Methods

View File

@ -88,7 +88,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
private void UiWindow_Closing(object sender, CancelEventArgs e) private void UiWindow_Closing(object sender, CancelEventArgs e)
{ {
if (!_isClosing) if (!_isClosing)
Bootstrapper?.CancelInstall(); Bootstrapper?.Cancel();
} }
#region IBootstrapperDialog Methods #region IBootstrapperDialog Methods

View File

@ -102,7 +102,7 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
private void UiWindow_Closing(object sender, CancelEventArgs e) private void UiWindow_Closing(object sender, CancelEventArgs e)
{ {
if (!_isClosing) if (!_isClosing)
Bootstrapper?.CancelInstall(); Bootstrapper?.Cancel();
} }
#region IBootstrapperDialog Methods #region IBootstrapperDialog Methods

View File

@ -35,7 +35,7 @@ namespace Bloxstrap.UI.ViewModels.Bootstrapper
private void CancelInstall() private void CancelInstall()
{ {
_dialog.Bootstrapper?.CancelInstall(); _dialog.Bootstrapper?.Cancel();
_dialog.CloseBootstrapper(); _dialog.CloseBootstrapper();
} }
} }