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 { ///