summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/ContentManagers
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework/ContentManagers')
-rw-r--r--src/SMAPI/Framework/ContentManagers/BaseContentManager.cs85
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs57
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs47
-rw-r--r--src/SMAPI/Framework/ContentManagers/IContentManager.cs10
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs26
5 files changed, 138 insertions, 87 deletions
diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
index 92264f8c..1a64dab8 100644
--- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs
@@ -5,12 +5,12 @@ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
-using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
using StardewValley;
+using xTile;
namespace StardewModdingAPI.Framework.ContentManagers
{
@@ -29,6 +29,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>Encapsulates monitoring and logging.</summary>
protected readonly IMonitor Monitor;
+ /// <summary>Whether to enable more aggressive memory optimizations.</summary>
+ protected readonly bool AggressiveMemoryOptimizations;
+
/// <summary>Whether the content coordinator has been disposed.</summary>
private bool IsDisposed;
@@ -49,16 +52,16 @@ namespace StardewModdingAPI.Framework.ContentManagers
/*********
** Accessors
*********/
- /// <summary>A name for the mod manager. Not guaranteed to be unique.</summary>
+ /// <inheritdoc />
public string Name { get; }
- /// <summary>The current language as a constant.</summary>
+ /// <inheritdoc />
public LanguageCode Language => this.GetCurrentLanguage();
- /// <summary>The absolute path to the <see cref="ContentManager.RootDirectory"/>.</summary>
+ /// <inheritdoc />
public string FullRootDirectory => Path.Combine(Constants.ExecutionPath, this.RootDirectory);
- /// <summary>Whether this content manager can be targeted by managed asset keys (e.g. to load assets from a mod folder).</summary>
+ /// <inheritdoc />
public bool IsNamespaced { get; }
@@ -75,7 +78,8 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
/// <param name="isNamespaced">Whether this content manager handles managed asset keys (e.g. to load assets from a mod folder).</param>
- protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, bool isNamespaced)
+ /// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
+ protected BaseContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, bool isNamespaced, bool aggressiveMemoryOptimizations)
: base(serviceProvider, rootDirectory, currentCulture)
{
// init
@@ -85,59 +89,49 @@ namespace StardewModdingAPI.Framework.ContentManagers
this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor));
this.OnDisposing = onDisposing;
this.IsNamespaced = isNamespaced;
+ this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
// get asset data
this.LanguageCodes = this.GetKeyLocales().ToDictionary(p => p.Value, p => p.Key, StringComparer.OrdinalIgnoreCase);
this.BaseDisposableReferences = reflection.GetField<List<IDisposable>>(this, "disposableAssets").GetValue();
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
+ /// <inheritdoc />
public override T Load<T>(string assetName)
{
return this.Load<T>(assetName, this.Language, useCache: true);
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="language">The language code for which to load content.</param>
+ /// <inheritdoc />
public override T Load<T>(string assetName, LanguageCode language)
{
return this.Load<T>(assetName, language, useCache: true);
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="language">The language code for which to load content.</param>
- /// <param name="useCache">Whether to read/write the loaded asset to the asset cache.</param>
+ /// <inheritdoc />
public abstract T Load<T>(string assetName, LocalizedContentManager.LanguageCode language, bool useCache);
- /// <summary>Load the base asset without localization.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
+ /// <inheritdoc />
[Obsolete("This method is implemented for the base game and should not be used directly. To load an asset from the underlying content manager directly, use " + nameof(BaseContentManager.RawLoad) + " instead.")]
public override T LoadBase<T>(string assetName)
{
return this.Load<T>(assetName, LanguageCode.en, useCache: true);
}
- /// <summary>Perform any cleanup needed when the locale changes.</summary>
+ /// <inheritdoc />
public virtual void OnLocaleChanged() { }
- /// <summary>Normalize path separators in a file path. For asset keys, see <see cref="AssertAndNormalizeAssetName"/> instead.</summary>
- /// <param name="path">The file path to normalize.</param>
+ /// <inheritdoc />
+ public virtual void OnReturningToTitleScreen() { }
+
+ /// <inheritdoc />
[Pure]
public string NormalizePathSeparators(string path)
{
return this.Cache.NormalizePathSeparators(path);
}
- /// <summary>Assert that the given key has a valid format and return a normalized form consistent with the underlying cache.</summary>
- /// <param name="assetName">The asset key to check.</param>
- /// <exception cref="SContentLoadException">The asset key is empty or contains invalid characters.</exception>
+ /// <inheritdoc />
[SuppressMessage("ReSharper", "ParameterOnlyUsedForPreconditionCheck.Local", Justification = "Parameter is only used for assertion checks by design.")]
public string AssertAndNormalizeAssetName(string assetName)
{
@@ -154,29 +148,26 @@ namespace StardewModdingAPI.Framework.ContentManagers
/****
** Content loading
****/
- /// <summary>Get the current content locale.</summary>
+ /// <inheritdoc />
public string GetLocale()
{
return this.GetLocale(this.GetCurrentLanguage());
}
- /// <summary>The locale for a language.</summary>
- /// <param name="language">The language.</param>
+ /// <inheritdoc />
public string GetLocale(LanguageCode language)
{
return this.LanguageCodeString(language);
}
- /// <summary>Get whether the content manager has already loaded and cached the given asset.</summary>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="language">The language.</param>
+ /// <inheritdoc />
public bool IsLoaded(string assetName, LanguageCode language)
{
assetName = this.Cache.NormalizeKey(assetName);
return this.IsNormalizedKeyLoaded(assetName, language);
}
- /// <summary>Get the cached asset keys.</summary>
+ /// <inheritdoc />
public IEnumerable<string> GetAssetKeys()
{
return this.Cache.Keys
@@ -187,10 +178,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/****
** Cache invalidation
****/
- /// <summary>Purge matched assets from the cache.</summary>
- /// <param name="predicate">Matches the asset keys to invalidate.</param>
- /// <param name="dispose">Whether to dispose invalidated assets. This should only be <c>true</c> when they're being invalidated as part of a dispose, to avoid crashing the game.</param>
- /// <returns>Returns the invalidated asset names and instances.</returns>
+ /// <inheritdoc />
public IDictionary<string, object> InvalidateCache(Func<string, Type, bool> predicate, bool dispose = false)
{
IDictionary<string, object> removeAssets = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
@@ -198,21 +186,28 @@ namespace StardewModdingAPI.Framework.ContentManagers
{
this.ParseCacheKey(key, out string assetName, out _);
- if (removeAssets.ContainsKey(assetName))
- return true;
- if (predicate(assetName, asset.GetType()))
+ // check if asset should be removed
+ bool remove = removeAssets.ContainsKey(assetName);
+ if (!remove && predicate(assetName, asset.GetType()))
{
removeAssets[assetName] = asset;
- return true;
+ remove = true;
+ }
+
+ // dispose if safe
+ if (remove && this.AggressiveMemoryOptimizations)
+ {
+ if (asset is Map map)
+ map.DisposeTileSheets(Game1.mapDisplayDevice);
}
- return false;
+
+ return remove;
}, dispose);
return removeAssets;
}
- /// <summary>Dispose held resources.</summary>
- /// <param name="isDisposing">Whether the content manager is being disposed (rather than finalized).</param>
+ /// <inheritdoc />
protected override void Dispose(bool isDisposing)
{
// ignore if disposed
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 665c019b..8e78faba 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -52,17 +52,14 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
/// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param>
- public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset)
- : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false)
+ /// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
+ public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
{
this.OnLoadingFirstAsset = onLoadingFirstAsset;
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="language">The language code for which to load content.</param>
- /// <param name="useCache">Whether to read/write the loaded asset to the asset cache.</param>
+ /// <inheritdoc />
public override T Load<T>(string assetName, LocalizedContentManager.LanguageCode language, bool useCache)
{
// raise first-load callback
@@ -94,7 +91,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
if (this.AssetsBeingLoaded.Contains(assetName))
{
this.Monitor.Log($"Broke loop while loading asset '{assetName}'.", LogLevel.Warn);
- this.Monitor.Log($"Bypassing mod loaders for this asset. Stack trace:\n{Environment.StackTrace}", LogLevel.Trace);
+ this.Monitor.Log($"Bypassing mod loaders for this asset. Stack trace:\n{Environment.StackTrace}");
data = this.RawLoad<T>(assetName, language, useCache);
}
else
@@ -116,7 +113,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
return data;
}
- /// <summary>Perform any cleanup needed when the locale changes.</summary>
+ /// <inheritdoc />
public override void OnLocaleChanged()
{
base.OnLocaleChanged();
@@ -136,10 +133,35 @@ namespace StardewModdingAPI.Framework.ContentManagers
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToArray();
if (invalidated.Any())
- this.Monitor.Log($"Invalidated {invalidated.Length} asset names: {string.Join(", ", invalidated)} for locale change.", LogLevel.Trace);
+ this.Monitor.Log($"Invalidated {invalidated.Length} asset names: {string.Join(", ", invalidated)} for locale change.");
+ }
+
+ /// <inheritdoc />
+ public override void OnReturningToTitleScreen()
+ {
+ // The game clears LocalizedContentManager.localizedAssetNames after returning to the title screen. That
+ // causes an inconsistency in the SMAPI asset cache, which leads to an edge case where assets already
+ // provided by mods via IAssetLoader when playing in non-English are ignored.
+ //
+ // For example, let's say a mod provides the 'Data\mail' asset through IAssetLoader when playing in
+ // Portuguese. Here's the normal load process after it's loaded:
+ // 1. The game requests Data\mail.
+ // 2. SMAPI sees that it's already cached, and calls LoadRaw to bypass asset interception.
+ // 3. LoadRaw sees that there's a localized key mapping, and gets the mapped key.
+ // 4. In this case "Data\mail" is mapped to "Data\mail" since it was loaded by a mod, so it loads that
+ // asset.
+ //
+ // When the game clears localizedAssetNames, that process goes wrong in step 4:
+ // 3. LoadRaw sees that there's no localized key mapping *and* the locale is non-English, so it attempts
+ // to load from the localized key format.
+ // 4. In this case that's 'Data\mail.pt-BR', so it successfully loads that asset.
+ // 5. Since we've bypassed asset interception at this point, it's loaded directly from the base content
+ // manager without mod changes.
+ if (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.en)
+ this.InvalidateCache((_, _) => true);
}
- /// <summary>Create a new content manager for temporary use.</summary>
+ /// <inheritdoc />
public override LocalizedContentManager CreateTemporary()
{
return this.Coordinator.CreateGameContentManager("(temporary)");
@@ -149,9 +171,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/*********
** Private methods
*********/
- /// <summary>Get whether an asset has already been loaded.</summary>
- /// <param name="normalizedAssetName">The normalized asset name.</param>
- /// <param name="language">The language to check.</param>
+ /// <inheritdoc />
protected override bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language)
{
string cachedKey = null;
@@ -165,12 +185,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
: this.Cache.ContainsKey(normalizedAssetName);
}
- /// <summary>Add tracking data to an asset and add it to the cache.</summary>
- /// <typeparam name="T">The type of asset to inject.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="value">The asset value.</param>
- /// <param name="language">The language code for which to inject the asset.</param>
- /// <param name="useCache">Whether to save the asset to the asset cache.</param>
+ /// <inheritdoc />
protected override void TrackAsset<T>(string assetName, T value, LanguageCode language, bool useCache)
{
// handle explicit language in asset name
@@ -358,7 +373,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
try
{
editor.Edit<T>(asset);
- this.Monitor.Log($"{mod.DisplayName} edited {info.AssetName}.", LogLevel.Trace);
+ this.Monitor.Log($"{mod.DisplayName} edited {info.AssetName}.");
}
catch (Exception ex)
{
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
new file mode 100644
index 00000000..61683ce6
--- /dev/null
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Globalization;
+using Microsoft.Xna.Framework.Graphics;
+using StardewModdingAPI.Framework.Reflection;
+using StardewValley;
+
+namespace StardewModdingAPI.Framework.ContentManagers
+{
+ /// <summary>An extension of <see cref="GameContentManager"/> specifically optimized for asset propagation.</summary>
+ /// <remarks>This avoids sharing an asset cache with <see cref="Game1.content"/> or mods, so that assets can be safely disposed when the vanilla game no longer references them.</remarks>
+ internal class GameContentManagerForAssetPropagation : GameContentManager
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>A unique value used in <see cref="Texture2D"/> to identify assets loaded through this instance.</summary>
+ private readonly string Tag = $"Pathoschild.SMAPI/LoadedBy:{nameof(GameContentManagerForAssetPropagation)}";
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <inheritdoc />
+ public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, aggressiveMemoryOptimizations) { }
+
+ /// <inheritdoc />
+ public override T Load<T>(string assetName, LanguageCode language, bool useCache)
+ {
+ T data = base.Load<T>(assetName, language, useCache);
+
+ if (data is Texture2D texture)
+ texture.Tag = this.Tag;
+
+ return data;
+ }
+
+ /// <summary>Get whether a texture was loaded by this content manager.</summary>
+ /// <param name="texture">The texture to check.</param>
+ public bool IsResponsibleFor(Texture2D texture)
+ {
+ return
+ texture?.Tag is string tag
+ && tag.Contains(this.Tag);
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/ContentManagers/IContentManager.cs b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
index 0e7edd8f..1e222472 100644
--- a/src/SMAPI/Framework/ContentManagers/IContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/IContentManager.cs
@@ -36,9 +36,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="useCache">Whether to read/write the loaded asset to the asset cache.</param>
T Load<T>(string assetName, LocalizedContentManager.LanguageCode language, bool useCache);
- /// <summary>Perform any cleanup needed when the locale changes.</summary>
- void OnLocaleChanged();
-
/// <summary>Normalize path separators in a file path. For asset keys, see <see cref="AssertAndNormalizeAssetName"/> instead.</summary>
/// <param name="path">The file path to normalize.</param>
[Pure]
@@ -69,5 +66,12 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="dispose">Whether to dispose invalidated assets. This should only be <c>true</c> when they're being invalidated as part of a dispose, to avoid crashing the game.</param>
/// <returns>Returns the invalidated asset names and instances.</returns>
IDictionary<string, object> InvalidateCache(Func<string, Type, bool> predicate, bool dispose = false);
+
+ /// <summary>Perform any cleanup needed when the locale changes.</summary>
+ void OnLocaleChanged();
+
+ /// <summary>Clean up when the player is returning to the title screen.</summary>
+ /// <remarks>This is called after the player returns to the title screen, but before <see cref="Game1.CleanupReturningToTitle"/> runs.</remarks>
+ void OnReturningToTitleScreen();
}
}
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index 1456d3c1..9af14cb5 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -50,36 +50,28 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
/// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param>
- public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing)
- : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true)
+ /// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
+ public ModContentManager(string name, IContentManager gameContentManager, IServiceProvider serviceProvider, string modName, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action<BaseContentManager> onDisposing, bool aggressiveMemoryOptimizations)
+ : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: true, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations)
{
this.GameContentManager = gameContentManager;
this.JsonHelper = jsonHelper;
this.ModName = modName;
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
+ /// <inheritdoc />
public override T Load<T>(string assetName)
{
return this.Load<T>(assetName, this.DefaultLanguage, useCache: false);
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="language">The language code for which to load content.</param>
+ /// <inheritdoc />
public override T Load<T>(string assetName, LanguageCode language)
{
return this.Load<T>(assetName, language, useCache: false);
}
- /// <summary>Load an asset that has been processed by the content pipeline.</summary>
- /// <typeparam name="T">The type of asset to load.</typeparam>
- /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
- /// <param name="language">The language code for which to load content.</param>
- /// <param name="useCache">Whether to read/write the loaded asset to the asset cache.</param>
+ /// <inheritdoc />
public override T Load<T>(string assetName, LanguageCode language, bool useCache)
{
assetName = this.AssertAndNormalizeAssetName(assetName);
@@ -189,7 +181,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
return asset;
}
- /// <summary>Create a new content manager for temporary use.</summary>
+ /// <inheritdoc />
public override LocalizedContentManager CreateTemporary()
{
throw new NotSupportedException("Can't create a temporary mod content manager.");
@@ -209,9 +201,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
/*********
** Private methods
*********/
- /// <summary>Get whether an asset has already been loaded.</summary>
- /// <param name="normalizedAssetName">The normalized asset name.</param>
- /// <param name="language">The language to check.</param>
+ /// <inheritdoc />
protected override bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language)
{
return this.Cache.ContainsKey(normalizedAssetName);