using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using BepInEx.Logging; using Mono.Cecil; using Mono.Collections.Generic; 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 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; try { this.Version = new Version(Version); } catch { this.Version = 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 { 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 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; } internal static IEnumerable FromCecilType(TypeDefinition td) { var attrs = MetadataHelper.GetCustomAttributes(td, true); return attrs.Select(customAttribute => new BepInDependency((string)customAttribute.ConstructorArguments[0].Value, (DependencyFlags)customAttribute.ConstructorArguments[1].Value)).ToList(); } } /// /// 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 IEnumerable 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. /// All currently loaded plugin types. /// A list of all plugin types that the specified plugin type depends upon. public static IEnumerable GetDependencies(Type Plugin, IEnumerable AllPlugins) { return Plugin.GetCustomAttributes(typeof(BepInDependency), true).Cast(); } } /// /// An exception which is thrown when a plugin's dependencies cannot be found. /// public class MissingDependencyException : Exception { public MissingDependencyException(string message) : base(message) { } } #endregion #region Build configuration /// /// This class is appended to AssemblyInfo.cs when BepInEx is built via a CI pipeline. /// It is mainly intended to signify that the current build is not a release build and is special, like for instance a bleeding edge build. /// internal class BuildInfoAttribute : Attribute { public string Info { get; } public BuildInfoAttribute(string info) { Info = info; } } #endregion }