feat: Allow /xldev to disable Safe Mode (#2166)

- Adds new menu item to /xldev to disable Safe Mode, allowing users to load plugins again.
  - Safe mode cannot be re-enabled once disabled.
- Add new ModuleLog.Create<T> for eventual ILogger magic
- Make safe mode writable
- Remove redundant check in CheckPolicy
This commit is contained in:
KazWolfe 2025-01-09 13:01:46 -08:00 committed by GitHub
parent da8be03124
commit a656fefb2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 13 deletions

View file

@ -1012,6 +1012,11 @@ internal class DalamudInterface : IInternalDisposableService
pluginManager.LoadBannedPlugins = !pluginManager.LoadBannedPlugins; pluginManager.LoadBannedPlugins = !pluginManager.LoadBannedPlugins;
} }
if (pluginManager.SafeMode && ImGui.MenuItem("Disable Safe Mode"))
{
pluginManager.SafeMode = false;
}
ImGui.Separator(); ImGui.Separator();
ImGui.MenuItem("API Level:" + PluginManager.DalamudApiLevel, false); ImGui.MenuItem("API Level:" + PluginManager.DalamudApiLevel, false);
ImGui.MenuItem("Loaded plugins:" + pluginManager.InstalledPlugins.Count(), false); ImGui.MenuItem("Loaded plugins:" + pluginManager.InstalledPlugins.Count(), false);

View file

@ -1,5 +1,6 @@
using Serilog; using Serilog;
using Serilog.Core; using Serilog.Core;
using Serilog.Core.Enrichers;
using Serilog.Events; using Serilog.Events;
namespace Dalamud.Logging.Internal; namespace Dalamud.Logging.Internal;
@ -11,7 +12,7 @@ public class ModuleLog
{ {
private readonly string moduleName; private readonly string moduleName;
private readonly ILogger moduleLogger; private readonly ILogger moduleLogger;
// FIXME (v9): Deprecate this class in favor of using contextualized ILoggers with proper formatting. // FIXME (v9): Deprecate this class in favor of using contextualized ILoggers with proper formatting.
// We can keep this class around as a Serilog helper, but ModuleLog should no longer be a returned // We can keep this class around as a Serilog helper, but ModuleLog should no longer be a returned
// type, instead returning a (prepared) ILogger appropriately. // type, instead returning a (prepared) ILogger appropriately.
@ -27,6 +28,28 @@ public class ModuleLog
this.moduleLogger = Log.ForContext("Dalamud.ModuleName", this.moduleName); this.moduleLogger = Log.ForContext("Dalamud.ModuleName", this.moduleName);
} }
/// <summary>
/// Initializes a new instance of the <see cref="ModuleLog"/> class.
/// This class will properly attach SourceContext and other attributes per Serilog standards.
/// </summary>
/// <param name="type">The type of the class this logger is for.</param>
public ModuleLog(Type type)
{
this.moduleName = type.Name;
this.moduleLogger = Log.ForContext(
[
new PropertyEnricher(Constants.SourceContextPropertyName, type.FullName),
new PropertyEnricher("Dalamud.ModuleName", this.moduleName)
]);
}
/// <summary>
/// Helper method to create a new <see cref="ModuleLog"/> instance based on a type.
/// </summary>
/// <typeparam name="T">The class to create this ModuleLog for.</typeparam>
/// <returns>Returns a ModuleLog with name set.</returns>
internal static ModuleLog Create<T>() => new(typeof(T));
/// <summary> /// <summary>
/// Log a templated verbose message to the in-game debug log. /// Log a templated verbose message to the in-game debug log.
/// </summary> /// </summary>

View file

@ -48,7 +48,7 @@ internal class PluginManager : IInternalDisposableService
/// </summary> /// </summary>
public const int PluginWaitBeforeFreeDefault = 1000; // upped from 500ms, seems more stable public const int PluginWaitBeforeFreeDefault = 1000; // upped from 500ms, seems more stable
private static readonly ModuleLog Log = new("PLUGINM"); private static readonly ModuleLog Log = ModuleLog.Create<PluginManager>();
private readonly object pluginListLock = new(); private readonly object pluginListLock = new();
private readonly DirectoryInfo pluginDirectory; private readonly DirectoryInfo pluginDirectory;
@ -243,9 +243,9 @@ internal class PluginManager : IInternalDisposableService
public bool ReposReady { get; private set; } public bool ReposReady { get; private set; }
/// <summary> /// <summary>
/// Gets a value indicating whether the plugin manager started in safe mode. /// Gets or sets a value indicating whether the plugin manager started in safe mode.
/// </summary> /// </summary>
public bool SafeMode { get; init; } public bool SafeMode { get; set; }
/// <summary> /// <summary>
/// Gets the <see cref="PluginConfigurations"/> object used when initializing plugins. /// Gets the <see cref="PluginConfigurations"/> object used when initializing plugins.

View file

@ -30,7 +30,7 @@ internal class LocalPlugin : IAsyncDisposable
#pragma warning disable SA1401 #pragma warning disable SA1401
protected LocalPluginManifest manifest; protected LocalPluginManifest manifest;
#pragma warning restore SA1401 #pragma warning restore SA1401
private static readonly ModuleLog Log = new("LOCALPLUGIN"); private static readonly ModuleLog Log = new("LOCALPLUGIN");
private readonly FileInfo manifestFile; private readonly FileInfo manifestFile;
@ -314,7 +314,7 @@ internal class LocalPlugin : IAsyncDisposable
this.State = PluginState.Loading; this.State = PluginState.Loading;
Log.Information($"Loading {this.DllFile.Name}"); Log.Information($"Loading {this.DllFile.Name}");
this.EnsureLoader(); this.EnsureLoader();
if (this.DllFile.DirectoryName != null && if (this.DllFile.DirectoryName != null &&
@ -413,7 +413,7 @@ internal class LocalPlugin : IAsyncDisposable
if (ex is not InvalidPluginOperationException) if (ex is not InvalidPluginOperationException)
this.State = PluginState.LoadError; this.State = PluginState.LoadError;
// If a precondition fails, don't record it as an error, as it isn't really. // If a precondition fails, don't record it as an error, as it isn't really.
if (ex is PluginPreconditionFailedException) if (ex is PluginPreconditionFailedException)
Log.Warning(ex.Message); Log.Warning(ex.Message);
else else
@ -510,9 +510,6 @@ internal class LocalPlugin : IAsyncDisposable
var startInfo = Service<Dalamud>.Get().StartInfo; var startInfo = Service<Dalamud>.Get().StartInfo;
var manager = Service<PluginManager>.Get(); var manager = Service<PluginManager>.Get();
if (startInfo.NoLoadPlugins)
return false;
if (startInfo.NoLoadThirdPartyPlugins && this.manifest.IsThirdParty) if (startInfo.NoLoadThirdPartyPlugins && this.manifest.IsThirdParty)
return false; return false;
@ -556,7 +553,7 @@ internal class LocalPlugin : IAsyncDisposable
/// </summary> /// </summary>
/// <param name="reason">Why it should be saved.</param> /// <param name="reason">Why it should be saved.</param>
protected void SaveManifest(string reason) => this.manifest.Save(this.manifestFile, reason); protected void SaveManifest(string reason) => this.manifest.Save(this.manifestFile, reason);
/// <summary> /// <summary>
/// Called before a plugin is reloaded. /// Called before a plugin is reloaded.
/// </summary> /// </summary>
@ -595,7 +592,7 @@ internal class LocalPlugin : IAsyncDisposable
// but plugins may load other versions of assemblies that Dalamud depends on. // but plugins may load other versions of assemblies that Dalamud depends on.
config.SharedAssemblies.Add((typeof(EntryPoint).Assembly.GetName(), false)); config.SharedAssemblies.Add((typeof(EntryPoint).Assembly.GetName(), false));
config.SharedAssemblies.Add((typeof(Common.DalamudStartInfo).Assembly.GetName(), false)); config.SharedAssemblies.Add((typeof(Common.DalamudStartInfo).Assembly.GetName(), false));
// Pin Lumina since we expose it as an API surface. Before anyone removes this again, please see #1598. // Pin Lumina since we expose it as an API surface. Before anyone removes this again, please see #1598.
// Changes to Lumina should be upstreamed if feasible, and if there is a desire to re-add unpinned Lumina we // Changes to Lumina should be upstreamed if feasible, and if there is a desire to re-add unpinned Lumina we
// will need to put this behind some kind of feature flag somewhere. // will need to put this behind some kind of feature flag somewhere.
@ -607,7 +604,7 @@ internal class LocalPlugin : IAsyncDisposable
{ {
if (this.loader != null) if (this.loader != null)
return; return;
try try
{ {
this.loader = PluginLoader.CreateFromAssemblyFile(this.DllFile.FullName, SetupLoaderConfig); this.loader = PluginLoader.CreateFromAssemblyFile(this.DllFile.FullName, SetupLoaderConfig);