diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj
index 50d6b9e5..4c5db1e3 100644
--- a/IPA.Loader/IPA.Loader.csproj
+++ b/IPA.Loader/IPA.Loader.csproj
@@ -92,10 +92,10 @@
-
+
diff --git a/IPA.Loader/Loader/Features/AddInFeature.cs b/IPA.Loader/Loader/Features/AddInFeature.cs
deleted file mode 100644
index 08da7fdc..00000000
--- a/IPA.Loader/Loader/Features/AddInFeature.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace IPA.Loader.Features
-{
- internal class AddInFeature : Feature
- {
- private PluginLoader.PluginMetadata selfMeta;
-
- public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
- {
- selfMeta = meta;
-
- RequireLoaded(meta);
-
- return true;
- }
-
- public override bool BeforeLoad(PluginLoader.PluginMetadata plugin)
- {
- return plugin != selfMeta;
- }
-
- public override string InvalidMessage
- {
- get => "Plugin is an add-in for some other mod, therefore should not be loaded.";
- protected set { }
- }
- }
-}
diff --git a/IPA.Loader/Loader/Features/ConfigProviderFeature.cs b/IPA.Loader/Loader/Features/ConfigProviderFeature.cs
index 41037db2..b22c71ee 100644
--- a/IPA.Loader/Loader/Features/ConfigProviderFeature.cs
+++ b/IPA.Loader/Loader/Features/ConfigProviderFeature.cs
@@ -39,7 +39,7 @@ namespace IPA.Loader.Features
goto hasFilename;
case BadImageFormatException bi:
filename = bi.FileName;
- hasFilename:
+ hasFilename:
InvalidMessage = $"Could not find {filename} while loading type";
break;
default:
diff --git a/IPA.Loader/Loader/Features/NoRuntimeEnableFeature.cs b/IPA.Loader/Loader/Features/NoRuntimeEnableFeature.cs
new file mode 100644
index 00000000..36fe6cfb
--- /dev/null
+++ b/IPA.Loader/Loader/Features/NoRuntimeEnableFeature.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IPA.Loader.Features
+{
+ internal class NoRuntimeEnableFeature : Feature
+ {
+ internal static bool HaveLoadedPlugins = false;
+
+ public override bool Initialize(PluginLoader.PluginMetadata meta, string[] parameters)
+ {
+ return parameters.Length == 0;
+ }
+
+ public override bool BeforeLoad(PluginLoader.PluginMetadata plugin)
+ {
+ return !HaveLoadedPlugins;
+ }
+
+ public override string InvalidMessage
+ {
+ get => "Plugin requested to not be loaded after initial plugin load";
+ protected set { }
+ }
+ }
+}
diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs
index 23a8a79a..4fdb6ad6 100644
--- a/IPA.Loader/Loader/PluginLoader.cs
+++ b/IPA.Loader/Loader/PluginLoader.cs
@@ -666,24 +666,23 @@ namespace IPA.Loader
foreach (var feature in meta.Features)
try
{
- // TODO: remove need for cast
- feature.AfterInit(info, info.Plugin as IPlugin);
+ feature.AfterInit(info, info.Plugin);
}
catch (Exception e)
{
Logger.loader.Critical($"Feature errored in {nameof(Feature.AfterInit)}: {e}");
}
- if (instance is IPlugin newPlugin) // TODO: remove this check, all plugins should be IPlugin
- try // TODO: move this out to after all plugins have been inited
- {
- newPlugin.OnEnable();
- }
- catch (Exception e)
- {
- Logger.loader.Error($"Error occurred trying to enable {meta.Name}");
- Logger.loader.Error(e);
- }
+ try // TODO: move this out to after all plugins have been inited
+ {
+ instance.OnEnable();
+ }
+ catch (Exception e)
+ {
+ Logger.loader.Error($"Error occurred trying to enable {meta.Name}");
+ Logger.loader.Error(e);
+ return null; // is enable failure a full load failure?
+ }
}
catch (AmbiguousMatchException)
{
diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs
index df8fd3c5..13cd23fa 100644
--- a/IPA.Loader/Loader/PluginManager.cs
+++ b/IPA.Loader/Loader/PluginManager.cs
@@ -13,6 +13,7 @@ using Mono.Cecil;
using UnityEngine;
using Logger = IPA.Logging.Logger;
using static IPA.Loader.PluginLoader;
+using IPA.Loader.Features;
#if NET3
using Net3_Proxy;
using Path = Net3_Proxy.Path;
@@ -323,6 +324,7 @@ namespace IPA.Loader
// initialize BSIPA plugins first
_bsPlugins.AddRange(PluginLoader.LoadPlugins());
+ NoRuntimeEnableFeature.HaveLoadedPlugins = true;
var metadataPaths = PluginsMetadata.Select(m => m.File.FullName).ToList();
var ignoredPaths = ignoredPlugins.Select(m => m.File.FullName).ToList();
diff --git a/IPA.Loader/Loader/manifest.json b/IPA.Loader/Loader/manifest.json
index 3d7f1a9c..9102c7d8 100644
--- a/IPA.Loader/Loader/manifest.json
+++ b/IPA.Loader/Loader/manifest.json
@@ -1,27 +1,28 @@
{
- "$schema": "https://raw.githubusercontent.com/beat-saber-modding-group/BSIPA-MetadataFileSchema/master/Schema.json",
- "author": "DaNike",
- "description": [
- "#![IPA.Loader.description.md]",
- "A mod loader specifically for Beat Saber."
- ],
- "gameVersion": "1.3.0",
- "id": "BSIPA",
- "name": "Beat Saber IPA",
- "version": "4.0.0-beta.1",
- "icon": "IPA.icon_white.png",
- "features": [
- "define-feature(print, IPA.Loader.Features.PrintFeature)",
- "define-feature(debug, IPA.Loader.Features.DebugFeature)",
- "define-feature(warn, IPA.Loader.Features.WarnFeature)",
- "define-feature(no-update, IPA.Loader.Features.NoUpdateFeature)",
- "define-feature(add-in, IPA.Loader.Features.AddInFeature)",
- "define-feature(init-injector, IPA.Loader.Features.InitInjectorFeature)",
- "define-feature(config-provider, IPA.Loader.Features.ConfigProviderFeature)"
- ],
- "links": {
- "project-home": "https://beat-saber-modding-group.github.io/BeatSaber-IPA-Reloaded/index.html",
- "project-source": "https://github.com/beat-saber-modding-group/BeatSaber-IPA-Reloaded",
- "donate": "https://ko-fi.com/danike"
- }
+ "$schema": "https://raw.githubusercontent.com/beat-saber-modding-group/BSIPA-MetadataFileSchema/master/Schema.json",
+ "author": "DaNike",
+ "description": [
+ "#![IPA.Loader.description.md]",
+ "A mod loader specifically for Beat Saber."
+ ],
+ "gameVersion": "1.5.0",
+ "id": "BSIPA",
+ "name": "Beat Saber IPA",
+ "version": "4.0.0-beta.1",
+ "icon": "IPA.icon_white.png",
+ "features": [
+ "define-feature(print, IPA.Loader.Features.PrintFeature)",
+ "define-feature(debug, IPA.Loader.Features.DebugFeature)",
+ "define-feature(warn, IPA.Loader.Features.WarnFeature)",
+ "define-feature(no-update, IPA.Loader.Features.NoUpdateFeature)",
+ "define-feature(no-runtime-enable, IPA.Loader.Features.NoRuntimeEnableFeature)",
+ "define-feature(init-injector, IPA.Loader.Features.InitInjectorFeature)",
+ "define-feature(config-provider, IPA.Loader.Features.ConfigProviderFeature)",
+ ""
+ ],
+ "links": {
+ "project-home": "https://beat-saber-modding-group.github.io/BeatSaber-IPA-Reloaded/index.html",
+ "project-source": "https://github.com/beat-saber-modding-group/BeatSaber-IPA-Reloaded",
+ "donate": "https://ko-fi.com/danike"
+ }
}
\ No newline at end of file
diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs
index af28e0a9..f74c3b02 100644
--- a/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs
+++ b/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs
@@ -1,7 +1,7 @@
namespace IPA
{
///
- /// Provides methods to allow runtime enabling and disabling of a plugin.
+ /// Provides methods to allow runtime disabling of a plugin.
///
public interface IDisablablePlugin
{
diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs
index b7ea8e88..b8ae28b1 100644
--- a/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs
+++ b/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs
@@ -6,6 +6,10 @@ namespace IPA
/// Interface for BSIPA plugins. Every class that implements this will be loaded if the DLL is placed at
/// <install dir>/Plugins.
///
+ ///
+ /// Mods implemented with this interface should handle being enabled at runtime properly, unless marked
+ /// with the "no-runtime-enable" feature.
+ ///
public interface IPlugin
{
///