summaryrefslogtreecommitdiff
path: root/src/SMAPI.Mods.ErrorHandler
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Mods.ErrorHandler')
-rw-r--r--src/SMAPI.Mods.ErrorHandler/ModEntry.cs29
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs67
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs (renamed from src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs)14
-rw-r--r--src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs63
-rw-r--r--src/SMAPI.Mods.ErrorHandler/manifest.json4
5 files changed, 157 insertions, 20 deletions
diff --git a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs
index 2f6f1939..d9426d75 100644
--- a/src/SMAPI.Mods.ErrorHandler/ModEntry.cs
+++ b/src/SMAPI.Mods.ErrorHandler/ModEntry.cs
@@ -26,21 +26,17 @@ namespace StardewModdingAPI.Mods.ErrorHandler
public override void Entry(IModHelper helper)
{
// get SMAPI core types
- SCore core = SCore.Instance;
- LogManager logManager = core.GetType().GetField("LogManager", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(core) as LogManager;
- if (logManager == null)
- {
- this.Monitor.Log($"Can't access SMAPI's internal log manager. Error-handling patches won't be applied.", LogLevel.Error);
- return;
- }
+ IMonitor monitorForGame = this.GetMonitorForGame();
// apply patches
new GamePatcher(this.Monitor).Apply(
- new EventErrorPatch(logManager.MonitorForGame),
- new DialogueErrorPatch(logManager.MonitorForGame, this.Helper.Reflection),
+ new DialogueErrorPatch(monitorForGame, this.Helper.Reflection),
+ new EventPatches(monitorForGame),
+ new GameLocationPatches(monitorForGame),
new ObjectErrorPatch(),
new LoadErrorPatch(this.Monitor, this.OnSaveContentRemoved),
- new ScheduleErrorPatch(logManager.MonitorForGame),
+ new ScheduleErrorPatch(monitorForGame),
+ new SpriteBatchValidationPatches(),
new UtilityErrorPatches()
);
@@ -61,7 +57,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler
/// <summary>The method invoked when a save is loaded.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
- public void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
+ private void OnSaveLoaded(object sender, SaveLoadedEventArgs e)
{
// show in-game warning for removed save content
if (this.IsSaveContentRemoved)
@@ -70,5 +66,16 @@ namespace StardewModdingAPI.Mods.ErrorHandler
Game1.addHUDMessage(new HUDMessage(this.Helper.Translation.Get("warn.invalid-content-removed"), HUDMessage.error_type));
}
}
+
+ /// <summary>Get the monitor with which to log game errors.</summary>
+ private IMonitor GetMonitorForGame()
+ {
+ SCore core = SCore.Instance;
+ LogManager logManager = core.GetType().GetField("LogManager", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(core) as LogManager;
+ if (logManager == null)
+ this.Monitor.Log("Can't access SMAPI's internal log manager. Some game errors may be reported as being from Error Handler.", LogLevel.Error);
+
+ return logManager?.MonitorForGame ?? this.Monitor;
+ }
}
}
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs
new file mode 100644
index 00000000..a15c1d32
--- /dev/null
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/EventPatches.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+#if HARMONY_2
+using HarmonyLib;
+#else
+using Harmony;
+#endif
+using StardewModdingAPI.Framework.Patching;
+using StardewValley;
+
+namespace StardewModdingAPI.Mods.ErrorHandler.Patches
+{
+ /// <summary>Harmony patches for <see cref="Event"/> which intercept errors to log more details.</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 EventPatches : IHarmonyPatch
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>Writes messages to the console and log file on behalf of the game.</summary>
+ private static IMonitor MonitorForGame;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <inheritdoc />
+ public string Name => nameof(EventPatches);
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="monitorForGame">Writes messages to the console and log file on behalf of the game.</param>
+ public EventPatches(IMonitor monitorForGame)
+ {
+ EventPatches.MonitorForGame = monitorForGame;
+ }
+
+ /// <inheritdoc />
+#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)),
+ postfix: new HarmonyMethod(this.GetType(), nameof(EventPatches.After_Event_LogErrorAndHalt))
+ );
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <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)
+ {
+ EventPatches.MonitorForGame.Log(e.ToString(), LogLevel.Error);
+ }
+ }
+}
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs
index fabc6cad..c10f2de7 100644
--- a/src/SMAPI.Mods.ErrorHandler/Patches/EventErrorPatch.cs
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/GameLocationPatches.cs
@@ -15,7 +15,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 EventErrorPatch : IHarmonyPatch
+ internal class GameLocationPatches : IHarmonyPatch
{
/*********
** Fields
@@ -28,7 +28,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
** Accessors
*********/
/// <inheritdoc />
- public string Name => nameof(EventErrorPatch);
+ public string Name => nameof(GameLocationPatches);
/*********
@@ -36,9 +36,9 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
*********/
/// <summary>Construct an instance.</summary>
/// <param name="monitorForGame">Writes messages to the console and log file on behalf of the game.</param>
- public EventErrorPatch(IMonitor monitorForGame)
+ public GameLocationPatches(IMonitor monitorForGame)
{
- EventErrorPatch.MonitorForGame = monitorForGame;
+ GameLocationPatches.MonitorForGame = monitorForGame;
}
/// <inheritdoc />
@@ -55,7 +55,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
{
harmony.Patch(
original: AccessTools.Method(typeof(GameLocation), "checkEventPrecondition"),
- prefix: new HarmonyMethod(this.GetType(), nameof(EventErrorPatch.Before_GameLocation_CheckEventPrecondition))
+ prefix: new HarmonyMethod(this.GetType(), nameof(GameLocationPatches.Before_GameLocation_CheckEventPrecondition))
);
}
#endif
@@ -89,7 +89,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <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(EventErrorPatch.Before_GameLocation_CheckEventPrecondition);
+ const string key = nameof(GameLocationPatches.Before_GameLocation_CheckEventPrecondition);
if (!PatchHelper.StartIntercept(key))
return true;
@@ -101,7 +101,7 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
catch (TargetInvocationException ex)
{
__result = -1;
- EventErrorPatch.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{ex.InnerException}", LogLevel.Error);
+ GameLocationPatches.MonitorForGame.Log($"Failed parsing event precondition ({precondition}):\n{ex.InnerException}", LogLevel.Error);
return false;
}
finally
diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs b/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs
new file mode 100644
index 00000000..0211cfb1
--- /dev/null
+++ b/src/SMAPI.Mods.ErrorHandler/Patches/SpriteBatchValidationPatches.cs
@@ -0,0 +1,63 @@
+#if HARMONY_2
+using HarmonyLib;
+#else
+using Harmony;
+#endif
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Xna.Framework.Graphics;
+using StardewModdingAPI.Framework.Patching;
+
+namespace StardewModdingAPI.Mods.ErrorHandler.Patches
+{
+ /// <summary>Harmony patch for <see cref="SpriteBatch"/> to validate textures earlier.</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 SpriteBatchValidationPatches : IHarmonyPatch
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <inheritdoc />
+ public string Name => nameof(SpriteBatchValidationPatches);
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <inheritdoc />
+#if HARMONY_2
+ public void Apply(Harmony harmony)
+#else
+ public void Apply(HarmonyInstance harmony)
+#endif
+ {
+ harmony.Patch(
+#if SMAPI_FOR_WINDOWS
+ original: AccessTools.Method(typeof(SpriteBatch), "InternalDraw"),
+#else
+ original: AccessTools.Method(typeof(SpriteBatch), "CheckValid", new[] { typeof(Texture2D) }),
+#endif
+ postfix: new HarmonyMethod(this.GetType(), nameof(SpriteBatchValidationPatches.After_SpriteBatch_CheckValid))
+ );
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+#if SMAPI_FOR_WINDOWS
+ /// <summary>The method to call instead of <see cref="SpriteBatch.InternalDraw"/>.</summary>
+ /// <param name="texture">The texture to validate.</param>
+#else
+ /// <summary>The method to call instead of <see cref="SpriteBatch.CheckValid"/>.</summary>
+ /// <param name="texture">The texture to validate.</param>
+#endif
+ private static void After_SpriteBatch_CheckValid(Texture2D texture)
+ {
+ if (texture?.IsDisposed == true)
+ throw new ObjectDisposedException("Cannot draw this texture because it's disposed.");
+ }
+ }
+}
diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json
index bb9942d1..b6df0f49 100644
--- a/src/SMAPI.Mods.ErrorHandler/manifest.json
+++ b/src/SMAPI.Mods.ErrorHandler/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Error Handler",
"Author": "SMAPI",
- "Version": "3.9.1",
+ "Version": "3.9.2",
"Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.",
"UniqueID": "SMAPI.ErrorHandler",
"EntryDll": "ErrorHandler.dll",
- "MinimumApiVersion": "3.9.1"
+ "MinimumApiVersion": "3.9.2"
}