diff --git a/BepInEx/Bootstrap/Chainloader.cs b/BepInEx/Bootstrap/Chainloader.cs index babc88e..667a7e4 100644 --- a/BepInEx/Bootstrap/Chainloader.cs +++ b/BepInEx/Bootstrap/Chainloader.cs @@ -79,6 +79,44 @@ namespace BepInEx.Bootstrap _initialized = true; } + private static PluginInfo ToPluginInfo(TypeDefinition type) + { + if (type.IsInterface || type.IsAbstract || !type.IsSubtypeOf(typeof(BaseUnityPlugin))) + return null; + + var metadata = BepInPlugin.FromCecilType(type); + + if (metadata == null) + { + Logger.LogWarning($"Skipping over type [{type.Name}] as no metadata attribute is specified"); + return null; + } + + //Perform a filter for currently running process + var filters = BepInProcess.FromCecilType(type); + + bool invalidProcessName = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == Paths.ProcessName); + + if (invalidProcessName) + { + Logger.LogWarning($"Skipping over plugin [{metadata.GUID}] due to process filter"); + return null; + } + + var dependencies = BepInDependency.FromCecilType(type); + + Logger.LogInfo($"Type path: {type.Module.FileName}"); + + return new PluginInfo + { + Metadata = metadata, + Processes = filters, + Dependencies = dependencies, + CecilType = type, + Location = type.Module.FileName + }; + } + /// /// The entrypoint for the BepInEx plugin system. /// @@ -108,7 +146,7 @@ namespace BepInEx.Bootstrap UnityEngine.Object.DontDestroyOnLoad(ManagerObject); - var pluginsToLoad = TypeLoader.FindPluginTypes(Paths.PluginPath); + var pluginsToLoad = TypeLoader.FindPluginTypes(Paths.PluginPath, ToPluginInfo); var pluginInfos = pluginsToLoad.SelectMany(p => p.Value).ToList(); @@ -184,8 +222,8 @@ namespace BepInEx.Bootstrap if (missingDependencies.Count != 0) { Logger.LogError($@"Missing the following dependencies for [{pluginInfo.Metadata.Name}]: {"\r\n"}{ - string.Join("\r\n", missingDependencies.Select(s => $"- {s}").ToArray()) - }{"\r\n"}Loading will be skipped; expect further errors and unstabilities."); + string.Join("\r\n", missingDependencies.Select(s => $"- {s}").ToArray()) + }{"\r\n"}Loading will be skipped; expect further errors and unstabilities."); invalidPlugins.Add(pluginGUID); continue; diff --git a/BepInEx/Bootstrap/TypeLoader.cs b/BepInEx/Bootstrap/TypeLoader.cs index 3fab44a..ad2ab5f 100644 --- a/BepInEx/Bootstrap/TypeLoader.cs +++ b/BepInEx/Bootstrap/TypeLoader.cs @@ -16,13 +16,6 @@ namespace BepInEx.Bootstrap /// public static class TypeLoader { - private static bool Is(this TypeDefinition self, Type td) - { - if (self.FullName == td.FullName) - return true; - return self.FullName != "System.Object" && (self.BaseType?.Resolve().Is(td) ?? false); - } - private static DefaultAssemblyResolver resolver; private static ReaderParameters readerParameters; @@ -35,7 +28,9 @@ namespace BepInEx.Bootstrap { var name = new AssemblyName(reference.FullName); - if (Utility.TryResolveDllAssembly(name, Paths.BepInExAssemblyDirectory, readerParameters, out AssemblyDefinition assembly) || Utility.TryResolveDllAssembly(name, Paths.PluginPath, readerParameters, out assembly) || Utility.TryResolveDllAssembly(name, Paths.ManagedPath, readerParameters, out assembly)) + if (Utility.TryResolveDllAssembly(name, Paths.BepInExAssemblyDirectory, readerParameters, out AssemblyDefinition assembly) || + Utility.TryResolveDllAssembly(name, Paths.PluginPath, readerParameters, out assembly) || + Utility.TryResolveDllAssembly(name, Paths.ManagedPath, readerParameters, out assembly)) return assembly; return null; @@ -48,11 +43,9 @@ namespace BepInEx.Bootstrap /// The specific base type to search for. /// The directory to search for assemblies. /// Returns a list of found derivative types. - public static Dictionary> FindPluginTypes(string directory) + public static Dictionary> FindPluginTypes(string directory, Func typeSelector) where T : class { - var result = new Dictionary>(); - var pluginType = typeof(BaseUnityPlugin); - string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower(); + var result = new Dictionary>(); foreach (string dll in Directory.GetFiles(Path.GetFullPath(directory), "*.dll", SearchOption.AllDirectories)) { @@ -60,47 +53,15 @@ namespace BepInEx.Bootstrap { var ass = AssemblyDefinition.ReadAssembly(dll, readerParameters); - var matchingTypes = ass.MainModule.Types.Where(t => !t.IsInterface && !t.IsAbstract && t.Is(pluginType)).ToList(); + var matches = ass.MainModule.Types.Select(typeSelector).Where(t => t != null).ToList(); - if (matchingTypes.Count == 0) - continue; - - var pluginInfos = new List(); - - foreach (var pluginTypeDefinition in matchingTypes) + if (matches.Count == 0) { - var metadata = BepInPlugin.FromCecilType(pluginTypeDefinition); - - if (metadata == null) - { - Logger.LogWarning($"Skipping over type [{pluginTypeDefinition.Name}] as no metadata attribute is specified"); - continue; - } - - //Perform a filter for currently running process - var filters = BepInProcess.FromCecilType(pluginTypeDefinition); - - bool invalidProcessName = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess); - - if (invalidProcessName) - { - Logger.LogInfo($"Skipping over plugin [{metadata.GUID}] due to process filter"); - continue; - } - - var dependencies = BepInDependency.FromCecilType(pluginTypeDefinition); - - pluginInfos.Add(new PluginInfo - { - Metadata = metadata, - Processes = filters, - Dependencies = dependencies, - CecilType = pluginTypeDefinition, - Location = dll - }); + ass.Dispose(); + continue; } - result[ass] = pluginInfos; + result[ass] = matches; } catch (Exception e) { diff --git a/BepInEx/Utility.cs b/BepInEx/Utility.cs index 76373fd..0e922d0 100644 --- a/BepInEx/Utility.cs +++ b/BepInEx/Utility.cs @@ -25,7 +25,7 @@ namespace BepInEx /// The string to parse /// The value to return if parsing is unsuccessful. /// Boolean value of input if able to be parsed, otherwise default value. - public static bool SafeParseBool(string input, bool defaultValue = false) { return bool.TryParse(input, out bool result) ? result : defaultValue; } + public static bool SafeParseBool(string input, bool defaultValue = false) { return Boolean.TryParse(input, out bool result) ? result : defaultValue; } /// /// Converts a file path into a UnityEngine.WWW format. @@ -39,7 +39,7 @@ namespace BepInEx /// /// The string to test. /// True if the value parameter is null or empty, or if value consists exclusively of white-space characters. - public static bool IsNullOrWhiteSpace(this string self) { return self == null || self.All(char.IsWhiteSpace); } + public static bool IsNullOrWhiteSpace(this string self) { return self == null || self.All(Char.IsWhiteSpace); } public static IEnumerable TopologicalSort(IEnumerable nodes, Func> dependencySelector) { @@ -128,14 +128,21 @@ namespace BepInEx return false; } + public static bool IsSubtypeOf(this TypeDefinition self, Type td) + { + if (self.FullName == td.FullName) + return true; + return self.FullName != "System.Object" && (self.BaseType?.Resolve().IsSubtypeOf(td) ?? false); + } + /// - /// Try to resolve and load the given assembly DLL. - /// - /// 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 TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly) { return TryResolveDllAssembly(assemblyName, directory, Assembly.LoadFile, out assembly); } + /// Try to resolve and load the given assembly DLL. + /// + /// 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 TryResolveDllAssembly(AssemblyName assemblyName, string directory, out Assembly assembly) { return TryResolveDllAssembly(assemblyName, directory, Assembly.LoadFile, out assembly); } /// /// Try to resolve and load the given assembly DLL.