From dcd2c647a2abd836e8ee20f8ddad6568c9b4fbf2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 15 Jun 2020 22:17:32 -0400 Subject: temporarily restore Harmony 1.x support with compile flag (#711) --- src/SMAPI/Patches/DialogueErrorPatch.cs | 94 +++++++++++++++++++++++++++++++-- src/SMAPI/Patches/EventErrorPatch.cs | 48 ++++++++++++++++- src/SMAPI/Patches/LoadContextPatch.cs | 8 +++ src/SMAPI/Patches/LoadErrorPatch.cs | 8 +++ src/SMAPI/Patches/ObjectErrorPatch.cs | 50 +++++++++++++++++- src/SMAPI/Patches/ScheduleErrorPatch.cs | 50 ++++++++++++++++-- 6 files changed, 249 insertions(+), 9 deletions(-) (limited to 'src/SMAPI/Patches') diff --git a/src/SMAPI/Patches/DialogueErrorPatch.cs b/src/SMAPI/Patches/DialogueErrorPatch.cs index cddf29d6..8043eda3 100644 --- a/src/SMAPI/Patches/DialogueErrorPatch.cs +++ b/src/SMAPI/Patches/DialogueErrorPatch.cs @@ -1,11 +1,16 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using HarmonyLib; -using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Patching; using StardewModdingAPI.Framework.Reflection; using StardewValley; +#if HARMONY_2 +using HarmonyLib; +using StardewModdingAPI.Framework; +#else +using System.Reflection; +using Harmony; +#endif namespace StardewModdingAPI.Patches { @@ -47,6 +52,7 @@ namespace StardewModdingAPI.Patches /// Apply the Harmony patch. /// The Harmony instance. +#if HARMONY_2 public void Apply(Harmony harmony) { harmony.Patch( @@ -58,11 +64,24 @@ namespace StardewModdingAPI.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. @@ -102,5 +121,74 @@ namespace StardewModdingAPI.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"); + IReflectedField> dialogues = DialogueErrorPatch.Reflection.GetField>(__instance, "dialogues"); + + // replicate base constructor + if (dialogues.GetValue() == null) + dialogues.SetValue(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(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/Patches/EventErrorPatch.cs b/src/SMAPI/Patches/EventErrorPatch.cs index de9dea29..4dbb25f3 100644 --- a/src/SMAPI/Patches/EventErrorPatch.cs +++ b/src/SMAPI/Patches/EventErrorPatch.cs @@ -1,6 +1,11 @@ -using System; using System.Diagnostics.CodeAnalysis; +#if HARMONY_2 +using System; using HarmonyLib; +#else +using System.Reflection; +using Harmony; +#endif using StardewModdingAPI.Framework.Patching; using StardewValley; @@ -38,6 +43,7 @@ namespace StardewModdingAPI.Patches /// Apply the Harmony patch. /// The Harmony instance. +#if HARMONY_2 public void Apply(Harmony harmony) { harmony.Patch( @@ -45,11 +51,21 @@ namespace StardewModdingAPI.Patches 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 the GameLocation.CheckEventPrecondition. /// The return value of the original method. /// The precondition to be parsed. @@ -65,5 +81,35 @@ namespace StardewModdingAPI.Patches return null; } +#else + /// The method to call instead of the 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(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 } } diff --git a/src/SMAPI/Patches/LoadContextPatch.cs b/src/SMAPI/Patches/LoadContextPatch.cs index 9c707676..768ddd6b 100644 --- a/src/SMAPI/Patches/LoadContextPatch.cs +++ b/src/SMAPI/Patches/LoadContextPatch.cs @@ -1,6 +1,10 @@ using System; using System.Diagnostics.CodeAnalysis; +#if HARMONY_2 using HarmonyLib; +#else +using Harmony; +#endif using StardewModdingAPI.Enums; using StardewModdingAPI.Framework.Patching; using StardewModdingAPI.Framework.Reflection; @@ -47,7 +51,11 @@ namespace StardewModdingAPI.Patches /// Apply the Harmony patch. /// The Harmony instance. +#if HARMONY_2 public void Apply(Harmony harmony) +#else + public void Apply(HarmonyInstance harmony) +#endif { // detect CreatedBasicInfo harmony.Patch( diff --git a/src/SMAPI/Patches/LoadErrorPatch.cs b/src/SMAPI/Patches/LoadErrorPatch.cs index f8ad6693..5e67b169 100644 --- a/src/SMAPI/Patches/LoadErrorPatch.cs +++ b/src/SMAPI/Patches/LoadErrorPatch.cs @@ -2,7 +2,11 @@ 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; @@ -49,7 +53,11 @@ namespace StardewModdingAPI.Patches /// Apply the Harmony patch. /// The Harmony instance. +#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/Patches/ObjectErrorPatch.cs b/src/SMAPI/Patches/ObjectErrorPatch.cs index 189a14a0..4edcc64e 100644 --- a/src/SMAPI/Patches/ObjectErrorPatch.cs +++ b/src/SMAPI/Patches/ObjectErrorPatch.cs @@ -1,11 +1,16 @@ -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.Patches { @@ -27,7 +32,11 @@ namespace StardewModdingAPI.Patches *********/ /// Apply the Harmony patch. /// The Harmony instance. +#if HARMONY_2 public void Apply(Harmony harmony) +#else + public void Apply(HarmonyInstance harmony) +#endif { // object.getDescription harmony.Patch( @@ -38,7 +47,11 @@ namespace StardewModdingAPI.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 @@ -68,6 +81,7 @@ namespace StardewModdingAPI.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. @@ -82,6 +96,38 @@ namespace StardewModdingAPI.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(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/Patches/ScheduleErrorPatch.cs b/src/SMAPI/Patches/ScheduleErrorPatch.cs index df6ffab3..cc2238b0 100644 --- a/src/SMAPI/Patches/ScheduleErrorPatch.cs +++ b/src/SMAPI/Patches/ScheduleErrorPatch.cs @@ -1,10 +1,15 @@ -using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using HarmonyLib; -using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Patching; using StardewValley; +#if HARMONY_2 +using System; +using HarmonyLib; +using StardewModdingAPI.Framework; +#else +using System.Reflection; +using Harmony; +#endif namespace StardewModdingAPI.Patches { @@ -40,11 +45,19 @@ namespace StardewModdingAPI.Patches /// Apply the Harmony patch. /// The Harmony instance. +#if HARMONY_2 public void Apply(Harmony harmony) +#else + public void Apply(HarmonyInstance harmony) +#endif { harmony.Patch( original: AccessTools.Method(typeof(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 ); } @@ -52,6 +65,7 @@ namespace StardewModdingAPI.Patches /********* ** Private methods *********/ +#if HARMONY_2 /// The method to call instead of . /// The raw schedule data to parse. /// The instance being patched. @@ -68,5 +82,35 @@ namespace StardewModdingAPI.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(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 } } -- cgit