Decompiled source of Chest Stacks 1000 v1.0.0

plugins/ChestStacks.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("BenjSammy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Chest Stacks 1000 by BenjSammy")]
[assembly: AssemblyTitle("ChestStacks")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 BenjSammy.ChestStacks
{
	[BepInPlugin("benjsammy.cheststacks", "Chest Stacks 1000 by BenjSammy", "1.0.0")]
	public sealed class ChestStacksPlugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			_harmony = new Harmony("benjsammy.cheststacks");
			try
			{
				GameInventory game = new GameInventory(!Application.isBatchMode);
				int num = ChestPatches.Install(_harmony, game);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Chest Stacks 1000 loaded. {num} native stack limit reads patched.");
			}
			catch (Exception ex)
			{
				_harmony.UnpatchSelf();
				_harmony = null;
				ChestPatches.Reset();
				((BaseUnityPlugin)this).Logger.LogError((object)("Chest Stacks 1000 could not start with this Valheim API. " + ex));
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			ChestPatches.Reset();
		}
	}
	internal static class ChestPatches
	{
		private static class ReadingScope
		{
			internal static void Enter(object __instance, out object __state)
			{
				__state = _readingInventory;
				_readingInventory = __instance;
			}

			internal static void Exit(object __state)
			{
				_readingInventory = __state;
			}
		}

		private static class LoadScope
		{
			internal readonly struct State
			{
				internal readonly object Inventory;

				internal readonly int Count;

				internal State(object inventory, int count)
				{
					Inventory = inventory;
					Count = count;
				}
			}

			internal static void Enter(object __instance, out State __state)
			{
				__state = new State(_loadingInventory, _savedCount);
				_loadingInventory = __instance;
				_savedCount = 0;
			}

			internal static void Exit(State __state)
			{
				_loadingInventory = __state.Inventory;
				_savedCount = __state.Count;
			}
		}

		private static class SavedStackScope
		{
			internal static void Enter(object __instance, int __1, out int __state)
			{
				__state = _savedCount;
				_savedCount = ((__instance == _loadingInventory) ? __1 : 0);
			}

			internal static void Exit(int __state)
			{
				_savedCount = __state;
			}
		}

		private static class SlotTransfer
		{
			internal static bool Before(object __instance, object __1, ref int __2, int __3, int __4, ref bool __result, out bool __state)
			{
				__state = false;
				if (__1 == null)
				{
					return true;
				}
				int num = ItemLimit(__1, __instance);
				if (Math.Min(_game.Stack(__1), __2) <= num)
				{
					return true;
				}
				int num2 = SlotRoom(__instance, __1, _game.Slot(__instance, __3, __4), num);
				if (num2 == 0)
				{
					__result = false;
					return false;
				}
				__2 = num2;
				__state = true;
				return true;
			}

			internal static void After(ref bool __result, bool __state)
			{
				if (__state)
				{
					__result = false;
				}
			}
		}

		private const int ChestCapacity = 1000;

		private static readonly object ChestTag = new object();

		private static ConditionalWeakTable<object, object> _chests = new ConditionalWeakTable<object, object>();

		private static GameInventory _game;

		[ThreadStatic]
		private static object _readingInventory;

		[ThreadStatic]
		private static object _loadingInventory;

		[ThreadStatic]
		private static int _savedCount;

		internal static int Install(Harmony harmony, GameInventory game)
		{
			_game = game;
			MethodInfo[] source = (from method in game.InventoryType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where !method.IsStatic && !method.ContainsGenericParameters
				select method).ToArray();
			MethodInfo[] array = source.Where(ReadsMaximum).ToArray();
			if (!array.Any((MethodInfo method) => method.Name == "AddItem"))
			{
				throw new MissingMemberException("Inventory.AddItem no longer reads the native stack limit.");
			}
			MethodInfo[] array2 = array;
			foreach (MethodInfo methodInfo in array2)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, Hook("InventoryLimits"), (HarmonyMethod)null, (HarmonyMethod)null);
			}
			int num2 = 0;
			HashSet<MethodInfo> hashSet = new HashSet<MethodInfo>();
			Type[] nestedTypes = game.InventoryType.GetNestedTypes(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			for (int num = 0; num < nestedTypes.Length; num++)
			{
				array2 = nestedTypes[num].GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo2 in array2)
				{
					if (!methodInfo2.Name.StartsWith("<", StringComparison.Ordinal) || !ReadsMaximum(methodInfo2))
					{
						continue;
					}
					int num4 = methodInfo2.Name.IndexOf('>');
					if (num4 < 2)
					{
						continue;
					}
					string name = methodInfo2.Name.Substring(1, num4 - 1);
					MethodInfo[] array3 = source.Where((MethodInfo method) => method.Name == name).ToArray();
					if (array3.Length == 0)
					{
						continue;
					}
					MethodInfo[] array4 = array3;
					foreach (MethodInfo methodInfo3 in array4)
					{
						if (hashSet.Add(methodInfo3))
						{
							harmony.Patch((MethodBase)methodInfo3, Hook(typeof(ReadingScope), "Enter"), (HarmonyMethod)null, (HarmonyMethod)null, Hook(typeof(ReadingScope), "Exit"), (HarmonyMethod)null);
						}
					}
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, Hook("ClosureLimits"), (HarmonyMethod)null, (HarmonyMethod)null);
					num2++;
				}
			}
			harmony.Patch((MethodBase)game.ContainerLoad, Hook("RegisterChest"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)game.ContainerAwake, (HarmonyMethod)null, Hook("RegisterChest"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (game.AddDefaultItems != null)
			{
				harmony.Patch((MethodBase)game.AddDefaultItems, Hook("RegisterChest"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			harmony.Patch((MethodBase)game.LoadInventory, Hook(typeof(LoadScope), "Enter"), (HarmonyMethod)null, (HarmonyMethod)null, Hook(typeof(LoadScope), "Exit"), (HarmonyMethod)null);
			foreach (MethodInfo item in source.Where(IsSavedStackLoader))
			{
				harmony.Patch((MethodBase)item, Hook(typeof(SavedStackScope), "Enter"), (HarmonyMethod)null, (HarmonyMethod)null, Hook(typeof(SavedStackScope), "Exit"), (HarmonyMethod)null);
			}
			harmony.Patch((MethodBase)game.AddItem, Hook("AddOversized"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)game.MoveItem, Hook("MoveOversized"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)game.MoveAll, Hook("MoveAllOversized"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)game.MoveAt, Hook(typeof(SlotTransfer), "Before"), Hook(typeof(SlotTransfer), "After"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)game.DropItem, Hook("DropOversized"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (game.GridDrop != null)
			{
				harmony.Patch((MethodBase)game.GridDrop, Hook("PreventOversizedSwap"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (game.GridUpdate != null)
			{
				harmony.Patch((MethodBase)game.GridUpdate, (HarmonyMethod)null, (HarmonyMethod)null, Hook("GridLimits"), (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (game.CanCheckDrag)
			{
				harmony.Patch((MethodBase)game.CanDropDrag, Hook("ChestTargetHighlight"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			return array.Length + num2;
		}

		private static bool IsSavedStackLoader(MethodInfo method)
		{
			if (method.Name != "AddItem" || method.ReturnType != typeof(bool))
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			if (parameters.Length >= 9 && (parameters[0].ParameterType == typeof(int) || parameters[0].ParameterType == typeof(string)) && parameters[1].ParameterType == typeof(int))
			{
				return parameters[2].ParameterType == typeof(float);
			}
			return false;
		}

		private static HarmonyMethod Hook(string method)
		{
			return Hook(typeof(ChestPatches), method);
		}

		private static HarmonyMethod Hook(Type type, string method)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			return new HarmonyMethod(type.GetMethod(method, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
		}

		private static bool ReadsMaximum(MethodInfo method)
		{
			if (!method.ContainsGenericParameters && method.GetMethodBody() != null)
			{
				return PatchProcessor.GetOriginalInstructions((MethodBase)method, (ILGenerator)null).Any(IsMaximumRead);
			}
			return false;
		}

		private static bool IsMaximumRead(CodeInstruction instruction)
		{
			if (instruction.opcode == OpCodes.Ldfld)
			{
				return object.Equals(instruction.operand, _game.MaximumField);
			}
			return false;
		}

		private static IEnumerable<CodeInstruction> InventoryLimits(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo limit = typeof(ChestPatches).GetMethod("Limit", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (CodeInstruction instruction in instructions)
			{
				yield return instruction;
				if (IsMaximumRead(instruction))
				{
					yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					yield return new CodeInstruction(OpCodes.Call, (object)limit);
				}
			}
		}

		private static IEnumerable<CodeInstruction> ClosureLimits(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo limit = typeof(ChestPatches).GetMethod("CurrentLimit", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (CodeInstruction instruction in instructions)
			{
				yield return instruction;
				if (IsMaximumRead(instruction))
				{
					yield return new CodeInstruction(OpCodes.Call, (object)limit);
				}
			}
		}

		private static IEnumerable<CodeInstruction> GridLimits(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
		{
			MethodInfo limit = typeof(ChestPatches).GetMethod("Limit", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo intToString = typeof(int).GetMethod("ToString", Type.EmptyTypes);
			List<CodeInstruction> code = instructions.ToList();
			LocalBuilder displayLimit = null;
			for (int i = 0; i < code.Count; i++)
			{
				CodeInstruction instruction = code[i];
				bool formatsAddress = instruction.opcode == OpCodes.Ldflda && object.Equals(instruction.operand, _game.MaximumField) && i + 1 < code.Count && code[i + 1].opcode == OpCodes.Call && object.Equals(code[i + 1].operand, intToString);
				if (formatsAddress)
				{
					instruction.opcode = OpCodes.Ldfld;
				}
				yield return instruction;
				if (!IsMaximumRead(instruction))
				{
					continue;
				}
				yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
				yield return new CodeInstruction(OpCodes.Ldfld, (object)_game.GridInventory);
				yield return new CodeInstruction(OpCodes.Call, (object)limit);
				if (formatsAddress)
				{
					if (displayLimit == null)
					{
						displayLimit = generator.DeclareLocal(typeof(int));
					}
					yield return new CodeInstruction(OpCodes.Stloc, (object)displayLimit);
					yield return new CodeInstruction(OpCodes.Ldloca, (object)displayLimit);
				}
			}
		}

		private static void RegisterChest(object __instance)
		{
			if (!_game.IsChest(__instance))
			{
				return;
			}
			object obj = _game.ContainerInventory(__instance);
			if (obj != null)
			{
				_chests.GetValue(obj, (object _) => ChestTag);
			}
		}

		private static bool IsChest(object inventory)
		{
			object value;
			if (inventory != null)
			{
				return _chests.TryGetValue(inventory, out value);
			}
			return false;
		}

		private static int Limit(int normal, object inventory)
		{
			if (normal <= 1 || !IsChest(inventory))
			{
				return normal;
			}
			if (inventory != _loadingInventory)
			{
				return 1000;
			}
			return Math.Max(1000, _savedCount);
		}

		private static int CurrentLimit(int normal)
		{
			return Limit(normal, _readingInventory);
		}

		private static int ItemLimit(object item, object inventory)
		{
			return Math.Max(1, Limit(_game.NormalLimit(item), inventory));
		}

		private static bool AddOversized(object __instance, object __0, ref bool __result)
		{
			if (__0 == null)
			{
				return true;
			}
			int num = ItemLimit(__0, __instance);
			int num2 = _game.Stack(__0);
			if (num2 <= num || _game.Contains(__instance, __0))
			{
				return true;
			}
			__result = TransferPieces(__instance, null, __0, num2, num);
			return false;
		}

		private static bool MoveOversized(object __instance, object __0, object __1)
		{
			if (__0 == null || __1 == null || __0 == __instance || !_game.Contains(__0, __1))
			{
				return true;
			}
			int num = ItemLimit(__1, __instance);
			int num2 = _game.Stack(__1);
			if (num2 <= num)
			{
				return true;
			}
			TransferPieces(__instance, __0, __1, num2, num);
			return false;
		}

		private static bool MoveAllOversized(object __instance, object __0)
		{
			if (__0 == null || __instance == __0 || (!IsChest(__0) && !IsChest(__instance)))
			{
				return true;
			}
			object[] array = _game.Items(__0);
			if (!array.Any((object item2) => _game.Stack(item2) > ItemLimit(item2, __instance)))
			{
				return true;
			}
			object[] array2 = array;
			foreach (object item in array2)
			{
				if (_game.Contains(__0, item))
				{
					_game.Move(__instance, __0, item);
				}
			}
			return false;
		}

		private static bool TransferPieces(object to, object from, object item, int remaining, int maximum)
		{
			while (remaining > 0)
			{
				int num = Math.Min(maximum, remaining);
				object item2 = _game.Piece(item, num);
				bool flag = false;
				int num2 = 0;
				try
				{
					flag = _game.Add(to, item2);
				}
				finally
				{
					num2 = ((flag || _game.Contains(to, item2)) ? num : Math.Max(0, Math.Min(num, num - _game.Stack(item2))));
					if (num2 > 0)
					{
						if (from == null)
						{
							_game.SetStack(item, remaining - num2);
						}
						else
						{
							_game.Remove(from, item, num2);
						}
					}
				}
				remaining -= num2;
				if (num2 < num)
				{
					return false;
				}
			}
			return true;
		}

		private static int SlotRoom(object inventory, object item, object target, int limit)
		{
			if (target == null)
			{
				return limit;
			}
			if (target == item || !_game.SameType(item, target))
			{
				return 0;
			}
			return Math.Max(0, Math.Min(limit, ItemLimit(target, inventory) - _game.Stack(target)));
		}

		private static bool PreventOversizedSwap(object __instance, object __0, object __1, int __2, object __3, ref bool __result)
		{
			object value = _game.GridInventory.GetValue(__instance);
			if (value == null || __0 == null || __1 == null || __2 <= 0 || (!IsChest(value) && !IsChest(__0)) || !_game.Contains(__0, __1))
			{
				return true;
			}
			int x = _game.X(__3);
			int y = _game.Y(__3);
			object obj = _game.Slot(value, x, y);
			if (obj == __1)
			{
				return true;
			}
			int num = _game.Stack(__1);
			int num2 = Math.Min(num, __2);
			int num3 = ItemLimit(__1, value);
			bool flag = obj != null && _game.Stack(obj) > ItemLimit(obj, __0);
			if (num2 <= num3 && !flag)
			{
				return true;
			}
			int num4 = Math.Min(num2, SlotRoom(value, __1, obj, num3));
			if (num4 > 0)
			{
				_game.MoveSlot(value, __0, __1, num4, x, y);
			}
			int num5 = (_game.Contains(__0, __1) ? _game.Stack(__1) : 0);
			__result = num - num5 >= num2;
			return false;
		}

		private static bool ChestTargetHighlight(object __instance, object __0, ref bool __result)
		{
			if (__0 == null)
			{
				return true;
			}
			object inventory = _game.CurrentChest(__instance);
			if (!IsChest(inventory) || !_game.Contains(inventory, __0))
			{
				return true;
			}
			object value = _game.DragItem.GetValue(__instance);
			if (value == null || value == __0 || !_game.SameType(__0, value))
			{
				return true;
			}
			__result = _game.Stack(__0) < ItemLimit(__0, inventory);
			return false;
		}

		private static bool DropOversized(object __0, int __1, Vector3 __2, Quaternion __3, ref object __result)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (__0 == null)
			{
				return true;
			}
			int num = _game.NormalLimit(__0);
			int num2 = ((__1 > 0) ? __1 : _game.Stack(__0));
			if (num <= 1 || num2 <= num)
			{
				return true;
			}
			List<object> list = new List<object>();
			try
			{
				int num3 = num2;
				while (num3 > 0)
				{
					int num4 = Math.Min(num3, num);
					object item = _game.Piece(__0, num4);
					object obj = _game.Spawn(item, num4, __2, __3);
					if (obj == null)
					{
						throw new InvalidOperationException("Valheim did not create the item drop.");
					}
					list.Add(obj);
					num3 -= num4;
				}
			}
			catch
			{
				foreach (object item2 in list)
				{
					_game.UndoDrop(item2);
				}
				throw;
			}
			__result = list[0];
			return false;
		}

		internal static void Reset()
		{
			_game = null;
			_chests = new ConditionalWeakTable<object, object>();
			_readingInventory = null;
			_loadingInventory = null;
			_savedCount = 0;
		}
	}
	internal sealed class GameInventory
	{
		internal const BindingFlags Members = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal readonly Type InventoryType;

		internal readonly Type ContainerType;

		internal readonly Type GridType;

		internal readonly Type GuiType;

		internal readonly FieldInfo MaximumField;

		internal readonly FieldInfo GridInventory;

		internal readonly FieldInfo DragItem;

		internal readonly FieldInfo OpenContainer;

		internal readonly MethodInfo AddItem;

		internal readonly MethodInfo MoveItem;

		internal readonly MethodInfo MoveAt;

		internal readonly MethodInfo MoveAll;

		internal readonly MethodInfo LoadInventory;

		internal readonly MethodInfo ContainerAwake;

		internal readonly MethodInfo ContainerLoad;

		internal readonly MethodInfo AddDefaultItems;

		internal readonly MethodInfo GridDrop;

		internal readonly MethodInfo GridUpdate;

		internal readonly MethodInfo CanDropDrag;

		internal readonly MethodInfo DropItem;

		private readonly FieldInfo _containerInventory;

		private readonly FieldInfo _rootOverride;

		private readonly FieldInfo _view;

		private readonly FieldInfo _stack;

		private readonly FieldInfo _shared;

		private readonly FieldInfo _x;

		private readonly FieldInfo _y;

		private readonly FieldInfo _itemName;

		private readonly FieldInfo _quality;

		private readonly FieldInfo _worldLevel;

		private readonly FieldInfo _variant;

		private readonly MethodInfo _clone;

		private readonly MethodInfo _contains;

		private readonly MethodInfo _removeAmount;

		private readonly MethodInfo _all;

		private readonly MethodInfo _at;

		private readonly MethodInfo _sameType;

		private readonly PropertyInfo _sceneInstance;

		private readonly MethodInfo _destroy;

		internal bool CanCheckDrag
		{
			get
			{
				if (CanDropDrag != null && DragItem != null)
				{
					return OpenContainer != null;
				}
				return false;
			}
		}

		internal GameInventory(bool client)
		{
			InventoryType = FindType("Inventory");
			ContainerType = FindType("Container");
			Type type = FindType("ItemDrop");
			Type type2 = type.GetNestedType("ItemData", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new TypeLoadException("ItemDrop.ItemData");
			_shared = Field(type2, "m_shared");
			MaximumField = Field(_shared.FieldType, "m_maxStackSize");
			_stack = Field(type2, "m_stack");
			_clone = Method(type2, "Clone");
			_sameType = type2.GetMethod("IsSameType", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { type2 }, null);
			if (_sameType == null)
			{
				_itemName = Field(_shared.FieldType, "m_name");
				_quality = Field(type2, "m_quality");
				_worldLevel = type2.GetField("m_worldLevel", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_variant = type2.GetField("m_variant", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
			_containerInventory = Field(ContainerType, "m_inventory");
			_rootOverride = ContainerType.GetField("m_rootObjectOverride", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_view = ContainerType.GetField("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			ContainerAwake = Method(ContainerType, "Awake");
			ContainerLoad = Method(ContainerType, "Load");
			AddDefaultItems = ContainerType.GetMethod("AddDefaultItems", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			AddItem = Method(InventoryType, "AddItem", type2);
			MoveItem = Method(InventoryType, "MoveItemToThis", InventoryType, type2);
			MoveAt = Method(InventoryType, "MoveItemToThis", InventoryType, type2, typeof(int), typeof(int), typeof(int));
			MoveAll = Method(InventoryType, "MoveAll", InventoryType);
			LoadInventory = Method(InventoryType, "Load", FindType("ZPackage"));
			_contains = Method(InventoryType, "ContainsItem", type2);
			_removeAmount = Method(InventoryType, "RemoveItem", type2, typeof(int));
			_all = Method(InventoryType, "GetAllItems");
			_at = Method(InventoryType, "GetItemAt", typeof(int), typeof(int));
			DropItem = Method(type, "DropItem", type2, typeof(int), typeof(Vector3), typeof(Quaternion));
			Type type3 = FindType("ZNetScene");
			_sceneInstance = type3.GetProperty("instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_destroy = type3.GetMethod("Destroy", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(GameObject) }, null);
			if (client)
			{
				GridType = FindType("InventoryGrid");
				GuiType = FindType("InventoryGui");
				GridInventory = Field(GridType, "m_inventory");
				Type type4 = FindType("Vector2i");
				_x = Field(type4, "x");
				_y = Field(type4, "y");
				GridDrop = Method(GridType, "DropItem", InventoryType, type2, typeof(int), type4);
				GridUpdate = GridType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "UpdateGui");
				DragItem = GuiType.GetField("m_dragItem", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				OpenContainer = GuiType.GetField("m_currentContainer", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				CanDropDrag = GuiType.GetMethod("CanDropDragOntoItem", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { type2 }, null);
			}
		}

		internal object ContainerInventory(object container)
		{
			if (container != null)
			{
				return _containerInventory.GetValue(container);
			}
			return null;
		}

		internal object CurrentChest(object gui)
		{
			return ContainerInventory(OpenContainer?.GetValue(gui));
		}

		internal bool SameType(object item, object other)
		{
			if (_sameType != null)
			{
				return (bool)_sameType.Invoke(item, new object[1] { other });
			}
			object value = _shared.GetValue(item);
			object value2 = _shared.GetValue(other);
			if (value != null && value2 != null && object.Equals(_itemName.GetValue(value), _itemName.GetValue(value2)) && object.Equals(_quality.GetValue(item), _quality.GetValue(other)) && object.Equals(_worldLevel?.GetValue(item), _worldLevel?.GetValue(other)))
			{
				return object.Equals(_variant?.GetValue(item), _variant?.GetValue(other));
			}
			return false;
		}

		internal bool IsChest(object instance)
		{
			Component val = (Component)((instance is Component) ? instance : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			object? obj = _rootOverride?.GetValue(instance);
			Component val2 = (Component)((obj is Component) ? obj : null);
			if (Object.op_Implicit((Object)(object)val2))
			{
				return IsChestName(((Object)val2.gameObject).name);
			}
			if (IsChestName(((Object)val.gameObject).name))
			{
				return true;
			}
			object? obj2 = _view?.GetValue(instance);
			Component val3 = (Component)((obj2 is Component) ? obj2 : null);
			if (Object.op_Implicit((Object)(object)val3))
			{
				return IsChestName(((Object)val3.gameObject).name);
			}
			return false;
		}

		private static bool IsChestName(string name)
		{
			if (name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(Array.Empty<char>());
			}
			if (!name.Equals("piece_chest", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("piece_chest_", StringComparison.OrdinalIgnoreCase))
			{
				return name.StartsWith("TreasureChest_", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		internal int Stack(object item)
		{
			return (int)_stack.GetValue(item);
		}

		internal void SetStack(object item, int amount)
		{
			_stack.SetValue(item, amount);
		}

		internal int NormalLimit(object item)
		{
			object value = _shared.GetValue(item);
			if (value != null)
			{
				return (int)MaximumField.GetValue(value);
			}
			return 1;
		}

		internal object Piece(object item, int amount)
		{
			object obj = _clone.Invoke(item, null);
			SetStack(obj, amount);
			return obj;
		}

		internal bool Contains(object inventory, object item)
		{
			return (bool)_contains.Invoke(inventory, new object[1] { item });
		}

		internal bool Add(object inventory, object item)
		{
			return (bool)AddItem.Invoke(inventory, new object[1] { item });
		}

		internal object[] Items(object inventory)
		{
			return ((IEnumerable)_all.Invoke(inventory, null)).Cast<object>().ToArray();
		}

		internal void Remove(object inventory, object item, int amount)
		{
			_removeAmount.Invoke(inventory, new object[2] { item, amount });
		}

		internal void Move(object to, object from, object item)
		{
			MoveItem.Invoke(to, new object[2] { from, item });
		}

		internal void MoveSlot(object to, object from, object item, int count, int x, int y)
		{
			MoveAt.Invoke(to, new object[5] { from, item, count, x, y });
		}

		internal object Slot(object inventory, int x, int y)
		{
			return _at.Invoke(inventory, new object[2] { x, y });
		}

		internal int X(object position)
		{
			return (int)_x.GetValue(position);
		}

		internal int Y(object position)
		{
			return (int)_y.GetValue(position);
		}

		internal object Spawn(object item, int amount, Vector3 position, Quaternion rotation)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			return DropItem.Invoke(null, new object[4] { item, amount, position, rotation });
		}

		internal void UndoDrop(object drop)
		{
			Component val = (Component)((drop is Component) ? drop : null);
			if (val != null && Object.op_Implicit((Object)(object)val))
			{
				object obj = _sceneInstance?.GetValue(null, null);
				if (obj != null && _destroy != null)
				{
					_destroy.Invoke(obj, new object[1] { val.gameObject });
				}
				else
				{
					Object.Destroy((Object)(object)val.gameObject);
				}
			}
		}

		private static Type FindType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType(name, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			throw new TypeLoadException(name);
		}

		private static FieldInfo Field(Type type, string name)
		{
			return type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.Name, name);
		}

		private static MethodInfo Method(Type type, string name, params Type[] arguments)
		{
			return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, arguments, null) ?? throw new MissingMethodException(type.Name, name);
		}
	}
}