From f311429bc2db14f4327b0170674737ea6899924a Mon Sep 17 00:00:00 2001 From: Matt Sijbers Date: Wed, 20 May 2026 22:07:03 +0100 Subject: [PATCH] launcher: in-place self-update via download + swap + restart Adds Services/SelfUpdateService.cs implementing the Chrome-style pattern: 1. Download new exe to ".new" 2. Rename running exe to ".old" (NTFS allows MoveFile on a running exe -- it resolves processes by handle, not by path) 3. Rename ".new" to canonical path 4. Spawn the new exe with our argv 5. Environment.Exit(0) 6. Next start of the new exe runs CleanupAfterUpdate() in App.OFIC() which deletes the leftover ".old" UI: the existing "Download" banner button is now "Install update". Click runs the self-update flow with a percent/MB progress label; any failure (network, AV write-block, dir not writable) falls back to opening the URL in the browser so users always have a path forward. Bumps to 0.4.7. Also fixes a stale fallback URL that pointed at sijbers.uk/pack/... -- now correctly points at bns.sijbers.uk/launcher/. --- launcher/App.axaml.cs | 6 ++ launcher/MainWindow.axaml | 2 +- launcher/MainWindow.axaml.cs | 47 +++++++-- launcher/ModpackLauncher.csproj | 2 +- launcher/Services/SelfUpdateService.cs | 138 +++++++++++++++++++++++++ 5 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 launcher/Services/SelfUpdateService.cs diff --git a/launcher/App.axaml.cs b/launcher/App.axaml.cs index 8372e5e..459506b 100644 --- a/launcher/App.axaml.cs +++ b/launcher/App.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; +using ModpackLauncher.Services; namespace ModpackLauncher; @@ -13,6 +14,11 @@ public partial class App : Application public override void OnFrameworkInitializationCompleted() { + // Clean up any .old launcher left behind by a self-update on a prior run. + // Best-effort: if the previous process hasn't fully released the handle yet + // we just retry on the next startup. + SelfUpdateService.CleanupAfterUpdate(); + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow(); diff --git a/launcher/MainWindow.axaml b/launcher/MainWindow.axaml index c5dbf52..edd63e6 100644 --- a/launcher/MainWindow.axaml +++ b/launcher/MainWindow.axaml @@ -259,7 +259,7 @@ Foreground="#E8DFC8" FontSize="13" TextWrapping="Wrap" Text="A newer launcher is available." />