summaryrefslogtreecommitdiff
path: root/src/SMAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-07-30 00:54:15 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-07-30 00:54:15 -0400
commit948c800a98f00b1bdcfd05ee6228e3423f9eb465 (patch)
treeb958b3cdb4428fa8788a5698a207a45912923de7 /src/SMAPI
parent4074f697d73f5cac6699836550b144fd0c4e2803 (diff)
downloadSMAPI-948c800a98f00b1bdcfd05ee6228e3423f9eb465.tar.gz
SMAPI-948c800a98f00b1bdcfd05ee6228e3423f9eb465.tar.bz2
SMAPI-948c800a98f00b1bdcfd05ee6228e3423f9eb465.zip
migrate to the new Harmony patch pattern used in my mods
That improves validation and error-handling.
Diffstat (limited to 'src/SMAPI')
-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
15 files changed, 41 insertions, 123 deletions
diff --git a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
index 037d9f89..10488b84 100644
--- a/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
+++ b/src/SMAPI/Framework/Content/AssetInterceptorChange.cs
@@ -1,5 +1,6 @@
using System;
using System.Reflection;
+using StardewModdingAPI.Internal;
namespace StardewModdingAPI.Framework.Content
{
diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
index 80a9937a..63cd1759 100644
--- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs
@@ -9,6 +9,7 @@ using StardewModdingAPI.Framework.Content;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Framework.Utilities;
+using StardewModdingAPI.Internal;
using StardewValley;
using xTile;
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index bc5a8b74..d24ffb81 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using StardewModdingAPI.Framework.Exceptions;
using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Internal;
using StardewModdingAPI.Toolkit.Serialization;
using StardewModdingAPI.Toolkit.Utilities;
using StardewValley;
diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs
index 2204966c..fa20a079 100644
--- a/src/SMAPI/Framework/Events/ManagedEvent.cs
+++ b/src/SMAPI/Framework/Events/ManagedEvent.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using StardewModdingAPI.Events;
+using StardewModdingAPI.Internal;
namespace StardewModdingAPI.Framework.Events
{
diff --git a/src/SMAPI/Framework/InternalExtensions.cs b/src/SMAPI/Framework/InternalExtensions.cs
index ab7f1e6c..6c9a5f3b 100644
--- a/src/SMAPI/Framework/InternalExtensions.cs
+++ b/src/SMAPI/Framework/InternalExtensions.cs
@@ -14,6 +14,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Provides extension methods for SMAPI's internal use.</summary>
internal static class InternalExtensions
{
+ /*********
+ ** Public methods
+ *********/
/****
** IMonitor
****/
@@ -55,38 +58,6 @@ namespace StardewModdingAPI.Framework
}
/****
- ** Exceptions
- ****/
- /// <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;
- }
-
- /****
** ReaderWriterLockSlim
****/
/// <summary>Run code within a read lock.</summary>
diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs
index e16b5c0d..a3d4f23d 100644
--- a/src/SMAPI/Framework/Logging/LogManager.cs
+++ b/src/SMAPI/Framework/Logging/LogManager.cs
@@ -9,6 +9,7 @@ using System.Threading;
using StardewModdingAPI.Framework.Commands;
using StardewModdingAPI.Framework.Models;
using StardewModdingAPI.Framework.ModLoading;
+using StardewModdingAPI.Internal;
using StardewModdingAPI.Internal.ConsoleWriting;
using StardewModdingAPI.Toolkit.Framework.ModData;
using StardewModdingAPI.Toolkit.Utilities;
diff --git a/src/SMAPI/Framework/Patching/GamePatcher.cs b/src/SMAPI/Framework/Patching/GamePatcher.cs
deleted file mode 100644
index 3ce22ee9..00000000
--- a/src/SMAPI/Framework/Patching/GamePatcher.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using HarmonyLib;
-
-namespace StardewModdingAPI.Framework.Patching
-{
- /// <summary>Encapsulates applying Harmony patches to the game.</summary>
- internal class GamePatcher
- {
- /*********
- ** Fields
- *********/
- /// <summary>Encapsulates monitoring and logging.</summary>
- private readonly IMonitor Monitor;
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an instance.</summary>
- /// <param name="monitor">Encapsulates monitoring and logging.</param>
- public GamePatcher(IMonitor monitor)
- {
- this.Monitor = monitor;
- }
-
- /// <summary>Apply all loaded patches to the game.</summary>
- /// <param name="patches">The patches to apply.</param>
- public void Apply(params IHarmonyPatch[] patches)
- {
- Harmony harmony = new Harmony("SMAPI");
- foreach (IHarmonyPatch patch in patches)
- {
- try
- {
- patch.Apply(harmony);
- }
- catch (Exception ex)
- {
- this.Monitor.Log($"Couldn't apply runtime patch '{patch.GetType().Name}' to the game. Some SMAPI features may not work correctly. See log file for details.", LogLevel.Error);
- this.Monitor.Log(ex.GetLogSummary(), LogLevel.Trace);
- }
- }
- }
- }
-}
diff --git a/src/SMAPI/Framework/Patching/IHarmonyPatch.cs b/src/SMAPI/Framework/Patching/IHarmonyPatch.cs
deleted file mode 100644
index c1ff3040..00000000
--- a/src/SMAPI/Framework/Patching/IHarmonyPatch.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using HarmonyLib;
-
-namespace StardewModdingAPI.Framework.Patching
-{
- /// <summary>A Harmony patch to apply.</summary>
- internal interface IHarmonyPatch
- {
- /*********
- ** Methods
- *********/
- /// <summary>Apply the Harmony patch.</summary>
- /// <param name="harmony">The Harmony instance.</param>
- void Apply(Harmony harmony);
- }
-}
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 35db2da2..a34b3eff 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -31,13 +31,14 @@ using StardewModdingAPI.Framework.Models;
using StardewModdingAPI.Framework.ModHelpers;
using StardewModdingAPI.Framework.ModLoading;
using StardewModdingAPI.Framework.Networking;
-using StardewModdingAPI.Framework.Patching;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Framework.Rendering;
using StardewModdingAPI.Framework.Serialization;
using StardewModdingAPI.Framework.StateTracking.Comparers;
using StardewModdingAPI.Framework.StateTracking.Snapshots;
using StardewModdingAPI.Framework.Utilities;
+using StardewModdingAPI.Internal;
+using StardewModdingAPI.Internal.Patching;
using StardewModdingAPI.Patches;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
@@ -254,7 +255,7 @@ namespace StardewModdingAPI.Framework
// apply game patches
MiniMonoModHotfix.Apply();
- new GamePatcher(this.Monitor).Apply(
+ HarmonyPatcher.Apply("SMAPI", this.Monitor,
new Game1Patcher(this.Reflection, this.OnLoadStageChanged),
new TitleMenuPatcher(this.OnLoadStageChanged)
);
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index af7fa387..55ab8377 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -11,6 +11,7 @@ using StardewModdingAPI.Framework.Input;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Framework.StateTracking.Snapshots;
using StardewModdingAPI.Framework.Utilities;
+using StardewModdingAPI.Internal;
using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.BellsAndWhistles;
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index 5641f90f..9273864e 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -8,6 +8,7 @@ using Netcode;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.ContentManagers;
using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Internal;
using StardewModdingAPI.Toolkit.Utilities;
using StardewValley;
using StardewValley.BellsAndWhistles;
diff --git a/src/SMAPI/Patches/Game1Patcher.cs b/src/SMAPI/Patches/Game1Patcher.cs
index 82b13869..173a2055 100644
--- a/src/SMAPI/Patches/Game1Patcher.cs
+++ b/src/SMAPI/Patches/Game1Patcher.cs
@@ -2,19 +2,19 @@ using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
using StardewModdingAPI.Enums;
-using StardewModdingAPI.Framework.Patching;
using StardewModdingAPI.Framework.Reflection;
+using StardewModdingAPI.Internal.Patching;
using StardewValley;
using StardewValley.Menus;
using StardewValley.Minigames;
namespace StardewModdingAPI.Patches
{
- /// <summary>Harmony patches which notify SMAPI for save creation load stages.</summary>
+ /// <summary>Harmony patches for <see cref="Game1"/> which notify SMAPI for save load stages.</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 Game1Patcher : IHarmonyPatch
+ internal class Game1Patcher : BasePatcher
{
/*********
** Fields
@@ -42,25 +42,25 @@ namespace StardewModdingAPI.Patches
}
/// <inheritdoc />
- public void Apply(Harmony harmony)
+ public override void Apply(Harmony harmony, IMonitor monitor)
{
// detect CreatedInitialLocations and SaveAddedLocations
harmony.Patch(
- original: AccessTools.Method(typeof(Game1), nameof(Game1.AddModNPCs)),
- prefix: new HarmonyMethod(this.GetType(), nameof(Game1Patcher.Before_Game1_AddModNPCs))
+ original: this.RequireMethod<Game1>(nameof(Game1.AddModNPCs)),
+ prefix: this.GetHarmonyMethod(nameof(Game1Patcher.Before_AddModNpcs))
);
// detect CreatedLocations, and track IsInLoadForNewGame
harmony.Patch(
- original: AccessTools.Method(typeof(Game1), nameof(Game1.loadForNewGame)),
- prefix: new HarmonyMethod(this.GetType(), nameof(Game1Patcher.Before_Game1_LoadForNewGame)),
- postfix: new HarmonyMethod(this.GetType(), nameof(Game1Patcher.After_Game1_LoadForNewGame))
+ original: this.RequireMethod<Game1>(nameof(Game1.loadForNewGame)),
+ prefix: this.GetHarmonyMethod(nameof(Game1Patcher.Before_LoadForNewGame)),
+ postfix: this.GetHarmonyMethod(nameof(Game1Patcher.After_LoadForNewGame))
);
// detect ReturningToTitle
harmony.Patch(
- original: AccessTools.Method(typeof(Game1), nameof(Game1.CleanupReturningToTitle)),
- prefix: new HarmonyMethod(this.GetType(), nameof(Game1Patcher.Before_Game1_CleanupReturningToTitle))
+ original: this.RequireMethod<Game1>(nameof(Game1.CleanupReturningToTitle)),
+ prefix: this.GetHarmonyMethod(nameof(Game1Patcher.Before_CleanupReturningToTitle))
);
}
@@ -68,10 +68,10 @@ namespace StardewModdingAPI.Patches
/*********
** Private methods
*********/
- /// <summary>Called before <see cref="Game1.AddModNPCs"/>.</summary>
+ /// <summary>The method to call before <see cref="Game1.AddModNPCs"/>.</summary>
/// <returns>Returns whether to execute the original method.</returns>
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
- private static bool Before_Game1_AddModNPCs()
+ private static bool Before_AddModNpcs()
{
// When this method is called from Game1.loadForNewGame, it happens right after adding the vanilla
// locations but before initializing them.
@@ -86,27 +86,27 @@ namespace StardewModdingAPI.Patches
return true;
}
- /// <summary>Called before <see cref="Game1.CleanupReturningToTitle"/>.</summary>
+ /// <summary>The method to call before <see cref="Game1.CleanupReturningToTitle"/>.</summary>
/// <returns>Returns whether to execute the original method.</returns>
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
- private static bool Before_Game1_CleanupReturningToTitle()
+ private static bool Before_CleanupReturningToTitle()
{
Game1Patcher.OnStageChanged(LoadStage.ReturningToTitle);
return true;
}
- /// <summary>Called before <see cref="Game1.loadForNewGame"/>.</summary>
+ /// <summary>The method to call before <see cref="Game1.loadForNewGame"/>.</summary>
/// <returns>Returns whether to execute the original method.</returns>
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
- private static bool Before_Game1_LoadForNewGame()
+ private static bool Before_LoadForNewGame()
{
Game1Patcher.IsInLoadForNewGame = true;
return true;
}
- /// <summary>Called after <see cref="Game1.loadForNewGame"/>.</summary>
+ /// <summary>The method to call after <see cref="Game1.loadForNewGame"/>.</summary>
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
- private static void After_Game1_LoadForNewGame()
+ private static void After_LoadForNewGame()
{
Game1Patcher.IsInLoadForNewGame = false;
diff --git a/src/SMAPI/Patches/TitleMenuPatcher.cs b/src/SMAPI/Patches/TitleMenuPatcher.cs
index a889adfc..b4320ce0 100644
--- a/src/SMAPI/Patches/TitleMenuPatcher.cs
+++ b/src/SMAPI/Patches/TitleMenuPatcher.cs
@@ -2,16 +2,16 @@ using System;
using System.Diagnostics.CodeAnalysis;
using HarmonyLib;
using StardewModdingAPI.Enums;
-using StardewModdingAPI.Framework.Patching;
+using StardewModdingAPI.Internal.Patching;
using StardewValley.Menus;
namespace StardewModdingAPI.Patches
{
- /// <summary>Harmony patches which notify SMAPI for save creation load stages.</summary>
+ /// <summary>Harmony patches for <see cref="TitleMenu"/> which notify SMAPI when a new character was created.</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 TitleMenuPatcher : IHarmonyPatch
+ internal class TitleMenuPatcher : BasePatcher
{
/*********
** Fields
@@ -31,12 +31,11 @@ namespace StardewModdingAPI.Patches
}
/// <inheritdoc />
- public void Apply(Harmony harmony)
+ public override void Apply(Harmony harmony, IMonitor monitor)
{
- // detect CreatedBasicInfo
harmony.Patch(
- original: AccessTools.Method(typeof(TitleMenu), nameof(TitleMenu.createdNewCharacter)),
- prefix: new HarmonyMethod(this.GetType(), nameof(TitleMenuPatcher.Before_TitleMenu_CreatedNewCharacter))
+ original: this.RequireMethod<TitleMenu>(nameof(TitleMenu.createdNewCharacter)),
+ prefix: this.GetHarmonyMethod(nameof(TitleMenuPatcher.Before_CreatedNewCharacter))
);
}
@@ -44,10 +43,10 @@ namespace StardewModdingAPI.Patches
/*********
** Private methods
*********/
- /// <summary>Called before <see cref="TitleMenu.createdNewCharacter"/>.</summary>
+ /// <summary>The method to call before <see cref="TitleMenu.createdNewCharacter"/>.</summary>
/// <returns>Returns whether to execute the original method.</returns>
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
- private static bool Before_TitleMenu_CreatedNewCharacter()
+ private static bool Before_CreatedNewCharacter()
{
TitleMenuPatcher.OnStageChanged(LoadStage.CreatedBasicInfo);
return true;
diff --git a/src/SMAPI/Properties/AssemblyInfo.cs b/src/SMAPI/Properties/AssemblyInfo.cs
index ae758e9b..ee8a1674 100644
--- a/src/SMAPI/Properties/AssemblyInfo.cs
+++ b/src/SMAPI/Properties/AssemblyInfo.cs
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("SMAPI.Tests")]
-[assembly: InternalsVisibleTo("ErrorHandler")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Moq for unit testing
diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj
index 0c6cfdd3..7d5e7ef9 100644
--- a/src/SMAPI/SMAPI.csproj
+++ b/src/SMAPI/SMAPI.csproj
@@ -76,4 +76,5 @@
</ItemGroup>
<Import Project="..\SMAPI.Internal\SMAPI.Internal.projitems" Label="Shared" />
+ <Import Project="..\SMAPI.Internal.Patching\SMAPI.Internal.Patching.projitems" Label="Shared" />
</Project>