summaryrefslogtreecommitdiff
path: root/src/SMAPI
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI')
-rw-r--r--src/SMAPI/Framework/Content/AssetName.cs12
-rw-r--r--src/SMAPI/Framework/ContentPack.cs24
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs9
-rw-r--r--src/SMAPI/Framework/ModHelpers/GameContentHelper.cs129
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModContentHelper.cs75
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs38
-rw-r--r--src/SMAPI/Framework/SCore.cs12
-rw-r--r--src/SMAPI/IAssetName.cs5
-rw-r--r--src/SMAPI/IContentHelper.cs10
-rw-r--r--src/SMAPI/IContentPack.cs5
-rw-r--r--src/SMAPI/IGameContentHelper.cs73
-rw-r--r--src/SMAPI/IModContentHelper.cs32
-rw-r--r--src/SMAPI/IModHelper.cs10
13 files changed, 398 insertions, 36 deletions
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
@@ -101,6 +101,18 @@ namespace StardewModdingAPI.Framework.Content
}
/// <inheritdoc />
+ 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);
+ }
+
+ /// <inheritdoc />
public bool StartsWith(string prefix, bool allowPartialWord = true, bool allowSubfolder = true)
{
// asset keys never start with null
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
*********/
- /// <summary>Provides an API for loading content assets.</summary>
- private readonly IContentHelper Content;
-
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
private readonly JsonHelper JsonHelper;
@@ -35,6 +32,9 @@ namespace StardewModdingAPI.Framework
/// <inheritdoc />
public ITranslationHelper Translation => this.TranslationImpl;
+ /// <inheritdoc />
+ public IModContentHelper ModContent { get; }
+
/// <summary>The underlying translation helper.</summary>
internal TranslationHelper TranslationImpl { get; set; }
@@ -45,14 +45,14 @@ namespace StardewModdingAPI.Framework
/// <summary>Construct an instance.</summary>
/// <param name="directoryPath">The full path to the content pack's folder.</param>
/// <param name="manifest">The content pack's manifest.</param>
- /// <param name="content">Provides an API for loading content assets.</param>
+ /// <param name="content">Provides an API for loading content assets from the content pack's folder.</param>
/// <param name="translation">Provides translations stored in the content pack's <c>i18n</c> folder.</param>
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
- 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
}
/// <inheritdoc />
+ [Obsolete]
public T LoadAsset<T>(string key)
{
- key = PathUtilities.NormalizePath(key);
-
- key = this.GetCaseInsensitiveRelativePath(key);
- return this.Content.Load<T>(key, ContentSource.ModFolder);
+ return this.ModContent.Load<T>(key);
}
/// <inheritdoc />
+ [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
{
/// <summary>Provides an API for loading content assets.</summary>
+ [Obsolete]
internal class ContentHelper : BaseHelper, IContentHelper
{
/*********
@@ -44,11 +45,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.GameContentManager.Language;
/// <summary>The observable implementation of <see cref="AssetEditors"/>.</summary>
- [Obsolete]
internal ObservableCollection<IAssetEditor> ObservableAssetEditors { get; } = new();
/// <summary>The observable implementation of <see cref="AssetLoaders"/>.</summary>
- [Obsolete]
internal ObservableCollection<IAssetLoader> ObservableAssetLoaders { get; } = new();
/// <inheritdoc />
@@ -106,12 +105,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IAssetName ParseAssetName(string rawName)
- {
- return this.ContentCore.ParseAssetName(rawName);
- }
-
- /// <inheritdoc />
public T Load<T>(string key, ContentSource source = ContentSource.ModFolder)
{
IAssetName assetName = this.ContentCore.ParseAssetName(key);
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
+{
+ /// <inheritdoc cref="IGameContentHelper"/>
+ internal class GameContentHelper : BaseHelper, IGameContentHelper
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>SMAPI's core content logic.</summary>
+ private readonly ContentCoordinator ContentCore;
+
+ /// <summary>The underlying game content manager.</summary>
+ private readonly IContentManager GameContentManager;
+
+ /// <summary>The friendly mod name for use in errors.</summary>
+ private readonly string ModName;
+
+ /// <summary>Encapsulates monitoring and logging.</summary>
+ private readonly IMonitor Monitor;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <inheritdoc />
+ public string CurrentLocale => this.GameContentManager.GetLocale();
+
+ /// <inheritdoc />
+ public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.GameContentManager.Language;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="contentCore">SMAPI's core content logic.</param>
+ /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="modName">The friendly mod name for use in errors.</param>
+ /// <param name="monitor">Encapsulates monitoring and logging.</param>
+ 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;
+ }
+
+ /// <inheritdoc />
+ public IAssetName ParseAssetName(string rawName)
+ {
+ return this.ContentCore.ParseAssetName(rawName);
+ }
+
+ /// <inheritdoc />
+ public T Load<T>(string key)
+ {
+ IAssetName assetName = this.ContentCore.ParseAssetName(key);
+ return this.Load<T>(assetName);
+ }
+
+ /// <inheritdoc />
+ public T Load<T>(IAssetName assetName)
+ {
+ try
+ {
+ return this.GameContentManager.LoadLocalized<T>(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);
+ }
+ }
+
+ /// <inheritdoc />
+ public bool InvalidateCache(string key)
+ {
+ IAssetName assetName = this.ParseAssetName(key);
+ return this.InvalidateCache(assetName);
+ }
+
+ /// <inheritdoc />
+ public bool InvalidateCache(IAssetName assetName)
+ {
+ this.Monitor.Log($"Requested cache invalidation for '{assetName}'.");
+ return this.ContentCore.InvalidateCache(asset => asset.Name.IsEquivalentTo(assetName)).Any();
+ }
+
+ /// <inheritdoc />
+ public bool InvalidateCache<T>()
+ {
+ 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();
+ }
+
+ /// <inheritdoc />
+ public bool InvalidateCache(Func<IAssetInfo, bool> predicate)
+ {
+ this.Monitor.Log("Requested cache invalidation for all assets matching a predicate.");
+ return this.ContentCore.InvalidateCache(predicate).Any();
+ }
+
+ /// <inheritdoc />
+ public IAssetData GetPatchHelper<T>(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);
+ }
+
+ /// <summary>Get the underlying game content manager.</summary>
+ 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
+{
+ /// <inheritdoc cref="IModContentHelper"/>
+ internal class ModContentHelper : BaseHelper, IModContentHelper
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>SMAPI's core content logic.</summary>
+ private readonly ContentCoordinator ContentCore;
+
+ /// <summary>A content manager for this mod which manages files from the mod's folder.</summary>
+ private readonly ModContentManager ModContentManager;
+
+ /// <summary>The friendly mod name for use in errors.</summary>
+ private readonly string ModName;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="contentCore">SMAPI's core content logic.</param>
+ /// <param name="modFolderPath">The absolute path to the mod folder.</param>
+ /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="modName">The friendly mod name for use in errors.</param>
+ /// <param name="gameContentManager">The game content manager used for map tilesheets not provided by the mod.</param>
+ 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;
+ }
+
+ /// <inheritdoc />
+ public T Load<T>(string relativePath)
+ {
+ IAssetName assetName = this.ContentCore.ParseAssetName(relativePath);
+
+ try
+ {
+ return this.ModContentManager.LoadExact<T>(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);
+ }
+ }
+
+ /// <inheritdoc />
+ public IAssetName GetInternalAssetName(string relativePath)
+ {
+ return this.ModContentManager.GetInternalAssetKey(relativePath);
+ }
+
+ /// <inheritdoc />
+ public IAssetData GetPatchHelper<T>(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
@@ -9,6 +9,14 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class ModHelper : BaseHelper, IModHelper, IDisposable
{
/*********
+ ** Fields
+ *********/
+ /// <summary>The backing field for <see cref="Content"/>.</summary>
+ [Obsolete]
+ private readonly IContentHelper ContentImpl;
+
+
+ /*********
** Accessors
*********/
/// <inheritdoc />
@@ -18,7 +26,27 @@ namespace StardewModdingAPI.Framework.ModHelpers
public IModEvents Events { get; }
/// <inheritdoc />
- 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;
+ }
+ }
+
+ /// <inheritdoc />
+ public IGameContentHelper GameContent { get; }
+
+ /// <inheritdoc />
+ public IModContentHelper ModContent { get; }
/// <inheritdoc />
public IContentPackHelper ContentPacks { get; }
@@ -54,6 +82,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="currentInputState">Manages the game's input state for the current player instance. That may not be the main player in split-screen mode.</param>
/// <param name="events">Manages access to events raised by SMAPI.</param>
/// <param name="contentHelper">An API for loading content assets.</param>
+ /// <param name="gameContentHelper">An API for loading content assets from the game's <c>Content</c> folder or via <see cref="IModEvents.Content"/>.</param>
+ /// <param name="modContentHelper">An API for loading content assets from your mod's files.</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>
@@ -63,7 +93,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="translationHelper">An API for reading translations stored in the mod's <c>i18n</c> folder.</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, Func<SInputState> 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<SInputState> 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<IContentPack[]>(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
/// <param name="useBaseName">Whether to compare the given name with the <see cref="BaseName"/> (if true) or <see cref="Name"/> (if false). This has no effect on any locale included in the given <paramref name="assetName"/>.</param>
bool IsEquivalentTo(string assetName, bool useBaseName = false);
+ /// <summary>Get whether the given asset name is equivalent, ignoring capitalization and formatting.</summary>
+ /// <param name="assetName">The asset name to compare this instance to.</param>
+ /// <param name="useBaseName">Whether to compare the given name with the <see cref="BaseName"/> (if true) or <see cref="Name"/> (if false).</param>
+ bool IsEquivalentTo(IAssetName assetName, bool useBaseName = false);
+
/// <summary>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 <c>Data/</c>.</summary>
/// <param name="prefix">The prefix to match.</param>
/// <param name="allowPartialWord">Whether to match if the prefix occurs mid-word, so <c>Data/AchievementsToIgnore</c> matches prefix <c>Data/Achievements</c>. If this is false, the prefix only matches if the asset name starts with the prefix followed by a non-alphanumeric character (including <c>.</c>, <c>/</c>, or <c>\\</c>) or the end of string.</param>
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
{
/// <summary>Provides an API for loading content assets.</summary>
+ [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
*********/
/// <summary>Interceptors which provide the initial versions of matching content assets.</summary>
- [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<IAssetLoader> AssetLoaders { get; }
/// <summary>Interceptors which edit matching content assets after they're loaded.</summary>
- [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<IAssetEditor> AssetEditors { get; }
/// <summary>The game's current locale code (like <c>pt-BR</c>).</summary>
@@ -33,11 +34,6 @@ namespace StardewModdingAPI
/*********
** Public methods
*********/
- /// <summary>Parse a raw asset name.</summary>
- /// <param name="rawName">The raw asset name to parse.</param>
- /// <exception cref="ArgumentException">The <paramref name="rawName"/> is null or empty.</exception>
- IAssetName ParseAssetName(string rawName);
-
/// <summary>Load content from the game folder or mod folder (if not already cached), and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary>
/// <typeparam name="T">The expected data type. The main supported types are <see cref="Map"/>, <see cref="Texture2D"/>, dictionaries, and lists; other types may be supported by the game's content pipeline.</typeparam>
/// <param name="key">The asset key to fetch (if the <paramref name="source"/> is <see cref="ContentSource.GameContent"/>), or the local path to a content file relative to the mod folder.</param>
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
/// <summary>Provides translations stored in the content pack's <c>i18n</c> folder. See <see cref="IModHelper.Translation"/> for more info.</summary>
ITranslationHelper Translation { get; }
+ /// <summary>An API for loading content assets from the content pack's files.</summary>
+ IModContentHelper ModContent { get; }
+
/*********
** Public methods
@@ -47,11 +50,13 @@ namespace StardewModdingAPI
/// <param name="key">The relative file path within the content pack (case-insensitive).</param>
/// <exception cref="ArgumentException">The <paramref name="key"/> is empty or contains invalid characters.</exception>
/// <exception cref="ContentLoadException">The content asset couldn't be loaded (e.g. because it doesn't exist).</exception>
+ [Obsolete($"Use {nameof(IContentPack.ModContent)}.{nameof(IModContentHelper.Load)} instead. This method will be removed in SMAPI 4.0.0.")]
T LoadAsset<T>(string key);
/// <summary>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.</summary>
/// <param name="key">The relative file path within the content pack (case-insensitive).</param>
/// <exception cref="ArgumentException">The <paramref name="key"/> is empty or contains invalid characters.</exception>
+ [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
+{
+ /// <summary>Provides an API for loading content assets from the game's <c>Content</c> folder or via <see cref="IModEvents.Content"/>.</summary>
+ public interface IGameContentHelper : IModLinked
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The game's current locale code (like <c>pt-BR</c>).</summary>
+ string CurrentLocale { get; }
+
+ /// <summary>The game's current locale as an enum value.</summary>
+ LocalizedContentManager.LanguageCode CurrentLocaleConstant { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Parse a raw asset name.</summary>
+ /// <param name="rawName">The raw asset name to parse.</param>
+ /// <exception cref="ArgumentException">The <paramref name="rawName"/> is null or empty.</exception>
+ IAssetName ParseAssetName(string rawName);
+
+ /// <summary>Load content from the game folder or mod folder (if not already cached), and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary>
+ /// <typeparam name="T">The expected data type. The main supported types are <see cref="Map"/>, <see cref="Texture2D"/>, dictionaries, and lists; other types may be supported by the game's content pipeline.</typeparam>
+ /// <param name="assetName">The asset name to load.</param>
+ /// <exception cref="ArgumentException">The <paramref name="assetName"/> is empty or contains invalid characters.</exception>
+ /// <exception cref="ContentLoadException">The content asset couldn't be loaded (e.g. because it doesn't exist).</exception>
+ T Load<T>(string assetName);
+
+ /// <summary>Load content from the game folder or mod folder (if not already cached), and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary>
+ /// <typeparam name="T">The expected data type. The main supported types are <see cref="Map"/>, <see cref="Texture2D"/>, dictionaries, and lists; other types may be supported by the game's content pipeline.</typeparam>
+ /// <param name="assetName">The asset name to load.</param>
+ /// <exception cref="ArgumentException">The <paramref name="assetName"/> is empty or contains invalid characters.</exception>
+ /// <exception cref="ContentLoadException">The content asset couldn't be loaded (e.g. because it doesn't exist).</exception>
+ T Load<T>(IAssetName assetName);
+
+ /// <summary>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.</summary>
+ /// <param name="assetName">The asset key to invalidate in the content folder.</param>
+ /// <exception cref="ArgumentException">The <paramref name="assetName"/> is empty or contains invalid characters.</exception>
+ /// <returns>Returns whether the given asset key was cached.</returns>
+ bool InvalidateCache(string assetName);
+
+ /// <summary>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.</summary>
+ /// <param name="assetName">The asset key to invalidate in the content folder.</param>
+ /// <exception cref="ArgumentException">The <paramref name="assetName"/> is empty or contains invalid characters.</exception>
+ /// <returns>Returns whether the given asset key was cached.</returns>
+ bool InvalidateCache(IAssetName assetName);
+
+ /// <summary>Remove all assets of the given type from the cache so they're reloaded on the next request. <b>This can be a very expensive operation and should only be used in very specific cases.</b> This will reload core game assets if needed, but references to the former assets will still show the previous content.</summary>
+ /// <typeparam name="T">The asset type to remove from the cache.</typeparam>
+ /// <returns>Returns whether any assets were invalidated.</returns>
+ bool InvalidateCache<T>();
+
+ /// <summary>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.</summary>
+ /// <param name="predicate">A predicate matching the assets to invalidate.</param>
+ /// <returns>Returns whether any cache entries were invalidated.</returns>
+ bool InvalidateCache(Func<IAssetInfo, bool> predicate);
+
+ /// <summary>Get a patch helper for arbitrary data.</summary>
+ /// <typeparam name="T">The data type.</typeparam>
+ /// <param name="data">The asset data.</param>
+ /// <param name="assetName">The asset name. This is only used for tracking purposes and has no effect on the patch helper.</param>
+ IAssetData GetPatchHelper<T>(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
+{
+ /// <summary>Provides an API for loading content assets from the current mod's folder.</summary>
+ public interface IModContentHelper : IModLinked
+ {
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Load content from the mod folder and return it. When loading a <c>.png</c> file, this must be called outside the game's draw loop.</summary>
+ /// <typeparam name="T">The expected data type. The main supported types are <see cref="Map"/>, <see cref="Texture2D"/>, dictionaries, and lists; other types may be supported by the game's content pipeline.</typeparam>
+ /// <param name="relativePath">The local path to a content file relative to the mod folder.</param>
+ /// <exception cref="ArgumentException">The <paramref name="relativePath"/> is empty or contains invalid characters.</exception>
+ /// <exception cref="ContentLoadException">The content asset couldn't be loaded (e.g. because it doesn't exist).</exception>
+ T Load<T>(string relativePath);
+
+ /// <summary>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 <see cref="Load{T}"/> instead. This does not validate whether the asset exists.</summary>
+ /// <param name="relativePath">The local path to a content file relative to the mod folder.</param>
+ /// <exception cref="ArgumentException">The <paramref name="relativePath"/> is empty or contains invalid characters.</exception>
+ IAssetName GetInternalAssetName(string relativePath);
+
+ /// <summary>Get a patch helper for arbitrary data.</summary>
+ /// <typeparam name="T">The data type.</typeparam>
+ /// <param name="data">The asset data.</param>
+ /// <param name="relativePath">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.</param>
+ IAssetData GetPatchHelper<T>(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
/// <summary>An API for managing console commands.</summary>
ICommandHelper ConsoleCommands { get; }
+ /// <summary>An API for loading content assets from the game's <c>Content</c> folder or using the <see cref="IModEvents.Content"/> events.</summary>
+ IGameContentHelper GameContent { get; }
+
+ /// <summary>An API for loading content assets from your mod's files.</summary>
+ /// <remarks>This API is intended for reading content assets from the mod files (like game data, images, etc); see also <see cref="Data"/> which is intended for persisting internal mod data.</remarks>
+ IModContentHelper ModContent { get; }
+
/// <summary>An API for loading content assets.</summary>
+ [Obsolete($"Use {nameof(IGameContentHelper)} or {nameof(IModContentHelper)} instead.")]
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>
+ /// <remarks>This API is intended for persisting internal mod data; see also <see cref="ModContent"/> which is intended for reading content assets (like game data, images, etc).</remarks>
IDataHelper Data { get; }
/// <summary>An API for checking and changing input state.</summary>