From 9b301af67c4cb280e171ff1dc4a17bf45b9c953c Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Wed, 15 Jan 2020 20:59:46 -0600 Subject: [PATCH] Added checks for enablability and calls to enable/disable events --- IPA.Loader/IPA.Loader.csproj | 1 + .../Loader/CannotRuntimeDisableException.cs | 37 ++++++++++++++ IPA.Loader/Loader/PluginManager.cs | 50 ++++++++++++------- 3 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 IPA.Loader/Loader/CannotRuntimeDisableException.cs diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index 4aebd238..0b37ea80 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -107,6 +107,7 @@ + diff --git a/IPA.Loader/Loader/CannotRuntimeDisableException.cs b/IPA.Loader/Loader/CannotRuntimeDisableException.cs new file mode 100644 index 00000000..fe0e9973 --- /dev/null +++ b/IPA.Loader/Loader/CannotRuntimeDisableException.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IPA.Loader +{ + /// + /// Indicates that a plugin cannot be disabled at runtime. Generally not considered an error, however. + /// + [Serializable] + public class CannotRuntimeDisableException : Exception + { + /// + /// The plugin that cannot be disabled at runtime. + /// + public PluginMetadata Plugin { get; } + /// + /// Creates an exception for the given plugin metadata. + /// + /// the plugin that cannot be disabled + public CannotRuntimeDisableException(PluginMetadata plugin) : base($"Cannot runtime disable plugin \"{plugin.Name}\" ({plugin.Id})") + => Plugin = plugin; + + /// + /// Creates an exception from a serialization context. Not currently implemented. + /// + /// + /// + /// + protected CannotRuntimeDisableException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) + { + throw new NotImplementedException(); + } + } +} diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index 20e373da..98dc9afc 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -88,7 +88,6 @@ namespace IPA.Loader throw new InvalidOperationException("Transaction no longer resembles the current state of plugins"); } - var toEnable = transaction.ToEnable; var toDisable = transaction.ToDisable; transaction.Dispose(); @@ -111,26 +110,35 @@ namespace IPA.Loader foreach (var meta in enableOrder) { var executor = runtimeDisabledPlugins.FirstOrDefault(e => e.Metadata == meta); - if (executor != null) - runtimeDisabledPlugins.Remove(executor); - else - executor = PluginLoader.InitPlugin(meta, AllPlugins); + if (meta.RuntimeOptions == RuntimeOptions.DynamicInit) + { + if (executor != null) + runtimeDisabledPlugins.Remove(executor); + else + executor = PluginLoader.InitPlugin(meta, AllPlugins); - if (executor == null) continue; // couldn't initialize, skip to next + if (executor == null) continue; // couldn't initialize, skip to next + } PluginLoader.DisabledPlugins.Remove(meta); DisabledConfig.Instance.DisabledModIds.Remove(meta.Id ?? meta.Name); - _bsPlugins.Add(executor); - try - { - executor.Enable(); - } - catch (Exception e) + PluginEnabled(meta, meta.RuntimeOptions != RuntimeOptions.DynamicInit); + + if (meta.RuntimeOptions == RuntimeOptions.DynamicInit) { - Logger.loader.Error($"Error while enabling {meta.Id}:"); - Logger.loader.Error(e); - // this should still be considered enabled, hence its position + _bsPlugins.Add(executor); + + try + { + executor.Enable(); + } + catch (Exception e) + { + Logger.loader.Error($"Error while enabling {meta.Id}:"); + Logger.loader.Error(e); + // this should still be considered enabled, hence its position + } } } } @@ -149,10 +157,15 @@ namespace IPA.Loader foreach (var exec in disableExecs) { - runtimeDisabledPlugins.Add(exec); PluginLoader.DisabledPlugins.Add(exec.Metadata); DisabledConfig.Instance.DisabledModIds.Add(exec.Metadata.Id ?? exec.Metadata.Name); - _bsPlugins.Remove(exec); + if (exec.Metadata.RuntimeOptions == RuntimeOptions.DynamicInit) + { + runtimeDisabledPlugins.Add(exec); + _bsPlugins.Remove(exec); + } + + PluginDisabled(exec.Metadata, exec.Metadata.RuntimeOptions != RuntimeOptions.DynamicInit); } var disableStructure = disableExecs.Select(MakeDisableExec); @@ -163,6 +176,9 @@ namespace IPA.Loader return task; else { + if (exec.Executor.Metadata.RuntimeOptions != RuntimeOptions.DynamicInit) + return TaskEx.FromException(new CannotRuntimeDisableException(exec.Executor.Metadata)); + var res = TaskEx.WhenAll(exec.Dependents.Select(d => Disable(d, alreadyDisabled))) .ContinueWith(t => TaskEx.WhenAll(t, exec.Executor.Disable())).Unwrap(); // The WhenAll above allows us to wait for the executor to disable, but still propagate errors