diff options
Diffstat (limited to 'src')
36 files changed, 394 insertions, 221 deletions
diff --git a/src/SMAPI.Internal.Patching/BasePatcher.cs b/src/SMAPI.Internal.Patching/BasePatcher.cs new file mode 100644 index 00000000..87155d7f --- /dev/null +++ b/src/SMAPI.Internal.Patching/BasePatcher.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using HarmonyLib; + +namespace StardewModdingAPI.Internal.Patching +{ + /// <summary>Provides base implementation logic for <see cref="IPatcher"/> instances.</summary> + internal abstract class BasePatcher : IPatcher + { + /********* + ** Public methods + *********/ + /// <inheritdoc /> + public abstract void Apply(Harmony harmony, IMonitor monitor); + + + /********* + ** Protected methods + *********/ + /// <summary>Get a method and assert that it was found.</summary> + /// <typeparam name="TTarget">The type containing the method.</typeparam> + /// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param> + protected ConstructorInfo RequireConstructor<TTarget>(params Type[] parameters) + { + return PatchHelper.RequireConstructor<TTarget>(parameters); + } + + /// <summary>Get a method and assert that it was found.</summary> + /// <typeparam name="TTarget">The type containing the method.</typeparam> + /// <param name="name">The method name.</param> + /// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param> + /// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param> + protected MethodInfo RequireMethod<TTarget>(string name, Type[] parameters = null, Type[] generics = null) + { + return PatchHelper.RequireMethod<TTarget>(name, parameters, generics); + } + + /// <summary>Get a Harmony patch method on the current patcher instance.</summary> + /// <param name="name">The method name.</param> + /// <param name="priority">The patch priority to apply, usually specified using Harmony's <see cref="Priority"/> enum, or <c>null</c> to keep the default value.</param> + protected HarmonyMethod GetHarmonyMethod(string name, int? priority = null) + { + var method = new HarmonyMethod( + AccessTools.Method(this.GetType(), name) + ?? throw new InvalidOperationException($"Can't find patcher method {PatchHelper.GetMethodString(this.GetType(), name)}.") + ); + + if (priority.HasValue) + method.priority = priority.Value; + + return method; + } + } +} diff --git a/src/SMAPI.Internal.Patching/HarmonyPatcher.cs b/src/SMAPI.Internal.Patching/HarmonyPatcher.cs new file mode 100644 index 00000000..c07e3b41 --- /dev/null +++ b/src/SMAPI.Internal.Patching/HarmonyPatcher.cs @@ -0,0 +1,36 @@ +using System; +using HarmonyLib; + +namespace StardewModdingAPI.Internal.Patching +{ + /// <summary>Simplifies applying <see cref="IPatcher"/> instances to the game.</summary> + internal static class HarmonyPatcher + { + /********* + ** Public methods + *********/ + /// <summary>Apply the given Harmony patchers.</summary> + /// <param name="id">The mod ID applying the patchers.</param> + /// <param name="monitor">The monitor with which to log any errors.</param> + /// <param name="patchers">The patchers to apply.</param> + public static Harmony Apply(string id, IMonitor monitor, params IPatcher[] patchers) + { + Harmony harmony = new Harmony(id); + + foreach (IPatcher patcher in patchers) + { + try + { + patcher.Apply(harmony, monitor); + } + catch (Exception ex) + { + monitor.Log($"Couldn't apply runtime patch '{patcher.GetType().Name}' to the game. Some SMAPI features may not work correctly. See log file for details.", LogLevel.Error); + monitor.Log($"Technical details:\n{ex.GetLogSummary()}"); + } + } + + return harmony; + } + } +} diff --git a/src/SMAPI.Internal.Patching/IPatcher.cs b/src/SMAPI.Internal.Patching/IPatcher.cs new file mode 100644 index 00000000..a732d64f --- /dev/null +++ b/src/SMAPI.Internal.Patching/IPatcher.cs @@ -0,0 +1,16 @@ +using HarmonyLib; + +namespace StardewModdingAPI.Internal.Patching +{ + /// <summary>A set of Harmony patches to apply.</summary> + internal interface IPatcher + { + /********* + ** Public methods + *********/ + /// <summary>Apply the Harmony patches for this instance.</summary> + /// <param name="harmony">The Harmony instance.</param> + /// <param name="monitor">The monitor with which to log any errors.</param> + public void Apply(Harmony harmony, IMonitor monitor); + } +} diff --git a/src/SMAPI.Internal.Patching/PatchHelper.cs b/src/SMAPI.Internal.Patching/PatchHelper.cs new file mode 100644 index 00000000..c9758616 --- /dev/null +++ b/src/SMAPI.Internal.Patching/PatchHelper.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Text; +using HarmonyLib; + +namespace StardewModdingAPI.Internal.Patching +{ + /// <summary>Provides utility methods for patching game code with Harmony.</summary> + internal static class PatchHelper + { + /********* + ** Public methods + *********/ + /// <summary>Get a constructor and assert that it was found.</summary> + /// <typeparam name="TTarget">The type containing the method.</typeparam> + /// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param> + /// <exception cref="InvalidOperationException">The type has no matching constructor.</exception> + public static ConstructorInfo RequireConstructor<TTarget>(Type[] parameters = null) + { + return + AccessTools.Constructor(typeof(TTarget), parameters) + ?? throw new InvalidOperationException($"Can't find constructor {PatchHelper.GetMethodString(typeof(TTarget), null, parameters)} to patch."); + } + + /// <summary>Get a method and assert that it was found.</summary> + /// <typeparam name="TTarget">The type containing the method.</typeparam> + /// <param name="name">The method name.</param> + /// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param> + /// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param> + /// <exception cref="InvalidOperationException">The type has no matching method.</exception> + public static MethodInfo RequireMethod<TTarget>(string name, Type[] parameters = null, Type[] generics = null) + { + return + AccessTools.Method(typeof(TTarget), name, parameters, generics) + ?? throw new InvalidOperationException($"Can't find method {PatchHelper.GetMethodString(typeof(TTarget), name, parameters, generics)} to patch."); + } + + /// <summary>Get a human-readable representation of a method target.</summary> + /// <param name="type">The type containing the method.</param> + /// <param name="name">The method name, or <c>null</c> for a constructor.</param> + /// <param name="parameters">The method parameter types, or <c>null</c> if it's not overloaded.</param> + /// <param name="generics">The method generic types, or <c>null</c> if it's not generic.</param> + public static string GetMethodString(Type type, string name, Type[] parameters = null, Type[] generics = null) + { + StringBuilder str = new(); + + // type + str.Append(type.FullName); + + // method name (if not constructor) + if (name != null) + { + str.Append('.'); + str.Append(name); + } + + // generics + if (generics?.Any() == true) + { + str.Append('<'); + str.Append(string.Join(", ", generics.Select(p => p.FullName))); + str.Append('>'); + } + + // parameters + if (parameters?.Any() == true) + { + str.Append('('); + str.Append(string.Join(", ", parameters.Select(p => p.FullName))); + str.Append(')'); + } + + return str.ToString(); + } + } +} diff --git a/src/SMAPI.Internal.Patching/SMAPI.Internal.Patching.projitems b/src/SMAPI.Internal.Patching/SMAPI.Internal.Patching.projitems new file mode 100644 index 00000000..4fa2a062 --- /dev/null +++ b/src/SMAPI.Internal.Patching/SMAPI.Internal.Patching.projitems @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' < '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> + <HasSharedItems>true</HasSharedItems> + <SharedGUID>6c16e948-3e5c-47a7-bf4b-07a7469a87a5</SharedGUID> + </PropertyGroup> + <PropertyGroup Label="Configuration"> + <Import_RootNamespace>SMAPI.Internal.Patching</Import_RootNamespace> + </PropertyGroup> + <ItemGroup> + <Compile Include="$(MSBuildThisFileDirectory)BasePatcher.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)HarmonyPatcher.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)IPatcher.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)PatchHelper.cs" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/SMAPI.Internal.Patching/SMAPI.Internal.Patching.shproj b/src/SMAPI.Internal.Patching/SMAPI.Internal.Patching.shproj new file mode 100644 index 00000000..1a102c82 --- /dev/null +++ b/src/SMAPI.Internal.Patching/SMAPI.Internal.Patching.shproj @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup Label="Globals"> + <ProjectGuid>6c16e948-3e5c-47a7-bf4b-07a7469a87a5</ProjectGuid> + <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> + <PropertyGroup /> + <Import Project="SMAPI.Internal.Patching.projitems" Label="Shared" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> +</Project> diff --git a/src/SMAPI.Internal/ExceptionExtensions.cs b/src/SMAPI.Internal/ExceptionExtensions.cs new file mode 100644 index 00000000..d7a2252b --- /dev/null +++ b/src/SMAPI.Internal/ExceptionExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Reflection; + +namespace StardewModdingAPI.Internal +{ + /// <summary>Provides extension methods for handling exceptions.</summary> + internal static class ExceptionExtensions + { + /********* + ** Public methods + *********/ + /// <summary>Get a string representation of an exception suitable for writing to the error log.</summary> + /// <param name="exception">The error to summarize.</param> + public static string GetLogSummary(this Exception exception) + { + switch (exception) + { + case TypeLoadException ex: + return $"Failed loading type '{ex.TypeName}': {exception}"; + + case ReflectionTypeLoadException ex: + string summary = exception.ToString(); + foreach (Exception childEx in ex.LoaderExceptions) + summary += $"\n\n{childEx.GetLogSummary()}"; + return summary; + + default: + return exception.ToString(); + } + } + + /// <summary>Get the lowest exception in an exception stack.</summary> + /// <param name="exception">The exception from which to search.</param> + public static Exception GetInnermostException(this Exception exception) + { + while (exception.InnerException != null) + exception = exception.InnerException; + return exception; + } + } +} diff --git a/src/SMAPI.Internal/SMAPI.Internal.projitems b/src/SMAPI.Internal/SMAPI.Internal.projitems index 0d583a6d..0ee94a5b 100644 --- a/src/SMAPI.Internal/SMAPI.Internal.projitems +++ b/src/SMAPI.Internal/SMAPI.Internal.projitems @@ -14,5 +14,6 @@ <Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\ConsoleLogLevel.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\IConsoleWriter.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ConsoleWriting\MonitorColorScheme.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)ExceptionExtensions.cs" /> </ItemGroup> </Project>
\ No newline at end of file diff --git a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs index ac9d1b94..067f6a8d 100644 --- a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs +++ b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs @@ -1,7 +1,7 @@ using System; using System.Reflection; using StardewModdingAPI.Events; -using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Internal.Patching; using StardewModdingAPI.Mods.ErrorHandler.Patches; using StardewValley; @@ -28,7 +28,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler IMonitor monitorForGame = this.GetMonitorForGame(); // apply patches - new GamePatcher(this.Monitor).Apply( + HarmonyPatcher.Apply(this.ModManifest.UniqueID, this.Monitor, new DialoguePatcher(monitorForGame, this.Helper.Reflection), new DictionaryPatcher(this.Helper.Reflection), new EventPatcher(monitorForGame), diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs index 7b730ee5..7a3af39c 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs @@ -1,17 +1,17 @@ using System; using System.Diagnostics.CodeAnalysis; using HarmonyLib; -using StardewModdingAPI.Framework; -using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Internal; +using StardewModdingAPI.Internal.Patching; using StardewValley; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { - /// <summary>A Harmony patch for the <see cref="Dialogue"/> constructor which intercepts invalid dialogue lines and logs an error instead of crashing.</summary> + /// <summary>Harmony patches for <see cref="Dialogue"/> which intercept invalid dialogue lines and logs an error 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.")] - internal class DialoguePatcher : IHarmonyPatch + internal class DialoguePatcher : BasePatcher { /********* ** Fields @@ -36,11 +36,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// <inheritdoc /> - public void Apply(Harmony harmony) + public override void Apply(Harmony harmony, IMonitor monitor) { harmony.Patch( - original: AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }), - finalizer: new HarmonyMethod(this.GetType(), nameof(DialoguePatcher.Finalize_Dialogue_Constructor)) + original: this.RequireConstructor<Dialogue>(typeof(string), typeof(NPC)), + finalizer: this.GetHarmonyMethod(nameof(DialoguePatcher.Finalize_Constructor)) ); } @@ -48,13 +48,13 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /********* ** Private methods *********/ - /// <summary>The method to call after the Dialogue constructor.</summary> + /// <summary>The method to call when the Dialogue constructor throws an exception.</summary> /// <param name="__instance">The instance being patched.</param> /// <param name="masterDialogue">The dialogue being parsed.</param> /// <param name="speaker">The NPC for which the dialogue is being parsed.</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 Finalize_Dialogue_Constructor(Dialogue __instance, string masterDialogue, NPC speaker, Exception __exception) + private static Exception Finalize_Constructor(Dialogue __instance, string masterDialogue, NPC speaker, Exception __exception) { if (__exception != null) { diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs index 3c5240b6..6ad64e16 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs @@ -2,18 +2,18 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using HarmonyLib; -using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Internal.Patching; using StardewValley.GameData; using StardewValley.GameData.HomeRenovations; using StardewValley.GameData.Movies; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { - /// <summary>A Harmony patch for <see cref="Dictionary{TKey,TValue}"/> which adds the accessed key to <see cref="KeyNotFoundException"/> exceptions.</summary> + /// <summary>Harmony patches for <see cref="Dictionary{TKey,TValue}"/> which add the accessed key to <see cref="KeyNotFoundException"/> exceptions.</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.")] - internal class DictionaryPatcher : IHarmonyPatch + internal class DictionaryPatcher : BasePatcher { /********* ** Fields @@ -33,7 +33,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// <inheritdoc /> - public void Apply(Harmony harmony) + public override void Apply(Harmony harmony, IMonitor monitor) { Type[] keyTypes = { typeof(int), typeof(string) }; Type[] valueTypes = { typeof(int), typeof(string), typeof(HomeRenovation), typeof(MovieData), typeof(SpecialOrderData) }; @@ -45,8 +45,8 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); harmony.Patch( - original: AccessTools.Method(dictionaryType, "get_Item"), - finalizer: new HarmonyMethod(this.GetType(), nameof(DictionaryPatcher.Finalize_GetItem)) + original: AccessTools.Method(dictionaryType, "get_Item") ?? throw new InvalidOperationException($"Can't find method {PatchHelper.GetMethodString(dictionaryType, "get_Item")} to patch."), + finalizer: this.GetHarmonyMethod(nameof(DictionaryPatcher.Finalize_GetItem)) ); } } @@ -63,19 +63,13 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches private static Exception Finalize_GetItem(object key, Exception __exception) { if (__exception is KeyNotFoundException) - AddKeyTo(__exception, key?.ToString()); + { + DictionaryPatcher.Reflection + .GetField<string>(__exception, "_message") + .SetValue($"{__exception.Message}\nkey: '{key}'"); + } return __exception; } - - /// <summary>Add the accessed key to an exception message.</summary> - /// <param name="exception">The exception to modify.</param> - /// <param name="key">The dictionary key.</param> - private static void AddKeyTo(Exception exception, string key) - { - DictionaryPatcher.Reflection - .GetField<string>(exception, "_message") - .SetValue($"{exception.Message}\nkey: '{key}'"); - } } } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs index 9a7b34d8..1b706147 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using HarmonyLib; -using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Internal.Patching; using StardewValley; namespace StardewModdingAPI.Mods.ErrorHandler.Patches @@ -10,7 +10,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /// <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.")] - internal class EventPatcher : IHarmonyPatch + internal class EventPatcher : BasePatcher { /********* ** Fields @@ -30,11 +30,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// <inheritdoc /> - public void Apply(Harmony harmony) + public override void Apply(Harmony harmony, IMonitor monitor) { harmony.Patch( - original: AccessTools.Method(typeof(Event), nameof(Event.LogErrorAndHalt)), - postfix: new HarmonyMethod(this.GetType(), nameof(EventPatcher.After_Event_LogErrorAndHalt)) + original: this.RequireMethod<Event>(nameof(Event.LogErrorAndHalt)), + postfix: this.GetHarmonyMethod(nameof(EventPatcher.After_LogErrorAndHalt)) ); } @@ -44,7 +44,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches *********/ /// <summary>The method to call after <see cref="Event.LogErrorAndHalt"/>.</summary> /// <param name="e">The exception being logged.</param> - private static void After_Event_LogErrorAndHalt(Exception e) + private static void After_LogErrorAndHalt(Exception e) { EventPatcher.MonitorForGame.Log(e.ToString(), LogLevel.Error); } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs index 7427fe48..7df6b0a2 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs @@ -1,17 +1,17 @@ using System; using System.Diagnostics.CodeAnalysis; using HarmonyLib; -using StardewModdingAPI.Framework.Patching; +using StardewModdingAPI.Internal.Patching; using StardewValley; using xTile; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { - /// <summary>Harmony patches for <see cref="GameLocation.checkEventPrecondition"/> and <see cref="GameLocation.updateSeasonalTileSheets"/> which intercept errors instead of crashing.</summary> + /// <summary>Harmony patches for <see cref="GameLocation"/> 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.")] - internal class GameLocationPatcher : IHarmonyPatch + internal class GameLocationPatcher : BasePatcher { /********* ** Fields @@ -31,15 +31,15 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// <inheritdoc /> - public void Apply(Harmony harmony) + public override void Apply(Harmony harmony, IMonitor monitor) { harmony.Patch( - original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkEventPrecondition)), - finalizer: new HarmonyMethod(this.GetType(), nameof(GameLocationPatcher.Finalize_GameLocation_CheckEventPrecondition)) + original: this.RequireMethod<GameLocation>(nameof(GameLocation.checkEventPrecondition)), + finalizer: this.GetHarmonyMethod(nameof(GameLocationPatcher.Finalize_CheckEventPrecondition)) ); harmony.Patch( - original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)), - finalizer: new HarmonyMethod(this.GetType(), nameof(GameLocationPatcher.Before_GameLocation_UpdateSeasonalTileSheets)) + original: this.RequireMethod<GameLocation>(nameof(GameLocation.updateSeasonalTileSheets)), + finalizer: this.GetHarmonyMethod(nameof(GameLocationPatcher.Finalize_UpdateSeasonalTileSheets)) ); } @@ -47,12 +47,12 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /********* ** Private methods *********/ - /// <summary>The method to call instead of GameLocation.checkEventPrecondition.</summary> + /// <summary>The method to call when <see cref="GameLocation.checkEventPrecondition"/> throws an exception.</summary> /// <param name="__result">The return value of the original method.</param> /// <param name="precondition">The precondition to be parsed.</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 Finalize_GameLocation_CheckEventPrecondition(ref int __result, string precondition, Exception __exception) + private static Exception Finalize_CheckEventPrecondition(ref int __result, string precondition, Exception __exception) { if (__exception != null) { @@ -63,12 +63,12 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return null; } - /// <summary>The method to call instead of <see cref="GameLocation.updateSeasonalTileSheets"/>.</summary> + /// <summary>The method to call when <see cref="GameLocation.updateSeasonalTileSheets"/> throws an exception.</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) + private static Exception Finalize_UpdateSeasonalTileSheets(GameLocation __instance, Map map, E |
