mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-20 01:46:13 +00:00
Merge pull request #90 from ManlyMarco/pluginVersions
Added MinimumVersion support to BepInDependency and added BepInIncompatibility
This commit is contained in:
3 files changed
+162
-29
No files matched your search
@@ -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;
|
||||
@@ -28,6 +27,8 @@ namespace BepInEx.Bootstrap
|
||||
|
||||
public static List<BaseUnityPlugin> Plugins { get; } = new List<BaseUnityPlugin>();
|
||||
|
||||
public static List<string> DependencyErrors { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The GameObject that all plugins are attached to as components.
|
||||
/// </summary>
|
||||
@@ -126,12 +127,14 @@ namespace BepInEx.Bootstrap
|
||||
|
||||
var filters = BepInProcess.FromCecilType(type);
|
||||
var dependencies = BepInDependency.FromCecilType(type);
|
||||
var incompatibilities = BepInIncompatibility.FromCecilType(type);
|
||||
|
||||
return new PluginInfo
|
||||
{
|
||||
Metadata = metadata,
|
||||
Processes = filters,
|
||||
Dependencies = dependencies,
|
||||
Incompatibilities = incompatibilities,
|
||||
TypeName = type.FullName
|
||||
};
|
||||
}
|
||||
@@ -207,7 +210,7 @@ namespace BepInEx.Bootstrap
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -218,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)
|
||||
{
|
||||
@@ -227,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;
|
||||
}
|
||||
|
||||
@@ -247,19 +251,45 @@ namespace BepInEx.Bootstrap
|
||||
}
|
||||
}
|
||||
|
||||
processedPlugins.Add(pluginGUID);
|
||||
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);
|
||||
|
||||
if (dependsOnInvalidPlugin)
|
||||
{
|
||||
Logger.LogWarning($"Skipping [{pluginInfo.Metadata.Name}] because it has a dependency that was not loaded. See above errors for details.");
|
||||
string message = $"Skipping [{pluginInfo.Metadata.Name}] because it has a dependency that was not loaded. See above errors for details.";
|
||||
DependencyErrors.Add(message);
|
||||
Logger.LogWarning(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
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 ToMissingString(BepInDependency s)
|
||||
{
|
||||
bool emptyVersion = s.MinimumVersion.Major == 0 && s.MinimumVersion.Minor == 0 && s.MinimumVersion.Build == 0 && s.MinimumVersion.Revision == 0;
|
||||
if (emptyVersion) return "- " + s.DependencyGUID;
|
||||
return $"- {s.DependencyGUID} (at least v{s.MinimumVersion})";
|
||||
}
|
||||
|
||||
string message = $@"Could not load [{pluginInfo.Metadata.Name}] because it has missing dependencies: {string.Join(", ", missingDependencies.Select(ToMissingString).ToArray())}";
|
||||
DependencyErrors.Add(message);
|
||||
Logger.LogError(message);
|
||||
|
||||
invalidPlugins.Add(pluginGUID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (incompatibilities.Count != 0)
|
||||
{
|
||||
string message = $@"Could not load [{pluginInfo.Metadata.Name}] because it is incompatible with: {string.Join(", ", incompatibilities.Select(i => i.IncompatibilityGUID).ToArray())}";
|
||||
DependencyErrors.Add(message);
|
||||
Logger.LogError(message);
|
||||
|
||||
invalidPlugins.Add(pluginGUID);
|
||||
continue;
|
||||
@@ -306,15 +336,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(
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using BepInEx.Logging;
|
||||
using BepInEx.Bootstrap;
|
||||
using Mono.Cecil;
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace BepInEx
|
||||
{
|
||||
@@ -66,7 +65,7 @@ namespace BepInEx
|
||||
/// This attribute specifies any dependencies that this plugin has on other plugins.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class BepInDependency : Attribute
|
||||
public class BepInDependency : Attribute, ICacheable
|
||||
{
|
||||
public enum DependencyFlags
|
||||
{
|
||||
@@ -91,18 +90,102 @@ 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>
|
||||
/// <remarks>When version is supplied the dependency is always treated as HardDependency</remarks>
|
||||
public BepInDependency(string DependencyGUID, string MinimumDependencyVersion) : this(DependencyGUID)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void ICacheable.Save(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(DependencyGUID);
|
||||
bw.Write((int)Flags);
|
||||
bw.Write(MinimumVersion.ToString());
|
||||
}
|
||||
|
||||
void ICacheable.Load(BinaryReader br)
|
||||
{
|
||||
DependencyGUID = br.ReadString();
|
||||
Flags = (DependencyFlags)br.ReadInt32();
|
||||
MinimumVersion = new Version(br.ReadString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,15 @@ namespace BepInEx.Contract
|
||||
|
||||
public IEnumerable<BepInDependency> Dependencies { get; internal set; }
|
||||
|
||||
public IEnumerable<BepInIncompatibility> Incompatibilities { get; internal set; }
|
||||
|
||||
public string Location { get; internal set; }
|
||||
|
||||
public BaseUnityPlugin Instance { get; internal set; }
|
||||
|
||||
internal string TypeName { get; set; }
|
||||
|
||||
public void Save(BinaryWriter bw)
|
||||
void ICacheable.Save(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(TypeName);
|
||||
|
||||
@@ -35,13 +37,15 @@ namespace BepInEx.Contract
|
||||
var depList = Dependencies.ToList();
|
||||
bw.Write(depList.Count);
|
||||
foreach (var bepInDependency in depList)
|
||||
{
|
||||
bw.Write(bepInDependency.DependencyGUID);
|
||||
bw.Write((int)bepInDependency.Flags);
|
||||
}
|
||||
((ICacheable)bepInDependency).Save(bw);
|
||||
|
||||
var incList = Incompatibilities.ToList();
|
||||
bw.Write(incList.Count);
|
||||
foreach (var bepInIncompatibility in incList)
|
||||
((ICacheable)bepInIncompatibility).Save(bw);
|
||||
}
|
||||
|
||||
public void Load(BinaryReader br)
|
||||
void ICacheable.Load(BinaryReader br)
|
||||
{
|
||||
TypeName = br.ReadString();
|
||||
|
||||
@@ -56,8 +60,24 @@ 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()));
|
||||
{
|
||||
var dep = new BepInDependency("");
|
||||
((ICacheable)dep).Load(br);
|
||||
depList.Add(dep);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user