From 1574ef7c33b4ea36d846c1f62c2a6f20e8d52795 Mon Sep 17 00:00:00 2001 From: Aireil <33433913+Aireil@users.noreply.github.com> Date: Sat, 23 Jul 2022 15:47:24 +0200 Subject: [PATCH] Rework plugin deletion + misc (#927) * fix: stuck overlay on errors * feat: delete old plugin versions * feat: ignore error tag when outdated, banned, or orphaned * feat: rework plugin deletion --- .../PluginInstaller/PluginInstallerWindow.cs | 73 ++++++++++++++----- Dalamud/Plugin/Internal/PluginManager.cs | 27 ++++++- Dalamud/Plugin/Internal/Types/LocalPlugin.cs | 11 +++ .../Internal/Types/LocalPluginManifest.cs | 5 ++ 4 files changed, 97 insertions(+), 19 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index ced291282..c8ec0a9ac 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1689,7 +1689,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller } // Load error - if (plugin.State is PluginState.LoadError or PluginState.DependencyResolutionFailed && plugin.CheckPolicy()) + if (plugin.State is PluginState.LoadError or PluginState.DependencyResolutionFailed && plugin.CheckPolicy() + && !plugin.IsOutdated && !plugin.IsBanned && !plugin.IsOrphaned) { label += Locs.PluginTitleMod_LoadError; trouble = true; @@ -1749,6 +1750,12 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller trouble = true; } + // Scheduled for deletion + if (plugin.Manifest.ScheduledForDeletion) + { + label += Locs.PluginTitleMod_ScheduledForDeletion; + } + ImGui.PushID($"installed{index}{plugin.Manifest.InternalName}"); var hasChangelog = !plugin.Manifest.Changelog.IsNullOrEmpty(); @@ -1959,7 +1966,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller unloadTask.Wait(); if (!unloadTask.Result) + { + this.enableDisableStatus = OperationStatus.Complete; return; + } var disableTask = Task.Run(() => plugin.Disable()) .ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_DisableFail(plugin.Name)); @@ -1985,7 +1995,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller enableTask.Wait(); if (!enableTask.Result) + { + this.enableDisableStatus = OperationStatus.Complete; return; + } var loadTask = Task.Run(() => plugin.LoadAsync(PluginLoadReason.Installer)) .ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_LoadFail(plugin.Name)); @@ -2152,8 +2165,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller var pluginManager = Service.Get(); + var devNotDeletable = plugin.IsDev && plugin.State != PluginState.Unloaded && plugin.State != PluginState.DependencyResolutionFailed; + ImGui.SameLine(); - if (plugin.State != PluginState.Unloaded && plugin.State != PluginState.LoadError) + if (plugin.State == PluginState.Loaded || devNotDeletable) { ImGui.PushFont(InterfaceManager.IconFont); ImGuiComponents.DisabledButton(FontAwesomeIcon.TrashAlt.ToIconString()); @@ -2161,18 +2176,9 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller if (ImGui.IsItemHovered()) { - ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePluginLoaded); - } - } - else if (plugin.HasEverStartedLoad && !plugin.IsDev) - { - ImGui.PushFont(InterfaceManager.IconFont); - ImGuiComponents.DisabledButton(FontAwesomeIcon.TrashAlt.ToIconString()); - ImGui.PopFont(); - - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePluginRestricted); + ImGui.SetTooltip(plugin.State == PluginState.Loaded + ? Locs.PluginButtonToolTip_DeletePluginLoaded + : Locs.PluginButtonToolTip_DeletePluginRestricted); } } else @@ -2181,8 +2187,19 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller { try { - plugin.DllFile.Delete(); - pluginManager.RemovePlugin(plugin); + if (plugin.IsDev) + { + plugin.DllFile.Delete(); + } + else + { + plugin.ScheduleDeletion(!plugin.Manifest.ScheduledForDeletion); + } + + if (plugin.State is PluginState.Unloaded or PluginState.DependencyResolutionFailed) + { + pluginManager.RemovePlugin(plugin); + } } catch (Exception ex) { @@ -2194,7 +2211,21 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller if (ImGui.IsItemHovered()) { - ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePlugin); + string tooltipMessage; + if (plugin.Manifest.ScheduledForDeletion) + { + tooltipMessage = Locs.PluginButtonToolTip_DeletePluginScheduledCancel; + } + else if (plugin.State is PluginState.Unloaded or PluginState.DependencyResolutionFailed) + { + tooltipMessage = Locs.PluginButtonToolTip_DeletePlugin; + } + else + { + tooltipMessage = Locs.PluginButtonToolTip_DeletePluginScheduled; + } + + ImGui.SetTooltip(tooltipMessage); } } } @@ -2530,9 +2561,11 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string PluginTitleMod_OutdatedError => Loc.Localize("InstallerOutdatedError", " (outdated)"); public static string PluginTitleMod_BannedError => Loc.Localize("InstallerBannedError", " (automatically disabled)"); - + public static string PluginTitleMod_OrphanedError => Loc.Localize("InstallerOrphanedError", " (unknown repository)"); + public static string PluginTitleMod_ScheduledForDeletion => Loc.Localize("InstallerScheduledForDeletion", " (scheduled for deletion)"); + public static string PluginTitleMod_New => Loc.Localize("InstallerNewPlugin ", " New!"); #endregion @@ -2608,6 +2641,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string PluginButtonToolTip_DeletePluginRestricted => Loc.Localize("InstallerDeletePluginRestricted", "Cannot delete right now - please restart the game."); + public static string PluginButtonToolTip_DeletePluginScheduled => Loc.Localize("InstallerDeletePluginScheduled", "Delete plugin on next restart"); + + public static string PluginButtonToolTip_DeletePluginScheduledCancel => Loc.Localize("InstallerDeletePluginScheduledCancel", "Cancel scheduled deletion"); + public static string PluginButtonToolTip_DeletePluginLoaded => Loc.Localize("InstallerDeletePluginLoaded", "Disable this plugin before deleting it."); public static string PluginButtonToolTip_VisitPluginUrl => Loc.Localize("InstallerVisitPluginUrl", "Visit plugin URL"); diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 92307cff1..8f8a2a963 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -848,10 +848,17 @@ internal partial class PluginManager : IDisposable, IServiceType } else { - foreach (var versionDir in versionDirs) + for (var i = 0; i < versionDirs.Length; i++) { + var versionDir = versionDirs[i]; try { + if (i != 0) + { + Log.Information($"Old version: cleaning up {versionDir.FullName}"); + versionDir.Delete(true); + continue; + } var dllFile = new FileInfo(Path.Combine(versionDir.FullName, $"{pluginDir.Name}.dll")); if (!dllFile.Exists) { @@ -865,6 +872,21 @@ internal partial class PluginManager : IDisposable, IServiceType { Log.Information($"Missing manifest: cleaning up {versionDir.FullName}"); versionDir.Delete(true); + continue; + } + + if (manifestFile.Length == 0) + { + Log.Information($"Manifest empty: cleaning up {versionDir.FullName}"); + versionDir.Delete(true); + continue; + } + + var manifest = LocalPluginManifest.Load(manifestFile); + if (manifest.ScheduledForDeletion) + { + Log.Information($"Scheduled deletion: cleaning up {versionDir.FullName}"); + versionDir.Delete(true); } } catch (Exception ex) @@ -902,6 +924,9 @@ internal partial class PluginManager : IDisposable, IServiceType if (plugin.InstalledPlugin.Manifest.Disabled && ignoreDisabled) continue; + if (plugin.InstalledPlugin.Manifest.ScheduledForDeletion) + continue; + var result = await this.UpdateSinglePluginAsync(plugin, false, dryRun); if (result != null) updatedList.Add(result); diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index f1647e217..d9e4719c9 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -560,6 +560,7 @@ internal class LocalPlugin : IDisposable throw new InvalidPluginOperationException($"Unable to enable {this.Name}, not disabled"); this.Manifest.Disabled = false; + this.Manifest.ScheduledForDeletion = false; this.SaveManifest(); } @@ -614,6 +615,16 @@ internal class LocalPlugin : IDisposable this.SaveManifest(); } + /// + /// Schedule the deletion of this plugin on next cleanup. + /// + /// Schedule or cancel the deletion. + public void ScheduleDeletion(bool status = true) + { + this.Manifest.ScheduledForDeletion = status; + this.SaveManifest(); + } + private static void SetupLoaderConfig(LoaderConfig config) { config.IsUnloadable = true; diff --git a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs index a3d89fd3b..634fbc75c 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPluginManifest.cs @@ -24,6 +24,11 @@ internal record LocalPluginManifest : PluginManifest /// public bool Testing { get; set; } + /// + /// Gets or sets a value indicating whether the plugin should be deleted during the next cleanup. + /// + public bool ScheduledForDeletion { get; set; } + /// /// Gets or sets the 3rd party repo URL that this plugin was installed from. Used to display where the plugin was /// sourced from on the installed plugin view. This should not be included in the plugin master. This value is null