diff options
-rw-r--r-- | docs/release-notes.md | 1 | ||||
-rw-r--r-- | src/SMAPI/Framework/IModMetadata.cs | 6 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModLoading/ModMetadata.cs | 11 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 133 |
4 files changed, 102 insertions, 49 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index 8037c10c..ee8bd468 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,6 +4,7 @@ ## Upcoming released * For modders: * Added `Multiplayer.PeerConnected` event. + * Simplified paranoid warnings in the log and reduced their log level. * Fixed asset propagation for Gil's portraits. ## 3.5 diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs index 37927482..1231b494 100644 --- a/src/SMAPI/Framework/IModMetadata.cs +++ b/src/SMAPI/Framework/IModMetadata.cs @@ -112,9 +112,9 @@ namespace StardewModdingAPI.Framework /// <summary>Whether the mod has at least one valid update key set.</summary> bool HasValidUpdateKeys(); - /// <summary>Get whether the mod has a given warning and it hasn't been suppressed in the <see cref="DataRecord"/>.</summary> - /// <param name="warning">The warning to check.</param> - bool HasUnsuppressWarning(ModWarning warning); + /// <summary>Get whether the mod has any of the given warnings which haven't been suppressed in the <see cref="DataRecord"/>.</summary> + /// <param name="warnings">The warnings to check.</param> + bool HasUnsuppressedWarnings(params ModWarning[] warnings); /// <summary>Get a relative path which includes the root folder name.</summary> string GetRelativePathWithRoot(); diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index 0e90362e..30701552 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -215,13 +215,14 @@ namespace StardewModdingAPI.Framework.ModLoading return this.GetUpdateKeys(validOnly: true).Any(); } - /// <summary>Get whether the mod has a given warning and it hasn't been suppressed in the <see cref="DataRecord"/>.</summary> - /// <param name="warning">The warning to check.</param> - public bool HasUnsuppressWarning(ModWarning warning) + /// <summary>Get whether the mod has any of the given warnings which haven't been suppressed in the <see cref="IModMetadata.DataRecord"/>.</summary> + /// <param name="warnings">The warnings to check.</param> + public bool HasUnsuppressedWarnings(params ModWarning[] warnings) { - return + return warnings.Any(warning => this.Warnings.HasFlag(warning) - && (this.DataRecord?.DataRecord == null || !this.DataRecord.DataRecord.SuppressWarnings.HasFlag(warning)); + && (this.DataRecord?.DataRecord == null || !this.DataRecord.DataRecord.SuppressWarnings.HasFlag(warning)) + ); } /// <summary>Get a relative path which includes the root folder name.</summary> diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index de9c955d..8c9424c1 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1129,67 +1129,118 @@ namespace StardewModdingAPI.Framework // log warnings if (modsWithWarnings.Any()) { - // issue block format logic - void LogWarningGroup(ModWarning warning, LogLevel logLevel, string heading, params string[] blurb) - { - IModMetadata[] matches = modsWithWarnings - .Where(mod => mod.HasUnsuppressWarning(warning)) - .ToArray(); - if (!matches.Any()) - return; - - this.Monitor.Log(" " + heading, logLevel); - this.Monitor.Log(" " + "".PadRight(50, '-'), logLevel); - foreach (string line in blurb) - this.Monitor.Log(" " + line, logLevel); - this.Monitor.Newline(); - foreach (IModMetadata match in matches) - this.Monitor.Log($" - {match.DisplayName}", logLevel); - this.Monitor.Newline(); - } - - // supported issues - LogWarningGroup(ModWarning.BrokenCodeLoaded, LogLevel.Error, "Broken mods", + // broken code + this.LogModWarningGroup(modsWithWarnings, ModWarning.BrokenCodeLoaded, LogLevel.Error, "Broken mods", "These mods have broken code, but you configured SMAPI to load them anyway. This may cause bugs,", "errors, or crashes in-game." ); - LogWarningGroup(ModWarning.ChangesSaveSerializer, LogLevel.Warn, "Changed save serializer", + + // changes serializer + this.LogModWarningGroup(modsWithWarnings, ModWarning.ChangesSaveSerializer, LogLevel.Warn, "Changed save serializer", "These mods change the save serializer. They may corrupt your save files, or make them unusable if", "you uninstall these mods." ); - if (this.Settings.ParanoidWarnings) - { - LogWarningGroup(ModWarning.AccessesConsole, LogLevel.Warn, "Accesses the console directly", - "These mods directly access the SMAPI console, and you enabled paranoid warnings. (Note that this may be", - "legitimate and innocent usage; this warning is meaningless without further investigation.)" - ); - LogWarningGroup(ModWarning.AccessesFilesystem, LogLevel.Warn, "Accesses filesystem directly", - "These mods directly access the filesystem, and you enabled paranoid warnings. (Note that this may be", - "legitimate and innocent usage; this warning is meaningless without further investigation.)" - ); - LogWarningGroup(ModWarning.AccessesShell, LogLevel.Warn, "Accesses shell/process directly", - "These mods directly access the OS shell or processes, and you enabled paranoid warnings. (Note that", - "this may be legitimate and innocent usage; this warning is meaningless without further investigation.)" - ); - } - LogWarningGroup(ModWarning.PatchesGame, LogLevel.Info, "Patched game code", + + // patched game code + this.LogModWarningGroup(modsWithWarnings, ModWarning.PatchesGame, LogLevel.Info, "Patched game code", "These mods directly change the game code. They're more likely to cause errors or bugs in-game; if", "your game has issues, try removing these first. Otherwise you can ignore this warning." ); - LogWarningGroup(ModWarning.UsesUnvalidatedUpdateTick, LogLevel.Info, "Bypassed safety checks", + + // unvalidated update tick + this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesUnvalidatedUpdateTick, LogLevel.Info, "Bypassed safety checks", "These mods bypass SMAPI's normal safety checks, so they're more likely to cause errors or save", "corruption. If your game has issues, try removing these first." ); - LogWarningGroup(ModWarning.NoUpdateKeys, LogLevel.Debug, "No update keys", + + // paranoid warnings + if (this.Settings.ParanoidWarnings) + { + this.LogModWarningGroup( + modsWithWarnings, + match: mod => mod.HasUnsuppressedWarnings(ModWarning.AccessesConsole, ModWarning.AccessesFilesystem, ModWarning.AccessesShell), + level: LogLevel.Debug, + heading: "Direct system access", + blurb: new[] + { + "You enabled paranoid warnings and these mods directly access the filesystem, shells/processes, or", + "SMAPI console. (This is usually legitimate and innocent usage; this warning is only useful for", + "further investigation.)" + }, + modLabel: mod => + { + List<string> labels = new List<string>(); + if (mod.HasUnsuppressedWarnings(ModWarning.AccessesConsole)) + labels.Add("console"); + if (mod.HasUnsuppressedWarnings(ModWarning.AccessesFilesystem)) + labels.Add("files"); + if (mod.HasUnsuppressedWarnings(ModWarning.AccessesShell)) + labels.Add("shells/processes"); + + return $"{mod.DisplayName} ({string.Join(", ", labels)})"; + } + ); + } + + // no update keys + this.LogModWarningGroup(modsWithWarnings, ModWarning.NoUpdateKeys, LogLevel.Debug, "No update keys", "These mods have no update keys in their manifest. SMAPI may not notify you about updates for these", "mods. Consider notifying the mod authors about this problem." ); - LogWarningGroup(ModWarning.UsesDynamic, LogLevel.Debug, "Not crossplatform", + + // not crossplatform + this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesDynamic, LogLevel.Debug, "Not crossplatform", "These mods use the 'dynamic' keyword, and won't work on Linux/Mac." ); } } + /// <summary>Write a mod warning group to the console and log.</summary> + /// <param name="mods">The mods to search.</param> + /// <param name="match">Matches mods to include in the warning group.</param> + /// <param name="level">The log level for the logged messages.</param> + /// <param name="heading">A brief heading label for the group.</param> + /// <param name="blurb">A detailed explanation of the warning, split into lines.</param> + /// <param name="modLabel">Formats the mod label, or <c>null</c> to use the <see cref="IModMetadata.DisplayName"/>.</param> + private void LogModWarningGroup(IModMetadata[] mods, Func<IModMetadata, bool> match, LogLevel level, string heading, string[] blurb, Func<IModMetadata, string> modLabel = null) + { + // get matching mods + IModMetadata[] matches = mods + .Where(match) + .ToArray(); + if (!matches.Any()) + return; + + // log header/blurb + this.Monitor.Log(" " + heading, level); + this.Monitor.Log(" " + "".PadRight(50, '-'), level); + foreach (string line in blurb) + this.Monitor.Log(" " + line, level); + this.Monitor.Newline(); + + // log mod list + foreach (IModMetadata modMatch in matches) + { + string label = modLabel != null + ? modLabel(modMatch) + : modMatch.DisplayName; + this.Monitor.Log($" - {label}", level); + } + + this.Monitor.Newline(); + } + + /// <summary>Write a mod warning group to the console and log.</summary> + /// <param name="mods">The mods to search.</param> + /// <param name="warning">The mod warning to match.</param> + /// <param name="level">The log level for the logged messages.</param> + /// <param name="heading">A brief heading label for the group.</param> + /// <param name="blurb">A detailed explanation of the warning, split into lines.</param> + void LogModWarningGroup(IModMetadata[] mods, ModWarning warning, LogLevel level, string heading, params string[] blurb) + { + this.LogModWarningGroup(mods, mod => mod.HasUnsuppressedWarnings(warning), level, heading, blurb); + } + /// <summary>Load a mod's entry class.</summary> /// <param name="modAssembly">The mod assembly.</param> /// <param name="mod">The loaded instance.</param> |