From bad2ac2a29b8775be97133e4c4cfb67a4a7406ee Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 24 Feb 2019 19:56:08 -0500 Subject: remove deprecated APIs (#606) --- src/SMAPI/Framework/ModHelpers/ModHelper.cs | 71 +---------------------------- 1 file changed, 1 insertion(+), 70 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 6c9838c9..86e8eb28 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.IO; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Input; using StardewModdingAPI.Toolkit.Serialisation; -using StardewModdingAPI.Toolkit.Serialisation.Models; -using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.Framework.ModHelpers { @@ -18,11 +15,6 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The full path to the mod's folder. public string DirectoryPath { get; } -#if !SMAPI_3_0_STRICT - /// Encapsulates SMAPI's JSON file parsing. - private readonly JsonHelper JsonHelper; -#endif - /// Manages access to events raised by SMAPI, which let your mod react when something happens in the game. public IModEvents Events { get; } @@ -60,7 +52,6 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Construct an instance. /// The mod's unique ID. /// The full path to the mod's folder. - /// Encapsulate SMAPI's JSON parsing. /// Manages the game's input state. /// Manages access to events raised by SMAPI. /// An API for loading content assets. @@ -73,7 +64,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// An API for reading translations stored in the mod's i18n folder. /// An argument is null or empty. /// The path does not exist on disk. - 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) + public ModHelper(string modID, string modDirectory, SInputState inputState, IModEvents events, IContentHelper contentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper) : base(modID) { // validate directory @@ -94,9 +85,6 @@ namespace StardewModdingAPI.Framework.ModHelpers this.Multiplayer = multiplayer ?? throw new ArgumentNullException(nameof(multiplayer)); this.Translation = translationHelper ?? throw new ArgumentNullException(nameof(translationHelper)); this.Events = events; -#if !SMAPI_3_0_STRICT - this.JsonHelper = jsonHelper ?? throw new ArgumentNullException(nameof(jsonHelper)); -#endif } /**** @@ -121,63 +109,6 @@ namespace StardewModdingAPI.Framework.ModHelpers this.Data.WriteJsonFile("config.json", config); } -#if !SMAPI_3_0_STRICT - /**** - ** Generic JSON files - ****/ - /// Read a JSON file. - /// The model type. - /// The file path relative to the mod directory. - /// Returns the deserialised model, or null if the file doesn't exist or is empty. - [Obsolete("Use " + nameof(ModHelper.Data) + "." + nameof(IDataHelper.ReadJsonFile) + " instead")] - public TModel ReadJsonFile(string path) - where TModel : class - { - path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); - return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data) - ? data - : null; - } - - /// Save to a JSON file. - /// The model type. - /// The file path relative to the mod directory. - /// The model to save. - [Obsolete("Use " + nameof(ModHelper.Data) + "." + nameof(IDataHelper.WriteJsonFile) + " instead")] - public void WriteJsonFile(string path, TModel model) - where TModel : class - { - path = Path.Combine(this.DirectoryPath, PathUtilities.NormalisePathSeparators(path)); - this.JsonHelper.WriteJsonFile(path, model); - } -#endif - - /**** - ** Content packs - ****/ -#if !SMAPI_3_0_STRICT - /// 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("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.CreateTemporary) + " instead")] - public IContentPack CreateTransitionalContentPack(string directoryPath, string id, string name, string description, string author, ISemanticVersion version) - { - SCore.DeprecationManager.Warn($"{nameof(IModHelper)}.{nameof(IModHelper.CreateTransitionalContentPack)}", "2.5", DeprecationLevel.PendingRemoval); - return this.ContentPacks.CreateTemporary(directoryPath, id, name, description, author, version); - } - - /// Get all content packs loaded for this mod. - [Obsolete("Use " + nameof(IModHelper) + "." + nameof(IModHelper.ContentPacks) + "." + nameof(IContentPackHelper.GetOwned) + " instead")] - public IEnumerable GetContentPacks() - { - return this.ContentPacks.GetOwned(); - } -#endif - /**** ** Disposal ****/ -- cgit From 161618d0d92bad5b31e2780c8899c73339d38b62 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 15 Apr 2019 21:45:27 -0400 Subject: fix cache miss when not playing in English (#634) --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 8b86fdeb..e02748d2 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -126,7 +126,7 @@ namespace StardewModdingAPI.Framework.ModHelpers this.FixCustomTilesheetPaths(map, relativeMapPath: key); // inject map - this.ModContentManager.Inject(internalKey, map); + this.ModContentManager.Inject(internalKey, map, this.CurrentLocaleConstant); return (T)(object)map; } -- cgit From d4e09c5a8524fad43e2ecf94ade86673a94b85a5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 May 2019 01:59:32 -0400 Subject: fix tilesheets seasonalised when loading an indoor map --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index e02748d2..5c4c3bca 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -13,6 +13,7 @@ using StardewModdingAPI.Toolkit.Utilities; using StardewValley; using xTile; using xTile.Format; +using xTile.ObjectModel; using xTile.Tiles; namespace StardewModdingAPI.Framework.ModHelpers @@ -247,6 +248,7 @@ namespace StardewModdingAPI.Framework.ModHelpers return; relativeMapPath = this.ModContentManager.AssertAndNormaliseAssetName(relativeMapPath); // Mono's Path.GetDirectoryName doesn't handle Windows dir separators string relativeMapFolder = Path.GetDirectoryName(relativeMapPath) ?? ""; // folder path containing the map, relative to the mod folder + bool isOutdoors = map.Properties.TryGetValue("Outdoors", out PropertyValue outdoorsProperty) && outdoorsProperty != null; // fix tilesheets foreach (TileSheet tilesheet in map.TileSheets) @@ -259,7 +261,7 @@ namespace StardewModdingAPI.Framework.ModHelpers // get seasonal name (if applicable) string seasonalImageSource = null; - if (Context.IsSaveLoaded && Game1.currentSeason != null) + if (isOutdoors && Context.IsSaveLoaded && Game1.currentSeason != null) { string filename = Path.GetFileName(imageSource) ?? throw new InvalidOperationException($"The '{imageSource}' tilesheet couldn't be loaded: filename is unexpectedly null."); bool hasSeasonalPrefix = -- cgit From fff5e8c93921449afcfd1cebf7fd5616abc15d45 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 29 May 2019 22:32:52 -0400 Subject: move most mod asset loading logic into content managers (#644) This fixes mods needing to load Map assets manually before the game could load them via internal key. --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 215 +----------------------- 1 file changed, 6 insertions(+), 209 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 5c4c3bca..2d65f1f6 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -9,12 +9,8 @@ using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.ContentManagers; using StardewModdingAPI.Framework.Exceptions; -using StardewModdingAPI.Toolkit.Utilities; using StardewValley; using xTile; -using xTile.Format; -using xTile.ObjectModel; -using xTile.Tiles; namespace StardewModdingAPI.Framework.ModHelpers { @@ -31,10 +27,7 @@ namespace StardewModdingAPI.Framework.ModHelpers private readonly IContentManager GameContentManager; /// A content manager for this mod which manages files from the mod's folder. - private readonly IContentManager ModContentManager; - - /// The absolute path to the mod folder. - private readonly string ModFolderPath; + private readonly ModContentManager ModContentManager; /// The friendly mod name for use in errors. private readonly string ModName; @@ -79,8 +72,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { this.ContentCore = contentCore; this.GameContentManager = contentCore.CreateGameContentManager(this.ContentCore.GetManagedAssetPrefix(modID) + ".content"); - this.ModContentManager = contentCore.CreateModContentManager(this.ContentCore.GetManagedAssetPrefix(modID), rootDirectory: modFolderPath); - this.ModFolderPath = modFolderPath; + this.ModContentManager = contentCore.CreateModContentManager(this.ContentCore.GetManagedAssetPrefix(modID), modFolderPath, this.GameContentManager); this.ModName = modName; this.Monitor = monitor; } @@ -93,8 +85,6 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The content asset couldn't be loaded (e.g. because it doesn't exist). public T Load(string key, ContentSource source = ContentSource.ModFolder) { - SContentLoadException GetContentError(string reasonPhrase) => new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: {reasonPhrase}."); - try { this.AssertAndNormaliseAssetName(key); @@ -104,38 +94,10 @@ namespace StardewModdingAPI.Framework.ModHelpers return this.GameContentManager.Load(key); case ContentSource.ModFolder: - // get file - FileInfo file = this.GetModFile(key); - if (!file.Exists) - throw GetContentError($"there's no matching file at path '{file.FullName}'."); - string internalKey = this.GetInternalModAssetKey(file); - - // try cache - if (this.ModContentManager.IsLoaded(internalKey)) - return this.ModContentManager.Load(internalKey); - - // fix map tilesheets - if (file.Extension.ToLower() == ".tbin") - { - // validate - if (typeof(T) != typeof(Map)) - throw GetContentError($"can't read file with extension '{file.Extension}' as type '{typeof(T)}'; must be type '{typeof(Map)}'."); - - // fetch & cache - FormatManager formatManager = FormatManager.Instance; - Map map = formatManager.LoadMap(file.FullName); - this.FixCustomTilesheetPaths(map, relativeMapPath: key); - - // inject map - this.ModContentManager.Inject(internalKey, map, this.CurrentLocaleConstant); - return (T)(object)map; - } - - // load through content manager - return this.ModContentManager.Load(internalKey); + return this.ModContentManager.Load(key); default: - throw GetContentError($"unknown content source '{source}'."); + throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: unknown content source '{source}'."); } } catch (Exception ex) when (!(ex is SContentLoadException)) @@ -164,8 +126,7 @@ namespace StardewModdingAPI.Framework.ModHelpers return this.GameContentManager.AssertAndNormaliseAssetName(key); case ContentSource.ModFolder: - FileInfo file = this.GetModFile(key); - return this.GetInternalModAssetKey(file); + return this.ModContentManager.GetInternalAssetKey(key); default: throw new NotSupportedException($"Unknown content source '{source}'."); @@ -201,6 +162,7 @@ namespace StardewModdingAPI.Framework.ModHelpers return this.ContentCore.InvalidateCache(predicate).Any(); } + /********* ** Private methods *********/ @@ -214,170 +176,5 @@ namespace StardewModdingAPI.Framework.ModHelpers if (Path.IsPathRooted(key)) throw new ArgumentException("The asset key must not be an absolute path."); } - - /// Get the internal key in the content cache for a mod asset. - /// The asset file. - private string GetInternalModAssetKey(FileInfo modFile) - { - string relativePath = PathUtilities.GetRelativePath(this.ModFolderPath, modFile.FullName); - return Path.Combine(this.ModContentManager.Name, relativePath); - } - - /// Fix custom map tilesheet paths so they can be found by the content manager. - /// The map whose tilesheets to fix. - /// The relative map path within the mod folder. - /// A map tilesheet couldn't be resolved. - /// - /// The game's logic for tilesheets in is a bit specialised. It boils - /// down to this: - /// * If the location is indoors or the desert, or the image source contains 'path' or 'object', it's loaded - /// as-is relative to the Content folder. - /// * Else it's loaded from Content\Maps with a seasonal prefix. - /// - /// That logic doesn't work well in our case, mainly because we have no location metadata at this point. - /// Instead we use a more heuristic approach: check relative to the map file first, then relative to - /// Content\Maps, then Content. If the image source filename contains a seasonal prefix, try for a - /// seasonal variation and then an exact match. - /// - /// While that doesn't exactly match the game logic, it's close enough that it's unlikely to make a difference. - /// - private void FixCustomTilesheetPaths(Map map, string relativeMapPath) - { - // get map info - if (!map.TileSheets.Any()) - return; - relativeMapPath = this.ModContentManager.AssertAndNormaliseAssetName(relativeMapPath); // Mono's Path.GetDirectoryName doesn't handle Windows dir separators - string relativeMapFolder = Path.GetDirectoryName(relativeMapPath) ?? ""; // folder path containing the map, relative to the mod folder - bool isOutdoors = map.Properties.TryGetValue("Outdoors", out PropertyValue outdoorsProperty) && outdoorsProperty != null; - - // fix tilesheets - foreach (TileSheet tilesheet in map.TileSheets) - { - string imageSource = tilesheet.ImageSource; - - // validate tilesheet path - if (Path.IsPathRooted(imageSource) || PathUtilities.GetSegments(imageSource).Contains("..")) - throw new ContentLoadException($"The '{imageSource}' tilesheet couldn't be loaded. Tilesheet paths must be a relative path without directory climbing (../)."); - - // get seasonal name (if applicable) - string seasonalImageSource = null; - if (isOutdoors && Context.IsSaveLoaded && Game1.currentSeason != null) - { - string filename = Path.GetFileName(imageSource) ?? throw new InvalidOperationException($"The '{imageSource}' tilesheet couldn't be loaded: filename is unexpectedly null."); - bool hasSeasonalPrefix = - filename.StartsWith("spring_", StringComparison.CurrentCultureIgnoreCase) - || filename.StartsWith("summer_", StringComparison.CurrentCultureIgnoreCase) - || filename.StartsWith("fall_", StringComparison.CurrentCultureIgnoreCase) - || filename.StartsWith("winter_", StringComparison.CurrentCultureIgnoreCase); - if (hasSeasonalPrefix && !filename.StartsWith(Game1.currentSeason + "_")) - { - string dirPath = imageSource.Substring(0, imageSource.LastIndexOf(filename, StringComparison.CurrentCultureIgnoreCase)); - seasonalImageSource = $"{dirPath}{Game1.currentSeason}_{filename.Substring(filename.IndexOf("_", StringComparison.CurrentCultureIgnoreCase) + 1)}"; - } - } - - // load best match - try - { - string key = - this.GetTilesheetAssetName(relativeMapFolder, seasonalImageSource) - ?? this.GetTilesheetAssetName(relativeMapFolder, imageSource); - if (key != null) - { - tilesheet.ImageSource = key; - continue; - } - } - catch (Exception ex) - { - throw new ContentLoadException($"The '{imageSource}' tilesheet couldn't be loaded relative to either map file or the game's content folder.", ex); - } - - // none found - throw new ContentLoadException($"The '{imageSource}' tilesheet couldn't be loaded relative to either map file or the game's content folder."); - } - } - - /// Get the actual asset name for a tilesheet. - /// The folder path containing the map, relative to the mod folder. - /// The tilesheet image source to load. - /// Returns the asset name. - /// See remarks on . - private string GetTilesheetAssetName(string modRelativeMapFolder, string imageSource) - { - if (imageSource == null) - return null; - - // check relative to map file - { - string localKey = Path.Combine(modRelativeMapFolder, imageSource); - FileInfo localFile = this.GetModFile(localKey); - if (localFile.Exists) - return this.GetActualAssetKey(localKey); - } - - // check relative to content folder - { - foreach (string candidateKey in new[] { imageSource, Path.Combine("Maps", imageSource) }) - { - string contentKey = candidateKey.EndsWith(".png") - ? candidateKey.Substring(0, candidateKey.Length - 4) - : candidateKey; - - try - { - this.Load(contentKey, ContentSource.GameContent); - return contentKey; - } - catch - { - // ignore file-not-found errors - // TODO: while it's useful to suppress an asset-not-found error here to avoid - // confusion, this is a pretty naive approach. Even if the file doesn't exist, - // the file may have been loaded through an IAssetLoader which failed. So even - // if the content file doesn't exist, that doesn't mean the error here is a - // content-not-found error. Unfortunately XNA doesn't provide a good way to - // detect the error type. - if (this.GetContentFolderFile(contentKey).Exists) - throw; - } - } - } - - // not found - return null; - } - - /// Get a file from the mod folder. - /// The asset path relative to the mod folder. - private FileInfo GetModFile(string path) - { - // try exact match - path = Path.Combine(this.ModFolderPath, this.ModContentManager.NormalisePathSeparators(path)); - FileInfo file = new FileInfo(path); - - // try with default extension - if (!file.Exists && file.Extension.ToLower() != ".xnb") - { - FileInfo result = new FileInfo(path + ".xnb"); - if (result.Exists) - file = result; - } - - return file; - } - - /// Get a file from the game's content folder. - /// The asset key. - private FileInfo GetContentFolderFile(string key) - { - // get file path - string path = Path.Combine(this.GameContentManager.FullRootDirectory, key); - if (!path.EndsWith(".xnb")) - path += ".xnb"; - - // get file - return new FileInfo(path); - } } } -- cgit From b9dec734693f50b3b32977cd505f091a2c8b8382 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 30 May 2019 17:40:21 -0400 Subject: disable mod-level asset caching (#644) This fixes an issue where some asset references could be shared between content managers, causing changes to propagate unintentionally. --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 2d65f1f6..0be9aea5 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -91,10 +91,10 @@ namespace StardewModdingAPI.Framework.ModHelpers switch (source) { case ContentSource.GameContent: - return this.GameContentManager.Load(key); + return this.GameContentManager.Load(key, this.CurrentLocaleConstant, useCache: false); case ContentSource.ModFolder: - return this.ModContentManager.Load(key); + return this.ModContentManager.Load(key, this.CurrentLocaleConstant, useCache: false); default: throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: unknown content source '{source}'."); -- cgit From 059a59a7bcb560b938f0baf54575719e57962e0c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 16 Jun 2019 14:12:54 -0400 Subject: fix error when loading a mod asset through a translated content manager (#647) --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 0be9aea5..15b164b1 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -94,7 +94,7 @@ namespace StardewModdingAPI.Framework.ModHelpers return this.GameContentManager.Load(key, this.CurrentLocaleConstant, useCache: false); case ContentSource.ModFolder: - return this.ModContentManager.Load(key, this.CurrentLocaleConstant, useCache: false); + return this.ModContentManager.Load(key, Constants.DefaultLanguage, useCache: false); default: throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: unknown content source '{source}'."); -- cgit From fd77ae93d59222d70c86aebfc044f3af11063372 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 9 Aug 2019 01:18:05 -0400 Subject: fix typos and inconsistent spelling --- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 14 +++++++------- src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs | 4 ++-- src/SMAPI/Framework/ModHelpers/DataHelper.cs | 12 ++++++------ src/SMAPI/Framework/ModHelpers/ModHelper.cs | 4 ++-- src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs | 4 ++-- src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 15b164b1..043ae376 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -87,7 +87,7 @@ namespace StardewModdingAPI.Framework.ModHelpers { try { - this.AssertAndNormaliseAssetName(key); + this.AssertAndNormalizeAssetName(key); switch (source) { case ContentSource.GameContent: @@ -106,12 +106,12 @@ namespace StardewModdingAPI.Framework.ModHelpers } } - /// Normalise an asset name so it's consistent with those generated by the game. This is mainly useful for string comparisons like on generated asset names, and isn't necessary when passing asset names into other content helper methods. + /// Normalize an asset name so it's consistent with those generated by the game. This is mainly useful for string comparisons like on generated asset names, and isn't necessary when passing asset names into other content helper methods. /// The asset key. [Pure] - public string NormaliseAssetName(string assetName) + public string NormalizeAssetName(string assetName) { - return this.ModContentManager.AssertAndNormaliseAssetName(assetName); + return this.ModContentManager.AssertAndNormalizeAssetName(assetName); } /// Get the underlying key in the game's content cache for an asset. This can be used to load custom map tilesheets, but should be avoided when you can use the content API instead. This does not validate whether the asset exists. @@ -123,7 +123,7 @@ namespace StardewModdingAPI.Framework.ModHelpers switch (source) { case ContentSource.GameContent: - return this.GameContentManager.AssertAndNormaliseAssetName(key); + return this.GameContentManager.AssertAndNormalizeAssetName(key); case ContentSource.ModFolder: return this.ModContentManager.GetInternalAssetKey(key); @@ -170,9 +170,9 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The asset key to check. /// The asset key is empty or contains invalid characters. [SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local", Justification = "Parameter is only used for assertion checks by design.")] - private void AssertAndNormaliseAssetName(string key) + private void AssertAndNormalizeAssetName(string key) { - this.ModContentManager.AssertAndNormaliseAssetName(key); + this.ModContentManager.AssertAndNormalizeAssetName(key); if (Path.IsPathRooted(key)) throw new ArgumentException("The asset key must not be an absolute path."); } diff --git a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs index 34f24d65..acdd82a0 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using StardewModdingAPI.Toolkit.Serialisation.Models; +using StardewModdingAPI.Toolkit.Serialization.Models; namespace StardewModdingAPI.Framework.ModHelpers { @@ -38,7 +38,7 @@ namespace StardewModdingAPI.Framework.ModHelpers return this.ContentPacks.Value; } - /// Create a temporary content pack to read files from a directory, using randomised manifest fields. This will generate fake manifest data; any manifest.json in the directory will be ignored. Temporary content packs will not appear in the SMAPI log and update checks will not be performed. + /// Create a temporary content pack to read files from a directory, using randomized manifest fields. This will generate fake manifest data; any manifest.json in the directory will be ignored. Temporary content packs will not appear in the SMAPI log and update checks will not be performed. /// The absolute directory path containing the content pack files. public IContentPack CreateFake(string directoryPath) { diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs index 3b5c1752..cc08c42b 100644 --- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs @@ -1,7 +1,7 @@ using System; using System.IO; using Newtonsoft.Json; -using StardewModdingAPI.Toolkit.Serialisation; +using StardewModdingAPI.Toolkit.Serialization; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; @@ -40,14 +40,14 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Read data from a JSON file in the mod's folder. /// The model type. This should be a plain class that has public properties for the data you want. The properties can be complex types. /// The file path relative to the mod folder. - /// Returns the deserialised model, or null if the file doesn't exist or is empty. + /// Returns the deserialized model, or null if the file doesn't exist or is empty. /// The is not relative or contains directory climbing (../). public TModel ReadJsonFile(string path) where TModel : class { if (!PathUtilities.IsSafeRelativePath(path)) throw new InvalidOperationException($"You must call {nameof(IModHelper.Data)}.{nameof(this.ReadJsonFile)} with a relative path."); - path = Path.Combine(this.ModFolderPath, PathUtilities.NormalisePathSeparators(path)); + path = Path.Combine(this.ModFolderPath, PathUtilities.NormalizePathSeparators(path)); return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data) ? data : null; @@ -63,7 +63,7 @@ namespace StardewModdingAPI.Framework.ModHelpers if (!PathUtilities.IsSafeRelativePath(path)) throw new InvalidOperationException($"You must call {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteJsonFile)} with a relative path (without directory climbing)."); - path = Path.Combine(this.ModFolderPath, PathUtilities.NormalisePathSeparators(path)); + path = Path.Combine(this.ModFolderPath, PathUtilities.NormalizePathSeparators(path)); this.JsonHelper.WriteJsonFile(path, data); } @@ -83,7 +83,7 @@ namespace StardewModdingAPI.Framework.ModHelpers throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)"); return Game1.CustomData.TryGetValue(this.GetSaveFileKey(key), out string value) - ? this.JsonHelper.Deserialise(value) + ? this.JsonHelper.Deserialize(value) : null; } @@ -101,7 +101,7 @@ namespace StardewModdingAPI.Framework.ModHelpers string internalKey = this.GetSaveFileKey(key); if (data != null) - Game1.CustomData[internalKey] = this.JsonHelper.Serialise(data, Formatting.None); + Game1.CustomData[internalKey] = this.JsonHelper.Serialize(data, Formatting.None); else Game1.CustomData.Remove(internalKey); } diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 86e8eb28..25401e23 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -2,7 +2,7 @@ using System; using System.IO; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Input; -using StardewModdingAPI.Toolkit.Serialisation; +using StardewModdingAPI.Toolkit.Serialization; namespace StardewModdingAPI.Framework.ModHelpers { @@ -73,7 +73,7 @@ namespace StardewModdingAPI.Framework.ModHelpers if (!Directory.Exists(modDirectory)) throw new InvalidOperationException("The specified mod directory does not exist."); - // initialise + // initialize this.DirectoryPath = modDirectory; this.Content = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper)); this.ContentPacks = contentPackHelper ?? throw new ArgumentNullException(nameof(contentPackHelper)); diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs index 8330e078..24bed3bb 100644 --- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs @@ -75,9 +75,9 @@ namespace StardewModdingAPI.Framework.ModHelpers public TInterface GetApi(string uniqueID) where TInterface : class { // validate - if (!this.Registry.AreAllModsInitialised) + if (!this.Registry.AreAllModsInitialized) { - this.Monitor.Log("Tried to access a mod-provided API before all mods were initialised.", LogLevel.Error); + this.Monitor.Log("Tried to access a mod-provided API before all mods were initialized.", LogLevel.Error); return null; } if (!typeof(TInterface).IsInterface) diff --git a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs index 0ce72a9e..86c327ed 100644 --- a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs @@ -5,7 +5,7 @@ using StardewModdingAPI.Framework.Reflection; namespace StardewModdingAPI.Framework.ModHelpers { /// Provides helper methods for accessing private game code. - /// This implementation searches up the type hierarchy, and caches the reflected fields and methods with a sliding expiry (to optimise performance without unnecessary memory usage). + /// This implementation searches up the type hierarchy, and caches the reflected fields and methods with a sliding expiry (to optimize performance without unnecessary memory usage). internal class ReflectionHelper : BaseHelper, IReflectionHelper { /********* -- cgit From 673510b3941dd35a127e4f4a8a406f34b72b6a66 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 1 Oct 2019 20:04:58 -0400 Subject: remove unused translation field & method --- src/SMAPI/Framework/ModHelpers/TranslationHelper.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs index 3252e047..65850384 100644 --- a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs @@ -11,9 +11,6 @@ namespace StardewModdingAPI.Framework.ModHelpers /********* ** Fields *********/ - /// The name of the relevant mod for error messages. - private readonly string ModName; - /// The translations for each locale. private readonly IDictionary> All = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); @@ -36,15 +33,11 @@ namespace StardewModdingAPI.Framework.ModHelpers *********/ /// Construct an instance. /// The unique ID of the relevant mod. - /// The name of the relevant mod for error messages. /// The initial locale. /// The game's current language code. - public TranslationHelper(string modID, string modName, string locale, LocalizedContentManager.LanguageCode languageCode) + public TranslationHelper(string modID, string locale, LocalizedContentManager.LanguageCode languageCode) : base(modID) { - // save data - this.ModName = modName; - // set locale this.SetLocale(locale, languageCode); } @@ -60,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers public Translation Get(string key) { this.ForLocale.TryGetValue(key, out Translation translation); - return translation ?? new Translation(this.ModName, this.Locale, key, null); + return translation ?? new Translation(this.Locale, key, null); } /// Get a translation for the current locale. @@ -105,7 +98,7 @@ namespace StardewModdingAPI.Framework.ModHelpers foreach (var pair in translations) { if (!this.ForLocale.ContainsKey(pair.Key)) - this.ForLocale.Add(pair.Key, new Translation(this.ModName, this.Locale, pair.Key, pair.Value)); + this.ForLocale.Add(pair.Key, new Translation(this.Locale, pair.Key, pair.Value)); } } } -- cgit From 845deb43d60147603ec31fe4ae5fd7d747556d8c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 1 Oct 2019 21:27:49 -0400 Subject: add support for core translation files --- .../Framework/ModHelpers/TranslationHelper.cs | 78 +++------------------- 1 file changed, 11 insertions(+), 67 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs index 65850384..be7768e8 100644 --- a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Linq; using StardewValley; namespace StardewModdingAPI.Framework.ModHelpers @@ -11,21 +9,18 @@ namespace StardewModdingAPI.Framework.ModHelpers /********* ** Fields *********/ - /// The translations for each locale. - private readonly IDictionary> All = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); - - /// The translations for the current locale, with locale fallback taken into account. - private IDictionary ForLocale; + /// The underlying translation manager. + private readonly Translator Translator; /********* ** Accessors *********/ /// The current locale. - public string Locale { get; private set; } + public string Locale => this.Translator.Locale; /// The game's current language code. - public LocalizedContentManager.LanguageCode LocaleEnum { get; private set; } + public LocalizedContentManager.LanguageCode LocaleEnum => this.Translator.LocaleEnum; /********* @@ -38,22 +33,21 @@ namespace StardewModdingAPI.Framework.ModHelpers public TranslationHelper(string modID, string locale, LocalizedContentManager.LanguageCode languageCode) : base(modID) { - // set locale - this.SetLocale(locale, languageCode); + this.Translator = new Translator(); + this.Translator.SetLocale(locale, languageCode); } /// Get all translations for the current locale. public IEnumerable GetTranslations() { - return this.ForLocale.Values.ToArray(); + return this.Translator.GetTranslations(); } /// Get a translation for the current locale. /// The translation key. public Translation Get(string key) { - this.ForLocale.TryGetValue(key, out Translation translation); - return translation ?? new Translation(this.Locale, key, null); + return this.Translator.Get(key); } /// Get a translation for the current locale. @@ -61,21 +55,14 @@ namespace StardewModdingAPI.Framework.ModHelpers /// An object containing token key/value pairs. This can be an anonymous object (like new { value = 42, name = "Cranberries" }), a dictionary, or a class instance. public Translation Get(string key, object tokens) { - return this.Get(key).Tokens(tokens); + return this.Translator.Get(key, tokens); } /// Set the translations to use. /// The translations to use. internal TranslationHelper SetTranslations(IDictionary> translations) { - // reset translations - this.All.Clear(); - foreach (var pair in translations) - this.All[pair.Key] = new Dictionary(pair.Value, StringComparer.InvariantCultureIgnoreCase); - - // rebuild cache - this.SetLocale(this.Locale, this.LocaleEnum); - + this.Translator.SetTranslations(translations); return this; } @@ -84,50 +71,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The game's current language code. internal void SetLocale(string locale, LocalizedContentManager.LanguageCode localeEnum) { - this.Locale = locale.ToLower().Trim(); - this.LocaleEnum = localeEnum; - - this.ForLocale = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (string next in this.GetRelevantLocales(this.Locale)) - { - // skip if locale not defined - if (!this.All.TryGetValue(next, out IDictionary translations)) - continue; - - // add missing translations - foreach (var pair in translations) - { - if (!this.ForLocale.ContainsKey(pair.Key)) - this.ForLocale.Add(pair.Key, new Translation(this.Locale, pair.Key, pair.Value)); - } - } - } - - - /********* - ** Private methods - *********/ - /// Get the locales which can provide translations for the given locale, in precedence order. - /// The locale for which to find valid locales. - private IEnumerable GetRelevantLocales(string locale) - { - // given locale - yield return locale; - - // broader locales (like pt-BR => pt) - while (true) - { - int dashIndex = locale.LastIndexOf('-'); - if (dashIndex <= 0) - break; - - locale = locale.Substring(0, dashIndex); - yield return locale; - } - - // default - if (locale != "default") - yield return "default"; + this.Translator.SetLocale(locale, localeEnum); } } } -- cgit From 175ebf907134e1e32992f62637a5d7e43bfc6a20 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 3 Oct 2019 12:08:22 -0400 Subject: fix non-generic GetAPI not checking that all mods are loaded (#662) --- .../Framework/ModHelpers/ModRegistryHelper.cs | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'src/SMAPI/Framework/ModHelpers') diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs index 24bed3bb..f42cb085 100644 --- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using StardewModdingAPI.Framework.Reflection; namespace StardewModdingAPI.Framework.ModHelpers @@ -63,6 +62,14 @@ namespace StardewModdingAPI.Framework.ModHelpers /// 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) { + // validate ready + if (!this.Registry.AreAllModsInitialized) + { + this.Monitor.Log("Tried to access a mod-provided API before all mods were initialized.", LogLevel.Error); + return null; + } + + // get raw API 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); @@ -74,12 +81,12 @@ namespace StardewModdingAPI.Framework.ModHelpers /// The mod's unique ID. public TInterface GetApi(string uniqueID) where TInterface : class { - // validate - if (!this.Registry.AreAllModsInitialized) - { - this.Monitor.Log("Tried to access a mod-provided API before all mods were initialized.", LogLevel.Error); + // get raw API + object api = this.GetApi(uniqueID); + if (api == null) return null; - } + + // validate mapping if (!typeof(TInterface).IsInterface) { this.Monitor.Log($"Tried to map a mod-provided API to class '{typeof(TInterface).FullName}'; must be a public interface.", LogLevel.Error); @@ -91,11 +98,6 @@ namespace StardewModdingAPI.Framework.ModHelpers 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; -- cgit