diff --git a/BepInEx.Core/Bootstrap/BaseChainloader.cs b/BepInEx.Core/Bootstrap/BaseChainloader.cs index dab5eb0..15509b7 100644 --- a/BepInEx.Core/Bootstrap/BaseChainloader.cs +++ b/BepInEx.Core/Bootstrap/BaseChainloader.cs @@ -120,10 +120,12 @@ public abstract class BaseChainloader /// Obtains the corresponding to its GUID /// /// The GUID of the plugin to get the info from - /// The info of the plugin, null if the plugin wasn't loaded by the chainloader - public static PluginInfo GetPluginInfoFromGuid(string pluginGuid) + /// When this method returns, contains the + /// associated with the specified GUID, if the plugin was loaded; otherwise, null. This parameter is passed uninitialized. + /// true if the plugin with the GUID was loaded by the chainloader; otherwise, false. + public bool TryGetPluginInfoFromGuid(string pluginGuid, out PluginInfo foundPluginInfo) { - return Plugins.TryGetValue(pluginGuid, out var pluginInfo) ? pluginInfo : null; + return Plugins.TryGetValue(pluginGuid, out foundPluginInfo); } #region Contract @@ -132,12 +134,17 @@ public abstract class BaseChainloader private bool _initialized; + /// + /// The current chainloader instance + /// + public static BaseChainloader Instance { get; protected set; } + /// /// List of all instances of all plugins loaded via the chainloader. /// If a plugin fails to load, it is removed. This list only contains the plugins that were actually /// loaded and have their dependencies satisfied. /// - public static Dictionary Plugins { get; } = new(); + public Dictionary Plugins { get; } = new(); /// /// List of all instances of all providers loaded via the chainloader. @@ -147,8 +154,9 @@ public abstract class BaseChainloader public Dictionary Providers { get; } = new(); /// - /// List of all discovered from all the providers - /// with each context associated with its provider info + /// List of all objects received from plugin providers. + /// Value holds info of the originating provider. These contexts are used by chainloader to + /// load plugins. Plugins that are loaded successfully are added to . /// public Dictionary LoadContexts { get; } = new(); @@ -349,7 +357,7 @@ public abstract class BaseChainloader var builtinProviders = DiscoverPluginProvidersFrom(Paths.BepInExAssemblyDirectory); var providers = DiscoverPluginProvidersFrom(Paths.PluginProviderPath); Logger.Log(LogLevel.Info, $"{providers.Count + builtinProviders.Count} " + - $"plugins provider{(providers.Count + builtinProviders.Count == 1 ? "" : "s")} to load"); + $"plugin provider{(providers.Count + builtinProviders.Count == 1 ? "" : "s")} to load"); LoadPlugins(builtinProviders, provider: true); LoadPlugins(providers, provider: true); @@ -376,67 +384,67 @@ public abstract class BaseChainloader private void LoadPlugins(List plugins, bool provider) { - var existingPlugins = provider ? Providers : Plugins; - var sortedPlugins = ModifyLoadOrder(plugins, existingPlugins); + var existingPluginInfos = provider ? Providers : Plugins; + var sortedPluginInfos = ModifyLoadOrder(plugins, existingPluginInfos); var invalidPlugins = new HashSet(); var processedPlugins = new Dictionary(); var loadedAssemblies = new Dictionary(); - foreach (var plugin in sortedPlugins) + foreach (var pluginInfo in sortedPluginInfos) { - if (!CheckDependencies(plugin, processedPlugins, invalidPlugins, existingPlugins)) + if (!CheckDependencies(pluginInfo, processedPlugins, invalidPlugins, existingPluginInfos)) { continue; } try { - Logger.Log(LogLevel.Info, $"Loading [{plugin}]"); + Logger.Log(LogLevel.Info, $"Loading [{pluginInfo}]"); Assembly ass = null; - if (provider && !loadedAssemblies.TryGetValue(plugin.Location, out ass)) + if (provider && !loadedAssemblies.TryGetValue(pluginInfo.Location, out ass)) { - ass = Assembly.LoadFrom(plugin.Location); - loadedAssemblies[plugin.Location] = ass; + ass = Assembly.LoadFrom(pluginInfo.Location); + loadedAssemblies[pluginInfo.Location] = ass; } - else if (!provider && !loadedAssemblies.TryGetValue(plugin.LoadContext.AssemblyIdentifier, out ass)) + else if (!provider && !loadedAssemblies.TryGetValue(pluginInfo.LoadContext.AssemblyIdentifier, out ass)) { - ass = Assembly.Load(plugin.LoadContext.GetAssemblyData()); - loadedAssemblies[plugin.LoadContext.AssemblyIdentifier] = ass; + ass = Assembly.Load(pluginInfo.LoadContext.GetAssemblyData()); + loadedAssemblies[pluginInfo.LoadContext.AssemblyIdentifier] = ass; } - existingPlugins[plugin.Metadata.GUID] = plugin; - TryRunModuleCtor(plugin, ass); + existingPluginInfos[pluginInfo.Metadata.GUID] = pluginInfo; + TryRunModuleCtor(pluginInfo, ass); if (provider) { - var type = ass.GetType(plugin.TypeName); - plugin.Instance = Activator.CreateInstance(type); - AppDomain.CurrentDomain.AssemblyResolve += (_, args) => ((BasePluginProvider)plugin.Instance).ResolveAssembly(args.Name); - var pluginLoadContexts = ((BasePluginProvider)plugin.Instance).GetPlugins(); + var type = ass.GetType(pluginInfo.TypeName); + pluginInfo.Instance = Activator.CreateInstance(type); + AppDomain.CurrentDomain.AssemblyResolve += (_, args) => ((BasePluginProvider)pluginInfo.Instance).ResolveAssembly(args.Name); + var pluginLoadContexts = ((BasePluginProvider)pluginInfo.Instance).GetPlugins(); foreach (IPluginLoadContext context in pluginLoadContexts) { - LoadContexts[context] = plugin; + LoadContexts[context] = pluginInfo; } - ProviderLoaded?.Invoke(plugin); + ProviderLoaded?.Invoke(pluginInfo); } else { - plugin.Source = LoadContexts[plugin.LoadContext]; - plugin.Instance = LoadPlugin(plugin, ass); - PluginLoaded?.Invoke(plugin); - plugin.LoadContext.Dispose(); + pluginInfo.Source = LoadContexts[pluginInfo.LoadContext]; + pluginInfo.Instance = LoadPlugin(pluginInfo, ass); + PluginLoaded?.Invoke(pluginInfo); + pluginInfo.LoadContext.Dispose(); } } catch (Exception ex) { - invalidPlugins.Add(plugin.Metadata.GUID); - existingPlugins.Remove(plugin.Metadata.GUID); - plugin.LoadContext.Dispose(); + invalidPlugins.Add(pluginInfo.Metadata.GUID); + existingPluginInfos.Remove(pluginInfo.Metadata.GUID); + pluginInfo.LoadContext.Dispose(); Logger.Log(LogLevel.Error, - $"Error loading [{plugin}]: {(ex is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : ex.ToString())}"); + $"Error loading [{pluginInfo}]: {(ex is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : ex.ToString())}"); } } } diff --git a/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs b/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs index f417838..810dd19 100644 --- a/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs +++ b/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs @@ -1,3 +1,4 @@ +using System; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Contract; @@ -14,8 +15,15 @@ namespace BepInEx.NET.Common HarmonyInstance = new Harmony("BepInEx.Plugin." + metadata.GUID); - Info = BaseChainloader.GetPluginInfoFromGuid(metadata.GUID); - Info.Instance = this; + if (BaseChainloader.Instance.TryGetPluginInfoFromGuid(metadata.GUID, out var pluginInfo)) + { + Info = pluginInfo; + Info.Instance = this; + } + else + { + throw new InvalidOperationException($"The plugin information for {metadata.GUID} couldn't be found on the chainloader"); + } Logger = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); diff --git a/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs b/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs index 5e4af66..9f9717c 100644 --- a/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs +++ b/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs @@ -7,9 +7,6 @@ namespace BepInEx.NET.Common { public class NetChainloader : BaseChainloader { - // TODO: Remove once proper instance handling exists - public static NetChainloader Instance { get; set; } - public override void Initialize(string gameExePath = null) { Instance = this; diff --git a/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs b/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs index 581a6b4..3ffa16d 100644 --- a/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs +++ b/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs @@ -1,4 +1,5 @@ -using BepInEx.Bootstrap; +using System; +using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Contract; using BepInEx.Logging; @@ -12,8 +13,15 @@ public abstract class BasePlugin : IPlugin { var metadata = MetadataHelper.GetPluginMetadata(this); - Info = BaseChainloader.GetPluginInfoFromGuid(metadata.GUID); - Info.Instance = this; + if (BaseChainloader.Instance.TryGetPluginInfoFromGuid(metadata.GUID, out var pluginInfo)) + { + Info = pluginInfo; + Info.Instance = this; + } + else + { + throw new InvalidOperationException($"The plugin information for {metadata.GUID} couldn't be found on the chainloader"); + } Logger = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); diff --git a/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs b/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs index 5f857cd..b493789 100644 --- a/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs +++ b/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs @@ -32,8 +32,6 @@ public class IL2CPPChainloader : BaseChainloader private static INativeDetour RuntimeInvokeDetour { get; set; } - public static IL2CPPChainloader Instance { get; set; } - /// /// Register and add a Unity Component (for example MonoBehaviour) into BepInEx global manager. /// Automatically registers the type with Il2Cpp type system if it isn't initialised already. diff --git a/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs b/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs index cdf73c2..4c0552b 100644 --- a/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs +++ b/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs @@ -23,8 +23,15 @@ public abstract class BaseUnityPlugin : MonoBehaviour, IPlugin throw new InvalidOperationException("Can't create an instance of " + GetType().FullName + " because it inherits from BaseUnityPlugin and the BepInPlugin attribute is missing."); - Info = BaseChainloader.GetPluginInfoFromGuid(metadata.GUID); - Info.Instance = this; + if (BaseChainloader.Instance.TryGetPluginInfoFromGuid(metadata.GUID, out var pluginInfo)) + { + Info = pluginInfo; + Info.Instance = this; + } + else + { + throw new InvalidOperationException($"The plugin information for {metadata.GUID} couldn't be found on the chainloader"); + } Logger = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); diff --git a/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs b/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs index c08a8de..1c163b9 100644 --- a/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs +++ b/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs @@ -37,9 +37,6 @@ public class UnityChainloader : BaseChainloader private string _consoleTitle; - // TODO: Remove once proper instance handling exists - public static UnityChainloader Instance { get; set; } - /// /// The GameObject that all plugins are attached to as components. /// @@ -70,9 +67,9 @@ public class UnityChainloader : BaseChainloader Logger.Log(LogLevel.Debug, "Entering chainloader StaticStart"); - var instance = new UnityChainloader(); - instance.Initialize(gameExePath); - instance.Execute(); + Instance = new UnityChainloader(); + Instance.Initialize(gameExePath); + Instance.Execute(); Logger.Log(LogLevel.Debug, "Exiting chainloader StaticStart"); }