Merge pull request #90 from ManlyMarco/pluginVersions

Added MinimumVersion support to BepInDependency and added BepInIncompatibility
This commit is contained in:
MarC0 authored and GitHub committed 2019-08-29 17:55:18 +02:00
commit cf634c521a
3 files changed
+162 -29

No files matched your search

+47 -17
View File
@@ -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(
+88 -5
View File
@@ -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();
}
}
+27 -7
View File
@@ -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;
}
}
}