diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 1d12288c1..48e0bd7ae 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -8,7 +8,7 @@ - 6.4.0.34 + 6.4.0.36 XIV Launcher addon framework $(DalamudVersion) $(DalamudVersion) diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index b19176d12..28031c2a4 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -210,7 +210,7 @@ namespace Dalamud.Interface.Internal /// Opens the . /// /// The data kind to switch to after opening. - public void OpenDataWindow(string dataKind = null) + public void OpenDataWindow(string? dataKind = null) { this.dataWindow.IsOpen = true; if (dataKind != null && this.dataWindow.IsOpen) diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index 4b0a30a10..16a682106 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -1724,7 +1724,6 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller } else if (plugin.State == PluginState.Loaded || plugin.State == PluginState.LoadError || plugin.State == PluginState.DependencyResolutionFailed) { - // TODO: We should draw the trash can button for policy-blocked plugins in safe mode, so plugins can be deleted if (pluginManager.SafeMode) { ImGuiComponents.DisabledButton(Locs.PluginButton_SafeMode); @@ -1932,8 +1931,10 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller private void DrawDeletePluginButton(LocalPlugin plugin) { - var unloaded = plugin.State == PluginState.Unloaded; - var showButton = unloaded && (plugin.IsDev || plugin.IsOutdated || plugin.IsBanned || plugin.IsOrphaned); + var unloaded = plugin.State == PluginState.Unloaded || plugin.State == PluginState.LoadError; + + // When policy check fails, the plugin is never loaded + var showButton = unloaded && (plugin.IsDev || plugin.IsOutdated || plugin.IsBanned || plugin.IsOrphaned || !plugin.CheckPolicy()); if (!showButton) return; @@ -1941,26 +1942,40 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller var pluginManager = Service.Get(); ImGui.SameLine(); - if (ImGuiComponents.IconButton(FontAwesomeIcon.TrashAlt)) + if (plugin.HasEverStartedLoad) { - try - { - plugin.DllFile.Delete(); - pluginManager.RemovePlugin(plugin); - } - catch (Exception ex) - { - Log.Error(ex, $"Plugin installer threw an error during removal of {plugin.Name}"); + ImGui.PushFont(InterfaceManager.IconFont); + ImGuiComponents.DisabledButton(FontAwesomeIcon.TrashAlt.ToIconString()); + ImGui.PopFont(); - this.errorModalMessage = Locs.ErrorModal_DeleteFail(plugin.Name); - this.errorModalDrawing = true; - this.errorModalOnNextFrame = true; + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePluginRestricted); } } - - if (ImGui.IsItemHovered()) + else { - ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePlugin); + if (ImGuiComponents.IconButton(FontAwesomeIcon.TrashAlt)) + { + try + { + plugin.DllFile.Delete(); + pluginManager.RemovePlugin(plugin); + } + catch (Exception ex) + { + Log.Error(ex, $"Plugin installer threw an error during removal of {plugin.Name}"); + + this.errorModalMessage = Locs.ErrorModal_DeleteFail(plugin.Name); + this.errorModalDrawing = true; + this.errorModalOnNextFrame = true; + } + } + + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(Locs.PluginButtonToolTip_DeletePlugin); + } } } @@ -2369,6 +2384,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string PluginButtonToolTip_DeletePlugin => Loc.Localize("InstallerDeletePlugin ", "Delete plugin"); + public static string PluginButtonToolTip_DeletePluginRestricted => Loc.Localize("InstallerDeletePluginRestricted", "Cannot delete - please try restarting the game."); + public static string PluginButtonToolTip_VisitPluginUrl => Loc.Localize("InstallerVisitPluginUrl", "Visit plugin URL"); public static string PluginButtonToolTip_UpdateSingle(string version) => Loc.Localize("InstallerUpdateSingle", "Update to {0}").Format(version); diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 29138b3ed..c8a18cc3f 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -48,7 +48,7 @@ internal partial class PluginManager : IDisposable, IServiceType private readonly object pluginListLock = new(); private readonly DirectoryInfo pluginDirectory; private readonly DirectoryInfo devPluginDirectory; - private readonly BannedPlugin[] bannedPlugins; + private readonly BannedPlugin[]? bannedPlugins; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -78,7 +78,11 @@ internal partial class PluginManager : IDisposable, IServiceType this.PluginConfigs = new PluginConfigurations(Path.Combine(Path.GetDirectoryName(this.startInfo.ConfigurationPath) ?? string.Empty, "pluginConfigs")); var bannedPluginsJson = File.ReadAllText(Path.Combine(this.startInfo.AssetDirectory!, "UIRes", "bannedplugin.json")); - this.bannedPlugins = JsonConvert.DeserializeObject(bannedPluginsJson) ?? Array.Empty(); + this.bannedPlugins = JsonConvert.DeserializeObject(bannedPluginsJson); + if (this.bannedPlugins == null) + { + throw new InvalidDataException("Couldn't deserialize banned plugins manifest."); + } this.ApplyPatches(); } @@ -771,8 +775,8 @@ internal partial class PluginManager : IDisposable, IServiceType /// Plugin to remove. public void RemovePlugin(LocalPlugin plugin) { - if (plugin.State != PluginState.Unloaded) - throw new InvalidPluginOperationException($"Unable to remove {plugin.Name}, not unloaded"); + if (plugin.State != PluginState.Unloaded && plugin.HasEverStartedLoad) + throw new InvalidPluginOperationException($"Unable to remove {plugin.Name}, not unloaded and had loaded before"); lock (this.pluginListLock) { @@ -846,7 +850,7 @@ internal partial class PluginManager : IDisposable, IServiceType continue; } - if (manifest.DalamudApiLevel < DalamudApiLevel - 1 && !configuration.LoadAllApiLevels) + if (manifest.DalamudApiLevel < DalamudApiLevel - 1 && !this.configuration.LoadAllApiLevels) { Log.Information($"Lower API: cleaning up {versionDir.FullName}"); versionDir.Delete(true); @@ -1060,6 +1064,8 @@ internal partial class PluginManager : IDisposable, IServiceType /// A value indicating whether the plugin/manifest has been banned. public bool IsManifestBanned(PluginManifest manifest) { + Debug.Assert(this.bannedPlugins != null, "this.bannedPlugins != null"); + return !this.configuration.LoadBannedPlugins && this.bannedPlugins.Any(ban => (ban.Name == manifest.InternalName || ban.Name == Hash.GetStringSha256Hash(manifest.InternalName)) && ban.AssemblyVersion >= manifest.AssemblyVersion); } @@ -1071,6 +1077,8 @@ internal partial class PluginManager : IDisposable, IServiceType /// The reason of the ban, if any. public string GetBanReason(PluginManifest manifest) { + Debug.Assert(this.bannedPlugins != null, "this.bannedPlugins != null"); + return this.bannedPlugins.LastOrDefault(ban => ban.Name == manifest.InternalName).Reason; } diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 32343feff..bb5b3d42d 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -189,6 +189,11 @@ internal class LocalPlugin : IDisposable /// public string BanReason { get; } + /// + /// Gets a value indicating whether the plugin has ever started to load. + /// + public bool HasEverStartedLoad { get; private set; } + /// /// Gets a value indicating whether the plugin is loaded and running. /// @@ -335,6 +340,8 @@ internal class LocalPlugin : IDisposable "Please refer to https://github.com/goatcorp/Dalamud/discussions/603 for more information."); } + this.HasEverStartedLoad = true; + this.loader ??= PluginLoader.CreateFromAssemblyFile(this.DllFile.FullName, SetupLoaderConfig); if (reloading || this.IsDev)