using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using HarmonyLib; using StardewModdingAPI.Framework.Patching; using StardewValley.GameData; using StardewValley.GameData.HomeRenovations; using StardewValley.GameData.Movies; namespace StardewModdingAPI.Mods.ErrorHandler.Patches { /// A Harmony patch for which adds the accessed key to exceptions. /// Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments. [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 DictionaryPatches : IHarmonyPatch { /********* ** Fields *********/ /// Simplifies access to private code. private static IReflectionHelper Reflection; /********* ** Public methods *********/ /// Construct an instance. /// Simplifies access to private code. public DictionaryPatches(IReflectionHelper reflector) { DictionaryPatches.Reflection = reflector; } /// public void Apply(Harmony harmony) { Type[] keyTypes = { typeof(int), typeof(string) }; Type[] valueTypes = { typeof(int), typeof(string), typeof(HomeRenovation), typeof(MovieData), typeof(SpecialOrderData) }; foreach (Type keyType in keyTypes) { foreach (Type valueType in valueTypes) { Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); harmony.Patch( original: AccessTools.Method(dictionaryType, "get_Item"), finalizer: new HarmonyMethod(this.GetType(), nameof(DictionaryPatches.Finalize_GetItem)) ); } } } /********* ** Private methods *********/ /// The method to call after the dictionary indexer throws an exception. /// The dictionary key being fetched. /// The exception thrown by the wrapped method, if any. /// Returns the exception to throw, if any. private static Exception Finalize_GetItem(object key, Exception __exception) { if (__exception is KeyNotFoundException) AddKeyTo(__exception, key?.ToString()); return __exception; } /// Add the accessed key to an exception message. /// The exception to modify. /// The dictionary key. private static void AddKeyTo(Exception exception, string key) { DictionaryPatches.Reflection .GetField(exception, "_message") .SetValue($"{exception.Message}\nkey: '{key}'"); } } }