From 0ee89183cc28b3cd39e14c30a4ec83a353a9db9d Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Wed, 26 Jun 2024 15:25:41 -0400 Subject: [PATCH 01/23] initial implementation --- osu.Desktop/OsuGameDesktop.cs | 34 +++----- osu.Desktop/Program.cs | 39 +++------ ...lUpdateManager.cs => VeloUpdateManager.cs} | 86 ++++--------------- osu.Desktop/osu.Desktop.csproj | 2 +- 4 files changed, 38 insertions(+), 123 deletions(-) rename osu.Desktop/Updater/{SquirrelUpdateManager.cs => VeloUpdateManager.cs} (52%) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index e8783c997a..245d00dc53 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -19,7 +19,6 @@ using osu.Framework.Allocation; using osu.Game.IO; using osu.Game.IPC; -using osu.Game.Online.Multiplayer; using osu.Game.Performance; using osu.Game.Utils; using SDL; @@ -103,35 +102,22 @@ protected override UpdateManager CreateUpdateManager() if (!string.IsNullOrEmpty(packageManaged)) return new NoActionUpdateManager(); - switch (RuntimeInfo.OS) - { - case RuntimeInfo.Platform.Windows: - Debug.Assert(OperatingSystem.IsWindows()); - - return new SquirrelUpdateManager(); - - default: - return new SimpleUpdateManager(); - } + return new VeloUpdateManager(); } public override bool RestartAppWhenExited() { - switch (RuntimeInfo.OS) + try { - case RuntimeInfo.Platform.Windows: - Debug.Assert(OperatingSystem.IsWindows()); - - // Of note, this is an async method in squirrel that adds an arbitrary delay before returning - // likely to ensure the external process is in a good state. - // - // We're not waiting on that here, but the outro playing before the actual exit should be enough - // to cover this. - Squirrel.UpdateManager.RestartAppWhenExited().FireAndForget(); - return true; + Process.Start(Process.GetCurrentProcess().MainModule?.FileName ?? throw new InvalidOperationException()); + Environment.Exit(0); + return true; + } + catch (Exception e) + { + Logger.Error(e, "Failed to restart application"); + return base.RestartAppWhenExited(); } - - return base.RestartAppWhenExited(); } protected override void LoadComplete() diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 23e56cdce9..7c23c15d5a 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Runtime.Versioning; using osu.Desktop.LegacyIpc; using osu.Desktop.Windows; using osu.Framework; @@ -14,7 +13,7 @@ using osu.Game.IPC; using osu.Game.Tournament; using SDL; -using Squirrel; +using Velopack; namespace osu.Desktop { @@ -66,10 +65,10 @@ public static void Main(string[] args) return; } } - - setupSquirrel(); } + setupVelo(); + // NVIDIA profiles are based on the executable name of a process. // Lazer and stable share the same executable name. // Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup. @@ -177,32 +176,14 @@ private static bool trySendIPCMessage(IIpcHost host, string cwd, string[] args) return false; } - [SupportedOSPlatform("windows")] - private static void setupSquirrel() + private static void setupVelo() { - SquirrelAwareApp.HandleEvents(onInitialInstall: (_, tools) => - { - tools.CreateShortcutForThisExe(); - tools.CreateUninstallerRegistryEntry(); - WindowsAssociationManager.InstallAssociations(); - }, onAppUpdate: (_, tools) => - { - tools.CreateUninstallerRegistryEntry(); - WindowsAssociationManager.UpdateAssociations(); - }, onAppUninstall: (_, tools) => - { - tools.RemoveShortcutForThisExe(); - tools.RemoveUninstallerRegistryEntry(); - WindowsAssociationManager.UninstallAssociations(); - }, onEveryRun: (_, _, _) => - { - // While setting the `ProcessAppUserModelId` fixes duplicate icons/shortcuts on the taskbar, it currently - // causes the right-click context menu to function incorrectly. - // - // This may turn out to be non-required after an alternative solution is implemented. - // see https://github.com/clowd/Clowd.Squirrel/issues/24 - // tools.SetProcessAppUserModelId(); - }); + VelopackApp + .Build() + .WithFirstRun(v => + { + if (OperatingSystem.IsWindows()) WindowsAssociationManager.InstallAssociations(); + }).Run(); } } } diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/VeloUpdateManager.cs similarity index 52% rename from osu.Desktop/Updater/SquirrelUpdateManager.cs rename to osu.Desktop/Updater/VeloUpdateManager.cs index dba157a6e9..8fc68f77cd 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/VeloUpdateManager.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Runtime.Versioning; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Logging; @@ -10,30 +9,15 @@ using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Screens.Play; -using Squirrel.SimpleSplat; -using Squirrel.Sources; -using LogLevel = Squirrel.SimpleSplat.LogLevel; -using UpdateManager = osu.Game.Updater.UpdateManager; +using osu.Game.Updater; namespace osu.Desktop.Updater { - [SupportedOSPlatform("windows")] - public partial class SquirrelUpdateManager : UpdateManager + public partial class VeloUpdateManager : UpdateManager { - private Squirrel.UpdateManager? updateManager; + private Velopack.UpdateManager? updateManager; private INotificationOverlay notificationOverlay = null!; - public Task PrepareUpdateAsync() => Squirrel.UpdateManager.RestartAppWhenExited(); - - private static readonly Logger logger = Logger.GetLogger("updater"); - - /// - /// Whether an update has been downloaded but not yet applied. - /// - private bool updatePending; - - private readonly SquirrelLogger squirrelLogger = new SquirrelLogger(); - [Resolved] private OsuGameBase game { get; set; } = null!; @@ -44,13 +28,11 @@ public partial class SquirrelUpdateManager : UpdateManager private void load(INotificationOverlay notifications) { notificationOverlay = notifications; - - SquirrelLocator.CurrentMutable.Register(() => squirrelLogger, typeof(ILogger)); } protected override async Task PerformUpdateCheck() => await checkForUpdateAsync().ConfigureAwait(false); - private async Task checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification? notification = null) + private async Task checkForUpdateAsync(UpdateProgressNotification? notification = null) { // should we schedule a retry on completion of this check? bool scheduleRecheck = true; @@ -63,27 +45,27 @@ private async Task checkForUpdateAsync(bool useDeltaPatching = true, Updat if (localUserInfo?.IsPlaying.Value == true) return false; - updateManager ??= new Squirrel.UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), @"osulazer"); + updateManager ??= new Velopack.UpdateManager(new Velopack.Sources.GithubSource(@"https://github.com/ppy/osu", github_token, false)); - var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false); + var info = await updateManager.CheckForUpdatesAsync().ConfigureAwait(false); - if (info.ReleasesToApply.Count == 0) + if (info == null) { - if (updatePending) + // If there is an update pending restart, show the notification again. + if (updateManager.IsUpdatePendingRestart) { - // the user may have dismissed the completion notice, so show it again. notificationOverlay.Post(new UpdateApplicationCompleteNotification { Activated = () => { restartToApplyUpdate(); return true; - }, + } }); return true; } - // no updates available. bail and retry later. + // Otherwise there's no updates available. Bail and retry later. return false; } @@ -103,31 +85,17 @@ private async Task checkForUpdateAsync(bool useDeltaPatching = true, Updat try { - await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f).ConfigureAwait(false); + await updateManager.DownloadUpdatesAsync(info, p => notification.Progress = p / 100f).ConfigureAwait(false); notification.StartInstall(); - await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f).ConfigureAwait(false); - notification.State = ProgressNotificationState.Completed; - updatePending = true; } catch (Exception e) { - if (useDeltaPatching) - { - logger.Add(@"delta patching failed; will attempt full download!"); - - // could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) - // try again without deltas. - await checkForUpdateAsync(false, notification).ConfigureAwait(false); - } - else - { // In the case of an error, a separate notification will be displayed. notification.FailDownload(); Logger.Error(e, @"update failed!"); - } } } catch (Exception) @@ -149,32 +117,12 @@ private async Task checkForUpdateAsync(bool useDeltaPatching = true, Updat private bool restartToApplyUpdate() { - PrepareUpdateAsync() - .ContinueWith(_ => Schedule(() => game.AttemptExit())); + if (updateManager == null) + return false; + + updateManager.WaitExitThenApplyUpdates(null); + Schedule(() => game.AttemptExit()); return true; } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - updateManager?.Dispose(); - } - - private class SquirrelLogger : ILogger, IDisposable - { - public LogLevel Level { get; set; } = LogLevel.Info; - - public void Write(string message, LogLevel logLevel) - { - if (logLevel < Level) - return; - - logger.Add(message); - } - - public void Dispose() - { - } - } } } diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index e7a63bd921..7df82e1281 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -23,10 +23,10 @@ - + From 1025e5b3cc756b1cbd01216be157b3e0b270225f Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Wed, 26 Jun 2024 21:55:22 -0400 Subject: [PATCH 02/23] rewrite the restart function --- osu.Desktop/OsuGameDesktop.cs | 11 ++++++++--- .../Screens/Setup/TournamentSwitcher.cs | 3 +-- osu.Game/OsuGameBase.cs | 2 +- .../Settings/Sections/Graphics/RendererSettings.cs | 3 +-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 245d00dc53..3019aefdc0 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -105,18 +105,23 @@ protected override UpdateManager CreateUpdateManager() return new VeloUpdateManager(); } - public override bool RestartAppWhenExited() + public override bool RestartApp() { try { - Process.Start(Process.GetCurrentProcess().MainModule?.FileName ?? throw new InvalidOperationException()); + var startInfo = new ProcessStartInfo + { + FileName = Process.GetCurrentProcess().MainModule!.FileName, + UseShellExecute = true + }; + Process.Start(startInfo); Environment.Exit(0); return true; } catch (Exception e) { Logger.Error(e, "Failed to restart application"); - return base.RestartAppWhenExited(); + return base.RestartApp(); } } diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs index e55cbc2dbb..69ead451ba 100644 --- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -31,8 +31,7 @@ private void load(TournamentStorage storage) Action = () => { - game.RestartAppWhenExited(); - game.AttemptExit(); + game.RestartApp(); }; folderButton.Action = () => storage.PresentExternally(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5533ee8337..db603f0046 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -513,7 +513,7 @@ public virtual void AttemptExit() /// If supported by the platform, the game will automatically restart after the next exit. /// /// Whether a restart operation was queued. - public virtual bool RestartAppWhenExited() => false; + public virtual bool RestartApp() => false; public bool Migrate(string path) { diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index a8b127d522..17e345c2c8 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -67,9 +67,8 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi if (r.NewValue == RendererType.Automatic && automaticRendererInUse) return; - if (game?.RestartAppWhenExited() == true) + if (game?.RestartApp() == true) { - game.AttemptExit(); } else { From 36a3765ee4e2ef3240a265549ebc6a734f696c62 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 27 Jun 2024 12:57:24 -0400 Subject: [PATCH 03/23] Replace with attemptexit to better display how restarting is borked --- osu.Desktop/OsuGameDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b6053edf77..28f3d3dc5d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -114,7 +114,7 @@ public override bool RestartApp() UseShellExecute = true }; Process.Start(startInfo); - Environment.Exit(0); + base.AttemptExit(); return true; } catch (Exception e) From fae8f5f81b4d2de10fe1e2f2e58f0258158a794b Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 17:28:49 -0400 Subject: [PATCH 04/23] Refactor VeloUpdateManager --- osu.Desktop/Updater/VeloUpdateManager.cs | 49 +++++++++++------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/osu.Desktop/Updater/VeloUpdateManager.cs b/osu.Desktop/Updater/VeloUpdateManager.cs index 8fc68f77cd..137e48f135 100644 --- a/osu.Desktop/Updater/VeloUpdateManager.cs +++ b/osu.Desktop/Updater/VeloUpdateManager.cs @@ -10,12 +10,13 @@ using osu.Game.Overlays.Notifications; using osu.Game.Screens.Play; using osu.Game.Updater; +using Velopack.Sources; namespace osu.Desktop.Updater { public partial class VeloUpdateManager : UpdateManager { - private Velopack.UpdateManager? updateManager; + private readonly Velopack.UpdateManager updateManager; private INotificationOverlay notificationOverlay = null!; [Resolved] @@ -24,6 +25,12 @@ public partial class VeloUpdateManager : UpdateManager [Resolved] private ILocalUserPlayInfo? localUserInfo { get; set; } + public VeloUpdateManager() + { + const string? github_token = null; // TODO: populate. + updateManager = new Velopack.UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false)); + } + [BackgroundDependencyLoader] private void load(INotificationOverlay notifications) { @@ -37,36 +44,30 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific // should we schedule a retry on completion of this check? bool scheduleRecheck = true; - const string? github_token = null; // TODO: populate. - try { // Avoid any kind of update checking while gameplay is running. if (localUserInfo?.IsPlaying.Value == true) return false; - updateManager ??= new Velopack.UpdateManager(new Velopack.Sources.GithubSource(@"https://github.com/ppy/osu", github_token, false)); - var info = await updateManager.CheckForUpdatesAsync().ConfigureAwait(false); + // Handle no updates available. if (info == null) { - // If there is an update pending restart, show the notification again. - if (updateManager.IsUpdatePendingRestart) - { - notificationOverlay.Post(new UpdateApplicationCompleteNotification - { - Activated = () => - { - restartToApplyUpdate(); - return true; - } - }); - return true; - } + // If there's no updates pending restart, bail and retry later. + if (!updateManager.IsUpdatePendingRestart) return false; - // Otherwise there's no updates available. Bail and retry later. - return false; + // If there is an update pending restart, show the notification to restart again. + notificationOverlay.Post(new UpdateApplicationCompleteNotification + { + Activated = () => + { + restartToApplyUpdate(); + return true; + } + }); + return true; } scheduleRecheck = false; @@ -87,8 +88,6 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific { await updateManager.DownloadUpdatesAsync(info, p => notification.Progress = p / 100f).ConfigureAwait(false); - notification.StartInstall(); - notification.State = ProgressNotificationState.Completed; } catch (Exception e) @@ -98,10 +97,11 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific Logger.Error(e, @"update failed!"); } } - catch (Exception) + catch (Exception e) { // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. scheduleRecheck = true; + Logger.Error(e, @"update check failed!"); } finally { @@ -117,9 +117,6 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific private bool restartToApplyUpdate() { - if (updateManager == null) - return false; - updateManager.WaitExitThenApplyUpdates(null); Schedule(() => game.AttemptExit()); return true; From cae3607caf0d9322497c7be8c0bab4a9d2314cee Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 17:30:42 -0400 Subject: [PATCH 05/23] Fix up restarting Earlier I changed the restarting logic to not wait until the program exits and instead try to facilitate restarting alone. This did not work, and it became clear we'd need Velopack to do the restarting. This reverts back and supposedly brings restarting logic in line with how Velopack does it --- osu.Desktop/OsuGameDesktop.cs | 13 +++---------- .../Screens/Setup/TournamentSwitcher.cs | 3 ++- osu.Game/OsuGameBase.cs | 2 +- .../Settings/Sections/Graphics/RendererSettings.cs | 3 ++- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 28f3d3dc5d..c0c8d1e504 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.Versioning; @@ -104,23 +103,17 @@ protected override UpdateManager CreateUpdateManager() return new VeloUpdateManager(); } - public override bool RestartApp() + public override bool RestartAppWhenExited() { try { - var startInfo = new ProcessStartInfo - { - FileName = Process.GetCurrentProcess().MainModule!.FileName, - UseShellExecute = true - }; - Process.Start(startInfo); - base.AttemptExit(); + Velopack.UpdateExe.Start(null, true); return true; } catch (Exception e) { Logger.Error(e, "Failed to restart application"); - return base.RestartApp(); + return base.RestartAppWhenExited(); } } diff --git a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs index 69ead451ba..e55cbc2dbb 100644 --- a/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs +++ b/osu.Game.Tournament/Screens/Setup/TournamentSwitcher.cs @@ -31,7 +31,8 @@ private void load(TournamentStorage storage) Action = () => { - game.RestartApp(); + game.RestartAppWhenExited(); + game.AttemptExit(); }; folderButton.Action = () => storage.PresentExternally(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 573695613d..5e4ec5a61d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -513,7 +513,7 @@ public virtual void AttemptExit() /// If supported by the platform, the game will automatically restart after the next exit. /// /// Whether a restart operation was queued. - public virtual bool RestartApp() => false; + public virtual bool RestartAppWhenExited() => false; public bool Migrate(string path) { diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs index 17e345c2c8..a8b127d522 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/RendererSettings.cs @@ -67,8 +67,9 @@ private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDi if (r.NewValue == RendererType.Automatic && automaticRendererInUse) return; - if (game?.RestartApp() == true) + if (game?.RestartAppWhenExited() == true) { + game.AttemptExit(); } else { From c13f24d553a829b0d55ccdad733cee43d1509b5d Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 17:32:30 -0400 Subject: [PATCH 06/23] Remove InstallingUpdate progress notification Velopack won't install the updates while the program is open, it'll do it in between restarts or before starting. --- osu.Game/Localisation/NotificationsStrings.cs | 5 ----- osu.Game/Updater/UpdateManager.cs | 6 ------ 2 files changed, 11 deletions(-) diff --git a/osu.Game/Localisation/NotificationsStrings.cs b/osu.Game/Localisation/NotificationsStrings.cs index 698fe230b2..d8f768f2d8 100644 --- a/osu.Game/Localisation/NotificationsStrings.cs +++ b/osu.Game/Localisation/NotificationsStrings.cs @@ -135,11 +135,6 @@ public static class NotificationsStrings /// public static LocalisableString DownloadingUpdate => new TranslatableString(getKey(@"downloading_update"), @"Downloading update..."); - /// - /// "Installing update..." - /// - public static LocalisableString InstallingUpdate => new TranslatableString(getKey(@"installing_update"), @"Installing update..."); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index bcb28d8b14..c114e3a8d0 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -176,12 +176,6 @@ public void StartDownload() Text = NotificationsStrings.DownloadingUpdate; } - public void StartInstall() - { - Progress = 0; - Text = NotificationsStrings.InstallingUpdate; - } - public void FailDownload() { State = ProgressNotificationState.Cancelled; From 461b791532ee9abebebc615d2e23f5a38f7324c8 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 17:32:56 -0400 Subject: [PATCH 07/23] Remove SimpleUpdateManager No longer needed. Velopack supports the platforms that this covers for --- osu.Game/Updater/SimpleUpdateManager.cs | 116 ------------------------ 1 file changed, 116 deletions(-) delete mode 100644 osu.Game/Updater/SimpleUpdateManager.cs diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs deleted file mode 100644 index 0f9d5b929f..0000000000 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using osu.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Platform; -using osu.Game.Online.API; -using osu.Game.Overlays.Notifications; - -namespace osu.Game.Updater -{ - /// - /// An update manager that shows notifications if a newer release is detected. - /// Installation is left up to the user. - /// - public partial class SimpleUpdateManager : UpdateManager - { - private string version = null!; - - [Resolved] - private GameHost host { get; set; } = null!; - - [BackgroundDependencyLoader] - private void load(OsuGameBase game) - { - version = game.Version; - } - - protected override async Task PerformUpdateCheck() - { - try - { - var releases = new OsuJsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest"); - - await releases.PerformAsync().ConfigureAwait(false); - - var latest = releases.ResponseObject; - - // avoid any discrepancies due to build suffixes for now. - // eventually we will want to support release streams and consider these. - version = version.Split('-').First(); - string latestTagName = latest.TagName.Split('-').First(); - - if (latestTagName != version && tryGetBestUrl(latest, out string? url)) - { - Notifications.Post(new SimpleNotification - { - Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" - + "Click here to download the new version, which can be installed over the top of your existing installation", - Icon = FontAwesome.Solid.Download, - Activated = () => - { - host.OpenUrlExternally(url); - return true; - } - }); - - return true; - } - } - catch - { - // we shouldn't crash on a web failure. or any failure for the matter. - return true; - } - - return false; - } - - private bool tryGetBestUrl(GitHubRelease release, [NotNullWhen(true)] out string? url) - { - url = null; - GitHubAsset? bestAsset = null; - - switch (RuntimeInfo.OS) - { - case RuntimeInfo.Platform.Windows: - bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe", StringComparison.Ordinal)); - break; - - case RuntimeInfo.Platform.macOS: - string arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "Apple.Silicon" : "Intel"; - bestAsset = release.Assets?.Find(f => f.Name.EndsWith($".app.{arch}.zip", StringComparison.Ordinal)); - break; - - case RuntimeInfo.Platform.Linux: - bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".AppImage", StringComparison.Ordinal)); - break; - - case RuntimeInfo.Platform.iOS: - if (release.Assets?.Exists(f => f.Name.EndsWith(".ipa", StringComparison.Ordinal)) == true) - // iOS releases are available via testflight. this link seems to work well enough for now. - // see https://stackoverflow.com/a/32960501 - url = "itms-beta://beta.itunes.apple.com/v1/app/1447765923"; - - break; - - case RuntimeInfo.Platform.Android: - if (release.Assets?.Exists(f => f.Name.EndsWith(".apk", StringComparison.Ordinal)) == true) - // on our testing device using the .apk URL causes the download to magically disappear. - url = release.HtmlUrl; - - break; - } - - url ??= bestAsset?.BrowserDownloadUrl; - return url != null; - } - } -} From 9e01cf7fc23812c4d0a76b09dd78ce6b38c06028 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 17:33:05 -0400 Subject: [PATCH 08/23] Move setupVelo logic higher up --- osu.Desktop/Program.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 7c23c15d5a..92c8f2104c 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -30,19 +30,9 @@ public static class Program [STAThread] public static void Main(string[] args) { - /* - * WARNING: DO NOT PLACE **ANY** CODE ABOVE THE FOLLOWING BLOCK! - * - * Logic handling Squirrel MUST run before EVERYTHING if you do not want to break it. - * To be more precise: Squirrel is internally using a rather... crude method to determine whether it is running under NUnit, - * namely by checking loaded assemblies: - * https://github.com/clowd/Clowd.Squirrel/blob/24427217482deeeb9f2cacac555525edfc7bd9ac/src/Squirrel/SimpleSplat/PlatformModeDetector.cs#L17-L32 - * - * If it finds ANY assembly from the ones listed above - REGARDLESS of the reason why it is loaded - - * the app will then do completely broken things like: - * - not creating system shortcuts (as the logic is if'd out if "running tests") - * - not exiting after the install / first-update / uninstall hooks are ran (as the `Environment.Exit()` calls are if'd out if "running tests") - */ + // Velopack needs to run before anything else + setupVelo(); + if (OperatingSystem.IsWindows()) { var windowsVersion = Environment.OSVersion.Version; @@ -67,8 +57,6 @@ public static void Main(string[] args) } } - setupVelo(); - // NVIDIA profiles are based on the executable name of a process. // Lazer and stable share the same executable name. // Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup. From 6a0309294440dfd7136d2ceed72ca19e1be951eb Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 17:45:34 -0400 Subject: [PATCH 09/23] Reformat --- osu.Desktop/Updater/VeloUpdateManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Desktop/Updater/VeloUpdateManager.cs b/osu.Desktop/Updater/VeloUpdateManager.cs index 137e48f135..8aa580caa8 100644 --- a/osu.Desktop/Updater/VeloUpdateManager.cs +++ b/osu.Desktop/Updater/VeloUpdateManager.cs @@ -92,9 +92,9 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific } catch (Exception e) { - // In the case of an error, a separate notification will be displayed. - notification.FailDownload(); - Logger.Error(e, @"update failed!"); + // In the case of an error, a separate notification will be displayed. + notification.FailDownload(); + Logger.Error(e, @"update failed!"); } } catch (Exception e) From 72cf6bb12c71405464ce606a0334b2d6836c1bbc Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 18:00:45 -0400 Subject: [PATCH 10/23] Allow downgrading Also better address UpdateManager conflict --- osu.Desktop/Updater/VeloUpdateManager.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Desktop/Updater/VeloUpdateManager.cs b/osu.Desktop/Updater/VeloUpdateManager.cs index 8aa580caa8..6d3eb3f3f0 100644 --- a/osu.Desktop/Updater/VeloUpdateManager.cs +++ b/osu.Desktop/Updater/VeloUpdateManager.cs @@ -9,14 +9,15 @@ using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using osu.Game.Screens.Play; -using osu.Game.Updater; +using Velopack; using Velopack.Sources; +using UpdateManager = Velopack.UpdateManager; namespace osu.Desktop.Updater { - public partial class VeloUpdateManager : UpdateManager + public partial class VeloUpdateManager : Game.Updater.UpdateManager { - private readonly Velopack.UpdateManager updateManager; + private readonly UpdateManager updateManager; private INotificationOverlay notificationOverlay = null!; [Resolved] @@ -28,7 +29,10 @@ public partial class VeloUpdateManager : UpdateManager public VeloUpdateManager() { const string? github_token = null; // TODO: populate. - updateManager = new Velopack.UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false)); + updateManager = new UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), new UpdateOptions + { + AllowVersionDowngrade = true + }); } [BackgroundDependencyLoader] From 4898cff7a4186c3202cdef540c2480b48e22d55d Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Thu, 4 Jul 2024 18:25:02 -0400 Subject: [PATCH 11/23] Restart patch --- osu.Desktop/OsuGameDesktop.cs | 2 +- osu.Desktop/osu.Desktop.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index c0c8d1e504..ee73c84ba3 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -107,7 +107,7 @@ public override bool RestartAppWhenExited() { try { - Velopack.UpdateExe.Start(null, true); + Velopack.UpdateExe.Start(); return true; } catch (Exception e) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 7df82e1281..7a2bb599fd 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -26,7 +26,7 @@ - + From 71816c09dccf0a17932acb647e749190371e84a0 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Fri, 5 Jul 2024 03:29:09 -0400 Subject: [PATCH 12/23] Resurrect SimpleUpdateManager as MobileUpdateNotifier While removing the desktop specific logic from it --- osu.Android/OsuGameAndroid.cs | 2 +- osu.Game/Updater/MobileUpdateNotifier.cs | 102 +++++++++++++++++++++++ osu.iOS/OsuGameIOS.cs | 2 +- 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Updater/MobileUpdateNotifier.cs diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index a235913ef3..ffab7dd86d 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -80,7 +80,7 @@ public override void SetHost(GameHost host) host.Window.CursorState |= CursorState.Hidden; } - protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier(); protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo(); diff --git a/osu.Game/Updater/MobileUpdateNotifier.cs b/osu.Game/Updater/MobileUpdateNotifier.cs new file mode 100644 index 0000000000..04b54df3c0 --- /dev/null +++ b/osu.Game/Updater/MobileUpdateNotifier.cs @@ -0,0 +1,102 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Platform; +using osu.Game.Online.API; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Updater +{ + /// + /// An update manager that shows notifications if a newer release is detected for mobile platforms. + /// Installation is left up to the user. + /// + public partial class MobileUpdateNotifier : UpdateManager + { + private string version = null!; + + [Resolved] + private GameHost host { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load(OsuGameBase game) + { + version = game.Version; + } + + protected override async Task PerformUpdateCheck() + { + try + { + var releases = new OsuJsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest"); + + await releases.PerformAsync().ConfigureAwait(false); + + var latest = releases.ResponseObject; + + // avoid any discrepancies due to build suffixes for now. + // eventually we will want to support release streams and consider these. + version = version.Split('-').First(); + string latestTagName = latest.TagName.Split('-').First(); + + if (latestTagName != version && tryGetBestUrl(latest, out string? url)) + { + Notifications.Post(new SimpleNotification + { + Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n" + + "Click here to download the new version, which can be installed over the top of your existing installation", + Icon = FontAwesome.Solid.Download, + Activated = () => + { + host.OpenUrlExternally(url); + return true; + } + }); + + return true; + } + } + catch + { + // we shouldn't crash on a web failure. or any failure for the matter. + return true; + } + + return false; + } + + private bool tryGetBestUrl(GitHubRelease release, [NotNullWhen(true)] out string? url) + { + url = null; + GitHubAsset? bestAsset = null; + + switch (RuntimeInfo.OS) + { + case RuntimeInfo.Platform.iOS: + if (release.Assets?.Exists(f => f.Name.EndsWith(".ipa", StringComparison.Ordinal)) == true) + // iOS releases are available via testflight. this link seems to work well enough for now. + // see https://stackoverflow.com/a/32960501 + url = "itms-beta://beta.itunes.apple.com/v1/app/1447765923"; + + break; + + case RuntimeInfo.Platform.Android: + if (release.Assets?.Exists(f => f.Name.EndsWith(".apk", StringComparison.Ordinal)) == true) + // on our testing device using the .apk URL causes the download to magically disappear. + url = release.HtmlUrl; + + break; + } + + url ??= bestAsset?.BrowserDownloadUrl; + return url != null; + } + } +} diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 502f302157..2a4f9b87ac 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -15,7 +15,7 @@ public partial class OsuGameIOS : OsuGame { public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); - protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier(); protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo(); From fcede9abd786854bc0858fe90db2cbdf320a56ac Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Wed, 7 Aug 2024 03:34:07 -0400 Subject: [PATCH 13/23] Bump velopack version --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 7a2bb599fd..d86fcec396 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -26,7 +26,7 @@ - + From 636ee50eb9cbdc2d5d75de884b4be79ddd914e45 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 31 Aug 2024 23:03:10 +0900 Subject: [PATCH 14/23] Rename to VelopackUpdateManager --- osu.Desktop/OsuGameDesktop.cs | 2 +- .../{VeloUpdateManager.cs => VelopackUpdateManager.cs} | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) rename osu.Desktop/Updater/{VeloUpdateManager.cs => VelopackUpdateManager.cs} (96%) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index ee73c84ba3..94f6ef0fc3 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -100,7 +100,7 @@ protected override UpdateManager CreateUpdateManager() if (!string.IsNullOrEmpty(packageManaged)) return new NoActionUpdateManager(); - return new VeloUpdateManager(); + return new VelopackUpdateManager(); } public override bool RestartAppWhenExited() diff --git a/osu.Desktop/Updater/VeloUpdateManager.cs b/osu.Desktop/Updater/VelopackUpdateManager.cs similarity index 96% rename from osu.Desktop/Updater/VeloUpdateManager.cs rename to osu.Desktop/Updater/VelopackUpdateManager.cs index 6d3eb3f3f0..5cdc87e539 100644 --- a/osu.Desktop/Updater/VeloUpdateManager.cs +++ b/osu.Desktop/Updater/VelopackUpdateManager.cs @@ -11,11 +11,10 @@ using osu.Game.Screens.Play; using Velopack; using Velopack.Sources; -using UpdateManager = Velopack.UpdateManager; namespace osu.Desktop.Updater { - public partial class VeloUpdateManager : Game.Updater.UpdateManager + public partial class VelopackUpdateManager : Game.Updater.UpdateManager { private readonly UpdateManager updateManager; private INotificationOverlay notificationOverlay = null!; @@ -26,7 +25,7 @@ public partial class VeloUpdateManager : Game.Updater.UpdateManager [Resolved] private ILocalUserPlayInfo? localUserInfo { get; set; } - public VeloUpdateManager() + public VelopackUpdateManager() { const string? github_token = null; // TODO: populate. updateManager = new UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), new UpdateOptions From a038799c4745bf8f47189d13beec664f708785ac Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Sat, 31 Aug 2024 17:14:53 -0400 Subject: [PATCH 15/23] Update osu.Desktop.csproj bump version --- osu.Desktop/osu.Desktop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index ffa0c30b0c..3588317b8a 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -25,7 +25,7 @@ - + From b990af6adad67f239c31f3cc28ccfe657c9428d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2024 13:08:14 +0900 Subject: [PATCH 16/23] Use full name --- osu.Desktop/Program.cs | 4 ++-- osu.sln.DotSettings | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 92c8f2104c..609af2a8a7 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -31,7 +31,7 @@ public static class Program public static void Main(string[] args) { // Velopack needs to run before anything else - setupVelo(); + setupVelopack(); if (OperatingSystem.IsWindows()) { @@ -164,7 +164,7 @@ private static bool trySendIPCMessage(IIpcHost host, string cwd, string[] args) return false; } - private static void setupVelo() + private static void setupVelopack() { VelopackApp .Build() diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index a792b956dd..38686d8508 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -1060,5 +1060,6 @@ private void load() True True True + True True True From 42e1168b35072115839e266ada4baebb9cfb6915 Mon Sep 17 00:00:00 2001 From: smallketchup82 Date: Mon, 2 Sep 2024 01:23:05 -0400 Subject: [PATCH 17/23] Remove github token variable & pass null for the github token --- osu.Desktop/Updater/VelopackUpdateManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Desktop/Updater/VelopackUpdateManager.cs b/osu.Desktop/Updater/VelopackUpdateManager.cs index 5cdc87e539..eb1463483f 100644 --- a/osu.Desktop/Updater/VelopackUpdateManager.cs +++ b/osu.Desktop/Updater/VelopackUpdateManager.cs @@ -27,8 +27,7 @@ public partial class VelopackUpdateManager : Game.Updater.UpdateManager public VelopackUpdateManager() { - const string? github_token = null; // TODO: populate. - updateManager = new UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), new UpdateOptions + updateManager = new UpdateManager(new GithubSource(@"https://github.com/ppy/osu", null, false), new UpdateOptions { AllowVersionDowngrade = true }); From f8a6a6a8aef5db35a3d5cdf7aca50247d4cfedd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2024 16:43:46 +0900 Subject: [PATCH 18/23] Request restart asynchronously to avoid blocking update thread --- osu.Desktop/OsuGameDesktop.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 94f6ef0fc3..c75a3f0a1a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reflection; using System.Runtime.Versioning; +using System.Threading.Tasks; using Microsoft.Win32; using osu.Desktop.Performance; using osu.Desktop.Security; @@ -18,6 +19,7 @@ using osu.Framework.Allocation; using osu.Game.IO; using osu.Game.IPC; +using osu.Game.Online.Multiplayer; using osu.Game.Performance; using osu.Game.Utils; @@ -105,16 +107,8 @@ protected override UpdateManager CreateUpdateManager() public override bool RestartAppWhenExited() { - try - { - Velopack.UpdateExe.Start(); - return true; - } - catch (Exception e) - { - Logger.Error(e, "Failed to restart application"); - return base.RestartAppWhenExited(); - } + Task.Run(() => Velopack.UpdateExe.Start()).FireAndForget(); + return true; } protected override void LoadComplete() From 68e6fa286e00c0fef777d5d2fcfa49f503a5bd45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2024 16:46:29 +0900 Subject: [PATCH 19/23] Make comment about velopack's init a bit more loud --- osu.Desktop/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 609af2a8a7..5103663815 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -30,7 +30,9 @@ public static class Program [STAThread] public static void Main(string[] args) { - // Velopack needs to run before anything else + // IMPORTANT DON'T IGNORE: For general sanity, velopack's setup needs to run before anything else. + // This has bitten us in the rear before (bricked updater), and although the underlying issue from + // last time has been fixed, let's not tempt fate. setupVelopack(); if (OperatingSystem.IsWindows()) From cd9b82253e4639d3a94dc049244c9ccbcc59ea47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 2 Sep 2024 17:20:33 +0900 Subject: [PATCH 20/23] Pass through correct update to apply when calling `WaitExitThenApplyUpdates` --- osu.Desktop/Updater/VelopackUpdateManager.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Updater/VelopackUpdateManager.cs b/osu.Desktop/Updater/VelopackUpdateManager.cs index eb1463483f..4d2535ed32 100644 --- a/osu.Desktop/Updater/VelopackUpdateManager.cs +++ b/osu.Desktop/Updater/VelopackUpdateManager.cs @@ -52,7 +52,7 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific if (localUserInfo?.IsPlaying.Value == true) return false; - var info = await updateManager.CheckForUpdatesAsync().ConfigureAwait(false); + UpdateInfo? info = await updateManager.CheckForUpdatesAsync().ConfigureAwait(false); // Handle no updates available. if (info == null) @@ -65,7 +65,7 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific { Activated = () => { - restartToApplyUpdate(); + restartToApplyUpdate(null); return true; } }); @@ -78,7 +78,7 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific { notification = new UpdateProgressNotification { - CompletionClickAction = restartToApplyUpdate, + CompletionClickAction = () => restartToApplyUpdate(info), }; Schedule(() => notificationOverlay.Post(notification)); @@ -117,9 +117,9 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific return true; } - private bool restartToApplyUpdate() + private bool restartToApplyUpdate(UpdateInfo? info) { - updateManager.WaitExitThenApplyUpdates(null); + updateManager.WaitExitThenApplyUpdates(info?.TargetFullRelease); Schedule(() => game.AttemptExit()); return true; } From 08224b416e9664f264366a4280edd891d1439782 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2024 19:11:34 +0900 Subject: [PATCH 21/23] Simplify update process by caching pending update info and early-handling edge case --- osu.Desktop/Updater/VelopackUpdateManager.cs | 28 +++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Desktop/Updater/VelopackUpdateManager.cs b/osu.Desktop/Updater/VelopackUpdateManager.cs index 4d2535ed32..bf7122d720 100644 --- a/osu.Desktop/Updater/VelopackUpdateManager.cs +++ b/osu.Desktop/Updater/VelopackUpdateManager.cs @@ -25,11 +25,13 @@ public partial class VelopackUpdateManager : Game.Updater.UpdateManager [Resolved] private ILocalUserPlayInfo? localUserInfo { get; set; } + private UpdateInfo? pendingUpdate; + public VelopackUpdateManager() { updateManager = new UpdateManager(new GithubSource(@"https://github.com/ppy/osu", null, false), new UpdateOptions { - AllowVersionDowngrade = true + AllowVersionDowngrade = true, }); } @@ -52,33 +54,33 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific if (localUserInfo?.IsPlaying.Value == true) return false; - UpdateInfo? info = await updateManager.CheckForUpdatesAsync().ConfigureAwait(false); - - // Handle no updates available. - if (info == null) + if (pendingUpdate != null) { - // If there's no updates pending restart, bail and retry later. - if (!updateManager.IsUpdatePendingRestart) return false; - // If there is an update pending restart, show the notification to restart again. notificationOverlay.Post(new UpdateApplicationCompleteNotification { Activated = () => { - restartToApplyUpdate(null); + restartToApplyUpdate(); return true; } }); return true; } + pendingUpdate = await updateManager.CheckForUpdatesAsync().ConfigureAwait(false); + + // Handle no updates available. + if (pendingUpdate == null) + return false; + scheduleRecheck = false; if (notification == null) { notification = new UpdateProgressNotification { - CompletionClickAction = () => restartToApplyUpdate(info), + CompletionClickAction = restartToApplyUpdate, }; Schedule(() => notificationOverlay.Post(notification)); @@ -88,7 +90,7 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific try { - await updateManager.DownloadUpdatesAsync(info, p => notification.Progress = p / 100f).ConfigureAwait(false); + await updateManager.DownloadUpdatesAsync(pendingUpdate, p => notification.Progress = p / 100f).ConfigureAwait(false); notification.State = ProgressNotificationState.Completed; } @@ -117,9 +119,9 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific return true; } - private bool restartToApplyUpdate(UpdateInfo? info) + private bool restartToApplyUpdate() { - updateManager.WaitExitThenApplyUpdates(info?.TargetFullRelease); + updateManager.WaitExitThenApplyUpdates(pendingUpdate?.TargetFullRelease); Schedule(() => game.AttemptExit()); return true; } From b61023385a85a4508cd28b153d94fa233cc251a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 3 Sep 2024 19:16:10 +0900 Subject: [PATCH 22/23] Don't log probable network failures to sentry --- osu.Desktop/Updater/VelopackUpdateManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop/Updater/VelopackUpdateManager.cs b/osu.Desktop/Updater/VelopackUpdateManager.cs index bf7122d720..5b4d281f80 100644 --- a/osu.Desktop/Updater/VelopackUpdateManager.cs +++ b/osu.Desktop/Updater/VelopackUpdateManager.cs @@ -105,7 +105,7 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific { // we'll ignore this and retry later. can be triggered by no internet connection or thread abortion. scheduleRecheck = true; - Logger.Error(e, @"update check failed!"); + Logger.Log($@"update check failed ({e.Message})"); } finally { From e564e8c04895336559a16b093cbc905de8162b9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 4 Sep 2024 16:08:18 +0900 Subject: [PATCH 23/23] Add todo about fixing stutter on update application --- osu.Desktop/Updater/VelopackUpdateManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Desktop/Updater/VelopackUpdateManager.cs b/osu.Desktop/Updater/VelopackUpdateManager.cs index 5b4d281f80..527892413a 100644 --- a/osu.Desktop/Updater/VelopackUpdateManager.cs +++ b/osu.Desktop/Updater/VelopackUpdateManager.cs @@ -121,6 +121,8 @@ private async Task checkForUpdateAsync(UpdateProgressNotification? notific private bool restartToApplyUpdate() { + // TODO: Migrate this to async flow whenever available (see https://github.com/ppy/osu/pull/28743#discussion_r1740505665). + // Currently there's an internal Thread.Sleep(300) which will cause a stutter when the user clicks to restart. updateManager.WaitExitThenApplyUpdates(pendingUpdate?.TargetFullRelease); Schedule(() => game.AttemptExit()); return true;