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\" },
};
public virtual string FinalDirectory { get; } = null!;
public string StagingDirectory => $"{FinalDirectory}.staging";
public virtual string ExecutableName { get; } = null!;
public virtual string Directory { get; } = null!;
public string ExecutablePath => Path.Combine(FinalDirectory, ExecutableName);
public string LockFilePath => Path.Combine(Directory, "Bloxstrap.lock");
public string ExecutablePath => Path.Combine(Directory, ExecutableName);
public virtual IReadOnlyDictionary<string, string> PackageDirectoryMap { get; set; }

View File

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

View File

@ -18,7 +18,7 @@ namespace Bloxstrap.AppData
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;

View File

@ -12,7 +12,7 @@
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;

View File

@ -47,6 +47,9 @@ namespace Bloxstrap
private double _progressIncrement;
private long _totalDownloadedBytes = 0;
private bool _mustUpgrade => File.Exists(AppData.LockFilePath) || !File.Exists(AppData.ExecutablePath);
private bool _skipUpgrade = false;
public IBootstrapperDialog? Dialog = null;
public bool IsStudioLaunch => _launchMode != LaunchMode.Player;
@ -162,8 +165,8 @@ namespace Bloxstrap
await GetLatestVersionInfo();
// 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)
await InstallLatestVersion();
if (!_skipUpgrade && (AppData.State.VersionGuid != _latestVersionGuid || _mustUpgrade))
await UpgradeRoblox();
//await ApplyModifications();
@ -284,7 +287,7 @@ namespace Bloxstrap
{
FileName = AppData.ExecutablePath,
Arguments = _launchCommandLine,
WorkingDirectory = AppData.FinalDirectory
WorkingDirectory = AppData.Directory
};
if (_launchMode == LaunchMode.StudioAuth)
@ -362,14 +365,17 @@ namespace Bloxstrap
{
using var ipl = new InterProcessLock("Watcher", TimeSpan.FromSeconds(5));
// TODO: look into if this needs to be launched *before* roblox starts
if (ipl.IsAcquired)
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)
{
@ -381,20 +387,23 @@ namespace Bloxstrap
if (_cancelTokenSource.IsCancellationRequested)
return;
App.Logger.WriteLine(LOG_IDENT, "Cancelling install...");
App.Logger.WriteLine(LOG_IDENT, "Cancelling launch...");
_cancelTokenSource.Cancel();
try
if (_isInstalling)
{
// clean up install
if (Directory.Exists(AppData.StagingDirectory))
Directory.Delete(AppData.StagingDirectory, true);
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not fully clean up installation!");
App.Logger.WriteException(LOG_IDENT, ex);
try
{
// clean up install
if (Directory.Exists(AppData.Directory))
Directory.Delete(AppData.Directory, true);
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, "Could not fully clean up installation!");
App.Logger.WriteException(LOG_IDENT, ex);
}
}
Dialog?.CloseBootstrapper();
@ -509,22 +518,46 @@ namespace Bloxstrap
#endregion
#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);
Directory.CreateDirectory(Paths.Base);
Directory.CreateDirectory(Paths.Downloads);
Directory.CreateDirectory(Paths.Roblox);
if (Directory.Exists(AppData.StagingDirectory))
Directory.Delete(AppData.StagingDirectory, true);
if (Directory.Exists(AppData.Directory))
{
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
// 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")
continue;
// extract the package immediately after download asynchronously
// discard is just used to suppress the warning
// extract the package async immediately after download
extractionTasks.Add(Task.Run(() => ExtractPackage(package), _cancelTokenSource.Token));
}
@ -586,11 +618,13 @@ namespace Bloxstrap
await Task.WhenAll(extractionTasks);
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)
return;
// only prompt on fresh install, since we don't want to be prompting them for every single launch
// TODO: state entry?
if (FreshInstall)
{
using var hklmKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}");
@ -612,7 +646,7 @@ namespace Bloxstrap
return;
}
string baseDirectory = Path.Combine(AppData.StagingDirectory, AppData.PackageDirectoryMap[package.Name]);
string baseDirectory = Path.Combine(AppData.Directory, AppData.PackageDirectoryMap[package.Name]);
ExtractPackage(package);
@ -681,37 +715,13 @@ namespace Bloxstrap
App.Logger.WriteLine(LOG_IDENT, $"Registered as {totalSize} KB");
App.State.Save();
lockFile.Delete();
if (Dialog is not null)
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;
}
@ -996,7 +1006,7 @@ namespace Bloxstrap
const string LOG_IDENT = "Bootstrapper::ExtractPackage";
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}...");

View File

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

View File

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

View File

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

View File

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

View File

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