Decompiled source of ExtraSlotsDepositGuard v1.1.0

ExtraSlotsDepositGuard.dll

Decompiled a week ago
using 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;
		}
	}
}