From 8a475b35790506a18aa94a68530b40e8326017ca Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 15 Jan 2021 18:48:29 -0500 Subject: move error-handling Harmony patches into a new Error Handler bundled mod --- .../Patches/EventErrorPatch.cs | 114 +++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs (limited to 'src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs') diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs new file mode 100644 index 00000000..fabc6cad --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs @@ -0,0 +1,114 @@ +using System.Diagnostics.CodeAnalysis; +#if HARMONY_2 +using System; +using HarmonyLib; +#else +using System.Reflection; +using Harmony; +#endif +using StardewModdingAPI.Framework.Patching; +using StardewValley; + +namespace StardewModdingAPI.Mods.ErrorHandler.Patches +{ + /// A Harmony patch for which intercepts invalid preconditions and logs an error instead of crashing. + /// Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments. + [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.")] + internal class EventErrorPatch : IHarmonyPatch + { + /********* + ** Fields + *********/ + /// Writes messages to the console and log file on behalf of the game. + private static IMonitor MonitorForGame; + + + /********* + ** Accessors + *********/ + /// + public string Name => nameof(EventErrorPatch); + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Writes messages to the console and log file on behalf of the game. + public EventErrorPatch(IMonitor monitorForGame) + { + EventErrorPatch.MonitorForGame = monitorForGame; + } + + /// +#if HARMONY_2 + public void Apply(Harmony harmony) + { + harmony.Patch( + original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"), + finalizer: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Finalize_GameLocation_CheckEventPrecondition)) + ); + } +#else + public void Apply(HarmonyInstance harmony) + { + harmony.Patch( + original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"), + prefix: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Before_GameLocation_CheckEventPrecondition)) + ); + } +#endif + + + /********* + ** Private methods + *********/ +#if HARMONY_2 + /// The method to call instead of GameLocation.checkEventPrecondition. + /// The return value of the original method. + /// The precondition to be parsed. + /// The exception thrown by the wrapped method, if any. + /// Returns the exception to throw, if any. + private static Exception Finalize_GameLocation_CheckEventPrecondition(ref int __result, string precondition, Exception __exception) + { + if (__exception != null) + { + __result = -1; + EventErrorPatch.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{__exception.InnerException}", LogLevel.Error); + } + + return null; + } +#else + /// The method to call instead of GameLocation.checkEventPrecondition. + /// The instance being patched. + /// The return value of the original method. + /// The precondition to be parsed. + /// The method being wrapped. + /// Returns whether to execute the original method. + private static bool Before_GameLocation_CheckEventPrecondition(GameLocation __instance, ref int __result, string precondition, MethodInfo __originalMethod) + { + const string key = nameof(EventErrorPatch.Before_GameLocation_CheckEventPrecondition); + if (!PatchHelper.StartIntercept(key)) + return true; + + try + { + __result = (int)__originalMethod.Invoke(__instance, new object[] { precondition }); + return false; + } + catch (TargetInvocationException ex) + { + __result = -1; + EventErrorPatch.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{ex.InnerException}", LogLevel.Error); + return false; + } + finally + { + PatchHelper.StopIntercept(key); + } + } +#endif + } +} -- cgit