using System; using System.Collections.Generic; using System.IO; using System.Linq; using BepInEx.Bootstrap; using BepInEx.Core; using Mono.Cecil; namespace BepInEx { #region BaseUnityPlugin /// /// This attribute denotes that a class is a plugin, and specifies the required metadata. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class BepInPlugin : Attribute { /// /// The unique identifier of the plugin. Should not change between plugin versions. /// public string GUID { get; protected set; } /// /// The user friendly name of the plugin. Is able to be changed between versions. /// public string Name { get; protected set; } /// /// The specfic version of the plugin. /// public SemVer.Version Version { get; protected set; } /// The unique identifier of the plugin. Should not change between plugin versions. /// The user friendly name of the plugin. Is able to be changed between versions. /// The specfic version of the plugin. public BepInPlugin(string GUID, string Name, string Version) { this.GUID = GUID; this.Name = Name; this.Version = SemVer.Version.TryParse(Version, out var v) ? v : null; } internal static BepInPlugin FromCecilType(TypeDefinition td) { var attr = MetadataHelper.GetCustomAttributes(td, false).FirstOrDefault(); if (attr == null) return null; return new BepInPlugin((string)attr.ConstructorArguments[0].Value, (string)attr.ConstructorArguments[1].Value, (string)attr.ConstructorArguments[2].Value); } } /// /// This attribute specifies any dependencies that this plugin has on other plugins. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class BepInDependency : Attribute, ICacheable { /// /// Flags that are applied to a dependency /// [Flags] public enum DependencyFlags { /// /// The plugin has a hard dependency on the referenced plugin, and will not run without it. /// HardDependency = 1, /// /// This plugin has a soft dependency on the referenced plugin, and is able to run without it. /// SoftDependency = 2, } /// /// The GUID of the referenced plugin. /// public string DependencyGUID { get; protected set; } /// /// The flags associated with this dependency definition. /// public DependencyFlags Flags { get; protected set; } /// /// The version range of the referenced plugin. /// public SemVer.Range VersionRange { get; protected set; } /// /// Marks this 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 parameter. /// /// The GUID of the referenced plugin. /// The flags associated with this dependency definition. public BepInDependency(string DependencyGUID, DependencyFlags Flags = DependencyFlags.HardDependency) { this.DependencyGUID = DependencyGUID; this.Flags = Flags; VersionRange = null; } /// /// Marks this 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 not satisfying , this plugin will not load and an error will be logged instead. /// /// The GUID of the referenced plugin. /// The version range of the referenced plugin. /// When version is supplied the dependency is always treated as HardDependency public BepInDependency(string guid, string version) : this(guid) { VersionRange = SemVer.Range.Parse(version); } internal static IEnumerable FromCecilType(TypeDefinition td) { var attrs = MetadataHelper.GetCustomAttributes(td, true); 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(VersionRange.ToString()); } void ICacheable.Load(BinaryReader br) { DependencyGUID = br.ReadString(); Flags = (DependencyFlags)br.ReadInt32(); VersionRange = SemVer.Range.Parse(br.ReadString()); } } /// /// This attribute specifies other plugins that are incompatible with this plugin. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class BepInIncompatibility : Attribute, ICacheable { /// /// The GUID of the referenced plugin. /// public string IncompatibilityGUID { get; protected set; } /// /// Marks this as incompatible with another plugin. /// If the other plugin exists, this plugin will not be loaded and a warning will be shown. /// /// The GUID of the referenced plugin. public BepInIncompatibility(string IncompatibilityGUID) { this.IncompatibilityGUID = IncompatibilityGUID; } internal static IEnumerable FromCecilType(TypeDefinition td) { var attrs = MetadataHelper.GetCustomAttributes(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(); } } /// /// This attribute specifies which processes this plugin should be run for. Not specifying this attribute will load the plugin under every process. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class BepInProcess : Attribute { /// /// The name of the process that this plugin will run under. /// public string ProcessName { get; protected set; } /// The name of the process that this plugin will run under. public BepInProcess(string ProcessName) { this.ProcessName = ProcessName; } internal static List FromCecilType(TypeDefinition td) { var attrs = MetadataHelper.GetCustomAttributes(td, true); return attrs.Select(customAttribute => new BepInProcess((string)customAttribute.ConstructorArguments[0].Value)).ToList(); } } #endregion #region MetadataHelper /// /// Helper class to use for retrieving metadata about a plugin, defined as attributes. /// public static class MetadataHelper { internal static IEnumerable GetCustomAttributes(TypeDefinition td, bool inherit) where T : Attribute { var result = new List(); var type = typeof(T); var currentType = td; do { result.AddRange(currentType.CustomAttributes.Where(ca => ca.AttributeType.FullName == type.FullName)); currentType = currentType.BaseType?.Resolve(); } while (inherit && currentType?.FullName != "System.Object"); return result; } /// /// Retrieves the BepInPlugin metadata from a plugin type. /// /// The plugin type. /// The BepInPlugin metadata of the plugin type. public static BepInPlugin GetMetadata(Type pluginType) { object[] attributes = pluginType.GetCustomAttributes(typeof(BepInPlugin), false); if (attributes.Length == 0) return null; return (BepInPlugin)attributes[0]; } /// /// Retrieves the BepInPlugin metadata from a plugin instance. /// /// The plugin instance. /// The BepInPlugin metadata of the plugin instance. public static BepInPlugin GetMetadata(object plugin) => GetMetadata(plugin.GetType()); /// /// Gets the specified attributes of a type, if they exist. /// /// The attribute type to retrieve. /// The plugin type. /// The attributes of the type, if existing. public static T[] GetAttributes(Type pluginType) where T : Attribute { return (T[])pluginType.GetCustomAttributes(typeof(T), true); } /// /// Gets the specified attributes of an instance, if they exist. /// /// The attribute type to retrieve. /// The plugin instance. /// The attributes of the instance, if existing. public static IEnumerable GetAttributes(object plugin) where T : Attribute => GetAttributes(plugin.GetType()); /// /// Retrieves the dependencies of the specified plugin type. /// /// The plugin type. /// A list of all plugin types that the specified plugin type depends upon. public static IEnumerable GetDependencies(Type plugin) { return plugin.GetCustomAttributes(typeof(BepInDependency), true).Cast(); } } #endregion }