diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index 1cf1965e..fe782f84 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -111,6 +111,8 @@ + + @@ -167,7 +169,7 @@ - + \ No newline at end of file diff --git a/IPA.Loader/Loader/Composite/CompositeBSPlugin.cs b/IPA.Loader/Loader/Composite/CompositeBSPlugin.cs index d4e9c755..9b0a1b9a 100644 --- a/IPA.Loader/Loader/Composite/CompositeBSPlugin.cs +++ b/IPA.Loader/Loader/Composite/CompositeBSPlugin.cs @@ -5,7 +5,11 @@ using Logger = IPA.Logging.Logger; namespace IPA.Loader.Composite { - internal class CompositeBSPlugin : IBeatSaberPlugin + internal class CompositeBSPlugin : +#pragma warning disable CS0618 // Type or member is obsolete + IBeatSaberPlugin, +#pragma warning restore CS0618 // Type or member is obsolete + _IPlugin { private readonly IEnumerable plugins; @@ -15,8 +19,9 @@ namespace IPA.Loader.Composite this.plugins = plugins; } + [Obsolete] public void OnApplicationStart() { - Invoke(plugin => plugin.Plugin.OnApplicationStart()); + Invoke(plugin => (plugin.Plugin as IBeatSaberPlugin)?.OnApplicationStart()); } public void OnApplicationQuit() { @@ -61,7 +66,7 @@ namespace IPA.Loader.Composite public void OnLateUpdate() { Invoke(plugin => { - if (plugin.Plugin is IEnhancedBeatSaberPlugin saberPlugin) + if (plugin.Plugin is IGenericEnhancedPlugin saberPlugin) saberPlugin.OnLateUpdate(); }); } diff --git a/IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs b/IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs index 9825f6bc..6817eb8e 100644 --- a/IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs +++ b/IPA.Loader/Loader/Composite/CompositeIPAPlugin.cs @@ -6,13 +6,13 @@ using Logger = IPA.Logging.Logger; namespace IPA.Loader.Composite { #pragma warning disable CS0618 // Type or member is obsolete - internal class CompositeIPAPlugin : IPlugin + internal class CompositeIPAPlugin : Old.IPlugin { - private readonly IEnumerable plugins; + private readonly IEnumerable plugins; - private delegate void CompositeCall(IPlugin plugin); + private delegate void CompositeCall(Old.IPlugin plugin); - public CompositeIPAPlugin(IEnumerable plugins) { + public CompositeIPAPlugin(IEnumerable plugins) { this.plugins = plugins; } @@ -49,7 +49,7 @@ namespace IPA.Loader.Composite public void OnLateUpdate() { Invoke(plugin => { - if (plugin is IEnhancedPlugin saberPlugin) + if (plugin is Old.IEnhancedPlugin saberPlugin) saberPlugin.OnLateUpdate(); }); } diff --git a/IPA.Loader/Loader/Features/Feature.cs b/IPA.Loader/Loader/Features/Feature.cs index 5817bc74..b790f2c0 100644 --- a/IPA.Loader/Loader/Features/Feature.cs +++ b/IPA.Loader/Loader/Features/Feature.cs @@ -68,7 +68,7 @@ namespace IPA.Loader.Features /// /// the plugin that was just initialized /// the instance of the plugin being initialized - public virtual void AfterInit(PluginLoader.PluginInfo plugin, IBeatSaberPlugin pluginInstance) => AfterInit(plugin); + public virtual void AfterInit(PluginLoader.PluginInfo plugin, IPlugin pluginInstance) => AfterInit(plugin); /// /// Called after a plugin has been fully initialized, whether or not there is an `Init` method. This should never throw an exception. diff --git a/IPA.Loader/Loader/LibLoader.cs b/IPA.Loader/Loader/LibLoader.cs index d6e2601a..01b23a3d 100644 --- a/IPA.Loader/Loader/LibLoader.cs +++ b/IPA.Loader/Loader/LibLoader.cs @@ -7,16 +7,23 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Linq; using IPA.Logging; +using IPA.Utilities; using Mono.Cecil; namespace IPA.Loader { internal class CecilLibLoader : BaseAssemblyResolver { + private static string CurrentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name; + private static string CurrentAssemblyPath = Assembly.GetExecutingAssembly().Location; + public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) { LibLoader.SetupAssemblyFilenames(); + if (name.Name == CurrentAssemblyName) + return AssemblyDefinition.ReadAssembly(CurrentAssemblyPath, parameters); + if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.{name.Version}.dll", out var path)) { if (File.Exists(path)) diff --git a/IPA.Loader/Loader/PluginComponent.cs b/IPA.Loader/Loader/PluginComponent.cs index c0fcf8d0..88457e79 100644 --- a/IPA.Loader/Loader/PluginComponent.cs +++ b/IPA.Loader/Loader/PluginComponent.cs @@ -34,7 +34,9 @@ namespace IPA.Loader gameObject.AddComponent(); #endif +#pragma warning disable CS0612 // Type or member is obsolete bsPlugins.OnApplicationStart(); +#pragma warning restore CS0612 // Type or member is obsolete ipaPlugins.OnApplicationStart(); SceneManager.activeSceneChanged += OnActiveSceneChanged; diff --git a/IPA.Loader/Loader/PluginLoader.cs b/IPA.Loader/Loader/PluginLoader.cs index 99deaaa5..e7b0eff6 100644 --- a/IPA.Loader/Loader/PluginLoader.cs +++ b/IPA.Loader/Loader/PluginLoader.cs @@ -124,7 +124,7 @@ namespace IPA.Loader /// public class PluginInfo { - internal IBeatSaberPlugin Plugin { get; set; } + internal _IPlugin Plugin { get; set; } /// /// Metadata for the plugin. @@ -243,17 +243,14 @@ namespace IPA.Loader { if (type.Namespace != pluginNs) continue; - foreach (var inter in type.Interfaces) + // TODO: change this to just IPlugin + if (type.HasInterface(typeof(_IPlugin).FullName)) { - if (typeof(IBeatSaberPlugin).FullName == inter.InterfaceType.FullName) - { - metadata.PluginType = type; - goto type_loop_done; // break out of both loops - } + metadata.PluginType = type; + break; } } - type_loop_done: if (metadata.PluginType == null) { Logger.loader.Error($"No plugin found in the manifest namespace ({pluginNs}) in {Path.GetFileName(plugin)}"); @@ -645,7 +642,7 @@ namespace IPA.Loader } var type = meta.Assembly.GetType(meta.PluginType.FullName); - var instance = (IBeatSaberPlugin)Activator.CreateInstance(type); + var instance = Activator.CreateInstance(type) as _IPlugin; info.Metadata = meta; info.Plugin = instance; @@ -668,17 +665,18 @@ namespace IPA.Loader foreach (var feature in meta.Features) try { - feature.AfterInit(info, info.Plugin); + // TODO: remove need for cast + feature.AfterInit(info, info.Plugin as IPlugin); } catch (Exception e) { Logger.loader.Critical($"Feature errored in {nameof(Feature.AfterInit)}: {e}"); } - if (instance is IDisablablePlugin disable) + 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 { - disable.OnEnable(); + newPlugin.OnEnable(); } catch (Exception e) { diff --git a/IPA.Loader/Loader/PluginManager.cs b/IPA.Loader/Loader/PluginManager.cs index 6f081c98..a26c6385 100644 --- a/IPA.Loader/Loader/PluginManager.cs +++ b/IPA.Loader/Loader/PluginManager.cs @@ -32,7 +32,7 @@ namespace IPA.Loader /// /// An of new Beat Saber plugins /// - internal static IEnumerable BSPlugins => (_bsPlugins ?? throw new InvalidOperationException()).Select(p => p.Plugin); + internal static IEnumerable<_IPlugin> BSPlugins => (_bsPlugins ?? throw new InvalidOperationException()).Select(p => p.Plugin); private static List _bsPlugins; internal static IEnumerable BSMetas => _bsPlugins; @@ -174,11 +174,11 @@ namespace IPA.Loader var depsNeedRestart = plugin.Dependencies.Aggregate(false, (b, p) => EnablePlugin(p) || b); var runtimeInfo = runtimeDisabled.FirstOrDefault(p => p.Metadata == plugin); - if (runtimeInfo != null && runtimeInfo.Plugin is IDisablablePlugin disable) + if (runtimeInfo != null && runtimeInfo.Plugin is IPlugin newPlugin) { try { - disable.OnEnable(); + newPlugin.OnEnable(); } catch (Exception e) { @@ -290,8 +290,8 @@ namespace IPA.Loader /// /// all legacy plugin instances [Obsolete("I mean, IPlugin shouldn't be used, so why should this? Not renaming to extend support for old plugins.")] - public static IEnumerable Plugins => _ipaPlugins; - private static List _ipaPlugins; + public static IEnumerable Plugins => _ipaPlugins; + private static List _ipaPlugins; internal static IConfigProvider SelfConfigProvider { get; set; } @@ -303,7 +303,7 @@ namespace IPA.Loader // so we need to resort to P/Invoke string exeName = Path.GetFileNameWithoutExtension(AppInfo.StartupPath); _bsPlugins = new List(); - _ipaPlugins = new List(); + _ipaPlugins = new List(); if (!Directory.Exists(pluginDirectory)) return; @@ -398,12 +398,12 @@ namespace IPA.Loader Logger.log.Info("-----------------------------"); } - private static Tuple, IEnumerable> LoadPluginsFromFile(string file) + private static Tuple, IEnumerable> LoadPluginsFromFile(string file) { - List ipaPlugins = new List(); + var ipaPlugins = new List(); if (!File.Exists(file) || !file.EndsWith(".dll", true, null)) - return new Tuple, IEnumerable>(null, ipaPlugins); + return new Tuple, IEnumerable>(null, ipaPlugins); T OptionalGetPlugin(Type t) where T : class { @@ -431,7 +431,7 @@ namespace IPA.Loader foreach (Type t in assembly.GetTypes()) { - IPlugin ipaPlugin = OptionalGetPlugin(t); + var ipaPlugin = OptionalGetPlugin(t); if (ipaPlugin != null) { ipaPlugins.Add(ipaPlugin); @@ -450,7 +450,7 @@ namespace IPA.Loader Logger.loader.Error(e); } - return new Tuple, IEnumerable>(null, ipaPlugins); + return new Tuple, IEnumerable>(null, ipaPlugins); } internal static class AppInfo diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IBeatSaberPlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IBeatSaberPlugin.cs index 96936c94..2d16355e 100644 --- a/IPA.Loader/PluginInterfaces/BeatSaber/IBeatSaberPlugin.cs +++ b/IPA.Loader/PluginInterfaces/BeatSaber/IBeatSaberPlugin.cs @@ -1,4 +1,4 @@ -using UnityEngine.SceneManagement; +using System; // ReSharper disable CheckNamespace namespace IPA @@ -7,48 +7,14 @@ namespace IPA /// Interface for Beat Saber plugins. Every class that implements this will be loaded if the DLL is placed at /// data/Managed/Plugins. /// - public interface IBeatSaberPlugin + [Obsolete("Use IPA.IPlugin instead")] + public interface IBeatSaberPlugin : _IPlugin { /// /// Gets invoked when the application is started. /// - /// THIS EVENT WILL NOT BE GUARANTEED TO FIRE. USE Init OR INSTEAD. + /// THIS EVENT WILL NOT BE GUARANTEED TO FIRE. USE Init OR INSTEAD. /// void OnApplicationStart(); - - /// - /// Gets invoked when the application is closed. - /// - void OnApplicationQuit(); - - /// - /// Gets invoked on every graphic update. - /// - void OnUpdate(); - - /// - /// Gets invoked on ever physics update. - /// - void OnFixedUpdate(); - - /// - /// Gets invoked whenever a scene is loaded. - /// - /// The scene currently loaded - /// The type of loading - void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode); - - /// - /// Gets invoked whenever a scene is unloaded - /// - /// The unloaded scene - void OnSceneUnloaded(Scene scene); - - /// - /// Gets invoked whenever a scene is changed - /// - /// The Scene that was previously loaded - /// The Scene being loaded - void OnActiveSceneChanged(Scene prevScene, Scene nextScene); } } diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs index 14c918fe..fdabfd74 100644 --- a/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs +++ b/IPA.Loader/PluginInterfaces/BeatSaber/IDisablablePlugin.cs @@ -5,7 +5,7 @@ /// public interface IDisablablePlugin { - /// + /*/// /// Called when a plugin is enabled. This is where you should set up Harmony patches and the like. /// /// @@ -14,14 +14,14 @@ /// /// Init will only ever be called once. /// - void OnEnable(); + void OnEnable();*/ /// /// Called when a plugin is disabled at runtime. This should disable things like Harmony patches and unsubscribe /// from events. After this is called there should be no lingering effects of the mod. /// /// - /// This will get called at shutdown, after , as well as when the + /// This will get called at shutdown, after , as well as when the /// plugin is disabled at runtime. /// void OnDisable(); diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedBeatSaberPlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedBeatSaberPlugin.cs index da67e31d..e83ce7f3 100644 --- a/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedBeatSaberPlugin.cs +++ b/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedBeatSaberPlugin.cs @@ -5,6 +5,7 @@ namespace IPA /// /// An enhanced version of a standard BeatSaber plugin. /// + [System.Obsolete] public interface IEnhancedBeatSaberPlugin : IBeatSaberPlugin, IGenericEnhancedPlugin { } diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedPlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedPlugin.cs new file mode 100644 index 00000000..c0dcc842 --- /dev/null +++ b/IPA.Loader/PluginInterfaces/BeatSaber/IEnhancedPlugin.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IPA +{ + /// + /// + /// An enhanced version of a standard BeatSaber plugin. + /// + public interface IEnhancedPlugin : IPlugin, IGenericEnhancedPlugin + { + } +} diff --git a/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs b/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs new file mode 100644 index 00000000..60cbce66 --- /dev/null +++ b/IPA.Loader/PluginInterfaces/BeatSaber/IPlugin.cs @@ -0,0 +1,61 @@ +using UnityEngine.SceneManagement; + +namespace IPA +{ + /// + /// Interface for BSIPA plugins. Every class that implements this will be loaded if the DLL is placed at + /// <install dir>/Plugins. + /// + public interface IPlugin : _IPlugin + { + /// + /// Called when a plugin is enabled. This is where you should set up Harmony patches and the like. + /// + /// + /// This will be called after Init, and will be called when the plugin loads normally too. + /// When a plugin is disabled at startup, neither this nor Init will be called until it is enabled. + /// + /// Init will only ever be called once. + /// + void OnEnable(); + } + /// + /// An interface for providing compatability with BSIPA 3.x.x. Do not use. + /// + public interface _IPlugin { + /// + /// Gets invoked when the application is closed. + /// + void OnApplicationQuit(); + + /// + /// Gets invoked on every graphic update. + /// + void OnUpdate(); + + /// + /// Gets invoked on ever physics update. + /// + void OnFixedUpdate(); + + /// + /// Gets invoked whenever a scene is loaded. + /// + /// The scene currently loaded + /// The type of loading + void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode); + + /// + /// Gets invoked whenever a scene is unloaded + /// + /// The unloaded scene + void OnSceneUnloaded(Scene scene); + + /// + /// Gets invoked whenever a scene is changed + /// + /// The Scene that was previously loaded + /// The Scene being loaded + void OnActiveSceneChanged(Scene prevScene, Scene nextScene); + } +} diff --git a/IPA.Loader/Utilities/Utils.cs b/IPA.Loader/Utilities/Utils.cs index 87743ed8..b3205068 100644 --- a/IPA.Loader/Utilities/Utils.cs +++ b/IPA.Loader/Utilities/Utils.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text; using System.Linq; using System.Collections.Generic; +using Mono.Cecil; #if NET3 using File = Net3_Proxy.File; #endif @@ -187,6 +188,11 @@ namespace IPA.Utilities cmpVal = l.Patch - r.Patch; return cmpVal; } + internal static bool HasInterface(this TypeDefinition type, string interfaceFullName) + { + return (type.Interfaces.Any(i => i.InterfaceType.FullName == interfaceFullName) + || type.Interfaces.Any(t => HasInterface(t.InterfaceType.Resolve(), interfaceFullName))); + } #if NET4 internal static IEnumerable StrJP(this IEnumerable a) => a; diff --git a/Refs/Unity.TextMeshPro.dll b/Refs/Unity.TextMeshPro.dll index 4e1ca61e..4a3242e5 100644 Binary files a/Refs/Unity.TextMeshPro.dll and b/Refs/Unity.TextMeshPro.dll differ diff --git a/Refs/UnityEngine.CoreModule.Net4.dll b/Refs/UnityEngine.CoreModule.Net4.dll index b6940600..158ef534 100644 Binary files a/Refs/UnityEngine.CoreModule.Net4.dll and b/Refs/UnityEngine.CoreModule.Net4.dll differ