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 {