diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-03-25 21:46:37 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-03-25 21:46:37 -0400 |
commit | e1fc566e0afeb6eb92418bb039365611abd33829 (patch) | |
tree | b3716a570106473f5daf772b4d0d2b9aaebc5c76 /src/SMAPI/Framework | |
parent | b0011bf65c6ea7ba8d66a219501ac181cbd64c90 (diff) | |
download | SMAPI-e1fc566e0afeb6eb92418bb039365611abd33829.tar.gz SMAPI-e1fc566e0afeb6eb92418bb039365611abd33829.tar.bz2 SMAPI-e1fc566e0afeb6eb92418bb039365611abd33829.zip |
add content pack labels (#766)
Diffstat (limited to 'src/SMAPI/Framework')
-rw-r--r-- | src/SMAPI/Framework/Content/AssetEditOperation.cs | 7 | ||||
-rw-r--r-- | src/SMAPI/Framework/Content/AssetLoadOperation.cs | 9 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentCoordinator.cs | 12 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/GameContentManager.cs | 26 | ||||
-rw-r--r-- | src/SMAPI/Framework/InternalExtensions.cs | 9 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 32 |
6 files changed, 81 insertions, 14 deletions
diff --git a/src/SMAPI/Framework/Content/AssetEditOperation.cs b/src/SMAPI/Framework/Content/AssetEditOperation.cs index fa189d44..14db231c 100644 --- a/src/SMAPI/Framework/Content/AssetEditOperation.cs +++ b/src/SMAPI/Framework/Content/AssetEditOperation.cs @@ -11,6 +11,9 @@ namespace StardewModdingAPI.Framework.Content /// <summary>The mod applying the edit.</summary> public IModMetadata Mod { get; } + /// <summary>The content pack on whose behalf the edit is being applied, if any.</summary> + public IModMetadata OnBehalfOf { get; } + /// <summary>Apply the edit to an asset.</summary> public Action<IAssetData> ApplyEdit { get; } @@ -20,10 +23,12 @@ namespace StardewModdingAPI.Framework.Content *********/ /// <summary>Construct an instance.</summary> /// <param name="mod">The mod applying the edit.</param> + /// <param name="onBehalfOf">The content pack on whose behalf the edit is being applied, if any.</param> /// <param name="applyEdit">Apply the edit to an asset.</param> - public AssetEditOperation(IModMetadata mod, Action<IAssetData> applyEdit) + public AssetEditOperation(IModMetadata mod, IModMetadata onBehalfOf, Action<IAssetData> applyEdit) { this.Mod = mod; + this.OnBehalfOf = onBehalfOf; this.ApplyEdit = applyEdit; } } diff --git a/src/SMAPI/Framework/Content/AssetLoadOperation.cs b/src/SMAPI/Framework/Content/AssetLoadOperation.cs index d773cadd..29bf1518 100644 --- a/src/SMAPI/Framework/Content/AssetLoadOperation.cs +++ b/src/SMAPI/Framework/Content/AssetLoadOperation.cs @@ -8,9 +8,12 @@ namespace StardewModdingAPI.Framework.Content /********* ** Accessors *********/ - /// <summary>The mod applying the edit.</summary> + /// <summary>The mod loading the asset.</summary> public IModMetadata Mod { get; } + /// <summary>The content pack on whose behalf the asset is being loaded, if any.</summary> + public IModMetadata OnBehalfOf { get; } + /// <summary>Load the initial value for an asset.</summary> public Func<IAssetInfo, object> GetData { get; } @@ -20,10 +23,12 @@ namespace StardewModdingAPI.Framework.Content *********/ /// <summary>Construct an instance.</summary> /// <param name="mod">The mod applying the edit.</param> + /// <param name="onBehalfOf">The content pack on whose behalf the asset is being loaded, if any.</param> /// <param name="getData">Load the initial value for an asset.</param> - public AssetLoadOperation(IModMetadata mod, Func<IAssetInfo, object> getData) + public AssetLoadOperation(IModMetadata mod, IModMetadata onBehalfOf, Func<IAssetInfo, object> getData) { this.Mod = mod; + this.OnBehalfOf = onBehalfOf; this.GetData = getData; } } diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 4dbbae15..3b304f0d 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -606,7 +606,11 @@ namespace StardewModdingAPI.Framework mod: loader.Mod, loadOperations: new[] { - new AssetLoadOperation(loader.Mod, assetInfo => loader.Data.Load<T>(assetInfo)) + new AssetLoadOperation( + mod: loader.Mod, + onBehalfOf: null, + getData: assetInfo => loader.Data.Load<T>(assetInfo) + ) }, editOperations: Array.Empty<AssetEditOperation>() ); @@ -633,7 +637,11 @@ namespace StardewModdingAPI.Framework loadOperations: Array.Empty<AssetLoadOperation>(), editOperations: new[] { - new AssetEditOperation(editor.Mod, assetData => editor.Data.Edit<T>(assetData)) + new AssetEditOperation( + mod: editor.Mod, + onBehalfOf: null, + applyEdit: assetData => editor.Data.Edit<T>(assetData) + ) } ); } diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index 12ed5506..58e36128 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -267,7 +267,7 @@ namespace StardewModdingAPI.Framework.ContentManagers } } - /// <summary>Load the initial asset from the registered <see cref="Loaders"/>.</summary> + /// <summary>Load the initial asset from the registered loaders.</summary> /// <param name="info">The basic asset metadata.</param> /// <returns>Returns the loaded asset metadata, or <c>null</c> if no loader matched.</returns> private IAssetData ApplyLoader<T>(IAssetInfo info) @@ -296,11 +296,11 @@ namespace StardewModdingAPI.Framework.ContentManagers try { data = (T)loader.GetData(info); - this.Monitor.Log($"{mod.DisplayName} loaded asset '{info.Name}'."); + this.Monitor.Log($"{mod.DisplayName} loaded asset '{info.Name}'{this.GetOnBehalfOfLabel(loader.OnBehalfOf)}."); } catch (Exception ex) { - mod.LogAsMod($"Mod crashed when loading asset '{info.Name}'. SMAPI will use the default asset instead. Error details:\n{ex.GetLogSummary()}", LogLevel.Error); + mod.LogAsMod($"Mod crashed when loading asset '{info.Name}'{this.GetOnBehalfOfLabel(loader.OnBehalfOf)}. SMAPI will use the default asset instead. Error details:\n{ex.GetLogSummary()}", LogLevel.Error); return null; } @@ -310,7 +310,7 @@ namespace StardewModdingAPI.Framework.ContentManagers : null; } - /// <summary>Apply any <see cref="Editors"/> to a loaded asset.</summary> + /// <summary>Apply any editors to a loaded asset.</summary> /// <typeparam name="T">The asset type.</typeparam> /// <param name="info">The basic asset metadata.</param> /// <param name="asset">The loaded asset.</param> @@ -343,22 +343,22 @@ namespace StardewModdingAPI.Framework.ContentManagers try { editor.ApplyEdit(asset); - this.Monitor.Log($"{mod.DisplayName} edited {info.Name}."); + this.Monitor.Log($"{mod.DisplayName} edited {info.Name}{this.GetOnBehalfOfLabel(editor.OnBehalfOf)}."); } catch (Exception ex) { - mod.LogAsMod($"Mod crashed when editing asset '{info.Name}', which may cause errors in-game. Error details:\n{ex.GetLogSummary()}", LogLevel.Error); + mod.LogAsMod($"Mod crashed when editing asset '{info.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)}, which may cause errors in-game. Error details:\n{ex.GetLogSummary()}", LogLevel.Error); } // validate edit if (asset.Data == null) { - mod.LogAsMod($"Mod incorrectly set asset '{info.Name}' to a null value; ignoring override.", LogLevel.Warn); + mod.LogAsMod($"Mod incorrectly set asset '{info.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)} to a null value; ignoring override.", LogLevel.Warn); asset = GetNewData(prevAsset); } else if (!(asset.Data is T)) { - mod.LogAsMod($"Mod incorrectly set asset '{asset.Name}' to incompatible type '{asset.Data.GetType()}', expected '{typeof(T)}'; ignoring override.", LogLevel.Warn); + mod.LogAsMod($"Mod incorrectly set asset '{asset.Name}'{this.GetOnBehalfOfLabel(editor.OnBehalfOf)} to incompatible type '{asset.Data.GetType()}', expected '{typeof(T)}'; ignoring override.", LogLevel.Warn); asset = GetNewData(prevAsset); } } @@ -409,6 +409,16 @@ namespace StardewModdingAPI.Framework.ContentManagers return false; } + /// <summary>Get a parenthetical label for log messages for the content pack on whose behalf the action is being performed, if any.</summary> + /// <param name="onBehalfOf">The content pack on whose behalf the action is being performed.</param> + private string GetOnBehalfOfLabel(IModMetadata onBehalfOf) + { + if (onBehalfOf == null) + return string.Empty; + + return $" (for the '{onBehalfOf.Manifest.Name}' content pack)"; + } + /// <summary>Validate that an asset loaded by a mod is valid and won't cause issues, and fix issues if possible.</summary> /// <typeparam name="T">The asset type.</typeparam> /// <param name="info">The basic asset metadata.</param> diff --git a/src/SMAPI/Framework/InternalExtensions.cs b/src/SMAPI/Framework/InternalExtensions.cs index 4cb77a45..fe10b045 100644 --- a/src/SMAPI/Framework/InternalExtensions.cs +++ b/src/SMAPI/Framework/InternalExtensions.cs @@ -45,6 +45,15 @@ namespace StardewModdingAPI.Framework metadata.Monitor.Log(message, level); } + /// <summary>Log a message using the mod's monitor, but only if it hasn't already been logged since the last game launch.</summary> + /// <param name="metadata">The mod whose monitor to use.</param> + /// <param name="message">The message to log.</param> + /// <param name="level">The log severity level.</param> + public static void LogAsModOnce(this IModMetadata metadata, string message, LogLevel level = LogLevel.Trace) + { + metadata.Monitor.LogOnce(message, level); + } + /**** ** ManagedEvent ****/ diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 9d97ec7d..dd682e40 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1133,7 +1133,7 @@ namespace StardewModdingAPI.Framework this.EventManager.AssetRequested.Raise( invoke: (mod, invoke) => { - AssetRequestedEventArgs args = new(mod, asset.Name); + AssetRequestedEventArgs args = new(mod, asset.Name, this.GetOnBehalfOfContentPack); invoke(args); @@ -1149,6 +1149,36 @@ namespace StardewModdingAPI.Framework return operations; } + /// <summary>Get the mod metadata for a content pack whose ID matches <paramref name="id"/>, if it's a valid content pack for the given <paramref name="mod"/>.</summary> + /// <param name="mod">The mod requesting to act on the content pack's behalf.</param> + /// <param name="id">The content pack ID.</param> + /// <param name="verb">The verb phrase indicating what action will be performed, like 'load assets' or 'edit assets'.</param> + /// <returns>Returns the content pack metadata if valid, else <c>null</c>.</returns> + private IModMetadata GetOnBehalfOfContentPack(IModMetadata mod, string id, string verb) + { + if (id == null) + return null; + + string errorPrefix = $"Can't {verb} on behalf of content pack ID '{id}'"; + + // get target mod + IModMetadata onBehalfOf = this.ModRegistry.Get(id); + if (onBehalfOf == null) + { + mod.LogAsModOnce($"{errorPrefix}: there's no content pack installed with that ID.", LogLevel.Warn); + return null; + } + + // make sure it's a content pack for the requesting mod + if (!onBehalfOf.IsContentPack || !string.Equals(onBehalfOf.Manifest?.ContentPackFor?.UniqueID, mod.Manifest.UniqueID)) + { + mod.LogAsModOnce($"{errorPrefix}: that isn't a content pack for this mod.", LogLevel.Warn); + return null; + } + + return onBehalfOf; + } + /// <summary>Raised immediately before the player returns to the title screen.</summary> private void OnReturningToTitle() { |