From 0d1bc80114954c02dc55343ffaeea749c1d1be72 Mon Sep 17 00:00:00 2001 From: flustix Date: Sun, 8 Sep 2024 09:53:15 +0200 Subject: [PATCH] migrate updates to velopack --- fluXis.Desktop/FluXisGameDesktop.cs | 2 +- fluXis.Desktop/Program.cs | 3 + fluXis.Desktop/VelopackUpdatePerformer.cs | 61 +++++++ fluXis.Desktop/WindowsUpdatePerformer.cs | 167 ------------------ fluXis.Desktop/fluXis.Desktop.csproj | 1 + fluXis.Game/FluXisGameBase.cs | 23 +-- .../Settings/SettingsDebugStrings.cs | 4 - .../Overlay/Settings/Sections/DebugSection.cs | 14 -- .../Sections/General/GeneralUpdatesSection.cs | 11 +- fluXis.Game/Updater/GitHub/GitHubAsset.cs | 15 ++ fluXis.Game/Updater/GitHub/GitHubRelease.cs | 13 ++ fluXis.Game/Updater/IUpdatePerformer.cs | 17 +- 12 files changed, 106 insertions(+), 225 deletions(-) create mode 100644 fluXis.Desktop/VelopackUpdatePerformer.cs delete mode 100644 fluXis.Desktop/WindowsUpdatePerformer.cs create mode 100644 fluXis.Game/Updater/GitHub/GitHubAsset.cs diff --git a/fluXis.Desktop/FluXisGameDesktop.cs b/fluXis.Desktop/FluXisGameDesktop.cs index 2b6bb5cb..357a2ab3 100644 --- a/fluXis.Desktop/FluXisGameDesktop.cs +++ b/fluXis.Desktop/FluXisGameDesktop.cs @@ -58,5 +58,5 @@ protected override void Dispose(bool isDisposing) } public override LightController CreateLightController() => new OpenRGBController(); - public override IUpdatePerformer CreateUpdatePerformer() => OperatingSystem.IsWindows() ? new WindowsUpdatePerformer(NotificationManager) : null; + public override IUpdatePerformer CreateUpdatePerformer() => OperatingSystem.IsWindows() ? new VelopackUpdatePerformer(NotificationManager) : null; } diff --git a/fluXis.Desktop/Program.cs b/fluXis.Desktop/Program.cs index b5cb7fbc..f7d691f2 100644 --- a/fluXis.Desktop/Program.cs +++ b/fluXis.Desktop/Program.cs @@ -12,6 +12,7 @@ using osu.Framework; using osu.Framework.Localisation; using osu.Framework.Logging; +using Velopack; namespace fluXis.Desktop; @@ -21,6 +22,8 @@ public static class Program public static void Main(string[] args) { + VelopackApp.Build().Run(); + if (args.Contains("--generate-langfiles")) { generateDefaultLangfiles(); diff --git a/fluXis.Desktop/VelopackUpdatePerformer.cs b/fluXis.Desktop/VelopackUpdatePerformer.cs new file mode 100644 index 00000000..6718aa03 --- /dev/null +++ b/fluXis.Desktop/VelopackUpdatePerformer.cs @@ -0,0 +1,61 @@ +using fluXis.Game; +using fluXis.Game.Graphics.Sprites; +using fluXis.Game.Overlay.Notifications; +using fluXis.Game.Overlay.Notifications.Tasks; +using fluXis.Game.Updater; +using osu.Framework.Logging; +using Velopack; +using Velopack.Sources; + +namespace fluXis.Desktop; + +public partial class VelopackUpdatePerformer : IUpdatePerformer +{ + private NotificationManager notifications { get; } + private readonly Logger logger = Logger.GetLogger("update"); + + public VelopackUpdatePerformer(NotificationManager notifications) + { + this.notifications = notifications; + } + + public void Perform(bool silent, bool beta) + { + if (FluXisGameBase.IsDebug) + { + logger.Add("Skipping update in debug."); + return; + } + + logger.Add("Checking for updates..."); + var mgr = new UpdateManager(new GithubSource("https://github.com/TeamFluXis/fluXis", "", beta)); + + var update = mgr.CheckForUpdates(); + + if (update is null) + { + logger.Add("No update found."); + + if (!silent) + notifications.SendText("No updates available.", "You are running the latest version.", FontAwesome6.Solid.Check); + + return; + } + + var notification = new TaskNotificationData + { + Text = "New update available!", + TextWorking = "Downloading...", + TextFailed = "Failed! Check update.log for more information.", + TextFinished = "Done! Starting update..." + }; + + notifications.AddTask(notification); + logger.Add($"Downloading {update.TargetFullRelease.Version}..."); + mgr.DownloadUpdates(update, i => notification.Progress = i / 100f); + + logger.Add("Applying..."); + notification.State = LoadingState.Complete; + mgr.ApplyUpdatesAndRestart(update); + } +} diff --git a/fluXis.Desktop/WindowsUpdatePerformer.cs b/fluXis.Desktop/WindowsUpdatePerformer.cs deleted file mode 100644 index 322cd584..00000000 --- a/fluXis.Desktop/WindowsUpdatePerformer.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Runtime.Versioning; -using fluXis.Game.Overlay.Notifications; -using fluXis.Game.Overlay.Notifications.Tasks; -using fluXis.Game.Updater; -using osu.Framework.IO.Network; -using osu.Framework.Logging; - -namespace fluXis.Desktop; - -[SupportedOSPlatform("windows")] -public partial class WindowsUpdatePerformer : IUpdatePerformer -{ - private NotificationManager notifications { get; } - private readonly Logger logger = Logger.GetLogger("update"); - - private string folder => $"{AppContext.BaseDirectory}"; - private string patcher => @$"{folder}\patcher.exe"; - private string patches => @$"{folder}\patches"; - - public WindowsUpdatePerformer(NotificationManager notifications) - { - this.notifications = notifications; - } - - public void Perform(string version) - { - if (string.IsNullOrEmpty(version)) - return; - - if (!File.Exists(patcher)) - getPatcher(() => startUpdate(version)); - else - startUpdate(version); - } - - public void UpdateFromFile(FileInfo file) - { - if (!File.Exists(patcher)) - { - getPatcher(() => UpdateFromFile(file)); - return; - } - - try - { - // open the as zip and check if it contains a fluXis.exe - using var zip = ZipFile.OpenRead(file.FullName); - - if (zip.Entries.All(e => e.Name != "fluXis.exe")) - { - logger.Add("Invalid update file."); - notifications.SendError("Invalid update file."); - return; - } - - // the backslash to forward slash is absolutely needed here - // else the patcher thinks the closing quote of the folder - // is escaped since it ends in \ - Process.Start(new ProcessStartInfo - { - FileName = patcher, - UseShellExecute = true, - Arguments = $"\"{file.FullName.Replace("\\", "/")}\" \"{folder.Replace("\\", "/")}\" ", - WorkingDirectory = folder - }); - Environment.Exit(0); - } - catch (Exception e) - { - logger.Add("Failed to install update.", LogLevel.Error, e); - notifications.SendError("Failed to install update.", "Check update.log for more information."); - } - } - - private void startUpdate(string latest) - { - var notification = new TaskNotificationData - { - Text = "New update available!", - TextWorking = "Downloading...", - TextFailed = "Failed! Check update.log for more information.", - TextFinished = "Done! Starting update..." - }; - - notifications.AddTask(notification); - - try - { - var request = new WebRequest($"https://dl.flux.moe/fluXis/{latest}.zip"); - request.DownloadProgress += (currentBytes, totalBytes) => notification.Progress = (float)currentBytes / totalBytes; - request.Failed += e => - { - logger.Add($"Failed to download update. {e.Message}", LogLevel.Error, e); - notifications.SendError("Failed to download update.", "Check update.log for more information."); - notification.State = LoadingState.Failed; - }; - request.Finished += () => - { - logger.Add("Downloaded update. Starting update..."); - Directory.CreateDirectory(patches); - - var bytes = request.GetResponseData(); - using var stream = new MemoryStream(bytes); - - var path = patches + $"/{latest}.zip"; - File.WriteAllBytes(path, bytes); - - notification.State = LoadingState.Complete; - logger.Add("Update complete. Launching patcher..."); - UpdateFromFile(new FileInfo(path)); - }; - - request.Perform(); - } - catch (Exception e) - { - logger.Add($"Failed to download update. {e.Message}"); - notifications.SendError("Failed to download update.", "Check update.log for more information."); - notification.State = LoadingState.Failed; - } - } - - private async void getPatcher(Action callback) - { - var notification = new TaskNotificationData - { - Text = "Game patcher download", - TextWorking = "Downloading...", - TextFailed = "Failed! Check update.log for more information.", - TextFinished = "Done! Starting update...", - }; - - notifications.AddTask(notification); - - const string url = "https://dl.flux.moe/fluXis/patcher.exe"; - var request = new WebRequest(url); - request.DownloadProgress += (currentBytes, totalBytes) => notification.Progress = (float)currentBytes / totalBytes; - request.Failed += e => - { - logger.Add($"Failed to download patcher. {e.Message}"); - notification.State = LoadingState.Failed; - }; - request.Finished += () => - { - Directory.CreateDirectory(folder); - - var bytes = request.GetResponseData(); - using var stream = new MemoryStream(bytes); - File.WriteAllBytes(patcher, bytes); - - notification.State = LoadingState.Complete; - callback?.Invoke(); - }; - - try { await request.PerformAsync(); } - catch (Exception e) - { - logger.Add("Failed to download patcher.", LogLevel.Error, e); - notification.State = LoadingState.Failed; - } - } -} diff --git a/fluXis.Desktop/fluXis.Desktop.csproj b/fluXis.Desktop/fluXis.Desktop.csproj index c048c503..2aef60cd 100644 --- a/fluXis.Desktop/fluXis.Desktop.csproj +++ b/fluXis.Desktop/fluXis.Desktop.csproj @@ -19,6 +19,7 @@ + diff --git a/fluXis.Game/FluXisGameBase.cs b/fluXis.Game/FluXisGameBase.cs index e0520abe..ca66bc41 100644 --- a/fluXis.Game/FluXisGameBase.cs +++ b/fluXis.Game/FluXisGameBase.cs @@ -289,26 +289,15 @@ protected override void LoadComplete() }, true); } - public void PerformUpdateCheck(bool silent, bool forceUpdate = false) + public void PerformUpdateCheck(bool silent) => Task.Run(() => { - Task.Run(() => - { - var checker = new UpdateChecker(Config.Get(FluXisSetting.ReleaseChannel)); + var performer = CreateUpdatePerformer(); - if (forceUpdate || checker.UpdateAvailable) - { - var performer = CreateUpdatePerformer(); - var version = checker.LatestVersion; + if (performer is null) + return; - if (performer != null) - performer.Perform(version); - else - NotificationManager.SendText($"New update available! ({version})", "Check the github releases to download the latest version.", FontAwesome6.Solid.Download); - } - else if (!silent) - NotificationManager.SendText("No updates available.", "You are running the latest version.", FontAwesome6.Solid.Check); - }); - } + performer.Perform(silent, Config.Get(FluXisSetting.ReleaseChannel) == ReleaseChannel.Beta); + }); private Season getSeason() { diff --git a/fluXis.Game/Localization/Categories/Settings/SettingsDebugStrings.cs b/fluXis.Game/Localization/Categories/Settings/SettingsDebugStrings.cs index 5083c337..3a72307c 100644 --- a/fluXis.Game/Localization/Categories/Settings/SettingsDebugStrings.cs +++ b/fluXis.Game/Localization/Categories/Settings/SettingsDebugStrings.cs @@ -10,10 +10,6 @@ public class SettingsDebugStrings : LocalizationCategory public TranslatableString ShowLogOverlay => Get("show-log-overlay", "Show Log Overlay"); public TranslatableString ImportFile => Get("import-file", "Import File"); - public TranslatableString InstallUpdateFromFile => Get("update-from-file", "Install Update From File"); - - public TranslatableString InstallUpdateFromFileDescription => - Get("update-from-file-description", "Installs an update from a .zip file. Be careful from where you download the file from though!"); public TranslatableString LogAPI => Get("log-api", "Log API Responses."); public TranslatableString LogAPIDescription => Get("log-api-description", "Logs all API request responses to the console and log files. This might contain sensitive info like emails and tokens."); diff --git a/fluXis.Game/Overlay/Settings/Sections/DebugSection.cs b/fluXis.Game/Overlay/Settings/Sections/DebugSection.cs index 43d672d1..8a5025d8 100644 --- a/fluXis.Game/Overlay/Settings/Sections/DebugSection.cs +++ b/fluXis.Game/Overlay/Settings/Sections/DebugSection.cs @@ -42,20 +42,6 @@ private void load(FrameworkConfigManager frameworkConfig, FluXisConfig config, F }; } }, - new SettingsButton - { - Label = strings.InstallUpdateFromFile, - Description = strings.InstallUpdateFromFileDescription, - ButtonText = "Find", - Action = () => - { - panels.Content = new FileSelect - { - AllowedExtensions = new[] { ".zip" }, - OnFileSelected = file => game.CreateUpdatePerformer()?.UpdateFromFile(file) - }; - } - }, new SettingsToggle { Label = strings.LogAPI, diff --git a/fluXis.Game/Overlay/Settings/Sections/General/GeneralUpdatesSection.cs b/fluXis.Game/Overlay/Settings/Sections/General/GeneralUpdatesSection.cs index 89b4166a..b1ea01c7 100644 --- a/fluXis.Game/Overlay/Settings/Sections/General/GeneralUpdatesSection.cs +++ b/fluXis.Game/Overlay/Settings/Sections/General/GeneralUpdatesSection.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input; using osu.Framework.Localisation; namespace fluXis.Game.Overlay.Settings.Sections.General; @@ -19,8 +18,6 @@ public partial class GeneralUpdatesSection : SettingsSubSection private SettingsGeneralStrings strings => LocalizationStrings.Settings.General; - private InputManager inputManager; - [BackgroundDependencyLoader] private void load(FluXisGameBase game) { @@ -38,14 +35,8 @@ private void load(FluXisGameBase game) Label = strings.UpdatesCheck, Description = strings.UpdatesCheckDescription, ButtonText = "Check", - Action = () => game.PerformUpdateCheck(false, inputManager.CurrentState.Keyboard.AltPressed) + Action = () => game.PerformUpdateCheck(false) } }); } - - protected override void LoadComplete() - { - base.LoadComplete(); - inputManager = GetContainingInputManager(); - } } diff --git a/fluXis.Game/Updater/GitHub/GitHubAsset.cs b/fluXis.Game/Updater/GitHub/GitHubAsset.cs new file mode 100644 index 00000000..d2004a83 --- /dev/null +++ b/fluXis.Game/Updater/GitHub/GitHubAsset.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace fluXis.Game.Updater.GitHub; + +public class GitHubAsset +{ + [JsonProperty("url")] + public string Url { get; set; } = string.Empty; + + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } = string.Empty; +} diff --git a/fluXis.Game/Updater/GitHub/GitHubRelease.cs b/fluXis.Game/Updater/GitHub/GitHubRelease.cs index 0c5c5e9e..b38b849d 100644 --- a/fluXis.Game/Updater/GitHub/GitHubRelease.cs +++ b/fluXis.Game/Updater/GitHub/GitHubRelease.cs @@ -1,12 +1,25 @@ +using System; using Newtonsoft.Json; namespace fluXis.Game.Updater.GitHub; public class GitHubRelease { + [JsonProperty("name")] + public string Name { get; set; } = ""; + [JsonProperty("tag_name")] public string TagName { get; set; } = ""; [JsonProperty("prerelease")] public bool PreRelease { get; set; } + + [JsonProperty("draft")] + public bool Draft { get; set; } + + [JsonProperty("target_commitish")] + public string TargetCommitish { get; set; } + + [JsonProperty("assets")] + public GitHubAsset[] Assets { get; set; } = Array.Empty(); } diff --git a/fluXis.Game/Updater/IUpdatePerformer.cs b/fluXis.Game/Updater/IUpdatePerformer.cs index 4b21789a..cf9771aa 100644 --- a/fluXis.Game/Updater/IUpdatePerformer.cs +++ b/fluXis.Game/Updater/IUpdatePerformer.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace fluXis.Game.Updater; /// @@ -10,16 +8,11 @@ public interface IUpdatePerformer /// /// Performs an update. (If available) /// - /// - /// The version to update to. + /// + /// Whether to show a notification when there is no new update. /// - public void Perform(string version); - - /// - /// Installs an update from a file. - /// - /// - /// The file to install from. + /// + /// Whether to include beta versions. /// - public void UpdateFromFile(FileInfo file); + public void Perform(bool silent, bool beta); }