diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs index 381598b08..2ef37e4b0 100644 --- a/Dalamud.Injector/EntryPoint.cs +++ b/Dalamud.Injector/EntryPoint.cs @@ -91,6 +91,8 @@ namespace Dalamud.Injector args.Remove("--etw"); args.Remove("--veh"); args.Remove("--veh-full"); + args.Remove("--no-plugin"); + args.Remove("--no-3rd-plugin"); var mainCommand = args[1].ToLowerInvariant(); if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand) @@ -322,6 +324,8 @@ namespace Dalamud.Injector startInfo.BootWaitMessageBox |= args.Contains("--msgbox3") ? 4 : 0; startInfo.BootVehEnabled = args.Contains("--veh"); startInfo.BootVehFull = args.Contains("--veh-full"); + startInfo.NoLoadPlugins = args.Contains("--no-plugin"); + startInfo.NoLoadThirdPartyPlugins = args.Contains("--no-third-plugin"); // startInfo.BootUnhookDlls = new List() { "kernel32.dll", "ntdll.dll", "user32.dll" }; return startInfo; @@ -362,6 +366,7 @@ namespace Dalamud.Injector Console.WriteLine("Enable ETW:\t[--etw]"); Console.WriteLine("Enable VEH:\t[--veh], [--veh-full]"); Console.WriteLine("Show messagebox:\t[--msgbox1], [--msgbox2], [--msgbox3]"); + Console.WriteLine("No plugins:\t[--no-plugin] [--no-3rd-plugin]"); return 0; } diff --git a/Dalamud/DalamudStartInfo.cs b/Dalamud/DalamudStartInfo.cs index 904b245fd..2e4edbf9b 100644 --- a/Dalamud/DalamudStartInfo.cs +++ b/Dalamud/DalamudStartInfo.cs @@ -34,6 +34,8 @@ namespace Dalamud this.Language = other.Language; this.GameVersion = other.GameVersion; this.DelayInitializeMs = other.DelayInitializeMs; + this.NoLoadPlugins = other.NoLoadPlugins; + this.NoLoadThirdPartyPlugins = other.NoLoadThirdPartyPlugins; this.BootLogPath = other.BootLogPath; this.BootShowConsole = other.BootShowConsole; this.BootDisableFallbackConsole = other.BootDisableFallbackConsole; @@ -88,6 +90,16 @@ namespace Dalamud /// public int DelayInitializeMs { get; set; } + /// + /// Gets or sets a value indicating whether no plugins should be loaded. + /// + public bool NoLoadPlugins { get; set; } + + /// + /// Gets or sets a value indicating whether no third-party plugins should be loaded. + /// + public bool NoLoadThirdPartyPlugins { get; set; } + /// /// Gets or sets the path the boot log file is supposed to be written to. /// diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index ffb05e170..4b0a30a10 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -317,7 +317,11 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller var ready = pluginManager.PluginsReady && pluginManager.ReposReady; - if (!ready || this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress) + if (pluginManager.SafeMode) + { + ImGuiComponents.DisabledButton(Locs.FooterButton_UpdateSafeMode); + } + else if (!ready || this.updateStatus == OperationStatus.InProgress || this.installStatus == OperationStatus.InProgress) { ImGuiComponents.DisabledButton(Locs.FooterButton_UpdatePlugins); } @@ -1039,12 +1043,6 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller { var pluginManager = Service.Get(); - if (pluginManager.SafeMode) - { - ImGui.Text(Locs.TabBody_SafeMode); - return false; - } - var ready = pluginManager.PluginsReady && pluginManager.ReposReady; if (!ready) @@ -1186,6 +1184,12 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller ImGui.TextWrapped(Locs.PluginBody_Orphaned); ImGui.PopStyleColor(); } + else if (plugin != null && !plugin.CheckPolicy()) + { + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + ImGui.TextWrapped(Locs.PluginBody_Policy); + ImGui.PopStyleColor(); + } else if (plugin is { State: PluginState.LoadError or PluginState.DependencyResolutionFailed }) // Load failed warning { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); @@ -1339,7 +1343,11 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller ? $"{manifest.TestingAssemblyVersion}" : $"{manifest.AssemblyVersion}"; - if (disabled) + if (pluginManager.SafeMode) + { + ImGuiComponents.DisabledButton(Locs.PluginButton_SafeMode); + } + else if (disabled) { ImGuiComponents.DisabledButton(Locs.PluginButton_InstallVersion(versionString)); } @@ -1463,14 +1471,14 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller } // Disabled - if (plugin.IsDisabled) + if (plugin.IsDisabled || !plugin.CheckPolicy()) { label += Locs.PluginTitleMod_Disabled; trouble = true; } // Load error - if (plugin.State is PluginState.LoadError or PluginState.DependencyResolutionFailed) + if (plugin.State is PluginState.LoadError or PluginState.DependencyResolutionFailed && plugin.CheckPolicy()) { label += Locs.PluginTitleMod_LoadError; trouble = true; @@ -1716,7 +1724,12 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller } else if (plugin.State == PluginState.Loaded || plugin.State == PluginState.LoadError || plugin.State == PluginState.DependencyResolutionFailed) { - if (disabled) + // 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); + } + else if (disabled) { ImGuiComponents.DisabledButton(Locs.PluginButton_Disable); } @@ -2323,6 +2336,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string PluginBody_Banned => Loc.Localize("InstallerBannedPluginBody ", "This plugin was automatically disabled due to incompatibilities and is not available at the moment. Please wait for it to be updated by its author."); + public static string PluginBody_Policy => Loc.Localize("InstallerPolicyPluginBody ", "Plugin loads for this type of plugin were manually disabled."); + public static string PluginBody_BannedReason(string message) => Loc.Localize("InstallerBannedPluginBodyReason ", "This plugin was automatically disabled: {0}").Format(message); @@ -2340,6 +2355,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string PluginButton_Unload => Loc.Localize("InstallerUnload", "Unload"); + public static string PluginButton_SafeMode => Loc.Localize("InstallerSafeModeButton", "Can't change in safe mode"); + #endregion #region Plugin button tooltips @@ -2388,6 +2405,8 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller public static string FooterButton_UpdatePlugins => Loc.Localize("InstallerUpdatePlugins", "Update plugins"); + public static string FooterButton_UpdateSafeMode => Loc.Localize("InstallerUpdateSafeMode", "Can't update in safe mode"); + public static string FooterButton_InProgress => Loc.Localize("InstallerInProgress", "Install in progress..."); public static string FooterButton_NoUpdates => Loc.Localize("InstallerNoUpdates", "No updates found!"); diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 533b2acd9..29138b3ed 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -68,7 +68,7 @@ internal partial class PluginManager : IDisposable, IServiceType if (!this.devPluginDirectory.Exists) this.devPluginDirectory.Create(); - this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode; + this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode || this.startInfo.NoLoadPlugins; if (this.SafeMode) { this.configuration.PluginSafeMode = false; @@ -281,12 +281,6 @@ internal partial class PluginManager : IDisposable, IServiceType /// public void LoadAllPlugins() { - if (this.SafeMode) - { - Log.Information("PluginSafeMode was enabled, not loading any plugins."); - return; - } - var pluginDefs = new List(); var devPluginDefs = new List(); @@ -504,12 +498,6 @@ internal partial class PluginManager : IDisposable, IServiceType /// public void ScanDevPlugins() { - if (this.SafeMode) - { - Log.Information("PluginSafeMode was enabled, not scanning any dev plugins."); - return; - } - if (!this.devPluginDirectory.Exists) this.devPluginDirectory.Create(); @@ -753,6 +741,14 @@ internal partial class PluginManager : IDisposable, IServiceType // NOTE(goat): This can't work - plugins don't "unload" if they fail to load. // plugin.Disable(); // Disable here, otherwise you can't enable+load later } + else if (!plugin.CheckPolicy()) + { + // During boot load, plugins always get added to the list so they can be fiddled with in the UI + Log.Information(ex, $"Plugin not loaded due to policy, adding anyways: {dllFile.Name}"); + + // NOTE(goat): This can't work - plugins don't "unload" if they fail to load. + // plugin.Disable(); // Disable here, otherwise you can't enable+load later + } else { PluginLocations.Remove(plugin.AssemblyName?.FullName ?? string.Empty, out _); @@ -1064,7 +1060,7 @@ internal partial class PluginManager : IDisposable, IServiceType /// A value indicating whether the plugin/manifest has been banned. public bool IsManifestBanned(PluginManifest manifest) { - return !configuration.LoadBannedPlugins && this.bannedPlugins.Any(ban => (ban.Name == manifest.InternalName || ban.Name == Hash.GetStringSha256Hash(manifest.InternalName)) + return !this.configuration.LoadBannedPlugins && this.bannedPlugins.Any(ban => (ban.Name == manifest.InternalName || ban.Name == Hash.GetStringSha256Hash(manifest.InternalName)) && ban.AssemblyVersion >= manifest.AssemblyVersion); } diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 344ba3c28..32343feff 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -311,6 +311,9 @@ internal class LocalPlugin : IDisposable if (this.IsOrphaned) throw new InvalidPluginOperationException($"Plugin {this.Name} had no associated repo."); + if (!this.CheckPolicy()) + throw new InvalidPluginOperationException("Plugin was not loaded as per policy"); + this.State = PluginState.Loading; Log.Information($"Loading {this.DllFile.Name}"); @@ -553,6 +556,27 @@ internal class LocalPlugin : IDisposable this.SaveManifest(); } + /// + /// Check if anything forbids this plugin from loading. + /// + /// Whether or not this plugin shouldn't load. + public bool CheckPolicy() + { + var startInfo = Service.Get(); + var manager = Service.Get(); + + if (startInfo.NoLoadPlugins) + return false; + + if (startInfo.NoLoadThirdPartyPlugins && this.Manifest.IsThirdParty) + return false; + + if (manager.SafeMode) + return false; + + return true; + } + /// /// Disable this plugin, must be unloaded first. ///