浏览代码

Fix chainloader to skip plugins that are also skipped

denikson 5 年之前
父节点
当前提交
84dfcdfa87
共有 2 个文件被更改,包括 65 次插入44 次删除
  1. 63 22
      BepInEx/Bootstrap/Chainloader.cs
  2. 2 22
      BepInEx/Contract/Attributes.cs

+ 63 - 22
BepInEx/Bootstrap/Chainloader.cs

@@ -1,12 +1,12 @@
-using System;
+using BepInEx.Configuration;
+using BepInEx.Logging;
+using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text;
-using BepInEx.Configuration;
-using BepInEx.Logging;
 using UnityEngine;
 using UnityInjector.ConsoleUtil;
 using Logger = BepInEx.Logging.Logger;
@@ -139,41 +139,82 @@ namespace BepInEx.Bootstrap
 
 				Logger.LogInfo($"{selectedPluginTypes.Count} / {globalPluginTypes.Count} plugins to load");
 
-				Dictionary<Type, IEnumerable<Type>> dependencyDict = new Dictionary<Type, IEnumerable<Type>>();
-
+				var dependencyDict = new Dictionary<string, IEnumerable<string>>();
+				var pluginsByGUID = new Dictionary<string, Type>();
 
 				foreach (Type t in selectedPluginTypes)
 				{
-					try
+					var dependencies = MetadataHelper.GetDependencies(t, selectedPluginTypes);
+					var metadata = MetadataHelper.GetMetadata(t);
+					dependencyDict[metadata.GUID] = dependencies.Select(d => d.DependencyGUID);
+					pluginsByGUID[metadata.GUID] = t;
+				}
+
+				var emptyDependencies = new string[0];
+
+				// Sort plugins by their dependencies.
+				// Give missing dependencies no dependencies of its own, which will cause missing plugins to be first in the resulting list.
+				var sortedPlugins = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict.TryGetValue(x, out var deps) ? deps : emptyDependencies).ToList();
+
+				var invalidPlugins = new HashSet<string>();
+				var processedPlugins = new HashSet<string>();
+
+				foreach (var pluginGUID in sortedPlugins)
+				{
+					// If the plugin is missing, don't process it
+					if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginType))
+						continue;
+
+					var metadata = MetadataHelper.GetMetadata(pluginType);
+					var dependencies = MetadataHelper.GetDependencies(pluginType, selectedPluginTypes);
+					var dependsOnInvalidPlugin = false;
+					var missingDependencies = new List<string>();
+					foreach (var dependency in dependencies)
 					{
-						IEnumerable<Type> dependencies = MetadataHelper.GetDependencies(t, selectedPluginTypes);
+						// If the depenency wasn't already processed, it's missing altogether
+						if (!processedPlugins.Contains(dependency.DependencyGUID))
+						{
+							// If the dependency is hard, collect it into a list to show
+							if ((dependency.Flags & BepInDependency.DependencyFlags.HardDependency) != 0)
+								missingDependencies.Add(dependency.DependencyGUID);
+							continue;
+						}
+
+						// If the dependency is invalid (e.g. has missing depedencies), report that to the user
+						if (invalidPlugins.Contains(dependency.DependencyGUID))
+						{
+							dependsOnInvalidPlugin = true;
+							break;
+						}
+					}
+
+					processedPlugins.Add(pluginGUID);
 
-						dependencyDict[t] = dependencies;
+					if (dependsOnInvalidPlugin)
+					{
+						Logger.LogWarning($"Skipping [{metadata.Name}] because it has a dependency that was not loaded. See above errors for details.");
+						continue;
 					}
-					catch (MissingDependencyException)
+
+					if (missingDependencies.Count != 0)
 					{
-						var metadata = MetadataHelper.GetMetadata(t);
+						Logger.LogError($@"Missing the following dependencies for [{metadata.Name}]: {"\r\n"}{
+							string.Join("\r\n", missingDependencies.Select(s => $"- {s}").ToArray())
+							}{"\r\n"}Loading will be skipped; expect further errors and unstabilities.");
 
-						Logger.LogWarning($"Cannot load [{metadata.Name}] due to missing dependencies.");
+						invalidPlugins.Add(pluginGUID);
+						continue;
 					}
-				}
 
-				var sortedTypes = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList();
-
-				foreach (Type t in sortedTypes)
-				{
 					try
 					{
-						var metadata = MetadataHelper.GetMetadata(t);
 						Logger.LogInfo($"Loading [{metadata.Name} {metadata.Version}]");
-
-						var plugin = (BaseUnityPlugin)ManagerObject.AddComponent(t);
-
-						Plugins.Add(plugin);
+						Plugins.Add((BaseUnityPlugin)ManagerObject.AddComponent(pluginType));
 					}
 					catch (Exception ex)
 					{
-						Logger.LogError($"Error loading [{t.Name}] : {ex.Message}");
+						invalidPlugins.Add(pluginGUID);
+						Logger.LogError($"Error loading [{metadata.Name}] : {ex.Message}");
 						Logger.LogDebug(ex);
 					}
 				}

+ 2 - 22
BepInEx/Contract/Attributes.cs

@@ -154,29 +154,9 @@ namespace BepInEx
 		/// <param name="Plugin">The plugin type.</param>
 		/// <param name="AllPlugins">All currently loaded plugin types.</param>
 		/// <returns>A list of all plugin types that the specified plugin type depends upon.</returns>
-		public static IEnumerable<Type> GetDependencies(Type Plugin, IEnumerable<Type> AllPlugins)
+		public static IEnumerable<BepInDependency> 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;
+			return Plugin.GetCustomAttributes(typeof(BepInDependency), true).Cast<BepInDependency>();
 		}
 	}