diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-05-11 21:36:45 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-05-11 21:36:45 -0400 |
commit | bbe5983acdd082d2185a69e2ad37d659a298223d (patch) | |
tree | ad6fe68ebd0da25a832e9fad3832741dee50954f /src/SMAPI/Framework/ContentManagers/GameContentManager.cs | |
parent | 42a797a01240893e9a8e645253a269087b2d178d (diff) | |
download | SMAPI-bbe5983acdd082d2185a69e2ad37d659a298223d.tar.gz SMAPI-bbe5983acdd082d2185a69e2ad37d659a298223d.tar.bz2 SMAPI-bbe5983acdd082d2185a69e2ad37d659a298223d.zip |
rewrite asset operations to reduce allocations
• When raising AssetRequested, SMAPI now creates a single event args model and reuses it for each handler.
• There's now a single AssetOperationGroup per asset, which tracks the loaders/editors registered by every mod for that asset.
• The operation group's loader/editor lists are now used directly instead of querying them.
Diffstat (limited to 'src/SMAPI/Framework/ContentManagers/GameContentManager.cs')
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/GameContentManager.cs | 71 |
1 files changed, 28 insertions, 43 deletions
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index c53040e1..2aa50542 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -75,15 +75,19 @@ namespace StardewModdingAPI.Framework.ContentManagers // custom asset from a loader string locale = this.GetLocale(); IAssetInfo info = new AssetInfo(locale, assetName, typeof(T), this.AssertAndNormalizeAssetName); - AssetLoadOperation[] loaders = this.GetLoaders<object>(info).ToArray(); - - if (!this.AssertMaxOneRequiredLoader(info, loaders, out string? error)) + AssetOperationGroup? operations = this.Coordinator.GetAssetOperations<object>(info); + if (operations?.LoadOperations.Count > 0) { - this.Monitor.Log(error, LogLevel.Warn); - return false; + if (!this.AssertMaxOneRequiredLoader(info, operations.LoadOperations, out string? error)) + { + this.Monitor.Log(error, LogLevel.Warn); + return false; + } + + return true; } - return loaders.Any(); + return false; } /// <inheritdoc /> @@ -121,10 +125,11 @@ namespace StardewModdingAPI.Framework.ContentManagers data = this.AssetsBeingLoaded.Track(assetName.Name, () => { IAssetInfo info = new AssetInfo(assetName.LocaleCode, assetName, typeof(T), this.AssertAndNormalizeAssetName); + AssetOperationGroup? operations = this.Coordinator.GetAssetOperations<T>(info); IAssetData asset = - this.ApplyLoader<T>(info) + this.ApplyLoader<T>(info, operations?.LoadOperations) ?? new AssetDataForObject(info, this.RawLoad<T>(assetName, useCache), this.AssertAndNormalizeAssetName, this.Reflection); - asset = this.ApplyEditors<T>(info, asset); + asset = this.ApplyEditors<T>(info, asset, operations?.EditOperations); return (T)asset.Data; }); } @@ -149,25 +154,23 @@ namespace StardewModdingAPI.Framework.ContentManagers *********/ /// <summary>Load the initial asset from the registered loaders.</summary> /// <param name="info">The basic asset metadata.</param> + /// <param name="loadOperations">The load operations to apply to the asset.</param> /// <returns>Returns the loaded asset metadata, or <c>null</c> if no loader matched.</returns> - private IAssetData? ApplyLoader<T>(IAssetInfo info) + private IAssetData? ApplyLoader<T>(IAssetInfo info, List<AssetLoadOperation>? loadOperations) where T : notnull { // find matching loader - AssetLoadOperation? loader; + AssetLoadOperation? loader = null; + if (loadOperations?.Count > 0) { - AssetLoadOperation[] loaders = this.GetLoaders<T>(info).OrderByDescending(p => p.Priority).ToArray(); - - if (!this.AssertMaxOneRequiredLoader(info, loaders, out string? error)) + if (!this.AssertMaxOneRequiredLoader(info, loadOperations, out string? error)) { this.Monitor.Log(error, LogLevel.Warn); return null; } - loader = loaders.FirstOrDefault(); + loader = loadOperations.OrderByDescending(p => p.Priority).FirstOrDefault(); } - - // no loader found if (loader == null) return null; @@ -195,9 +198,13 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <typeparam name="T">The asset type.</typeparam> /// <param name="info">The basic asset metadata.</param> /// <param name="asset">The loaded asset.</param> - private IAssetData ApplyEditors<T>(IAssetInfo info, IAssetData asset) + /// <param name="editOperations">The edit operations to apply to the asset.</param> + private IAssetData ApplyEditors<T>(IAssetInfo info, IAssetData asset, List<AssetEditOperation>? editOperations) where T : notnull { + if (editOperations?.Count is not > 0) + return asset; + IAssetData GetNewData(object data) => new AssetDataForObject(info, data, this.AssertAndNormalizeAssetName, this.Reflection); // special case: if the asset was loaded with a more general type like 'object', call editors with the actual type instead. @@ -210,12 +217,12 @@ namespace StardewModdingAPI.Framework.ContentManagers return (IAssetData)this.GetType() .GetMethod(nameof(this.ApplyEditors), BindingFlags.NonPublic | BindingFlags.Instance)! .MakeGenericMethod(actualType) - .Invoke(this, new object[] { info, asset })!; + .Invoke(this, new object[] { info, asset, editOperations })!; } } // edit asset - AssetEditOperation[] editors = this.GetEditors<T>(info).OrderBy(p => p.Priority).ToArray(); + AssetEditOperation[] editors = editOperations.OrderBy(p => p.Priority).ToArray(); foreach (AssetEditOperation editor in editors) { IModMetadata mod = editor.Mod; @@ -250,34 +257,12 @@ namespace StardewModdingAPI.Framework.ContentManagers return asset; } - /// <summary>Get the asset loaders which handle an asset.</summary> - /// <typeparam name="T">The asset type.</typeparam> - /// <param name="info">The basic asset metadata.</param> - private IEnumerable<AssetLoadOperation> GetLoaders<T>(IAssetInfo info) - where T : notnull - { - return this.Coordinator - .GetAssetOperations<T>(info) - .SelectMany(p => p.LoadOperations); - } - - /// <summary>Get the asset editors to apply to an asset.</summary> - /// <typeparam name="T">The asset type.</typeparam> - /// <param name="info">The basic asset metadata.</param> - private IEnumerable<AssetEditOperation> GetEditors<T>(IAssetInfo info) - where T : notnull - { - return this.Coordinator - .GetAssetOperations<T>(info) - .SelectMany(p => p.EditOperations); - } - /// <summary>Assert that at most one loader will be applied to an asset.</summary> /// <param name="info">The basic asset metadata.</param> /// <param name="loaders">The asset loaders to apply.</param> /// <param name="error">The error message to show to the user, if the method returns false.</param> /// <returns>Returns true if only one loader will apply, else false.</returns> - private bool AssertMaxOneRequiredLoader(IAssetInfo info, AssetLoadOperation[] loaders, [NotNullWhen(false)] out string? error) + private bool AssertMaxOneRequiredLoader(IAssetInfo info, List<AssetLoadOperation> loaders, [NotNullWhen(false)] out string? error) { AssetLoadOperation[] required = loaders.Where(p => p.Priority == AssetLoadPriority.Exclusive).ToArray(); if (required.Length <= 1) @@ -295,7 +280,7 @@ namespace StardewModdingAPI.Framework.ContentManagers ? $"Multiple mods want to provide the '{info.Name}' asset: {string.Join(", ", loaderNames)}" : $"The '{loaderNames[0]}' mod wants to provide the '{info.Name}' asset multiple times"; - error = $"{errorPhrase}. An asset can't be loaded multiple times, so SMAPI will use the default asset instead. Uninstall one of the mods to fix this. (Message for modders: you should usually use {typeof(IAssetEditor)} instead to avoid conflicts.)"; + error = $"{errorPhrase}. An asset can't be loaded multiple times, so SMAPI will use the default asset instead. Uninstall one of the mods to fix this. (Message for modders: you should avoid {nameof(AssetLoadPriority)}.{nameof(AssetLoadPriority.Exclusive)} and {nameof(IAssetLoader)} if possible to avoid conflicts.)"; return false; } |