Browse Source

Add MinimumVersion support to BepInDependency

ManlyMarco 5 năm trước cách đây
mục cha
commit
d6491da60a

+ 19 - 13
BepInEx/Bootstrap/Chainloader.cs

@@ -2,7 +2,6 @@
 using BepInEx.Logging;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Reflection;
@@ -222,7 +221,7 @@ namespace BepInEx.Bootstrap
 				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>();
+				var processedPlugins = new Dictionary<string, Version>();
 
 				foreach (var pluginGUID in sortedPlugins)
 				{
@@ -231,15 +230,16 @@ namespace BepInEx.Bootstrap
 						continue;
 
 					var dependsOnInvalidPlugin = false;
-					var missingDependencies = new List<string>();
+					var missingDependencies = new List<BepInDependency>();
 					foreach (var dependency in pluginInfo.Dependencies)
 					{
 						// If the depenency wasn't already processed, it's missing altogether
-						if (!processedPlugins.Contains(dependency.DependencyGUID))
+						bool depenencyExists = processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion);
+						if (!depenencyExists || pluginVersion < dependency.MinimumVersion)
 						{
 							// If the dependency is hard, collect it into a list to show
 							if ((dependency.Flags & BepInDependency.DependencyFlags.HardDependency) != 0)
-								missingDependencies.Add(dependency.DependencyGUID);
+								missingDependencies.Add(dependency);
 							continue;
 						}
 
@@ -251,7 +251,7 @@ namespace BepInEx.Bootstrap
 						}
 					}
 
-					processedPlugins.Add(pluginGUID);
+					processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version);
 
 					if (dependsOnInvalidPlugin)
 					{
@@ -261,8 +261,14 @@ namespace BepInEx.Bootstrap
 
 					if (missingDependencies.Count != 0)
 					{
+						string ToMissingString(BepInDependency s)
+						{
+							if (s.MinimumVersion.IsZero()) return "- " + s.DependencyGUID;
+							return $"- {s.DependencyGUID} (at least v{s.MinimumVersion})";
+						}
+
 						Logger.LogError($@"Missing the following dependencies for [{pluginInfo.Metadata.Name}]: {"\r\n"}{
-								string.Join("\r\n", missingDependencies.Select(s => $"- {s}").ToArray())
+								string.Join("\r\n", missingDependencies.Select(ToMissingString).ToArray())
 							}{"\r\n"}Loading will be skipped; expect further errors and unstabilities.");
 
 						invalidPlugins.Add(pluginGUID);
@@ -310,15 +316,15 @@ namespace BepInEx.Bootstrap
 		#region Config
 
 		private static readonly ConfigWrapper<bool> ConfigUnityLogging = ConfigFile.CoreConfig.Wrap(
-			"Logging", 
-			"UnityLogListening", 
-			"Enables showing unity log messages in the BepInEx logging system.", 
+			"Logging",
+			"UnityLogListening",
+			"Enables showing unity log messages in the BepInEx logging system.",
 			true);
 
 		private static readonly ConfigWrapper<bool> ConfigDiskLogging = ConfigFile.CoreConfig.Wrap(
-			"Logging.Disk", 
-			"Enabled", 
-			"Enables writing log messages to disk.", 
+			"Logging.Disk",
+			"Enabled",
+			"Enables writing log messages to disk.",
 			true);
 
 		private static readonly ConfigWrapper<string> ConfigDiskConsoleDisplayedLevel = ConfigFile.CoreConfig.Wrap(

+ 34 - 1
BepInEx/Contract/Attributes.cs

@@ -91,18 +91,51 @@ namespace BepInEx
 		/// </summary>
 		public DependencyFlags Flags { get; protected set; }
 
+		/// <summary>
+		/// The minimum version of the referenced plugin.
+		/// </summary>
+		public Version MinimumVersion { get; protected set; }
+
+		/// <summary>
+		/// Marks this <see cref="BaseUnityPlugin"/> as depenant on another plugin. The other plugin will be loaded before this one.
+		/// If the other plugin doesn't exist, what happens depends on the <see cref="Flags"/> parameter.
+		/// </summary>
 		/// <param name="DependencyGUID">The GUID of the referenced plugin.</param>
 		/// <param name="Flags">The flags associated with this dependency definition.</param>
 		public BepInDependency(string DependencyGUID, DependencyFlags Flags = DependencyFlags.HardDependency)
 		{
 			this.DependencyGUID = DependencyGUID;
 			this.Flags = Flags;
+			MinimumVersion = new Version();
+		}
+
+		/// <summary>
+		/// Marks this <see cref="BaseUnityPlugin"/> as depenant on another plugin. The other plugin will be loaded before this one.
+		/// If the other plugin doesn't exist or is of a version below <see cref="MinimumDependencyVersion"/>, this plugin will not load and an error will be logged instead.
+		/// </summary>
+		/// <param name="DependencyGUID">The GUID of the referenced plugin.</param>
+		/// <param name="MinimumDependencyVersion">The minimum version of the referenced plugin.</param>
+		public BepInDependency(string DependencyGUID, string MinimumDependencyVersion) : this(DependencyGUID)
+		{
+			MinimumVersion = new Version(MinimumDependencyVersion);
+		}
+
+		internal BepInDependency(string DependencyGUID, DependencyFlags Flags, string MinimumDependencyVersion) : this(DependencyGUID, Flags)
+		{
+			if (!string.IsNullOrEmpty(MinimumDependencyVersion))
+				MinimumVersion = new Version(MinimumDependencyVersion);
 		}
 
 		internal static IEnumerable<BepInDependency> FromCecilType(TypeDefinition td)
 		{
 			var attrs = MetadataHelper.GetCustomAttributes<BepInDependency>(td, true);
-			return attrs.Select(customAttribute => new BepInDependency((string)customAttribute.ConstructorArguments[0].Value, (DependencyFlags)customAttribute.ConstructorArguments[1].Value)).ToList();
+			return attrs.Select(customAttribute =>
+			{
+				var dependencyGuid = (string)customAttribute.ConstructorArguments[0].Value;
+				var secondArg = customAttribute.ConstructorArguments[1].Value;
+				if (secondArg is string minVersion) return new BepInDependency(dependencyGuid, minVersion);
+				return new BepInDependency(dependencyGuid, (DependencyFlags)secondArg);
+			}).ToList();
 		}
 	}
 

+ 2 - 1
BepInEx/Contract/PluginInfo.cs

@@ -38,6 +38,7 @@ namespace BepInEx.Contract
 			{
 				bw.Write(bepInDependency.DependencyGUID);
 				bw.Write((int)bepInDependency.Flags);
+				bw.Write(bepInDependency.MinimumVersion.ToString());
 			}
 		}
 
@@ -56,7 +57,7 @@ namespace BepInEx.Contract
 			var depCount = br.ReadInt32();
 			var depList = new List<BepInDependency>(depCount);
 			for (int i = 0; i < depCount; i++)
-				depList.Add(new BepInDependency(br.ReadString(), (BepInDependency.DependencyFlags) br.ReadInt32()));
+				depList.Add(new BepInDependency(br.ReadString(), (BepInDependency.DependencyFlags) br.ReadInt32(), br.ReadString()));
 			Dependencies = depList;
 		}
 	}

+ 5 - 0
BepInEx/Utility.cs

@@ -237,5 +237,10 @@ namespace BepInEx
 				return false;
 			}
 		}
+
+		internal static bool IsZero(this Version v)
+		{
+			return v.Major == 0 && v.Minor == 0 && v.Build == 0 && v.Revision == 0;
+		}
 	}
 }