Please disclose if any significant portion of your mod was created 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 ExtraSlotsDepositGuard v1.1.0
ExtraSlotsDepositGuard.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PICS0UL")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+73cb09176af5c0e17cd0de0fa59487819ab800e3")] [assembly: AssemblyProduct("ExtraSlotsDepositGuard")] [assembly: AssemblyTitle("ExtraSlotsDepositGuard")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 ExtraSlotsDepositGuard { [BepInPlugin("com.pics0ul.valheim.extraslotsdepositguard", "ExtraSlots Deposit Guard", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class DepositGuardPlugin : BaseUnityPlugin { public const string PluginGuid = "com.pics0ul.valheim.extraslotsdepositguard"; public const string PluginName = "ExtraSlots Deposit Guard"; public const string PluginVersion = "1.1.0"; internal const string NearbyCraftingGuid = "com.mikeg.valheim.nearbycrafting"; internal const string StoreAndCraftGuid = "com.morda.storeandcraft"; internal const string ExtraSlotsGuid = "shudnal.ExtraSlots"; internal static ManualLogSource ModLogger; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ProtectQuickSlots; internal static ConfigEntry<bool> ProtectMiscSlots; internal static ConfigEntry<bool> ProtectAmmoSlots; internal static ConfigEntry<bool> ProtectFoodSlots; internal static ConfigEntry<bool> ProtectEquipmentSlots; internal static ConfigEntry<bool> DebugLogging; private Harmony _harmony; internal static bool DebugEnabled { get { if (DebugLogging != null) { return DebugLogging.Value; } return false; } } private void Awake() { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown ModLogger = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Keep bulk-deposit features from emptying Extra Slots slots. Covers Nearby Crafting's quick-deposit hotkey and StoreAndCraft's dump and store-one actions."); ProtectQuickSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protected Slots", "QuickSlots", true, "Never deposit items sitting in Extra Slots quick slots."); ProtectMiscSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protected Slots", "MiscSlots", true, "Never deposit items sitting in Extra Slots misc slots (trophies, fish, coins, keys, quest items)."); ProtectAmmoSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protected Slots", "AmmoSlots", true, "Never deposit items sitting in Extra Slots ammo slots."); ProtectFoodSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protected Slots", "FoodSlots", true, "Never deposit items sitting in Extra Slots food slots."); ProtectEquipmentSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protected Slots", "EquipmentSlots", true, "Never deposit items sitting in Extra Slots equipment slots, including extra utility slots and custom slots added through the Extra Slots API."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Log every item this mod holds back from a deposit."); if (!ExtraSlotsApi.Resolve()) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not bind to the Extra Slots API. Deposit protection is inactive."); return; } try { _harmony = new Harmony("com.pics0ul.valheim.extraslotsdepositguard"); List<string> list = new List<string>(); if (TryPatchNearbyCrafting()) { list.Add("Nearby Crafting"); } if (TryPatchStoreAndCraft()) { list.Add("StoreAndCraft"); } if (list.Count == 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Neither Nearby Crafting nor StoreAndCraft was found. There is nothing to guard, so deposit protection is inactive."); try { _harmony.UnpatchSelf(); } catch { } _harmony = null; } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ExtraSlots Deposit Guard 1.1.0 loaded; protecting Extra Slots slots from: " + string.Join(", ", list.ToArray()) + ".")); } } catch (Exception ex) { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } _harmony = null; ((BaseUnityPlugin)this).Logger.LogError((object)"Failed to apply Harmony patches; deposit protection is inactive."); ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } _harmony = null; DepositScope.Reset(); } private bool TryPatchNearbyCrafting() { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00b9: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown if (!Chainloader.PluginInfos.ContainsKey("com.mikeg.valheim.nearbycrafting")) { return false; } Type type = AccessTools.TypeByName("NearbyCrafting.NearbyCraftingPlugin"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Nearby Crafting is loaded but its plugin type was not found. It has probably changed; quick-deposit protection is inactive."); return false; } MethodInfo methodInfo = AccessTools.Method(type, "MassQuickDeposit", new Type[1] { typeof(Player) }, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Nearby Crafting is installed but its MassQuickDeposit method was not found. It has probably changed; quick-deposit protection is inactive."); return false; } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(DepositScope), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(DepositScope), "Finalizer", (Type[])null, (Type[])null)), (HarmonyMethod)null); HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(MoveItemGuard), "Prefix", (Type[])null, (Type[])null)) { priority = 800 }; _harmony.Patch((MethodBase)AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[5] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int) }, (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } private bool TryPatchStoreAndCraft() { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_0169: Expected O, but got Unknown //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown if (!Chainloader.PluginInfos.ContainsKey("com.morda.storeandcraft")) { return false; } Type type = AccessTools.TypeByName("StoreAndCraft.InventoryDump"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"StoreAndCraft is loaded but its InventoryDump type was not found. It has probably changed; StoreAndCraft protection is inactive."); return false; } MethodInfo methodInfo = AccessTools.Method(type, "DumpNearby", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(type, "ShouldDump", new Type[2] { typeof(ItemData), typeof(Inventory) }, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(type, "StoreOne", new Type[1] { typeof(ItemData) }, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo3 == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("StoreAndCraft is installed but its dump methods were not found (DumpNearby: " + (methodInfo != null) + ", ShouldDump: " + (methodInfo2 != null) + ", StoreOne: " + (methodInfo3 != null) + "). It has probably changed; StoreAndCraft protection is inactive.")); return false; } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(DepositScope), "PrefixLocalPlayer", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(DepositScope), "Finalizer", (Type[])null, (Type[])null)), (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(ShouldDumpGuard), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(AccessTools.Method(typeof(StoreOneGuard), "Prefix", (Type[])null, (Type[])null)) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } internal static void Debug(string message) { if (DebugEnabled) { ModLogger.LogInfo((object)message); } } } internal static class ExtraSlotsApi { private static Func<List<ItemData>> _quickSlotItems; private static Func<List<ItemData>> _miscSlotItems; private static Func<List<ItemData>> _ammoSlotItems; private static Func<List<ItemData>> _foodSlotItems; private static Func<List<ItemData>> _equipmentSlotItems; internal static bool Resolve() { Type type = AccessTools.TypeByName("ExtraSlots.API"); if (type == null) { return false; } _quickSlotItems = BindGetter(type, "GetQuickSlotsItems"); _miscSlotItems = BindGetter(type, "GetMiscSlotsItems"); _ammoSlotItems = BindGetter(type, "GetAmmoSlotsItems"); _foodSlotItems = BindGetter(type, "GetFoodSlotsItems"); _equipmentSlotItems = BindGetter(type, "GetEquipmentSlotsItems"); if (_quickSlotItems != null && _miscSlotItems != null && _ammoSlotItems != null && _foodSlotItems != null) { return _equipmentSlotItems != null; } return false; } private static Func<List<ItemData>> BindGetter(Type api, string name) { MethodInfo method = AccessTools.Method(api, name, Type.EmptyTypes, (Type[])null); if (method == null || !typeof(IEnumerable).IsAssignableFrom(method.ReturnType)) { DepositGuardPlugin.ModLogger.LogWarning((object)("Extra Slots API method '" + name + "' is missing or has an unexpected shape.")); return null; } return delegate { List<ItemData> list = new List<ItemData>(); if (!(method.Invoke(null, null) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { ItemData val = (ItemData)((item is ItemData) ? item : null); if (val != null) { list.Add(val); } } return list; }; } internal static void AddProtectedItems(HashSet<ItemData> target) { Collect(target, _quickSlotItems, DepositGuardPlugin.ProtectQuickSlots.Value, "quick"); Collect(target, _miscSlotItems, DepositGuardPlugin.ProtectMiscSlots.Value, "misc"); Collect(target, _ammoSlotItems, DepositGuardPlugin.ProtectAmmoSlots.Value, "ammo"); Collect(target, _foodSlotItems, DepositGuardPlugin.ProtectFoodSlots.Value, "food"); Collect(target, _equipmentSlotItems, DepositGuardPlugin.ProtectEquipmentSlots.Value, "equipment"); } private static void Collect(HashSet<ItemData> target, Func<List<ItemData>> getter, bool wanted, string category) { if (!wanted || getter == null) { return; } try { foreach (ItemData item in getter()) { if (item != null) { target.Add(item); } } } catch (Exception ex) { DepositGuardPlugin.ModLogger.LogWarning((object)("Could not read Extra Slots " + category + " slots (" + ex.GetType().Name + "): " + ex.Message)); } } } internal static class DepositScope { private sealed class ReferenceComparer : IEqualityComparer<ItemData> { internal static readonly ReferenceComparer Instance = new ReferenceComparer(); public bool Equals(ItemData x, ItemData y) { return x == y; } public int GetHashCode(ItemData obj) { return RuntimeHelpers.GetHashCode(obj); } } private static readonly HashSet<ItemData> ProtectedItems = new HashSet<ItemData>(ReferenceComparer.Instance); private static Inventory _guardedInventory; private static int _depth; internal static int BlockedMoves; internal static bool IsProtected(Inventory fromInventory, ItemData item) { if (_depth > 0 && item != null && fromInventory != null && fromInventory == _guardedInventory) { return ProtectedItems.Contains(item); } return false; } internal static bool IsProtectedSingle(ItemData item) { if (item == null) { return false; } if (DepositGuardPlugin.Enabled == null || !DepositGuardPlugin.Enabled.Value) { return false; } if (_depth > 0) { return ProtectedItems.Contains(item); } HashSet<ItemData> hashSet = new HashSet<ItemData>(ReferenceComparer.Instance); try { ExtraSlotsApi.AddProtectedItems(hashSet); } catch (Exception ex) { DepositGuardPlugin.ModLogger.LogWarning((object)("Could not read Extra Slots contents (" + ex.GetType().Name + "): " + ex.Message)); return false; } return hashSet.Contains(item); } internal static void Prefix(Player player) { if (_depth++ > 0) { return; } ProtectedItems.Clear(); BlockedMoves = 0; _guardedInventory = null; if (DepositGuardPlugin.Enabled == null || !DepositGuardPlugin.Enabled.Value || (Object)(object)player == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { _guardedInventory = inventory; try { ExtraSlotsApi.AddProtectedItems(ProtectedItems); } catch (Exception ex) { DepositGuardPlugin.ModLogger.LogWarning((object)("Could not snapshot Extra Slots contents (" + ex.GetType().Name + "): " + ex.Message)); ProtectedItems.Clear(); } DepositGuardPlugin.Debug("Deposit started; holding back " + ProtectedItems.Count + " item stack(s) in Extra Slots."); } } internal static void PrefixLocalPlayer() { Prefix(Player.m_localPlayer); } internal static Exception Finalizer(Exception __exception) { if (_depth > 0) { _depth--; } if (_depth == 0) { if (BlockedMoves > 0) { DepositGuardPlugin.Debug("Deposit finished; blocked " + BlockedMoves + " move(s) out of Extra Slots."); } ProtectedItems.Clear(); _guardedInventory = null; BlockedMoves = 0; } return __exception; } internal static void Reset() { _depth = 0; ProtectedItems.Clear(); _guardedInventory = null; BlockedMoves = 0; } internal static void LogHeldBack(ItemData item, string action) { if (DepositGuardPlugin.DebugEnabled && item != null) { string text = ((item.m_shared != null) ? item.m_shared.m_name : "<unknown>"); DepositGuardPlugin.Debug("Held back '" + text + "' at slot " + item.m_gridPos.x + "," + item.m_gridPos.y + " during " + action + "."); } } } internal static class MoveItemGuard { internal static bool Prefix(Inventory fromInventory, ItemData item, ref bool __result) { if (!DepositScope.IsProtected(fromInventory, item)) { return true; } DepositScope.BlockedMoves++; DepositScope.LogHeldBack(item, "quick-deposit"); __result = false; return false; } } internal static class ShouldDumpGuard { internal static void Postfix(ItemData item, Inventory inv, ref bool __result) { if (__result && DepositScope.IsProtected(inv, item)) { DepositScope.BlockedMoves++; DepositScope.LogHeldBack(item, "dump"); __result = false; } } } internal static class StoreOneGuard { internal static bool Prefix(ItemData item, ref bool __result) { if (!DepositScope.IsProtectedSingle(item)) { return true; } DepositScope.LogHeldBack(item, "store-one"); __result = false; return false; } } }