Browse Source

Added BepInIncompatibility attribute

ManlyMarco 5 years ago
parent
commit
da14816b84
3 changed files with 83 additions and 1 deletions
  1. 22 1
      BepInEx/Bootstrap/Chainloader.cs
  2. 43 0
      BepInEx/Contract/Attributes.cs
  3. 18 0
      BepInEx/Contract/PluginInfo.cs

+ 22 - 1
BepInEx/Bootstrap/Chainloader.cs

@@ -127,12 +127,14 @@ namespace BepInEx.Bootstrap
 
 
 			var filters = BepInProcess.FromCecilType(type);
 			var filters = BepInProcess.FromCecilType(type);
 			var dependencies = BepInDependency.FromCecilType(type);
 			var dependencies = BepInDependency.FromCecilType(type);
+			var incompatibilities = BepInIncompatibility.FromCecilType(type);
 
 
 			return new PluginInfo
 			return new PluginInfo
 			{
 			{
 				Metadata = metadata,
 				Metadata = metadata,
 				Processes = filters,
 				Processes = filters,
 				Dependencies = dependencies,
 				Dependencies = dependencies,
+				Incompatibilities = incompatibilities,
 				TypeName = type.FullName
 				TypeName = type.FullName
 			};
 			};
 		}
 		}
@@ -208,7 +210,7 @@ namespace BepInEx.Bootstrap
 						continue;
 						continue;
 					}
 					}
 
 
-					dependencyDict[pluginInfo.Metadata.GUID] = pluginInfo.Dependencies.Select(d => d.DependencyGUID);
+					dependencyDict[pluginInfo.Metadata.GUID] = pluginInfo.Dependencies.Select(d => d.DependencyGUID).Concat(pluginInfo.Incompatibilities.Select(i => i.IncompatibilityGUID));
 					pluginsByGUID[pluginInfo.Metadata.GUID] = pluginInfo;
 					pluginsByGUID[pluginInfo.Metadata.GUID] = pluginInfo;
 				}
 				}
 
 
@@ -249,6 +251,13 @@ namespace BepInEx.Bootstrap
 						}
 						}
 					}
 					}
 
 
+					var incompatibilities = new List<BepInIncompatibility>();
+					foreach (var incompatibility in pluginInfo.Incompatibilities)
+					{
+						if (processedPlugins.ContainsKey(incompatibility.IncompatibilityGUID))
+							incompatibilities.Add(incompatibility);
+					}
+
 					processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version);
 					processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version);
 
 
 					if (dependsOnInvalidPlugin)
 					if (dependsOnInvalidPlugin)
@@ -277,6 +286,18 @@ namespace BepInEx.Bootstrap
 						continue;
 						continue;
 					}
 					}
 
 
+					if (incompatibilities.Count != 0)
+					{
+						string message = $@"Found incompatible plugins for [{pluginInfo.Metadata.Name}]: {"\r\n"}{
+								string.Join("\r\n", incompatibilities.Select(i => i.IncompatibilityGUID).ToArray())
+							}{"\r\n"}Loading will be skipped; expect further errors and unstabilities.";
+						DependencyErrors.Add(message);
+						Logger.LogError(message);
+
+						invalidPlugins.Add(pluginGUID);
+						continue;
+					}
+
 					try
 					try
 					{
 					{
 						Logger.LogInfo($"Loading [{pluginInfo.Metadata.Name} {pluginInfo.Metadata.Version}]");
 						Logger.LogInfo($"Loading [{pluginInfo.Metadata.Name} {pluginInfo.Metadata.Version}]");

+ 43 - 0
BepInEx/Contract/Attributes.cs

@@ -114,6 +114,7 @@ namespace BepInEx
 		/// </summary>
 		/// </summary>
 		/// <param name="DependencyGUID">The GUID of the referenced plugin.</param>
 		/// <param name="DependencyGUID">The GUID of the referenced plugin.</param>
 		/// <param name="MinimumDependencyVersion">The minimum version of the referenced plugin.</param>
 		/// <param name="MinimumDependencyVersion">The minimum version of the referenced plugin.</param>
+		/// <remarks>When version is supplied the dependency is always treated as HardDependency</remarks>
 		public BepInDependency(string DependencyGUID, string MinimumDependencyVersion) : this(DependencyGUID)
 		public BepInDependency(string DependencyGUID, string MinimumDependencyVersion) : this(DependencyGUID)
 		{
 		{
 			MinimumVersion = new Version(MinimumDependencyVersion);
 			MinimumVersion = new Version(MinimumDependencyVersion);
@@ -147,6 +148,48 @@ namespace BepInEx
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>
+	/// This attribute specifies other plugins that are incompatible with this plugin.
+	/// </summary>
+	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+	public class BepInIncompatibility : Attribute, ICacheable
+	{
+		/// <summary>
+		/// The GUID of the referenced plugin.
+		/// </summary>
+		public string IncompatibilityGUID { get; protected set; }
+		
+		/// <summary>
+		/// Marks this <see cref="BaseUnityPlugin"/> as incompatible with another plugin. 
+		/// If the other plugin exists, this plugin will not be loaded and a warning will be shown.
+		/// </summary>
+		/// <param name="IncompatibilityGUID">The GUID of the referenced plugin.</param>
+		public BepInIncompatibility(string IncompatibilityGUID)
+		{
+			this.IncompatibilityGUID = IncompatibilityGUID;
+		}
+
+		internal static IEnumerable<BepInIncompatibility> FromCecilType(TypeDefinition td)
+		{
+			var attrs = MetadataHelper.GetCustomAttributes<BepInIncompatibility>(td, true);
+			return attrs.Select(customAttribute =>
+			{
+				var dependencyGuid = (string)customAttribute.ConstructorArguments[0].Value;
+				return new BepInIncompatibility(dependencyGuid);
+			}).ToList();
+		}
+
+		void ICacheable.Save(BinaryWriter bw)
+		{
+			bw.Write(IncompatibilityGUID);
+		}
+
+		void ICacheable.Load(BinaryReader br)
+		{
+			IncompatibilityGUID = br.ReadString();
+		}
+	}
+
+	/// <summary>
 	/// This attribute specifies which processes this plugin should be run for. Not specifying this attribute will load the plugin under every process.
 	/// This attribute specifies which processes this plugin should be run for. Not specifying this attribute will load the plugin under every process.
 	/// </summary>
 	/// </summary>
 	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
 	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]

+ 18 - 0
BepInEx/Contract/PluginInfo.cs

@@ -13,6 +13,8 @@ namespace BepInEx.Contract
 
 
 		public IEnumerable<BepInDependency> Dependencies { get; internal set; }
 		public IEnumerable<BepInDependency> Dependencies { get; internal set; }
 
 
+		public IEnumerable<BepInIncompatibility> Incompatibilities { get; set; }
+
 		public string Location { get; internal set; }
 		public string Location { get; internal set; }
 
 
 		public BaseUnityPlugin Instance { get; internal set; }
 		public BaseUnityPlugin Instance { get; internal set; }
@@ -36,6 +38,11 @@ namespace BepInEx.Contract
 			bw.Write(depList.Count);
 			bw.Write(depList.Count);
 			foreach (var bepInDependency in depList)
 			foreach (var bepInDependency in depList)
 				((ICacheable)bepInDependency).Save(bw);
 				((ICacheable)bepInDependency).Save(bw);
+
+			var incList = Incompatibilities.ToList();
+			bw.Write(incList.Count);
+			foreach (var bepInIncompatibility in incList)
+				((ICacheable)bepInIncompatibility).Save(bw);
 		}
 		}
 
 
 		void ICacheable.Load(BinaryReader br)
 		void ICacheable.Load(BinaryReader br)
@@ -60,6 +67,17 @@ namespace BepInEx.Contract
 			}
 			}
 
 
 			Dependencies = depList;
 			Dependencies = depList;
+
+			var incCount = br.ReadInt32();
+			var incList = new List<BepInIncompatibility>(incCount);
+			for (int i = 0; i < incCount; i++)
+			{
+				var inc = new BepInIncompatibility("");
+				((ICacheable)inc).Load(br);
+				incList.Add(inc);
+			}
+
+			Incompatibilities = incList;
 		}
 		}
 	}
 	}
 }
 }