From 1d3c99cc25f6c0d504fd5e43ea71ef327b6e9066 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 27 Mar 2022 13:42:14 -0400 Subject: split helper.Content API into game/mod content APIs --- src/SMAPI/Framework/Content/AssetName.cs | 12 ++ src/SMAPI/Framework/ContentPack.cs | 24 ++-- src/SMAPI/Framework/ModHelpers/ContentHelper.cs | 9 +- .../Framework/ModHelpers/GameContentHelper.cs | 129 +++++++++++++++++++++ src/SMAPI/Framework/ModHelpers/ModContentHelper.cs | 75 ++++++++++++ src/SMAPI/Framework/ModHelpers/ModHelper.cs | 38 +++++- src/SMAPI/Framework/SCore.cs | 12 +- src/SMAPI/IAssetName.cs | 5 + src/SMAPI/IContentHelper.cs | 10 +- src/SMAPI/IContentPack.cs | 5 + src/SMAPI/IGameContentHelper.cs | 73 ++++++++++++ src/SMAPI/IModContentHelper.cs | 32 +++++ src/SMAPI/IModHelper.cs | 10 ++ 13 files changed, 398 insertions(+), 36 deletions(-) create mode 100644 src/SMAPI/Framework/ModHelpers/GameContentHelper.cs create mode 100644 src/SMAPI/Framework/ModHelpers/ModContentHelper.cs create mode 100644 src/SMAPI/IGameContentHelper.cs create mode 100644 src/SMAPI/IModContentHelper.cs (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/Content/AssetName.cs b/src/SMAPI/Framework/Content/AssetName.cs index 7ce0f8ee..a1d37b0b 100644 --- a/src/SMAPI/Framework/Content/AssetName.cs +++ b/src/SMAPI/Framework/Content/AssetName.cs @@ -100,6 +100,18 @@ namespace StardewModdingAPI.Framework.Content return compareTo.Equals(assetName, StringComparison.OrdinalIgnoreCase); } + /// + public bool IsEquivalentTo(IAssetName assetName, bool useBaseName = false) + { + if (useBaseName) + return this.BaseName.Equals(assetName?.BaseName, StringComparison.OrdinalIgnoreCase); + + if (assetName is AssetName impl) + return this.ComparableName == impl?.ComparableName; + + return this.Name.Equals(assetName?.Name, StringComparison.OrdinalIgnoreCase); + } + /// public bool StartsWith(string prefix, bool allowPartialWord = true, bool allowSubfolder = true) { diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs index b6add7b5..3920354e 100644 --- a/src/SMAPI/Framework/ContentPack.cs +++ b/src/SMAPI/Framework/ContentPack.cs @@ -13,9 +13,6 @@ namespace StardewModdingAPI.Framework /********* ** Fields *********/ - /// Provides an API for loading content assets. - private readonly IContentHelper Content; - /// Encapsulates SMAPI's JSON file parsing. private readonly JsonHelper JsonHelper; @@ -35,6 +32,9 @@ namespace StardewModdingAPI.Framework /// public ITranslationHelper Translation => this.TranslationImpl; + /// + public IModContentHelper ModContent { get; } + /// The underlying translation helper. internal TranslationHelper TranslationImpl { get; set; } @@ -45,14 +45,14 @@ namespace StardewModdingAPI.Framework /// Construct an instance. /// The full path to the content pack's folder. /// The content pack's manifest. - /// Provides an API for loading content assets. + /// Provides an API for loading content assets from the content pack's folder. /// Provides translations stored in the content pack's i18n folder. /// Encapsulates SMAPI's JSON file parsing. - public ContentPack(string directoryPath, IManifest manifest, IContentHelper content, TranslationHelper translation, JsonHelper jsonHelper) + public ContentPack(string directoryPath, IManifest manifest, IModContentHelper content, TranslationHelper translation, JsonHelper jsonHelper) { this.DirectoryPath = directoryPath; this.Manifest = manifest; - this.Content = content; + this.ModContent = content; this.TranslationImpl = translation; this.JsonHelper = jsonHelper; @@ -95,21 +95,17 @@ namespace StardewModdingAPI.Framework } /// + [Obsolete] public T LoadAsset(string key) { - key = PathUtilities.NormalizePath(key); - - key = this.GetCaseInsensitiveRelativePath(key); - return this.Content.Load(key, ContentSource.ModFolder); + return this.ModContent.Load(key); } /// + [Obsolete] public string GetActualAssetKey(string key) { - key = PathUtilities.NormalizePath(key); - - key = this.GetCaseInsensitiveRelativePath(key); - return this.Content.GetActualAssetKey(key, ContentSource.ModFolder); + return this.ModContent.GetInternalAssetName(key)?.Name; } diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index 3a5c8938..b0064532 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -13,6 +13,7 @@ using StardewValley; namespace StardewModdingAPI.Framework.ModHelpers { /// Provides an API for loading content assets. + [Obsolete] internal class ContentHelper : BaseHelper, IContentHelper { /********* @@ -44,11 +45,9 @@ namespace StardewModdingAPI.Framework.ModHelpers public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.GameContentManager.Language; /// The observable implementation of . - [Obsolete] internal ObservableCollection ObservableAssetEditors { get; } = new(); /// The observable implementation of . - [Obsolete] internal ObservableCollection ObservableAssetLoaders { get; } = new(); /// @@ -105,12 +104,6 @@ namespace StardewModdingAPI.Framework.ModHelpers this.Monitor = monitor; } - /// - public IAssetName ParseAssetName(string rawName) - { - return this.ContentCore.ParseAssetName(rawName); - } - /// public T Load(string key, ContentSource source = ContentSource.ModFolder) { diff --git a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs new file mode 100644 index 00000000..42a4de20 --- /dev/null +++ b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs @@ -0,0 +1,129 @@ +using System; +using System.Linq; +using StardewModdingAPI.Framework.Content; +using StardewModdingAPI.Framework.ContentManagers; +using StardewModdingAPI.Framework.Exceptions; +using StardewValley; + +namespace StardewModdingAPI.Framework.ModHelpers +{ + /// + internal class GameContentHelper : BaseHelper, IGameContentHelper + { + /********* + ** Fields + *********/ + /// SMAPI's core content logic. + private readonly ContentCoordinator ContentCore; + + /// The underlying game content manager. + private readonly IContentManager GameContentManager; + + /// The friendly mod name for use in errors. + private readonly string ModName; + + /// Encapsulates monitoring and logging. + private readonly IMonitor Monitor; + + + /********* + ** Accessors + *********/ + /// + public string CurrentLocale => this.GameContentManager.GetLocale(); + + /// + public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.GameContentManager.Language; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// SMAPI's core content logic. + /// The unique ID of the relevant mod. + /// The friendly mod name for use in errors. + /// Encapsulates monitoring and logging. + public GameContentHelper(ContentCoordinator contentCore, string modID, string modName, IMonitor monitor) + : base(modID) + { + string managedAssetPrefix = contentCore.GetManagedAssetPrefix(modID); + + this.ContentCore = contentCore; + this.GameContentManager = contentCore.CreateGameContentManager(managedAssetPrefix + ".content"); + this.ModName = modName; + this.Monitor = monitor; + } + + /// + public IAssetName ParseAssetName(string rawName) + { + return this.ContentCore.ParseAssetName(rawName); + } + + /// + public T Load(string key) + { + IAssetName assetName = this.ContentCore.ParseAssetName(key); + return this.Load(assetName); + } + + /// + public T Load(IAssetName assetName) + { + try + { + return this.GameContentManager.LoadLocalized(assetName, this.CurrentLocaleConstant, useCache: false); + } + catch (Exception ex) when (ex is not SContentLoadException) + { + throw new SContentLoadException($"{this.ModName} failed loading content asset '{assetName}' from the game content.", ex); + } + } + + /// + public bool InvalidateCache(string key) + { + IAssetName assetName = this.ParseAssetName(key); + return this.InvalidateCache(assetName); + } + + /// + public bool InvalidateCache(IAssetName assetName) + { + this.Monitor.Log($"Requested cache invalidation for '{assetName}'."); + return this.ContentCore.InvalidateCache(asset => asset.Name.IsEquivalentTo(assetName)).Any(); + } + + /// + public bool InvalidateCache() + { + this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible."); + return this.ContentCore.InvalidateCache((_, _, type) => typeof(T).IsAssignableFrom(type)).Any(); + } + + /// + public bool InvalidateCache(Func predicate) + { + this.Monitor.Log("Requested cache invalidation for all assets matching a predicate."); + return this.ContentCore.InvalidateCache(predicate).Any(); + } + + /// + public IAssetData GetPatchHelper(T data, string assetName = null) + { + if (data == null) + throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value."); + + assetName ??= $"temp/{Guid.NewGuid():N}"; + + return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName), data, key => this.ParseAssetName(key).Name); + } + + /// Get the underlying game content manager. + internal IContentManager GetUnderlyingContentManager() + { + return this.GameContentManager; + } + } +} diff --git a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs new file mode 100644 index 00000000..45899dd7 --- /dev/null +++ b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs @@ -0,0 +1,75 @@ +using System; +using StardewModdingAPI.Framework.Content; +using StardewModdingAPI.Framework.ContentManagers; +using StardewModdingAPI.Framework.Exceptions; + +namespace StardewModdingAPI.Framework.ModHelpers +{ + /// + internal class ModContentHelper : BaseHelper, IModContentHelper + { + /********* + ** Fields + *********/ + /// SMAPI's core content logic. + private readonly ContentCoordinator ContentCore; + + /// A content manager for this mod which manages files from the mod's folder. + private readonly ModContentManager ModContentManager; + + /// The friendly mod name for use in errors. + private readonly string ModName; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// SMAPI's core content logic. + /// The absolute path to the mod folder. + /// The unique ID of the relevant mod. + /// The friendly mod name for use in errors. + /// The game content manager used for map tilesheets not provided by the mod. + public ModContentHelper(ContentCoordinator contentCore, string modFolderPath, string modID, string modName, IContentManager gameContentManager) + : base(modID) + { + string managedAssetPrefix = contentCore.GetManagedAssetPrefix(modID); + + this.ContentCore = contentCore; + this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, modName, modFolderPath, gameContentManager); + this.ModName = modName; + } + + /// + public T Load(string relativePath) + { + IAssetName assetName = this.ContentCore.ParseAssetName(relativePath); + + try + { + return this.ModContentManager.LoadExact(assetName, useCache: false); + } + catch (Exception ex) when (ex is not SContentLoadException) + { + throw new SContentLoadException($"{this.ModName} failed loading content asset '{relativePath}' from its mod folder.", ex); + } + } + + /// + public IAssetName GetInternalAssetName(string relativePath) + { + return this.ModContentManager.GetInternalAssetKey(relativePath); + } + + /// + public IAssetData GetPatchHelper(T data, string relativePath = null) + { + if (data == null) + throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value."); + + relativePath ??= $"temp/{Guid.NewGuid():N}"; + + return new AssetDataForObject(this.ContentCore.GetLocale(), this.ContentCore.ParseAssetName(relativePath), data, key => this.ContentCore.ParseAssetName(key).Name); + } + } +} diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index 058bff83..d28faacc 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -8,6 +8,14 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Provides simplified APIs for writing mods. internal class ModHelper : BaseHelper, IModHelper, IDisposable { + /********* + ** Fields + *********/ + /// The backing field for . + [Obsolete] + private readonly IContentHelper ContentImpl; + + /********* ** Accessors *********/ @@ -18,7 +26,27 @@ namespace StardewModdingAPI.Framework.ModHelpers public IModEvents Events { get; } /// - public IContentHelper Content { get; } + [Obsolete] + public IContentHelper Content + { + get + { + SCore.DeprecationManager.Warn( + source: SCore.DeprecationManager.GetSourceName(this.ModID), + nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.Content)}", + version: "3.14.0", + severity: DeprecationLevel.Notice + ); + + return this.ContentImpl; + } + } + + /// + public IGameContentHelper GameContent { get; } + + /// + public IModContentHelper ModContent { get; } /// public IContentPackHelper ContentPacks { get; } @@ -54,6 +82,8 @@ namespace StardewModdingAPI.Framework.ModHelpers /// Manages the game's input state for the current player instance. That may not be the main player in split-screen mode. /// Manages access to events raised by SMAPI. /// An API for loading content assets. + /// An API for loading content assets from the game's Content folder or via . + /// An API for loading content assets from your mod's files. /// An API for managing content packs. /// An API for managing console commands. /// An API for reading and writing persistent mod data. @@ -63,7 +93,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, Func currentInputState, IModEvents events, IContentHelper contentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper) + public ModHelper(string modID, string modDirectory, Func currentInputState, IModEvents events, IContentHelper contentHelper, IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper) : base(modID) { // validate directory @@ -74,7 +104,9 @@ namespace StardewModdingAPI.Framework.ModHelpers // initialize this.DirectoryPath = modDirectory; - this.Content = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper)); + this.ContentImpl = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper)); + this.GameContent = gameContentHelper ?? throw new ArgumentNullException(nameof(gameContentHelper)); + this.ModContent = modContentHelper ?? throw new ArgumentNullException(nameof(modContentHelper)); this.ContentPacks = contentPackHelper ?? throw new ArgumentNullException(nameof(contentPackHelper)); this.Data = dataHelper ?? throw new ArgumentNullException(nameof(dataHelper)); this.Input = new InputHelper(modID, currentInputState); diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index efdfabe7..b4aa3595 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1775,9 +1775,10 @@ namespace StardewModdingAPI.Framework { IManifest manifest = mod.Manifest; IMonitor monitor = this.LogManager.GetMonitor(mod.DisplayName); - IContentHelper contentHelper = new ContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor); + GameContentHelper gameContentHelper = new(this.ContentCore, manifest.UniqueID, mod.DisplayName, monitor); + IModContentHelper modContentHelper = new ModContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager()); TranslationHelper translationHelper = new(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language); - IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, contentHelper, translationHelper, jsonHelper); + IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, modContentHelper, translationHelper, jsonHelper); mod.SetMod(contentPack, monitor, translationHelper); this.ModRegistry.Add(mod); @@ -1855,7 +1856,8 @@ namespace StardewModdingAPI.Framework IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest) { IMonitor packMonitor = this.LogManager.GetMonitor(packManifest.Name); - IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor); + GameContentHelper gameContentHelper = new(contentCore, packManifest.UniqueID, packManifest.Name, packMonitor); + IModContentHelper packContentHelper = new ModContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, gameContentHelper.GetUnderlyingContentManager()); TranslationHelper packTranslationHelper = new(packManifest.UniqueID, contentCore.GetLocale(), contentCore.Language); ContentPack contentPack = new(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper); @@ -1867,13 +1869,15 @@ namespace StardewModdingAPI.Framework IModEvents events = new ModEvents(mod, this.EventManager); ICommandHelper commandHelper = new CommandHelper(mod, this.CommandManager); IContentHelper contentHelper = new ContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor); + GameContentHelper gameContentHelper = new(contentCore, manifest.UniqueID, mod.DisplayName, monitor); + IModContentHelper modContentHelper = new ModContentHelper(contentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, gameContentHelper.GetUnderlyingContentManager()); IContentPackHelper contentPackHelper = new ContentPackHelper(manifest.UniqueID, new Lazy(GetContentPacks), CreateFakeContentPack); IDataHelper dataHelper = new DataHelper(manifest.UniqueID, mod.DirectoryPath, jsonHelper); IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, mod.DisplayName, this.Reflection); IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry, proxyFactory, monitor); IMultiplayerHelper multiplayerHelper = new MultiplayerHelper(manifest.UniqueID, this.Multiplayer); - modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, () => this.GetCurrentGameInstance().Input, events, contentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper); + modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, () => this.GetCurrentGameInstance().Input, events, contentHelper, gameContentHelper, modContentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper); } // init mod diff --git a/src/SMAPI/IAssetName.cs b/src/SMAPI/IAssetName.cs index 89f02adf..c91da266 100644 --- a/src/SMAPI/IAssetName.cs +++ b/src/SMAPI/IAssetName.cs @@ -30,6 +30,11 @@ namespace StardewModdingAPI /// Whether to compare the given name with the (if true) or (if false). This has no effect on any locale included in the given . bool IsEquivalentTo(string assetName, bool useBaseName = false); + /// Get whether the given asset name is equivalent, ignoring capitalization and formatting. + /// The asset name to compare this instance to. + /// Whether to compare the given name with the (if true) or (if false). + bool IsEquivalentTo(IAssetName assetName, bool useBaseName = false); + /// Get whether the asset name starts with the given value, ignoring capitalization and formatting. This can be used with a trailing slash to test for an asset folder, like Data/. /// The prefix to match. /// Whether to match if the prefix occurs mid-word, so Data/AchievementsToIgnore matches prefix Data/Achievements. If this is false, the prefix only matches if the asset name starts with the prefix followed by a non-alphanumeric character (including ., /, or \\) or the end of string. diff --git a/src/SMAPI/IContentHelper.cs b/src/SMAPI/IContentHelper.cs index 1d36abff..48f6bfd8 100644 --- a/src/SMAPI/IContentHelper.cs +++ b/src/SMAPI/IContentHelper.cs @@ -10,17 +10,18 @@ using xTile; namespace StardewModdingAPI { /// Provides an API for loading content assets. + [Obsolete($"Use {nameof(IMod.Helper)}.{nameof(IModHelper.GameContent)} or {nameof(IMod.Helper)}.{nameof(IModHelper.ModContent)} instead. This interface will be removed in SMAPI 4.0.0.")] public interface IContentHelper : IModLinked { /********* ** Accessors *********/ /// Interceptors which provide the initial versions of matching content assets. - [Obsolete($"Use {nameof(IMod.Helper)}.{nameof(IModHelper.Events)}.{nameof(IModEvents.Content)} instead. This interface will be removed in SMAPI 4.0.0.")] + [Obsolete($"Use {nameof(IMod.Helper)}.{nameof(IModHelper.Events)}.{nameof(IModEvents.Content)} instead. This property will be removed in SMAPI 4.0.0.")] IList AssetLoaders { get; } /// Interceptors which edit matching content assets after they're loaded. - [Obsolete($"Use {nameof(IMod.Helper)}.{nameof(IModHelper.Events)}.{nameof(IModEvents.Content)} instead. This interface will be removed in SMAPI 4.0.0.")] + [Obsolete($"Use {nameof(IMod.Helper)}.{nameof(IModHelper.Events)}.{nameof(IModEvents.Content)} instead. This property will be removed in SMAPI 4.0.0.")] IList AssetEditors { get; } /// The game's current locale code (like pt-BR). @@ -33,11 +34,6 @@ namespace StardewModdingAPI /********* ** Public methods *********/ - /// Parse a raw asset name. - /// The raw asset name to parse. - /// The is null or empty. - IAssetName ParseAssetName(string rawName); - /// Load content from the game folder or mod folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. /// The expected data type. The main supported types are , , dictionaries, and lists; other types may be supported by the game's content pipeline. /// The asset key to fetch (if the is ), or the local path to a content file relative to the mod folder. diff --git a/src/SMAPI/IContentPack.cs b/src/SMAPI/IContentPack.cs index 9cc64dcd..3c66faff 100644 --- a/src/SMAPI/IContentPack.cs +++ b/src/SMAPI/IContentPack.cs @@ -20,6 +20,9 @@ namespace StardewModdingAPI /// Provides translations stored in the content pack's i18n folder. See for more info. ITranslationHelper Translation { get; } + /// An API for loading content assets from the content pack's files. + IModContentHelper ModContent { get; } + /********* ** Public methods @@ -47,11 +50,13 @@ namespace StardewModdingAPI /// The relative file path within the content pack (case-insensitive). /// The is empty or contains invalid characters. /// The content asset couldn't be loaded (e.g. because it doesn't exist). + [Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.Load)} instead. This method will be removed in SMAPI 4.0.0.")] T LoadAsset(string key); /// 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. /// The relative file path within the content pack (case-insensitive). /// The is empty or contains invalid characters. + [Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.GetInternalAssetName)} instead. This method will be removed in SMAPI 4.0.0.")] string GetActualAssetKey(string key); } } diff --git a/src/SMAPI/IGameContentHelper.cs b/src/SMAPI/IGameContentHelper.cs new file mode 100644 index 00000000..86bc3e0e --- /dev/null +++ b/src/SMAPI/IGameContentHelper.cs @@ -0,0 +1,73 @@ +using System; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI.Events; +using StardewValley; +using xTile; + +namespace StardewModdingAPI +{ + /// Provides an API for loading content assets from the game's Content folder or via . + public interface IGameContentHelper : IModLinked + { + /********* + ** Accessors + *********/ + /// The game's current locale code (like pt-BR). + string CurrentLocale { get; } + + /// The game's current locale as an enum value. + LocalizedContentManager.LanguageCode CurrentLocaleConstant { get; } + + + /********* + ** Public methods + *********/ + /// Parse a raw asset name. + /// The raw asset name to parse. + /// The is null or empty. + IAssetName ParseAssetName(string rawName); + + /// Load content from the game folder or mod folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. + /// The expected data type. The main supported types are , , dictionaries, and lists; other types may be supported by the game's content pipeline. + /// The asset name to load. + /// The is empty or contains invalid characters. + /// The content asset couldn't be loaded (e.g. because it doesn't exist). + T Load(string assetName); + + /// Load content from the game folder or mod folder (if not already cached), and return it. When loading a .png file, this must be called outside the game's draw loop. + /// The expected data type. The main supported types are , , dictionaries, and lists; other types may be supported by the game's content pipeline. + /// The asset name to load. + /// The is empty or contains invalid characters. + /// The content asset couldn't be loaded (e.g. because it doesn't exist). + T Load(IAssetName assetName); + + /// Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. + /// The asset key to invalidate in the content folder. + /// The is empty or contains invalid characters. + /// Returns whether the given asset key was cached. + bool InvalidateCache(string assetName); + + /// Remove an asset from the content cache so it's reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. + /// The asset key to invalidate in the content folder. + /// The is empty or contains invalid characters. + /// Returns whether the given asset key was cached. + bool InvalidateCache(IAssetName assetName); + + /// Remove all assets of the given type from the cache so they're reloaded on the next request. This can be a very expensive operation and should only be used in very specific cases. This will reload core game assets if needed, but references to the former assets will still show the previous content. + /// The asset type to remove from the cache. + /// Returns whether any assets were invalidated. + bool InvalidateCache(); + + /// Remove matching assets from the content cache so they're reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content. + /// A predicate matching the assets to invalidate. + /// Returns whether any cache entries were invalidated. + bool InvalidateCache(Func predicate); + + /// Get a patch helper for arbitrary data. + /// The data type. + /// The asset data. + /// The asset name. This is only used for tracking purposes and has no effect on the patch helper. + IAssetData GetPatchHelper(T data, string assetName = null); + } +} diff --git a/src/SMAPI/IModContentHelper.cs b/src/SMAPI/IModContentHelper.cs new file mode 100644 index 00000000..e3431365 --- /dev/null +++ b/src/SMAPI/IModContentHelper.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; +using xTile; + +namespace StardewModdingAPI +{ + /// Provides an API for loading content assets from the current mod's folder. + public interface IModContentHelper : IModLinked + { + /********* + ** Public methods + *********/ + /// Load content from the mod folder and return it. When loading a .png file, this must be called outside the game's draw loop. + /// The expected data type. The main supported types are , , dictionaries, and lists; other types may be supported by the game's content pipeline. + /// The local path to a content file relative to the mod folder. + /// The is empty or contains invalid characters. + /// The content asset couldn't be loaded (e.g. because it doesn't exist). + T Load(string relativePath); + + /// Get the internal asset name which allows loading a mod file through any of the game's content managers. This can be used when passing asset names directly to the game (e.g. for map tilesheets), but should be avoided if you can use instead. This does not validate whether the asset exists. + /// The local path to a content file relative to the mod folder. + /// The is empty or contains invalid characters. + IAssetName GetInternalAssetName(string relativePath); + + /// Get a patch helper for arbitrary data. + /// The data type. + /// The asset data. + /// The local path to the content file being edited relative to the mod folder. This is only used for tracking purposes and has no effect on the patch helper. + IAssetData GetPatchHelper(T data, string relativePath = null); + } +} diff --git a/src/SMAPI/IModHelper.cs b/src/SMAPI/IModHelper.cs index cd746e06..15e4ed8d 100644 --- a/src/SMAPI/IModHelper.cs +++ b/src/SMAPI/IModHelper.cs @@ -1,3 +1,4 @@ +using System; using StardewModdingAPI.Events; namespace StardewModdingAPI @@ -17,13 +18,22 @@ namespace StardewModdingAPI /// An API for managing console commands. ICommandHelper ConsoleCommands { get; } + /// An API for loading content assets from the game's Content folder or using the events. + IGameContentHelper GameContent { get; } + + /// An API for loading content assets from your mod's files. + /// This API is intended for reading content assets from the mod files (like game data, images, etc); see also which is intended for persisting internal mod data. + IModContentHelper ModContent { get; } + /// An API for loading content assets. + [Obsolete($"Use {nameof(IGameContentHelper)} or {nameof(IModContentHelper)} instead.")] IContentHelper Content { get; } /// An API for managing content packs. IContentPackHelper ContentPacks { get; } /// An API for reading and writing persistent mod data. + /// This API is intended for persisting internal mod data; see also which is intended for reading content assets (like game data, images, etc). IDataHelper Data { get; } /// An API for checking and changing input state. -- cgit