diff options
-rw-r--r-- | docs/release-notes.md | 3 | ||||
-rw-r--r-- | src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs | 59 | ||||
-rw-r--r-- | src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj | 1 |
3 files changed, 58 insertions, 5 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index 701471ee..8b81264a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -11,7 +11,8 @@ * For players: * Added support for unofficial 64-bit Stardew Valley, including automatic support in the SMAPI installer. * Added update checks for Stardew64Installer if it patched the game. - * When many mods fail to load, root dependencies are now listed in their own group so it's easier to see which ones you should try updating first. + * Added smarter grouping for skipped mods, so it's easier to see root dependencies to update first. + * Added error-handling to prevent a crash when the game can't update a map's seasonal tilesheets _(in Error Handler)_. * On macOS, the `StardewModdingAPI.bin.osx` file is no longer overwritten if it's identical to avoid resetting file permissions (thanks to 007wayne!). * `*.ico` files are now ignored when scanning for mods. * Fixed error for non-English players after returning to title, reloading, and entering town with a completed movie theater. diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs index 1edf2d6a..7a48133e 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs @@ -8,10 +8,11 @@ using Harmony; #endif using StardewModdingAPI.Framework.Patching; using StardewValley; +using xTile; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { - /// <summary>A Harmony patch for <see cref="GameLocation.checkEventPrecondition"/> which intercepts invalid preconditions and logs an error instead of crashing.</summary> + /// <summary>Harmony patches for <see cref="GameLocation.checkEventPrecondition"/> and <see cref="GameLocation.updateSeasonalTileSheets"/> which intercept errors instead of crashing.</summary> /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony and methods are named for clarity.")] [SuppressMessage("ReSharper", "IdentifierTypo", Justification = "Argument names are defined by Harmony and methods are named for clarity.")] @@ -39,17 +40,25 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches public void Apply(Harmony harmony) { harmony.Patch( - original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"), + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkEventPrecondition)), finalizer: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Finalize_GameLocation_CheckEventPrecondition)) ); +harmony.Patch( + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)), + finalizer: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Before_GameLocation_UpdateSeasonalTileSheets)) + ); } #else public void Apply(HarmonyInstance harmony) { harmony.Patch( - original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"), + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkEventPrecondition)), prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_CheckEventPrecondition)) ); + harmony.Patch( + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)), + prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_UpdateSeasonalTileSheets)) + ); } #endif @@ -74,7 +83,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return null; } #else - /// <summary>The method to call instead of GameLocation.checkEventPrecondition.</summary> + /// <summary>The method to call instead of <see cref="GameLocation.checkEventPrecondition"/>.</summary> /// <param name="__instance">The instance being patched.</param> /// <param name="__result">The return value of the original method.</param> /// <param name="precondition">The precondition to be parsed.</param> @@ -103,5 +112,47 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } } #endif + +#if HARMONY_2 + /// <summary>The method to call instead of <see cref="GameLocation.updateSeasonalTileSheets"/>.</summary> + /// <param name="__instance">The instance being patched.</param> + /// <param name="map">The map whose tilesheets to update.</param> + /// <param name="__exception">The exception thrown by the wrapped method, if any.</param> + /// <returns>Returns the exception to throw, if any.</returns> + private static Exception Before_GameLocation_UpdateSeasonalTileSheets(GameLocation __instance, Map map, Exception __exception) + { + if (__exception != null) + GameLocationPatches.MonitorForGame.Log($"Failed updating seasonal tilesheets for location '{__instance.NameOrUniqueName}': \n{__exception}", LogLevel.Error); + + return null; + } +#else + /// <summary>The method to call instead of <see cref="GameLocation.updateSeasonalTileSheets"/>.</summary> + /// <param name="__instance">The instance being patched.</param> + /// <param name="map">The map whose tilesheets to update.</param> + /// <param name="__originalMethod">The method being wrapped.</param> + /// <returns>Returns whether to execute the original method.</returns> + private static bool Before_GameLocation_UpdateSeasonalTileSheets(GameLocation __instance, Map map, MethodInfo __originalMethod) + { + const string key = nameof(GameLocationPatches.Before_GameLocation_UpdateSeasonalTileSheets); + if (!PatchHelper.StartIntercept(key)) + return true; + + try + { + __originalMethod.Invoke(__instance, new object[] { map }); + return false; + } + catch (TargetInvocationException ex) + { + GameLocationPatches.MonitorForGame.Log($"Failed updating seasonal tilesheets for location '{__instance.NameOrUniqueName}'. Technical details:\n{ex.InnerException}", LogLevel.Error); + return false; + } + finally + { + PatchHelper.StopIntercept(key); + } + } +#endif } } diff --git a/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj b/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj index 56daa710..006a09ca 100644 --- a/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj +++ b/src/SMAPI.Mods.ErrorHandler/SMAPI.Mods.ErrorHandler.csproj @@ -15,6 +15,7 @@ <ItemGroup> <Reference Include="$(GameExecutableName)" HintPath="$(GamePath)\$(GameExecutableName).exe" Private="False" /> + <Reference Include="xTile" HintPath="$(GamePath)\xTile.dll" Private="False" /> </ItemGroup> <!-- Windows only --> |