let mods invalidate assets matching a predicate (#363)

This commit is contained in:
Jesse Plamondon-Willard 2017-10-30 00:02:20 -04:00
parent 6b5c03da4d
commit 08c30eeffd
4 changed files with 41 additions and 14 deletions

View File

@ -1,15 +1,16 @@
# Release notes
## 2.1 (upcoming)
* For players:
* Fixed compatibility check crashing for players with Stardew Valley 1.08.
* Fixed the game's test messages being shown in the console and log.
* Fixed TrainerMod's `player_setlevel` command not also setting XP.
* Renamed the default _TrainerMod_ mod to _Console Commands_ to clarify its purpose.
* Added a log parser service at [log.smapi.io](https://log.smapi.io).
* Added better Steam instructions to the SMAPI installer.
* Renamed the default _TrainerMod_ mod to _Console Commands_ to clarify its purpose.
* Hid the game's test messages from the console log.
* Fixed compatibility check crashing for players with Stardew Valley 1.08.
* Fixed TrainerMod's `player_setlevel` command not also setting XP.
* For modders:
* Added support for public code in reflection API, to simplify mod integrations.
* The reflection API now works with public code to simplify mod integrations.
* The content API now lets you invalidated multiple assets at once.
* Improved input events:
* Added `e.IsActionButton` and `e.IsUseToolButton`.
* Added `ToSButton()` extension for the game's `Game1.options` button type.

View File

@ -163,9 +163,9 @@ namespace StardewModdingAPI.Framework.ModHelpers
/// <returns>Returns whether the given asset key was cached.</returns>
public bool InvalidateCache(string key)
{
this.Monitor.Log($"Requested cache invalidation for '{key}'.", LogLevel.Trace);
string actualKey = this.GetActualAssetKey(key, ContentSource.GameContent);
return this.ContentManager.InvalidateCache((otherKey, type) => otherKey.Equals(actualKey, StringComparison.InvariantCultureIgnoreCase));
this.Monitor.Log($"Requested cache invalidation for '{actualKey}'.", LogLevel.Trace);
return this.ContentManager.InvalidateCache(asset => asset.AssetNameEquals(actualKey));
}
/// <summary>Remove all assets of the given type from the cache so they're reloaded on the next request. <b>This can be a very expensive operation and should only be used in very specific cases.</b> This will reload core game assets if needed, but references to the former assets will still show the previous content.</summary>
@ -177,6 +177,15 @@ namespace StardewModdingAPI.Framework.ModHelpers
return this.ContentManager.InvalidateCache((key, type) => typeof(T).IsAssignableFrom(type));
}
/// <summary>Remove matching assets from the content cache so they're reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content.</summary>
/// <param name="predicate">A predicate matching the assets to invalidate.</param>
/// <returns>Returns whether any cache entries were invalidated.</returns>
public bool InvalidateCache(Func<IAssetInfo, bool> predicate)
{
this.Monitor.Log("Requested cache invalidation for all assets matching a predicate.", LogLevel.Trace);
return this.ContentManager.InvalidateCache(predicate);
}
/*********
** Private methods
*********/

View File

@ -287,18 +287,30 @@ namespace StardewModdingAPI.Framework
throw new InvalidOperationException("SMAPI could not access the interceptor methods."); // should never happen
// invalidate matching keys
return this.InvalidateCache((assetName, assetType) =>
return this.InvalidateCache(asset =>
{
IAssetInfo info = new AssetInfo(this.GetLocale(), assetName, assetType, this.NormaliseAssetName);
// check loaders
MethodInfo canLoadGeneric = canLoad.MakeGenericMethod(assetType);
if (loaders.Any(loader => (bool)canLoadGeneric.Invoke(loader, new object[] { info })))
MethodInfo canLoadGeneric = canLoad.MakeGenericMethod(asset.DataType);
if (loaders.Any(loader => (bool)canLoadGeneric.Invoke(loader, new object[] { asset })))
return true;
// check editors
MethodInfo canEditGeneric = canEdit.MakeGenericMethod(assetType);
return editors.Any(editor => (bool)canEditGeneric.Invoke(editor, new object[] { info }));
MethodInfo canEditGeneric = canEdit.MakeGenericMethod(asset.DataType);
return editors.Any(editor => (bool)canEditGeneric.Invoke(editor, new object[] { asset }));
});
}
/// <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 whether any cache entries were invalidated.</returns>
public bool InvalidateCache(Func<IAssetInfo, bool> predicate, bool dispose = false)
{
string locale = this.GetLocale();
return this.InvalidateCache((assetName, type) =>
{
IAssetInfo info = new AssetInfo(locale, assetName, type, this.NormaliseAssetName);
return predicate(info);
});
}

View File

@ -53,5 +53,10 @@ namespace StardewModdingAPI
/// <typeparam name="T">The asset type to remove from the cache.</typeparam>
/// <returns>Returns whether any assets were invalidated.</returns>
bool InvalidateCache<T>();
/// <summary>Remove matching assets from the content cache so they're reloaded on the next request. This will reload core game assets if needed, but references to the former asset will still show the previous content.</summary>
/// <param name="predicate">A predicate matching the assets to invalidate.</param>
/// <returns>Returns whether any cache entries were invalidated.</returns>
bool InvalidateCache(Func<IAssetInfo, bool> predicate);
}
}