From aa65b2e2f6ae2578f9d1f81d6fc1f4c5a261d90f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 30 Jul 2021 00:34:53 -0400 Subject: split patch classes which target multiple types --- src/SMAPI.Mods.ErrorHandler/ModEntry.cs | 3 +- .../Patches/DialogueErrorPatch.cs | 21 -------- .../Patches/IClickableMenuPatcher.cs | 44 +++++++++++++++++ .../Patches/ObjectErrorPatch.cs | 19 -------- .../Patches/ScheduleErrorPatch.cs | 21 ++++++++ src/SMAPI/Framework/SCore.cs | 3 +- src/SMAPI/Patches/LoadContextPatch.cs | 15 ------ src/SMAPI/Patches/TitleMenuPatcher.cs | 56 ++++++++++++++++++++++ 8 files changed, 124 insertions(+), 58 deletions(-) create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs create mode 100644 src/SMAPI/Patches/TitleMenuPatcher.cs diff --git a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs index b1081218..f6f9a150 100644 --- a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs +++ b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs @@ -1,8 +1,6 @@ using System; using System.Reflection; using StardewModdingAPI.Events; -using StardewModdingAPI.Framework; -using StardewModdingAPI.Framework.Logging; using StardewModdingAPI.Framework.Patching; using StardewModdingAPI.Mods.ErrorHandler.Patches; using StardewValley; @@ -35,6 +33,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler new DictionaryPatches(this.Helper.Reflection), new EventPatches(monitorForGame), new GameLocationPatches(monitorForGame), + new IClickablePatcher(), new ObjectErrorPatch(), new LoadErrorPatch(this.Monitor, this.OnSaveContentRemoved), new ScheduleErrorPatch(monitorForGame), diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs index a065e3d7..ca5bb0bf 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using HarmonyLib; using StardewModdingAPI.Framework; @@ -43,10 +42,6 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches original: AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }), finalizer: new HarmonyMethod(this.GetType(), nameof(DialogueErrorPatch.Finalize_Dialogue_Constructor)) ); - harmony.Patch( - original: AccessTools.Property(typeof(NPC), nameof(NPC.CurrentDialogue)).GetMethod, - finalizer: new HarmonyMethod(this.GetType(), nameof(DialogueErrorPatch.Finalize_NPC_CurrentDialogue)) - ); } @@ -76,21 +71,5 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return null; } - - /// The method to call after . - /// The instance being patched. - /// The return value of the original method. - /// The exception thrown by the wrapped method, if any. - /// Returns the exception to throw, if any. - private static Exception Finalize_NPC_CurrentDialogue(NPC __instance, ref Stack __result, Exception __exception) - { - if (__exception == null) - return null; - - DialogueErrorPatch.MonitorForGame.Log($"Failed loading current dialogue for NPC {__instance.Name}:\n{__exception.GetLogSummary()}", LogLevel.Error); - __result = new Stack(); - - return null; - } } } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs new file mode 100644 index 00000000..a5a90db2 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs @@ -0,0 +1,44 @@ +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Framework.Patching; +using StardewValley; +using StardewValley.Menus; +using SObject = StardewValley.Object; + +namespace StardewModdingAPI.Mods.ErrorHandler.Patches +{ + /// A Harmony patch for which intercepts crashes due to invalid items. + /// 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 IClickablePatcher : IHarmonyPatch + { + /********* + ** Public methods + *********/ + /// + public void Apply(Harmony harmony) + { + harmony.Patch( + original: AccessTools.Method(typeof(IClickableMenu), nameof(IClickableMenu.drawToolTip)), + prefix: new HarmonyMethod(this.GetType(), nameof(IClickablePatcher.Before_IClickableMenu_DrawTooltip)) + ); + } + + + /********* + ** Private methods + *********/ + /// The method to call instead of . + /// The item for which to draw a tooltip. + /// Returns whether to execute the original method. + private static bool Before_IClickableMenu_DrawTooltip(Item hoveredItem) + { + // invalid edible item cause crash when drawing tooltips + if (hoveredItem is SObject obj && obj.Edibility != -300 && !Game1.objectInformation.ContainsKey(obj.ParentSheetIndex)) + return false; + + return true; + } + } +} diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs index a68e359c..7a4b3cfa 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs @@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis; using HarmonyLib; using StardewModdingAPI.Framework.Patching; using StardewValley; -using StardewValley.Menus; using SObject = StardewValley.Object; namespace StardewModdingAPI.Mods.ErrorHandler.Patches @@ -32,12 +31,6 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches original: AccessTools.Method(typeof(SObject), "loadDisplayName"), finalizer: new HarmonyMethod(this.GetType(), nameof(ObjectErrorPatch.Finalize_Object_loadDisplayName)) ); - - // IClickableMenu.drawToolTip - harmony.Patch( - original: AccessTools.Method(typeof(IClickableMenu), nameof(IClickableMenu.drawToolTip)), - prefix: new HarmonyMethod(this.GetType(), nameof(ObjectErrorPatch.Before_IClickableMenu_DrawTooltip)) - ); } @@ -74,17 +67,5 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return __exception; } - - /// The method to call instead of . - /// The item for which to draw a tooltip. - /// Returns whether to execute the original method. - private static bool Before_IClickableMenu_DrawTooltip(Item hoveredItem) - { - // invalid edible item cause crash when drawing tooltips - if (hoveredItem is SObject obj && obj.Edibility != -300 && !Game1.objectInformation.ContainsKey(obj.ParentSheetIndex)) - return false; - - return true; - } } } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs index d2e332b4..b8ab9361 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs @@ -34,6 +34,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /// public void Apply(Harmony harmony) { + harmony.Patch( + original: AccessTools.Property(typeof(NPC), nameof(NPC.CurrentDialogue)).GetMethod, + finalizer: new HarmonyMethod(this.GetType(), nameof(ScheduleErrorPatch.Finalize_NPC_CurrentDialogue)) + ); + harmony.Patch( original: AccessTools.Method(typeof(NPC), nameof(NPC.parseMasterSchedule)), finalizer: new HarmonyMethod(this.GetType(), nameof(ScheduleErrorPatch.Finalize_NPC_parseMasterSchedule)) @@ -44,6 +49,22 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /********* ** Private methods *********/ + /// The method to call after . + /// The instance being patched. + /// The return value of the original method. + /// The exception thrown by the wrapped method, if any. + /// Returns the exception to throw, if any. + private static Exception Finalize_NPC_CurrentDialogue(NPC __instance, ref Stack __result, Exception __exception) + { + if (__exception == null) + return null; + + ScheduleErrorPatch.MonitorForGame.Log($"Failed loading current dialogue for NPC {__instance.Name}:\n{__exception.GetLogSummary()}", LogLevel.Error); + __result = new Stack(); + + return null; + } + /// The method to call instead of . /// The raw schedule data to parse. /// The instance being patched. diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index b607f95d..419afd4b 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -255,7 +255,8 @@ namespace StardewModdingAPI.Framework // apply game patches MiniMonoModHotfix.Apply(); new GamePatcher(this.Monitor).Apply( - new LoadContextPatch(this.Reflection, this.OnLoadStageChanged) + new LoadContextPatch(this.Reflection, this.OnLoadStageChanged), + new TitleMenuPatcher(this.OnLoadStageChanged) ); // add exit handler diff --git a/src/SMAPI/Patches/LoadContextPatch.cs b/src/SMAPI/Patches/LoadContextPatch.cs index 721cf53d..c7f7d986 100644 --- a/src/SMAPI/Patches/LoadContextPatch.cs +++ b/src/SMAPI/Patches/LoadContextPatch.cs @@ -44,12 +44,6 @@ namespace StardewModdingAPI.Patches /// public void Apply(Harmony harmony) { - // detect CreatedBasicInfo - harmony.Patch( - original: AccessTools.Method(typeof(TitleMenu), nameof(TitleMenu.createdNewCharacter)), - prefix: new HarmonyMethod(this.GetType(), nameof(LoadContextPatch.Before_TitleMenu_CreatedNewCharacter)) - ); - // detect CreatedInitialLocations and SaveAddedLocations harmony.Patch( original: AccessTools.Method(typeof(Game1), nameof(Game1.AddModNPCs)), @@ -74,15 +68,6 @@ namespace StardewModdingAPI.Patches /********* ** Private methods *********/ - /// Called before . - /// Returns whether to execute the original method. - /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. - private static bool Before_TitleMenu_CreatedNewCharacter() - { - LoadContextPatch.OnStageChanged(LoadStage.CreatedBasicInfo); - return true; - } - /// Called before . /// Returns whether to execute the original method. /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. diff --git a/src/SMAPI/Patches/TitleMenuPatcher.cs b/src/SMAPI/Patches/TitleMenuPatcher.cs new file mode 100644 index 00000000..a889adfc --- /dev/null +++ b/src/SMAPI/Patches/TitleMenuPatcher.cs @@ -0,0 +1,56 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Enums; +using StardewModdingAPI.Framework.Patching; +using StardewValley.Menus; + +namespace StardewModdingAPI.Patches +{ + /// Harmony patches which notify SMAPI for save creation load stages. + /// 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 TitleMenuPatcher : IHarmonyPatch + { + /********* + ** Fields + *********/ + /// A callback to invoke when the load stage changes. + private static Action OnStageChanged; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A callback to invoke when the load stage changes. + public TitleMenuPatcher(Action onStageChanged) + { + TitleMenuPatcher.OnStageChanged = onStageChanged; + } + + /// + public void Apply(Harmony harmony) + { + // detect CreatedBasicInfo + harmony.Patch( + original: AccessTools.Method(typeof(TitleMenu), nameof(TitleMenu.createdNewCharacter)), + prefix: new HarmonyMethod(this.GetType(), nameof(TitleMenuPatcher.Before_TitleMenu_CreatedNewCharacter)) + ); + } + + + /********* + ** Private methods + *********/ + /// Called before . + /// Returns whether to execute the original method. + /// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments. + private static bool Before_TitleMenu_CreatedNewCharacter() + { + TitleMenuPatcher.OnStageChanged(LoadStage.CreatedBasicInfo); + return true; + } + } +} -- cgit