diff --git a/docs/release-notes.md b/docs/release-notes.md index 46c0951d..3bc4f46e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -3,6 +3,8 @@ # Release notes ## Upcoming release * For players: + * Added option to disable Harmony/MonoMod fix for players with certain crashes. + _This should only be changed if no installed mods use Harmony._ * Fixed split-screen crash for non-English players when mods added translated variants of content assets. * SMAPI no longer rewrites mods which use Harmony 1.x, to help reduce Harmony crashes. _This should affect very few mods that still work otherwise, and any Harmony mod updated after July 2021 should be unaffected._ diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs index 4d17efe9..af59068d 100644 --- a/src/SMAPI/Framework/Logging/LogManager.cs +++ b/src/SMAPI/Framework/Logging/LogManager.cs @@ -261,7 +261,8 @@ namespace StardewModdingAPI.Framework.Logging /// The mods which could not be loaded. /// Whether to log issues for mods which directly use potentially sensitive .NET APIs like file or shell access. /// Whether to include more technical details about broken mods in the TRACE logs. This is mainly useful for creating compatibility rewriters. - public void LogModInfo(IModMetadata[] loaded, IModMetadata[] loadedContentPacks, IModMetadata[] loadedMods, IModMetadata[] skippedMods, bool logParanoidWarnings, bool logTechnicalDetailsForBrokenMods) + /// Whether Harmony was fixed to work with Stardew Valley. + public void LogModInfo(IModMetadata[] loaded, IModMetadata[] loadedContentPacks, IModMetadata[] loadedMods, IModMetadata[] skippedMods, bool logParanoidWarnings, bool logTechnicalDetailsForBrokenMods, bool hasHarmonyFix) { // log loaded mods this.Monitor.Log($"Loaded {loadedMods.Length} mods" + (loadedMods.Length > 0 ? ":" : "."), LogLevel.Info); @@ -300,7 +301,7 @@ namespace StardewModdingAPI.Framework.Logging } // log mod warnings - this.LogModWarnings(loaded, skippedMods, logParanoidWarnings, logTechnicalDetailsForBrokenMods); + this.LogModWarnings(loaded, skippedMods, logParanoidWarnings, logTechnicalDetailsForBrokenMods, hasHarmonyFix); } /// @@ -318,8 +319,9 @@ namespace StardewModdingAPI.Framework.Logging /// The mods which could not be loaded. /// Whether to log issues for mods which directly use potentially sensitive .NET APIs like file or shell access. /// Whether to include more technical details about broken mods in the TRACE logs. This is mainly useful for creating compatibility rewriters. + /// Whether Harmony was fixed to work with Stardew Valley. [SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "Manifests aren't guaranteed non-null at this point in the loading process.")] - private void LogModWarnings(IEnumerable mods, IModMetadata[] skippedMods, bool logParanoidWarnings, bool logTechnicalDetailsForBrokenMods) + private void LogModWarnings(IEnumerable mods, IModMetadata[] skippedMods, bool logParanoidWarnings, bool logTechnicalDetailsForBrokenMods, bool hasHarmonyFix) { // get mods with warnings IModMetadata[] modsWithWarnings = mods @@ -390,6 +392,15 @@ namespace StardewModdingAPI.Framework.Logging "errors, or crashes in-game." ); + // missing Harmony fix + if (!hasHarmonyFix) + { + this.LogModWarningGroup(modsWithWarnings, ModWarning.PatchesGame, LogLevel.Warn, "Patched game code without Harmony fix", + $"These mods directly change the game code using Harmony, but you disabled the {nameof(SConfig.FixHarmony)} option.", + "The game will probably crash soon." + ); + } + // 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", @@ -397,10 +408,13 @@ namespace StardewModdingAPI.Framework.Logging ); // 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." - ); + if (hasHarmonyFix) + { + 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." + ); + } // unvalidated update tick this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesUnvalidatedUpdateTick, LogLevel.Info, "Bypassed safety checks", diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs index 470e5501..40bdb130 100644 --- a/src/SMAPI/Framework/Models/SConfig.cs +++ b/src/SMAPI/Framework/Models/SConfig.cs @@ -24,6 +24,7 @@ namespace StardewModdingAPI.Framework.Models [nameof(LogNetworkTraffic)] = false, [nameof(LogTechnicalDetailsForBrokenMods)] = false, [nameof(RewriteMods)] = true, + [nameof(FixHarmony)] = true, [nameof(UseCaseInsensitivePaths)] = Constants.Platform is Platform.Android or Platform.Linux, [nameof(SuppressHarmonyDebugMode)] = true }; @@ -71,6 +72,9 @@ namespace StardewModdingAPI.Framework.Models /// Whether SMAPI should rewrite mods for compatibility. public bool RewriteMods { get; set; } + /// Whether to apply fixes to Harmony so it works with Stardew Valley. + public bool FixHarmony { get; set; } + /// Whether to make SMAPI file APIs case-insensitive, even on Linux. public bool UseCaseInsensitivePaths { get; set; } @@ -109,6 +113,7 @@ namespace StardewModdingAPI.Framework.Models /// /// /// + /// /// /// /// @@ -117,7 +122,7 @@ namespace StardewModdingAPI.Framework.Models /// /// /// - public SConfig(bool developerMode, bool? checkForUpdates, bool? listenForConsoleInput, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, bool? logTechnicalDetailsForBrokenMods, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadEarly, string[]? modsToLoadLate) + public SConfig(bool developerMode, bool? checkForUpdates, bool? listenForConsoleInput, bool? paranoidWarnings, bool? useBetaChannel, string gitHubProjectName, string webApiBaseUrl, string[]? verboseLogging, bool? rewriteMods, bool? fixHarmony, bool? useCaseInsensitivePaths, bool? logNetworkTraffic, bool? logTechnicalDetailsForBrokenMods, ColorSchemeConfig consoleColors, bool? suppressHarmonyDebugMode, string[]? suppressUpdateChecks, string[]? modsToLoadEarly, string[]? modsToLoadLate) { this.DeveloperMode = developerMode; this.CheckForUpdates = checkForUpdates ?? (bool)SConfig.DefaultValues[nameof(this.CheckForUpdates)]; @@ -128,6 +133,7 @@ namespace StardewModdingAPI.Framework.Models this.WebApiBaseUrl = webApiBaseUrl; this.VerboseLogging = new HashSet(verboseLogging ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); this.RewriteMods = rewriteMods ?? (bool)SConfig.DefaultValues[nameof(this.RewriteMods)]; + this.FixHarmony = fixHarmony ?? (bool)SConfig.DefaultValues[nameof(this.FixHarmony)]; this.UseCaseInsensitivePaths = useCaseInsensitivePaths ?? (bool)SConfig.DefaultValues[nameof(this.UseCaseInsensitivePaths)]; this.LogNetworkTraffic = logNetworkTraffic ?? (bool)SConfig.DefaultValues[nameof(this.LogNetworkTraffic)]; this.LogTechnicalDetailsForBrokenMods = logTechnicalDetailsForBrokenMods ?? (bool)SConfig.DefaultValues[nameof(this.LogTechnicalDetailsForBrokenMods)]; diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 9401af1e..e82c8ceb 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -280,7 +280,8 @@ namespace StardewModdingAPI.Framework GameRunner.instance = this.Game; // fix Harmony for mods - MiniMonoModHotfix.Apply(); + if (this.Settings.FixHarmony) + MiniMonoModHotfix.Apply(); // set window titles this.UpdateWindowTitles(); @@ -1743,7 +1744,7 @@ namespace StardewModdingAPI.Framework this.ModRegistry.AreAllModsLoaded = true; // log mod info - this.LogManager.LogModInfo(loaded, loadedContentPacks, loadedMods, skippedMods.ToArray(), this.Settings.ParanoidWarnings, this.Settings.LogTechnicalDetailsForBrokenMods); + this.LogManager.LogModInfo(loaded, loadedContentPacks, loadedMods, skippedMods.ToArray(), this.Settings.ParanoidWarnings, this.Settings.LogTechnicalDetailsForBrokenMods, this.Settings.FixHarmony); // initialize translations this.ReloadTranslations(loaded); diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json index 26a3caaa..55f9869b 100644 --- a/src/SMAPI/SMAPI.config.json +++ b/src/SMAPI/SMAPI.config.json @@ -53,6 +53,13 @@ in future SMAPI versions. */ "RewriteMods": true, + /** + * Whether to fix the library mods use to patch game code, so it works with Stardew Valley. + * + * If you disable this, mods which use Harmony are likely to cause game crashes. + */ + "FixHarmony": true, + /** * Whether to make SMAPI file APIs case-insensitive (even if the filesystem is case-sensitive). *