using System.Collections.Generic; using System.Linq; using StardewModdingAPI.Framework.Reflection; namespace StardewModdingAPI.Framework.ModHelpers { /// Provides metadata about installed mods. internal class ModRegistryHelper : BaseHelper, IModRegistry { /********* ** Properties *********/ /// The underlying mod registry. private readonly ModRegistry Registry; /// Encapsulates monitoring and logging for the mod. private readonly IMonitor Monitor; /// The mod IDs for APIs accessed by this instanced. private readonly HashSet AccessedModApis = new HashSet(); /// Generates proxy classes to access mod APIs through an arbitrary interface. private readonly InterfaceProxyFactory ProxyFactory; /********* ** Public methods *********/ /// Construct an instance. /// The unique ID of the relevant mod. /// The underlying mod registry. /// Generates proxy classes to access mod APIs through an arbitrary interface. /// Encapsulates monitoring and logging for the mod. public ModRegistryHelper(string modID, ModRegistry registry, InterfaceProxyFactory proxyFactory, IMonitor monitor) : base(modID) { this.Registry = registry; this.ProxyFactory = proxyFactory; this.Monitor = monitor; } /// Get metadata for all loaded mods. public IEnumerable GetAll() { return this.Registry.GetAll().Select(p => p.Manifest); } /// Get metadata for a loaded mod. /// The mod's unique ID. /// Returns the matching mod's metadata, or null if not found. public IManifest Get(string uniqueID) { return this.Registry.Get(uniqueID)?.Manifest; } /// Get whether a mod has been loaded. /// The mod's unique ID. public bool IsLoaded(string uniqueID) { return this.Registry.Get(uniqueID) != null; } /// Get the API provided by a mod, or null if it has none. This signature requires using the API to access the API's properties and methods. public object GetApi(string uniqueID) { IModMetadata mod = this.Registry.Get(uniqueID); if (mod?.Api != null && this.AccessedModApis.Add(mod.Manifest.UniqueID)) this.Monitor.Log($"Accessed mod-provided API for {mod.DisplayName}.", LogLevel.Trace); return mod?.Api; } /// Get the API provided by a mod, mapped to a given interface which specifies the expected properties and methods. If the mod has no API or it's not compatible with the given interface, get null. /// The interface which matches the properties and methods you intend to access. /// The mod's unique ID. public TInterface GetApi(string uniqueID) where TInterface : class { // validate if (!this.Registry.AreAllModsInitialised) { this.Monitor.Log("Tried to access a mod-provided API before all mods were initialised.", LogLevel.Error); return null; } if (!typeof(TInterface).IsInterface) { this.Monitor.Log("Tried to map a mod-provided API to a class; must be a public interface.", LogLevel.Error); return null; } if (!typeof(TInterface).IsPublic) { this.Monitor.Log("Tried to map a mod-provided API to a non-public interface; must be a public interface.", LogLevel.Error); return null; } // get raw API object api = this.GetApi(uniqueID); if (api == null) return null; // get API of type if (api is TInterface castApi) return castApi; return this.ProxyFactory.CreateProxy(api, this.ModID, uniqueID); } } }