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