diff options
-rw-r--r-- | src/SMAPI/Framework/IModMetadata.cs | 9 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModLoading/ModMetadata.cs | 12 | ||||
-rw-r--r-- | src/SMAPI/IMod.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/IModProvidedApi.cs | 6 | ||||
-rw-r--r-- | src/SMAPI/IModRegistry.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/Mod.cs | 3 | ||||
-rw-r--r-- | src/SMAPI/Program.cs | 86 | ||||
-rw-r--r-- | src/SMAPI/StardewModdingAPI.csproj | 1 |
9 files changed, 40 insertions, 88 deletions
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs index c4be7daf..a36994fd 100644 --- a/src/SMAPI/Framework/IModMetadata.cs +++ b/src/SMAPI/Framework/IModMetadata.cs @@ -31,7 +31,7 @@ namespace StardewModdingAPI.Framework IMod Mod { get; } /// <summary>The mod-provided API (if any).</summary> - IModProvidedApi Api { get; } + object Api { get; } /********* @@ -45,7 +45,10 @@ namespace StardewModdingAPI.Framework /// <summary>Set the mod instance.</summary> /// <param name="mod">The mod instance to set.</param> - /// <param name="api">The mod-provided API (if any).</param> - IModMetadata SetMod(IMod mod, IModProvidedApi api); + IModMetadata SetMod(IMod mod); + + /// <summary>Set the mod-provided API instance.</summary> + /// <param name="api">The mod-provided API.</param> + IModMetadata SetApi(object api); } } diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs index 340205f3..949d986a 100644 --- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs @@ -47,7 +47,7 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// <summary>Get the API provided by a mod, or <c>null</c> if it has none. This signature requires using the <see cref="IModHelper.Reflection"/> API to access the API's properties and methods.</summary> - public IModProvidedApi GetApi(string uniqueID) + public object GetApi(string uniqueID) { return this.Registry.Get(uniqueID)?.Api; } diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index 2e5c27be..30fe211b 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -30,7 +30,7 @@ namespace StardewModdingAPI.Framework.ModLoading public IMod Mod { get; private set; } /// <summary>The mod-provided API (if any).</summary> - public IModProvidedApi Api { get; private set; } + public object Api { get; private set; } /********* @@ -62,10 +62,16 @@ namespace StardewModdingAPI.Framework.ModLoading /// <summary>Set the mod instance.</summary> /// <param name="mod">The mod instance to set.</param> - /// <param name="api">The mod-provided API (if any).</param> - public IModMetadata SetMod(IMod mod, IModProvidedApi api) + public IModMetadata SetMod(IMod mod) { this.Mod = mod; + return this; + } + + /// <summary>Set the mod-provided API instance.</summary> + /// <param name="api">The mod-provided API.</param> + public IModMetadata SetApi(object api) + { this.Api = api; return this; } diff --git a/src/SMAPI/IMod.cs b/src/SMAPI/IMod.cs index 35ac7c0f..44ef32c9 100644 --- a/src/SMAPI/IMod.cs +++ b/src/SMAPI/IMod.cs @@ -1,4 +1,4 @@ -namespace StardewModdingAPI +namespace StardewModdingAPI { /// <summary>The implementation for a Stardew Valley mod.</summary> public interface IMod @@ -22,5 +22,8 @@ /// <summary>The mod entry point, called after the mod is first loaded.</summary> /// <param name="helper">Provides simplified APIs for writing mods.</param> void Entry(IModHelper helper); + + /// <summary>Get an API that other mods can access. This is always called after <see cref="Entry"/>.</summary> + object GetApi(); } -}
\ No newline at end of file +} diff --git a/src/SMAPI/IModProvidedApi.cs b/src/SMAPI/IModProvidedApi.cs deleted file mode 100644 index 9884ca78..00000000 --- a/src/SMAPI/IModProvidedApi.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace StardewModdingAPI -{ - /// <summary>An API provided by a mod for other mods to use.</summary> - /// <remarks>This is a marker interface. Each mod can only have one implementation of <see cref="IModProvidedApi"/>.</remarks> - public interface IModProvidedApi { } -} diff --git a/src/SMAPI/IModRegistry.cs b/src/SMAPI/IModRegistry.cs index fd71d72a..f84cfcfb 100644 --- a/src/SMAPI/IModRegistry.cs +++ b/src/SMAPI/IModRegistry.cs @@ -19,6 +19,6 @@ namespace StardewModdingAPI /// <summary>Get the API provided by a mod, or <c>null</c> if it has none. This signature requires using the <see cref="IModHelper.Reflection"/> API to access the API's properties and methods.</summary> /// <param name="uniqueID">The mod's unique ID.</param> - IModProvidedApi GetApi(string uniqueID); + object GetApi(string uniqueID); } } diff --git a/src/SMAPI/Mod.cs b/src/SMAPI/Mod.cs index ee75ba54..3a753afc 100644 --- a/src/SMAPI/Mod.cs +++ b/src/SMAPI/Mod.cs @@ -25,6 +25,9 @@ namespace StardewModdingAPI /// <param name="helper">Provides simplified APIs for writing mods.</param> public abstract void Entry(IModHelper helper); + /// <summary>Get an API that other mods can access. This is always called after <see cref="Entry"/>.</summary> + public virtual object GetApi() => null; + /// <summary>Release or reset unmanaged resources.</summary> public void Dispose() { diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs index 6330cc1a..e7552630 100644 --- a/src/SMAPI/Program.cs +++ b/src/SMAPI/Program.cs @@ -711,11 +711,9 @@ namespace StardewModdingAPI modHelper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, translationHelper); } - // get mod instances + // get mod instance if (!this.TryLoadModEntry(modAssembly, error => TrackSkip(metadata, error), out Mod mod)) continue; - if (this.TryLoadModProvidedApi(modAssembly, modHelper, monitor, error => this.Monitor.Log($"Failed loading {metadata.DisplayName}'s mod-provided API. Integrations may not work correctly. Error: {error}", LogLevel.Warn), out IModProvidedApi api)) - this.Monitor.Log($" Found mod-provided API ({api.GetType().FullName}).", LogLevel.Trace); // init mod mod.ModManifest = manifest; @@ -723,7 +721,7 @@ namespace StardewModdingAPI mod.Monitor = monitor; // track mod - metadata.SetMod(mod, api); + metadata.SetMod(mod); this.ModRegistry.Add(metadata); } catch (Exception ex) @@ -796,6 +794,19 @@ namespace StardewModdingAPI { this.Monitor.Log($"{metadata.DisplayName} failed on entry and might not work correctly. Technical details:\n{ex.GetLogSummary()}", LogLevel.Error); } + + // get mod API + try + { + object api = metadata.Mod.GetApi(); + if (api != null) + this.Monitor.Log($" Found mod-provided API ({api.GetType().FullName}).", LogLevel.Trace); + metadata.SetApi(api); + } + catch (Exception ex) + { + this.Monitor.Log($"Failed loading mod-provided API for {metadata.DisplayName}. Integrations with other mods may not work. Error: {ex.GetLogSummary()}", LogLevel.Error); + } } // invalidate cache entries when needed @@ -865,73 +876,6 @@ namespace StardewModdingAPI return true; } - /// <summary>Load a mod's <see cref="IModProvidedApi"/> implementation.</summary> - /// <param name="modAssembly">The mod assembly.</param> - /// <param name="modHelper">The mod's helper instance.</param> - /// <param name="monitor">The mod's monitor instance.</param> - /// <param name="onError">A callback invoked when loading fails.</param> - /// <param name="api">The loaded instance.</param> - private bool TryLoadModProvidedApi(Assembly modAssembly, IModHelper modHelper, IMonitor monitor, Action<string> onError, out IModProvidedApi api) - { - api = null; - - // find type - TypeInfo[] apis = modAssembly.DefinedTypes.Where(type => typeof(IModProvidedApi).IsAssignableFrom(type) && !type.IsAbstract).Take(2).ToArray(); - if (apis.Length == 0) - return false; - if (apis.Length > 1) - { - onError($"its DLL contains multiple '{nameof(IModProvidedApi)}' implementations."); - return false; - } - - // get constructor - ConstructorInfo constructor = ( - from constr in apis[0].GetConstructors() - let args = constr.GetParameters() - where - !args.Any() - || args.All(arg => typeof(IModHelper).IsAssignableFrom(arg.ParameterType) || typeof(IMonitor).IsAssignableFrom(arg.ParameterType)) - orderby args.Length descending - select constr - ).FirstOrDefault(); - if (constructor == null) - { - onError($"its {nameof(IModProvidedApi)} must have a constructor with zero arguments, or only arguments of type {nameof(IModHelper)} or {nameof(IMonitor)}."); - return false; - } - - // construct instance - try - { - // prepare constructor args - ParameterInfo[] args = constructor.GetParameters(); - object[] values = new object[args.Length]; - for (int i = 0; i < args.Length; i++) - { - if (typeof(IModHelper).IsAssignableFrom(args[i].ParameterType)) - values[i] = modHelper; - else if (typeof(IMonitor).IsAssignableFrom(args[i].ParameterType)) - values[i] = monitor; - else - { - // shouldn't happen - onError($"its {nameof(IModProvidedApi)} instance's constructor has unexpected argument type {args[i].ParameterType.FullName}."); - return false; - } - } - - // instantiate - api = (IModProvidedApi)constructor.Invoke(values); - return true; - } - catch (Exception ex) - { - onError($"its {nameof(IModProvidedApi)} couldn't be constructed: {ex.GetLogSummary()}"); - return false; - } - } - /// <summary>Reload translations for all mods.</summary> private void ReloadTranslations() { diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 579ed487..0db94843 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -114,7 +114,6 @@ <Compile Include="IReflectedField.cs" /> <Compile Include="IReflectedMethod.cs" /> <Compile Include="IReflectedProperty.cs" /> - <Compile Include="IModProvidedApi.cs" /> <Compile Include="Metadata\CoreAssets.cs" /> <Compile Include="ContentSource.cs" /> <Compile Include="Events\ContentEvents.cs" /> |