From 4444b590f016ebecfc113a0dd4584723b0250f41 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 17 Feb 2018 16:34:31 -0500 Subject: add content pack feature (#436) --- src/SMAPI/Framework/ModHelpers/ModHelper.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers/ModHelper.cs') diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 665b9cf4..c73dc307 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -1,5 +1,7 @@ -using System; +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using StardewModdingAPI.Framework.Serialisation; namespace StardewModdingAPI.Framework.ModHelpers @@ -13,6 +15,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Encapsulates SMAPI's JSON file parsing. private readonly JsonHelper JsonHelper; + /// The content packs loaded for this mod. + private readonly IContentPack[] ContentPacks; + /********* ** Accessors @@ -48,9 +53,10 @@ namespace StardewModdingAPI.Framework.ModHelpers /// an API for fetching metadata about loaded mods. /// An API for accessing private game code. /// An API for reading translations stored in the mod's i18n folder. + /// The content packs loaded for this mod. /// An argument is null or empty. /// The path does not exist on disk. - public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, ITranslationHelper translationHelper) + public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, ITranslationHelper translationHelper, IEnumerable contentPacks) : base(modID) { // validate directory @@ -67,6 +73,7 @@ namespace StardewModdingAPI.Framework.ModHelpers this.ConsoleCommands = commandHelper ?? throw new ArgumentNullException(nameof(commandHelper)); this.Reflection = reflectionHelper ?? throw new ArgumentNullException(nameof(reflectionHelper)); this.Translation = translationHelper ?? throw new ArgumentNullException(nameof(translationHelper)); + this.ContentPacks = contentPacks.ToArray(); } /**** @@ -116,6 +123,14 @@ namespace StardewModdingAPI.Framework.ModHelpers this.JsonHelper.WriteJsonFile(path, model); } + /**** + ** Content packs + ****/ + /// Get all content packs loaded for this mod. + public IEnumerable GetContentPacks() + { + return this.ContentPacks; + } /**** ** Disposal -- cgit From b6cc17112d95345de83348dd918ed1f7711926f4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 19 Feb 2018 20:22:01 -0500 Subject: normalise path separators in read/write JSON file methods exposed to mods --- docs/release-notes.md | 1 + src/SMAPI/Framework/ContentPack.cs | 3 ++- src/SMAPI/Framework/ModHelpers/ModHelper.cs | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers/ModHelper.cs') diff --git a/docs/release-notes.md b/docs/release-notes.md index 597fe0e5..f6498f06 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -16,6 +16,7 @@ * Fixed unhelpful error when a mod exposes a non-public API. * Fixed input events being raised for keyboard buttons when a textbox is receiving input. * Fixed some JSON field names being case-sensitive. + * Fixed `helper.ReadJsonFile` and `helper.WriteJsonFile` not normalising path separators. * For SMAPI developers: * Overhauled mod DB format to be more concise, reduce the memory footprint, and support versioning/defaulting more fields. diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs index 0a8f223e..071fb872 100644 --- a/src/SMAPI/Framework/ContentPack.cs +++ b/src/SMAPI/Framework/ContentPack.cs @@ -3,6 +3,7 @@ using System.IO; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Serialisation; +using StardewModdingAPI.Framework.Utilities; using xTile; namespace StardewModdingAPI.Framework @@ -52,7 +53,7 @@ namespace StardewModdingAPI.Framework /// Returns the deserialised model, or null if the file doesn't exist or is empty. public TModel ReadJsonFile(string path) where TModel : class { - path = Path.Combine(this.DirectoryPath, path); + path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); return this.JsonHelper.ReadJsonFile(path); } diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index c73dc307..07dada7e 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using StardewModdingAPI.Framework.Serialisation; +using StardewModdingAPI.Framework.Utilities; namespace StardewModdingAPI.Framework.ModHelpers { @@ -108,7 +109,7 @@ namespace StardewModdingAPI.Framework.ModHelpers public TModel ReadJsonFile(string path) where TModel : class { - path = Path.Combine(this.DirectoryPath, path); + path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); return this.JsonHelper.ReadJsonFile(path); } @@ -119,7 +120,7 @@ namespace StardewModdingAPI.Framework.ModHelpers public void WriteJsonFile(string path, TModel model) where TModel : class { - path = Path.Combine(this.DirectoryPath, path); + path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); this.JsonHelper.WriteJsonFile(path, model); } -- cgit From ec1e5a169828d95c6cf0c779089b25627754c894 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 20 Feb 2018 19:43:05 -0500 Subject: support transitional content packs (#436) This commit adds an API to generate a content pack from an arbitrary folder, to support mods which already had their own content pack format before SMAPI standardised it. This lets them support both formats using the same APIs while they transition. --- src/SMAPI/Framework/ModHelpers/ModHelper.cs | 52 ++++++++++++++++++++++++++++- src/SMAPI/IModHelper.cs | 11 ++++++ src/SMAPI/Program.cs | 17 ++++++++-- 3 files changed, 76 insertions(+), 4 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers/ModHelper.cs') diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 07dada7e..b5758d21 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using StardewModdingAPI.Framework.Models; using StardewModdingAPI.Framework.Serialisation; using StardewModdingAPI.Framework.Utilities; @@ -19,6 +20,12 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The content packs loaded for this mod. private readonly IContentPack[] ContentPacks; + /// Create a transitional content pack. + private readonly Func CreateContentPack; + + /// Manages deprecation warnings. + private readonly DeprecationManager DeprecationManager; + /********* ** Accessors @@ -55,9 +62,11 @@ namespace StardewModdingAPI.Framework.ModHelpers /// An API for accessing private game code. /// An API for reading translations stored in the mod's i18n folder. /// The content packs loaded for this mod. + /// Create a transitional content pack. + /// Manages deprecation warnings. /// An argument is null or empty. /// The path does not exist on disk. - public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, ITranslationHelper translationHelper, IEnumerable contentPacks) + public ModHelper(string modID, string modDirectory, JsonHelper jsonHelper, IContentHelper contentHelper, ICommandHelper commandHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, ITranslationHelper translationHelper, IEnumerable contentPacks, Func createContentPack, DeprecationManager deprecationManager) : base(modID) { // validate directory @@ -75,6 +84,8 @@ namespace StardewModdingAPI.Framework.ModHelpers this.Reflection = reflectionHelper ?? throw new ArgumentNullException(nameof(reflectionHelper)); this.Translation = translationHelper ?? throw new ArgumentNullException(nameof(translationHelper)); this.ContentPacks = contentPacks.ToArray(); + this.CreateContentPack = createContentPack; + this.DeprecationManager = deprecationManager; } /**** @@ -127,6 +138,45 @@ namespace StardewModdingAPI.Framework.ModHelpers /**** ** Content packs ****/ + /// 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. + /// The absolute directory path containing the content pack files. + /// The content pack's unique ID. + /// The content pack name. + /// The content pack description. + /// The content pack author's name. + /// The content pack version. + [Obsolete("This method supports mods which previously had their own content packs, and shouldn't be used by new mods. It will be removed in SMAPI 3.0.")] + public IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version) + { + // raise deprecation notice + this.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.Notice); + + // 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 + { + Name = name, + Author = author, + Description = description, + Version = version, + UniqueID = id, + UpdateKeys = new string[0], + ContentPackFor = new ManifestContentPackFor { UniqueID = this.ModID } + }; + + // create content pack + return this.CreateContentPack(directoryPath, manifest); + } + /// Get all content packs loaded for this mod. public IEnumerable GetContentPacks() { diff --git a/src/SMAPI/IModHelper.cs b/src/SMAPI/IModHelper.cs index 96265c85..e9554fdc 100644 --- a/src/SMAPI/IModHelper.cs +++ b/src/SMAPI/IModHelper.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace StardewModdingAPI @@ -60,6 +61,16 @@ namespace StardewModdingAPI /**** ** Content packs ****/ + /// 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. + /// The absolute directory path containing the content pack files. + /// The content pack's unique ID. + /// The content pack name. + /// The content pack description. + /// The content pack author's name. + /// The content pack version. + [Obsolete("This method supports mods which previously had their own content packs, and shouldn't be used by new mods. It will be removed in SMAPI 3.0.")] + IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version); + /// Get all content packs loaded for this mod. IEnumerable GetContentPacks(); } diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs index 2d0908a1..aecf5b30 100644 --- a/src/SMAPI/Program.cs +++ b/src/SMAPI/Program.cs @@ -88,6 +88,9 @@ namespace StardewModdingAPI new Regex(@"^(?:FRUIT )?TREE: IsClient:(?:True|False) randomOutput: \d+$", RegexOptions.Compiled | RegexOptions.CultureInvariant) }; + /// Encapsulates SMAPI's JSON file parsing. + private readonly JsonHelper JsonHelper = new JsonHelper(); + /********* ** Public methods @@ -360,14 +363,14 @@ namespace StardewModdingAPI ModResolver resolver = new ModResolver(); // load manifests - IModMetadata[] mods = resolver.ReadManifests(Constants.ModPath, new JsonHelper(), modDatabase).ToArray(); + IModMetadata[] mods = resolver.ReadManifests(Constants.ModPath, this.JsonHelper, modDatabase).ToArray(); resolver.ValidateManifests(mods, Constants.ApiVersion, Constants.GetUpdateUrl); // process dependencies mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); // load mods - this.LoadMods(mods, new JsonHelper(), this.ContentManager); + this.LoadMods(mods, this.JsonHelper, this.ContentManager); // check for updates this.CheckForUpdatesAsync(mods); @@ -755,7 +758,15 @@ namespace StardewModdingAPI IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, metadata.DisplayName, this.Reflection, this.DeprecationManager); IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry, proxyFactory, monitor); ITranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentManager.GetLocale(), contentManager.GetCurrentLanguage()); - modHelper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, translationHelper, contentPacks); + + IContentPack CreateTransitionalContentPack(string packDirPath, IManifest packManifest) + { + IMonitor packMonitor = this.GetSecondaryMonitor(packManifest.Name); + IContentHelper packContentHelper = new ContentHelper(contentManager, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor); + return new ContentPack(packDirPath, packManifest, packContentHelper, this.JsonHelper); + } + + modHelper = new ModHelper(manifest.UniqueID, metadata.DirectoryPath, jsonHelper, contentHelper, commandHelper, modRegistryHelper, reflectionHelper, translationHelper, contentPacks, CreateTransitionalContentPack, this.DeprecationManager); } // get mod instance -- cgit