From 437dfd5f022499502f35f9405a9288152dcca76d Mon Sep 17 00:00:00 2001 From: Archi Date: Wed, 20 Mar 2024 11:13:10 +0100 Subject: [PATCH] Allow forced plugin updates as well --- ArchiSteamFarm/Core/ASF.cs | 8 ++++---- .../Plugins/Interfaces/IGitHubPluginUpdates.cs | 8 ++++---- .../Plugins/Interfaces/IPluginUpdates.cs | 3 ++- ArchiSteamFarm/Plugins/PluginsCore.cs | 14 +++++++------- ArchiSteamFarm/Steam/Interaction/Actions.cs | 8 ++++---- ArchiSteamFarm/Steam/Interaction/Commands.cs | 8 +++++++- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/ArchiSteamFarm/Core/ASF.cs b/ArchiSteamFarm/Core/ASF.cs index f112e47a3..b86314270 100644 --- a/ArchiSteamFarm/Core/ASF.cs +++ b/ArchiSteamFarm/Core/ASF.cs @@ -202,7 +202,7 @@ public static class ASF { if (!updated) { // ASF wasn't updated as part of the process, update the plugins alone - updated = await PluginsCore.UpdatePlugins(SharedInfo.Version, false, updateChannel).ConfigureAwait(false); + updated = await PluginsCore.UpdatePlugins(SharedInfo.Version, false, updateChannel, forced).ConfigureAwait(false); } return (updated, newVersion); @@ -929,7 +929,7 @@ public static class ASF { await using (memoryStream.ConfigureAwait(false)) { using ZipArchive zipArchive = new(memoryStream); - if (!await UpdateFromArchive(newVersion, channel.Value, zipArchive).ConfigureAwait(false)) { + if (!await UpdateFromArchive(newVersion, channel.Value, forced, zipArchive).ConfigureAwait(false)) { ArchiLogger.LogGenericError(Strings.WarningFailed); } } @@ -965,7 +965,7 @@ public static class ASF { } } - private static async Task UpdateFromArchive(Version newVersion, GlobalConfig.EUpdateChannel updateChannel, ZipArchive zipArchive) { + private static async Task UpdateFromArchive(Version newVersion, GlobalConfig.EUpdateChannel updateChannel, bool forced, ZipArchive zipArchive) { ArgumentNullException.ThrowIfNull(newVersion); if (!Enum.IsDefined(updateChannel)) { @@ -986,7 +986,7 @@ public static class ASF { } // We're ready to start update process, handle any plugin updates ready for new version - await PluginsCore.UpdatePlugins(newVersion, true, updateChannel).ConfigureAwait(false); + await PluginsCore.UpdatePlugins(newVersion, true, updateChannel, forced).ConfigureAwait(false); return Utilities.UpdateFromArchive(zipArchive, SharedInfo.HomeDirectory); } diff --git a/ArchiSteamFarm/Plugins/Interfaces/IGitHubPluginUpdates.cs b/ArchiSteamFarm/Plugins/Interfaces/IGitHubPluginUpdates.cs index 99134f657..fe5a6befd 100644 --- a/ArchiSteamFarm/Plugins/Interfaces/IGitHubPluginUpdates.cs +++ b/ArchiSteamFarm/Plugins/Interfaces/IGitHubPluginUpdates.cs @@ -58,7 +58,7 @@ public interface IGitHubPluginUpdates : IPluginUpdates { /// JustArchiNET/ArchiSteamFarm string RepositoryName { get; } - Task IPluginUpdates.GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, GlobalConfig.EUpdateChannel updateChannel) { + Task IPluginUpdates.GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, GlobalConfig.EUpdateChannel updateChannel, bool forced) { ArgumentNullException.ThrowIfNull(asfVersion); ArgumentException.ThrowIfNullOrEmpty(asfVariant); @@ -66,7 +66,7 @@ public interface IGitHubPluginUpdates : IPluginUpdates { throw new InvalidEnumArgumentException(nameof(updateChannel), (int) updateChannel, typeof(GlobalConfig.EUpdateChannel)); } - return GetTargetReleaseURL(asfVersion, asfVariant, asfUpdate, updateChannel == GlobalConfig.EUpdateChannel.Stable); + return GetTargetReleaseURL(asfVersion, asfVariant, asfUpdate, updateChannel == GlobalConfig.EUpdateChannel.Stable, forced); } /// @@ -137,7 +137,7 @@ public interface IGitHubPluginUpdates : IPluginUpdates { return Task.FromResult(null); } - protected async Task GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, bool stable) { + protected async Task GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, bool stable, bool forced) { ArgumentNullException.ThrowIfNull(asfVersion); ArgumentException.ThrowIfNullOrEmpty(asfVariant); @@ -159,7 +159,7 @@ public interface IGitHubPluginUpdates : IPluginUpdates { Version newVersion = new(releaseResponse.Tag); - if (Version >= newVersion) { + if (!forced && (Version >= newVersion)) { ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginUpdateNotFound, Name, Version, newVersion)); return null; diff --git a/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs b/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs index 97e2dfe6b..62a932368 100644 --- a/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs +++ b/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs @@ -42,8 +42,9 @@ public interface IPluginUpdates : IPlugin { /// ASF variant of current instance, which may be useful if you're providing different versions for different ASF variants. /// Boolean value specifying whether ASF is being updated right now to given version. /// ASF update channel specified for this request. This might be different from the one specified in , as user might've specified other one for this request. + /// Boolean value specifying whether user has requested forced plugin update, that is, one that should always execute if possible - even with the same and lower version (downgrade). /// Target release asset URL that should be used for auto-update. It's permitted to return null if you want to skip update, e.g. because no new version is available. - Task GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, GlobalConfig.EUpdateChannel updateChannel); + Task GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, GlobalConfig.EUpdateChannel updateChannel, bool forced); /// /// ASF will call this method after update to the new plugin version has been finished, just before restart of the process. diff --git a/ArchiSteamFarm/Plugins/PluginsCore.cs b/ArchiSteamFarm/Plugins/PluginsCore.cs index a2d382508..c39ed739c 100644 --- a/ArchiSteamFarm/Plugins/PluginsCore.cs +++ b/ArchiSteamFarm/Plugins/PluginsCore.cs @@ -709,7 +709,7 @@ public static class PluginsCore { } } - internal static async Task UpdatePlugins(Version asfVersion, bool asfUpdate, GlobalConfig.EUpdateChannel? updateChannel = null) { + internal static async Task UpdatePlugins(Version asfVersion, bool asfUpdate, GlobalConfig.EUpdateChannel? updateChannel = null, bool forced = false) { ArgumentNullException.ThrowIfNull(asfVersion); if (updateChannel.HasValue && !Enum.IsDefined(updateChannel.Value)) { @@ -720,10 +720,10 @@ public static class PluginsCore { return false; } - return await UpdatePlugins(asfVersion, asfUpdate, ActivePluginUpdates, updateChannel).ConfigureAwait(false); + return await UpdatePlugins(asfVersion, asfUpdate, ActivePluginUpdates, updateChannel, forced).ConfigureAwait(false); } - internal static async Task UpdatePlugins(Version asfVersion, bool asfUpdate, IReadOnlyCollection plugins, GlobalConfig.EUpdateChannel? updateChannel = null) { + internal static async Task UpdatePlugins(Version asfVersion, bool asfUpdate, IReadOnlyCollection plugins, GlobalConfig.EUpdateChannel? updateChannel = null, bool forced = false) { ArgumentNullException.ThrowIfNull(asfVersion); if ((plugins == null) || (plugins.Count == 0)) { @@ -746,9 +746,9 @@ public static class PluginsCore { ASF.ArchiLogger.LogGenericInfo(Strings.PluginUpdatesChecking); - IList pluginUpdates = await Utilities.InParallel(plugins.Select(plugin => UpdatePlugin(asfVersion, asfUpdate, plugin, updateChannel.Value))).ConfigureAwait(false); + IList pluginUpdates = await Utilities.InParallel(plugins.Select(plugin => UpdatePlugin(asfVersion, asfUpdate, plugin, updateChannel.Value, forced))).ConfigureAwait(false); - return pluginUpdates.Any(static restartNeeded => restartNeeded); + return pluginUpdates.Any(static updated => updated); } [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode", Justification = "We don't care about trimmed assemblies, as we need it to work only with the known (used) ones")] @@ -794,7 +794,7 @@ public static class PluginsCore { } [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3000", Justification = "We don't care about trimmed assemblies, as we need it to work only with the known (used) ones")] - private static async Task UpdatePlugin(Version asfVersion, bool asfUpdate, IPluginUpdates plugin, GlobalConfig.EUpdateChannel updateChannel) { + private static async Task UpdatePlugin(Version asfVersion, bool asfUpdate, IPluginUpdates plugin, GlobalConfig.EUpdateChannel updateChannel, bool forced) { ArgumentNullException.ThrowIfNull(asfVersion); ArgumentNullException.ThrowIfNull(plugin); @@ -827,7 +827,7 @@ public static class PluginsCore { Directory.Delete(backupDirectory, true); } - Uri? releaseURL = await plugin.GetTargetReleaseURL(asfVersion, SharedInfo.BuildInfo.Variant, asfUpdate, updateChannel).ConfigureAwait(false); + Uri? releaseURL = await plugin.GetTargetReleaseURL(asfVersion, SharedInfo.BuildInfo.Variant, asfUpdate, updateChannel, forced).ConfigureAwait(false); if (releaseURL == null) { return false; diff --git a/ArchiSteamFarm/Steam/Interaction/Actions.cs b/ArchiSteamFarm/Steam/Interaction/Actions.cs index 3c75e0d2e..5a60544b0 100644 --- a/ArchiSteamFarm/Steam/Interaction/Actions.cs +++ b/ArchiSteamFarm/Steam/Interaction/Actions.cs @@ -487,7 +487,7 @@ public sealed class Actions : IAsyncDisposable, IDisposable { } [PublicAPI] - public static async Task<(bool Success, string? Message)> UpdatePlugins(IReadOnlyCollection plugins, GlobalConfig.EUpdateChannel? channel = null) { + public static async Task<(bool Success, string? Message)> UpdatePlugins(IReadOnlyCollection plugins, GlobalConfig.EUpdateChannel? channel = null, bool forced = false) { if ((plugins == null) || (plugins.Count == 0)) { throw new ArgumentNullException(nameof(plugins)); } @@ -504,13 +504,13 @@ public sealed class Actions : IAsyncDisposable, IDisposable { return (false, Strings.NothingFound); } - bool restartNeeded = await PluginsCore.UpdatePlugins(SharedInfo.Version, false, pluginsForUpdate, channel).ConfigureAwait(false); + bool updated = await PluginsCore.UpdatePlugins(SharedInfo.Version, false, pluginsForUpdate, channel, forced).ConfigureAwait(false); - if (restartNeeded) { + if (updated) { Utilities.InBackground(ASF.RestartOrExit); } - string message = restartNeeded ? Strings.UpdateFinished : Strings.NothingFound; + string message = updated ? Strings.UpdateFinished : Strings.NothingFound; return (true, message); } diff --git a/ArchiSteamFarm/Steam/Interaction/Commands.cs b/ArchiSteamFarm/Steam/Interaction/Commands.cs index 17936e69d..e6d068025 100644 --- a/ArchiSteamFarm/Steam/Interaction/Commands.cs +++ b/ArchiSteamFarm/Steam/Interaction/Commands.cs @@ -3184,9 +3184,15 @@ public sealed class Commands { return null; } + bool forced = false; GlobalConfig.EUpdateChannel? channel = null; if (!string.IsNullOrEmpty(channelText)) { + if (channelText.EndsWith('!')) { + forced = true; + channelText = channelText[..^1]; + } + if (!Enum.TryParse(channelText, true, out GlobalConfig.EUpdateChannel parsedChannel) || (parsedChannel == GlobalConfig.EUpdateChannel.None)) { return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(channelText))); } @@ -3200,7 +3206,7 @@ public sealed class Commands { return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(plugins))); } - (bool success, string? message) = await Actions.UpdatePlugins(plugins, channel).ConfigureAwait(false); + (bool success, string? message) = await Actions.UpdatePlugins(plugins, channel, forced).ConfigureAwait(false); return FormatStaticResponse($"{(success ? Strings.Success : Strings.WarningFailed)}{(!string.IsNullOrEmpty(message) ? $" {message}" : "")}"); }