Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EitrExtended v0.1.1
EitrExtended.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Configs; using HarmonyLib; using Jotunn.Extensions; using Jotunn.Managers; using Jotunn.Utils; using Logging; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("EitrExtended")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("EitrExtended")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("08a6f7d7-cf93-4931-aecd-abf2ce6ed34c")] [assembly: AssemblyFileVersion("0.1.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Logging { internal static class Log { internal enum InfoLevel { Low, Medium, High } private static ManualLogSource logSource; internal static ConfigEntry<InfoLevel> Verbosity { get; set; } internal static InfoLevel VerbosityLevel => Verbosity.Value; internal static bool IsVerbosityLow => Verbosity.Value >= InfoLevel.Low; internal static bool IsVerbosityMedium => Verbosity.Value >= InfoLevel.Medium; internal static bool IsVerbosityHigh => Verbosity.Value >= InfoLevel.High; internal static void Init(ManualLogSource logSource) { Log.logSource = logSource; } internal static void LogDebug(object data) { logSource.LogDebug(data); } internal static void LogError(object data) { logSource.LogError(data); } internal static void LogFatal(object data) { logSource.LogFatal(data); } internal static void LogMessage(object data) { logSource.LogMessage(data); } internal static void LogWarning(object data) { logSource.LogWarning(data); } internal static void LogInfo(object data, InfoLevel level = InfoLevel.Low) { if (Verbosity == null || VerbosityLevel >= level) { logSource.LogInfo(data); } } internal static void LogGameObject(GameObject prefab, bool includeChildren = false) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown LogInfo("***** " + ((Object)prefab).name + " *****"); Component[] components = prefab.GetComponents<Component>(); for (int i = 0; i < components.Length; i++) { LogComponent(components[i]); } if (!includeChildren) { return; } LogInfo("***** " + ((Object)prefab).name + " (children) *****"); foreach (Transform item in prefab.transform) { Transform val = item; if (Object.op_Implicit((Object)(object)val)) { LogInfo(" - " + ((Object)val).name); components = ((Component)val).GetComponents<Component>(); for (int i = 0; i < components.Length; i++) { LogComponent(components[i]); } } } } internal static void LogComponent(Component compo) { if (!Object.op_Implicit((Object)(object)compo)) { return; } try { LogInfo("--- " + ((object)compo).GetType().Name + ": " + ((Object)compo).name + " ---"); } catch (Exception ex) { LogError(ex.ToString()); LogWarning("Could not get type name for component!"); return; } try { foreach (PropertyInfo declaredProperty in AccessTools.GetDeclaredProperties(((object)compo).GetType())) { try { LogInfo($" - {declaredProperty.Name} = {declaredProperty.GetValue(compo)}"); } catch (Exception ex2) { LogError(ex2.ToString()); LogWarning("Could not get property: " + declaredProperty.Name + " for component!"); } } } catch (Exception ex3) { LogError(ex3.ToString()); LogWarning("Could not get properties for component!"); } try { foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(((object)compo).GetType())) { try { LogInfo($" - {declaredField.Name} = {declaredField.GetValue(compo)}"); } catch (Exception ex4) { LogError(ex4.ToString()); LogWarning("Could not get field: " + declaredField.Name + " for component!"); } } } catch (Exception ex5) { LogError(ex5.ToString()); LogWarning("Could not get fields for component!"); } } } } namespace EitrExtended { [BepInPlugin("Searica.Valheim.EitrExtended", "EitrExtended", "0.1.1")] [BepInDependency("com.jotunn.jotunn", "2.23.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [SynchronizationMode(/*Could not decode attribute arguments.*/)] internal sealed class EitrExtended : BaseUnityPlugin { public const string PluginName = "EitrExtended"; internal const string Author = "Searica"; public const string PluginGUID = "Searica.Valheim.EitrExtended"; public const string PluginVersion = "0.1.1"; internal static EitrExtended Instance; internal static ConfigFile ConfigFile; internal static ConfigFileWatcher ConfigFileWatcher; internal const string GlobalSection = "Global"; internal const string EitrRegenSection = "Eitr Regen"; internal const string EitrBaseSection = "Eitr Base"; internal ConfigEntry<bool> EnableEitrRegen; internal ConfigEntry<float> ExtraEitrRegenFlat; internal ConfigEntry<float> ExtraEitrRegen; internal ConfigEntry<bool> ExtraEitrRegenFoodOnly; internal ConfigEntry<bool> EnableEitrBase; internal ConfigEntry<float> BloodMagicBasePower; internal ConfigEntry<float> BloodMagicBaseCoeff; internal ConfigEntry<float> ElementMagicBasePower; internal ConfigEntry<float> ElementMagicBaseCoeff; public void Awake() { Instance = this; ConfigFile = ((BaseUnityPlugin)this).Config; Log.Init(((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Config.DisableSaveOnConfigSet(); SetUpConfigEntries(); ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "Searica.Valheim.EitrExtended"); Game.isModded = true; ConfigFileWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config); ConfigFileWatcher.OnConfigFileReloaded += delegate { }; SynchronizationManager.OnConfigurationSynchronized += delegate { }; SynchronizationManager.OnConfigurationWindowClosed += delegate { }; } internal void SetUpConfigEntries() { //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown Log.Verbosity = ConfigFileExtensions.BindConfigInOrder<Log.InfoLevel>(((BaseUnityPlugin)this).Config, "Global", "Verbosity", Log.InfoLevel.Low, "Information level of logging.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EnableEitrRegen = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Eitr Regen", "Enable Extra Eitr Regen", true, "Enable increased eitr regen.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ExtraEitrRegen = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Eitr Regen", "Extra Eitr Regen", 0.2f, "Increase eitr regen by X% of total eitr above base eitr value.", true, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ExtraEitrRegenFoodOnly = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Eitr Regen", "Total From Food Only", true, "Only count eitr from food when calcuating total eitr above base eitr value.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EnableEitrBase = ConfigFileExtensions.BindConfigInOrder<bool>(((BaseUnityPlugin)this).Config, "Eitr Base", "Enable Extra Base Eitr", true, "Enable magic skills granting base eitr as `extra eitr = coeff*(magic skil)^power`.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); BloodMagicBasePower = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Eitr Base", "Blood Magic Power", 0.5f, "The power to raise your magic skill to when calculating exta base eitr `(magic skill)^power`.", true, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); BloodMagicBaseCoeff = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Eitr Base", "Blood Magic Coeff", 2.5f, "The number to multiply your magic skill by after raising it to a power when calculating exta base eitr.", true, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), (Action<ConfigEntryBase>)null, new ConfigurationManagerAttributes { ShowRangeAsPercent = false }); ElementMagicBasePower = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Eitr Base", "Elemental Magic Power", 0.5f, "The power to raise your magic skill to when calculating exta base eitr `(magic skill)^power`.", true, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ElementMagicBaseCoeff = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Eitr Base", "Elemental Magic Coeff", 2.5f, "The number to multiply your magic skill by after raising it to a power when calculating exta base eitr.", true, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), (Action<ConfigEntryBase>)null, new ConfigurationManagerAttributes { ShowRangeAsPercent = false }); } public void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } } } namespace EitrExtended.Patches { [HarmonyPatch] internal static class BaseEitrPatches { [HarmonyTranspiler] [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] private static IEnumerable<CodeInstruction> ChangeBaseEitrTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) CodeMatch[] array = (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)0f, (string)null) }; CodeInstruction[] array2 = (CodeInstruction[])(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(BaseEitrPatches), "GetBaseEitr", (Type[])null, (Type[])null)) }; return new CodeMatcher(instructions, (ILGenerator)null).MatchStartForward(array).ThrowIfNotMatch("Failed to match code in ChangeBaseEitrTranspiler!", Array.Empty<CodeMatch>()).RemoveInstruction() .InsertAndAdvance(array2) .ThrowIfInvalid("Failed to insert code in ChangeBaseEitrTranspiler!") .InstructionEnumeration(); } public static float GetBaseEitr(Player player) { if (!EitrExtended.Instance.EnableEitrBase.Value || !Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)((Character)player).GetSkills())) { return 0f; } float num = 0f; if (TryGetSkillLevel(player, (SkillType)10, out var skillLevel)) { num += EitrExtended.Instance.BloodMagicBaseCoeff.Value * Mathf.Pow(skillLevel, EitrExtended.Instance.BloodMagicBasePower.Value); } if (TryGetSkillLevel(player, (SkillType)9, out var skillLevel2)) { num += EitrExtended.Instance.ElementMagicBaseCoeff.Value * Mathf.Pow(skillLevel2, EitrExtended.Instance.ElementMagicBasePower.Value); } return Mathf.Floor(num); } private static bool TryGetSkillLevel(Player player, SkillType skillType, out float skillLevel) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (((Character)player).GetSkills().m_skillData.ContainsKey(skillType)) { skillLevel = ((Character)player).GetSkillLevel(skillType); return true; } skillLevel = 0f; return false; } private static void UpdateBaseEitr(Player player) { if (!Object.op_Implicit((Object)(object)player)) { return; } try { float num = default(float); float num2 = default(float); float num3 = default(float); player.GetTotalFoodValue(ref num, ref num2, ref num3); player.SetMaxEitr(num3, true); } catch (Exception arg) { Log.LogWarning($"While updating base eitr caught exception {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "Awake")] private static void Player_Awake_Postfix(Player __instance) { UpdateBaseEitr(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "OnSkillLevelup")] private static void Player_OnSkillLevelUp_Postfix(Player __instance, SkillType skill) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (IsMagicSkill(skill)) { UpdateBaseEitr(__instance); } } [HarmonyPostfix] [HarmonyPatch(typeof(Skills), "Awake")] private static void Skills_Awake_Postfix(Skills __instance) { UpdateBaseEitr(Player.m_localPlayer); } [HarmonyPostfix] [HarmonyPatch(typeof(Terminal), "TryRunCommand")] private static void Terminal_TryRunCommand_Postfix(Terminal __instance, string text) { if (StringExtensions.ContainsAny(text.ToLower(), new string[2] { "puke", "skill" })) { UpdateBaseEitr(Player.m_localPlayer); } } private static bool IsMagicSkill(SkillType skillType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 if (skillType - 9 <= 1) { return true; } return false; } } [HarmonyPatch] internal static class EitrRegenPatches { [HarmonyPatch(typeof(TextsDialog), "AddActiveEffects")] public static class TextsDialog_AddActiveEffects_SeasonTooltipWhenBuffDisabled { private static void Postfix(TextsDialog __instance) { if (EitrExtended.Instance.EnableEitrRegen.Value && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { float num = GetEitrRegenMultiplier(Player.m_localPlayer) - 1f; if (!(num < 0.01f)) { TextInfo obj = __instance.m_texts[0]; obj.m_text += Localization.instance.Localize(string.Format("\n$se_eitrregen ({0}): <color=orange>{1:P1}</color>", EitrExtended.Instance.ExtraEitrRegenFoodOnly.Value ? "$item_food" : "$hud_misc", num)); } } } } private static readonly string[] tooltipTokens = new string[4] { "$item_food_regen", "$item_food_duration", "$item_food_eitr", "$item_food_stamina" }; [HarmonyTranspiler] [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] private static IEnumerable<CodeInstruction> Player_UpdateStats_EitrRegen(IEnumerable<CodeInstruction> instructions) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) CodeMatch[] array = (CodeMatch[])(object)new CodeMatch[3] { new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Player), "GetEquipmentEitrRegenModifier", (Type[])null, (Type[])null), (string)null), new CodeMatch((OpCode?)OpCodes.Add, (object)null, (string)null) }; CodeInstruction[] array2 = (CodeInstruction[])(object)new CodeInstruction[3] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), Transpilers.EmitDelegate<Func<Player, float>>((Func<Player, float>)GetEitrRegenMultiplier), new CodeInstruction(OpCodes.Mul, (object)null) }; return new CodeMatcher(instructions, (ILGenerator)null).MatchEndForward(array).ThrowIfNotMatch("Failed to match in Player.UpdateStats!", Array.Empty<CodeMatch>()).InsertAndAdvance(array2) .ThrowIfInvalid("Failed to insert code in Player.UpdateStats!") .InstructionEnumeration(); } public static float GetEitrRegenMultiplier(Player player) { if (!EitrExtended.Instance.EnableEitrRegen.Value || !Object.op_Implicit((Object)(object)player)) { return 1f; } float maxEitr = ((Character)player).GetMaxEitr(); if (!EitrExtended.Instance.ExtraEitrRegenFoodOnly.Value) { float num = default(float); float num2 = default(float); player.GetTotalFoodValue(ref num, ref num2, ref maxEitr); } float num3 = maxEitr - BaseEitrPatches.GetBaseEitr(player); float num4 = EitrExtended.Instance.ExtraEitrRegen.Value * num3 / 100f; return 1f + num4; } private static float GetEitrRegenMultiplierForFood(float foodEitr) { return EitrExtended.Instance.ExtraEitrRegen.Value * foodEitr / 100f; } [HarmonyPostfix] [HarmonyPriority(700)] [HarmonyAfter(new string[] { "shudnal.StaminaExtended" })] [HarmonyBefore(new string[] { "shudnal.MyLittleUI" })] [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] private static void ItemDrop_ItemData_GetTooltip_EitrRegenTooltip_Postfix(ItemData item, ref string __result) { if (!EitrExtended.Instance.EnableEitrRegen.Value || !IsEitrFood(item, out var foodEitr)) { return; } int foodToopTipStartIndex = GetFoodToopTipStartIndex(__result); if (foodToopTipStartIndex != -1) { string text = $"\n$se_eitrregen: <color=#ffff80ff>{GetEitrRegenMultiplierForFood(foodEitr):P1}</color> ($item_current:<color=yellow>{GetEitrRegenMultiplier(Player.m_localPlayer) - 1f:P1}</color>)"; int num = __result.IndexOf("\n", foodToopTipStartIndex, StringComparison.InvariantCulture); if (num != -1) { __result.Insert(num, text); } else { __result += text; } } } private static bool IsEitrFood(ItemData item, out float foodEitr) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 foodEitr = 0f; if (!EitrExtended.Instance.EnableEitrRegen.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return false; } if ((int)item.m_shared.m_itemType == 2) { foodEitr = item.m_shared.m_foodEitr; } if (!(foodEitr > 0f)) { if ((Object)(object)item.m_shared.m_appendToolTip != (Object)null) { return IsEitrFood(item.m_shared.m_appendToolTip.m_itemData, out foodEitr); } return false; } return true; } private static int GetFoodToopTipStartIndex(string text) { int num = -1; string[] array = tooltipTokens; foreach (string value in array) { num = text.IndexOf(value, StringComparison.InvariantCulture); if (num != -1) { break; } } return num; } } } namespace Configs { public static class ConfigFileExtensions { public static bool DisableSaveOnConfigSet(this ConfigFile configFile) { bool saveOnConfigSet = configFile.SaveOnConfigSet; configFile.SaveOnConfigSet = false; return saveOnConfigSet; } } internal sealed class ConfigFileWatcher { private const long RELOAD_DELAY = 10000000L; private DateTime lastReadTime = DateTime.MinValue; private readonly ConfigFile configFile; private readonly string ConfigFileDir; private readonly string ConfigFileName; internal event Action OnConfigFileReloaded; internal ConfigFileWatcher(ConfigFile configFile) { this.configFile = configFile; ConfigFileDir = Directory.GetParent(configFile.ConfigFilePath).FullName; ConfigFileName = Path.GetFileName(configFile.ConfigFilePath); FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(ConfigFileDir, ConfigFileName); fileSystemWatcher.Changed += ReloadConfigFile; fileSystemWatcher.Created += ReloadConfigFile; fileSystemWatcher.Renamed += ReloadConfigFile; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void InvokeOnConfigFileReloaded() { this.OnConfigFileReloaded?.SafeInvoke(); } internal void ReloadConfigFile(object sender, FileSystemEventArgs eventArgs) { DateTime now = DateTime.Now; long num = now.Ticks - lastReadTime.Ticks; if (!File.Exists(configFile.ConfigFilePath) || num < 10000000) { return; } try { Log.LogInfo("Reloading " + configFile.ConfigFilePath); bool saveOnConfigSet = configFile.DisableSaveOnConfigSet(); configFile.Reload(); configFile.SaveOnConfigSet = saveOnConfigSet; lastReadTime = now; InvokeOnConfigFileReloaded(); } catch { Log.LogError("There was an issue loading " + configFile.ConfigFilePath); Log.LogError("Please check your config entries for spelling and format!"); } } } internal static class SafeInvokeEvent { internal static void SafeInvoke(this Action events) { if (events == null) { return; } Delegate[] invocationList = events.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action action = (Action)invocationList[i]; try { action(); } catch (Exception arg) { Log.LogWarning("Exception thrown at event " + new StackFrame(1).GetMethod().Name + $" in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{arg}"); } } } } }