temporarily restore Harmony 1.x support with compile flag (#711)
This commit is contained in:
parent
6d1cd7d9b8
commit
dcd2c647a2
|
@ -1,7 +1,12 @@
|
|||
← [README](README.md)
|
||||
|
||||
# Release notes
|
||||
## Upcoming released
|
||||
## Upcoming release + 1
|
||||
* For modders:
|
||||
* Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).
|
||||
* Added `harmony_summary` console command which lists all current Harmony patches, optionally with a search filter.
|
||||
|
||||
## Upcoming release
|
||||
* For players:
|
||||
* Mod warnings are now listed alphabetically.
|
||||
* MacOS files starting with `._` are now ignored and can no longer cause skipped mods.
|
||||
|
@ -17,12 +22,10 @@
|
|||
* Internal changes to improve performance and reliability.
|
||||
|
||||
* For modders:
|
||||
* Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info).
|
||||
* Added [event priorities](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Events#Custom_priority) (thanks to spacechase0!).
|
||||
* Added [update subkeys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Update_checks#Update_subkeys).
|
||||
* Added `Multiplayer.PeerConnected` event.
|
||||
* Added ability to override update keys from the compatibility list.
|
||||
* Added `harmony_summary` console command which lists all current Harmony patches, optionally with a search filter.
|
||||
* Harmony mods which use the `[HarmonyPatch(type)]` attribute now work crossplatform. Previously SMAPI couldn't rewrite types in custom attributes for compatibility.
|
||||
* Improved mod rewriting for compatibility:
|
||||
* Fixed rewriting types in custom attributes.
|
||||
|
@ -33,6 +36,7 @@
|
|||
* Fixed `ModMessageReceived` event handlers not tracked for performance monitoring.
|
||||
|
||||
* For SMAPI developers:
|
||||
* Added support for bundling a custom Harmony build for upcoming use.
|
||||
* Eliminated MongoDB storage in the web services, which complicated the code unnecessarily. The app still uses an abstract interface for storage, so we can wrap a distributed cache in the future if needed.
|
||||
* Overhauled update checks to simplify individual clients, centralize common logic, and enable upcoming features.
|
||||
* Merged the separate legacy redirects app on AWS into the main app on Azure.
|
||||
|
|
|
@ -58,6 +58,7 @@ SMAPI uses a small number of conditional compilation constants, which you can se
|
|||
flag | purpose
|
||||
---- | -------
|
||||
`SMAPI_FOR_WINDOWS` | Whether SMAPI is being compiled on Windows for players on Windows. Set automatically in `crossplatform.targets`.
|
||||
`HARMONY_2` | Whether to enable experimental Harmony 2.0 support. Existing Harmony 1._x_ mods will be rewritten automatically for compatibility.
|
||||
|
||||
## For SMAPI developers
|
||||
### Compiling from source
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#if HARMONY_2
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -166,3 +167,4 @@ namespace StardewModdingAPI.Framework.Commands
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#if HARMONY_2
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
@ -40,3 +41,4 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#if HARMONY_2
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
@ -80,3 +81,4 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#if HARMONY_2
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
@ -43,3 +44,4 @@ namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#if HARMONY_2
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using Mono.Cecil;
|
||||
|
@ -125,3 +126,4 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
using System;
|
||||
#if HARMONY_2
|
||||
using HarmonyLib;
|
||||
#else
|
||||
using Harmony;
|
||||
#endif
|
||||
|
||||
namespace StardewModdingAPI.Framework.Patching
|
||||
{
|
||||
|
@ -27,7 +31,11 @@ namespace StardewModdingAPI.Framework.Patching
|
|||
/// <param name="patches">The patches to apply.</param>
|
||||
public void Apply(params IHarmonyPatch[] patches)
|
||||
{
|
||||
#if HARMONY_2
|
||||
Harmony harmony = new Harmony("SMAPI");
|
||||
#else
|
||||
HarmonyInstance harmony = HarmonyInstance.Create("SMAPI");
|
||||
#endif
|
||||
foreach (IHarmonyPatch patch in patches)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
#if HARMONY_2
|
||||
using HarmonyLib;
|
||||
#else
|
||||
using Harmony;
|
||||
#endif
|
||||
|
||||
namespace StardewModdingAPI.Framework.Patching
|
||||
{
|
||||
|
@ -10,6 +14,10 @@ namespace StardewModdingAPI.Framework.Patching
|
|||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#if HARMONY_2
|
||||
void Apply(Harmony harmony);
|
||||
#else
|
||||
void Apply(HarmonyInstance harmony);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
#if !HARMONY_2
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Patching
|
||||
{
|
||||
/// <summary>Provides generic methods for implementing Harmony patches.</summary>
|
||||
internal class PatchHelper
|
||||
{
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The interception keys currently being intercepted.</summary>
|
||||
private static readonly HashSet<string> InterceptingKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Track a method that will be intercepted.</summary>
|
||||
/// <param name="key">The intercept key.</param>
|
||||
/// <returns>Returns false if the method was already marked for interception, else true.</returns>
|
||||
public static bool StartIntercept(string key)
|
||||
{
|
||||
return PatchHelper.InterceptingKeys.Add(key);
|
||||
}
|
||||
|
||||
/// <summary>Track a method as no longer being intercepted.</summary>
|
||||
/// <param name="key">The intercept key.</param>
|
||||
public static void StopIntercept(string key)
|
||||
{
|
||||
PatchHelper.InterceptingKeys.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -511,7 +511,9 @@ namespace StardewModdingAPI.Framework
|
|||
this.Monitor.Log("Type 'help' for help, or 'help <cmd>' for a command's usage", LogLevel.Info);
|
||||
this.GameInstance.CommandManager
|
||||
.Add(new HelpCommand(this.GameInstance.CommandManager), this.Monitor)
|
||||
#if HARMONY_2
|
||||
.Add(new HarmonySummaryCommand(), this.Monitor)
|
||||
#endif
|
||||
.Add(new ReloadI18nCommand(this.ReloadTranslations), this.Monitor);
|
||||
|
||||
// start handling command line input
|
||||
|
|
|
@ -38,8 +38,10 @@ namespace StardewModdingAPI.Metadata
|
|||
// rewrite for Stardew Valley 1.3
|
||||
yield return new StaticFieldToConstantRewriter<int>(typeof(Game1), "tileSize", Game1.tileSize);
|
||||
|
||||
#if HARMONY_2
|
||||
// rewrite for SMAPI 3.6 (Harmony 1.x => 2.0 update)
|
||||
yield return new Harmony1AssemblyRewriter();
|
||||
#endif
|
||||
|
||||
/****
|
||||
** detect mod issues
|
||||
|
@ -51,7 +53,11 @@ namespace StardewModdingAPI.Metadata
|
|||
/****
|
||||
** detect code which may impact game stability
|
||||
****/
|
||||
#if HARMONY_2
|
||||
yield return new TypeFinder(typeof(HarmonyLib.Harmony).FullName, InstructionHandleResult.DetectedGamePatch);
|
||||
#else
|
||||
yield return new TypeFinder(typeof(Harmony.HarmonyInstance).FullName, InstructionHandleResult.DetectedGamePatch);
|
||||
#endif
|
||||
yield return new TypeFinder("System.Runtime.CompilerServices.CallSite", InstructionHandleResult.DetectedDynamic);
|
||||
yield return new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.serializer), InstructionHandleResult.DetectedSaveSerializer);
|
||||
yield return new FieldFinder(typeof(SaveGame).FullName, nameof(SaveGame.farmerSerializer), InstructionHandleResult.DetectedSaveSerializer);
|
||||
|
|
|
@ -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
|
|||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#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
|
||||
/// <summary>The method to call after the Dialogue constructor.</summary>
|
||||
/// <param name="__instance">The instance being patched.</param>
|
||||
/// <param name="masterDialogue">The dialogue being parsed.</param>
|
||||
|
@ -102,5 +121,74 @@ namespace StardewModdingAPI.Patches
|
|||
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
|
||||
/// <summary>The method to call instead of the Dialogue constructor.</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>
|
||||
/// <returns>Returns whether to execute the original method.</returns>
|
||||
private static bool Before_Dialogue_Constructor(Dialogue __instance, string masterDialogue, NPC speaker)
|
||||
{
|
||||
// get private members
|
||||
bool nameArraysTranslated = DialogueErrorPatch.Reflection.GetField<bool>(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<List<string>> dialogues = DialogueErrorPatch.Reflection.GetField<List<string>>(__instance, "dialogues");
|
||||
|
||||
// replicate base constructor
|
||||
if (dialogues.GetValue() == null)
|
||||
dialogues.SetValue(new List<string>());
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>The method to call instead of <see cref="NPC.CurrentDialogue"/>.</summary>
|
||||
/// <param name="__instance">The instance being patched.</param>
|
||||
/// <param name="__result">The return value of the original method.</param>
|
||||
/// <param name="__originalMethod">The method being wrapped.</param>
|
||||
/// <returns>Returns whether to execute the original method.</returns>
|
||||
private static bool Before_NPC_CurrentDialogue(NPC __instance, ref Stack<Dialogue> __result, MethodInfo __originalMethod)
|
||||
{
|
||||
const string key = nameof(Before_NPC_CurrentDialogue);
|
||||
if (!PatchHelper.StartIntercept(key))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
__result = (Stack<Dialogue>)__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<Dialogue>();
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
PatchHelper.StopIntercept(key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#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
|
||||
/// <summary>The method to call instead of the GameLocation.CheckEventPrecondition.</summary>
|
||||
/// <param name="__result">The return value of the original method.</param>
|
||||
/// <param name="precondition">The precondition to be parsed.</param>
|
||||
|
@ -65,5 +81,35 @@ namespace StardewModdingAPI.Patches
|
|||
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
/// <summary>The method to call instead of the GameLocation.CheckEventPrecondition.</summary>
|
||||
/// <param name="__instance">The instance being patched.</param>
|
||||
/// <param name="__result">The return value of the original method.</param>
|
||||
/// <param name="precondition">The precondition to be parsed.</param>
|
||||
/// <param name="__originalMethod">The method being wrapped.</param>
|
||||
/// <returns>Returns whether to execute the original method.</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#if HARMONY_2
|
||||
public void Apply(Harmony harmony)
|
||||
#else
|
||||
public void Apply(HarmonyInstance harmony)
|
||||
#endif
|
||||
{
|
||||
// detect CreatedBasicInfo
|
||||
harmony.Patch(
|
||||
|
|
|
@ -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
|
|||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#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)),
|
||||
|
|
|
@ -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
|
|||
*********/
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#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
|
||||
/// <summary>The method to call after <see cref="StardewValley.Object.loadDisplayName"/>.</summary>
|
||||
/// <param name="__result">The patched method's return value.</param>
|
||||
/// <param name="__exception">The exception thrown by the wrapped method, if any.</param>
|
||||
|
@ -82,6 +96,38 @@ namespace StardewModdingAPI.Patches
|
|||
|
||||
return __exception;
|
||||
}
|
||||
#else
|
||||
/// <summary>The method to call instead of <see cref="StardewValley.Object.loadDisplayName"/>.</summary>
|
||||
/// <param name="__instance">The instance being patched.</param>
|
||||
/// <param name="__result">The patched method's return value.</param>
|
||||
/// <param name="__originalMethod">The method being wrapped.</param>
|
||||
/// <returns>Returns whether to execute the original method.</returns>
|
||||
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
|
||||
|
||||
/// <summary>The method to call instead of <see cref="IClickableMenu.drawToolTip"/>.</summary>
|
||||
/// <param name="hoveredItem">The item for which to draw a tooltip.</param>
|
||||
|
|
|
@ -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
|
|||
|
||||
/// <summary>Apply the Harmony patch.</summary>
|
||||
/// <param name="harmony">The Harmony instance.</param>
|
||||
#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
|
||||
/// <summary>The method to call instead of <see cref="NPC.parseMasterSchedule"/>.</summary>
|
||||
/// <param name="rawData">The raw schedule data to parse.</param>
|
||||
/// <param name="__instance">The instance being patched.</param>
|
||||
|
@ -68,5 +82,35 @@ namespace StardewModdingAPI.Patches
|
|||
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
/// <summary>The method to call instead of <see cref="NPC.parseMasterSchedule"/>.</summary>
|
||||
/// <param name="rawData">The raw schedule data to parse.</param>
|
||||
/// <param name="__instance">The instance being patched.</param>
|
||||
/// <param name="__result">The patched method's return value.</param>
|
||||
/// <param name="__originalMethod">The method being wrapped.</param>
|
||||
/// <returns>Returns whether to execute the original method.</returns>
|
||||
private static bool Before_NPC_parseMasterSchedule(string rawData, NPC __instance, ref Dictionary<int, SchedulePathDescription> __result, MethodInfo __originalMethod)
|
||||
{
|
||||
const string key = nameof(Before_NPC_parseMasterSchedule);
|
||||
if (!PatchHelper.StartIntercept(key))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
__result = (Dictionary<int, SchedulePathDescription>)__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<int, SchedulePathDescription>();
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
PatchHelper.StopIntercept(key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LargeAddressAware" Version="1.0.4" />
|
||||
<PackageReference Include="Lib.Harmony" Version="2.0.0.10" Condition="!Exists('..\..\build\0Harmony.dll')" />
|
||||
<PackageReference Include="Lib.Harmony" Version="1.2.0.1" Condition="!Exists('..\..\build\0Harmony.dll')" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Platonymous.TMXTile" Version="1.3.8" />
|
||||
|
|
Loading…
Reference in New Issue