diff options
-rw-r--r-- | docs/release-notes.md | 3 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/CommandHelper.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs | 82 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/ModHelper.cs | 59 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 5 | ||||
-rw-r--r-- | src/SMAPI/ICommandHelper.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/IContentPackHelper.cs | 27 | ||||
-rw-r--r-- | src/SMAPI/IModHelper.cs | 22 | ||||
-rw-r--r-- | src/SMAPI/StardewModdingAPI.csproj | 2 |
9 files changed, 136 insertions, 68 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index 6c10b4ac..de0bdf71 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,9 @@ * Fixed invalid NPC data propagated when a mod changes NPC dispositions. * Fixed `Display.RenderedWorld` event broken in SMAPI 2.9.1. +* For modders: + * Added dedicated content pack API. + ## 2.9.1 * For players: * Fixed crash in SMAPI 2.9 when constructing certain buildings. diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs index 5a3304f3..a4fd21c1 100644 --- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs @@ -6,7 +6,7 @@ namespace StardewModdingAPI.Framework.ModHelpers internal class CommandHelper : BaseHelper, ICommandHelper { /********* - ** Accessors + ** Properties *********/ /// <summary>The mod using this instance.</summary> private readonly IModMetadata Mod; diff --git a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs new file mode 100644 index 00000000..c4b86cda --- /dev/null +++ b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using StardewModdingAPI.Toolkit.Serialisation.Models; + +namespace StardewModdingAPI.Framework.ModHelpers +{ + /// <summary>Provides an API for managing content packs.</summary> + internal class ContentPackHelper : BaseHelper, IContentPackHelper + { + /********* + ** Properties + *********/ + /// <summary>The content packs loaded for this mod.</summary> + private readonly Lazy<IContentPack[]> ContentPacks; + + /// <summary>Create a temporary content pack.</summary> + private readonly Func<string, IManifest, IContentPack> CreateContentPack; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="modID">The unique ID of the relevant mod.</param> + /// <param name="contentPacks">The content packs loaded for this mod.</param> + /// <param name="createContentPack">Create a temporary content pack.</param> + public ContentPackHelper(string modID, Lazy<IContentPack[]> contentPacks, Func<string, IManifest, IContentPack> createContentPack) + : base(modID) + { + this.ContentPacks = contentPacks; + this.CreateContentPack = createContentPack; + } + + /// <summary>Get all content packs loaded for this mod.</summary> + public IEnumerable<IContentPack> GetOwned() + { + return this.ContentPacks.Value; + } + + /// <summary>Create a temporary content pack to read files from a directory. This will generate fake manifest data; any <c>manifest.json</c> in the directory will be ignored. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary> + /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> + public IContentPack CreateFake(string directoryPath) + { + string id = Guid.NewGuid().ToString("N"); + return this.CreateFake(directoryPath, id, id, id, id, new SemanticVersion(1, 0, 0)); + } + + /// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary> + /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> + /// <param name="id">The content pack's unique ID.</param> + /// <param name="name">The content pack name.</param> + /// <param name="description">The content pack description.</param> + /// <param name="author">The content pack author's name.</param> + /// <param name="version">The content pack version.</param> + public IContentPack CreateFake(string directoryPath, string id, string name, string description, string author, ISemanticVersion version) + { + // validate + if (string.IsNullOrWhiteSpace(directoryPath)) + throw new ArgumentNullException(nameof(directoryPath)); + if (string.IsNullOrWhiteSpace(id)) + throw new ArgumentNullException(nameof(id)); + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullException(nameof(name)); + if (!Directory.Exists(directoryPath)) + throw new ArgumentException($"Can't create content pack for directory path '{directoryPath}' because no such directory exists."); + + // create manifest + IManifest manifest = new Manifest( + uniqueID: id, + name: name, + author: author, + description: description, + version: version, + contentPackFor: this.ModID + ); + + // create content pack + return this.CreateContentPack(directoryPath, manifest); + } + } +} diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 070d9c65..87d405e9 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -15,12 +15,6 @@ namespace StardewModdingAPI.Framework.ModHelpers /********* ** Properties *********/ - /// <summary>The content packs loaded for this mod.</summary> - private readonly Lazy<IContentPack[]> ContentPacks; - - /// <summary>Create a transitional content pack.</summary> - private readonly Func<string, IManifest, IContentPack> CreateContentPack; - #if !SMAPI_3_0_STRICT /// <summary>Manages deprecation warnings.</summary> private readonly DeprecationManager DeprecationManager; @@ -44,6 +38,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <summary>An API for loading content assets.</summary> public IContentHelper Content { get; } + /// <summary>An API for managing content packs.</summary> + public IContentPackHelper ContentPacks { get; } + /// <summary>An API for reading and writing persistent mod data.</summary> public IDataHelper Data { get; } @@ -76,18 +73,17 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="inputState">Manages the game's input state.</param> /// <param name="events">Manages access to events raised by SMAPI.</param> /// <param name="contentHelper">An API for loading content assets.</param> + /// <param name="contentPackHelper">An API for managing content packs.</param> /// <param name="commandHelper">An API for managing console commands.</param> /// <param name="dataHelper">An API for reading and writing persistent mod data.</param> /// <param name="modRegistry">an API for fetching metadata about loaded mods.</param> /// <param name="reflectionHelper">An API for accessing private game code.</param> /// <param name="multiplayer">Provides multiplayer utilities.</param> /// <param name="translationHelper">An API for reading translations stored in the mod's <c>i18n</c> folder.</param> - /// <param name="contentPacks">The content packs loaded for this mod.</param> - /// <param name="createContentPack">Create a transitional content pack.</param> /// <param name="deprecationManager">Manages deprecation warnings.</param> /// <exception cref="ArgumentNullException">An argument is null or empty.</exception> /// <exception cref="InvalidOperationException">The <paramref name="modDirectory"/> path does not exist on disk.</exception> - public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, SInputState inputState, IModEvents events, IContentHelper contentHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, Func<IContentPack[]> contentPacks, Func<string, IManifest, IContentPack> createContentPack, DeprecationManager deprecationManager) + public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, SInputState inputState, IModEvents events, IContentHelper contentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper, DeprecationManager deprecationManager) : base(modID) { // validate directory @@ -99,6 +95,7 @@ namespace StardewModdingAPI.Framework.ModHelpers // initialise this.DirectoryPath = modDirectory; this.Content = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper)); + this.ContentPacks = contentPackHelper ?? throw new ArgumentNullException(nameof(contentPackHelper)); this.Data = dataHelper ?? throw new ArgumentNullException(nameof(dataHelper)); this.Input = new InputHelper(modID, inputState); this.ModRegistry = modRegistry ?? throw new ArgumentNullException(nameof(modRegistry)); @@ -106,8 +103,6 @@ namespace StardewModdingAPI.Framework.ModHelpers this.Reflection = reflectionHelper ?? throw new ArgumentNullException(nameof(reflectionHelper)); this.Multiplayer = multiplayer ?? throw new ArgumentNullException(nameof(multiplayer)); this.Translation = translationHelper ?? throw new ArgumentNullException(nameof(translationHelper)); - this.ContentPacks = new Lazy<IContentPack[]>(contentPacks); - this.CreateContentPack = createContentPack; this.Events = events; #if !SMAPI_3_0_STRICT this.JsonHelper = jsonHelper ?? throw new ArgumentNullException(nameof(jsonHelper)); @@ -171,39 +166,6 @@ namespace StardewModdingAPI.Framework.ModHelpers /**** ** Content packs ****/ - /// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary> - /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> - /// <param name="id">The content pack's unique ID.</param> - /// <param name="name">The content pack name.</param> - /// <param name="description">The content pack description.</param> - /// <param name="author">The content pack author's name.</param> - /// <param name="version">The content pack version.</param> - public IContentPack CreateTemporaryContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version) - { - // validate - if (string.IsNullOrWhiteSpace(directoryPath)) - throw new ArgumentNullException(nameof(directoryPath)); - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); - if (!Directory.Exists(directoryPath)) - throw new ArgumentException($"Can't create content pack for directory path '{directoryPath}' because no such directory exists."); - - // create manifest - IManifest manifest = new Manifest( - uniqueID: id, - name: name, - author: author, - description: description, - version: version, - contentPackFor: this.ModID - ); - - // create content pack - return this.CreateContentPack(directoryPath, manifest); - } - #if !SMAPI_3_0_STRICT /// <summary>Manually create a transitional content pack to support pre-SMAPI content packs. This provides a way to access legacy content packs using the SMAPI content pack APIs, but the content pack will not be visible in the log or validated by SMAPI.</summary> /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> @@ -212,19 +174,20 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <param name="description">The content pack description.</param> /// <param name="author">The content pack author's name.</param> /// <param name="version">The content pack version.</param> - [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.CreateTemporaryContentPack) + " instead")] + [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.CreateFake) + " instead")] public IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version) { this.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.Notice); - return this.CreateTemporaryContentPack(directoryPath, id, name, description, author, version); + return this.ContentPacks.CreateFake(directoryPath, id, name, description, author, version); } -#endif /// <summary>Get all content packs loaded for this mod.</summary> + [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.GetOwned) + " instead")] public IEnumerable<IContentPack> GetContentPacks() { - return this.ContentPacks.Value; + return this.ContentPacks.GetOwned(); } +#endif /**** ** Disposal diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 800b9c09..76b091d0 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1014,20 +1014,21 @@ namespace StardewModdingAPI.Framework IModEvents events = new ModEvents(mod, this.EventManager); ICommandHelper commandHelper = new CommandHelper(mod, this.GameInstance.CommandManager); IContentHelper contentHelper = new ContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor); + IContentPackHelper contentPackHelper = new ContentPackHelper(manifest.UniqueID, new Lazy<IContentPack[]>(GetContentPacks), CreateFakeContentPack); IDataHelper dataHelper = new DataHelper(manifest.UniqueID, mod.DirectoryPath, jsonHelper); IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, mod.DisplayName, this.Reflection, this.DeprecationManager); IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry, proxyFactory, monitor); IMultiplayerHelper multiplayerHelper = new MultiplayerHelper(manifest.UniqueID, this.GameInstance.Multiplayer); ITranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentCore.GetLocale(), contentCore.Language); - IContentPack CreateTransitionalContentPack(string packDirPath, IManifest packManifest) + IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest) { IMonitor packMonitor = this.GetSecondaryMonitor(packManifest.Name); IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor); return new ContentPack(packDirPath, packManifest, packContentHelper, this.Toolkit.JsonHelper); } - modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, this.Toolkit.JsonHelper, this.GameInstance.Input, events, contentHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, GetContentPacks, CreateTransitionalContentPack, this.DeprecationManager); + modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, this.Toolkit.JsonHelper, this.GameInstance.Input, events, contentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper, this.DeprecationManager); } // init mod diff --git a/src/SMAPI/ICommandHelper.cs b/src/SMAPI/ICommandHelper.cs index fb562e32..196e1051 100644 --- a/src/SMAPI/ICommandHelper.cs +++ b/src/SMAPI/ICommandHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace StardewModdingAPI { diff --git a/src/SMAPI/IContentPackHelper.cs b/src/SMAPI/IContentPackHelper.cs new file mode 100644 index 00000000..0c734432 --- /dev/null +++ b/src/SMAPI/IContentPackHelper.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace StardewModdingAPI +{ + /// <summary>Provides an API for managing content packs.</summary> + public interface IContentPackHelper : IModLinked + { + /********* + ** Public methods + *********/ + /// <summary>Get all content packs loaded for this mod.</summary> + IEnumerable<IContentPack> GetOwned(); + + /// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary> + /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> + IContentPack CreateFake(string directoryPath); + + /// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary> + /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> + /// <param name="id">The content pack's unique ID.</param> + /// <param name="name">The content pack name.</param> + /// <param name="description">The content pack description.</param> + /// <param name="author">The content pack author's name.</param> + /// <param name="version">The content pack version.</param> + IContentPack CreateFake(string directoryPath, string id, string name, string description, string author, ISemanticVersion version); + } +} diff --git a/src/SMAPI/IModHelper.cs b/src/SMAPI/IModHelper.cs index fbe3d51f..21bc5727 100644 --- a/src/SMAPI/IModHelper.cs +++ b/src/SMAPI/IModHelper.cs @@ -22,6 +22,9 @@ namespace StardewModdingAPI /// <summary>An API for loading content assets.</summary> IContentHelper Content { get; } + /// <summary>An API for managing content packs.</summary> + IContentPackHelper ContentPacks { get; } + /// <summary>An API for reading and writing persistent mod data.</summary> IDataHelper Data { get; } @@ -73,21 +76,7 @@ namespace StardewModdingAPI /// <param name="model">The model to save.</param> [Obsolete("Use " + nameof(IModHelper.Data) + "." + nameof(IDataHelper.WriteJsonFile) + " instead")] void WriteJsonFile<TModel>(string path, TModel model) where TModel : class; -#endif - - /**** - ** Content packs - ****/ - /// <summary>Create a temporary content pack to read files from a directory. Temporary content packs will not appear in the SMAPI log and update checks will not be performed.</summary> - /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> - /// <param name="id">The content pack's unique ID.</param> - /// <param name="name">The content pack name.</param> - /// <param name="description">The content pack description.</param> - /// <param name="author">The content pack author's name.</param> - /// <param name="version">The content pack version.</param> - IContentPack CreateTemporaryContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version); -#if !SMAPI_3_0_STRICT /// <summary>Manually create a transitional content pack to support pre-SMAPI content packs. This provides a way to access legacy content packs using the SMAPI content pack APIs, but the content pack will not be visible in the log or validated by SMAPI.</summary> /// <param name="directoryPath">The absolute directory path containing the content pack files.</param> /// <param name="id">The content pack's unique ID.</param> @@ -95,11 +84,12 @@ namespace StardewModdingAPI /// <param name="description">The content pack description.</param> /// <param name="author">The content pack author's name.</param> /// <param name="version">The content pack version.</param> - [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.CreateTemporaryContentPack) + " instead")] + [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.CreateFake) + " instead")] IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version); -#endif /// <summary>Get all content packs loaded for this mod.</summary> + [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.GetOwned) + " instead")] IEnumerable<IContentPack> GetContentPacks(); +#endif } } diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 49a88f37..2c769f41 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -174,6 +174,7 @@ <Compile Include="Framework\Events\ModPlayerEvents.cs" /> <Compile Include="Framework\Events\ModSpecialisedEvents.cs" /> <Compile Include="Framework\Events\ModWorldEvents.cs" /> + <Compile Include="Framework\ModHelpers\ContentPackHelper.cs" /> <Compile Include="Framework\ModHelpers\DataHelper.cs" /> <Compile Include="Framework\Networking\MessageType.cs" /> <Compile Include="Framework\Networking\ModMessageModel.cs" /> @@ -207,6 +208,7 @@ <Compile Include="Framework\StateTracking\Comparers\GenericEqualsComparer.cs" /> <Compile Include="Framework\StateTracking\FieldWatchers\ComparableListWatcher.cs" /> <Compile Include="Framework\WatcherCore.cs" /> + <Compile Include="IContentPackHelper.cs" /> <Compile Include="IDataHelper.cs" /> <Compile Include="IInputHelper.cs" /> <Compile Include="Framework\Input\SInputState.cs" /> |