diff --git a/ArchiSteamFarm/IPC/ArchiKestrel.cs b/ArchiSteamFarm/IPC/ArchiKestrel.cs index 69893e128..e8ff8cd4d 100644 --- a/ArchiSteamFarm/IPC/ArchiKestrel.cs +++ b/ArchiSteamFarm/IPC/ArchiKestrel.cs @@ -172,8 +172,7 @@ internal static class ArchiKestrel { string? assemblyDirectory = Path.GetDirectoryName(plugin.GetType().Assembly.Location); if (string.IsNullOrEmpty(assemblyDirectory)) { - // Invalid path provided - continue; + throw new InvalidOperationException(nameof(assemblyDirectory)); } physicalPath = Path.Combine(assemblyDirectory, plugin.PhysicalPath); diff --git a/ArchiSteamFarm/Localization/Strings.Designer.cs b/ArchiSteamFarm/Localization/Strings.Designer.cs index 84e20f77a..f4fbce26c 100644 --- a/ArchiSteamFarm/Localization/Strings.Designer.cs +++ b/ArchiSteamFarm/Localization/Strings.Designer.cs @@ -1238,5 +1238,11 @@ namespace ArchiSteamFarm.Localization { return ResourceManager.GetString("IdlingGameNotPossiblePrivate", resourceCulture); } } + + public static string WarningSkipping { + get { + return ResourceManager.GetString("WarningSkipping", resourceCulture); + } + } } } diff --git a/ArchiSteamFarm/Localization/Strings.resx b/ArchiSteamFarm/Localization/Strings.resx index 990837d33..be52d3d00 100644 --- a/ArchiSteamFarm/Localization/Strings.resx +++ b/ArchiSteamFarm/Localization/Strings.resx @@ -764,4 +764,8 @@ Process uptime: {1} Farming {0} ({1}) is disabled, as that game is currently marked as private. If you intend from ASF to farm that game, then consider changing its privacy settings. {0} will be replaced by game's ID (number), {1} will be replaced by game's name + + Skipping: {0}... + {0} will be replaced by text value (string) of entry being skipped. + diff --git a/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs b/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs index e5fbbafaa..fad89ad77 100644 --- a/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs +++ b/ArchiSteamFarm/Plugins/Interfaces/IPluginUpdates.cs @@ -47,16 +47,16 @@ public interface IPluginUpdates : IPlugin { Task GetTargetReleaseAsset(Version asfVersion, string asfVariant, Version newPluginVersion, IReadOnlyCollection releaseAssets); /// - /// ASF will call this method after update to a particular ASF version has been finished, just before restart of the process. + /// ASF will call this method after update to a particular plugin version has been finished, just before restart of the process. /// - /// The current (old) version of ASF program. - /// The target (new) version of ASF program. + /// The current (old) version of plugin assembly. + /// The target (new) version of plugin assembly. Task OnUpdateFinished(Version currentVersion, Version newVersion); /// - /// ASF will call this method before proceeding with an update to a particular ASF version. + /// ASF will call this method before proceeding with an update to a particular plugin version. /// - /// The current (old) version of ASF program. - /// The target (new) version of ASF program. + /// The current (old) version of plugin assembly. + /// The target (new) version of plugin assembly. Task OnUpdateProceeding(Version currentVersion, Version newVersion); } diff --git a/ArchiSteamFarm/Plugins/PluginsCore.cs b/ArchiSteamFarm/Plugins/PluginsCore.cs index 7c75a71c2..c21068e5e 100644 --- a/ArchiSteamFarm/Plugins/PluginsCore.cs +++ b/ArchiSteamFarm/Plugins/PluginsCore.cs @@ -673,69 +673,81 @@ public static class PluginsCore { // We update plugins one-by-one to limit memory pressure from potentially big release assets foreach (IPluginUpdates plugin in ActivePlugins.OfType()) { - string repoName = plugin.RepositoryName; - - if (string.IsNullOrEmpty(repoName)) { - continue; - } - - ASF.ArchiLogger.LogGenericInfo($"Checking update for {plugin.Name} plugin..."); - - ReleaseResponse? releaseResponse = await GitHub.GetLatestRelease(repoName, stable).ConfigureAwait(false); - - if (releaseResponse == null) { - continue; - } - - Version newVersion = new(releaseResponse.Tag); - - if (plugin.Version >= newVersion) { - ASF.ArchiLogger.LogGenericInfo($"No update available for {plugin.Name} plugin: {plugin.Version} >= {newVersion}."); - - continue; - } - - ASF.ArchiLogger.LogGenericInfo($"Updating {plugin.Name} plugin from version {plugin.Version} to {newVersion}..."); - - ReleaseAsset? asset = await plugin.GetTargetReleaseAsset(asfVersion, SharedInfo.BuildInfo.Variant, newVersion, releaseResponse.Assets).ConfigureAwait(false); - - if ((asset == null) || !releaseResponse.Assets.Contains(asset)) { - continue; - } - - Progress progressReporter = new(); - - progressReporter.ProgressChanged += Utilities.OnProgressChanged; - - BinaryResponse? response; - try { - response = await ASF.WebBrowser.UrlGetToBinary(asset.DownloadURL, progressReporter: progressReporter).ConfigureAwait(false); - } finally { - progressReporter.ProgressChanged -= Utilities.OnProgressChanged; - } + ASF.ArchiLogger.LogGenericInfo($"Checking update for {plugin.Name} plugin..."); - if (response?.Content == null) { - continue; - } + string? assemblyDirectory = Path.GetDirectoryName(plugin.GetType().Assembly.Location); - ASF.ArchiLogger.LogGenericInfo(Strings.PatchingFiles); + if (string.IsNullOrEmpty(assemblyDirectory)) { + throw new InvalidOperationException(nameof(assemblyDirectory)); + } - string? assemblyDirectory = Path.GetDirectoryName(plugin.GetType().Assembly.Location); + string backupDirectory = Path.Combine(assemblyDirectory, SharedInfo.UpdateDirectory); - if (string.IsNullOrEmpty(assemblyDirectory)) { - // Invalid path provided - continue; - } + if (Directory.Exists(backupDirectory)) { + ASF.ArchiLogger.LogGenericInfo(Strings.UpdateCleanup); - byte[] responseBytes = response.Content as byte[] ?? response.Content.ToArray(); + Directory.Delete(backupDirectory, true); + } + + string repoName = plugin.RepositoryName; + + if (string.IsNullOrEmpty(repoName)) { + ASF.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(plugin.RepositoryName))); + + continue; + } + + ReleaseResponse? releaseResponse = await GitHub.GetLatestRelease(repoName, stable).ConfigureAwait(false); + + if (releaseResponse == null) { + continue; + } + + Version pluginVersion = plugin.Version; + Version newVersion = new(releaseResponse.Tag); + + if (pluginVersion >= newVersion) { + ASF.ArchiLogger.LogGenericInfo($"No update available for {plugin.Name} plugin: {pluginVersion} >= {newVersion}."); + + continue; + } + + ASF.ArchiLogger.LogGenericInfo($"Updating {plugin.Name} plugin from version {pluginVersion} to {newVersion}..."); + + ReleaseAsset? asset = await plugin.GetTargetReleaseAsset(asfVersion, SharedInfo.BuildInfo.Variant, newVersion, releaseResponse.Assets).ConfigureAwait(false); + + if ((asset == null) || !releaseResponse.Assets.Contains(asset)) { + continue; + } + + Progress progressReporter = new(); + + progressReporter.ProgressChanged += Utilities.OnProgressChanged; + + BinaryResponse? response; + + try { + response = await ASF.WebBrowser.UrlGetToBinary(asset.DownloadURL, progressReporter: progressReporter).ConfigureAwait(false); + } finally { + progressReporter.ProgressChanged -= Utilities.OnProgressChanged; + } + + if (response?.Content == null) { + continue; + } + + ASF.ArchiLogger.LogGenericInfo(Strings.PatchingFiles); + + byte[] responseBytes = response.Content as byte[] ?? response.Content.ToArray(); - try { MemoryStream memoryStream = new(responseBytes); await using (memoryStream.ConfigureAwait(false)) { using ZipArchive zipArchive = new(memoryStream); + await plugin.OnUpdateProceeding(pluginVersion, newVersion).ConfigureAwait(false); + if (!Utilities.UpdateFromArchive(zipArchive, assemblyDirectory)) { ASF.ArchiLogger.LogGenericError(Strings.WarningFailed); @@ -746,6 +758,8 @@ public static class PluginsCore { restartNeeded = true; ASF.ArchiLogger.LogGenericInfo($"Updating {plugin.Name} plugin has succeeded, the changes will be loaded on the next ASF launch."); + + await plugin.OnUpdateFinished(pluginVersion, newVersion).ConfigureAwait(false); } catch (Exception e) { ASF.ArchiLogger.LogGenericException(e); } @@ -766,6 +780,14 @@ public static class PluginsCore { try { foreach (string assemblyPath in Directory.EnumerateFiles(path, "*.dll", SearchOption.AllDirectories)) { + string? assemblyDirectoryName = Path.GetFileName(Path.GetDirectoryName(assemblyPath)); + + if (assemblyDirectoryName == SharedInfo.UpdateDirectory) { + ASF.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningSkipping, assemblyPath)); + + continue; + } + Assembly assembly; try {