diff --git a/docs/release-notes.md b/docs/release-notes.md index 65a2e93b..a7a5e6dd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,7 @@ * Fixed mod edits to the farmhouse shifting the player down one tile in some cases. * For mod authors: + * SMAPI now intercepts dictionary duplicate-key errors and adds the key to the error message to simplify troubleshooting. (Due to Harmony limitations, this only works for the dictionary types used by the game.) * Fixed map tile rotations/flips not working for farmhands in split-screen mode. * Fixed barn/coop exit warps being reset when you edit their interior map. diff --git a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs index 6ad64e16..8ceafcc5 100644 --- a/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs +++ b/src/SMAPI.Mods.ErrorHandler/Patches/DictionaryPatcher.cs @@ -48,6 +48,11 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches 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)) ); + + harmony.Patch( + original: AccessTools.Method(dictionaryType, "Add") ?? throw new InvalidOperationException($"Can't find method {PatchHelper.GetMethodString(dictionaryType, "Add")} to patch."), + finalizer: this.GetHarmonyMethod(nameof(DictionaryPatcher.Finalize_Add)) + ); } } } @@ -63,13 +68,31 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches private static Exception Finalize_GetItem(object key, Exception __exception) { if (__exception is KeyNotFoundException) - { - DictionaryPatcher.Reflection - .GetField(__exception, "_message") - .SetValue($"{__exception.Message}\nkey: '{key}'"); - } + DictionaryPatcher.AddKey(__exception, key); return __exception; } + + /// The method to call after a dictionary insert throws an exception. + /// The dictionary key being inserted. + /// The exception thrown by the wrapped method, if any. + /// Returns the exception to throw, if any. + private static Exception Finalize_Add(object key, Exception __exception) + { + if (__exception is ArgumentException) + DictionaryPatcher.AddKey(__exception, key); + + return __exception; + } + + /// Add the dictionary key to an exception message. + /// The exception whose message to edit. + /// The dictionary key. + private static void AddKey(Exception exception, object key) + { + DictionaryPatcher.Reflection + .GetField(exception, "_message") + .SetValue($"{exception.Message}\nkey: '{key}'"); + } } }