diff --git a/BepInEx.Core/Bootstrap/BaseChainloader.cs b/BepInEx.Core/Bootstrap/BaseChainloader.cs index 6b62ffd..c6fe182 100644 --- a/BepInEx.Core/Bootstrap/BaseChainloader.cs +++ b/BepInEx.Core/Bootstrap/BaseChainloader.cs @@ -19,23 +19,20 @@ public abstract class BaseChainloader private static Regex allowedGuidRegex { get; } = new(@"^[a-zA-Z0-9\._\-]+$"); - internal BaseChainloader() { } - /// /// Analyzes the given type definition and attempts to convert it to a valid /// /// Type definition to analyze. - /// The load context of the plugin /// The filepath of the assembly, to keep as metadata. /// If the type represent a valid plugin, returns a instance. Otherwise, return null. - public static PluginInfo ToPluginInfo(TypeDefinition type, IPluginLoadContext loadContext, string assemblyLocation) + public static PluginInfo ToPluginInfo(TypeDefinition type, string assemblyLocation) { if (type.IsInterface || type.IsAbstract) return null; try { - if (!type.IsSubtypeOf(typeof(T))) + if (!type.IsSubtypeOf(typeof(TPlugin))) return null; } catch (AssemblyResolutionException) @@ -44,13 +41,8 @@ public abstract class BaseChainloader return null; } - if (type.IsSubtypeOf(typeof(BasePluginProvider))) - return GetPluginInfo(type, BepInPluginProvider.FromCecilType(type), loadContext, assemblyLocation); - return GetPluginInfo(type, BepInPlugin.FromCecilType(type), loadContext, assemblyLocation); - } + var metadata = BepInPlugin.FromCecilType(type); - private static PluginInfo GetPluginInfo(TypeDefinition type, BepInPlugin metadata, IPluginLoadContext loadContext, string assemblyLocation) - { // Perform checks that will prevent the plugin from being loaded in ALL cases if (metadata == null) { @@ -93,16 +85,15 @@ public abstract class BaseChainloader Incompatibilities = incompatibilities, TypeName = type.FullName, TargettedBepInExVersion = bepinVersion, - _location = assemblyLocation, - LoadContext = loadContext + Location = assemblyLocation }; } - protected static bool HasBepinPluginType(AssemblyDefinition ass) + protected static bool HasBepinPlugins(AssemblyDefinition ass) { if (ass.MainModule.AssemblyReferences.All(r => r.Name != CurrentAssemblyName)) return false; - if (ass.MainModule.GetTypeReferences().All(r => r.FullName != typeof(T).FullName)) + if (ass.MainModule.GetTypeReferences().All(r => r.FullName != typeof(BepInPlugin).FullName)) return false; return true; @@ -125,47 +116,16 @@ 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. + /// List of all instances loaded via the chainloader. /// public Dictionary Plugins { get; } = new(); - /// - /// List of all instances of all providers loaded via the chainloader. - /// If a provider fails to load, it is removed. This list only contains the providers that were actually - /// loaded and have their dependencies satisfied. - /// - public Dictionary Providers { get; } = new(); - - /// - /// 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 . - /// - internal Dictionary LoadContexts { get; } = new(); - /// /// Collection of error chainloader messages that occured during plugin loading. /// Contains information about what certain plugins were not loaded. /// public List DependencyErrors { get; } = new(); - /// - /// Occurs after a plugin provider is loaded. - /// - public event Action ProviderLoaded; - - /// - /// Occurs after all plugins providers are loaded. - /// - public event Action FinishedProviders; - /// /// Occurs after a plugin is loaded. /// @@ -189,12 +149,12 @@ public abstract class BaseChainloader InitializeLoggers(); - if (!Directory.Exists(Paths.PluginProviderPath)) - Directory.CreateDirectory(Paths.PluginProviderPath); - if (!Directory.Exists(Paths.PluginPath)) Directory.CreateDirectory(Paths.PluginPath); + if (!Directory.Exists(Paths.PatcherPluginPath)) + Directory.CreateDirectory(Paths.PatcherPluginPath); + _initialized = true; Logger.Log(LogLevel.Message, "Chainloader initialized"); @@ -234,20 +194,29 @@ public abstract class BaseChainloader /// Path from which to search the plugins. /// Cache name to use. If null, results are not cached. /// List of discovered plugins and their metadata. - protected List DiscoverPluginProvidersFrom(string path, string cacheName = "chainloader_meta") + protected IList DiscoverPluginsFrom(string path, string cacheName = "chainloader") { var pluginsToLoad = - TypeLoader.FindPluginTypes(path, ToPluginInfo, HasBepinPluginType, cacheName); + TypeLoader.FindPluginTypes(path, ToPluginInfo, HasBepinPlugins, cacheName); return pluginsToLoad.SelectMany(p => p.Value).ToList(); } + /// + /// Discovers plugins to load. + /// + /// List of plugins to be loaded. + protected virtual IList DiscoverPlugins() + { + return DiscoverPluginsFrom(Paths.PluginPath); + } + /// /// Preprocess the plugins and modify the load order. /// /// Some plugins may be skipped if they cannot be loaded (wrong metadata, etc). /// Plugins to process. /// List of plugins to load in the correct load order. - protected virtual List ModifyLoadOrder(IList plugins, Dictionary existingPlugins) + protected virtual IList ModifyLoadOrder(IList plugins) { // We use a sorted dictionary to ensure consistent load order var dependencyDict = @@ -256,7 +225,7 @@ public abstract class BaseChainloader foreach (var pluginInfoGroup in plugins.GroupBy(info => info.Metadata.GUID)) { - if (existingPlugins.TryGetValue(pluginInfoGroup.Key, out var loadedPlugin)) + if (Plugins.TryGetValue(pluginInfoGroup.Key, out var loadedPlugin)) { Logger.Log(LogLevel.Warning, $"Skipping [{pluginInfoGroup.Key}] because a plugin with a similar GUID ([{loadedPlugin}]) has been already loaded."); @@ -297,7 +266,7 @@ public abstract class BaseChainloader foreach (var pluginInfo in pluginsByGuid.Values.ToList()) if (pluginInfo.Incompatibilities.Any(incompatibility => pluginsByGuid.ContainsKey(incompatibility.IncompatibilityGUID) - || existingPlugins.ContainsKey(incompatibility.IncompatibilityGUID)) + || Plugins.ContainsKey(incompatibility.IncompatibilityGUID)) ) { pluginsByGuid.Remove(pluginInfo.Metadata.GUID); @@ -306,7 +275,7 @@ public abstract class BaseChainloader var incompatiblePluginsNew = pluginInfo.Incompatibilities.Select(x => x.IncompatibilityGUID) .Where(x => pluginsByGuid.ContainsKey(x)); var incompatiblePluginsExisting = pluginInfo.Incompatibilities.Select(x => x.IncompatibilityGUID) - .Where(x => existingPlugins.ContainsKey(x)); + .Where(x => Plugins.ContainsKey(x)); var incompatiblePlugins = incompatiblePluginsNew.Concat(incompatiblePluginsExisting).ToArray(); var message = $@"Could not load [{pluginInfo}] because it is incompatible with: {string.Join(", ", incompatiblePlugins)}"; @@ -337,24 +306,15 @@ public abstract class BaseChainloader } /// - /// Run the chainloader and load all plugins from all providers. + /// Run the chainloader and load all plugins from the plugins folder. /// public virtual void Execute() { try { - var builtinProviders = DiscoverPluginProvidersFrom(Paths.BepInExAssemblyDirectory); - var providers = DiscoverPluginProvidersFrom(Paths.PluginProviderPath); - Logger.Log(LogLevel.Info, $"{providers.Count + builtinProviders.Count} " + - $"plugin provider{(providers.Count + builtinProviders.Count == 1 ? "" : "s")} to load"); - - LoadPlugins(builtinProviders, provider: true); - LoadPlugins(providers, provider: true); - FinishedProviders?.Invoke(); - - var plugins = TypeLoader.GetPluginsFromLoadContexts(LoadContexts.Keys.Cast(), ToPluginInfo, HasBepinPluginType, "chainloader"); + var plugins = DiscoverPlugins(); Logger.Log(LogLevel.Info, $"{plugins.Count} plugin{(plugins.Count == 1 ? "" : "s")} to load"); - LoadPlugins(plugins, provider: false); + LoadPlugins(plugins); Finished?.Invoke(); } catch (Exception ex) @@ -371,143 +331,117 @@ public abstract class BaseChainloader Logger.Log(LogLevel.Message, "Chainloader startup complete"); } - [Obsolete("Use a plugin provider instead", true)] - public IList LoadPlugins(params string[] pluginsPaths) + private IList LoadPlugins(IList plugins) { - throw new NotSupportedException("Use a plugin provider instead"); - } - - private void LoadPlugins(List plugins, bool provider) - { - var existingPluginInfos = provider ? Providers : Plugins; - var sortedPluginInfos = ModifyLoadOrder(plugins, existingPluginInfos); - + var sortedPlugins = ModifyLoadOrder(plugins); + var invalidPlugins = new HashSet(); var processedPlugins = new Dictionary(); var loadedAssemblies = new Dictionary(); + var loadedPlugins = new List(); - foreach (var pluginInfo in sortedPluginInfos) + foreach (var plugin in sortedPlugins) { - if (!CheckDependencies(pluginInfo, processedPlugins, invalidPlugins, existingPluginInfos)) + var dependsOnInvalidPlugin = false; + var missingDependencies = new List(); + foreach (var dependency in plugin.Dependencies) { + static bool IsHardDependency(BepInDependency dep) => + (dep.Flags & BepInDependency.DependencyFlags.HardDependency) != 0; + + // If the dependency wasn't already processed, it's missing altogether + var dependencyExists = + processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion); + // Alternatively, if the dependency hasn't been loaded before, it's missing too + if (!dependencyExists) + { + dependencyExists = Plugins.TryGetValue(dependency.DependencyGUID, out var pluginInfo); + pluginVersion = pluginInfo?.Metadata.Version; + } + + if (!dependencyExists || dependency.VersionRange != null && + !dependency.VersionRange.IsSatisfied(pluginVersion)) + { + // If the dependency is hard, collect it into a list to show + if (IsHardDependency(dependency)) + missingDependencies.Add(dependency); + continue; + } + + // If the dependency is a hard and is invalid (e.g. has missing dependencies), report that to the user + if (invalidPlugins.Contains(dependency.DependencyGUID) && IsHardDependency(dependency)) + { + dependsOnInvalidPlugin = true; + break; + } + } + + processedPlugins.Add(plugin.Metadata.GUID, plugin.Metadata.Version); + + if (dependsOnInvalidPlugin) + { + var message = + $"Skipping [{plugin}] because it has a dependency that was not loaded. See previous errors for details."; + DependencyErrors.Add(message); + Logger.Log(LogLevel.Warning, message); + continue; + } + + if (missingDependencies.Count != 0) + { + var message = $@"Could not load [{plugin}] because it has missing dependencies: { + string.Join(", ", missingDependencies.Select(s => s.VersionRange == null ? s.DependencyGUID : $"{s.DependencyGUID} ({s.VersionRange})").ToArray()) + }"; + DependencyErrors.Add(message); + Logger.Log(LogLevel.Error, message); + + invalidPlugins.Add(plugin.Metadata.GUID); continue; } try { - Logger.Log(LogLevel.Info, $"Loading [{pluginInfo}]"); + Logger.Log(LogLevel.Info, $"Loading [{plugin}]"); - Assembly ass = null; - if (provider && !loadedAssemblies.TryGetValue(pluginInfo._location, out ass)) - { - ass = Assembly.LoadFrom(pluginInfo._location); - loadedAssemblies[pluginInfo._location] = ass; - } - else if (!provider && !loadedAssemblies.TryGetValue(pluginInfo.LoadContext.AssemblyIdentifier, out ass)) - { - var symbols = pluginInfo.LoadContext.GetAssemblySymbolsData(); - ass = symbols != null ? Assembly.Load(pluginInfo.LoadContext.GetAssemblyData(), symbols) - : Assembly.Load(pluginInfo.LoadContext.GetAssemblyData()); - loadedAssemblies[pluginInfo.LoadContext.AssemblyIdentifier] = ass; - } + if (!loadedAssemblies.TryGetValue(plugin.Location, out var ass)) + loadedAssemblies[plugin.Location] = ass = Assembly.LoadFrom(plugin.Location); - existingPluginInfos[pluginInfo.Metadata.GUID] = pluginInfo; - TryRunModuleCtor(pluginInfo, ass); + Plugins[plugin.Metadata.GUID] = plugin; + TryRunModuleCtor(plugin, ass); + plugin.Instance = LoadPlugin(plugin, ass); + loadedPlugins.Add(plugin); - if (provider) - { - 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) - { - var cachedContext = new CachedPluginLoadContext(context); - LoadContexts[cachedContext] = pluginInfo; - } - ProviderLoaded?.Invoke(pluginInfo); - } - else - { - pluginInfo.Source = LoadContexts[(CachedPluginLoadContext)pluginInfo.LoadContext]; - pluginInfo.Instance = LoadPlugin(pluginInfo, ass); - PluginLoaded?.Invoke(pluginInfo); - foreach (CachedPluginLoadContext context in LoadContexts.Keys) - context.Dispose(); - } + PluginLoaded?.Invoke(plugin); } catch (Exception ex) { - invalidPlugins.Add(pluginInfo.Metadata.GUID); - existingPluginInfos.Remove(pluginInfo.Metadata.GUID); + invalidPlugins.Add(plugin.Metadata.GUID); + Plugins.Remove(plugin.Metadata.GUID); Logger.Log(LogLevel.Error, - $"Error loading [{pluginInfo}]: {(ex is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : ex.ToString())}"); + $"Error loading [{plugin}]: {(ex is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : ex.ToString())}"); } } + + return loadedPlugins; } - private bool CheckDependencies(T plugin, Dictionary processedPlugins, HashSet invalidPlugins, Dictionary existingPlugins) - where T : PluginInfo + /// + /// Detects and loads all plugins in the specified directories. + /// + /// + /// It is better to collect all paths at once and use a single call to LoadPlugins than multiple calls. + /// This allows to run proper dependency resolving and to load all plugins in one go. + /// + /// Directories to search the plugins from. + /// List of loaded plugin infos. + public IList LoadPlugins(params string[] pluginsPaths) { - var dependsOnInvalidPlugin = false; - var missingDependencies = new List(); - foreach (var dependency in plugin.Dependencies) - { - static bool IsHardDependency(BepInDependency dep) => - (dep.Flags & BepInDependency.DependencyFlags.HardDependency) != 0; - - // If the dependency wasn't already processed, it's missing altogether - var dependencyExists = - processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion); - // Alternatively, if the dependency hasn't been loaded before, it's missing too - if (!dependencyExists) - { - dependencyExists = existingPlugins.TryGetValue(dependency.DependencyGUID, out var pluginInfo); - pluginVersion = pluginInfo?.Metadata.Version; - } - - if (!dependencyExists || dependency.VersionRange != null && - !dependency.VersionRange.IsSatisfied(pluginVersion)) - { - // If the dependency is hard, collect it into a list to show - if (IsHardDependency(dependency)) - missingDependencies.Add(dependency); - continue; - } - - // If the dependency is a hard and is invalid (e.g. has missing dependencies), report that to the user - if (invalidPlugins.Contains(dependency.DependencyGUID) && IsHardDependency(dependency)) - { - dependsOnInvalidPlugin = true; - break; - } - } - - processedPlugins.Add(plugin.Metadata.GUID, plugin.Metadata.Version); - - if (dependsOnInvalidPlugin) - { - var message = - $"Skipping [{plugin}] because it has a dependency that was not loaded. See previous errors for details."; - DependencyErrors.Add(message); - Logger.Log(LogLevel.Warning, message); - return false; - } - - if (missingDependencies.Count != 0) - { - var message = $@"Could not load [{plugin}] because it has missing dependencies: { - string.Join(", ", missingDependencies.Select(s => s.VersionRange == null ? s.DependencyGUID : $"{s.DependencyGUID} ({s.VersionRange})").ToArray()) - }"; - DependencyErrors.Add(message); - Logger.Log(LogLevel.Error, message); - - invalidPlugins.Add(plugin.Metadata.GUID); - return false; - } - - return true; + // TODO: This is a temporary solution for 3rd party loaders. Instead, this should be done via metaplugins. + var plugins = new List(); + foreach (var pluginsPath in pluginsPaths) + plugins.AddRange(DiscoverPluginsFrom(pluginsPath)); + return LoadPlugins(plugins); } private static void TryRunModuleCtor(PluginInfo plugin, Assembly assembly) diff --git a/BepInEx.Core/Bootstrap/TypeLoader.cs b/BepInEx.Core/Bootstrap/TypeLoader.cs index 63c1295..69a7775 100644 --- a/BepInEx.Core/Bootstrap/TypeLoader.cs +++ b/BepInEx.Core/Bootstrap/TypeLoader.cs @@ -10,41 +10,6 @@ using Mono.Cecil; namespace BepInEx.Bootstrap; -internal class CachedPluginLoadContext : IPluginLoadContext, IDisposable -{ - public IPluginLoadContext PluginLoadContext { get; } - private byte[] assemblyData; - private byte[] assemblySymbolsData; - - public CachedPluginLoadContext(IPluginLoadContext pluginLoadContext) - { - PluginLoadContext = pluginLoadContext; - } - - public string AssemblyIdentifier => PluginLoadContext.AssemblyIdentifier; - public string AssemblyHash => PluginLoadContext.AssemblyHash; - public byte[] GetAssemblyData() - { - return assemblyData ??= PluginLoadContext.GetAssemblyData(); - } - - public byte[] GetAssemblySymbolsData() - { - return assemblySymbolsData ??= PluginLoadContext.GetAssemblySymbolsData(); - } - - public byte[] GetFile(string relativePath) - { - return PluginLoadContext.GetFile(relativePath); - } - - public void Dispose() - { - assemblyData = null; - assemblySymbolsData = null; - } -} - /// /// A cacheable metadata item. Can be used with and /// to cache plugin metadata. @@ -70,11 +35,6 @@ public interface ICacheable /// public class CachedAssembly where T : ICacheable { - /// - /// The version of the cache which increments on each format changes - /// - public const int Version = 0; - /// /// List of cached items inside the assembly. /// @@ -129,9 +89,7 @@ public static class TypeLoader { Paths.BepInExAssemblyDirectory, Paths.PluginPath, - Paths.PluginProviderPath, Paths.PatcherPluginPath, - Paths.PatcherProviderPath, Paths.ManagedPath }.Concat(SearchDirectories); @@ -168,8 +126,9 @@ public static class TypeLoader /// selector. /// public static Dictionary> FindPluginTypes(string directory, - Func typeSelector, - Func assemblyFilter = null, + Func typeSelector, + Func assemblyFilter = + null, string cacheName = null) where T : ICacheable, new() { @@ -194,7 +153,19 @@ public static class TypeLoader continue; } - result[dll] = ExamineStream(typeSelector, assemblyFilter, dllMs, null, dll); + using var ass = AssemblyDefinition.ReadAssembly(dllMs, ReaderParameters); + Logger.Log(LogLevel.Debug, $"Examining '{dll}'"); + + if (!assemblyFilter?.Invoke(ass) ?? false) + { + result[dll] = new List(); + continue; + } + + var matches = ass.MainModule.Types + .Select(t => typeSelector(t, dll)) + .Where(t => t != null).ToList(); + result[dll] = matches; } catch (BadImageFormatException e) { @@ -212,82 +183,6 @@ public static class TypeLoader return result; } - /// - /// Looks up assemblies using the given loaders and locates all types that can be loaded and collects their metadata. - /// - /// The specific base type to search for. - /// The load contexts to obtain the assemblies from. - /// A function to check if a type should be selected and to build the type metadata. - /// A filter function to quickly determine if the assembly can be loaded. - /// The name of the cache to get cached types from. - /// - /// A dictionary of all assemblies in the directory and the list of type metadatas of types that match the - /// selector. - /// - public static List GetPluginsFromLoadContexts(IEnumerable loadContexts, - Func typeSelector, - Func assemblyFilter = null, - string cacheName = null) - where T : ICacheable, new() - { - var result = new Dictionary>(); - var hashes = new Dictionary(); - Dictionary> cache = null; - - if (cacheName != null) - cache = LoadAssemblyCache(cacheName); - - foreach (IPluginLoadContext loadContext in loadContexts) - { - IList plugins; - if (cache != null && loadContext.AssemblyHash != null && - cache.TryGetValue(loadContext.AssemblyIdentifier, out var cacheEntry) && - loadContext.AssemblyHash == cacheEntry.Hash) - { - plugins = cacheEntry.CacheItems; - } - else - { - var assemblyData = loadContext.GetAssemblyData(); - using var memory = new MemoryStream(assemblyData); - plugins = ExamineStream(typeSelector, assemblyFilter, memory, loadContext, null); - } - - foreach (T pluginInfo in plugins) - { - if (!result.ContainsKey(loadContext.AssemblyIdentifier)) - result[loadContext.AssemblyIdentifier] = new(); - result[loadContext.AssemblyIdentifier].Add(pluginInfo); - } - } - - if (cache != null) - SaveAssemblyCache(cacheName, result, hashes); - - return result.SelectMany(x => x.Value).ToList(); - } - - private static List ExamineStream(Func typeSelector, - Func assemblyFilter, - MemoryStream dllMs, - IPluginLoadContext loadContext, - string location) - where T : ICacheable, new() - { - using var ass = AssemblyDefinition.ReadAssembly(dllMs, ReaderParameters); - Logger.Log(LogLevel.Debug, $"Examining '{ass.Name}'"); - - if (!assemblyFilter?.Invoke(ass) ?? false) - { - return new List(); - } - - var matches = ass.MainModule.Types - .Select(t => typeSelector(t, loadContext, location)) - .Where(t => t != null).ToList(); - return matches; - } - /// /// Loads an index of type metadatas from a cache. /// @@ -310,9 +205,7 @@ public static class TypeLoader if (!File.Exists(path)) return null; - using var br = new BinaryReader(File.OpenRead(path)); - var version = br.ReadInt32(); - if (version == CachedAssembly.Version) + using (var br = new BinaryReader(File.OpenRead(path))) { var entriesCount = br.ReadInt32(); @@ -366,7 +259,6 @@ public static class TypeLoader var path = Path.Combine(Paths.CachePath, $"{cacheName}_typeloader.dat"); using var bw = new BinaryWriter(File.OpenWrite(path)); - bw.Write(CachedAssembly.Version); bw.Write(entries.Count); foreach (var kv in entries) diff --git a/BepInEx.Core/Contract/Attributes.cs b/BepInEx.Core/Contract/Attributes.cs index 793f456..0de9e93 100644 --- a/BepInEx.Core/Contract/Attributes.cs +++ b/BepInEx.Core/Contract/Attributes.cs @@ -76,32 +76,6 @@ public class BepInPlugin : Attribute } } -/// -/// This attribute denotes that a class is a plugin provider, and specifies the required metadata. -/// -[AttributeUsage(AttributeTargets.Class)] -public class BepInPluginProvider : BepInPlugin -{ - /// The unique identifier of the plugin. Should not change between plugin versions. - /// The user friendly name of the plugin. Is able to be changed between versions. - /// The specific version of the plugin. - public BepInPluginProvider(string GUID, string Name, string Version) : base(GUID, Name, Version) - { - } - - internal new static BepInPluginProvider FromCecilType(TypeDefinition td) - { - var attr = MetadataHelper.GetCustomAttributes(td, false).FirstOrDefault(); - - if (attr == null) - return null; - - return new BepInPluginProvider((string) attr.ConstructorArguments[0].Value, - (string) attr.ConstructorArguments[1].Value, - (string) attr.ConstructorArguments[2].Value); - } -} - /// /// This attribute specifies any dependencies that this plugin has on other plugins. /// @@ -307,28 +281,6 @@ public static class MetadataHelper /// The BepInPlugin metadata of the plugin instance. public static BepInPlugin GetMetadata(object plugin) => GetMetadata(plugin.GetType()); - /// - /// Retrieves the BepInPluginProvider metadata from a plugin type. - /// - /// The plugin type. - /// The BepInPluginProvider metadata of the plugin type. - public static BepInPlugin GetPluginProviderMetadata(Type pluginType) - { - var attributes = pluginType.GetCustomAttributes(typeof(BepInPluginProvider), false); - - if (attributes.Length == 0) - return null; - - return (BepInPlugin) attributes[0]; - } - - /// - /// Retrieves the BepInPluginProvider metadata from a plugin instance. - /// - /// The plugin instance. - /// The BepInPluginProvider metadata of the plugin instance. - public static BepInPlugin GetPluginProviderMetadata(object plugin) => GetPluginProviderMetadata(plugin.GetType()); - /// /// Gets the specified attributes of a type, if they exist. /// diff --git a/BepInEx.Core/Contract/BasePluginProvider.cs b/BepInEx.Core/Contract/BasePluginProvider.cs deleted file mode 100644 index 594044a..0000000 --- a/BepInEx.Core/Contract/BasePluginProvider.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using BepInEx.Configuration; -using BepInEx.Logging; - -namespace BepInEx; - -/// -/// The base plugin provider type that is used by the BepInEx plugin loader. -/// -public abstract class BasePluginProvider -{ - /// - /// Create a new instance of a plugin provider and all of its tied in objects. - /// - /// BepInPluginProvider attribute is missing. - protected BasePluginProvider() - { - var metadata = MetadataHelper.GetPluginProviderMetadata(this); - if (metadata == null) - throw new InvalidOperationException("Can't create an instance of " + GetType().FullName + - " because it inherits from BasePluginProvider and the BepInPluginProvider attribute is missing."); - - Info = new PluginInfo - { - Metadata = metadata, - Instance = this, - Dependencies = MetadataHelper.GetDependencies(GetType()), - Processes = MetadataHelper.GetAttributes(GetType()), - }; - - Logger = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); - - Config = new ConfigFile(Utility.CombinePaths(Paths.ConfigPath, metadata.GUID + ".cfg"), false, metadata); - } - - /// - /// Information about this plugin provider as it was loaded. - /// - public PluginInfo Info { get; } - - /// - /// Logger instance tied to this plugin provider. - /// - protected ManualLogSource Logger { get; } - - /// - /// Default config file tied to this plugin provider. The config file will not be created until - /// any settings are added and changed, or is called. - /// - public ConfigFile Config { get; } - - /// - /// Obtains a list of assemblies containing plugins to load - /// - /// A list of load context, one per assembly - public abstract IList GetPlugins(); - - /// - /// A custom assembly resolver that can be used by this provider to resolve assemblies - /// that have failed to resolve - /// - /// The assembly's name - /// The resolved assembly or null if the assembly couldn't be resolved - public virtual Assembly ResolveAssembly(string name) - { - return null; - } -} diff --git a/BepInEx.Core/Contract/IPlugin.cs b/BepInEx.Core/Contract/IPlugin.cs index 150d151..6043fb0 100644 --- a/BepInEx.Core/Contract/IPlugin.cs +++ b/BepInEx.Core/Contract/IPlugin.cs @@ -7,22 +7,22 @@ using BepInEx.Logging; namespace BepInEx.Contract { - public interface IPlugin - { - /// - /// Information about this plugin as it was loaded. - /// - PluginInfo Info { get; } + public interface IPlugin + { + /// + /// Information about this plugin as it was loaded. + /// + PluginInfo Info { get; } - /// - /// Logger instance tied to this plugin. - /// - ManualLogSource Logger { get; } + /// + /// Logger instance tied to this plugin. + /// + ManualLogSource Logger { get; } - /// - /// Default config file tied to this plugin. The config file will not be created until - /// any settings are added and changed, or is called. - /// - ConfigFile Config { get; } - } + /// + /// Default config file tied to this plugin. The config file will not be created until + /// any settings are added and changed, or is called. + /// + ConfigFile Config { get; } + } } diff --git a/BepInEx.Core/Contract/IPluginLoadContext.cs b/BepInEx.Core/Contract/IPluginLoadContext.cs deleted file mode 100644 index 1812ea8..0000000 --- a/BepInEx.Core/Contract/IPluginLoadContext.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; - -namespace BepInEx; - -/// -/// An interface that allows to dynamically load and track an assembly -/// which can be obtained from a provider -/// -public interface IPluginLoadContext -{ - /// - /// An identifier that uniquely identifies an assembly from a provider no matter its revision - /// - public string AssemblyIdentifier { get; } - - /// - /// An optional hash that changes each time the assembly changes which can be tracked for cache - /// invalidation purposes. If this is null, no caching occurs for this assembly load - /// - public string AssemblyHash { get; } - - /// - /// Obtains the assembly's raw data without loading it into the appdomain. - /// This may be called multiple times by the chainloader - /// - /// The assembly's raw data in bytes - public byte[] GetAssemblyData(); - - /// - /// Obtains the assembly's symbols data which will be loaded by the chainloader alongside the assembly - /// - /// The assembly's symbols data in bytes (either in portable pdb or mdb format). Null if no symbols exists for this assembly - public byte[] GetAssemblySymbolsData(); - - /// - /// Obtains a file's raw data using a relative path from the plugin assembly - /// - /// The relative path from the plugin assembly used to locate the file - /// The file's raw data if resolved successfully, null if the file couldn't be loaded - public byte[] GetFile(string relativePath); -} diff --git a/BepInEx.Core/Contract/PluginInfo.cs b/BepInEx.Core/Contract/PluginInfo.cs index 3c5dfc8..0aa7585 100644 --- a/BepInEx.Core/Contract/PluginInfo.cs +++ b/BepInEx.Core/Contract/PluginInfo.cs @@ -16,17 +16,6 @@ public class PluginInfo : ICacheable /// General metadata about a plugin. /// public BepInPlugin Metadata { get; internal set; } - - /// - /// The of the plugin provider that loaded this plugin. - /// This is null if this plugin is a provider - /// - public PluginInfo Source { get; internal set; } - - /// - /// The load context used to load this plugin, null if it is a provider - /// - public IPluginLoadContext LoadContext { get; internal set; } /// /// Collection of attributes that describe what processes the plugin can run on. @@ -44,26 +33,24 @@ public class PluginInfo : ICacheable /// public IEnumerable Incompatibilities { get; internal set; } - public string TypeName { get; internal set; } - + /// + /// File path to the plugin DLL + /// + public string Location { get; internal set; } + /// /// Instance of the plugin that represents this info. NULL if no plugin is instantiated from info (yet) /// public object Instance { get; internal set; } - + + public string TypeName { get; internal set; } + internal Version TargettedBepInExVersion { get; set; } - - [Obsolete("Use LoadContext.AssemblyIdentifier instead", true)] - public string Location => LoadContext?.AssemblyIdentifier; - - internal string _location { get; set; } - - /// - public override string ToString() => $"{Metadata?.Name} {Metadata?.Version}"; void ICacheable.Save(BinaryWriter bw) { bw.Write(TypeName); + bw.Write(Location); bw.Write(Metadata.GUID); bw.Write(Metadata.Name); @@ -90,6 +77,7 @@ public class PluginInfo : ICacheable void ICacheable.Load(BinaryReader br) { TypeName = br.ReadString(); + Location = br.ReadString(); Metadata = new BepInPlugin(br.ReadString(), br.ReadString(), br.ReadString()); @@ -123,4 +111,7 @@ public class PluginInfo : ICacheable TargettedBepInExVersion = new Version(br.ReadString()); } + + /// + public override string ToString() => $"{Metadata?.Name} {Metadata?.Version}"; } diff --git a/BepInEx.Core/Paths.cs b/BepInEx.Core/Paths.cs index 3371986..6a37342 100644 --- a/BepInEx.Core/Paths.cs +++ b/BepInEx.Core/Paths.cs @@ -75,16 +75,6 @@ public static class Paths /// public static string PatcherPluginPath { get; private set; } - /// - /// The path to the plugin provider folder which resides in the BepInEx folder. - /// - public static string PluginProviderPath { get; private set; } - - /// - /// The path to the patcher plugin provider folder which resides in the BepInEx folder. - /// - public static string PatcherProviderPath { get; private set; } - /// /// The path to the plugin folder which resides in the BepInEx folder. /// @@ -137,9 +127,7 @@ public static class Paths BepInExRootPath = bepinRootPath ?? Path.Combine(GameRootPath, "BepInEx"); ConfigPath = Path.Combine(BepInExRootPath, "config"); BepInExConfigPath = Path.Combine(ConfigPath, "BepInEx.cfg"); - PluginProviderPath = Path.Combine(BepInExRootPath, "plugins-providers"); PluginPath = Path.Combine(BepInExRootPath, "plugins"); - PatcherProviderPath = Path.Combine(BepInExRootPath, "patchers-providers"); PatcherPluginPath = Path.Combine(BepInExRootPath, "patchers"); BepInExAssemblyDirectory = Path.Combine(BepInExRootPath, "core"); BepInExAssemblyPath = Path.Combine(BepInExAssemblyDirectory, diff --git a/BepInEx.Core/Properties/AssemblyInfo.cs b/BepInEx.Core/Properties/AssemblyInfo.cs index 96d56c1..749dcc2 100644 --- a/BepInEx.Core/Properties/AssemblyInfo.cs +++ b/BepInEx.Core/Properties/AssemblyInfo.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("BepInEx.Preloader.Core")] [assembly: InternalsVisibleTo("BepInEx.Unity.Mono")] -[assembly: InternalsVisibleTo("BepInEx.NET.Common")] [assembly: InternalsVisibleTo("BepInEx.NET.Framework.Launcher")] [assembly: InternalsVisibleTo("BepInEx.NET.CoreCLR")] [assembly: InternalsVisibleTo("BepInEx.Unity.IL2CPP")] diff --git a/BepInEx.Core/Utility.cs b/BepInEx.Core/Utility.cs index 08e6c35..a6b93bd 100644 --- a/BepInEx.Core/Utility.cs +++ b/BepInEx.Core/Utility.cs @@ -221,53 +221,6 @@ public static class Utility return false; } - /// - /// Try to resolve the symbols data from an assembly DLL. - /// - /// File path of the DLL assembly. - /// The symbols of the DLL assembly. - /// True, if the symbols were found. Otherwise, false. - public static bool TryResolveAssemblySymbols(string dllPath, out byte[] symbolsData) - { - var subDirectory = Path.GetDirectoryName(dllPath); - - var potentialSymbolsFileNames = new[] - { - $"{dllPath}.mdb", - Path.ChangeExtension(dllPath, ".pdb") - }; - - foreach (var symbolFileName in potentialSymbolsFileNames) - { - var symbolPath = Path.Combine(subDirectory, symbolFileName); - if (!File.Exists(symbolPath)) - continue; - - try - { - symbolsData = File.ReadAllBytes(symbolPath); - } - catch (Exception) - { - continue; - } - - return true; - } - - symbolsData = null; - return false; - } - - private static Assembly LoadAssemblyWithSymbols(string assemblyFilePath) - { - var assemblyData = File.ReadAllBytes(assemblyFilePath); - - return TryResolveAssemblySymbols(assemblyFilePath, out byte[] symbolsData) - ? Assembly.Load(assemblyData, symbolsData) - : Assembly.Load(assemblyData); - } - /// /// Checks whether a given cecil type definition is a subtype of a provided type. /// @@ -306,16 +259,6 @@ public static class Utility TryResolveDllAssembly(assemblyName, directory, s => AssemblyDefinition.ReadAssembly(s, readerParameters), out assembly); - /// - /// Try to resolve and load the given assembly DLL including their symbols file. - /// - /// Name of the assembly, of the type . - /// Directory to search the assembly from. - /// The loaded assembly. - /// True, if the assembly was found and loaded. Otherwise, false. - public static bool TryResolveDllAssemblyWithSymbols(AssemblyName assemblyName, string directory, out Assembly assembly) => - TryResolveDllAssembly(assemblyName, directory, LoadAssemblyWithSymbols, out assembly); - /// /// Tries to create a file with the given name /// diff --git a/BepInEx.PatcherProvider/BepInEx.PatcherProvider.csproj b/BepInEx.PatcherProvider/BepInEx.PatcherProvider.csproj deleted file mode 100644 index c37d3e6..0000000 --- a/BepInEx.PatcherProvider/BepInEx.PatcherProvider.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net35;netstandard2.0 - true - BepInEx patcher provider module - false - - - - - - - - - - diff --git a/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs b/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs deleted file mode 100644 index 9cb4cbc..0000000 --- a/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.IO; - -namespace BepInEx.PatcherProvider; - -internal class BepInExPatcherLoadContext : IPluginLoadContext -{ - public string AssemblyIdentifier { get; internal set; } - public string AssemblyHash { get; internal set; } - public byte[] GetAssemblyData() - { - return File.ReadAllBytes(AssemblyIdentifier); - } - - public byte[] GetAssemblySymbolsData() - { - if (Utility.TryResolveAssemblySymbols(AssemblyIdentifier, out var assemblySymbolsData)) - return assemblySymbolsData; - - return assemblySymbolsData; - } - - public byte[] GetFile(string relativePath) - { - if (relativePath == null) - throw new ArgumentNullException(nameof(relativePath)); - - string assemblyFolder = Path.GetDirectoryName(AssemblyIdentifier); - string filePath = Path.Combine(assemblyFolder, relativePath); - return File.ReadAllBytes(filePath); - } -} diff --git a/BepInEx.PatcherProvider/BepInExPatcherProvider.cs b/BepInEx.PatcherProvider/BepInExPatcherProvider.cs index 7685874..5f28270 100644 --- a/BepInEx.PatcherProvider/BepInExPatcherProvider.cs +++ b/BepInEx.PatcherProvider/BepInExPatcherProvider.cs @@ -1,70 +1 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using BepInEx.Logging; -using BepInEx.Preloader.Core.Patching; - -namespace BepInEx.PatcherProvider; - -[PatcherProviderPluginInfo("BepInExPatcherProvider", "BepInExPatcherProvider", "1.0")] -internal class BepInExPatcherProvider : BasePatcherProvider -{ - private static readonly Dictionary AssemblyLocationsByFilename = new(); - - public override IList GetPatchers() - { - var loadContexts = new List(); - foreach (var dll in Directory.GetFiles(Path.GetFullPath(Paths.PatcherPluginPath), "*.dll", SearchOption.AllDirectories)) - { - try - { - var filename = Path.GetFileNameWithoutExtension(dll); - var foundDirectory = Path.GetDirectoryName(dll); - - // Prioritize the shallowest path of each assembly name - if (AssemblyLocationsByFilename.TryGetValue(filename, out var existingDirectory)) - { - int levelExistingDirectory = existingDirectory?.Count(x => x == Path.DirectorySeparatorChar) ?? 0; - int levelFoundDirectory = foundDirectory?.Count(x => x == Path.DirectorySeparatorChar) ?? 0; - - bool shallowerPathFound = levelExistingDirectory > levelFoundDirectory; - Log.LogWarning($"Found duplicate assemblies filenames: {filename} was found at {foundDirectory} " + - $"while it exists already at {AssemblyLocationsByFilename[filename]}. " + - $"Only the {(shallowerPathFound ? "first" : "second")} will be examined and resolved"); - - if (levelExistingDirectory > levelFoundDirectory) - AssemblyLocationsByFilename[filename] = foundDirectory; - } - else - { - AssemblyLocationsByFilename.Add(filename, foundDirectory); - } - - loadContexts.Add(new BepInExPatcherLoadContext - { - AssemblyHash = File.GetLastWriteTimeUtc(dll).ToString("O"), - AssemblyIdentifier = dll - }); - } - catch (Exception e) - { - Log.Log(LogLevel.Error, e); - } - } - - return loadContexts; - } - - public override Assembly ResolveAssembly(string name) - { - if (!AssemblyLocationsByFilename.TryGetValue(name, out var location)) - return null; - - if (!Utility.TryResolveDllAssemblyWithSymbols(new(name), location, out var ass)) - return null; - - return ass; - } -} + \ No newline at end of file diff --git a/BepInEx.PluginProvider/BepInEx.PluginProvider.csproj b/BepInEx.PluginProvider/BepInEx.PluginProvider.csproj deleted file mode 100644 index e22ed74..0000000 --- a/BepInEx.PluginProvider/BepInEx.PluginProvider.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net35;netstandard2.0 - true - BepInEx plugin provider module - false - - - - - - - - - - diff --git a/BepInEx.PluginProvider/BepInExPluginLoadContext.cs b/BepInEx.PluginProvider/BepInExPluginLoadContext.cs deleted file mode 100644 index 354e972..0000000 --- a/BepInEx.PluginProvider/BepInExPluginLoadContext.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.IO; - -namespace BepInEx.PluginProvider; - -internal class BepInExPluginLoadContext : IPluginLoadContext -{ - public string AssemblyIdentifier { get; internal set; } - public string AssemblyHash { get; internal set; } - public byte[] GetAssemblyData() - { - return File.ReadAllBytes(AssemblyIdentifier); - } - - public byte[] GetAssemblySymbolsData() - { - if (Utility.TryResolveAssemblySymbols(AssemblyIdentifier, out var assemblySymbolsData)) - return assemblySymbolsData; - - return assemblySymbolsData; - } - - public byte[] GetFile(string relativePath) - { - if (relativePath == null) - throw new ArgumentNullException(nameof(relativePath)); - - string assemblyFolder = Path.GetDirectoryName(AssemblyIdentifier); - string filePath = Path.Combine(assemblyFolder, relativePath); - return File.ReadAllBytes(filePath); - } -} diff --git a/BepInEx.PluginProvider/BepInExPluginProvider.cs b/BepInEx.PluginProvider/BepInExPluginProvider.cs index 6f134ab..5f28270 100644 --- a/BepInEx.PluginProvider/BepInExPluginProvider.cs +++ b/BepInEx.PluginProvider/BepInExPluginProvider.cs @@ -1,69 +1 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using BepInEx.Logging; - -namespace BepInEx.PluginProvider; - -[BepInPluginProvider("BepInExPluginProvider", "BepInExPluginProvider", "1.0")] -internal class BepInExPluginProvider : BasePluginProvider -{ - private static readonly Dictionary AssemblyLocationsByFilename = new(); - - public override IList GetPlugins() - { - var loadContexts = new List(); - foreach (var dll in Directory.GetFiles(Path.GetFullPath(Paths.PluginPath), "*.dll", SearchOption.AllDirectories)) - { - try - { - var filename = Path.GetFileNameWithoutExtension(dll); - var foundDirectory = Path.GetDirectoryName(dll); - - // Prioritize the shallowest path of each assembly name - if (AssemblyLocationsByFilename.TryGetValue(filename, out var existingDirectory)) - { - int levelExistingDirectory = existingDirectory?.Count(x => x == Path.DirectorySeparatorChar) ?? 0; - int levelFoundDirectory = foundDirectory?.Count(x => x == Path.DirectorySeparatorChar) ?? 0; - - bool shallowerPathFound = levelExistingDirectory > levelFoundDirectory; - Logger.LogWarning($"Found duplicate assemblies filenames: {filename} was found at {foundDirectory} " + - $"while it exists already at {AssemblyLocationsByFilename[filename]}. " + - $"Only the {(shallowerPathFound ? "first" : "second")} will be examined and resolved"); - - if (levelExistingDirectory > levelFoundDirectory) - AssemblyLocationsByFilename[filename] = foundDirectory; - } - else - { - AssemblyLocationsByFilename.Add(filename, foundDirectory); - } - - loadContexts.Add(new BepInExPluginLoadContext - { - AssemblyHash = File.GetLastWriteTimeUtc(dll).ToString("O"), - AssemblyIdentifier = dll - }); - } - catch (Exception e) - { - Logger.Log(LogLevel.Error, e); - } - } - - return loadContexts; - } - - public override Assembly ResolveAssembly(string name) - { - if (!AssemblyLocationsByFilename.TryGetValue(name, out var location)) - return null; - - if (!Utility.TryResolveDllAssemblyWithSymbols(new(name), location, out var ass)) - return null; - - return ass; - } -} + \ No newline at end of file diff --git a/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs b/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs index 3022e6a..ad25d31 100644 --- a/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs +++ b/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs @@ -11,7 +11,6 @@ using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Mono.Cecil; -using MonoMod.Utils; namespace BepInEx.Preloader.Core.Patching; @@ -25,18 +24,9 @@ public class AssemblyPatcher : IDisposable private Func assemblyLoader; - private readonly List loadContexts = new(); - private readonly List providers = new(); - public AssemblyPatcher(Func assemblyLoader) { this.assemblyLoader = assemblyLoader; - - if (!Directory.Exists(Paths.PatcherProviderPath)) - Directory.CreateDirectory(Paths.PatcherProviderPath); - - if (!Directory.Exists(Paths.PatcherPluginPath)) - Directory.CreateDirectory(Paths.PatcherPluginPath); } /// @@ -71,18 +61,16 @@ public class AssemblyPatcher : IDisposable // Clear to allow GC collection. PatcherContext.PatcherPlugins.Clear(); - - AppDomain.CurrentDomain.AssemblyResolve -= PatcherProvidersAssemblyResolver; } - private PatcherPluginMetadata ToPatcherPlugin(TypeDefinition type, IPluginLoadContext loadContext, string assemblyPath) + private PatcherPluginMetadata ToPatcherPlugin(TypeDefinition type, string assemblyPath) { if (type.IsInterface || type.IsAbstract && !type.IsSealed) return null; try { - if (!type.IsSubtypeOf(typeof(T))) + if (!type.IsSubtypeOf(typeof(BasePatcher))) return null; } catch (AssemblyResolutionException) @@ -91,13 +79,8 @@ public class AssemblyPatcher : IDisposable return null; } - if (type.IsSubtypeOf(typeof(BasePatcherProvider))) - return GetPatcherInfo(type, loadContext, PatcherProviderPluginInfoAttribute.FromCecilType(type)); - return GetPatcherInfo(type, loadContext, PatcherPluginInfoAttribute.FromCecilType(type)); - } + var metadata = PatcherPluginInfoAttribute.FromCecilType(type); - private PatcherPluginMetadata GetPatcherInfo(TypeDefinition type, IPluginLoadContext loadContext, PatcherPluginInfoAttribute metadata) - { // Perform checks that will prevent the plugin from being loaded in ALL cases if (metadata == null) { @@ -126,176 +109,20 @@ public class AssemblyPatcher : IDisposable return new PatcherPluginMetadata { - TypeName = type.FullName, - LoadContext = loadContext + TypeName = type.FullName }; } - private bool HasPatcherType(AssemblyDefinition ass) + private bool HasPatcherPlugins(AssemblyDefinition ass) { if (ass.MainModule.AssemblyReferences.All(r => r.Name != CurrentAssemblyName) && ass.Name.Name != CurrentAssemblyName) return false; - if (ass.MainModule.GetTypeReferences().All(r => r.FullName != typeof(T).FullName)) + if (ass.MainModule.GetTypeReferences().All(r => r.FullName != typeof(BasePatcher).FullName)) return false; return true; } - - /// - /// Adds all patchers from all managed assemblies obtained from all patcher providers. - /// - public void AddPatchersFromProviders() - { - var allProviders = TypeLoader.FindPluginTypes(Paths.BepInExAssemblyDirectory, ToPatcherPlugin, HasPatcherType); - var foundProviders = TypeLoader.FindPluginTypes(Paths.PatcherProviderPath, ToPatcherPlugin, HasPatcherType); - allProviders.AddRange(foundProviders); - AppDomain.CurrentDomain.AssemblyResolve += PatcherProvidersAssemblyResolver; - - foreach (var keyValuePair in allProviders) - { - var assemblyPath = keyValuePair.Key; - var patcherCollection = keyValuePair.Value; - - if (patcherCollection.Count == 0) - continue; - - var ass = Assembly.LoadFrom(assemblyPath); - - foreach (var patcherPlugin in patcherCollection) - { - try - { - var type = ass.GetType(patcherPlugin.TypeName); - - var instance = (BasePatcherProvider) Activator.CreateInstance(type); - providers.Add(instance); - loadContexts.AddRange(instance.GetPatchers().Select(x => new CachedPluginLoadContext(x))); - } - catch (Exception e) - { - Logger.Log(LogLevel.Error, - $"Failed to load patchers from type [{patcherPlugin.TypeName}]: {(e is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : e.ToString())}"); - } - } - } - - var patchers = TypeLoader.GetPluginsFromLoadContexts(loadContexts.Cast(), ToPatcherPlugin, HasPatcherType); - - var sortedPatchers = new List(); - - // TODO: Add dependency ordering and process attribute filtering - - foreach (var patcherPlugin in patchers) - { - var ass = Assembly.Load(patcherPlugin.LoadContext.GetAssemblyData()); - var patchDefinitions = LoadPatcherPlugin(ass, patcherPlugin); - sortedPatchers.AddRange(patchDefinitions); - - var assName = ass.GetName(); - Logger.Log(LogLevel.Debug, $"Loaded patcher {patcherPlugin.TypeName} from [{assName.Name} {assName.Version}]"); - } - - PatcherContext.PatchDefinitions.AddRange(sortedPatchers); - Logger.Log(LogLevel.Info, $"Loaded {sortedPatchers.Count} patcher{(sortedPatchers.Count > 0 ? "s" : "")}"); - - foreach (CachedPluginLoadContext loadContext in loadContexts) - { - loadContext.Dispose(); - } - } - - private Assembly PatcherProvidersAssemblyResolver(object sender, ResolveEventArgs args) - { - BasePatcherProvider currentProvider = null; - try - { - foreach (BasePatcherProvider provider in providers) - { - currentProvider = provider; - var ass = provider.ResolveAssembly(args.Name); - if (ass != null) - return ass; - } - } - catch (Exception e) - { - Logger.Log(LogLevel.Error, $"The assembly resolver of the patcher provider [{currentProvider?.Info.GUID}] " + - $"threw an exception while resolving {args.Name}: {e}"); - return null; - } - - return null; - } - - private List LoadPatcherPlugin(Assembly ass, PatcherPluginMetadata patcher) - { - var patchDefinitions = new List(); - try - { - var type = ass.GetType(patcher.TypeName); - - var instance = (BasePatcher) Activator.CreateInstance(type); - instance.Context = PatcherContext; - - PatcherContext.PatcherPlugins.Add(instance); - - var methods = - type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - - foreach (var method in methods) - { - var targetAssemblies = MetadataHelper.GetAttributes(method); - var targetTypes = MetadataHelper.GetAttributes(method); - - if (targetAssemblies.Length == 0 && targetTypes.Length == 0) - continue; - - var parameters = method.GetParameters(); - - if (parameters.Length < 1 || parameters.Length > 2 - // Next few lines ensure that the first parameter is AssemblyDefinition and does not have any - // target type attributes, and vice versa - || !( - parameters[0].ParameterType == typeof(AssemblyDefinition) - || parameters[0].ParameterType == - typeof(AssemblyDefinition).MakeByRefType() - && targetTypes.Length == 0 - || parameters[0].ParameterType == typeof(TypeDefinition) - && targetAssemblies.Length == 0 - ) - || parameters.Length == 2 && - parameters[1].ParameterType != typeof(string) - || method.ReturnType != typeof(void) && - method.ReturnType != typeof(bool) - ) - { - Logger - .Log(LogLevel.Warning, - $"Skipping method [{method.FullDescription()}] as it is not a valid patcher method"); - continue; - } - - void AddDefinition(PatchDefinition definition) - { - Logger.Log(LogLevel.Debug, $"Discovered patch [{definition.FullName}]"); - patchDefinitions.Add(definition); - } - - foreach (var targetAssembly in targetAssemblies) - AddDefinition(new PatchDefinition(targetAssembly, instance, method)); - foreach (var targetType in targetTypes) - AddDefinition(new PatchDefinition(targetType, instance, method)); - } - } - catch (Exception e) - { - Logger.Log(LogLevel.Error, - $"Failed to load patchers from type [{patcher.TypeName}]: {(e is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : e.ToString())}"); - } - - return patchDefinitions; - } /// /// Adds all patchers from all managed assemblies specified in a directory. @@ -308,7 +135,7 @@ public class AssemblyPatcher : IDisposable var sortedPatchers = new List(); - var patchers = TypeLoader.FindPluginTypes(directory, ToPatcherPlugin, HasPatcherType); + var patchers = TypeLoader.FindPluginTypes(directory, ToPatcherPlugin, HasPatcherPlugins); // TODO: Add dependency ordering and process attribute filtering @@ -323,10 +150,68 @@ public class AssemblyPatcher : IDisposable var ass = Assembly.LoadFrom(assemblyPath); foreach (var patcherPlugin in patcherCollection) - { - var patchDefinitions = LoadPatcherPlugin(ass, patcherPlugin); - sortedPatchers.AddRange(patchDefinitions); - } + try + { + var type = ass.GetType(patcherPlugin.TypeName); + + var instance = (BasePatcher) Activator.CreateInstance(type); + instance.Context = PatcherContext; + + PatcherContext.PatcherPlugins.Add(instance); + + var methods = + type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + foreach (var method in methods) + { + var targetAssemblies = MetadataHelper.GetAttributes(method); + var targetTypes = MetadataHelper.GetAttributes(method); + + if (targetAssemblies.Length == 0 && targetTypes.Length == 0) + continue; + + var parameters = method.GetParameters(); + + if (parameters.Length < 1 || parameters.Length > 2 + // Next few lines ensure that the first parameter is AssemblyDefinition and does not have any + // target type attributes, and vice versa + || !( + parameters[0].ParameterType == typeof(AssemblyDefinition) + || parameters[0].ParameterType == + typeof(AssemblyDefinition).MakeByRefType() + && targetTypes.Length == 0 + || parameters[0].ParameterType == typeof(TypeDefinition) + && targetAssemblies.Length == 0 + ) + || parameters.Length == 2 && + parameters[1].ParameterType != typeof(string) + || method.ReturnType != typeof(void) && + method.ReturnType != typeof(bool) + ) + { + Logger + .Log(LogLevel.Warning, + $"Skipping method [{method.FullDescription()}] as it is not a valid patcher method"); + continue; + } + + void AddDefinition(PatchDefinition definition) + { + Logger.Log(LogLevel.Debug, $"Discovered patch [{definition.FullName}]"); + sortedPatchers.Add(definition); + } + + foreach (var targetAssembly in targetAssemblies) + AddDefinition(new PatchDefinition(targetAssembly, instance, method)); + foreach (var targetType in targetTypes) + AddDefinition(new PatchDefinition(targetType, instance, method)); + } + } + catch (Exception e) + { + Logger.Log(LogLevel.Error, + $"Failed to load patchers from type [{patcherPlugin.TypeName}]: {(e is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : e.ToString())}"); + } var assName = ass.GetName(); Logger.Log(patcherCollection.Any() ? LogLevel.Info : LogLevel.Debug, diff --git a/BepInEx.Preloader.Core/Patching/Attributes.cs b/BepInEx.Preloader.Core/Patching/Attributes.cs index 1b2ccab..95b21c4 100644 --- a/BepInEx.Preloader.Core/Patching/Attributes.cs +++ b/BepInEx.Preloader.Core/Patching/Attributes.cs @@ -79,31 +79,6 @@ public class PatcherPluginInfoAttribute : Attribute } } -/// -/// This attribute denotes that a class is a patcher plugin provider, and specifies the required metadata. -/// -[AttributeUsage(AttributeTargets.Class)] -public class PatcherProviderPluginInfoAttribute : PatcherPluginInfoAttribute { - /// The unique identifier of the plugin. Should not change between plugin versions. - /// The user friendly name of the plugin. Is able to be changed between versions. - /// The specific version of the plugin. - public PatcherProviderPluginInfoAttribute(string GUID, string Name, string Version) : base(GUID, Name, Version) - { - } - - internal new static PatcherProviderPluginInfoAttribute FromCecilType(TypeDefinition td) - { - var attr = MetadataHelper.GetCustomAttributes(td, false).FirstOrDefault(); - - if (attr == null) - return null; - - return new PatcherProviderPluginInfoAttribute((string) attr.ConstructorArguments[0].Value, - (string) attr.ConstructorArguments[1].Value, - (string) attr.ConstructorArguments[2].Value); - } -} - /// /// Defines an assembly that a patch method will target. /// diff --git a/BepInEx.Preloader.Core/Patching/BasePatcherProvider.cs b/BepInEx.Preloader.Core/Patching/BasePatcherProvider.cs deleted file mode 100644 index 08c3503..0000000 --- a/BepInEx.Preloader.Core/Patching/BasePatcherProvider.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using BepInEx.Configuration; -using BepInEx.Logging; - -namespace BepInEx.Preloader.Core.Patching; - -/// -/// A patcher provider that can discover and load assemblies containing patcher plugins -/// -public abstract class BasePatcherProvider -{ - protected BasePatcherProvider() - { - Info = PatcherPluginInfoAttribute.FromType(GetType()); - - Log = Logger.CreateLogSource(Info.Name); - - Config = new ConfigFile(Utility.CombinePaths(Paths.ConfigPath, Info.GUID + ".cfg"), false, - new BepInPlugin(Info.GUID, Info.Name, Info.Version.ToString())); - } - - /// - /// A instance created for use by this patcher plugin provider. - /// - public ManualLogSource Log { get; } - - /// - /// A configuration file binding created with the of this provider as the - /// filename. - /// - public ConfigFile Config { get; } - - /// - /// Metadata associated with this patcher plugin provider. - /// - public PatcherPluginInfoAttribute Info { get; } - - /// - /// Obtains a list of assemblies containing patchers to load - /// - /// A list of load context, one per assembly - public abstract IList GetPatchers(); - - /// - /// A custom assembly resolver that can be used by this provider to resolve assemblies - /// that have failed to resolve - /// - /// The assembly's name - /// The resolved assembly or null if the assembly couldn't be resolved - public virtual Assembly ResolveAssembly(string name) - { - return null; - } -} diff --git a/BepInEx.Preloader.Core/Patching/PatcherPluginMetadata.cs b/BepInEx.Preloader.Core/Patching/PatcherPluginMetadata.cs index ceb88b7..6994dea 100644 --- a/BepInEx.Preloader.Core/Patching/PatcherPluginMetadata.cs +++ b/BepInEx.Preloader.Core/Patching/PatcherPluginMetadata.cs @@ -13,11 +13,6 @@ internal class PatcherPluginMetadata : ICacheable /// public string TypeName { get; set; } = string.Empty; - /// - /// The load context used to load this patcher, null if it is a provider - /// - public IPluginLoadContext LoadContext { get; set; } - /// public void Save(BinaryWriter bw) => bw.Write(TypeName); diff --git a/BepInEx.sln b/BepInEx.sln index e723eb1..356b05d 100644 --- a/BepInEx.sln +++ b/BepInEx.sln @@ -51,10 +51,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Doorstop", "Doorstop", "{CE EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build", "build\Build.csproj", "{0C38DDF2-EAAA-43FA-BA0A-985636AC6040}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.PluginProvider", "BepInEx.PluginProvider\BepInEx.PluginProvider.csproj", "{CBE495BC-0258-4F36-8B3D-1CA64D97E90A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.PatcherProvider", "BepInEx.PatcherProvider\BepInEx.PatcherProvider.csproj", "{F88995B3-A5A2-4FC1-A11B-E2AA17483B20}" -EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution BepInEx.NetLauncher.Shared\BepInEx.NetLauncher.Shared.projitems*{067bcc8c-fc25-4929-b4f4-c6a5a7496994}*SharedItemsImports = 13 @@ -151,22 +147,6 @@ Global {0C38DDF2-EAAA-43FA-BA0A-985636AC6040}.Release_Unity|Any CPU.ActiveCfg = Debug|Any CPU {0C38DDF2-EAAA-43FA-BA0A-985636AC6040}.Release_Unity|Any CPU.Build.0 = Debug|Any CPU {0C38DDF2-EAAA-43FA-BA0A-985636AC6040}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Release_NetFramework|Any CPU.ActiveCfg = Debug|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Release_NetFramework|Any CPU.Build.0 = Debug|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Release_Unity|Any CPU.ActiveCfg = Debug|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Release_Unity|Any CPU.Build.0 = Debug|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CBE495BC-0258-4F36-8B3D-1CA64D97E90A}.Release|Any CPU.Build.0 = Release|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Release_NetFramework|Any CPU.ActiveCfg = Debug|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Release_NetFramework|Any CPU.Build.0 = Debug|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Release_Unity|Any CPU.ActiveCfg = Debug|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Release_Unity|Any CPU.Build.0 = Debug|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F88995B3-A5A2-4FC1-A11B-E2AA17483B20}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs b/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs index d9b375a..5d40b10 100644 --- a/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs +++ b/Runtimes/NET/BepInEx.NET.Common/BasePlugin.cs @@ -1,5 +1,3 @@ -using System; -using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; @@ -14,28 +12,13 @@ namespace BepInEx.NET.Common HarmonyInstance = new Harmony("BepInEx.Plugin." + metadata.GUID); - if (BaseChainloader.Instance.Plugins.TryGetValue(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"); - } - - Log = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); + Log = Logger.CreateLogSource(metadata.Name); Config = new ConfigFile(Utility.CombinePaths(Paths.ConfigPath, metadata.GUID + ".cfg"), false, metadata); } - - /// - public PluginInfo Info { get; } - - /// + public ManualLogSource Log { get; } - /// public ConfigFile Config { get; } public Harmony HarmonyInstance { get; set; } diff --git a/Runtimes/NET/BepInEx.NET.Common/BepInEx.NET.Common.csproj b/Runtimes/NET/BepInEx.NET.Common/BepInEx.NET.Common.csproj index f715bf1..6884e40 100644 --- a/Runtimes/NET/BepInEx.NET.Common/BepInEx.NET.Common.csproj +++ b/Runtimes/NET/BepInEx.NET.Common/BepInEx.NET.Common.csproj @@ -12,18 +12,6 @@ - - false - None - PreserveNewest - Build;DocumentationProjectOutputGroup - - - false - None - PreserveNewest - Build;DocumentationProjectOutputGroup - diff --git a/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs b/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs index 9f9717c..5e4af66 100644 --- a/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs +++ b/Runtimes/NET/BepInEx.NET.Common/NetChainloader.cs @@ -7,6 +7,9 @@ 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/NET/BepInEx.NET.CoreCLR/NetCorePreloader.cs b/Runtimes/NET/BepInEx.NET.CoreCLR/NetCorePreloader.cs index 3448cdb..90e7aa7 100644 --- a/Runtimes/NET/BepInEx.NET.CoreCLR/NetCorePreloader.cs +++ b/Runtimes/NET/BepInEx.NET.CoreCLR/NetCorePreloader.cs @@ -54,7 +54,7 @@ namespace BepInEx.NET.CoreCLR using (var assemblyPatcher = new AssemblyPatcher((data, _) => Assembly.Load(data))) { - assemblyPatcher.AddPatchersFromProviders(); + assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.LogInfo($"{assemblyPatcher.PatcherContext.PatchDefinitions.Count} patcher definition(s) loaded"); diff --git a/Runtimes/NET/BepInEx.NET.Framework.Launcher/NetPreloader.cs b/Runtimes/NET/BepInEx.NET.Framework.Launcher/NetPreloader.cs index 51fbe08..f7396bb 100644 --- a/Runtimes/NET/BepInEx.NET.Framework.Launcher/NetPreloader.cs +++ b/Runtimes/NET/BepInEx.NET.Framework.Launcher/NetPreloader.cs @@ -110,7 +110,7 @@ public static class NetPreloader using (var assemblyPatcher = new AssemblyPatcher((data, _) => Assembly.Load(data))) { - assemblyPatcher.AddPatchersFromProviders(); + assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.PatchDefinitions.Count} patcher definition(s) loaded"); diff --git a/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs b/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs index 1a0fda3..3b51e33 100644 --- a/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs +++ b/Runtimes/Unity/BepInEx.Unity.IL2CPP/BasePlugin.cs @@ -1,5 +1,4 @@ -using System; -using BepInEx.Configuration; +using BepInEx.Configuration; using BepInEx.Logging; using Il2CppInterop.Runtime.InteropTypes; @@ -11,23 +10,11 @@ public abstract class BasePlugin { var metadata = MetadataHelper.GetMetadata(this); - if (IL2CPPChainloader.Instance.Plugins.TryGetValue(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"); - } - - Log = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); + Log = Logger.CreateLogSource(metadata.Name); Config = new ConfigFile(Utility.CombinePaths(Paths.ConfigPath, metadata.GUID + ".cfg"), false, metadata); } - public PluginInfo Info { get; } - public ManualLogSource Log { get; } public ConfigFile Config { get; } diff --git a/Runtimes/Unity/BepInEx.Unity.IL2CPP/BepInEx.Unity.IL2CPP.csproj b/Runtimes/Unity/BepInEx.Unity.IL2CPP/BepInEx.Unity.IL2CPP.csproj index c92c6b8..bfc326a 100644 --- a/Runtimes/Unity/BepInEx.Unity.IL2CPP/BepInEx.Unity.IL2CPP.csproj +++ b/Runtimes/Unity/BepInEx.Unity.IL2CPP/BepInEx.Unity.IL2CPP.csproj @@ -9,18 +9,6 @@ - - false - None - PreserveNewest - Build;DocumentationProjectOutputGroup - - - false - None - PreserveNewest - Build;DocumentationProjectOutputGroup - diff --git a/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs b/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs index bc130b6..1ccfb9e 100644 --- a/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs +++ b/Runtimes/Unity/BepInEx.Unity.IL2CPP/IL2CPPChainloader.cs @@ -32,11 +32,7 @@ public class IL2CPPChainloader : BaseChainloader private static INativeDetour RuntimeInvokeDetour { get; set; } - public static new IL2CPPChainloader Instance - { - get => (IL2CPPChainloader)BaseChainloader.Instance; - set => BaseChainloader.Instance = value; - } + public static IL2CPPChainloader Instance { get; set; } /// /// Register and add a Unity Component (for example MonoBehaviour) into BepInEx global manager. diff --git a/Runtimes/Unity/BepInEx.Unity.IL2CPP/Preloader.cs b/Runtimes/Unity/BepInEx.Unity.IL2CPP/Preloader.cs index 0f4f52e..690d4ef 100644 --- a/Runtimes/Unity/BepInEx.Unity.IL2CPP/Preloader.cs +++ b/Runtimes/Unity/BepInEx.Unity.IL2CPP/Preloader.cs @@ -67,7 +67,7 @@ public static class Preloader using (var assemblyPatcher = new AssemblyPatcher((data, _) => Assembly.Load(data))) { - assemblyPatcher.AddPatchersFromProviders(); + assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.LogInfo($"{assemblyPatcher.PatcherContext.PatcherPlugins.Count} patcher plugin{(assemblyPatcher.PatcherContext.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); diff --git a/Runtimes/Unity/BepInEx.Unity.Mono.Preloader/UnityPreloader.cs b/Runtimes/Unity/BepInEx.Unity.Mono.Preloader/UnityPreloader.cs index b6b2a42..6cc3b33 100644 --- a/Runtimes/Unity/BepInEx.Unity.Mono.Preloader/UnityPreloader.cs +++ b/Runtimes/Unity/BepInEx.Unity.Mono.Preloader/UnityPreloader.cs @@ -77,7 +77,7 @@ internal static class UnityPreloader using (var assemblyPatcher = new AssemblyPatcher(MonoAssemblyHelper.LoadFromMemory)) { assemblyPatcher.AddPatchersFromDirectory(Paths.BepInExAssemblyDirectory); - assemblyPatcher.AddPatchersFromProviders(); + assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.PatcherPlugins.Count} patcher plugin{(assemblyPatcher.PatcherContext.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); diff --git a/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs b/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs index 21fde4f..4043771 100644 --- a/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs +++ b/Runtimes/Unity/BepInEx.Unity.Mono/BaseUnityPlugin.cs @@ -1,5 +1,4 @@ using System; -using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using UnityEngine; @@ -22,15 +21,14 @@ public abstract class BaseUnityPlugin : MonoBehaviour throw new InvalidOperationException("Can't create an instance of " + GetType().FullName + " because it inherits from BaseUnityPlugin and the BepInPlugin attribute is missing."); - if (BaseChainloader.Instance.Plugins.TryGetValue(metadata.GUID, out var pluginInfo)) + Info = new PluginInfo { - Info = pluginInfo; - Info.Instance = this; - } - else - { - throw new InvalidOperationException($"The plugin information for {metadata.GUID} couldn't be found on the chainloader"); - } + Metadata = metadata, + Instance = this, + Dependencies = MetadataHelper.GetDependencies(GetType()), + Processes = MetadataHelper.GetAttributes(GetType()), + Location = GetType().Assembly.Location + }; Logger = BepInEx.Logging.Logger.CreateLogSource(metadata.Name); @@ -45,7 +43,7 @@ public abstract class BaseUnityPlugin : MonoBehaviour /// /// Logger instance tied to this plugin. /// - public ManualLogSource Logger { get; } + protected ManualLogSource Logger { get; } /// /// Default config file tied to this plugin. The config file will not be created until diff --git a/Runtimes/Unity/BepInEx.Unity.Mono/BepInEx.Unity.Mono.csproj b/Runtimes/Unity/BepInEx.Unity.Mono/BepInEx.Unity.Mono.csproj index 558ed33..81b0d72 100644 --- a/Runtimes/Unity/BepInEx.Unity.Mono/BepInEx.Unity.Mono.csproj +++ b/Runtimes/Unity/BepInEx.Unity.Mono/BepInEx.Unity.Mono.csproj @@ -11,18 +11,6 @@ - - false - None - PreserveNewest - Build;DocumentationProjectOutputGroup - - - false - None - PreserveNewest - Build;DocumentationProjectOutputGroup - diff --git a/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs b/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs index 19769e8..c08a8de 100644 --- a/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs +++ b/Runtimes/Unity/BepInEx.Unity.Mono/Bootstrap/UnityChainloader.cs @@ -37,17 +37,14 @@ 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. /// public static GameObject ManagerObject { get; private set; } - public static new UnityChainloader Instance - { - get => (UnityChainloader)BaseChainloader.Instance; - set => BaseChainloader.Instance = value; - } - protected override string ConsoleTitle => _consoleTitle; @@ -73,9 +70,9 @@ public class UnityChainloader : BaseChainloader Logger.Log(LogLevel.Debug, "Entering chainloader StaticStart"); - Instance = new UnityChainloader(); - Instance.Initialize(gameExePath); - Instance.Execute(); + var instance = new UnityChainloader(); + instance.Initialize(gameExePath); + instance.Execute(); Logger.Log(LogLevel.Debug, "Exiting chainloader StaticStart"); } diff --git a/build/Program.cs b/build/Program.cs index ab63c15..0ba5f0a 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -239,10 +239,8 @@ public sealed class MakeDistTask : FrostingTask var bepInExCoreDir = bepInExDir.Combine("core"); ctx.CreateDirectory(bepInExDir); ctx.CreateDirectory(bepInExCoreDir); - ctx.CreateDirectory(bepInExDir.Combine("plugins-providers")); ctx.CreateDirectory(bepInExDir.Combine("plugins")); ctx.CreateDirectory(bepInExDir.Combine("patchers")); - ctx.CreateDirectory(bepInExDir.Combine("patchers-providers")); File.WriteAllText(targetDir.CombineWithFilePath("changelog.txt").FullPath, changelog);