summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/ModHelpers
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-01 18:16:09 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-01 18:16:09 -0400
commitc8ad50dad1d706a1901798f9396f6becfea36c0e (patch)
tree28bd818a5db39ec5ece1bd141a28de955950463b /src/SMAPI/Framework/ModHelpers
parent451b70953ff4c0b1b27ae0de203ad99379b45b2a (diff)
parentf78093bdb58d477b400cde3f19b70ffd6ddf833d (diff)
downloadSMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.tar.gz
SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.tar.bz2
SMAPI-c8ad50dad1d706a1901798f9396f6becfea36c0e.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI/Framework/ModHelpers')
-rw-r--r--src/SMAPI/Framework/ModHelpers/BaseHelper.cs15
-rw-r--r--src/SMAPI/Framework/ModHelpers/CommandHelper.cs9
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs108
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs6
-rw-r--r--src/SMAPI/Framework/ModHelpers/DataHelper.cs32
-rw-r--r--src/SMAPI/Framework/ModHelpers/GameContentHelper.cs145
-rw-r--r--src/SMAPI/Framework/ModHelpers/InputHelper.cs6
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModContentHelper.cs101
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs60
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs29
-rw-r--r--src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs12
-rw-r--r--src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs30
-rw-r--r--src/SMAPI/Framework/ModHelpers/TranslationHelper.cs10
13 files changed, 460 insertions, 103 deletions
diff --git a/src/SMAPI/Framework/ModHelpers/BaseHelper.cs b/src/SMAPI/Framework/ModHelpers/BaseHelper.cs
index 5a3d4bed..12390976 100644
--- a/src/SMAPI/Framework/ModHelpers/BaseHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/BaseHelper.cs
@@ -4,20 +4,27 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal abstract class BaseHelper : IModLinked
{
/*********
+ ** Fields
+ *********/
+ /// <summary>The mod using this instance.</summary>
+ protected readonly IModMetadata Mod;
+
+
+ /*********
** Accessors
*********/
/// <inheritdoc />
- public string ModID { get; }
+ public string ModID => this.Mod.Manifest.UniqueID;
/*********
** Protected methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
- protected BaseHelper(string modID)
+ /// <param name="mod">The mod using this instance.</param>
+ protected BaseHelper(IModMetadata mod)
{
- this.ModID = modID;
+ this.Mod = mod;
}
}
}
diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
index 69382009..226a8d69 100644
--- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs
@@ -1,4 +1,5 @@
using System;
+using StardewModdingAPI.Framework.Deprecations;
namespace StardewModdingAPI.Framework.ModHelpers
{
@@ -8,9 +9,6 @@ namespace StardewModdingAPI.Framework.ModHelpers
/*********
** Fields
*********/
- /// <summary>The mod using this instance.</summary>
- private readonly IModMetadata Mod;
-
/// <summary>Manages console commands.</summary>
private readonly CommandManager CommandManager;
@@ -22,9 +20,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <param name="mod">The mod using this instance.</param>
/// <param name="commandManager">Manages console commands.</param>
public CommandHelper(IModMetadata mod, CommandManager commandManager)
- : base(mod?.Manifest?.UniqueID ?? "SMAPI")
+ : base(mod)
{
- this.Mod = mod;
this.CommandManager = commandManager;
}
@@ -40,7 +37,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
public bool Trigger(string name, string[] arguments)
{
SCore.DeprecationManager.Warn(
- source: SCore.DeprecationManager.GetSourceName(this.ModID),
+ source: SCore.DeprecationManager.GetMod(this.ModID),
nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.ConsoleCommands)}.{nameof(ICommandHelper.Trigger)}",
version: "3.8.1",
severity: DeprecationLevel.Notice
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index bfca2264..3c2441e8 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -7,12 +7,15 @@ using System.IO;
using System.Linq;
using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.ContentManagers;
+using StardewModdingAPI.Framework.Deprecations;
using StardewModdingAPI.Framework.Exceptions;
+using StardewModdingAPI.Framework.Reflection;
using StardewValley;
namespace StardewModdingAPI.Framework.ModHelpers
{
/// <summary>Provides an API for loading content assets.</summary>
+ [Obsolete]
internal class ContentHelper : BaseHelper, IContentHelper
{
/*********
@@ -27,12 +30,12 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <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;
-
/// <summary>Encapsulates monitoring and logging.</summary>
private readonly IMonitor Monitor;
+ /// <summary>Simplifies access to private code.</summary>
+ private readonly Reflector Reflection;
+
/*********
** Accessors
@@ -44,16 +47,42 @@ namespace StardewModdingAPI.Framework.ModHelpers
public LocalizedContentManager.LanguageCode CurrentLocaleConstant => this.GameContentManager.Language;
/// <summary>The observable implementation of <see cref="AssetEditors"/>.</summary>
- internal ObservableCollection<IAssetEditor> ObservableAssetEditors { get; } = new ObservableCollection<IAssetEditor>();
+ internal ObservableCollection<IAssetEditor> ObservableAssetEditors { get; } = new();
/// <summary>The observable implementation of <see cref="AssetLoaders"/>.</summary>
- internal ObservableCollection<IAssetLoader> ObservableAssetLoaders { get; } = new ObservableCollection<IAssetLoader>();
+ internal ObservableCollection<IAssetLoader> ObservableAssetLoaders { get; } = new();
/// <inheritdoc />
- public IList<IAssetLoader> AssetLoaders => this.ObservableAssetLoaders;
+ public IList<IAssetLoader> AssetLoaders
+ {
+ get
+ {
+ SCore.DeprecationManager.Warn(
+ source: this.Mod,
+ nounPhrase: $"{nameof(IContentHelper)}.{nameof(IContentHelper.AssetLoaders)}",
+ version: "3.14.0",
+ severity: DeprecationLevel.Notice
+ );
+
+ return this.ObservableAssetLoaders;
+ }
+ }
/// <inheritdoc />
- public IList<IAssetEditor> AssetEditors => this.ObservableAssetEditors;
+ public IList<IAssetEditor> AssetEditors
+ {
+ get
+ {
+ SCore.DeprecationManager.Warn(
+ source: this.Mod,
+ nounPhrase: $"{nameof(IContentHelper)}.{nameof(IContentHelper.AssetEditors)}",
+ version: "3.14.0",
+ severity: DeprecationLevel.Notice
+ );
+
+ return this.ObservableAssetEditors;
+ }
+ }
/*********
@@ -62,48 +91,62 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <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="mod">The mod using this instance.</param>
/// <param name="monitor">Encapsulates monitoring and logging.</param>
- public ContentHelper(ContentCoordinator contentCore, string modFolderPath, string modID, string modName, IMonitor monitor)
- : base(modID)
+ /// <param name="reflection">Simplifies access to private code.</param>
+ public ContentHelper(ContentCoordinator contentCore, string modFolderPath, IModMetadata mod, IMonitor monitor, Reflector reflection)
+ : base(mod)
{
- string managedAssetPrefix = contentCore.GetManagedAssetPrefix(modID);
+ string managedAssetPrefix = contentCore.GetManagedAssetPrefix(mod.Manifest.UniqueID);
this.ContentCore = contentCore;
this.GameContentManager = contentCore.CreateGameContentManager(managedAssetPrefix + ".content");
- this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, modName, modFolderPath, this.GameContentManager);
- this.ModName = modName;
+ this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, this.Mod.DisplayName, modFolderPath, this.GameContentManager);
this.Monitor = monitor;
+ this.Reflection = reflection;
}
/// <inheritdoc />
public T Load<T>(string key, ContentSource source = ContentSource.ModFolder)
+ where T : notnull
{
+ IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: source == ContentSource.GameContent);
+
try
{
this.AssertAndNormalizeAssetName(key);
switch (source)
{
case ContentSource.GameContent:
- return this.GameContentManager.Load<T>(key, this.CurrentLocaleConstant, useCache: false);
+ if (assetName.Name.EndsWith(".xnb", StringComparison.OrdinalIgnoreCase))
+ {
+ assetName = this.ContentCore.ParseAssetName(assetName.Name[..^4], allowLocales: true);
+ SCore.DeprecationManager.Warn(
+ this.Mod,
+ "loading assets from the Content folder with a .xnb file extension",
+ "3.14.0",
+ DeprecationLevel.Notice
+ );
+ }
+
+ return this.GameContentManager.LoadLocalized<T>(assetName, this.CurrentLocaleConstant, useCache: false);
case ContentSource.ModFolder:
- return this.ModContentManager.Load<T>(key, Constants.DefaultLanguage, useCache: false);
+ return this.ModContentManager.LoadExact<T>(assetName, useCache: false);
default:
- throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}: unknown content source '{source}'.");
+ throw new SContentLoadException($"{this.Mod.DisplayName} failed loading content asset '{key}' from {source}: unknown content source '{source}'.");
}
}
- catch (Exception ex) when (!(ex is SContentLoadException))
+ catch (Exception ex) when (ex is not SContentLoadException)
{
- throw new SContentLoadException($"{this.ModName} failed loading content asset '{key}' from {source}.", ex);
+ throw new SContentLoadException($"{this.Mod.DisplayName} failed loading content asset '{key}' from {source}.", ex);
}
}
/// <inheritdoc />
[Pure]
- public string NormalizeAssetName(string assetName)
+ public string NormalizeAssetName(string? assetName)
{
return this.ModContentManager.AssertAndNormalizeAssetName(assetName);
}
@@ -117,7 +160,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this.GameContentManager.AssertAndNormalizeAssetName(key);
case ContentSource.ModFolder:
- return this.ModContentManager.GetInternalAssetKey(key);
+ return this.ModContentManager.GetInternalAssetKey(key).Name;
default:
throw new NotSupportedException($"Unknown content source '{source}'.");
@@ -128,32 +171,41 @@ namespace StardewModdingAPI.Framework.ModHelpers
public bool InvalidateCache(string key)
{
string actualKey = this.GetActualAssetKey(key, ContentSource.GameContent);
- this.Monitor.Log($"Requested cache invalidation for '{actualKey}'.", LogLevel.Trace);
- return this.ContentCore.InvalidateCache(asset => asset.AssetNameEquals(actualKey)).Any();
+ this.Monitor.Log($"Requested cache invalidation for '{actualKey}'.");
+ return this.ContentCore.InvalidateCache(asset => asset.Name.IsEquivalentTo(actualKey)).Any();
}
/// <inheritdoc />
public bool InvalidateCache<T>()
+ where T : notnull
{
- this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.", LogLevel.Trace);
- return this.ContentCore.InvalidateCache((contentManager, key, type) => typeof(T).IsAssignableFrom(type)).Any();
+ 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.", LogLevel.Trace);
+ 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)
+ public IAssetData GetPatchHelper<T>(T data, string? assetName = null)
+ where T : notnull
{
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, assetName, data, this.NormalizeAssetName);
+
+ return new AssetDataForObject(
+ locale: this.CurrentLocale,
+ assetName: this.ContentCore.ParseAssetName(assetName, allowLocales: true/* no way to know if it's a game or mod asset here*/),
+ data: data,
+ getNormalizedPath: this.NormalizeAssetName,
+ reflection: this.Reflection
+ );
}
diff --git a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
index d39abc7d..9f4a7ceb 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentPackHelper.cs
@@ -22,11 +22,11 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="contentPacks">The content packs loaded for this mod.</param>
/// <param name="createContentPack">Create a temporary content pack.</param>
- public ContentPackHelper(string modID, Lazy<IContentPack[]> contentPacks, Func<string, IManifest, IContentPack> createContentPack)
- : base(modID)
+ public ContentPackHelper(IModMetadata mod, Lazy<IContentPack[]> contentPacks, Func<string, IManifest, IContentPack> createContentPack)
+ : base(mod)
{
this.ContentPacks = contentPacks;
this.CreateContentPack = createContentPack;
diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
index 4cbfd73f..2eaa940a 100644
--- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
@@ -26,11 +26,11 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="modFolderPath">The absolute path to the mod folder.</param>
/// <param name="jsonHelper">The absolute path to the mod folder.</param>
- public DataHelper(string modID, string modFolderPath, JsonHelper jsonHelper)
- : base(modID)
+ public DataHelper(IModMetadata mod, string modFolderPath, JsonHelper jsonHelper)
+ : base(mod)
{
this.ModFolderPath = modFolderPath;
this.JsonHelper = jsonHelper;
@@ -40,19 +40,21 @@ namespace StardewModdingAPI.Framework.ModHelpers
** JSON file
****/
/// <inheritdoc />
- public TModel ReadJsonFile<TModel>(string path) where TModel : class
+ public TModel? ReadJsonFile<TModel>(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.NormalizePath(path));
- return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data)
+ return this.JsonHelper.ReadJsonFileIfExists(path, out TModel? data)
? data
: null;
}
/// <inheritdoc />
- public void WriteJsonFile<TModel>(string path, TModel data) where TModel : class
+ public void WriteJsonFile<TModel>(string path, TModel? data)
+ where TModel : class
{
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).");
@@ -69,7 +71,8 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Save file
****/
/// <inheritdoc />
- public TModel ReadSaveData<TModel>(string key) where TModel : class
+ public TModel? ReadSaveData<TModel>(string key)
+ where TModel : class
{
if (Context.LoadStage == LoadStage.None)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} when a save file isn't loaded.");
@@ -80,14 +83,15 @@ namespace StardewModdingAPI.Framework.ModHelpers
string internalKey = this.GetSaveFileKey(key);
foreach (IDictionary<string, string> dataField in this.GetDataFields(Context.LoadStage))
{
- if (dataField.TryGetValue(internalKey, out string value))
+ if (dataField.TryGetValue(internalKey, out string? value))
return this.JsonHelper.Deserialize<TModel>(value);
}
return null;
}
/// <inheritdoc />
- public void WriteSaveData<TModel>(string key, TModel model) where TModel : class
+ public void WriteSaveData<TModel>(string key, TModel? model)
+ where TModel : class
{
if (Context.LoadStage == LoadStage.None)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when a save file isn't loaded.");
@@ -95,7 +99,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when connected to a remote host. (Save files are stored on the main player's computer.)");
string internalKey = this.GetSaveFileKey(key);
- string data = model != null
+ string? data = model != null
? this.JsonHelper.Serialize(model, Formatting.None)
: null;
@@ -112,16 +116,18 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Global app data
****/
/// <inheritdoc />
- public TModel ReadGlobalData<TModel>(string key) where TModel : class
+ public TModel? ReadGlobalData<TModel>(string key)
+ where TModel : class
{
string path = this.GetGlobalDataPath(key);
- return this.JsonHelper.ReadJsonFileIfExists(path, out TModel data)
+ return this.JsonHelper.ReadJsonFileIfExists(path, out TModel? data)
? data
: null;
}
/// <inheritdoc />
- public void WriteGlobalData<TModel>(string key, TModel data) where TModel : class
+ public void WriteGlobalData<TModel>(string key, TModel? data)
+ where TModel : class
{
string path = this.GetGlobalDataPath(key);
if (data != null)
diff --git a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
new file mode 100644
index 00000000..232e9287
--- /dev/null
+++ b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Linq;
+using StardewModdingAPI.Framework.Content;
+using StardewModdingAPI.Framework.ContentManagers;
+using StardewModdingAPI.Framework.Exceptions;
+using StardewModdingAPI.Framework.Reflection;
+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;
+
+ /// <summary>Simplifies access to private code.</summary>
+ private readonly Reflector Reflection;
+
+
+ /*********
+ ** 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="mod">The mod using this instance.</param>
+ /// <param name="modName">The friendly mod name for use in errors.</param>
+ /// <param name="monitor">Encapsulates monitoring and logging.</param>
+ /// <param name="reflection">Simplifies access to private code.</param>
+ public GameContentHelper(ContentCoordinator contentCore, IModMetadata mod, string modName, IMonitor monitor, Reflector reflection)
+ : base(mod)
+ {
+ string managedAssetPrefix = contentCore.GetManagedAssetPrefix(mod.Manifest.UniqueID);
+
+ this.ContentCore = contentCore;
+ this.GameContentManager = contentCore.CreateGameContentManager(managedAssetPrefix + ".content");
+ this.ModName = modName;
+ this.Monitor = monitor;
+ this.Reflection = reflection;
+ }
+
+ /// <inheritdoc />
+ public IAssetName ParseAssetName(string rawName)
+ {
+ return this.ContentCore.ParseAssetName(rawName, allowLocales: true);
+ }
+
+ /// <inheritdoc />
+ public T Load<T>(string key)
+ where T : notnull
+ {
+ IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: true);
+ return this.Load<T>(assetName);
+ }
+
+ /// <inheritdoc />
+ public T Load<T>(IAssetName assetName)
+ where T : notnull
+ {
+ try
+ {
+ return this.GameContentManager.LoadLocalized<T>(assetName, this.CurrentLocaleConstant, useCache: true);
+ }
+ 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>()
+ where T : notnull
+ {
+ 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)
+ where T : notnull
+ {
+ 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(
+ locale: this.CurrentLocale,
+ assetName: this.ContentCore.ParseAssetName(assetName, allowLocales: true),
+ data: data,
+ getNormalizedPath: key => this.ParseAssetName(key).Name,
+ reflection: this.Reflection
+ );
+ }
+
+ /// <summary>Get the underlying game content manager.</summary>
+ internal IContentManager GetUnderlyingContentManager()
+ {
+ return this.GameContentManager;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/ModHelpers/InputHelper.cs b/src/SMAPI/Framework/ModHelpers/InputHelper.cs
index 88caf4c3..6c158258 100644
--- a/src/SMAPI/Framework/ModHelpers/InputHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/InputHelper.cs
@@ -18,10 +18,10 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <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>
- public InputHelper(string modID, Func<SInputState> currentInputState)
- : base(modID)
+ public InputHelper(IModMetadata mod, Func<SInputState> currentInputState)
+ : base(mod)
{
this.CurrentInputState = currentInputState;
}
diff --git a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
new file mode 100644
index 00000000..def0b728
--- /dev/null
+++ b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs
@@ -0,0 +1,101 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using StardewModdingAPI.Framework.Content;
+using StardewModdingAPI.Framework.ContentManagers;
+using StardewModdingAPI.Framework.Exceptions;
+using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Toolkit.Utilities;
+
+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;
+
+ /// <summary>A case-insensitive lookup of relative paths within the <see cref="ContentManager.RootDirectory"/>.</summary>
+ private readonly CaseInsensitivePathLookup RelativePathCache;
+
+ /// <summary>Simplifies access to private code.</summary>
+ private readonly Reflector Reflection;
+
+
+ /*********
+ ** 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="mod">The mod using this instance.</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>
+ /// <param name="relativePathCache">A case-insensitive lookup of relative paths within the <paramref name="relativePathCache"/>.</param>
+ /// <param name="reflection">Simplifies access to private code.</param>
+ public ModContentHelper(ContentCoordinator contentCore, string modFolderPath, IModMetadata mod, string modName, IContentManager gameContentManager, CaseInsensitivePathLookup relativePathCache, Reflector reflection)
+ : base(mod)
+ {
+ string managedAssetPrefix = contentCore.GetManagedAssetPrefix(mod.Manifest.UniqueID);
+
+ this.ContentCore = contentCore;
+ this.ModContentManager = contentCore.CreateModContentManager(managedAssetPrefix, modName, modFolderPath, gameContentManager);
+ this.ModName = modName;
+ this.RelativePathCache = relativePathCache;
+ this.Reflection = reflection;
+ }
+
+ /// <inheritdoc />
+ public T Load<T>(string relativePath)
+ where T : notnull
+ {
+ relativePath = this.RelativePathCache.GetAssetName(relativePath);
+
+ IAssetName assetName = this.ContentCore.ParseAssetName(relativePath, allowLocales: false);
+
+ 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)
+ {
+ relativePath = this.RelativePathCache.GetAssetName(relativePath);
+ return this.ModContentManager.GetInternalAssetKey(relativePath);
+ }
+
+ /// <inheritdoc />
+ public IAssetData GetPatchHelper<T>(T data, string? relativePath = null)
+ where T : notnull
+ {
+ if (data == null)
+ throw new ArgumentNullException(nameof(data), "Can't get a patch helper for a null value.");
+
+ relativePath = relativePath != null
+ ? this.RelativePathCache.GetAssetName(relativePath)
+ : $"temp/{Guid.NewGuid():N}";
+
+ return new AssetDataForObject(
+ locale: this.ContentCore.GetLocale(),
+ assetName: this.ContentCore.ParseAssetName(relativePath, allowLocales: false),
+ data: data,
+ getNormalizedPath: key => this.ContentCore.ParseAssetName(key, allowLocales: false).Name,
+ reflection: this.Reflection
+ );
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
index 058bff83..a23a9beb 100644
--- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs
@@ -1,6 +1,7 @@
using System;
using System.IO;
using StardewModdingAPI.Events;
+using StardewModdingAPI.Framework.Deprecations;
using StardewModdingAPI.Framework.Input;
namespace StardewModdingAPI.Framework.ModHelpers
@@ -9,6 +10,14 @@ namespace StardewModdingAPI.Framework.ModHelpers
internal class ModHelper : BaseHelper, IModHelper, IDisposable
{
/*********
+ ** Fields
+ *********/
+ /// <summary>The backing field for <see cref="Content"/>.</summary>
+ [Obsolete]
+ private readonly ContentHelper ContentImpl;
+
+
+ /*********
** Accessors
*********/
/// <inheritdoc />
@@ -18,7 +27,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.GetMod(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; }
@@ -49,11 +78,13 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The mod's unique ID.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="modDirectory">The full path to the mod's folder.</param>
/// <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,8 +94,14 @@ 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)
- : base(modID)
+ public ModHelper(
+ IModMetadata mod, string modDirectory, Func<SInputState> currentInputState, IModEvents events,
+#pragma warning disable CS0612 // deprecated code
+ ContentHelper contentHelper,
+#pragma warning restore CS0612
+ IGameContentHelper gameContentHelper, IModContentHelper modContentHelper, IContentPackHelper contentPackHelper, ICommandHelper commandHelper, IDataHelper dataHelper, IModRegistry modRegistry, IReflectionHelper reflectionHelper, IMultiplayerHelper multiplayer, ITranslationHelper translationHelper
+ )
+ : base(mod)
{
// validate directory
if (string.IsNullOrWhiteSpace(modDirectory))
@@ -74,10 +111,14 @@ namespace StardewModdingAPI.Framework.ModHelpers
// initialize
this.DirectoryPath = modDirectory;
- this.Content = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper));
+#pragma warning disable CS0612 // deprecated code
+ this.ContentImpl = contentHelper ?? throw new ArgumentNullException(nameof(contentHelper));
+#pragma warning restore CS0612
+ 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);
+ this.Input = new InputHelper(mod, currentInputState);
this.ModRegistry = modRegistry ?? throw new ArgumentNullException(nameof(modRegistry));
this.ConsoleCommands = commandHelper ?? throw new ArgumentNullException(nameof(commandHelper));
this.Reflection = reflectionHelper ?? throw new ArgumentNullException(nameof(reflectionHelper));
@@ -86,6 +127,13 @@ namespace StardewModdingAPI.Framework.ModHelpers
this.Events = events;
}
+ /// <summary>Get the underlying instance for <see cref="IContentHelper"/>.</summary>
+ [Obsolete]
+ public ContentHelper GetLegacyContentHelper()
+ {
+ return this.ContentImpl;
+ }
+
/****
** Mod config file
****/
diff --git a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
index ef1ad30c..348ba225 100644
--- a/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs
@@ -16,22 +16,22 @@ namespace StardewModdingAPI.Framework.ModHelpers
private readonly IMonitor Monitor;
/// <summary>The mod IDs for APIs accessed by this instanced.</summary>
- private readonly HashSet<string> AccessedModApis = new HashSet<string>();
+ private readonly HashSet<string> AccessedModApis = new();
/// <summary>Generates proxy classes to access mod APIs through an arbitrary interface.</summary>
- private readonly InterfaceProxyFactory ProxyFactory;
+ private readonly IInterfaceProxyFactory ProxyFactory;
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="registry">The underlying mod registry.</param>
/// <param name="proxyFactory">Generates proxy classes to access mod APIs through an arbitrary interface.</param>
/// <param name="monitor">Encapsulates monitoring and logging for the mod.</param>
- public ModRegistryHelper(string modID, ModRegistry registry, InterfaceProxyFactory proxyFactory, IMonitor monitor)
- : base(modID)
+ public ModRegistryHelper(IModMetadata mod, ModRegistry registry, IInterfaceProxyFactory proxyFactory, IMonitor monitor)
+ : base(mod)
{
this.Registry = registry;
this.ProxyFactory = proxyFactory;
@@ -45,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IModInfo Get(string uniqueID)
+ public IModInfo? Get(string uniqueID)
{
return this.Registry.Get(uniqueID);
}
@@ -57,7 +57,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public object GetApi(string uniqueID)
+ public object? GetApi(string uniqueID)
{
// validate ready
if (!this.Registry.AreAllModsInitialized)
@@ -67,17 +67,18 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
// get raw API
- IModMetadata mod = this.Registry.Get(uniqueID);
+ 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);
+ this.Monitor.Log($"Accessed mod-provided API for {mod.DisplayName}.");
return mod?.Api;
}
/// <inheritdoc />
- public TInterface GetApi<TInterface>(string uniqueID) where TInterface : class
+ public TInterface? GetApi<TInterface>(string uniqueID)
+ where TInterface : class
{
// get raw API
- object api = this.GetApi(uniqueID);
+ object? api = this.GetApi(uniqueID);
if (api == null)
return null;
@@ -94,9 +95,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
// get API of type
- if (api is TInterface castApi)
- return castApi;
- return this.ProxyFactory.CreateProxy<TInterface>(api, this.ModID, uniqueID);
+ return api is TInterface castApi
+ ? castApi
+ : this.ProxyFactory.CreateProxy<TInterface>(api, sourceModID: this.ModID, targetModID: uniqueID);
}
}
}
diff --git a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
index a7ce8692..6900a1d2 100644
--- a/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/MultiplayerHelper.cs
@@ -18,10 +18,10 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="multiplayer">SMAPI's core multiplayer utility.</param>
- public MultiplayerHelper(string modID, SMultiplayer multiplayer)
- : base(modID)
+ public MultiplayerHelper(IModMetadata mod, SMultiplayer multiplayer)
+ : base(mod)
{
this.Multiplayer = multiplayer;
}
@@ -39,9 +39,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public IMultiplayerPeer GetConnectedPlayer(long id)
+ public IMultiplayerPeer? GetConnectedPlayer(long id)
{
- return this.Multiplayer.Peers.TryGetValue(id, out MultiplayerPeer peer)
+ return this.Multiplayer.Peers.TryGetValue(id, out MultiplayerPeer? peer)
? peer
: null;
}
@@ -53,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public void SendMessage<TMessage>(TMessage message, string messageType, string[] modIDs = null, long[] playerIDs = null)
+ public void SendMessage<TMessage>(TMessage message, string messageType, string[]? modIDs = null, long[]? playerIDs = null)
{
this.Multiplayer.BroadcastModMessage(
message: message,
diff --git a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
index 5a4ea742..a559906b 100644
--- a/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs
@@ -22,11 +22,11 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="modName">The mod name for error messages.</param>
/// <param name="reflector">The underlying reflection helper.</param>
- public ReflectionHelper(string modID, string modName, Reflector reflector)
- : base(modID)
+ public ReflectionHelper(IModMetadata mod, string modName, Reflector reflector)
+ : base(mod)
{
this.ModName = modName;
this.Reflector = reflector;
@@ -37,7 +37,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetField<TValue>(obj, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -45,7 +45,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetField<TValue>(type, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -53,7 +53,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetProperty<TValue>(obj, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -61,7 +61,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetProperty<TValue>(type, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -69,7 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetMethod(obj, name, required)
- );
+ )!;
}
/// <inheritdoc />
@@ -77,7 +77,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
{
return this.AssertAccessAllowed(
this.Reflector.GetMethod(type, name, required)
- );
+ )!;
}
@@ -88,7 +88,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <typeparam name="T">The field value type.</typeparam>
/// <param name="field">The field being accessed.</param>
/// <returns>Returns the same field instance for convenience.</returns>
- private IReflectedField<T> AssertAccessAllowed<T>(IReflectedField<T> field)
+ private IReflectedField<T>? AssertAccessAllowed<T>(IReflectedField<T>? field)
{
this.AssertAccessAllowed(field?.FieldInfo);
return field;
@@ -98,7 +98,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <typeparam name="T">The property value type.</typeparam>
/// <param name="property">The property being accessed.</param>
/// <returns>Returns the same property instance for convenience.</returns>
- private IReflectedProperty<T> AssertAccessAllowed<T>(IReflectedProperty<T> property)
+ private IReflectedProperty<T>? AssertAccessAllowed<T>(IReflectedProperty<T>? property)
{
this.AssertAccessAllowed(property?.PropertyInfo.GetMethod?.GetBaseDefinition());
this.AssertAccessAllowed(property?.PropertyInfo.SetMethod?.GetBaseDefinition());
@@ -108,7 +108,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>Assert that mods can use the reflection helper to access the given member.</summary>
/// <param name="method">The method being accessed.</param>
/// <returns>Returns the same method instance for convenience.</returns>
- private IReflectedMethod AssertAccessAllowed(IReflectedMethod method)
+ private IReflectedMethod? AssertAccessAllowed(IReflectedMethod? method)
{
this.AssertAccessAllowed(method?.MethodInfo.GetBaseDefinition());
return method;
@@ -116,18 +116,18 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <summary>Assert that mods can use the reflection helper to access the given member.</summary>
/// <param name="member">The member being accessed.</param>
- private void AssertAccessAllowed(MemberInfo member)
+ private void AssertAccessAllowed(MemberInfo? member)
{
if (member == null)
return;
// get type which defines the member
- Type declaringType = member.DeclaringType;
+ Type? declaringType = member.DeclaringType;
if (declaringType == null)
throw new InvalidOperationException($"Can't validate access to {member.MemberType} {member.Name} because it has no declaring type."); // should never happen
// validate access
- string rootNamespace = typeof(Program).Namespace;
+ string? rootNamespace = typeof(Program).Namespace;
if (declaringType.Namespace == rootNamespace || declaringType.Namespace?.StartsWith(rootNamespace + ".") == true)
throw new InvalidOperationException($"SMAPI blocked access by {this.ModName} to its internals through the reflection API. Accessing the SMAPI internals is strongly discouraged since they're subject to change, which means the mod can break without warning. (Detected access to {declaringType.FullName}.{member.Name}.)");
}
diff --git a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
index 869664fe..ae49d651 100644
--- a/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/TranslationHelper.cs
@@ -27,11 +27,11 @@ namespace StardewModdingAPI.Framework.ModHelpers
** Public methods
*********/
/// <summary>Construct an instance.</summary>
- /// <param name="modID">The unique ID of the relevant mod.</param>
+ /// <param name="mod">The mod using this instance.</param>
/// <param name="locale">The initial locale.</param>
/// <param name="languageCode">The game's current language code.</param>
- public TranslationHelper(string modID, string locale, LocalizedContentManager.LanguageCode languageCode)
- : base(modID)
+ public TranslationHelper(IModMetadata mod, string locale, LocalizedContentManager.LanguageCode languageCode)
+ : base(mod)
{
this.Translator = new Translator();
this.Translator.SetLocale(locale, languageCode);
@@ -50,7 +50,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
}
/// <inheritdoc />
- public Translation Get(string key, object tokens)
+ public Translation Get(string key, object? tokens)
{
return this.Translator.Get(key, tokens);
}
@@ -69,7 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this;
}
- /// <summary>Set the current locale and precache translations.</summary>
+ /// <summary>Set the current locale and pre-cache translations.</summary>
/// <param name="locale">The current locale.</param>
/// <param name="localeEnum">The game's current language code.</param>
internal void SetLocale(string locale, LocalizedContentManager.LanguageCode localeEnum)