From 2b0ce2bb4d6690b7d00da0a243855db9bffffbf0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 24 Mar 2022 22:55:55 -0400 Subject: add AssetInvalidated content event (#766) --- src/SMAPI/Framework/ContentCoordinator.cs | 30 ++++++++++++++++---------- src/SMAPI/Framework/Events/EventManager.cs | 4 ++++ src/SMAPI/Framework/Events/ModContentEvents.cs | 7 ++++++ src/SMAPI/Framework/SCore.cs | 9 ++++++++ 4 files changed, 39 insertions(+), 11 deletions(-) (limited to 'src/SMAPI/Framework') 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 /// A callback to invoke the first time *any* game content manager loads an asset. private readonly Action OnLoadingFirstAsset; + /// A callback to invoke when any asset names have been invalidated from the cache. + private readonly Action> OnAssetsInvalidated; + + /// Get the load/edit operations to apply to an asset by querying registered event handlers. + private readonly Func> RequestAssetOperations; + /// The loaded content managers (including the ). private readonly List ContentManagers = new(); @@ -71,9 +77,6 @@ namespace StardewModdingAPI.Framework /// The language enum values indexed by locale code. private Lazy> LocaleCodes; - /// Get the load/edit operations to apply to an asset by querying registered event handlers. - private readonly Func> RequestAssetOperations; - /// The cached asset load/edit operations to apply, indexed by asset name. private readonly TickCacheDictionary AssetOperationsByKey = new(); @@ -109,14 +112,16 @@ namespace StardewModdingAPI.Framework /// Encapsulates SMAPI's JSON file parsing. /// A callback to invoke the first time *any* game content manager loads an asset. /// Whether to enable more aggressive memory optimizations. + /// A callback to invoke when any asset names have been invalidated from the cache. /// Get the load/edit operations to apply to an asset by querying registered event handlers. - public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Func> requestAssetOperations) + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations, Action> onAssetsInvalidated, Func> 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); } /// Parse a raw asset name. @@ -347,7 +352,7 @@ namespace StardewModdingAPI.Framework public IEnumerable InvalidateCache(Func 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 /// public readonly ManagedEvent AssetRequested; + /// + public readonly ManagedEvent AssetsInvalidated; + /**** ** Display @@ -198,6 +201,7 @@ namespace StardewModdingAPI.Framework.Events // init events this.AssetRequested = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested)); + this.AssetsInvalidated = ManageEventOf(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated)); this.MenuChanged = ManageEventOf(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged)); this.Rendering = ManageEventOf(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); } + /// + public event EventHandler 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(); } + /// A callback invoked after assets have been invalidated from the content cache. + /// The invalidated asset names. + private void OnAssetsInvalidated(IEnumerable assetNames) + { + if (this.EventManager.AssetsInvalidated.HasListeners()) + this.EventManager.AssetsInvalidated.Raise(new AssetsInvalidatedEventArgs(assetNames)); + } + /// Get the load/edit operations to apply to an asset by querying registered event handlers. /// The asset info being requested. private IList 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 ); -- cgit