summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-03-25 21:46:37 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-03-25 21:46:37 -0400
commite1fc566e0afeb6eb92418bb039365611abd33829 (patch)
treeb3716a570106473f5daf772b4d0d2b9aaebc5c76 /src/SMAPI/Framework
parentb0011bf65c6ea7ba8d66a219501ac181cbd64c90 (diff)
downloadSMAPI-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.cs7
-rw-r--r--src/SMAPI/Framework/Content/AssetLoadOperation.cs9
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs12
-rw-r--r--src/SMAPI/Framework/ContentManagers/GameContentManager.cs26
-rw-r--r--src/SMAPI/Framework/InternalExtensions.cs9
-rw-r--r--src/SMAPI/Framework/SCore.cs32
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()
{