fix errors caused by content managers finalizing asynchronously
This commit is contained in:
parent
29232ffd45
commit
bd4ed43829
|
@ -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>
|
/// <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)
|
public bool IsLoaded(string assetName)
|
||||||
{
|
{
|
||||||
assetName = this.NormaliseAssetName(assetName);
|
lock (this.Cache)
|
||||||
return this.IsNormalisedKeyLoaded(assetName);
|
{
|
||||||
|
assetName = this.NormaliseAssetName(assetName);
|
||||||
|
return this.IsNormalisedKeyLoaded(assetName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Load an asset that has been processed by the content pipeline.</summary>
|
/// <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>
|
/// <param name="instance">The content manager instance for which to load the asset.</param>
|
||||||
public T LoadFor<T>(string assetName, ContentManager instance)
|
public T LoadFor<T>(string assetName, ContentManager instance)
|
||||||
{
|
{
|
||||||
assetName = this.NormaliseAssetName(assetName);
|
lock (this.Cache)
|
||||||
|
{
|
||||||
|
assetName = this.NormaliseAssetName(assetName);
|
||||||
|
|
||||||
// skip if already loaded
|
// skip if already loaded
|
||||||
if (this.IsNormalisedKeyLoaded(assetName))
|
if (this.IsNormalisedKeyLoaded(assetName))
|
||||||
{
|
|
||||||
this.TrackAssetLoader(assetName, instance);
|
|
||||||
return base.Load<T>(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, () =>
|
|
||||||
{
|
{
|
||||||
IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, typeof(T), this.NormaliseAssetName);
|
this.TrackAssetLoader(assetName, instance);
|
||||||
IAssetData asset = this.ApplyLoader<T>(info) ?? new AssetDataForObject(info, base.Load<T>(assetName), this.NormaliseAssetName);
|
return base.Load<T>(assetName);
|
||||||
asset = this.ApplyEditors<T>(info, asset);
|
}
|
||||||
return (T)asset.Data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// update cache & return data
|
// load asset
|
||||||
this.Cache[assetName] = data;
|
T data;
|
||||||
this.TrackAssetLoader(assetName, instance);
|
if (this.AssetsBeingLoaded.Contains(assetName))
|
||||||
return data;
|
{
|
||||||
|
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>
|
/// <summary>Inject an asset into the cache.</summary>
|
||||||
|
@ -186,9 +192,12 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <param name="value">The asset value.</param>
|
/// <param name="value">The asset value.</param>
|
||||||
public void Inject<T>(string assetName, T value)
|
public void Inject<T>(string assetName, T value)
|
||||||
{
|
{
|
||||||
assetName = this.NormaliseAssetName(assetName);
|
lock (this.Cache)
|
||||||
this.Cache[assetName] = value;
|
{
|
||||||
this.TrackAssetLoader(assetName, this);
|
assetName = this.NormaliseAssetName(assetName);
|
||||||
|
this.Cache[assetName] = value;
|
||||||
|
this.TrackAssetLoader(assetName, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the current content locale.</summary>
|
/// <summary>Get the current content locale.</summary>
|
||||||
|
@ -200,16 +209,19 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>Get the cached asset keys.</summary>
|
/// <summary>Get the cached asset keys.</summary>
|
||||||
public IEnumerable<string> GetAssetKeys()
|
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 _);
|
foreach (string cacheKey in this.Cache.Keys)
|
||||||
yield return assetKey;
|
{
|
||||||
|
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>
|
/// <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>
|
/// <returns>Returns whether any cache entries were invalidated.</returns>
|
||||||
public bool InvalidateCache(Func<string, Type, bool> predicate, bool dispose = false)
|
public bool InvalidateCache(Func<string, Type, bool> predicate, bool dispose = false)
|
||||||
{
|
{
|
||||||
// find matching asset keys
|
lock (this.Cache)
|
||||||
HashSet<string> purgeCacheKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
|
||||||
HashSet<string> purgeAssetKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
|
||||||
foreach (string cacheKey in this.Cache.Keys)
|
|
||||||
{
|
{
|
||||||
this.ParseCacheKey(cacheKey, out string assetKey, out _);
|
// find matching asset keys
|
||||||
Type type = this.Cache[cacheKey].GetType();
|
HashSet<string> purgeCacheKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
||||||
if (predicate(assetKey, type))
|
HashSet<string> purgeAssetKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
|
||||||
|
foreach (string cacheKey in this.Cache.Keys)
|
||||||
{
|
{
|
||||||
purgeAssetKeys.Add(assetKey);
|
this.ParseCacheKey(cacheKey, out string assetKey, out _);
|
||||||
purgeCacheKeys.Add(cacheKey);
|
Type type = this.Cache[cacheKey].GetType();
|
||||||
|
if (predicate(assetKey, type))
|
||||||
|
{
|
||||||
|
purgeAssetKeys.Add(assetKey);
|
||||||
|
purgeCacheKeys.Add(cacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// purge assets
|
// purge assets
|
||||||
foreach (string key in purgeCacheKeys)
|
foreach (string key in purgeCacheKeys)
|
||||||
{
|
{
|
||||||
if (dispose && this.Cache[key] is IDisposable disposable)
|
if (dispose && this.Cache[key] is IDisposable disposable)
|
||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
this.Cache.Remove(key);
|
this.Cache.Remove(key);
|
||||||
this.AssetLoaders.Remove(key);
|
this.AssetLoaders.Remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload core game assets
|
// reload core game assets
|
||||||
int reloaded = 0;
|
int reloaded = 0;
|
||||||
foreach (string key in purgeAssetKeys)
|
foreach (string key in purgeAssetKeys)
|
||||||
{
|
{
|
||||||
if (this.CoreAssets.ReloadForKey(this, key))
|
if (this.CoreAssets.ReloadForKey(this, key))
|
||||||
reloaded++;
|
reloaded++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// report result
|
// report result
|
||||||
if (purgeCacheKeys.Any())
|
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);
|
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;
|
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>
|
/// <summary>Dispose assets for the given content manager shim.</summary>
|
||||||
|
|
Loading…
Reference in New Issue