summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2017-10-01 14:08:28 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2017-10-01 14:08:28 -0400
commitbd4ed4382909ff368536a7ce8f0e5f514658d288 (patch)
tree69692e93c76dbe1157717caf64326e679cece9f6 /src/StardewModdingAPI
parent29232ffd459f67813fd599cc074610519e3a5148 (diff)
downloadSMAPI-bd4ed4382909ff368536a7ce8f0e5f514658d288.tar.gz
SMAPI-bd4ed4382909ff368536a7ce8f0e5f514658d288.tar.bz2
SMAPI-bd4ed4382909ff368536a7ce8f0e5f514658d288.zip
fix errors caused by content managers finalizing asynchronously
Diffstat (limited to 'src/StardewModdingAPI')
-rw-r--r--src/StardewModdingAPI/Framework/SContentManager.cs157
1 files changed, 86 insertions, 71 deletions
diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs
index 33403841..43de6e96 100644
--- a/src/StardewModdingAPI/Framework/SContentManager.cs
+++ b/src/StardewModdingAPI/Framework/SContentManager.cs
@@ -128,8 +128,11 @@ namespace StardewModdingAPI.Framework
/// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param>
public bool IsLoaded(string assetName)
{
- assetName = this.NormaliseAssetName(assetName);
- return this.IsNormalisedKeyLoaded(assetName);
+ lock (this.Cache)
+ {
+ assetName = this.NormaliseAssetName(assetName);
+ return this.IsNormalisedKeyLoaded(assetName);
+ }
}
/// <summary>Load an asset that has been processed by the content pipeline.</summary>
@@ -146,38 +149,41 @@ namespace StardewModdingAPI.Framework
/// <param name="instance">The content manager instance for which to load the asset.</param>
public T LoadFor<T>(string assetName, ContentManager instance)
{
- assetName = this.NormaliseAssetName(assetName);
-
- // skip if already loaded
- if (this.IsNormalisedKeyLoaded(assetName))
+ lock (this.Cache)
{
- this.TrackAssetLoader(assetName, instance);
- return base.Load<T>(assetName);
- }
+ assetName = this.NormaliseAssetName(assetName);
- // load asset
- T data;
- 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);
- data = base.Load<T>(assetName);
- }
- else
- {
- data = this.AssetsBeingLoaded.Track(assetName, () =>
+ // skip if already loaded
+ if (this.IsNormalisedKeyLoaded(assetName))
{
- IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName);
- IAssetData asset = this.ApplyLoader<T>(info) ?? new AssetDataForObject(info, base.Load<T>(assetName), this.NormaliseAssetName);
- asset = this.ApplyEditors<T>(info, asset);
- return (T)asset.Data;
- });
- }
+ this.TrackAssetLoader(assetName, instance);
+ return base.Load<T>(assetName);
+ }
- // update cache & return data
- this.Cache[assetName] = data;
- this.TrackAssetLoader(assetName, instance);
- return data;
+ // load asset
+ T data;
+ 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);
+ data = base.Load<T>(assetName);
+ }
+ else
+ {
+ data = this.AssetsBeingLoaded.Track(assetName, () =>
+ {
+ IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName);
+ IAssetData asset = this.ApplyLoader<T>(info) ?? new AssetDataForObject(info, base.Load<T>(assetName), this.NormaliseAssetName);
+ asset = this.ApplyEditors<T>(info, asset);
+ return (T)asset.Data;
+ });
+ }
+
+ // update cache & return data
+ this.Cache[assetName] = data;
+ this.TrackAssetLoader(assetName, instance);
+ return data;
+ }
}
/// <summary>Inject an asset into the cache.</summary>
@@ -186,9 +192,12 @@ namespace StardewModdingAPI.Framework
/// <param name="value">The asset value.</param>
public void Inject<T>(string assetName, T value)
{
- assetName = this.NormaliseAssetName(assetName);
- this.Cache[assetName] = value;
- this.TrackAssetLoader(assetName, this);
+ lock (this.Cache)
+ {
+ assetName = this.NormaliseAssetName(assetName);
+ this.Cache[assetName] = value;
+ this.TrackAssetLoader(assetName, this);
+ }
}
/// <summary>Get the current content locale.</summary>
@@ -200,16 +209,19 @@ namespace StardewModdingAPI.Framework
/// <summary>Get the cached asset keys.</summary>
public IEnumerable<string> GetAssetKeys()
{
- IEnumerable<string> GetAllAssetKeys()
+ lock (this.Cache)
{
- foreach (string cacheKey in this.Cache.Keys)
+ IEnumerable<string> GetAllAssetKeys()
{
- this.ParseCacheKey(cacheKey, out string assetKey, out string _);
- yield return assetKey;
+ foreach (string cacheKey in this.Cache.Keys)
+ {
+ this.ParseCacheKey(cacheKey, out string assetKey, out string _);
+ yield return assetKey;
+ }
}
- }
- return GetAllAssetKeys().Distinct();
+ return GetAllAssetKeys().Distinct();
+ }
}
/// <summary>Purge assets from the cache that match one of the interceptors.</summary>
@@ -248,45 +260,48 @@ namespace StardewModdingAPI.Framework
/// <returns>Returns whether any cache entries were invalidated.</returns>
public bool InvalidateCache(Func<string, Type, bool> predicate, bool dispose = false)
{
- // find matching asset keys
- HashSet<string> purgeCacheKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
- HashSet<string> purgeAssetKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
- foreach (string cacheKey in this.Cache.Keys)
+ lock (this.Cache)
{
- this.ParseCacheKey(cacheKey, out string assetKey, out _);
- Type type = this.Cache[cacheKey].GetType();
- if (predicate(assetKey, type))
+ // find matching asset keys
+ HashSet<string> purgeCacheKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+ HashSet<string> purgeAssetKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+ foreach (string cacheKey in this.Cache.Keys)
{
- purgeAssetKeys.Add(assetKey);
- purgeCacheKeys.Add(cacheKey);
+ this.ParseCacheKey(cacheKey, out string assetKey, out _);
+ Type type = this.Cache[cacheKey].GetType();
+ if (predicate(assetKey, type))
+ {
+ purgeAssetKeys.Add(assetKey);
+ purgeCacheKeys.Add(cacheKey);
+ }
}
- }
- // purge assets
- foreach (string key in purgeCacheKeys)
- {
- if (dispose && this.Cache[key] is IDisposable disposable)
- disposable.Dispose();
- this.Cache.Remove(key);
- this.AssetLoaders.Remove(key);
- }
+ // purge assets
+ foreach (string key in purgeCacheKeys)
+ {
+ if (dispose && this.Cache[key] is IDisposable disposable)
+ disposable.Dispose();
+ this.Cache.Remove(key);
+ this.AssetLoaders.Remove(key);
+ }
- // reload core game assets
- int reloaded = 0;
- foreach (string key in purgeAssetKeys)
- {
- if (this.CoreAssets.ReloadForKey(this, key))
- reloaded++;
- }
+ // reload core game assets
+ int reloaded = 0;
+ foreach (string key in purgeAssetKeys)
+ {
+ if (this.CoreAssets.ReloadForKey(this, key))
+ reloaded++;
+ }
- // report result
- if (purgeCacheKeys.Any())
- {
- this.Monitor.Log($"Invalidated {purgeCacheKeys.Count} cache entries for {purgeAssetKeys.Count} asset keys: {string.Join(", ", purgeCacheKeys.OrderBy(p => p, StringComparer.InvariantCultureIgnoreCase))}. Reloaded {reloaded} core assets.", LogLevel.Trace);
- return true;
+ // report result
+ if (purgeCacheKeys.Any())
+ {
+ this.Monitor.Log($"Invalidated {purgeCacheKeys.Count} cache entries for {purgeAssetKeys.Count} asset keys: {string.Join(", ", purgeCacheKeys.OrderBy(p => p, StringComparer.InvariantCultureIgnoreCase))}. Reloaded {reloaded} core assets.", LogLevel.Trace);
+ return true;
+ }
+ this.Monitor.Log("Invalidated 0 cache entries.", LogLevel.Trace);
+ return false;
}
- this.Monitor.Log("Invalidated 0 cache entries.", LogLevel.Trace);
- return false;
}
/// <summary>Dispose assets for the given content manager shim.</summary>