diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/SMAPI/Events/AssetsInvalidatedEventArgs.cs | 27 | ||||
-rw-r--r-- | src/SMAPI/Events/IContentEvents.cs | 3 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentCoordinator.cs | 30 | ||||
-rw-r--r-- | src/SMAPI/Framework/Events/EventManager.cs | 4 | ||||
-rw-r--r-- | src/SMAPI/Framework/Events/ModContentEvents.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 9 |
6 files changed, 69 insertions, 11 deletions
diff --git a/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs new file mode 100644 index 00000000..0127f83a --- /dev/null +++ b/src/SMAPI/Events/AssetsInvalidatedEventArgs.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StardewModdingAPI.Events +{ + /// <summary>Event arguments for an <see cref="IContentEvents.AssetsInvalidated"/> event.</summary> + public class AssetsInvalidatedEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// <summary>The asset names that were invalidated.</summary> + public IEnumerable<IAssetName> Names { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="names">The asset names that were invalidated.</param> + internal AssetsInvalidatedEventArgs(IEnumerable<IAssetName> names) + { + this.Names = names.ToArray(); + } + } +} diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs index feaf9c0a..ede9ea23 100644 --- a/src/SMAPI/Events/IContentEvents.cs +++ b/src/SMAPI/Events/IContentEvents.cs @@ -13,5 +13,8 @@ namespace StardewModdingAPI.Events /// If the asset is requested multiple times in the same tick (e.g. once to check if it exists and once to load it), SMAPI might only raise the event once and reuse the cached result. /// </remarks> event EventHandler<AssetRequestedEventArgs> AssetRequested; + + /// <summary>Raised after one or more assets were invalidated from the content cache by a mod, so they'll be reloaded next time they're requested. If the assets will be reloaded or propagated automatically, this event is raised before that happens.</summary> + event EventHandler<AssetsInvalidatedEventArgs> AssetsInvalidated; } } diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 22ae0a18..4f696928 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -49,6 +49,12 @@ namespace StardewModdingAPI.Framework /// <summary>A callback to invoke the first time *any* game content manager loads an asset.</summary> private readonly Action OnLoadingFirstAsset; + /// <summary>A callback to invoke when any asset names have been invalidated from the cache.</summary> + private readonly Action<IEnumerable<IAssetName>> OnAssetsInvalidated; + + /// <summary>Get the load/edit operations to apply to an asset by querying registered <see cref="IContentEvents.AssetRequested"/> event handlers.</summary> + private readonly Func<IAssetInfo, IList<AssetOperationGroup>> RequestAssetOperations; + /// <summary>The loaded content managers (including the <see cref="MainContentManager"/>).</summary> private readonly List<IContentManager> ContentManagers = new(); @@ -71,9 +77,6 @@ namespace StardewModdingAPI.Framework /// <summary>The language enum values indexed by locale code.</summary> private Lazy<Dictionary<string, LocalizedContentManager.LanguageCode>> LocaleCodes; - /// <summary>Get the load/edit operations to apply to an asset by querying registered <see cref="IContentEvents.AssetRequested"/> event handlers.</summary> - private readonly Func<IAssetInfo, IList<AssetOperationGroup>> RequestAssetOperations; - /// <summary>The cached asset load/edit operations to apply, indexed by asset name.</summary> private readonly TickCacheDictionary<IAssetName, AssetOperationGroup[]> AssetOperationsByKey = new(); @@ -109,14 +112,16 @@ namespace StardewModdingAPI.Framework /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param> /// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param> /// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param> + /// <param name="onAssetsInvalidated">A callback to invoke when any asset names have been invalidated from the cache.</param> /// <param name="requestAssetOperations">Get the load/edit operations to apply to an asset by querying registered <see cref="IContentEvents.AssetRequested"/> event handlers.</param> - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Func<IAssetInfo, IList<AssetOperationGroup>> requestAssetOperations) + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Action<IEnumerable<IAssetName>> onAssetsInvalidated, Func<IAssetInfo, IList<AssetOperationGroup>> requestAssetOperations) { this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations; this.Monitor = monitor ?? throw new ArgumentNullException(nameof(monitor)); this.Reflection = reflection; this.JsonHelper = jsonHelper; this.OnLoadingFirstAsset = onLoadingFirstAsset; + this.OnAssetsInvalidated = onAssetsInvalidated; this.RequestAssetOperations = requestAssetOperations; this.FullRootDirectory = Path.Combine(Constants.GamePath, rootDirectory); this.ContentManagers.Add( @@ -257,7 +262,7 @@ namespace StardewModdingAPI.Framework // Note that we *must* propagate changes here, otherwise when mods invalidate the cache later to reapply // their changes, the assets won't be found in the cache so no changes will be propagated. if (LocalizedContentManager.CurrentLanguageCode != LocalizedContentManager.LanguageCode.en) - this.InvalidateCache((contentManager, key, type) => contentManager is GameContentManager); + this.InvalidateCache((contentManager, _, _) => contentManager is GameContentManager); } /// <summary>Parse a raw asset name.</summary> @@ -347,7 +352,7 @@ namespace StardewModdingAPI.Framework public IEnumerable<IAssetName> InvalidateCache(Func<IAssetInfo, bool> predicate, bool dispose = false) { string locale = this.GetLocale(); - return this.InvalidateCache((contentManager, rawName, type) => + return this.InvalidateCache((_, rawName, type) => { IAssetName assetName = this.ParseAssetName(rawName); IAssetInfo info = new AssetInfo(locale, assetName, type, this.MainContentManager.AssertAndNormalizeAssetName); @@ -393,13 +398,16 @@ namespace StardewModdingAPI.Framework } }); - // clear cached editor checks - foreach (IAssetName name in invalidatedAssets.Keys) - this.AssetOperationsByKey.Remove(name); - - // reload core game assets + // handle invalidation if (invalidatedAssets.Any()) { + // clear cached editor checks + foreach (IAssetName name in invalidatedAssets.Keys) + this.AssetOperationsByKey.Remove(name); + + // raise event + this.OnAssetsInvalidated(invalidatedAssets.Keys); + // propagate changes to the game this.CoreAssets.Propagate( assets: invalidatedAssets.ToDictionary(p => p.Key, p => p.Value), diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 8142f00e..96582380 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -16,6 +16,9 @@ namespace StardewModdingAPI.Framework.Events /// <inheritdoc cref="IContentEvents.AssetRequested" /> public readonly ManagedEvent<AssetRequestedEventArgs> AssetRequested; + /// <inheritdoc cref="IContentEvents.AssetsInvalidated" /> + public readonly ManagedEvent<AssetsInvalidatedEventArgs> AssetsInvalidated; + /**** ** Display @@ -198,6 +201,7 @@ namespace StardewModdingAPI.Framework.Events // init events this.AssetRequested = ManageEventOf<AssetRequestedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); + this.AssetsInvalidated = ManageEventOf<AssetsInvalidatedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated)); this.MenuChanged = ManageEventOf<MenuChangedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf<RenderingEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true); diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs index b4d4279c..4d0cfb97 100644 --- a/src/SMAPI/Framework/Events/ModContentEvents.cs +++ b/src/SMAPI/Framework/Events/ModContentEvents.cs @@ -16,6 +16,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.AssetRequested.Remove(value); } + /// <inheritdoc /> + public event EventHandler<AssetsInvalidatedEventArgs> AssetsInvalidated + { + add => this.EventManager.AssetsInvalidated.Add(value, this.Mod); + remove => this.EventManager.AssetsInvalidated.Remove(value); + } + /********* ** Public methods diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index f9f84206..e9bc9a2b 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1106,6 +1106,14 @@ namespace StardewModdingAPI.Framework this.EventManager.DayEnding.RaiseEmpty(); } + /// <summary>A callback invoked after assets have been invalidated from the content cache.</summary> + /// <param name="assetNames">The invalidated asset names.</param> + private void OnAssetsInvalidated(IEnumerable<IAssetName> assetNames) + { + if (this.EventManager.AssetsInvalidated.HasListeners()) + this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames)); + } + /// <summary>Get the load/edit operations to apply to an asset by querying registered <see cref="IContentEvents.AssetRequested"/> event handlers.</summary> /// <param name="asset">The asset info being requested.</param> private IList<AssetOperationGroup> RequestAssetOperations(IAssetInfo asset) @@ -1175,6 +1183,7 @@ namespace StardewModdingAPI.Framework reflection: this.Reflection, jsonHelper: this.Toolkit.JsonHelper, onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded, + onAssetsInvalidated: this.OnAssetsInvalidated, aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations, requestAssetOperations: this.RequestAssetOperations ); |