From e1fc566e0afeb6eb92418bb039365611abd33829 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 25 Mar 2022 21:46:37 -0400 Subject: add content pack labels (#766) --- src/SMAPI/Framework/Content/AssetEditOperation.cs | 7 ++++- src/SMAPI/Framework/Content/AssetLoadOperation.cs | 9 ++++-- src/SMAPI/Framework/ContentCoordinator.cs | 12 ++++++-- .../ContentManagers/GameContentManager.cs | 26 ++++++++++++------ src/SMAPI/Framework/InternalExtensions.cs | 9 ++++++ src/SMAPI/Framework/SCore.cs | 32 +++++++++++++++++++++- 6 files changed, 81 insertions(+), 14 deletions(-) (limited to 'src/SMAPI/Framework') 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 /// The mod applying the edit. public IModMetadata Mod { get; } + /// The content pack on whose behalf the edit is being applied, if any. + public IModMetadata OnBehalfOf { get; } + /// Apply the edit to an asset. public Action ApplyEdit { get; } @@ -20,10 +23,12 @@ namespace StardewModdingAPI.Framework.Content *********/ /// Construct an instance. /// The mod applying the edit. + /// The content pack on whose behalf the edit is being applied, if any. /// Apply the edit to an asset. - public AssetEditOperation(IModMetadata mod, Action applyEdit) + public AssetEditOperation(IModMetadata mod, IModMetadata onBehalfOf, Action 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 *********/ - /// The mod applying the edit. + /// The mod loading the asset. public IModMetadata Mod { get; } + /// The content pack on whose behalf the asset is being loaded, if any. + public IModMetadata OnBehalfOf { get; } + /// Load the initial value for an asset. public Func GetData { get; } @@ -20,10 +23,12 @@ namespace StardewModdingAPI.Framework.Content *********/ /// Construct an instance. /// The mod applying the edit. + /// The content pack on whose behalf the asset is being loaded, if any. /// Load the initial value for an asset. - public AssetLoadOperation(IModMetadata mod, Func getData) + public AssetLoadOperation(IModMetadata mod, IModMetadata onBehalfOf, Func 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(assetInfo)) + new AssetLoadOperation( + mod: loader.Mod, + onBehalfOf: null, + getData: assetInfo => loader.Data.Load(assetInfo) + ) }, editOperations: Array.Empty() ); @@ -633,7 +637,11 @@ namespace StardewModdingAPI.Framework loadOperations: Array.Empty(), editOperations: new[] { - new AssetEditOperation(editor.Mod, assetData => editor.Data.Edit(assetData)) + new AssetEditOperation( + mod: editor.Mod, + onBehalfOf: null, + applyEdit: assetData => editor.Data.Edit(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 } } - /// Load the initial asset from the registered . + /// Load the initial asset from the registered loaders. /// The basic asset metadata. /// Returns the loaded asset metadata, or null if no loader matched. private IAssetData ApplyLoader(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; } - /// Apply any to a loaded asset. + /// Apply any editors to a loaded asset. /// The asset type. /// The basic asset metadata. /// The loaded asset. @@ -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; } + /// Get a parenthetical label for log messages for the content pack on whose behalf the action is being performed, if any. + /// The content pack on whose behalf the action is being performed. + private string GetOnBehalfOfLabel(IModMetadata onBehalfOf) + { + if (onBehalfOf == null) + return string.Empty; + + return $" (for the '{onBehalfOf.Manifest.Name}' content pack)"; + } + /// Validate that an asset loaded by a mod is valid and won't cause issues, and fix issues if possible. /// The asset type. /// The basic asset metadata. 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); } + /// Log a message using the mod's monitor, but only if it hasn't already been logged since the last game launch. + /// The mod whose monitor to use. + /// The message to log. + /// The log severity level. + 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; } + /// Get the mod metadata for a content pack whose ID matches , if it's a valid content pack for the given . + /// The mod requesting to act on the content pack's behalf. + /// The content pack ID. + /// The verb phrase indicating what action will be performed, like 'load assets' or 'edit assets'. + /// Returns the content pack metadata if valid, else null. + 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; + } + /// Raised immediately before the player returns to the title screen. private void OnReturningToTitle() { -- cgit