summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework')
-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
7 files changed, 270 insertions, 29 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