Przeglądaj źródła

Bootstrap code cleanup

Bepis 6 lat temu
rodzic
commit
fb96717caf

+ 0 - 34
BepInEx.Common/Utility.cs

@@ -78,39 +78,5 @@ namespace BepInEx.Common
                 }
             }
         }
-
-        public static List<TNode> TopologicalSort<TNode>(List<TNode> nodes, Func<TNode, List<TNode>> dependencySelector)
-        {
-            List<TNode> sorted_list = new List<TNode>();
-
-            List<TNode> visited = new List<TNode>();
-            List<TNode> sorted = new List<TNode>();
-
-            foreach (TNode input in nodes)
-                Visit(input);
-
-            return sorted_list;
-
-            void Visit(TNode node)
-            {
-                if (visited.Contains(node))
-                {
-                    if (!sorted.Contains(node))
-                    {
-                        throw new Exception("Cyclic Dependency");
-                    }
-                }
-                else
-                {
-                    visited.Add(node);
-
-                    foreach (var dep in dependencySelector(node))
-                        Visit(dep);
-
-                    sorted.Add(node);
-                    sorted_list.Add(node);
-                }
-            }
-        }
     }
 }

+ 27 - 31
BepInEx/Bootstrap/AssemblyPatcher.cs

@@ -1,24 +1,21 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using BepInEx.Common;
 using Mono.Cecil;
 
-namespace BepInEx
+namespace BepInEx.Bootstrap
 {
-    internal static class AssemblyPatcher
-    {
-        public delegate void AssemblyLoadEventHandler(AssemblyDefinition assembly);
-
-        public static AssemblyLoadEventHandler AssemblyLoad;
+    public delegate void AssemblyPatcherDelegate(AssemblyDefinition assembly);
 
-
-        public static void PatchAll(string directory)
+    public static class AssemblyPatcher
+    {
+        public static void PatchAll(string directory, Dictionary<string, IList<AssemblyPatcherDelegate>> patcherMethodDictionary)
         {
             //load all the requested assemblies
             List<AssemblyDefinition> assemblies = new List<AssemblyDefinition>();
+            Dictionary<AssemblyDefinition, string> assemblyFilenames = new Dictionary<AssemblyDefinition, string>();
 
             foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
             {
@@ -29,18 +26,18 @@ namespace BepInEx
                 //It's also generally dangerous to change system.dll since so many things rely on it, 
                 // and it's already loaded into the appdomain since this loader references it, so we might as well skip it
                 if (assembly.Name.Name == "System"
-                    || assembly.Name.Name == "System.Core"
-                    || assembly.Name.Name == "mscorlib")
+                    || assembly.Name.Name == "mscorlib") //mscorlib is already loaded into the appdomain so it can't be patched
                 {
                     assembly.Dispose();
                     continue;
                 }
 
                 assemblies.Add(assembly);
+                assemblyFilenames[assembly] = Path.GetFileName(assemblyPath);
             }
 
             //generate a dictionary of each assembly's dependencies
-            Dictionary<AssemblyDefinition, List<AssemblyDefinition>> assemblyDependencyDict = new Dictionary<AssemblyDefinition, List<AssemblyDefinition>>();
+            Dictionary<AssemblyDefinition, IList<AssemblyDefinition>> assemblyDependencyDict = new Dictionary<AssemblyDefinition, IList<AssemblyDefinition>>();
             
             foreach (AssemblyDefinition assembly in assemblies)
             {
@@ -51,35 +48,34 @@ namespace BepInEx
                     var dependencyAssembly = assemblies.FirstOrDefault(x => x.FullName == dependencyRef.FullName);
 
                     if (dependencyAssembly != null)
-                    {
                         assemblyDependencyDict[assembly].Add(dependencyAssembly);
-                    }
                 }
             }
 
             //sort the assemblies so load the assemblies that are dependant upon first
-            List<AssemblyDefinition> sortedAssemblies = Utility.TopologicalSort(assemblies, x => assemblyDependencyDict[x]);
+            IEnumerable<AssemblyDefinition> sortedAssemblies = Utility.TopologicalSort(assemblies, x => assemblyDependencyDict[x]);
 
-            //special casing for UnityEngine, needs to be reordered to the front
-            var unityEngine = sortedAssemblies.FirstOrDefault(x => x.Name.Name == "UnityEngine");
-            if (unityEngine != null)
+            //call the patchers on the assemblies
+            foreach (var assembly in sortedAssemblies)
             {
-                sortedAssemblies.Remove(unityEngine);
-                sortedAssemblies.Insert(0, unityEngine);
+                //skip if we aren't patching it
+                if (!patcherMethodDictionary.TryGetValue(Path.GetFileName(assemblyFilenames[assembly]), out IList<AssemblyPatcherDelegate> patcherMethods))
+                    continue;
+
+                Patch(assembly, patcherMethods);
             }
+        }
 
-            //call the patchers on the assemblies
-            foreach (var assembly in sortedAssemblies)
+        public static void Patch(AssemblyDefinition assembly, IEnumerable<AssemblyPatcherDelegate> patcherMethods)
+        {
+            using (assembly)
+            using (MemoryStream assemblyStream = new MemoryStream())
             {
-                using (MemoryStream assemblyStream = new MemoryStream())
-                {
-                    AssemblyLoad?.Invoke(assembly);
-                    
-                    assembly.Write(assemblyStream);
-                    Assembly.Load(assemblyStream.ToArray());
-                }
+                foreach (AssemblyPatcherDelegate method in patcherMethods)
+                    method.Invoke(assembly);
 
-                assembly.Dispose();
+                assembly.Write(assemblyStream);
+                Assembly.Load(assemblyStream.ToArray());
             }
         }
     }

+ 5 - 5
BepInEx/Bootstrap/Chainloader.cs

@@ -7,7 +7,7 @@ using System.Linq;
 using System.Reflection;
 using UnityEngine;
 
-namespace BepInEx
+namespace BepInEx.Bootstrap
 {
 	/// <summary>
 	/// The manager and loader for all plugins, and the entry point for BepInEx.
@@ -67,7 +67,7 @@ namespace BepInEx
 				    .Where(plugin =>
 				    {
                         //Perform a filter for currently running process
-				        var filters = TypeLoader.GetAttributes<BepInProcess>(plugin);
+				        var filters = MetadataHelper.GetAttributes<BepInProcess>(plugin);
 
 				        if (!filters.Any())
 				            return true;
@@ -84,13 +84,13 @@ namespace BepInEx
 				{
 					try
 					{
-						IEnumerable<Type> dependencies = TypeLoader.GetDependencies(t, pluginTypes);
+						IEnumerable<Type> dependencies = MetadataHelper.GetDependencies(t, pluginTypes);
 
 						dependencyDict[t] = dependencies;
 					}
 					catch (MissingDependencyException)
 					{
-						var metadata = TypeLoader.GetMetadata(t);
+						var metadata = MetadataHelper.GetMetadata(t);
 
 						BepInLogger.Log($"Cannot load [{metadata.Name}] due to missing dependencies.");
 					}
@@ -102,7 +102,7 @@ namespace BepInEx
 				{
 					try
 					{
-						var metadata = TypeLoader.GetMetadata(t);
+						var metadata = MetadataHelper.GetMetadata(t);
 
 						var plugin = (BaseUnityPlugin) ManagerObject.AddComponent(t);
 

+ 33 - 10
BepInEx/Bootstrap/Preloader.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
@@ -9,20 +10,40 @@ using MethodAttributes = Mono.Cecil.MethodAttributes;
 
 namespace BepInEx.Bootstrap
 {
-    internal static class Preloader
+    public static class Preloader
     {
+        #region Path Properties
+
         public static string ExecutablePath { get; private set; }
 
-        internal static string CurrentExecutingAssemblyPath => Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", "").Replace('/', '\\');
+        public static string CurrentExecutingAssemblyPath => Assembly.GetExecutingAssembly().CodeBase.Replace("file:///", "").Replace('/', '\\');
+
+        public static string CurrentExecutingAssemblyDirectoryPath => Path.GetDirectoryName(CurrentExecutingAssemblyPath);
+
+        public static string GameName => Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().ProcessName);
+
+        public static string GameRootPath => Path.GetDirectoryName(ExecutablePath);
+
+        public static string ManagedPath => Path.Combine(GameRootPath, Path.Combine($"{GameName}_Data", "Managed"));
 
-        internal static string CurrentExecutingAssemblyDirectoryPath => Path.GetDirectoryName(CurrentExecutingAssemblyPath);
+        #endregion
 
-        internal static string GameName => Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().ProcessName);
 
-        internal static string GameRootPath => Path.GetDirectoryName(ExecutablePath);
+        public static Dictionary<string, IList<AssemblyPatcherDelegate>> PatcherDictionary = new Dictionary<string, IList<AssemblyPatcherDelegate>>(StringComparer.OrdinalIgnoreCase);
 
-        internal static string ManagedPath => Path.Combine(GameRootPath, Path.Combine($"{GameName}_Data", "Managed"));
+        public static void AddPatcher(string dllName, AssemblyPatcherDelegate patcher)
+        {
+            if (PatcherDictionary.TryGetValue(dllName, out IList<AssemblyPatcherDelegate> patcherList))
+                patcherList.Add(patcher);
+            else
+            {
+                patcherList = new List<AssemblyPatcherDelegate>();
+
+                patcherList.Add(patcher);
 
+                PatcherDictionary[dllName] = patcherList;
+            }
+        }
 
         public static void Main(string[] args)
         {
@@ -32,9 +53,11 @@ namespace BepInEx.Bootstrap
             
             try
             {
-                AssemblyPatcher.AssemblyLoad += PatchEntrypoint;
+                //AssemblyPatcher.AssemblyLoad += PatchEntrypoint;
+                
+                AddPatcher("UnityEngine.dll", PatchEntrypoint);
 
-                AssemblyPatcher.PatchAll(ManagedPath);
+                AssemblyPatcher.PatchAll(ManagedPath, PatcherDictionary);
             }
             catch (Exception ex)
             {
@@ -42,7 +65,7 @@ namespace BepInEx.Bootstrap
             }
         }
 
-        static void PatchEntrypoint(AssemblyDefinition assembly)
+        internal static void PatchEntrypoint(AssemblyDefinition assembly)
         {
             if (assembly.Name.Name == "UnityEngine")
             {
@@ -73,7 +96,7 @@ namespace BepInEx.Bootstrap
             }
         }
 
-        static Assembly LocalResolve(object sender, ResolveEventArgs args)
+        internal static Assembly LocalResolve(object sender, ResolveEventArgs args)
         {
             if (args.Name == "0Harmony, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null")
                 return Assembly.LoadFile(Path.Combine(CurrentExecutingAssemblyDirectoryPath, "0Harmony.dll"));

+ 2 - 67
BepInEx/Bootstrap/TypeLoader.cs

@@ -1,11 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using System.Reflection;
-using System.Text;
 
-namespace BepInEx
+namespace BepInEx.Bootstrap
 {
     public static class TypeLoader
     {
@@ -37,73 +35,10 @@ namespace BepInEx
                 catch (ReflectionTypeLoadException)
                 {
                     BepInLogger.Log($"ERROR! Could not load \"{Path.GetFileName(dll)}\" as a plugin!");
-                } 
-            }
-
-            return types;
-        }
-
-        public static BepInPlugin GetMetadata(object plugin)
-        {
-            return GetMetadata(plugin.GetType());
-        }
-
-        public static BepInPlugin GetMetadata(Type pluginType)
-        {
-            object[] attributes = pluginType.GetCustomAttributes(typeof(BepInPlugin), false);
-
-            if (attributes.Length == 0)
-                return null;
-
-            return (BepInPlugin)attributes[0];
-        }
-
-        public static IEnumerable<T> GetAttributes<T>(object plugin) where T : Attribute
-        {
-            return GetAttributes<T>(plugin.GetType());
-        }
-
-        public static IEnumerable<T> GetAttributes<T>(Type pluginType) where T : Attribute
-        {
-            return pluginType.GetCustomAttributes(typeof(T), true).Cast<T>();
-        }
-
-        public static IEnumerable<Type> GetDependencies(Type Plugin, IEnumerable<Type> AllPlugins)
-        {
-            object[] attributes = Plugin.GetCustomAttributes(typeof(BepInDependency), true);
-
-            List<Type> dependencyTypes = new List<Type>();
-
-            foreach (BepInDependency dependency in attributes)
-            {
-                Type dependencyType = AllPlugins.FirstOrDefault(x => GetMetadata(x)?.GUID == dependency.DependencyGUID);
-
-                if (dependencyType == null)
-                {
-                    if ((dependency.Flags & BepInDependency.DependencyFlags.SoftDependency) != 0)
-                        continue; //skip on soft dependencies
-
-                    throw new MissingDependencyException("Cannot find dependency type.");
                 }
-                    
-
-                dependencyTypes.Add(dependencyType);
             }
 
-            return dependencyTypes;
-        }
-    }
-
-    public class MissingDependencyException : Exception
-    {
-        public MissingDependencyException() : base()
-        {
-
-        }
-
-        public MissingDependencyException(string message) : base(message)
-        {
-
+            return types;
         }
     }
 }

+ 3 - 3
BepInEx/Config.cs

@@ -205,17 +205,17 @@ namespace BepInEx
 
         public static string GetEntry(this BaseUnityPlugin plugin, string key, string defaultValue = "")
         {
-            return GetEntry(key, defaultValue, TypeLoader.GetMetadata(plugin).GUID);
+            return GetEntry(key, defaultValue, MetadataHelper.GetMetadata(plugin).GUID);
         }
 
         public static void SetEntry(this BaseUnityPlugin plugin, string key, string value)
         {
-            SetEntry(key, value, TypeLoader.GetMetadata(plugin).GUID);
+            SetEntry(key, value, MetadataHelper.GetMetadata(plugin).GUID);
         }
 
         public static bool HasEntry(this BaseUnityPlugin plugin, string key)
         {
-            return HasEntry(key, TypeLoader.GetMetadata(plugin).GUID);
+            return HasEntry(key, MetadataHelper.GetMetadata(plugin).GUID);
         }
         #endregion Extensions
     }

+ 3 - 3
BepInEx/ConfigWrapper.cs

@@ -69,19 +69,19 @@ namespace BepInEx
         public ConfigWrapper(string key, BaseUnityPlugin plugin, T @default = default(T))
             : this(key, @default)
         {
-            Section = TypeLoader.GetMetadata(plugin).GUID;
+            Section = MetadataHelper.GetMetadata(plugin).GUID;
         }
 
         public ConfigWrapper(string key, BaseUnityPlugin plugin, Func<string, T> strToObj, Func<T, string> objToStr, T @default = default(T))
           : this(key, strToObj, objToStr, @default)
         {
-            Section = TypeLoader.GetMetadata(plugin).GUID;
+            Section = MetadataHelper.GetMetadata(plugin).GUID;
         }
 
         public ConfigWrapper(string key, BaseUnityPlugin plugin, IConfigConverter<T> converter, T @default = default(T))
           : this(key, converter.ConvertFromString, converter.ConvertToString, @default)
         {
-            Section = TypeLoader.GetMetadata(plugin).GUID;
+            Section = MetadataHelper.GetMetadata(plugin).GUID;
         }
 
         public ConfigWrapper(string key, string section, T @default = default(T))

+ 71 - 0
BepInEx/Contract/Attributes.cs

@@ -1,7 +1,11 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace BepInEx
 {
+    #region BaseUnityPlugin
+
     /// <summary>
     /// This attribute denotes that a class is a plugin, and specifies the required metadata.
     /// </summary>
@@ -91,4 +95,71 @@ namespace BepInEx
             this.ProcessName = ProcessName;
         }
     }
+
+    #endregion
+
+    #region MetadataHelper
+
+    public static class MetadataHelper
+    {
+        public static BepInPlugin GetMetadata(object plugin)
+        {
+            return GetMetadata(plugin.GetType());
+        }
+
+        public static BepInPlugin GetMetadata(Type pluginType)
+        {
+            object[] attributes = pluginType.GetCustomAttributes(typeof(BepInPlugin), false);
+
+            if (attributes.Length == 0)
+                return null;
+
+            return (BepInPlugin)attributes[0];
+        }
+
+        public static IEnumerable<T> GetAttributes<T>(object plugin) where T : Attribute
+        {
+            return GetAttributes<T>(plugin.GetType());
+        }
+
+        public static IEnumerable<T> GetAttributes<T>(Type pluginType) where T : Attribute
+        {
+            return pluginType.GetCustomAttributes(typeof(T), true).Cast<T>();
+        }
+
+        public static IEnumerable<Type> GetDependencies(Type Plugin, IEnumerable<Type> AllPlugins)
+        {
+            object[] attributes = Plugin.GetCustomAttributes(typeof(BepInDependency), true);
+
+            List<Type> dependencyTypes = new List<Type>();
+
+            foreach (BepInDependency dependency in attributes)
+            {
+                Type dependencyType = AllPlugins.FirstOrDefault(x => GetMetadata(x)?.GUID == dependency.DependencyGUID);
+
+                if (dependencyType == null)
+                {
+                    if ((dependency.Flags & BepInDependency.DependencyFlags.SoftDependency) != 0)
+                        continue; //skip on soft dependencies
+
+                    throw new MissingDependencyException("Cannot find dependency type.");
+                }
+                    
+
+                dependencyTypes.Add(dependencyType);
+            }
+
+            return dependencyTypes;
+        }
+    }
+
+    public class MissingDependencyException : Exception
+    {
+        public MissingDependencyException(string message) : base(message)
+        {
+
+        }
+    }
+
+    #endregion
 }

+ 2 - 2
BepInEx/Properties/AssemblyInfo.cs

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("3.2.0.0")]
-[assembly: AssemblyFileVersion("3.2.0.0")]
+[assembly: AssemblyVersion("4.0.0.0")]
+[assembly: AssemblyFileVersion("4.0.0.0")]