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