Decompiled source of InventoryLimit v0.2.2

InventoryLimit.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
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("InventoryLimit")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2")]
[assembly: AssemblyProduct("InventoryLimit")]
[assembly: AssemblyTitle("InventoryLimit")]
[assembly: AssemblyVersion("0.2.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 InventoryLimit
{
	internal static class ConfigurationSync
	{
		private static readonly MethodInfo SynchronizeChangedConfig = AccessTools.DeclaredMethod(typeof(SynchronizationManager), "SynchronizeChangedConfig", Type.EmptyTypes, (Type[])null) ?? throw new MissingMethodException(typeof(SynchronizationManager).FullName, "SynchronizeChangedConfig");

		private static int publishedSlotLimit = -1;

		internal static void Validate()
		{
			if (SynchronizeChangedConfig.IsStatic || SynchronizeChangedConfig.ReturnType != typeof(void))
			{
				throw new MissingMethodException("Unsupported Jotunn configuration synchronization signature.");
			}
		}

		internal static void Tick()
		{
			int slotLimit = Plugin.SlotLimit;
			if (slotLimit != publishedSlotLimit)
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					SynchronizeChangedConfig.Invoke(SynchronizationManager.Instance, null);
				}
				publishedSlotLimit = slotLimit;
			}
		}
	}
	internal static class InventoryCompatibility
	{
		internal const string EquipmentAndQuickSlots = "randyknapp.mods.equipmentandquickslots";

		internal const string AzuExtendedInventory = "Azumatt.AzuExtendedPlayerInventory";

		internal const string ExtraSlots = "shudnal.ExtraSlots";

		internal const string BetterArchery = "ishid4.mods.betterarchery";

		internal static void Refresh()
		{
			VanillaInventoryBackend.ConfigureBlockedReason(FindConflict());
		}

		private static string? FindConflict()
		{
			if (Chainloader.PluginInfos.ContainsKey("randyknapp.mods.equipmentandquickslots"))
			{
				return Conflict("Equipment and Quick Slots");
			}
			if (Chainloader.PluginInfos.ContainsKey("Azumatt.AzuExtendedPlayerInventory"))
			{
				return Conflict("AzuExtendedPlayerInventory");
			}
			if (Chainloader.PluginInfos.ContainsKey("shudnal.ExtraSlots"))
			{
				return Conflict("ExtraSlots");
			}
			if (Chainloader.PluginInfos.TryGetValue("ishid4.mods.betterarchery", out var value))
			{
				try
				{
					if ((((object)value.Instance)?.GetType().GetField("ConfigQuiverEnabled", BindingFlags.Static | BindingFlags.Public))?.GetValue(null) is ConfigEntry<bool> val && !val.Value)
					{
						return null;
					}
				}
				catch (Exception)
				{
				}
				return Conflict("BetterArchery's quiver");
			}
			return null;
		}

		private static string Conflict(string mod)
		{
			return "InventoryLimit: " + mod + " uses an unsupported special-slot layout without SeneaL UI. Inventory changes are blocked. Restore your previous inventory setup before continuing play, then empty its special slots and disable the conflicting feature before using standalone InventoryLimit.";
		}
	}
	internal static class InventoryPolicy
	{
		private static Action? backendTick;

		private static Action? backendShutdown;

		internal static void Configure(Action tick, Action shutdown)
		{
			backendTick = tick;
			backendShutdown = shutdown;
		}

		internal static void Tick()
		{
			ConfigurationSync.Tick();
			backendTick?.Invoke();
		}

		internal static void Shutdown()
		{
			Action? action = backendShutdown;
			backendTick = null;
			backendShutdown = null;
			action?.Invoke();
		}
	}
	internal static class MainInventoryCompaction
	{
		internal sealed class Plan
		{
			internal int Capacity { get; }

			internal int[] Destinations { get; }

			internal bool MovesItems { get; }

			internal Plan(int capacity, int[] destinations, bool movesItems)
			{
				Capacity = capacity;
				Destinations = destinations;
				MovesItems = movesItems;
			}
		}

		internal static bool TryPlan(int requested, int oldMainCells, IReadOnlyList<int> occupied, out Plan? plan)
		{
			plan = null;
			if (requested < 10 || requested > 64 || oldMainCells < 1 || occupied == null)
			{
				return false;
			}
			int num = Math.Max(requested, occupied.Count);
			HashSet<int> hashSet = new HashSet<int>();
			List<int> list = new List<int>();
			int[] array = new int[occupied.Count];
			for (int i = 0; i < occupied.Count; i++)
			{
				int num2 = occupied[i];
				if (num2 < 0 || num2 >= oldMainCells || !hashSet.Add(num2))
				{
					return false;
				}
				array[i] = num2;
				if (num2 >= num)
				{
					list.Add(i);
				}
			}
			list.Sort((int left, int right) => occupied[left].CompareTo(occupied[right]));
			int num3 = 0;
			foreach (int item in list)
			{
				for (; num3 < num && hashSet.Contains(num3); num3++)
				{
				}
				if (num3 >= num)
				{
					return false;
				}
				array[item] = num3;
				hashSet.Add(num3);
			}
			plan = new Plan(num, array, list.Count > 0);
			return true;
		}
	}
	[BepInPlugin("com.skyowner.inventorylimit", "InventoryLimit", "0.2.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.skyowner.inventorylimit";

		public const string PluginName = "InventoryLimit";

		public const string PluginVersion = "0.2.2";

		public const string SeneaLUiGuid = "seneaL.valheim.ui";

		public const string SeneaLUiVersion = "1.1.6";

		private const int MinimumSlotLimit = 10;

		private const int MaximumSlotLimit = 64;

		private const int DefaultSlotLimit = 32;

		private static readonly Version RequiredSeneaLUiVersion = new Version(1, 1, 6);

		private static ConfigEntry<int>? slotLimitConfig;

		private Harmony? harmony;

		private bool initialized;

		public static Plugin Instance { get; private set; } = null;

		public static int SlotLimit
		{
			get
			{
				int val = slotLimitConfig?.Value ?? 32;
				return Math.Max(10, Math.Min(64, val));
			}
		}

		private void Awake()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			Instance = this;
			slotLimitConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Inventory", "Slots", 32, new ConfigDescription("Server-wide main inventory limit: 10–64 slots, including hotbar slots 1–8. SeneaL UI is not required; optional support for version 1.1.6 preserves its separate food and equipment slots. Items are compacted when shrinking; existing excess items are preserved until moved out. Designed to override other mods' main-grid size settings at runtime. Compatibility with other inventory mods is not guaranteed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 64), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			harmony = new Harmony("com.skyowner.inventorylimit");
			try
			{
				ConfigurationSync.Validate();
				string arg;
				if (Chainloader.PluginInfos.TryGetValue("seneaL.valheim.ui", out var value))
				{
					RequireSupportedSeneaLUiVersion(value);
					InitializeSeneaLAdapter(harmony);
					arg = "SeneaL UI compatibility";
				}
				else
				{
					VanillaInventoryBackend.Validate();
					InventoryCompatibility.Refresh();
					InventoryPolicy.Configure(TickVanilla, VanillaInventoryBackend.Shutdown);
					VanillaInventoryBackend.Initialize(harmony);
					arg = "standard inventory";
				}
				initialized = true;
				LogInfo(string.Format("InventoryLimit {0}: {1} slots, {2}.", "0.2.2", SlotLimit, arg));
			}
			catch (Exception arg2)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("Could not initialize {0}; no inventory limit will be applied: {1}", "InventoryLimit", arg2));
				InventoryPolicy.Shutdown();
				TryUnpatch();
				((Behaviour)this).enabled = false;
			}
		}

		private void Update()
		{
			if (initialized)
			{
				InventoryPolicy.Tick();
			}
		}

		private void OnDestroy()
		{
			initialized = false;
			InventoryPolicy.Shutdown();
			TryUnpatch();
			if (Instance == this)
			{
				Instance = null;
			}
		}

		public static void LogWarning(string message)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)message);
			}
		}

		public static void LogInfo(string message)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)message);
			}
		}

		private static void TickVanilla()
		{
			InventoryCompatibility.Refresh();
			VanillaInventoryBackend.Tick();
		}

		private static void RequireSupportedSeneaLUiVersion(PluginInfo pluginInfo)
		{
			BepInPlugin metadata = pluginInfo.Metadata;
			Version version = ((metadata != null) ? metadata.Version : null);
			string text = ((metadata != null) ? metadata.VersionExtra : null) ?? string.Empty;
			if ((object)version == null || !version.Equals(RequiredSeneaLUiVersion) || !string.IsNullOrEmpty(text))
			{
				string text2 = (((object)version == null) ? "unknown" : (version?.ToString() + text));
				throw new NotSupportedException("Installed SeneaL UI " + text2 + " is unsupported. Use 1.1.6, or safely remove SeneaL UI to use the standard inventory backend.");
			}
		}

		private static void InitializeSeneaLAdapter(Harmony harmony)
		{
			Assembly assembly = Assembly.LoadFrom(Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location), "InventoryLimit.SeneaL.dll"));
			if (assembly.GetName().Version != typeof(Plugin).Assembly.GetName().Version)
			{
				throw new InvalidOperationException("InventoryLimit and its SeneaL adapter must have the same version.");
			}
			Type type = assembly.GetType("InventoryLimit.SeneaLAdapterBootstrap", throwOnError: true);
			Action action = BindAction(type, "Validate");
			Action tick = BindAction(type, "Tick");
			Action shutdown = BindAction(type, "Shutdown");
			MethodInfo methodInfo = type.GetMethod("Initialize", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Harmony) }, null) ?? throw new MissingMethodException(type.FullName, "Initialize");
			if (methodInfo.ReturnType != typeof(void))
			{
				throw new MissingMethodException("Invalid adapter Initialize signature.");
			}
			action();
			InventoryPolicy.Configure(tick, shutdown);
			((Action<Harmony>)Delegate.CreateDelegate(typeof(Action<Harmony>), methodInfo))(harmony);
		}

		private static Action BindAction(Type type, string name)
		{
			MethodInfo methodInfo = type.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null) ?? throw new MissingMethodException(type.FullName, name);
			if (methodInfo.ReturnType != typeof(void))
			{
				throw new MissingMethodException(type.FullName, name);
			}
			return (Action)Delegate.CreateDelegate(typeof(Action), methodInfo);
		}

		private void TryUnpatch()
		{
			if (harmony == null)
			{
				return;
			}
			try
			{
				harmony.UnpatchSelf();
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("Не удалось снять патчи {0}: {1}", "InventoryLimit", arg));
			}
			finally
			{
				harmony = null;
			}
		}
	}
	internal static class VanillaInventoryBackend
	{
		private sealed class State
		{
			internal Player Player;

			internal int Loading;

			internal bool Ready;

			internal bool Blocked;

			internal bool Warned;

			internal int DisplayCapacity = 32;

			internal int ReportedLimit = -1;

			internal int? SerializedStack;
		}

		[HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })]
		private static class PlayerLoadPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(Player __instance)
			{
				State state = Register(__instance);
				state.Ready = false;
				state.Blocked = false;
				state.Warned = false;
				state.Loading++;
			}

			[HarmonyPriority(0)]
			private static Exception? Finalizer(Player __instance, Exception? __exception)
			{
				State state = Register(__instance);
				state.Loading = Math.Max(0, state.Loading - 1);
				if (__exception == null)
				{
					CompleteLoad(__instance, state);
				}
				else
				{
					state.Blocked = true;
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "SetLocalPlayer")]
		private static class LocalPlayerPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Player __instance)
			{
				State state = Register(__instance);
				if (state.Loading == 0)
				{
					CompleteLoad(__instance, state);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Save")]
		private static class PlayerSavePatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(Player __instance)
			{
				if (TryState(((Humanoid)__instance).GetInventory(), out State state))
				{
					Apply(((Humanoid)__instance).GetInventory(), state);
				}
			}
		}

		[HarmonyPatch]
		private static class InventoryLoadPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Inventory), "Load", new Type[1] { typeof(ZPackage) }, (Type[])null);
				yield return AccessTools.Method(typeof(Inventory), "Load", new Type[2]
				{
					typeof(ZPackage),
					typeof(bool)
				}, (Type[])null);
			}

			[HarmonyPriority(800)]
			private static void Prefix(Inventory __instance)
			{
				if (TryState(__instance, out State state))
				{
					state.Loading++;
				}
			}

			[HarmonyPriority(0)]
			private static Exception? Finalizer(Inventory __instance, Exception? __exception)
			{
				if (TryState(__instance, out State state))
				{
					state.Loading = Math.Max(0, state.Loading - 1);
					Height.SetValue(__instance, Math.Max(__instance.GetHeight(), RequiredRows(__instance)));
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Inventory), "SetHeight")]
		private static class SetHeightPatch
		{
			[HarmonyPriority(0)]
			private static void Prefix(Inventory __instance, ref int height)
			{
				if (TryState(__instance, out State state))
				{
					int val = RequiredRows(__instance);
					height = ((state.Blocked || !state.Ready || state.Loading != 0) ? Math.Max(height, Math.Max(__instance.GetHeight(), val)) : Math.Max((Math.Max(Plugin.SlotLimit, __instance.NrOfItems()) + 8 - 1) / 8, val));
				}
			}

			[HarmonyPriority(0)]
			private static void Postfix(Inventory __instance)
			{
				if (TryState(__instance, out State state))
				{
					Apply(__instance, state);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "DropInvalidItems")]
		private static class DropInvalidPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Humanoid __instance)
			{
				State state;
				return !TryState(__instance.GetInventory(), out state);
			}
		}

		[HarmonyPatch(typeof(Inventory), "FindEmptySlot")]
		private static class FindEmptyPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Inventory __instance, bool topFirst, ref Vector2i __result)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				if (!Active(__instance, out State state))
				{
					return;
				}
				__result = new Vector2i(-1, -1);
				if (FreeCells(__instance, state) == 0)
				{
					return;
				}
				int num = (Plugin.SlotLimit + 8 - 1) / 8;
				for (int i = 0; i < num; i++)
				{
					int num2 = (topFirst ? i : (num - 1 - i));
					for (int j = 0; j < 8 && num2 * 8 + j < Plugin.SlotLimit; j++)
					{
						if (__instance.GetItemAt(j, num2) == null)
						{
							__result = new Vector2i(j, num2);
							return;
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "GetEmptySlots")]
		private static class EmptyCountPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Inventory __instance, ref int __result)
			{
				if (Active(__instance, out State state))
				{
					__result = FreeCells(__instance, state);
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "HaveEmptySlot")]
		private static class HasEmptyPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Inventory __instance, ref bool __result)
			{
				if (Active(__instance, out State state))
				{
					__result = FreeCells(__instance, state) > 0;
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int)
		})]
		private static class CanAddPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Inventory __instance, ItemData item, int stack, ref bool __result)
			{
				if (!Active(__instance, out State state))
				{
					return;
				}
				long num = (long)FreeCells(__instance, state) * (long)item.m_shared.m_maxStackSize;
				if (!state.Blocked && item.m_shared.m_maxStackSize > 1)
				{
					foreach (ItemData allItem in __instance.GetAllItems())
					{
						if (allItem.m_shared.m_name == item.m_shared.m_name && allItem.m_quality == item.m_quality && allItem.m_worldLevel == item.m_worldLevel)
						{
							num += Math.Max(0, allItem.m_shared.m_maxStackSize - allItem.m_stack);
						}
					}
				}
				__result = !state.Blocked && num >= ((stack <= 0) ? item.m_stack : stack);
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static class AddAtPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Inventory __instance, ItemData item, int amount, int x, int y, ref bool skipValidPositionCheck, ref bool __result)
			{
				//IL_004e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0053: Unknown result type (might be due to invalid IL or missing references)
				if (TryState(__instance, out State state) && state.Loading != 0)
				{
					ItemData val = item.Clone();
					val.m_stack = state.SerializedStack ?? Math.Min(amount, item.m_stack);
					val.m_gridPos = new Vector2i(x, y);
					__instance.GetAllItems().Add(val);
					item.m_stack = Math.Max(0, item.m_stack - val.m_stack);
					if (y >= 0)
					{
						Height.SetValue(__instance, Math.Max(__instance.GetHeight(), y + 1));
					}
					__result = true;
					return false;
				}
				if (CellAllowed(__instance, x, y) && (!Active(__instance, out state) || __instance.GetItemAt(x, y) != null || FreeCells(__instance, state) > 0))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(int),
			typeof(int),
			typeof(float),
			typeof(Vector2i),
			typeof(bool),
			typeof(int),
			typeof(int),
			typeof(long),
			typeof(string),
			typeof(Dictionary<string, string>),
			typeof(int),
			typeof(bool),
			typeof(bool),
			typeof(bool)
		})]
		private static class SerializedStackPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(Inventory __instance, int stack, out int? __state)
			{
				__state = null;
				if (TryState(__instance, out State state) && state.Loading != 0)
				{
					__state = state.SerializedStack;
					state.SerializedStack = stack;
				}
			}

			[HarmonyPriority(0)]
			private static Exception? Finalizer(Inventory __instance, int? __state, Exception? __exception)
			{
				if (TryState(__instance, out State state) && state.Loading != 0)
				{
					state.SerializedStack = __state;
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(Vector2i)
		})]
		private static class AddPositionPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Inventory __instance, Vector2i pos, ref bool __result)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: 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)
				if (CellAllowed(__instance, pos.x, pos.y) && (!Active(__instance, out State state) || __instance.GetItemAt(pos.x, pos.y) != null || FreeCells(__instance, state) > 0))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
		private static class AddBlockedPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Inventory __instance, ref bool __result)
			{
				if (!Active(__instance, out State state) || !state.Blocked)
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch]
		private static class BlockedTransferPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Inventory), "MoveAll", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Inventory), "StackAll", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[2]
				{
					typeof(Inventory),
					typeof(ItemData)
				}, (Type[])null);
				yield return AccessTools.Method(typeof(Inventory), "MoveItemToThis", new Type[5]
				{
					typeof(Inventory),
					typeof(ItemData),
					typeof(int),
					typeof(int),
					typeof(int)
				}, (Type[])null);
			}

			[HarmonyPriority(800)]
			private static bool Prefix(Inventory __instance, Inventory fromInventory)
			{
				if (!Blocked(__instance))
				{
					return !Blocked(fromInventory);
				}
				return false;
			}
		}

		[HarmonyPatch]
		private static class BlockedSelectionPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(InventoryGui), "OnSelectedItem", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(InventoryGui), "OnRightClickItem", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(InventoryGui), "OnReleasedItem", (Type[])null, (Type[])null);
			}

			[HarmonyPriority(800)]
			private static bool Prefix()
			{
				Player localPlayer = Player.m_localPlayer;
				if (Object.op_Implicit((Object)(object)localPlayer))
				{
					return !Blocked(((Humanoid)localPlayer).GetInventory());
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
		private static class CraftingPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(InventoryGui __instance, Player player)
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Expected O, but got Unknown
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (!Active(inventory, out State state))
				{
					return true;
				}
				ItemData val = (ItemData)CraftUpgradeItem.GetValue(__instance);
				if (val != null && !Dragging)
				{
					Apply(inventory, state);
				}
				if (state.Blocked)
				{
					((Character)player).Message((MessageType)2, configuredBlockReason ?? "InventoryLimit preserved an unsupported inventory layout. Restore the previous inventory mod before continuing play; do not use this character until restored. For SeneaL: restore SeneaL UI 1.1.6, empty all special slots, save and exit, then disable SeneaL again.", 0, (Sprite)null, false);
					return false;
				}
				if (val == null || (inventory.NrOfItems() <= Plugin.SlotLimit && CellAllowed(inventory, val.m_gridPos.x, val.m_gridPos.y)))
				{
					return true;
				}
				((Character)player).Message((MessageType)2, (inventory.NrOfItems() > Plugin.SlotLimit) ? "InventoryLimit: move excess stacks into a container before upgrading items." : "InventoryLimit: finish dragging before upgrading this item.", 0, (Sprite)null, false);
				return false;
			}
		}

		[HarmonyPatch(typeof(Player), "CreateTombStone", new Type[] { })]
		private static class BlockedTombStonePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Player __instance)
			{
				return !Blocked(((Humanoid)__instance).GetInventory());
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UseItem")]
		private static class BlockedUsePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Humanoid __instance, Inventory inventory)
			{
				return !Blocked(inventory ?? __instance.GetInventory());
			}
		}

		[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
		private static class GridDropPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemData item, int amount, Vector2i pos, ref bool __result)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				Inventory inventory = __instance.GetInventory();
				ItemData itemAt = inventory.GetItemAt(pos.x, pos.y);
				if (itemAt == item)
				{
					return true;
				}
				bool flag = itemAt != null && (itemAt.m_shared.m_name != item.m_shared.m_name || (item.m_shared.m_maxQuality > 1 && itemAt.m_quality != item.m_quality) || itemAt.m_shared.m_maxStackSize == 1) && item.m_stack == amount;
				if ((!Active(fromInventory, out State state) || !state.Blocked) && CellAllowed(inventory, pos.x, pos.y) && (!flag || CellAllowed(fromInventory, item.m_gridPos.x, item.m_gridPos.y)) && (!flag || ((!Active(inventory, out State state2) || inventory.NrOfItems() <= Plugin.SlotLimit) && (!Active(fromInventory, out state2) || fromInventory.NrOfItems() <= Plugin.SlotLimit))))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(InventoryGrid), "UpdateInventory")]
		private static class GridUpdatePatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(InventoryGrid __instance, Inventory inventory)
			{
				if (TryState(inventory, out State state))
				{
					Apply(inventory, state);
					if (state.Ready && !state.Blocked)
					{
						ClampSelection(__instance, state.DisplayCapacity);
					}
				}
			}

			[HarmonyPriority(0)]
			private static void Postfix(InventoryGrid __instance, Inventory inventory)
			{
				if (Active(inventory, out State state) && !state.Blocked)
				{
					List<InventoryElement> list = (List<InventoryElement>)Elements.GetValue(__instance);
					for (int i = 0; i < list.Count; i++)
					{
						((Component)list[i]).gameObject.SetActive(i < state.DisplayCapacity);
					}
					ClampSelection(__instance, state.DisplayCapacity);
					if (Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)InventoryGui.instance.m_playerGrid == (Object)(object)__instance)
					{
						InventoryGui.instance.SetInventorySize(inventory.GetHeight());
					}
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGrid), "GetGamepadSelectedElement")]
		private static class GamepadElementPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(InventoryGrid __instance)
			{
				Inventory inventory = __instance.GetInventory();
				if (inventory != null && Active(inventory, out State state) && !state.Blocked)
				{
					ClampSelection(__instance, state.DisplayCapacity);
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "SetInventorySize")]
		private static class GuiSizePatch
		{
			[HarmonyPriority(0)]
			private static void Prefix(ref int rows)
			{
				Player localPlayer = Player.m_localPlayer;
				if (Object.op_Implicit((Object)(object)localPlayer) && TryState(((Humanoid)localPlayer).GetInventory(), out State _))
				{
					rows = ((Humanoid)localPlayer).GetInventory().GetHeight();
				}
			}
		}

		private const int Width = 8;

		private const string SavedCapacity = "inventorylimit.vanilla.capacity";

		private const string LayoutWarning = "InventoryLimit preserved an unsupported inventory layout. Restore the previous inventory mod before continuing play; do not use this character until restored. For SeneaL: restore SeneaL UI 1.1.6, empty all special slots, save and exit, then disable SeneaL again.";

		private static ConditionalWeakTable<Inventory, State> states = new ConditionalWeakTable<Inventory, State>();

		private static readonly FieldInfo Height = RequireField(typeof(Inventory), "m_height");

		private static readonly FieldInfo DragItem = RequireField(typeof(InventoryGui), "m_dragItem");

		private static readonly FieldInfo CraftUpgradeItem = RequireField(typeof(InventoryGui), "m_craftUpgradeItem");

		private static readonly FieldInfo Elements = RequireField(typeof(InventoryGrid), "m_elements");

		private static readonly FieldInfo Selected = RequireField(typeof(InventoryGrid), "m_selected");

		private static float nextCheck;

		private static string? configuredBlockReason;

		private static bool Dragging
		{
			get
			{
				if (Object.op_Implicit((Object)(object)InventoryGui.instance))
				{
					return DragItem.GetValue(InventoryGui.instance) != null;
				}
				return false;
			}
		}

		internal static void ConfigureBlockedReason(string? reason)
		{
			configuredBlockReason = reason;
		}

		internal static void Validate()
		{
			_ = Height;
			_ = DragItem;
			_ = CraftUpgradeItem;
			_ = Elements;
			_ = Selected;
			Type[] nestedTypes = typeof(VanillaInventoryBackend).GetNestedTypes(BindingFlags.NonPublic);
			foreach (Type type in nestedTypes)
			{
				if (Attribute.IsDefined(type, typeof(HarmonyPatch)) && type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Length == 0)
				{
					throw new MissingMethodException(type.FullName);
				}
			}
		}

		internal static void Initialize(Harmony harmony)
		{
			Type[] nestedTypes = typeof(VanillaInventoryBackend).GetNestedTypes(BindingFlags.NonPublic);
			foreach (Type type in nestedTypes)
			{
				if (Attribute.IsDefined(type, typeof(HarmonyPatch)))
				{
					harmony.CreateClassProcessor(type).Patch();
				}
			}
		}

		internal static void Shutdown()
		{
			states = new ConditionalWeakTable<Inventory, State>();
			nextCheck = 0f;
			configuredBlockReason = null;
		}

		private static FieldInfo RequireField(Type type, string name)
		{
			return AccessTools.Field(type, name) ?? throw new MissingFieldException(type.FullName, name);
		}

		private static State Register(Player player)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			State value = states.GetValue(inventory, (Inventory _) => new State());
			value.Player = player;
			return value;
		}

		private static bool TryState(Inventory inventory, out State state)
		{
			return states.TryGetValue(inventory, out state);
		}

		internal static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer) && !(Time.unscaledTime < nextCheck))
			{
				nextCheck = Time.unscaledTime + 0.2f;
				State state = Register(localPlayer);
				if (!state.Ready && state.Loading == 0)
				{
					CompleteLoad(localPlayer, state);
				}
				Apply(((Humanoid)localPlayer).GetInventory(), state);
				if (state.Blocked && !state.Warned)
				{
					state.Warned = true;
					string text = configuredBlockReason ?? "InventoryLimit preserved an unsupported inventory layout. Restore the previous inventory mod before continuing play; do not use this character until restored. For SeneaL: restore SeneaL UI 1.1.6, empty all special slots, save and exit, then disable SeneaL again.";
					Plugin.LogWarning(text);
					((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
			}
		}

		private static void CompleteLoad(Player player, State state)
		{
			state.Ready = true;
			if (!string.IsNullOrEmpty(configuredBlockReason))
			{
				state.Blocked = true;
			}
			bool flag = false;
			foreach (string key in player.m_customData.Keys)
			{
				if (key.StartsWith("seneal.", StringComparison.OrdinalIgnoreCase))
				{
					flag = true;
				}
			}
			if (flag)
			{
				List<int> list = new List<int>();
				bool flag2 = true;
				foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
				{
					if (allItem.m_gridPos.x < 0 || allItem.m_gridPos.x >= 8 || allItem.m_gridPos.y < 0 || allItem.m_gridPos.y > 4096)
					{
						flag2 = false;
					}
					list.Add(allItem.m_gridPos.y * 8 + allItem.m_gridPos.x);
				}
				if (!state.Blocked && flag2 && VanillaLayoutPolicy.CanRemoveSeneaLMetadata(player.m_customData, ((Humanoid)player).GetInventory().GetWidth(), list))
				{
					player.m_customData.Remove("seneal.slots");
					player.m_customData.Remove("seneal.extra");
					player.m_customData.Remove("seneal.locks");
					Plugin.LogInfo("Removed empty SeneaL special-slot layout metadata; all main inventory items were preserved.");
				}
				else
				{
					state.Blocked = true;
				}
			}
			Apply(((Humanoid)player).GetInventory(), state);
		}

		private static int RequiredRows(Inventory inventory)
		{
			int num = 1;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_gridPos.y >= 0)
				{
					num = Math.Max(num, allItem.m_gridPos.y + 1);
				}
			}
			return num;
		}

		private static void Apply(Inventory inventory, State state)
		{
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			if (!state.Ready || state.Loading != 0)
			{
				return;
			}
			if (!string.IsNullOrEmpty(configuredBlockReason))
			{
				state.Blocked = true;
			}
			if (inventory.GetWidth() != 8)
			{
				state.Blocked = true;
			}
			int oldMainCells = Math.Max(inventory.GetHeight(), RequiredRows(inventory)) * 8;
			List<ItemData> allItems = inventory.GetAllItems();
			List<int> list = new List<int>(allItems.Count);
			foreach (ItemData item in allItems)
			{
				if (item.m_gridPos.x < 0 || item.m_gridPos.x >= 8 || item.m_gridPos.y < 0)
				{
					state.Blocked = true;
				}
				list.Add(item.m_gridPos.y * 8 + item.m_gridPos.x);
			}
			if (state.Blocked)
			{
				Height.SetValue(inventory, Math.Max(inventory.GetHeight(), RequiredRows(inventory)));
				return;
			}
			if (Dragging)
			{
				int num = 0;
				foreach (int item2 in list)
				{
					num = Math.Max(num, item2 + 1);
				}
				state.DisplayCapacity = Math.Max(Plugin.SlotLimit, Math.Max(state.DisplayCapacity, num));
				Height.SetValue(inventory, (state.DisplayCapacity + 8 - 1) / 8);
				state.Player.m_customData["inventorylimit.vanilla.capacity"] = state.DisplayCapacity.ToString(CultureInfo.InvariantCulture);
				return;
			}
			if (!MainInventoryCompaction.TryPlan(Plugin.SlotLimit, oldMainCells, list, out MainInventoryCompaction.Plan plan) || plan == null)
			{
				state.Blocked = true;
				return;
			}
			for (int i = 0; i < allItems.Count; i++)
			{
				allItems[i].m_gridPos = new Vector2i(plan.Destinations[i] % 8, plan.Destinations[i] / 8);
			}
			state.DisplayCapacity = plan.Capacity;
			Height.SetValue(inventory, (plan.Capacity + 8 - 1) / 8);
			state.Player.m_customData["inventorylimit.vanilla.capacity"] = plan.Capacity.ToString(CultureInfo.InvariantCulture);
			if (plan.Capacity > Plugin.SlotLimit && state.ReportedLimit != Plugin.SlotLimit)
			{
				state.ReportedLimit = Plugin.SlotLimit;
				Plugin.LogWarning($"Inventory limit {Plugin.SlotLimit}: preserved all {allItems.Count} stacks. Move excess stacks into a container; temporary overflow cells will close automatically.");
			}
			if (plan.Capacity <= Plugin.SlotLimit)
			{
				state.ReportedLimit = -1;
			}
		}

		private static bool Active(Inventory inventory, out State state)
		{
			if (TryState(inventory, out state) && state.Ready)
			{
				return state.Loading == 0;
			}
			return false;
		}

		private static bool Blocked(Inventory inventory)
		{
			if (Active(inventory, out State state))
			{
				return state.Blocked;
			}
			return false;
		}

		private static bool CellAllowed(Inventory inventory, int x, int y)
		{
			if (!Active(inventory, out State state))
			{
				return true;
			}
			if (!state.Blocked)
			{
				return VanillaLayoutPolicy.CellAllowed(Plugin.SlotLimit, x, y);
			}
			return false;
		}

		private static int FreeCells(Inventory inventory, State state)
		{
			if (state.Blocked)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < Plugin.SlotLimit; i++)
			{
				if (inventory.GetItemAt(i % 8, i / 8) == null)
				{
					num++;
				}
			}
			return VanillaLayoutPolicy.FreeCells(Plugin.SlotLimit, inventory.NrOfItems(), num);
		}

		private static void ClampSelection(InventoryGrid grid, int capacity)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			Vector2i val = (Vector2i)Selected.GetValue(grid);
			int num = Math.Max(0, Math.Min((capacity - 1) / 8, val.y));
			int num2 = Math.Max(0, Math.Min(Math.Min(7, capacity - num * 8 - 1), val.x));
			Selected.SetValue(grid, (object)new Vector2i(num2, num));
		}
	}
	internal static class VanillaLayoutPolicy
	{
		internal static bool CanRemoveSeneaLMetadata(IReadOnlyDictionary<string, string> metadata, int width, IReadOnlyList<int> cells)
		{
			if (width != 8 || !metadata.TryGetValue("seneal.slots", out string value) || !int.TryParse(value, out var result) || result < 1 || result > 4096)
			{
				return false;
			}
			if (metadata.TryGetValue("seneal.extra", out string value2) && (!int.TryParse(value2, out var result2) || (result2 != 2 && result2 != 4)))
			{
				return false;
			}
			foreach (string key in metadata.Keys)
			{
				if (key.StartsWith("seneal.", StringComparison.OrdinalIgnoreCase) && key != "seneal.slots" && key != "seneal.extra" && key != "seneal.locks")
				{
					return false;
				}
			}
			HashSet<int> hashSet = new HashSet<int>();
			foreach (int cell in cells)
			{
				if (cell < 0 || cell >= result || !hashSet.Add(cell))
				{
					return false;
				}
			}
			return true;
		}

		internal static int FreeCells(int limit, int stackCount, int emptyMainCells)
		{
			return Math.Max(0, Math.Min(limit - stackCount, emptyMainCells));
		}

		internal static bool CellAllowed(int limit, int x, int y)
		{
			if (x >= 0 && x < 8 && y >= 0)
			{
				return (long)y * 8L + x < limit;
			}
			return false;
		}
	}
}

InventoryLimit.SeneaL.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SeneaLUI;
using SeneaLUI.Components;
using SeneaLUI.Core;
using SeneaLUI.Hud;
using SeneaLUI.Inv;
using SeneaLUI.Mods;
using SeneaLUI.Net;
using SeneaLUI.Patches;
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("InventoryLimit.SeneaL")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2")]
[assembly: AssemblyProduct("InventoryLimit.SeneaL")]
[assembly: AssemblyTitle("InventoryLimit.SeneaL")]
[assembly: AssemblyVersion("0.2.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 InventoryLimit
{
	public static class SeneaLAdapterBootstrap
	{
		private static bool initialized;

		public static void Validate()
		{
			SeneaLBridge.Validate();
			SeneaLUiBridge.Validate();
			ServerSettingsBridge.Validate();
		}

		public static void Initialize(Harmony harmony)
		{
			if (harmony == null)
			{
				throw new ArgumentNullException("harmony");
			}
			if (!initialized)
			{
				harmony.PatchAll(typeof(SeneaLAdapterBootstrap).Assembly);
				initialized = true;
			}
		}

		public static void Tick()
		{
			if (initialized)
			{
				ServerSettingsBridge.Tick();
				SeneaLBridge.Tick();
			}
		}

		public static void Shutdown()
		{
			initialized = false;
			ServerSettingsBridge.Reset();
		}
	}
	internal static class SeneaLBridge
	{
		private sealed class SwapAdmission
		{
			internal Inventory Source;

			internal Inventory Destination;

			internal ItemData SourceItem;

			internal ItemData DestinationItem;

			internal Vector2i SourceCell;

			internal Vector2i DestinationCell;

			internal bool Matches(Inventory inventory, ItemData item, int x, int y)
			{
				if (inventory != Source || item != DestinationItem || x != SourceCell.x || y != SourceCell.y)
				{
					if (inventory == Destination && item == SourceItem && x == DestinationCell.x)
					{
						return y == DestinationCell.y;
					}
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Slots), "FreeMain", new Type[] { typeof(Inventory) })]
		private static class MainAdmissionCountPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Inventory inv, ref int __result)
			{
				if (AdmissionActive(inv))
				{
					__result = Math.Min(__result, Math.Max(0, Plugin.SlotLimit - MainStackCount(inv)));
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "FindEmptySlot", new Type[] { typeof(bool) })]
		private static class MainAdmissionFindPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Inventory __instance, ref Vector2i __result)
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				if (AdmissionActive(__instance) && __result.y >= 0 && __result.y < Slots.MainRows(__instance) && MainStackCount(__instance) >= Plugin.SlotLimit)
				{
					__result = new Vector2i(-1, -1);
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static class MainAdmissionAtPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Inventory __instance, ItemData item, int amount, int x, int y, bool skipValidPositionCheck, ref bool __result)
			{
				if (skipValidPositionCheck || AllowMainInsertion(__instance, item, amount, x, y))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(Vector2i)
		})]
		private static class MainAdmissionPositionPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Inventory __instance, ItemData item, Vector2i pos, ref bool __result)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				if (AllowMainInsertion(__instance, item, item.m_stack, pos.x, pos.y))
				{
					return true;
				}
				__result = __instance.AddItem(item);
				return false;
			}
		}

		[HarmonyPatch(typeof(InventoryGrid), "DropItem", new Type[]
		{
			typeof(Inventory),
			typeof(ItemData),
			typeof(int),
			typeof(Vector2i)
		})]
		private static class MainAdmissionSwapPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemData item, int amount, Vector2i pos, ref bool __result, out SwapAdmission? __state)
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
				__state = swapAdmission;
				Inventory inventory = __instance.GetInventory();
				ItemData itemAt = inventory.GetItemAt(pos.x, pos.y);
				swapAdmission = null;
				if (itemAt == null || itemAt == item || amount != item.m_stack || !fromInventory.ContainsItem(item) || (!AdmissionActive(fromInventory) && !AdmissionActive(inventory)))
				{
					return true;
				}
				if (itemAt.m_shared.m_name == item.m_shared.m_name && (item.m_shared.m_maxQuality <= 1 || itemAt.m_quality == item.m_quality) && item.m_shared.m_maxStackSize != 1)
				{
					return true;
				}
				if (!CanSwapInto(fromInventory, itemAt, item.m_gridPos) || !CanSwapInto(inventory, item, pos))
				{
					__result = false;
					return false;
				}
				swapAdmission = new SwapAdmission
				{
					Source = fromInventory,
					Destination = inventory,
					SourceItem = item,
					DestinationItem = itemAt,
					SourceCell = item.m_gridPos,
					DestinationCell = pos
				};
				return true;
			}

			private static Exception? Finalizer(Exception? __exception, SwapAdmission? __state)
			{
				swapAdmission = __state;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
		private static class MainAdmissionUpgradePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(InventoryGui __instance, Player player)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Expected O, but got Unknown
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (!AdmissionActive(inventory))
				{
					return true;
				}
				ItemData val = (ItemData)CraftUpgradeItem.GetValue(__instance);
				if (val == null || val.m_gridPos.y >= Slots.MainRows(inventory) || MainStackCount(inventory) <= Plugin.SlotLimit)
				{
					return true;
				}
				((Character)player).Message((MessageType)2, "InventoryLimit: move excess main stacks into a container before upgrading this item.", 0, (Sprite)null, false);
				return false;
			}
		}

		[HarmonyPatch(typeof(PlayerSlotPatches), "EquipInventoryItems", new Type[] { typeof(Player) })]
		private static class SafeBootstrapEquipmentPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Player __0)
			{
				if (!Object.op_Implicit((Object)(object)__0) || (Object)(object)__0 != (Object)(object)Player.m_localPlayer || Foreign.Inventory)
				{
					return true;
				}
				if (!AllowSeneaLBootstrap(__0))
				{
					return false;
				}
				if (IsEstablished)
				{
					return true;
				}
				Quiver.Refresh();
				if (!TryCapacity(__0, out var capacity))
				{
					PreserveUnknownLayout(__0);
					return false;
				}
				Slots.Resize(__0, (capacity + 8 - 1) / 8 + 4 + Slots.Tail);
				if (!IsEstablished)
				{
					return false;
				}
				Quiver.AfterResize(((Humanoid)__0).GetInventory().GetHeight());
				return true;
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static class PolicySlotsPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(ref int __result)
			{
				__result = Plugin.SlotLimit;
				return false;
			}
		}

		[HarmonyPatch(typeof(Slots), "WantedMain")]
		private static class WantedMainPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(ref int __result)
			{
				Slots.Bonus = 0;
				Player localPlayer = Player.m_localPlayer;
				int capacity = Plugin.SlotLimit;
				if (Object.op_Implicit((Object)(object)localPlayer) && !TryCapacity(localPlayer, out capacity))
				{
					__result = PreserveUnknownLayout(localPlayer);
					return false;
				}
				Slots.Cap = capacity;
				__result = (capacity + 8 - 1) / 8;
				return false;
			}
		}

		[HarmonyPatch(typeof(Slots), "Resize")]
		private static class SafeResizePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Player player, ref int hNew)
			{
				if (!TryCapacity(player, out var capacity))
				{
					PreserveUnknownLayout(player);
					return false;
				}
				Slots.Bonus = 0;
				Slots.Cap = capacity;
				hNew = (capacity + 8 - 1) / 8 + 4 + Slots.Tail;
				return true;
			}
		}

		[HarmonyPatch(typeof(Slots), "Ready")]
		private static class ReadyPatch
		{
			private static void Postfix(Inventory inv, ref bool __result)
			{
				Player localPlayer = Player.m_localPlayer;
				if (inv != null && Object.op_Implicit((Object)(object)localPlayer) && ((Humanoid)localPlayer).GetInventory() == inv && !IsEstablished && !TryOldMainRows(localPlayer, out var _, out var _))
				{
					__result = false;
				}
				else if (!__result && inv != null && !Foreign.Inventory && Slots.IsPlayer(inv))
				{
					__result = inv.GetHeight() >= 6 + Slots.Tail;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerSlotPatches), "SetInventorySize", new Type[]
		{
			typeof(Player),
			typeof(int)
		})]
		private static class SafeBootstrapSizePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Player __0, ref bool __result)
			{
				if (AllowSeneaLBootstrap(__0))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(PlayerSlotPatches), "Update", new Type[] { typeof(Player) })]
		private static class SafeBootstrapUpdatePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Player __0)
			{
				return AllowSeneaLBootstrap(__0);
			}
		}

		[HarmonyPatch(typeof(DeathPatches), "SmartGrab")]
		private static class GraveContextPatch
		{
			private static void Prefix(Inventory ginv, out int __state)
			{
				__state = graveBase;
				graveBase = Pouch.GraveBase(ginv);
			}

			private static Exception? Finalizer(Exception? __exception, int __state)
			{
				graveBase = __state;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(DeathPatches), "PlaceAt")]
		private static class GravePlacementPatch
		{
			private static bool Prefix(Inventory inv, ItemData it, int x, int y, ref bool __result)
			{
				if (!Slots.IsPlayer(inv) || Foreign.Inventory)
				{
					return true;
				}
				bool flag = x >= 0 && x < inv.GetWidth() && y >= 0 && y < inv.GetHeight();
				if (flag && y < Slots.MainRows(inv))
				{
					flag = !Slots.Dead(inv, x, y) && AllowMainInsertion(inv, it, it.m_stack, x, y);
				}
				else if (flag && Slots.IsReserved(inv, y))
				{
					flag = Slots.Fits(inv, it, x, y);
				}
				else if (flag)
				{
					flag = graveBase == inv.GetHeight();
				}
				if (flag)
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(SettingsView), "SliderRow")]
		private static class SettingsSliderPatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(ref string label, ref float min, ref float max, ref Func<float, string> fmt, ref Func<float> get, ref Action<float> set, ref Func<bool> enabled, ref Func<float> maxFn)
			{
				if (!(label != Loc.OptRows) && min == 32f && max == 64f)
				{
					label += " (InventoryLimit)";
					min = 10f;
					max = 64f;
					fmt = (float value) => Loc.SlotsCount(Mathf.RoundToInt(value));
					get = () => Plugin.SlotLimit;
					set = delegate
					{
					};
					enabled = () => false;
					maxFn = () => 64f;
				}
			}
		}

		private static readonly FieldInfo CraftUpgradeItem = RequireField(typeof(InventoryGui), "m_craftUpgradeItem", typeof(ItemData));

		private static SwapAdmission? swapAdmission;

		private const int Width = 8;

		private const int ReservedRows = 4;

		private const string SavedSlots = "seneal.slots";

		private const string SavedExtra = "seneal.extra";

		private const string SavedVanillaCapacity = "inventorylimit.vanilla.capacity";

		private static readonly FieldInfo Established = RequireField(typeof(Slots), "_established", typeof(bool));

		private static readonly FieldInfo DragItem = RequireField(typeof(InventoryGui), "m_dragItem", typeof(ItemData));

		private static Player? observedPlayer;

		private static float nextCheck;

		private static int reportedTarget = -1;

		private static bool reportedInvalidLayout;

		private static int graveBase = -1;

		private static bool applying;

		private static bool reportedInactive;

		private static bool reportedForeignInventory;

		private static bool reportedInvalidStandaloneLayout;

		private static bool IsEstablished => (bool)Established.GetValue(null);

		private static bool AdmissionActive(Inventory inventory)
		{
			if (inventory != null && !Foreign.Inventory && IsEstablished && Slots.IsPlayer(inventory))
			{
				return Slots.Ready(inventory);
			}
			return false;
		}

		private static int MainStackCount(Inventory inventory)
		{
			int num = Slots.MainRows(inventory);
			int num2 = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_gridPos.y >= 0 && allItem.m_gridPos.y < num)
				{
					num2++;
				}
			}
			return num2;
		}

		private static bool AllowMainInsertion(Inventory inventory, ItemData item, int amount, int x, int y)
		{
			if (!AdmissionActive(inventory) || y < 0 || y >= Slots.MainRows(inventory) || inventory.GetItemAt(x, y) != null || MainStackCount(inventory) < Plugin.SlotLimit)
			{
				return true;
			}
			if (inventory.ContainsItem(item) && item.m_gridPos.y >= 0 && item.m_gridPos.y < Slots.MainRows(inventory) && amount >= item.m_stack)
			{
				return true;
			}
			if (swapAdmission != null)
			{
				return swapAdmission.Matches(inventory, item, x, y);
			}
			return false;
		}

		private static bool CanSwapInto(Inventory inventory, ItemData item, Vector2i cell)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (cell.x < 0 || cell.x >= inventory.GetWidth() || cell.y < 0 || cell.y >= inventory.GetHeight())
			{
				return false;
			}
			if (AdmissionActive(inventory))
			{
				if (!Slots.IsReserved(inventory, cell.y))
				{
					if (!Slots.Dead(inventory, cell.x, cell.y))
					{
						if (!Slots.Manual)
						{
							return !Slots.Locked(cell.x, cell.y);
						}
						return true;
					}
					return false;
				}
				return Slots.Fits(inventory, item, cell.x, cell.y);
			}
			if (Slots.IsContainer(inventory) && !Slots.Manual)
			{
				return !Slots.CLocked(cell.x, cell.y);
			}
			return true;
		}

		internal static void Validate()
		{
			_ = Established;
			_ = DragItem;
			_ = CraftUpgradeItem;
			if (AccessTools.PropertyGetter(typeof(Policy), "Slots") == null || AccessTools.Method(typeof(Slots), "WantedMain", new Type[1] { typeof(int) }, (Type[])null) == null || AccessTools.Method(typeof(Slots), "Resize", new Type[2]
			{
				typeof(Player),
				typeof(int)
			}, (Type[])null) == null || AccessTools.Method(typeof(Slots), "Ready", new Type[1] { typeof(Inventory) }, (Type[])null) == null || AccessTools.Method(typeof(DeathPatches), "PlaceAt", new Type[4]
			{
				typeof(Inventory),
				typeof(ItemData),
				typeof(int),
				typeof(int)
			}, (Type[])null) == null || AccessTools.Method(typeof(DeathPatches), "SmartGrab", new Type[3]
			{
				typeof(Player),
				typeof(Inventory),
				typeof(Inventory)
			}, (Type[])null) == null || AccessTools.Method(typeof(PlayerSlotPatches), "EquipInventoryItems", new Type[1] { typeof(Player) }, (Type[])null) == null || AccessTools.Method(typeof(PlayerSlotPatches), "SetInventorySize", new Type[2]
			{
				typeof(Player),
				typeof(int)
			}, (Type[])null) == null || AccessTools.Method(typeof(PlayerSlotPatches), "Update", new Type[1] { typeof(Player) }, (Type[])null) == null || AccessTools.Method(typeof(Quiver), "Refresh", Type.EmptyTypes, (Type[])null) == null || AccessTools.Method(typeof(Quiver), "AfterResize", new Type[1] { typeof(int) }, (Type[])null) == null || AccessTools.Method(typeof(SettingsView), "SliderRow", new Type[10]
			{
				typeof(string),
				typeof(float).MakeByRefType(),
				typeof(float),
				typeof(float),
				typeof(Func<float, string>),
				typeof(Func<float>),
				typeof(Action<float>),
				typeof(Func<bool>),
				typeof(Func<float>),
				typeof(bool)
			}, (Type[])null) == null)
			{
				throw new MissingMethodException("Unsupported SeneaL UI inventory/settings signatures.");
			}
		}

		private static FieldInfo RequireField(Type type, string name, Type fieldType)
		{
			FieldInfo fieldInfo = AccessTools.Field(type, name);
			if (fieldInfo == null || fieldInfo.FieldType != fieldType)
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return fieldInfo;
		}

		private static bool TryOldMainRows(Player player, out int rows, out int bootstrapCapacity)
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			bootstrapCapacity = 0;
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (IsEstablished)
			{
				rows = inventory.GetHeight() - 4 - Slots.Tail;
				if (rows >= 1)
				{
					return inventory.GetWidth() == 8;
				}
				return false;
			}
			if (player.m_customData.TryGetValue("seneal.slots", out var value))
			{
				if (!int.TryParse(value, out var result) || result < 1 || result > 4096 || (player.m_customData.TryGetValue("seneal.extra", out var value2) && (!int.TryParse(value2, out var result2) || (result2 != 2 && result2 != 4))))
				{
					rows = 0;
					return false;
				}
				rows = (result + 8 - 1) / 8;
				return inventory.GetWidth() == 8;
			}
			if (player.m_customData.TryGetValue("inventorylimit.vanilla.capacity", out var value3))
			{
				List<int> list = new List<int>();
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					Vector2i gridPos = allItem.m_gridPos;
					long num = (long)gridPos.y * 8L + gridPos.x;
					if (gridPos.x < 0 || gridPos.x >= 8 || gridPos.y < 0 || num < 0 || num > int.MaxValue)
					{
						rows = 0;
						return false;
					}
					list.Add((int)num);
				}
				if (!StandaloneMigrationLayout.TryValidate(value3, inventory.GetWidth(), list, out var capacity, out rows))
				{
					return false;
				}
				bootstrapCapacity = capacity;
				return true;
			}
			rows = Math.Max(1, Slots.VanillaRows(player));
			bootstrapCapacity = rows * 8;
			return inventory.GetWidth() == 8;
		}

		private static bool TryCapacity(Player player, out int capacity)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			capacity = Plugin.SlotLimit;
			if (!TryOldMainRows(player, out var rows, out var bootstrapCapacity))
			{
				return false;
			}
			capacity = Math.Max(capacity, bootstrapCapacity);
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				Vector2i gridPos = allItem.m_gridPos;
				if (gridPos.y >= 0 && gridPos.y < rows)
				{
					if (gridPos.x < 0 || gridPos.x >= 8)
					{
						return false;
					}
					capacity = Math.Max(capacity, gridPos.y * 8 + gridPos.x + 1);
				}
			}
			return true;
		}

		private static bool HasInvalidStandaloneLayout(Player player)
		{
			int rows;
			int bootstrapCapacity;
			if (!IsEstablished && !player.m_customData.ContainsKey("seneal.slots") && player.m_customData.ContainsKey("inventorylimit.vanilla.capacity"))
			{
				return !TryOldMainRows(player, out rows, out bootstrapCapacity);
			}
			return false;
		}

		private static bool AllowSeneaLBootstrap(Player player)
		{
			if (Foreign.Inventory)
			{
				return true;
			}
			if (!HasInvalidStandaloneLayout(player))
			{
				if (IsEstablished || TryOldMainRows(player, out var _, out var _))
				{
					return true;
				}
				PreserveUnknownLayout(player);
				return false;
			}
			if (!reportedInvalidStandaloneLayout)
			{
				reportedInvalidStandaloneLayout = true;
				Plugin.LogWarning("InventoryLimit: standalone inventory metadata does not match the saved item coordinates; SeneaL migration was blocked to preserve items. Disable SeneaL UI and load/save with InventoryLimit. If the standalone backend also blocks the layout, restore the original inventory mod/configuration or a backup.");
			}
			return false;
		}

		private static int PreserveUnknownLayout(Player player)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = Math.Max(1, inventory.GetHeight() - 4 - Slots.Tail);
			Slots.Cap = Math.Max(num * 8, Plugin.SlotLimit);
			if (!reportedInvalidLayout)
			{
				reportedInvalidLayout = true;
				Plugin.LogWarning("InventoryLimit: неизвестная разметка сохранённого инвентаря; изменение размера отменено для сохранности предметов.");
			}
			return num;
		}

		private static bool TryCompactEstablished(Player player, out int stackCount, out bool moved)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			stackCount = 0;
			moved = false;
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (!IsEstablished || (Slots.Tail != 0 && Slots.Tail != 2) || !TryOldMainRows(player, out var rows, out var _) || !player.m_customData.TryGetValue("seneal.slots", out var value) || !int.TryParse(value, out var result) || result < 1 || result > 4096 || (result + 8 - 1) / 8 != rows || !player.m_customData.TryGetValue("seneal.extra", out var value2) || !int.TryParse(value2, out var result2) || result2 != 4)
			{
				return false;
			}
			List<ItemData> list = new List<ItemData>();
			List<int> list2 = new List<int>();
			HashSet<int> hashSet = new HashSet<int>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				Vector2i gridPos = allItem.m_gridPos;
				if (gridPos.x < 0 || gridPos.x >= 8 || gridPos.y < 0 || gridPos.y >= inventory.GetHeight() || !hashSet.Add(gridPos.y * 8 + gridPos.x))
				{
					return false;
				}
				if (gridPos.y < rows)
				{
					if (gridPos.y * 8 + gridPos.x >= result)
					{
						return false;
					}
					list.Add(allItem);
					list2.Add(gridPos.y * 8 + gridPos.x);
				}
			}
			if (!MainInventoryCompaction.TryPlan(Plugin.SlotLimit, rows * 8, list2, out MainInventoryCompaction.Plan plan))
			{
				return false;
			}
			stackCount = list.Count;
			moved = plan.MovesItems;
			if (!moved)
			{
				return true;
			}
			for (int i = 0; i < list.Count; i++)
			{
				int num = plan.Destinations[i];
				list[i].m_gridPos = new Vector2i(num % 8, num / 8);
			}
			return true;
		}

		internal static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || Slots.Suspend || Slots.Manual || applying)
			{
				return;
			}
			if (!Plugin.Active)
			{
				if (!reportedInactive)
				{
					reportedInactive = true;
					Plugin.LogWarning("InventoryLimit: SeneaL UI is inactive; the adapter will not resize the inventory.");
				}
				return;
			}
			if (Foreign.Inventory)
			{
				if (!reportedForeignInventory)
				{
					reportedForeignInventory = true;
					Plugin.LogWarning("InventoryLimit: SeneaL UI assigned inventory ownership to another mod; resizing was cancelled to preserve items.");
				}
				return;
			}
			if ((Object)(object)observedPlayer != (Object)(object)localPlayer)
			{
				observedPlayer = localPlayer;
				reportedTarget = -1;
				reportedInvalidLayout = false;
				nextCheck = 0f;
			}
			if (Time.unscaledTime < nextCheck)
			{
				return;
			}
			nextCheck = Time.unscaledTime + 0.25f;
			if (Object.op_Implicit((Object)(object)InventoryGui.instance) && DragItem.GetValue(InventoryGui.instance) != null)
			{
				return;
			}
			applying = true;
			try
			{
				int stackCount;
				bool moved;
				bool flag = TryCompactEstablished(localPlayer, out stackCount, out moved);
				if (!TryCapacity(localPlayer, out var capacity))
				{
					PreserveUnknownLayout(localPlayer);
					return;
				}
				int num = (capacity + 8 - 1) / 8 + 4 + Slots.Tail;
				if (Slots.Cap != capacity || ((Humanoid)localPlayer).GetInventory().GetHeight() != num || !IsEstablished || moved)
				{
					Slots.Apply(localPlayer);
				}
				if (flag && stackCount > Plugin.SlotLimit)
				{
					if (reportedTarget != Plugin.SlotLimit)
					{
						reportedTarget = Plugin.SlotLimit;
						string text = $"InventoryLimit: лимит {Plugin.SlotLimit}, основных стопок {stackCount}. " + $"Сохранено {stackCount} ячеек. Уберите ещё {stackCount - Plugin.SlotLimit} стопок для уменьшения до лимита.";
						Plugin.LogWarning(text);
						if (Object.op_Implicit((Object)(object)MessageHud.instance))
						{
							MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
						}
					}
				}
				else if (flag)
				{
					reportedTarget = -1;
				}
			}
			finally
			{
				applying = false;
			}
		}
	}
	internal static class SeneaLUiBridge
	{
		[HarmonyPatch(typeof(InventoryView), "Sync")]
		private static class InventoryLayoutPatch
		{
			private static void Prefix(InventoryView __instance, Player player)
			{
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				if (!Foreign.Inventory && Object.op_Implicit((Object)(object)player))
				{
					Inventory inventory = ((Humanoid)player).GetInventory();
					if (Slots.Ready(inventory) && ((int)Height.GetValue(__instance) != inventory.GetHeight() || (int)Capacity.GetValue(__instance) != Slots.Cap))
					{
						((GridSkin)Skin.GetValue(__instance)).Release();
						EquipmentKey.SetValue(__instance, -1);
					}
				}
			}

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				CodeInstruction obj = list[FindCompactThreshold(list)];
				obj.opcode = OpCodes.Ldc_I4;
				obj.operand = int.MaxValue;
				return list;
			}
		}

		[HarmonyPatch(typeof(ModsView), "Describe")]
		private static class DuplicateSettingsPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(ConfigEntryBase e)
			{
				if ((object)e != Prefs.Slots)
				{
					return e != ServerSlots.GetValue(null);
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ModsView), "Sync")]
		private static class ConfigAuthorityDisplayPatch
		{
			private static void Prefix(object __0)
			{
				RefreshConfigAuthority(__0);
			}
		}

		[HarmonyPatch(typeof(ModsView), "Apply")]
		private static class ConfigAuthorityWritePatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(object __0)
			{
				return RefreshConfigAuthority(__0);
			}
		}

		private static readonly FieldInfo Height = RequireField(typeof(InventoryView), "_h", typeof(int));

		private static readonly FieldInfo Capacity = RequireField(typeof(InventoryView), "_cap", typeof(int));

		private static readonly FieldInfo EquipmentKey = RequireField(typeof(InventoryView), "_eqCount", typeof(int));

		private static readonly FieldInfo Skin = RequireField(typeof(InventoryView), "_skin", typeof(GridSkin));

		private static readonly FieldInfo ServerSlots = RequireField(typeof(Policy), "_slots", typeof(ConfigEntry<int>));

		private static readonly FieldInfo Tail = RequireField(typeof(Slots), "Tail", typeof(int));

		private static readonly MethodInfo QuickCount = AccessTools.PropertyGetter(typeof(Slots), "QuickCount");

		private static readonly Type ConfigRow = typeof(ModsView).GetNestedType("Row", BindingFlags.NonPublic) ?? throw new MissingMemberException("SeneaL UI settings row is unavailable.");

		private static readonly Type ConfigDescription = typeof(ModsView).GetNestedType("Entry", BindingFlags.NonPublic) ?? throw new MissingMemberException("SeneaL UI settings description is unavailable.");

		private static readonly FieldInfo RowEntry = RequireField(ConfigRow, "Entry", ConfigDescription);

		private static readonly FieldInfo EntryConfig = RequireField(ConfigDescription, "E", typeof(ConfigEntryBase));

		private static readonly FieldInfo EntryReadOnly = RequireField(ConfigDescription, "ReadOnly", typeof(bool));

		private static readonly FieldInfo RowSlider = RequireField(ConfigRow, "Slider", typeof(SliderBar));

		internal static void Validate()
		{
			_ = Height;
			_ = Capacity;
			_ = EquipmentKey;
			_ = Skin;
			_ = ServerSlots;
			_ = Tail;
			MethodInfo methodInfo = AccessTools.Method(typeof(InventoryView), "Sync", new Type[1] { typeof(Player) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ModsView), "Describe", new Type[1] { typeof(ConfigEntryBase) }, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(ModsView), "Sync", new Type[2]
			{
				ConfigRow,
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.Method(typeof(ModsView), "Apply", new Type[2]
			{
				ConfigRow,
				typeof(object)
			}, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != typeof(void) || methodInfo.IsStatic || methodInfo2 == null || !methodInfo2.IsStatic || methodInfo2.ReturnType.FullName != "SeneaLUI.Hud.ModsView+Entry" || QuickCount == null || methodInfo3 == null || methodInfo3.IsStatic || methodInfo3.ReturnType != typeof(void) || methodInfo4 == null || !methodInfo4.IsStatic || methodInfo4.ReturnType != typeof(void))
			{
				throw new MissingMethodException("Unsupported SeneaL UI presentation signatures.");
			}
			MethodInfo methodInfo5 = null;
			MethodInfo[] methods = typeof(PatchProcessor).GetMethods();
			foreach (MethodInfo methodInfo6 in methods)
			{
				ParameterInfo[] parameters = methodInfo6.GetParameters();
				if (methodInfo6.Name == "GetOriginalInstructions" && parameters.Length == 2 && !parameters[1].ParameterType.IsByRef)
				{
					methodInfo5 = methodInfo6;
				}
			}
			if (methodInfo5 == null)
			{
				throw new MissingMethodException("Harmony original-instruction reader is unavailable.");
			}
			FindCompactThreshold((List<CodeInstruction>)methodInfo5.Invoke(null, new object[2] { methodInfo, null }));
		}

		private static bool RefreshConfigAuthority(object row)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			object value = RowEntry.GetValue(row);
			ConfigEntryBase val = (ConfigEntryBase)EntryConfig.GetValue(value);
			if (val != ((BaseUnityPlugin)Plugin.Instance).Config["Inventory", "Slots"])
			{
				return true;
			}
			bool flag = false;
			object[] tags = val.Description.Tags;
			foreach (object obj in tags)
			{
				ConfigurationManagerAttributes val2 = (ConfigurationManagerAttributes)((obj is ConfigurationManagerAttributes) ? obj : null);
				if (val2 != null && val2.ReadOnly == true)
				{
					flag = true;
				}
			}
			if ((bool)EntryReadOnly.GetValue(value) != flag)
			{
				EntryReadOnly.SetValue(value, flag);
				object? value2 = RowSlider.GetValue(row);
				object? obj2 = ((value2 is SliderBar) ? value2 : null);
				if (obj2 != null)
				{
					((SliderBar)obj2).SetEnabled(!flag);
				}
			}
			return !flag;
		}

		private static FieldInfo RequireField(Type type, string name, Type fieldType)
		{
			FieldInfo fieldInfo = AccessTools.Field(type, name);
			if (fieldInfo == null || fieldInfo.FieldType != fieldType)
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return fieldInfo;
		}

		private static int FindCompactThreshold(IList<CodeInstruction> code)
		{
			int num = -1;
			for (int i = 8; i + 2 < code.Count; i++)
			{
				if (!(code[i].opcode != OpCodes.Ldc_I4_5) && (!(code[i + 1].opcode != OpCodes.Blt_S) || !(code[i + 1].opcode != OpCodes.Blt)) && CodeInstructionExtensions.Calls(code[i + 2], QuickCount) && !(code[i - 1].opcode != OpCodes.Ldloc_S) && !(code[i - 2].opcode != OpCodes.Stloc_S) && object.Equals(code[i - 1].operand, code[i - 2].operand) && !(code[i - 3].opcode != OpCodes.Sub) && !(code[i - 4].opcode != OpCodes.Ldc_I4_4) && !(code[i - 5].opcode != OpCodes.Sub) && CodeInstructionExtensions.LoadsField(code[i - 6], Tail, false) && CodeInstructionExtensions.LoadsField(code[i - 7], Height, false) && !(code[i - 8].opcode != OpCodes.Ldarg_0))
				{
					if (num >= 0)
					{
						throw new InvalidOperationException("Ambiguous SeneaL UI compact-layout branch.");
					}
					num = i;
				}
			}
			if (num < 0)
			{
				throw new InvalidOperationException("SeneaL UI compact-layout branch was not found.");
			}
			return num;
		}
	}
	internal static class ServerSettingsBridge
	{
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static class OwnSlotsPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Limits? __result)
			{
				ApplySlotLimit(__result);
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static class CurrentSlotsPatch
		{
			[HarmonyPriority(0)]
			private static void Postfix(Limits? __result)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Invalid comparison between Unknown and I4
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Invalid comparison between Unknown and I4
				Mode state = Policy.State;
				if ((int)state == 1 || (int)state == 2)
				{
					ApplySlotLimit(__result);
				}
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static class MaximumSlotsPatch
		{
			[HarmonyPriority(800)]
			private static bool Prefix(ref int __result)
			{
				__result = Plugin.SlotLimit;
				return false;
			}
		}

		private static int publishedSlotLimit = -1;

		internal static void Validate()
		{
			if (AccessTools.PropertyGetter(typeof(Policy), "Own") == null || AccessTools.PropertyGetter(typeof(Policy), "Cur") == null || AccessTools.PropertyGetter(typeof(Policy), "MaxSlots") == null || AccessTools.Method(typeof(Policy), "Push", (Type[])null, (Type[])null) == null)
			{
				throw new MissingMethodException("Unsupported SeneaL UI server-policy signatures.");
			}
		}

		internal static void Reset()
		{
			publishedSlotLimit = -1;
		}

		internal static void Tick()
		{
			int slotLimit = Plugin.SlotLimit;
			if (slotLimit != publishedSlotLimit)
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance != (Object)null && instance.IsServer())
				{
					Policy.Push();
				}
				publishedSlotLimit = slotLimit;
			}
		}

		private static void ApplySlotLimit(Limits? limits)
		{
			if (limits != null)
			{
				limits.Slots = Plugin.SlotLimit;
			}
		}
	}
	internal static class StandaloneMigrationLayout
	{
		internal static bool TryValidate(string savedCapacity, int width, IReadOnlyList<int> occupiedCells, out int capacity, out int rows)
		{
			capacity = 0;
			rows = 0;
			if (width != 8 || occupiedCells == null || !int.TryParse(savedCapacity, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 1 || result > 4096)
			{
				return false;
			}
			HashSet<int> hashSet = new HashSet<int>();
			foreach (int occupiedCell in occupiedCells)
			{
				if (occupiedCell < 0 || occupiedCell >= result || !hashSet.Add(occupiedCell))
				{
					return false;
				}
			}
			capacity = result;
			rows = (result + width - 1) / width;
			return true;
		}
	}
	internal static class MainInventoryCompaction
	{
		internal sealed class Plan
		{
			internal int Capacity { get; }

			internal int[] Destinations { get; }

			internal bool MovesItems { get; }

			internal Plan(int capacity, int[] destinations, bool movesItems)
			{
				Capacity = capacity;
				Destinations = destinations;
				MovesItems = movesItems;
			}
		}

		internal static bool TryPlan(int requested, int oldMainCells, IReadOnlyList<int> occupied, out Plan? plan)
		{
			plan = null;
			if (requested < 10 || requested > 64 || oldMainCells < 1 || occupied == null)
			{
				return false;
			}
			int num = Math.Max(requested, occupied.Count);
			HashSet<int> hashSet = new HashSet<int>();
			List<int> list = new List<int>();
			int[] array = new int[occupied.Count];
			for (int i = 0; i < occupied.Count; i++)
			{
				int num2 = occupied[i];
				if (num2 < 0 || num2 >= oldMainCells || !hashSet.Add(num2))
				{
					return false;
				}
				array[i] = num2;
				if (num2 >= num)
				{
					list.Add(i);
				}
			}
			list.Sort((int left, int right) => occupied[left].CompareTo(occupied[right]));
			int num3 = 0;
			foreach (int item in list)
			{
				for (; num3 < num && hashSet.Contains(num3); num3++)
				{
				}
				if (num3 >= num)
				{
					return false;
				}
				array[item] = num3;
				hashSet.Add(num3);
			}
			plan = new Plan(num, array, list.Count > 0);
			return true;
		}
	}
}