diff options
-rw-r--r-- | src/SMAPI/Events/AssetReadyEventArgs.cs | 25 | ||||
-rw-r--r-- | src/SMAPI/Events/IContentEvents.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentCoordinator.cs | 10 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/GameContentManager.cs | 12 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs | 4 | ||||
-rw-r--r-- | src/SMAPI/Framework/Events/EventManager.cs | 6 | ||||
-rw-r--r-- | src/SMAPI/Framework/Events/ModContentEvents.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 11 |
8 files changed, 73 insertions, 9 deletions
diff --git a/src/SMAPI/Events/AssetReadyEventArgs.cs b/src/SMAPI/Events/AssetReadyEventArgs.cs new file mode 100644 index 00000000..946c9173 --- /dev/null +++ b/src/SMAPI/Events/AssetReadyEventArgs.cs @@ -0,0 +1,25 @@ +using System; + +namespace StardewModdingAPI.Events +{ + /// <summary>Event arguments for an <see cref="IContentEvents.AssetReady"/> event.</summary> + public class AssetReadyEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// <summary>The name of the asset being requested.</summary> + public IAssetName Name { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="name">The name of the asset being requested.</param> + internal AssetReadyEventArgs(IAssetName name) + { + this.Name = name; + } + } +} diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs index ede9ea23..abbaaf33 100644 --- a/src/SMAPI/Events/IContentEvents.cs +++ b/src/SMAPI/Events/IContentEvents.cs @@ -1,5 +1,4 @@ using System; -using StardewValley; namespace StardewModdingAPI.Events { @@ -8,7 +7,7 @@ namespace StardewModdingAPI.Events { /// <summary>Raised when an asset is being requested from the content pipeline.</summary> /// <remarks> - /// The asset isn't necessarily being loaded yet (e.g. the game may be checking if it exists). Mods can register the changes they want to apply using methods on the <paramref name="e"/> parameter. These will be applied when the asset is actually loaded. + /// The asset isn't necessarily being loaded yet (e.g. the game may be checking if it exists). Mods can register the changes they want to apply using methods on the event arguments. These will be applied when the asset is actually loaded. /// /// 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> @@ -16,5 +15,9 @@ namespace StardewModdingAPI.Events /// <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; + + /// <summary>Raised after an asset is loaded by the content pipeline, after all mod edits specified via <see cref="AssetRequested"/> have been applied.</summary> + /// <remarks>This event is only raised if something requested the asset from the content pipeline. Invalidating an asset from the content cache won't necessarily reload it automatically.</remarks> + event EventHandler<AssetReadyEventArgs> AssetReady; } } diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 4f696928..4dbbae15 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -49,6 +49,9 @@ 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 an asset is fully loaded.</summary> + private readonly Action<BaseContentManager, IAssetName> OnAssetLoaded; + /// <summary>A callback to invoke when any asset names have been invalidated from the cache.</summary> private readonly Action<IEnumerable<IAssetName>> OnAssetsInvalidated; @@ -111,16 +114,18 @@ namespace StardewModdingAPI.Framework /// <param name="reflection">Simplifies access to private code.</param> /// <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="onAssetLoaded">A callback to invoke when an asset is fully loaded.</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, Action<IEnumerable<IAssetName>> onAssetsInvalidated, Func<IAssetInfo, IList<AssetOperationGroup>> requestAssetOperations) + public ContentCoordinator(IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, IMonitor monitor, Reflector reflection, JsonHelper jsonHelper, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, 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.OnAssetLoaded = onAssetLoaded; this.OnAssetsInvalidated = onAssetsInvalidated; this.RequestAssetOperations = requestAssetOperations; this.FullRootDirectory = Path.Combine(Constants.GamePath, rootDirectory); @@ -135,6 +140,7 @@ namespace StardewModdingAPI.Framework reflection: reflection, onDisposing: this.OnDisposing, onLoadingFirstAsset: onLoadingFirstAsset, + onAssetLoaded: onAssetLoaded, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations ) ); @@ -148,6 +154,7 @@ namespace StardewModdingAPI.Framework reflection: reflection, onDisposing: this.OnDisposing, onLoadingFirstAsset: onLoadingFirstAsset, + onAssetLoaded: onAssetLoaded, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations ); this.ContentManagers.Add(contentManagerForAssetPropagation); @@ -172,6 +179,7 @@ namespace StardewModdingAPI.Framework reflection: this.Reflection, onDisposing: this.OnDisposing, onLoadingFirstAsset: this.OnLoadingFirstAsset, + onAssetLoaded: this.OnAssetLoaded, aggressiveMemoryOptimizations: this.AggressiveMemoryOptimizations ); this.ContentManagers.Add(manager); diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index 642e526c..12ed5506 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -35,6 +35,9 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <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 an asset is fully loaded.</summary> + private readonly Action<BaseContentManager, IAssetName> OnAssetLoaded; + /********* ** Public methods @@ -49,11 +52,13 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <param name="reflection">Simplifies access to private code.</param> /// <param name="onDisposing">A callback to invoke when the content manager is being disposed.</param> /// <param name="onLoadingFirstAsset">A callback to invoke the first time *any* game content manager loads an asset.</param> + /// <param name="onAssetLoaded">A callback to invoke when an asset is fully loaded.</param> /// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param> - public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations) + public GameContentManager(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, bool aggressiveMemoryOptimizations) : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, isNamespaced: false, aggressiveMemoryOptimizations: aggressiveMemoryOptimizations) { this.OnLoadingFirstAsset = onLoadingFirstAsset; + this.OnAssetLoaded = onAssetLoaded; } /// <inheritdoc /> @@ -129,8 +134,11 @@ namespace StardewModdingAPI.Framework.ContentManagers }); } - // update cache & return data + // update cache this.TrackAsset(assetName, data, language, useCache); + + // raise event & return data + this.OnAssetLoaded(this, assetName); return data; } diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs index 206ece30..847e2ce1 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManagerForAssetPropagation.cs @@ -21,8 +21,8 @@ namespace StardewModdingAPI.Framework.ContentManagers ** Public methods *********/ /// <inheritdoc /> - public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, bool aggressiveMemoryOptimizations) - : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, aggressiveMemoryOptimizations) { } + public GameContentManagerForAssetPropagation(string name, IServiceProvider serviceProvider, string rootDirectory, CultureInfo currentCulture, ContentCoordinator coordinator, IMonitor monitor, Reflector reflection, Action<BaseContentManager> onDisposing, Action onLoadingFirstAsset, Action<BaseContentManager, IAssetName> onAssetLoaded, bool aggressiveMemoryOptimizations) + : base(name, serviceProvider, rootDirectory, currentCulture, coordinator, monitor, reflection, onDisposing, onLoadingFirstAsset, onAssetLoaded, aggressiveMemoryOptimizations) { } /// <inheritdoc /> public override T Load<T>(IAssetName assetName, LanguageCode language, bool useCache) diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index 96582380..bcfd7dd7 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework.Events { /// <summary>Manages SMAPI events.</summary> - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Private fields are deliberately named to simplify organisation.")] internal class EventManager { /********* @@ -19,6 +17,9 @@ namespace StardewModdingAPI.Framework.Events /// <inheritdoc cref="IContentEvents.AssetsInvalidated" /> public readonly ManagedEvent<AssetsInvalidatedEventArgs> AssetsInvalidated; + /// <inheritdoc cref="IContentEvents.AssetReady" /> + public readonly ManagedEvent<AssetReadyEventArgs> AssetReady; + /**** ** Display @@ -202,6 +203,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.AssetReady = ManageEventOf<AssetReadyEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetReady)); 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 4d0cfb97..cb242e99 100644 --- a/src/SMAPI/Framework/Events/ModContentEvents.cs +++ b/src/SMAPI/Framework/Events/ModContentEvents.cs @@ -23,6 +23,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.AssetsInvalidated.Remove(value); } + /// <inheritdoc /> + public event EventHandler<AssetReadyEventArgs> AssetReady + { + add => this.EventManager.AssetReady.Add(value, this.Mod); + remove => this.EventManager.AssetReady.Remove(value); + } + /********* ** Public methods diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index e9bc9a2b..9d97ec7d 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -19,6 +19,7 @@ using Newtonsoft.Json; using StardewModdingAPI.Enums; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Content; +using StardewModdingAPI.Framework.ContentManagers; using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Input; @@ -1106,6 +1107,15 @@ namespace StardewModdingAPI.Framework this.EventManager.DayEnding.RaiseEmpty(); } + /// <summary>A callback invoked after an asset is fully loaded through a content manager.</summary> + /// <param name="contentManager">The content manager through which the asset was loaded.</param> + /// <param name="assetName">The asset name that was loaded.</param> + private void OnAssetLoaded(IContentManager contentManager, IAssetName assetName) + { + if (this.EventManager.AssetReady.HasListeners()) + this.EventManager.AssetReady.Raise(new AssetReadyEventArgs(assetName)); + } + /// <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) @@ -1183,6 +1193,7 @@ namespace StardewModdingAPI.Framework reflection: this.Reflection, jsonHelper: this.Toolkit.JsonHelper, onLoadingFirstAsset: this.InitializeBeforeFirstAssetLoaded, + onAssetLoaded: this.OnAssetLoaded, onAssetsInvalidated: this.OnAssetsInvalidated, aggressiveMemoryOptimizations: this.Settings.AggressiveMemoryOptimizations, requestAssetOperations: this.RequestAssetOperations |