From 8df578edb6d135796a48b219ecc7a7291c7ef460 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 13 Jul 2021 09:14:07 -0400 Subject: migrate to Harmony 2.1 (#711) --- .../Patches/DialogueErrorPatch.cs | 92 +--------------------- .../Patches/EventPatches.cs | 8 -- .../Patches/GameLocationPatches.cs | 87 +------------------- .../Patches/LoadErrorPatch.cs | 8 -- .../Patches/ObjectErrorPatch.cs | 50 +----------- .../Patches/ScheduleErrorPatch.cs | 50 +----------- .../Patches/SpriteBatchValidationPatches.cs | 10 +-- .../Patches/UtilityErrorPatches.cs | 48 +---------- 8 files changed, 13 insertions(+), 340 deletions(-) (limited to 'src/SMAPI.Mods.ErrorHandler/Patches') diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs index cce13064..a065e3d7 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs @@ -1,15 +1,10 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using StardewModdingAPI.Framework.Patching; -using StardewValley; -#if HARMONY_2 using HarmonyLib; using StardewModdingAPI.Framework; -#else -using System.Reflection; -using Harmony; -#endif +using StardewModdingAPI.Framework.Patching; +using StardewValley; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { @@ -41,9 +36,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches DialogueErrorPatch.Reflection = reflector; } - /// -#if HARMONY_2 public void Apply(Harmony harmony) { harmony.Patch( @@ -55,25 +48,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches finalizer: new HarmonyMethod(this.GetType(), nameof(DialogueErrorPatch.Finalize_NPC_CurrentDialogue)) ); } -#else - public void Apply(HarmonyInstance harmony) - { - harmony.Patch( - original: AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }), - prefix: new HarmonyMethod(this.GetType(), nameof(DialogueErrorPatch.Before_Dialogue_Constructor)) - ); - harmony.Patch( - original: AccessTools.Property(typeof(NPC), nameof(NPC.CurrentDialogue)).GetMethod, - prefix: new HarmonyMethod(this.GetType(), nameof(DialogueErrorPatch.Before_NPC_CurrentDialogue)) - ); - } -#endif /********* ** Private methods *********/ -#if HARMONY_2 /// The method to call after the Dialogue constructor. /// The instance being patched. /// The dialogue being parsed. @@ -113,72 +92,5 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return null; } -#else - - /// The method to call instead of the Dialogue constructor. - /// The instance being patched. - /// The dialogue being parsed. - /// The NPC for which the dialogue is being parsed. - /// Returns whether to execute the original method. - private static bool Before_Dialogue_Constructor(Dialogue __instance, string masterDialogue, NPC speaker) - { - // get private members - bool nameArraysTranslated = DialogueErrorPatch.Reflection.GetField(typeof(Dialogue), "nameArraysTranslated").GetValue(); - IReflectedMethod translateArraysOfStrings = DialogueErrorPatch.Reflection.GetMethod(typeof(Dialogue), "TranslateArraysOfStrings"); - IReflectedMethod parseDialogueString = DialogueErrorPatch.Reflection.GetMethod(__instance, "parseDialogueString"); - IReflectedMethod checkForSpecialDialogueAttributes = DialogueErrorPatch.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); - - // replicate base constructor - __instance.dialogues ??= new List(); - - // duplicate code with try..catch - try - { - if (!nameArraysTranslated) - translateArraysOfStrings.Invoke(); - __instance.speaker = speaker; - parseDialogueString.Invoke(masterDialogue); - checkForSpecialDialogueAttributes.Invoke(); - } - catch (Exception baseEx) when (baseEx.InnerException is TargetInvocationException invocationEx && invocationEx.InnerException is Exception ex) - { - string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; - DialogueErrorPatch.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{ex}", LogLevel.Error); - - parseDialogueString.Invoke("..."); - checkForSpecialDialogueAttributes.Invoke(); - } - - return false; - } - - /// The method to call instead of . - /// The instance being patched. - /// The return value of the original method. - /// The method being wrapped. - /// Returns whether to execute the original method. - private static bool Before_NPC_CurrentDialogue(NPC __instance, ref Stack __result, MethodInfo __originalMethod) - { - const string key = nameof(DialogueErrorPatch.Before_NPC_CurrentDialogue); - if (!PatchHelper.StartIntercept(key)) - return true; - - try - { - __result = (Stack)__originalMethod.Invoke(__instance, new object[0]); - return false; - } - catch (TargetInvocationException ex) - { - DialogueErrorPatch.MonitorForGame.Log($"Failed loading current dialogue for NPC {__instance.Name}:\n{ex.InnerException ?? ex}", LogLevel.Error); - __result = new Stack(); - return false; - } - finally - { - PatchHelper.StopIntercept(key); - } - } -#endif } } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs index 72863d17..390aa778 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs @@ -1,10 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -#if HARMONY_2 using HarmonyLib; -#else -using Harmony; -#endif using StardewModdingAPI.Framework.Patching; using StardewValley; @@ -34,11 +30,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// -#if HARMONY_2 public void Apply(Harmony harmony) -#else - public void Apply(HarmonyInstance harmony) -#endif { harmony.Patch( original: AccessTools.Method(typeof(Event), nameof(Event.LogErrorAndHalt)), diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs index 7a48133e..ec809fba 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs @@ -1,11 +1,6 @@ -using System.Diagnostics.CodeAnalysis; -#if HARMONY_2 using System; +using System.Diagnostics.CodeAnalysis; using HarmonyLib; -#else -using System.Reflection; -using Harmony; -#endif using StardewModdingAPI.Framework.Patching; using StardewValley; using xTile; @@ -36,37 +31,22 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// -#if HARMONY_2 public void Apply(Harmony harmony) { harmony.Patch( 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), nameof(GameLocation.checkEventPrecondition)), - prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_CheckEventPrecondition)) + finalizer: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Finalize_GameLocation_CheckEventPrecondition)) ); harmony.Patch( original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)), - prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_UpdateSeasonalTileSheets)) + finalizer: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_UpdateSeasonalTileSheets)) ); } -#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. @@ -77,43 +57,12 @@ harmony.Patch( if (__exception != null) { __result = -1; - EventErrorPatch.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{__exception.InnerException}", LogLevel.Error); + GameLocationPatches.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{__exception.InnerException}", LogLevel.Error); } return null; } -#else - /// The method to call instead of . - /// 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(GameLocationPatches.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; - GameLocationPatches.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{ex.InnerException}", LogLevel.Error); - return false; - } - finally - { - PatchHelper.StopIntercept(key); - } - } -#endif -#if HARMONY_2 /// The method to call instead of . /// The instance being patched. /// The map whose tilesheets to update. @@ -126,33 +75,5 @@ harmony.Patch( return null; } -#else - /// The method to call instead of . - /// The instance being patched. - /// The map whose tilesheets to update. - /// The method being wrapped. - /// Returns whether to execute the original method. - 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/Patches/LoadErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs index 52d5f5a1..e14dc662 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs @@ -2,11 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -#if HARMONY_2 using HarmonyLib; -#else -using Harmony; -#endif using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Patching; using StardewValley; @@ -45,11 +41,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /// -#if HARMONY_2 public void Apply(Harmony harmony) -#else - public void Apply(HarmonyInstance harmony) -#endif { harmony.Patch( original: AccessTools.Method(typeof(SaveGame), nameof(SaveGame.loadDataToLocations)), diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs index 9f8a98cd..a68e359c 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs @@ -1,16 +1,11 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using HarmonyLib; using StardewModdingAPI.Framework.Patching; using StardewValley; using StardewValley.Menus; using SObject = StardewValley.Object; -#if HARMONY_2 -using System; -using HarmonyLib; -#else -using System.Reflection; -using Harmony; -#endif namespace StardewModdingAPI.Mods.ErrorHandler.Patches { @@ -24,11 +19,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches ** Public methods *********/ /// -#if HARMONY_2 public void Apply(Harmony harmony) -#else - public void Apply(HarmonyInstance harmony) -#endif { // object.getDescription harmony.Patch( @@ -39,11 +30,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches // object.getDisplayName harmony.Patch( original: AccessTools.Method(typeof(SObject), "loadDisplayName"), -#if HARMONY_2 finalizer: new HarmonyMethod(this.GetType(), nameof(ObjectErrorPatch.Finalize_Object_loadDisplayName)) -#else - prefix: new HarmonyMethod(this.GetType(), nameof(ObjectErrorPatch.Before_Object_loadDisplayName)) -#endif ); // IClickableMenu.drawToolTip @@ -73,7 +60,6 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return true; } -#if HARMONY_2 /// The method to call after . /// The patched method's return value. /// The exception thrown by the wrapped method, if any. @@ -88,38 +74,6 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return __exception; } -#else - /// The method to call instead of . - /// The instance being patched. - /// The patched method's return value. - /// The method being wrapped. - /// Returns whether to execute the original method. - private static bool Before_Object_loadDisplayName(SObject __instance, ref string __result, MethodInfo __originalMethod) - { - const string key = nameof(ObjectErrorPatch.Before_Object_loadDisplayName); - if (!PatchHelper.StartIntercept(key)) - return true; - - try - { - __result = (string)__originalMethod.Invoke(__instance, new object[0]); - return false; - } - catch (TargetInvocationException ex) when (ex.InnerException is KeyNotFoundException) - { - __result = "???"; - return false; - } - catch - { - return true; - } - finally - { - PatchHelper.StopIntercept(key); - } - } -#endif /// The method to call instead of . /// The item for which to draw a tooltip. diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs index d2a5e988..d2e332b4 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs @@ -1,15 +1,10 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using StardewModdingAPI.Framework.Patching; -using StardewValley; -#if HARMONY_2 -using System; using HarmonyLib; using StardewModdingAPI.Framework; -#else -using System.Reflection; -using Harmony; -#endif +using StardewModdingAPI.Framework.Patching; +using StardewValley; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { @@ -37,19 +32,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches } /// -#if HARMONY_2 public void Apply(Harmony harmony) -#else - public void Apply(HarmonyInstance harmony) -#endif { harmony.Patch( original: AccessTools.Method(typeof(NPC), nameof(NPC.parseMasterSchedule)), -#if HARMONY_2 finalizer: new HarmonyMethod(this.GetType(), nameof(ScheduleErrorPatch.Finalize_NPC_parseMasterSchedule)) -#else - prefix: new HarmonyMethod(this.GetType(), nameof(ScheduleErrorPatch.Before_NPC_parseMasterSchedule)) -#endif ); } @@ -57,7 +44,6 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches /********* ** Private methods *********/ -#if HARMONY_2 /// The method to call instead of . /// The raw schedule data to parse. /// The instance being patched. @@ -74,35 +60,5 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches return null; } -#else - /// The method to call instead of . - /// The raw schedule data to parse. - /// The instance being patched. - /// The patched method's return value. - /// The method being wrapped. - /// Returns whether to execute the original method. - private static bool Before_NPC_parseMasterSchedule(string rawData, NPC __instance, ref Dictionary __result, MethodInfo __originalMethod) - { - const string key = nameof(ScheduleErrorPatch.Before_NPC_parseMasterSchedule); - if (!PatchHelper.StartIntercept(key)) - return true; - - try - { - __result = (Dictionary)__originalMethod.Invoke(__instance, new object[] { rawData }); - return false; - } - catch (TargetInvocationException ex) - { - ScheduleErrorPatch.MonitorForGame.Log($"Failed parsing schedule for NPC {__instance.Name}:\n{rawData}\n{ex.InnerException ?? ex}", LogLevel.Error); - __result = new Dictionary(); - return false; - } - finally - { - PatchHelper.StopIntercept(key); - } - } -#endif } } diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs index 8056fd71..7fdf5a48 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs @@ -1,10 +1,6 @@ -#if HARMONY_2 -using HarmonyLib; -#else -using Harmony; -#endif using System; using System.Diagnostics.CodeAnalysis; +using HarmonyLib; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.Framework.Patching; @@ -20,11 +16,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches ** Public methods *********/ /// -#if HARMONY_2 public void Apply(Harmony harmony) -#else - public void Apply(HarmonyInstance harmony) -#endif { harmony.Patch( original: Constants.GameFramework == GameFramework.Xna diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/UtilityErrorPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/UtilityErrorPatches.cs index 1ddd407c..cd57736f 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/UtilityErrorPatches.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/UtilityErrorPatches.cs @@ -1,12 +1,6 @@ -#if HARMONY_2 -using System; -using HarmonyLib; -#else -using Harmony; -#endif using System; using System.Diagnostics.CodeAnalysis; -using System.Reflection; +using HarmonyLib; using StardewModdingAPI.Framework.Patching; using StardewValley; @@ -22,7 +16,6 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches ** Public methods *********/ /// -#if HARMONY_2 public void Apply(Harmony harmony) { harmony.Patch( @@ -30,21 +23,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches finalizer: new HarmonyMethod(this.GetType(), nameof(UtilityErrorPatches.Finalize_Utility_GetItemFromStandardTextDescription)) ); } -#else - public void Apply(HarmonyInstance harmony) - { - harmony.Patch( - original: AccessTools.Method(typeof(Utility), nameof(Utility.getItemFromStandardTextDescription)), - prefix: new HarmonyMethod(this.GetType(), nameof(UtilityErrorPatches.Before_Utility_GetItemFromStandardTextDescription)) - ); - } -#endif /********* ** Private methods *********/ -#if HARMONY_2 /// The method to call instead of . /// The item text description to parse. /// The delimiter by which to split the text description. @@ -56,34 +39,5 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches ? new FormatException($"Failed to parse item text description \"{description}\" with delimiter \"{delimiter}\".", __exception) : null; } -#else - /// The method to call instead of . - /// The return value of the original method. - /// The item text description to parse. - /// The player for which the item is being parsed. - /// The delimiter by which to split the text description. - /// The method being wrapped. - /// Returns whether to execute the original method. - private static bool Before_Utility_GetItemFromStandardTextDescription(ref Item __result, string description, Farmer who, char delimiter, MethodInfo __originalMethod) - { - const string key = nameof(UtilityErrorPatches.Before_Utility_GetItemFromStandardTextDescription); - if (!PatchHelper.StartIntercept(key)) - return true; - - try - { - __result = (Item)__originalMethod.Invoke(null, new object[] { description, who, delimiter }); - return false; - } - catch (TargetInvocationException ex) - { - throw new FormatException($"Failed to parse item text description \"{description}\" with delimiter \"{delimiter}\".", ex.InnerException); - } - finally - { - PatchHelper.StopIntercept(key); - } - } -#endif } } -- cgit From 6a6c484b9867524d2eaa617da4dde36fec8c3110 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 28 Jul 2021 00:49:54 -0400 Subject: add accessed key to dictionary KeyNotFoundException message --- .../Patches/DictionaryPatches.cs | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs (limited to 'src/SMAPI.Mods.ErrorHandler/Patches') diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs new file mode 100644 index 00000000..ce88999d --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Framework.Patching; +using StardewValley.GameData; +using StardewValley.GameData.HomeRenovations; +using StardewValley.GameData.Movies; + +namespace StardewModdingAPI.Mods.ErrorHandler.Patches +{ + /// A Harmony patch for which adds the accessed key to exceptions. + /// 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 DictionaryPatches : IHarmonyPatch + { + /********* + ** Fields + *********/ + /// Simplifies access to private code. + private static IReflectionHelper Reflection; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Simplifies access to private code. + public DictionaryPatches(IReflectionHelper reflector) + { + DictionaryPatches.Reflection = reflector; + } + + /// + public void Apply(Harmony harmony) + { + Type[] keyTypes = { typeof(int), typeof(string) }; + Type[] valueTypes = { typeof(int), typeof(string), typeof(HomeRenovation), typeof(MovieData), typeof(SpecialOrderData) }; + + foreach (Type keyType in keyTypes) + { + foreach (Type valueType in valueTypes) + { + Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + harmony.Patch( + original: AccessTools.Method(dictionaryType, "get_Item"), + finalizer: new HarmonyMethod(this.GetType(), nameof(DictionaryPatches.Finalize_GetItem)) + ); + } + } + } + + + /********* + ** Private methods + *********/ + /// The method to call after the dictionary indexer throws an exception. + /// The dictionary key being fetched. + /// The exception thrown by the wrapped method, if any. + /// Returns the exception to throw, if any. + private static Exception Finalize_GetItem(object key, Exception __exception) + { + if (__exception is KeyNotFoundException) + AddKeyTo(__exception, key?.ToString()); + + return __exception; + } + + /// Add the accessed key to an exception message. + /// The exception to modify. + /// The dictionary key. + private static void AddKeyTo(Exception exception, string key) + { + DictionaryPatches.Reflection + .GetField(exception, "_message") + .SetValue($"{exception.Message}\nkey: '{key}'"); + } + } +} -- cgit From 737a434ad6704d22776f463c192b9a9748ce2a21 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 29 Jul 2021 22:50:50 -0400 Subject: reduce ErrorHandler's direct references to internal SMAPI code That will allow removing the InternalsVisibleTo attribute to avoid namespace conflicts in an upcoming commit. --- src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/SMAPI.Mods.ErrorHandler/Patches') diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs index e14dc662..cc0e5e52 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using HarmonyLib; +using Microsoft.Xna.Framework.Content; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Patching; using StardewValley; @@ -82,7 +83,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches { BluePrint _ = new BluePrint(building.buildingType.Value); } - catch (SContentLoadException) + catch (ContentLoadException) { LoadErrorPatch.Monitor.Log($"Removed invalid building type '{building.buildingType.Value}' in {location.Name} ({building.tileX}, {building.tileY}) to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom building mod?)", LogLevel.Warn); location.buildings.Remove(building); -- cgit 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 --- .../Patches/DialogueErrorPatch.cs | 21 ----------- .../Patches/IClickableMenuPatcher.cs | 44 ++++++++++++++++++++++ .../Patches/ObjectErrorPatch.cs | 19 ---------- .../Patches/ScheduleErrorPatch.cs | 21 +++++++++++ 4 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/IClickableMenuPatcher.cs (limited to 'src/SMAPI.Mods.ErrorHandler/Patches') 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. -- cgit From 4074f697d73f5cac6699836550b144fd0c4e2803 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 30 Jul 2021 00:40:12 -0400 Subject: rename patch classes for consistency --- .../Patches/DialogueErrorPatch.cs | 75 ----------- .../Patches/DialoguePatcher.cs | 75 +++++++++++ .../Patches/DictionaryPatcher.cs | 81 ++++++++++++ .../Patches/DictionaryPatches.cs | 81 ------------ .../Patches/EventPatcher.cs | 52 ++++++++ .../Patches/EventPatches.cs | 52 -------- .../Patches/GameLocationPatcher.cs | 79 ++++++++++++ .../Patches/GameLocationPatches.cs | 79 ------------ .../Patches/IClickableMenuPatcher.cs | 4 +- .../Patches/LoadErrorPatch.cs | 143 --------------------- src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs | 85 ++++++++++++ .../Patches/ObjectErrorPatch.cs | 71 ---------- .../Patches/ObjectPatcher.cs | 71 ++++++++++ .../Patches/SaveGamePatcher.cs | 142 ++++++++++++++++++++ .../Patches/ScheduleErrorPatch.cs | 85 ------------ .../Patches/SpriteBatchPatcher.cs | 46 +++++++ .../Patches/SpriteBatchValidationPatches.cs | 46 ------- .../Patches/UtilityErrorPatches.cs | 43 ------- .../Patches/UtilityPatcher.cs | 43 +++++++ 19 files changed, 676 insertions(+), 677 deletions(-) delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatcher.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/LoadErrorPatch.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/NpcPatcher.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/ObjectErrorPatch.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/ObjectPatcher.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/SaveGamePatcher.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/ScheduleErrorPatch.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchPatcher.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs delete mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/UtilityErrorPatches.cs create mode 100644 src/SMAPI.Mods.ErrorHandler/Patches/UtilityPatcher.cs (limited to 'src/SMAPI.Mods.ErrorHandler/Patches') diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs deleted file mode 100644 index ca5bb0bf..00000000 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DialogueErrorPatch.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using HarmonyLib; -using StardewModdingAPI.Framework; -using StardewModdingAPI.Framework.Patching; -using StardewValley; - -namespace StardewModdingAPI.Mods.ErrorHandler.Patches -{ - /// A Harmony patch for the constructor which intercepts invalid dialogue lines 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 DialogueErrorPatch : IHarmonyPatch - { - /********* - ** Fields - *********/ - /// Writes messages to the console and log file on behalf of the game. - private static IMonitor MonitorForGame; - - /// Simplifies access to private code. - private static IReflectionHelper Reflection; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// Writes messages to the console and log file on behalf of the game. - /// Simplifies access to private code. - public DialogueErrorPatch(IMonitor monitorForGame, IReflectionHelper reflector) - { - DialogueErrorPatch.MonitorForGame = monitorForGame; - DialogueErrorPatch.Reflection = reflector; - } - - /// - public void Apply(Harmony harmony) - { - harmony.Patch( - original: AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }), - finalizer: new HarmonyMethod(this.GetType(), nameof(DialogueErrorPatch.Finalize_Dialogue_Constructor)) - ); - } - - - /********* - ** Private methods - *********/ - /// The method to call after the Dialogue constructor. - /// The instance being patched. - /// The dialogue being parsed. - /// The NPC for which the dialogue is being parsed. - /// The exception thrown by the wrapped method, if any. - /// Returns the exception to throw, if any. - private static Exception Finalize_Dialogue_Constructor(Dialogue __instance, string masterDialogue, NPC speaker, Exception __exception) - { - if (__exception != null) - { - // log message - string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; - DialogueErrorPatch.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{__exception.GetLogSummary()}", LogLevel.Error); - - // set default dialogue - IReflectedMethod parseDialogueString = DialogueErrorPatch.Reflection.GetMethod(__instance, "parseDialogueString"); - IReflectedMethod checkForSpecialDialogueAttributes = DialogueErrorPatch.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); - parseDialogueString.Invoke("..."); - checkForSpecialDialogueAttributes.Invoke(); - } - - return null; - } - } -} diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs new file mode 100644 index 00000000..7b730ee5 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DialoguePatcher.cs @@ -0,0 +1,75 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Patching; +using StardewValley; + +namespace StardewModdingAPI.Mods.ErrorHandler.Patches +{ + /// A Harmony patch for the constructor which intercepts invalid dialogue lines 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 DialoguePatcher : IHarmonyPatch + { + /********* + ** Fields + *********/ + /// Writes messages to the console and log file on behalf of the game. + private static IMonitor MonitorForGame; + + /// Simplifies access to private code. + private static IReflectionHelper Reflection; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Writes messages to the console and log file on behalf of the game. + /// Simplifies access to private code. + public DialoguePatcher(IMonitor monitorForGame, IReflectionHelper reflector) + { + DialoguePatcher.MonitorForGame = monitorForGame; + DialoguePatcher.Reflection = reflector; + } + + /// + public void Apply(Harmony harmony) + { + harmony.Patch( + original: AccessTools.Constructor(typeof(Dialogue), new[] { typeof(string), typeof(NPC) }), + finalizer: new HarmonyMethod(this.GetType(), nameof(DialoguePatcher.Finalize_Dialogue_Constructor)) + ); + } + + + /********* + ** Private methods + *********/ + /// The method to call after the Dialogue constructor. + /// The instance being patched. + /// The dialogue being parsed. + /// The NPC for which the dialogue is being parsed. + /// The exception thrown by the wrapped method, if any. + /// Returns the exception to throw, if any. + private static Exception Finalize_Dialogue_Constructor(Dialogue __instance, string masterDialogue, NPC speaker, Exception __exception) + { + if (__exception != null) + { + // log message + string name = !string.IsNullOrWhiteSpace(speaker?.Name) ? speaker.Name : null; + DialoguePatcher.MonitorForGame.Log($"Failed parsing dialogue string{(name != null ? $" for {name}" : "")}:\n{masterDialogue}\n{__exception.GetLogSummary()}", LogLevel.Error); + + // set default dialogue + IReflectedMethod parseDialogueString = DialoguePatcher.Reflection.GetMethod(__instance, "parseDialogueString"); + IReflectedMethod checkForSpecialDialogueAttributes = DialoguePatcher.Reflection.GetMethod(__instance, "checkForSpecialDialogueAttributes"); + parseDialogueString.Invoke("..."); + checkForSpecialDialogueAttributes.Invoke(); + } + + return null; + } + } +} diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs new file mode 100644 index 00000000..3c5240b6 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Framework.Patching; +using StardewValley.GameData; +using StardewValley.GameData.HomeRenovations; +using StardewValley.GameData.Movies; + +namespace StardewModdingAPI.Mods.ErrorHandler.Patches +{ + /// A Harmony patch for which adds the accessed key to exceptions. + /// 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 DictionaryPatcher : IHarmonyPatch + { + /********* + ** Fields + *********/ + /// Simplifies access to private code. + private static IReflectionHelper Reflection; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Simplifies access to private code. + public DictionaryPatcher(IReflectionHelper reflector) + { + DictionaryPatcher.Reflection = reflector; + } + + /// + public void Apply(Harmony harmony) + { + Type[] keyTypes = { typeof(int), typeof(string) }; + Type[] valueTypes = { typeof(int), typeof(string), typeof(HomeRenovation), typeof(MovieData), typeof(SpecialOrderData) }; + + foreach (Type keyType in keyTypes) + { + foreach (Type valueType in valueTypes) + { + Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + harmony.Patch( + original: AccessTools.Method(dictionaryType, "get_Item"), + finalizer: new HarmonyMethod(this.GetType(), nameof(DictionaryPatcher.Finalize_GetItem)) + ); + } + } + } + + + /********* + ** Private methods + *********/ + /// The method to call after the dictionary indexer throws an exception. + /// The dictionary key being fetched. + /// The exception thrown by the wrapped method, if any. + /// Returns the exception to throw, if any. + private static Exception Finalize_GetItem(object key, Exception __exception) + { + if (__exception is KeyNotFoundException) + AddKeyTo(__exception, key?.ToString()); + + return __exception; + } + + /// Add the accessed key to an exception message. + /// The exception to modify. + /// The dictionary key. + private static void AddKeyTo(Exception exception, string key) + { + DictionaryPatcher.Reflection + .GetField(exception, "_message") + .SetValue($"{exception.Message}\nkey: '{key}'"); + } + } +} diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs deleted file mode 100644 index ce88999d..00000000 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatches.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using HarmonyLib; -using StardewModdingAPI.Framework.Patching; -using StardewValley.GameData; -using StardewValley.GameData.HomeRenovations; -using StardewValley.GameData.Movies; - -namespace StardewModdingAPI.Mods.ErrorHandler.Patches -{ - /// A Harmony patch for which adds the accessed key to exceptions. - /// 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 DictionaryPatches : IHarmonyPatch - { - /********* - ** Fields - *********/ - /// Simplifies access to private code. - private static IReflectionHelper Reflection; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// Simplifies access to private code. - public DictionaryPatches(IReflectionHelper reflector) - { - DictionaryPatches.Reflection = reflector; - } - - /// - public void Apply(Harmony harmony) - { - Type[] keyTypes = { typeof(int), typeof(string) }; - Type[] valueTypes = { typeof(int), typeof(string), typeof(HomeRenovation), typeof(MovieData), typeof(SpecialOrderData) }; - - foreach (Type keyType in keyTypes) - { - foreach (Type valueType in valueTypes) - { - Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); - - harmony.Patch( - original: AccessTools.Method(dictionaryType, "get_Item"), - finalizer: new HarmonyMethod(this.GetType(), nameof(DictionaryPatches.Finalize_GetItem)) - ); - } - } - } - - - /********* - ** Private methods - *********/ - /// The method to call after the dictionary indexer throws an exception. - /// The dictionary key being fetched. - /// The exception thrown by the wrapped method, if any. - /// Returns the exception to throw, if any. - private static Exception Finalize_GetItem(object key, Exception __exception) - { - if (__exception is KeyNotFoundException) - AddKeyTo(__exception, key?.ToString()); - - return __exception; - } - - /// Add the accessed key to an exception message. - /// The exception to modify. - /// The dictionary key. - private static void AddKeyTo(Exception exception, string key) - { - DictionaryPatches.Reflection - .GetField(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 new file mode 100644 index 00000000..9a7b34d8 --- /dev/null +++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatcher.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Framework.Patching; +using StardewValley; + +namespace StardewModdingAPI.Mods.ErrorHandler.Patches +{ + /// Harmony patches for which intercept errors to log more details. + /// 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 EventPatcher : IHarmonyPatch + { + /********* + ** Fields + *********/ + /// Writes messages to the console and log file on behalf of the game. + private static IMonitor MonitorForGame; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// Writes messages to the console and log file on behalf of the game. + public EventPatcher(IMonitor monitorForGame) + { + EventPatcher.MonitorForGame = monitorForGame; + } + + /// + public void Apply(Harmony harmony) + { + harmony.Patch( + original: AccessTools.Method(typeof(Event), nameof(Event.LogErrorAndHalt)), + postfix: new HarmonyMethod(this.GetType(), nameof(EventPatcher.After_Event_LogErrorAndHalt)) + ); + } + + + /********* + ** Private methods + *********/ + /// The method to call after . + /// The exception being logged. + private static void After_Event_LogErrorAndHalt(Exception e) + { + EventPatcher.MonitorForGame.Log(e.ToString(), LogLevel.Error); + } + } +} diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs deleted file mode 100644 index 390aa778..00000000 --- a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using HarmonyLib; -using StardewModdingAPI.Framework.Patching; -using StardewValley; - -namespace StardewModdingAPI.Mods.ErrorHandler.Patches -{ - /// Harmony patches for which intercept errors to log more details. - /// 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 EventPatches : IHarmonyPatch - { - /********* - ** Fields - *********/ - /// Writes messages to the console and log file on behalf of the game. - private static IMonitor MonitorForGame; - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// Writes messages to the console and log file on behalf of the game. - public EventPatches(IMonitor monitorForGame) - { - EventPatches.MonitorForGame = monitorForGame; - } - - /// - public void Apply(Harmony harmony) - { - harmony.Patch( - original: AccessTools.Method(typeof(Event), nameof(Event.LogErrorAndHalt)), - postfix: new HarmonyMethod(this.GetType(), nameof(EventPatches.After_Event_LogErrorAndHalt)) - ); - } - - - /********* - ** Private methods - *********/ - /// The method to call after . - /// The exception being logged. -