Decompiled source of Longhouse Core v1.4.0

plugins/Core/EzomicCore.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[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("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("Shared plumbing for the Ezomic mods: version gating, host-authoritative config, and the shared inventory height.")]
[assembly: AssemblyFileVersion("1.4.0.0")]
[assembly: AssemblyInformationalVersion("1.4.0+0350f49037b6655801af8e89de78f18cfaf597ea")]
[assembly: AssemblyProduct("Core")]
[assembly: AssemblyTitle("EzomicCore")]
[assembly: AssemblyVersion("1.4.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ezomic.Core
{
	internal static class ConfigSync
	{
		private static readonly Dictionary<ConfigEntryBase, object> Original = new Dictionary<ConfigEntryBase, object>();

		private static readonly Dictionary<ConfigEntryBase, object> Imposed = new Dictionary<ConfigEntryBase, object>();

		private static readonly HashSet<ConfigFile> Watched = new HashSet<ConfigFile>();

		private static bool _applying;

		internal static ZPackage BuildConfig()
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Expected O, but got Unknown
			List<string[]> list = new List<string[]>();
			foreach (KeyValuePair<string, ModEntry> mod in Suite.Mods)
			{
				Suite.AbsorbConfig(mod.Value);
				foreach (KeyValuePair<string, ConfigEntryBase> item in mod.Value.Synced)
				{
					string text;
					try
					{
						text = TomlTypeConverter.ConvertToString(item.Value.BoxedValue, item.Value.SettingType);
					}
					catch (Exception ex)
					{
						CorePlugin.Log.LogWarning((object)("Cannot send " + mod.Key + " " + item.Key + ": " + ex.Message));
						continue;
					}
					list.Add(new string[3] { mod.Key, item.Key, text });
				}
			}
			ZPackage val = new ZPackage();
			val.Write(list.Count);
			foreach (string[] item2 in list)
			{
				val.Write(item2[0]);
				val.Write(item2[1]);
				val.Write(item2[2]);
			}
			return val;
		}

		internal static void ReceiveConfig(ZRpc rpc, ZPackage pkg)
		{
			if (((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) || !CorePlugin.EnforceConfig.Value)
			{
				return;
			}
			int num = pkg.ReadInt();
			int num2 = 0;
			_applying = true;
			try
			{
				for (int i = 0; i < num; i++)
				{
					string guid = pkg.ReadString();
					string key = pkg.ReadString();
					string raw = pkg.ReadString();
					if (Apply(guid, key, raw))
					{
						num2++;
					}
				}
			}
			finally
			{
				_applying = false;
			}
			CorePlugin.Log.LogInfo((object)("Host settings applied: " + num2 + " of " + num + ". Your own config file is untouched and comes back on disconnect."));
		}

		private static bool Apply(string guid, string key, string raw)
		{
			if (!Suite.Mods.TryGetValue(guid, out var value))
			{
				return false;
			}
			if (!value.Synced.TryGetValue(key, out var value2))
			{
				return false;
			}
			object value3;
			try
			{
				value3 = TomlTypeConverter.ConvertToValue(raw, value2.SettingType);
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogWarning((object)("Cannot read host value for " + key + ": " + ex.Message));
				return false;
			}
			if (!Original.ContainsKey(value2))
			{
				Original[value2] = value2.BoxedValue;
			}
			Imposed[value2] = value3;
			SetQuietly(value.Config, value2, value3);
			SetReadOnly(value2, readOnly: true);
			Watch(value.Config);
			return true;
		}

		private static void Watch(ConfigFile config)
		{
			if (config == null || !Watched.Add(config))
			{
				return;
			}
			config.SettingChanged += delegate(object sender, SettingChangedEventArgs args)
			{
				if (_applying || !Imposed.TryGetValue(args.ChangedSetting, out var value))
				{
					return;
				}
				_applying = true;
				try
				{
					SetQuietly(config, args.ChangedSetting, value);
				}
				finally
				{
					_applying = false;
				}
			};
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static void RestoreOnShutdown()
		{
			if (Original.Count == 0)
			{
				return;
			}
			_applying = true;
			try
			{
				foreach (KeyValuePair<ConfigEntryBase, object> item in Original)
				{
					SetQuietly(item.Key.ConfigFile, item.Key, item.Value);
					SetReadOnly(item.Key, readOnly: false);
				}
			}
			finally
			{
				_applying = false;
			}
			CorePlugin.Log.LogInfo((object)("Restored " + Original.Count + " of your own settings."));
			Original.Clear();
			Imposed.Clear();
		}

		private static void SetQuietly(ConfigFile file, ConfigEntryBase entry, object value)
		{
			if (file == null)
			{
				entry.BoxedValue = value;
				return;
			}
			bool saveOnConfigSet = file.SaveOnConfigSet;
			file.SaveOnConfigSet = false;
			try
			{
				entry.BoxedValue = value;
			}
			finally
			{
				file.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private static void SetReadOnly(ConfigEntryBase entry, bool readOnly)
		{
			if (entry.Description == null || entry.Description.Tags == null)
			{
				return;
			}
			object[] tags = entry.Description.Tags;
			for (int i = 0; i < tags.Length; i++)
			{
				if (tags[i] is ConfigurationManagerAttributes configurationManagerAttributes)
				{
					configurationManagerAttributes.ReadOnly = readOnly;
				}
			}
		}
	}
	internal static class ConnectError
	{
		private static string _pending;

		internal static void Expect(string reason)
		{
			if (!string.IsNullOrEmpty(reason))
			{
				_pending = reason;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
		private static void Explain(FejdStartup __instance)
		{
			if (string.IsNullOrEmpty(_pending))
			{
				return;
			}
			string pending = _pending;
			_pending = null;
			FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_connectionFailedError");
			object obj = ((fieldInfo != null) ? fieldInfo.GetValue(__instance) : null);
			if (obj != null)
			{
				PropertyInfo propertyInfo = AccessTools.Property(obj.GetType(), "text");
				if (!(propertyInfo == null))
				{
					string text = propertyInfo.GetValue(obj, null) as string;
					propertyInfo.SetValue(obj, string.IsNullOrEmpty(text) ? pending : (text + "\n\n" + pending), null);
				}
			}
		}
	}
	[BepInPlugin("ezomic.valheim.core", "Core", "1.4.0")]
	public class CorePlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ezomic.valheim.core";

		public const string PluginName = "Core";

		public const string PluginVersion = "1.4.0";

		public const string PluginAuthor = "Robbin Thijssen";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> EnforceVersions;

		internal static ConfigEntry<bool> EnforceBuilds;

		internal static ConfigEntry<bool> EnforceConfig;

		private Harmony _harmony;

		internal static bool GateWired { get; private set; }

		internal static bool ConfigWired { get; private set; }

		internal static bool RowsSafe { get; private set; }

		private void Awake()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			EnforceVersions = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "EnforceVersions", true, "Refuse a connection when the client and the server disagree about which Ezomic mods are installed, or about their versions. Turning this off does not make a mismatch safe; it makes it silent.");
			EnforceBuilds = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "EnforceBuilds", true, "Also refuse a connection when both ends claim the same version but are actually different builds.\nA version string is whatever was last remembered to be edited, and during development every build says 0.1.0 - so a client three commits ahead of the server matches perfectly and connects. That is the mismatch that actually happens, and a version check is the least able to see it. This compares the compiler's build id instead, which no one has to remember.\nTurn it off if you build the mods yourself on more than one machine: deterministic builds also depend on source paths, so the same commit checked out to a different folder produces a different id.");
			EnforceConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "EnforceConfig", true, "The host's settings win. Clients keep their own file untouched and get it back the moment they disconnect - nothing is overwritten on disk.");
			_harmony = new Harmony("ezomic.valheim.core");
			bool flag = Apply("inventory load protection", typeof(InventoryLoad));
			bool flag2 = Apply("inventory rows", typeof(InventoryRows));
			bool flag3 = Apply("inventory row claim", typeof(VanillaRowsSize)) & Apply("inventory row eviction fence", typeof(VanillaRowsDrop)) & Apply("inventory row baseline", typeof(VanillaRowsSpawn));
			Verify(AccessTools.Method(typeof(Player), "OnSpawned", (Type[])null, (Type[])null), AccessTools.Method(typeof(Player), "SetInventorySize", (Type[])null, (Type[])null), AccessTools.Method(typeof(Humanoid), "DropInvalidItems", (Type[])null, (Type[])null));
			RowsSafe = flag2 && flag && flag3;
			if (flag2 && !(flag && flag3))
			{
				Log.LogError((object)"Core applied its extra inventory rows but NOT the load protection that keeps them safe, which would destroy every item in a claimed row on the next relog and at every grave. The rows are being left undriven - the inventory stays vanilla and nothing any mod claimed will be honoured.");
			}
			GateWired = Apply("version gate", typeof(NetworkPatches));
			ConfigWired = Apply("config sync", typeof(ConfigSync));
			Apply("connect-error text", typeof(ConnectError));
			Suite.Register("ezomic.valheim.core", "Core", "1.4.0", ((BaseUnityPlugin)this).Config, Requirement.Everyone, typeof(CorePlugin).Assembly);
			if (GateWired && ConfigWired && RowsSafe)
			{
				Log.LogInfo((object)"Core 1.4.0 by Robbin Thijssen - ready.");
				return;
			}
			Log.LogError((object)("Core 1.4.0 came up DEGRADED" + (GateWired ? "" : " - no version gate, so mismatched builds are NOT refused") + (ConfigWired ? "" : " - no config sync, so the host's settings are NOT imposed") + (RowsSafe ? "" : " - no extra inventory rows") + ". Read the errors above before playing on a shared world."));
		}

		private void Verify(params MethodBase[] expected)
		{
			foreach (MethodBase methodBase in expected)
			{
				if (!(methodBase == null))
				{
					Patches patchInfo = Harmony.GetPatchInfo(methodBase);
					if (patchInfo == null || (!Owns(patchInfo.Prefixes) && !Owns(patchInfo.Postfixes) && !Owns(patchInfo.Transpilers)))
					{
						Log.LogError((object)("Core has NO patch on " + methodBase.DeclaringType.Name + "." + methodBase.Name + " despite applying cleanly. On Valheim 1.0 that method is what decides whether items in mod-granted inventory rows are kept or dropped on the ground - treat those rows as unsafe."));
					}
				}
			}
		}

		private static bool Owns(IEnumerable<Patch> patches)
		{
			if (patches == null)
			{
				return false;
			}
			foreach (Patch patch in patches)
			{
				if (patch.owner == "ezomic.valheim.core")
				{
					return true;
				}
			}
			return false;
		}

		private bool Apply(string what, Type patches)
		{
			try
			{
				_harmony.PatchAll(patches);
				return true;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Core could not apply its " + what + " patches, so that feature is off for this session. The rest of Core is unaffected. " + ex.Message));
				return false;
			}
		}

		private void Update()
		{
			InventoryRows.Backdrop.Tick();
			if (RowsSafe)
			{
				InventoryRows.Tick();
			}
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	public enum Requirement
	{
		Everyone,
		HostOnly
	}
	internal sealed class ModEntry
	{
		internal string Guid;

		internal string Name;

		internal string Version;

		internal Requirement Requirement;

		internal ConfigFile Config;

		internal string Fingerprint;

		internal string Data;

		internal readonly List<string> Prefabs = new List<string>();

		internal readonly Dictionary<string, ConfigEntryBase> Synced = new Dictionary<string, ConfigEntryBase>();

		internal readonly HashSet<string> Local = new HashSet<string>(StringComparer.Ordinal);

		internal readonly HashSet<string> Forced = new HashSet<string>(StringComparer.Ordinal);
	}
	[HarmonyPatch]
	internal static class InventoryLoad
	{
		private static FieldInfo _height;

		private const int LoadSlack = 16;

		[HarmonyTargetMethods]
		private static IEnumerable<MethodBase> Targets()
		{
			List<MethodBase> list = new List<MethodBase>();
			MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "Load", new Type[1] { typeof(ZPackage) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Inventory), "Load", new Type[2]
			{
				typeof(ZPackage),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo != null)
			{
				list.Add(methodInfo);
			}
			if (methodInfo2 != null)
			{
				list.Add(methodInfo2);
			}
			if (methodInfo == null)
			{
				CorePlugin.Log.LogError((object)"Inventory.Load(ZPackage) is gone - the player's own inventory is the one that loads through it, so its rows are unprotected.");
			}
			if (list.Count == 0)
			{
				CorePlugin.Log.LogError((object)"No Inventory.Load overload could be found at all.");
			}
			return list;
		}

		[HarmonyPrefix]
		private static void Widen(Inventory __instance, out int __state)
		{
			__state = -1;
			if (__instance != null)
			{
				if (_height == null)
				{
					_height = AccessTools.Field(typeof(Inventory), "m_height");
				}
				if (!(_height == null))
				{
					__state = __instance.GetHeight();
					_height.SetValue(__instance, __state + 16);
				}
			}
		}

		[HarmonyPostfix]
		private static void Trim(Inventory __instance, int __state)
		{
			if (__instance != null && __state >= 0 && !(_height == null))
			{
				int num = Occupied(__instance);
				int num2 = Mathf.Max(__state, num);
				_height.SetValue(__instance, num2);
				if (num2 > __state)
				{
					CorePlugin.Log.LogInfo((object)("Inventory load: held at " + num2 + " rows rather than " + __state + " - something was saved below the grid, and cutting to fit would have destroyed it."));
				}
			}
		}

		private static int Occupied(Inventory inventory)
		{
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && allItem.m_gridPos.y + 1 > num)
				{
					num = allItem.m_gridPos.y + 1;
				}
			}
			return num;
		}
	}
	public static class InventoryRows
	{
		internal static class Backdrop
		{
			private static InventoryGui _seen;

			private static int _shown = -1;

			private static readonly List<RectTransform> Panels = new List<RectTransform>();

			private static readonly List<float> Heights = new List<float>();

			private static RectTransform _container;

			private static Vector2 _containerBase;

			internal static void Invalidate()
			{
				_shown = -1;
			}

			internal static void Tick()
			{
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_player == (Object)null)
				{
					_seen = null;
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null))
				{
					if (instance != _seen)
					{
						_seen = instance;
						_shown = -1;
						Capture(instance);
					}
					int num = Grown(instance, localPlayer);
					if (num != _shown)
					{
						_shown = num;
						Resize(instance, num);
					}
				}
			}

			private static int Grown(InventoryGui gui, Player player)
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory == null || Heights.Count == 0)
				{
					return 0;
				}
				InventoryGrid componentInChildren = ((Component)gui.m_player).GetComponentInChildren<InventoryGrid>(true);
				if ((Object)(object)componentInChildren == (Object)null || componentInChildren.m_elementSpace <= 0f)
				{
					return 0;
				}
				int num = Mathf.RoundToInt(Heights[0] / componentInChildren.m_elementSpace);
				return Mathf.Max(0, inventory.GetHeight() - num);
			}

			private static void Capture(InventoryGui gui)
			{
				//IL_004c: 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_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
				Panels.Clear();
				Heights.Clear();
				Remember(gui.m_player);
				_container = gui.m_container;
				if ((Object)(object)_container != (Object)null)
				{
					_containerBase = _container.anchoredPosition;
				}
				Rect rect = gui.m_player.rect;
				float width = ((Rect)(ref rect)).width;
				Image[] componentsInChildren = ((Component)gui.m_player).GetComponentsInChildren<Image>(true);
				foreach (Image val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sprite == (Object)null) && ((Object)val.sprite).name.IndexOf("woodpanel", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						rect = ((Graphic)val).rectTransform.rect;
						if (!(((Rect)(ref rect)).width < width * 0.6f) && (!((Object)(object)_container != (Object)null) || !((Component)val).transform.IsChildOf((Transform)(object)_container)))
						{
							Remember(((Graphic)val).rectTransform);
						}
					}
				}
			}

			private static void Remember(RectTransform rect)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: 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_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)rect == (Object)null) && !Panels.Contains(rect))
				{
					Panels.Add(rect);
					List<float> heights = Heights;
					Rect rect2 = rect.rect;
					heights.Add(((Rect)(ref rect2)).height);
					ManualLogSource log = CorePlugin.Log;
					string[] obj = new string[8]
					{
						"Inventory panel: grabbed ",
						Path((Transform)(object)rect),
						"  ",
						null,
						null,
						null,
						null,
						null
					};
					rect2 = rect.rect;
					obj[3] = ((Rect)(ref rect2)).width.ToString("F0");
					obj[4] = "x";
					rect2 = rect.rect;
					obj[5] = ((Rect)(ref rect2)).height.ToString("F0");
					obj[6] = "  pivot y ";
					obj[7] = rect.pivot.y.ToString("F2");
					log.LogInfo((object)string.Concat(obj));
				}
			}

			private static string Path(Transform t)
			{
				string text = ((Object)t).name;
				Transform parent = t.parent;
				while ((Object)(object)parent != (Object)null)
				{
					text = ((Object)parent).name + "/" + text;
					if (((Object)parent).name == "InventoryGui" || (Object)(object)parent.parent == (Object)null)
					{
						break;
					}
					parent = parent.parent;
				}
				return text;
			}

			private static void Resize(InventoryGui gui, int rows)
			{
				//IL_0174: Unknown result type (might be due to invalid IL or missing references)
				//IL_0180: Unknown result type (might be due to invalid IL or missing references)
				//IL_0185: Unknown result type (might be due to invalid IL or missing references)
				//IL_0218: Unknown result type (might be due to invalid IL or missing references)
				//IL_022b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0230: Unknown result type (might be due to invalid IL or missing references)
				InventoryGrid componentInChildren = ((Component)gui.m_player).GetComponentInChildren<InventoryGrid>(true);
				if ((Object)(object)componentInChildren == (Object)null || componentInChildren.m_elementSpace <= 0f)
				{
					return;
				}
				float num = (float)rows * componentInChildren.m_elementSpace;
				for (int i = 0; i < Panels.Count; i++)
				{
					if (!((Object)(object)Panels[i] == (Object)null))
					{
						Panels[i].SetSizeWithCurrentAnchors((Axis)1, Heights[i] + num);
					}
				}
				if (Panels.Count > 0)
				{
					CorePlugin.Log.LogInfo((object)("Inventory panel: captured " + Heights[0].ToString("F0") + "px, one row is " + componentInChildren.m_elementSpace.ToString("F0") + "px, so the captured art fits about " + Mathf.RoundToInt(Heights[0] / componentInChildren.m_elementSpace) + " rows; growing by " + rows + " row(s) to " + (Heights[0] + num).ToString("F0") + "px for a grid " + rows + " row(s) past what the capture covers."));
				}
				if ((Object)(object)_container == (Object)null)
				{
					return;
				}
				_container.anchoredPosition = _containerBase + new Vector2(0f, 0f - num);
				Canvas componentInParent = ((Component)_container).GetComponentInParent<Canvas>();
				if ((Object)(object)componentInParent == (Object)null)
				{
					return;
				}
				Transform transform = ((Component)componentInParent).transform;
				RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (!((Object)(object)val == (Object)null) && !(componentInParent.scaleFactor <= 0f))
				{
					Vector3[] array = (Vector3[])(object)new Vector3[4];
					Vector3[] array2 = (Vector3[])(object)new Vector3[4];
					_container.GetWorldCorners(array);
					val.GetWorldCorners(array2);
					float num2 = array2[0].y - array[0].y;
					if (!(num2 <= 0f))
					{
						RectTransform container = _container;
						container.anchoredPosition += new Vector2(0f, num2 / componentInParent.scaleFactor);
						CorePlugin.Log.LogInfo((object)("Inventory panel: the container window would have hung " + num2.ToString("F0") + "px below the screen, so it was lifted back on. It now overlaps the inventory - there is no room for both at this height."));
					}
				}
			}
		}

		private static readonly Dictionary<string, int> Claims = new Dictionary<string, int>();

		private static FieldInfo _height;

		private static Player _player;

		private static int _base = -1;

		private static int _applied = -1;

		private static bool _widened;

		private static int _preWiden = -1;

		private static int _effective = -1;

		private const int LoadSlack = 16;

		private static readonly HashSet<string> _truncated = new HashSet<string>();

		internal static int Base => _base;

		public static int Total
		{
			get
			{
				int num = 0;
				foreach (KeyValuePair<string, int> claim in Claims)
				{
					num += claim.Value;
				}
				return num;
			}
		}

		public static int Extra
		{
			get
			{
				if (_base < 0 || _effective < 0)
				{
					return 0;
				}
				return Mathf.Max(0, _effective - _base);
			}
		}

		internal static void SayTruncated(int vanillaRows, int claimed, int granted)
		{
			string item = vanillaRows + ":" + claimed + ":" + granted;
			if (_truncated.Add(item))
			{
				CorePlugin.Log.LogWarning((object)("Inventory rows: vanilla is already at " + vanillaRows + " and it caps the grid at " + 9 + ", so of " + claimed + " row(s) claimed only " + granted + " fit. The rows you bought are kept and nothing is dropped - the mod's rows are what give way."));
			}
		}

		internal static void LearnBase(int vanillaRows)
		{
			Player localPlayer = Player.m_localPlayer;
			if (_base != vanillaRows || localPlayer != _player)
			{
				_player = localPlayer;
				_base = vanillaRows;
				_applied = -1;
				_effective = -1;
				CorePlugin.Log.LogInfo((object)("Inventory rows: vanilla says " + vanillaRows + "."));
			}
		}

		public static void Claim(string owner, int rows)
		{
			if (!string.IsNullOrEmpty(owner))
			{
				rows = Mathf.Max(0, rows);
				if (!Claims.TryGetValue(owner, out var value) || value != rows)
				{
					Claims[owner] = rows;
					_applied = -1;
				}
			}
		}

		internal static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				_player = null;
				_base = -1;
				_applied = -1;
				_effective = -1;
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null)
			{
				return;
			}
			if (localPlayer != _player)
			{
				_player = localPlayer;
				_applied = -1;
				_effective = -1;
				if (_base < 0)
				{
					_base = ((_widened && _preWiden >= 0) ? _preWiden : inventory.GetHeight());
					CorePlugin.Log.LogWarning((object)("Inventory rows: vanilla height measured as " + _base + " because Player.SetInventorySize never told us. On Valheim 1.0 that patch is what keeps granted rows from being emptied onto the ground - treat rows above " + _base + " as unsafe until the errors above are fixed."));
				}
			}
			if (Claims.Count == 0 && !_widened)
			{
				return;
			}
			int total = Total;
			if (total != _applied)
			{
				if (_height == null)
				{
					_height = AccessTools.Field(typeof(Inventory), "m_height");
				}
				if (_height == null)
				{
					CorePlugin.Log.LogError((object)"Inventory.m_height not found - extra rows cannot work.");
					_applied = total;
					return;
				}
				int num = _base + total;
				int num2 = ((Claims.Count > 0 && Occupied(inventory) > num) ? Compact(inventory, num) : 0);
				int num3 = Mathf.Max(num, Occupied(inventory));
				_applied = total;
				_widened = false;
				_effective = num3;
				_height.SetValue(inventory, num3);
				CorePlugin.Log.LogInfo((object)("Inventory rows: " + _base + " + " + total + " claimed by " + Claims.Count + " mod(s)" + ((num2 > 0) ? (", " + num2 + " item(s) moved up") : "") + ((num3 > num) ? (", held at " + num3 + " by items in the grid") : "") + "."));
				Backdrop.Invalidate();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Load")]
		private static void WidenBeforeLoad(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)__instance).GetInventory();
			if (inventory != null)
			{
				if (_height == null)
				{
					_height = AccessTools.Field(typeof(Inventory), "m_height");
				}
				if (!(_height == null))
				{
					_applied = -1;
					_widened = true;
					_effective = -1;
					_preWiden = inventory.GetHeight();
					int num = ((_base >= 0) ? _base : _preWiden);
					_height.SetValue(inventory, num + 16);
				}
			}
		}

		private static int Compact(Inventory inventory, int keep)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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)
			if (keep < 1)
			{
				return 0;
			}
			List<ItemData> list = new List<ItemData>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && allItem.m_gridPos.y >= keep)
				{
					list.Add(allItem);
				}
			}
			if (list.Count == 0)
			{
				return 0;
			}
			int width = inventory.GetWidth();
			int num = 0;
			foreach (ItemData item in list)
			{
				Vector2i val = FreeSlot(inventory, width, keep);
				if (val.y < 0)
				{
					break;
				}
				item.m_gridPos = val;
				num++;
			}
			if (num > 0 && inventory.m_onChanged != null)
			{
				inventory.m_onChanged();
			}
			return num;
		}

		private static Vector2i FreeSlot(Inventory inventory, int width, int keep)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < keep; i++)
			{
				for (int j = 0; j < width; j++)
				{
					if (inventory.GetItemAt(j, i) == null)
					{
						return new Vector2i(j, i);
					}
				}
			}
			return new Vector2i(-1, -1);
		}

		private static int Occupied(Inventory inventory)
		{
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && allItem.m_gridPos.y + 1 > num)
				{
					num = allItem.m_gridPos.y + 1;
				}
			}
			return num;
		}
	}
	internal static class NetworkPatches
	{
		private struct RemoteMod
		{
			public string Version;

			public string Fingerprint;

			public string Data;

			public Requirement Requirement;
		}

		private const string RpcManifest = "Ezomic_Core_Manifest";

		private const string RpcConfig = "Ezomic_Core_Config";

		private static readonly Dictionary<ZRpc, Dictionary<string, RemoteMod>> Received = new Dictionary<ZRpc, Dictionary<string, RemoteMod>>();

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static void RegisterHandshake(ZNet __instance, ZNetPeer peer)
		{
			peer.m_rpc.Register<ZPackage>("Ezomic_Core_Manifest", (Action<ZRpc, ZPackage>)ReceiveManifest);
			peer.m_rpc.Register<ZPackage>("Ezomic_Core_Config", (Action<ZRpc, ZPackage>)ConfigSync.ReceiveConfig);
			peer.m_rpc.Register<string, string>("Ezomic_Core_ChatLine", (Action<ZRpc, string, string>)ServerChat.Receive);
			peer.m_rpc.Register<string, string, int>("Ezomic_Core_ChatSay", (Action<ZRpc, string, string, int>)ServerChat.ReceiveSay);
			peer.m_rpc.Invoke("Ezomic_Core_Manifest", new object[1] { BuildManifest() });
		}

		private static ZPackage BuildManifest()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(Suite.Mods.Count);
			foreach (KeyValuePair<string, ModEntry> mod in Suite.Mods)
			{
				val.Write(mod.Key);
				val.Write(mod.Value.Version ?? "");
				val.Write((int)mod.Value.Requirement);
				val.Write(mod.Value.Fingerprint ?? "");
				val.Write(mod.Value.Data ?? "");
			}
			return val;
		}

		private static void ReceiveManifest(ZRpc rpc, ZPackage pkg)
		{
			Dictionary<string, RemoteMod> dictionary = new Dictionary<string, RemoteMod>();
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				string key = pkg.ReadString();
				string version = pkg.ReadString();
				int num2 = pkg.ReadInt();
				string fingerprint = ((pkg.GetPos() < pkg.Size()) ? pkg.ReadString() : "");
				string data = ((pkg.GetPos() < pkg.Size()) ? pkg.ReadString() : "");
				dictionary[key] = new RemoteMod
				{
					Version = version,
					Fingerprint = fingerprint,
					Data = data,
					Requirement = ((num2 == 1) ? Requirement.HostOnly : Requirement.Everyone)
				};
			}
			Received[rpc] = dictionary;
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && CorePlugin.EnforceVersions.Value)
			{
				string text = Compare(dictionary);
				if (text != null)
				{
					CorePlugin.Log.LogError((object)("This server does not match your mods:\n" + text + "\nThe server will close the connection."));
					ConnectError.Expect(Screen(dictionary));
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static bool GateOnPeerInfo(ZNet __instance, ZRpc rpc)
		{
			if (!CorePlugin.EnforceVersions.Value)
			{
				return true;
			}
			if (!Received.TryGetValue(rpc, out var value))
			{
				value = new Dictionary<string, RemoteMod>();
			}
			string text = Compare(value);
			if (text == null)
			{
				return true;
			}
			if (__instance.IsServer())
			{
				CorePlugin.Log.LogWarning((object)("Refused a connection:\n" + text));
				rpc.Invoke("Error", new object[1] { 3 });
				return false;
			}
			CorePlugin.Log.LogError((object)("This server does not match your mods:\n" + text + "\nThe server will close the connection."));
			return true;
		}

		private static string Compare(Dictionary<string, RemoteMod> theirs, bool detailed = true)
		{
			StringBuilder builder = null;
			foreach (KeyValuePair<string, ModEntry> mod in Suite.Mods)
			{
				ModEntry value = mod.Value;
				if (!theirs.TryGetValue(mod.Key, out var value2))
				{
					if (value.Requirement != Requirement.HostOnly)
					{
						Append(ref builder, detailed ? ("  " + value.Name + " " + value.Version + " is missing on the other end.") : ("  " + value.Name + " - missing there"));
					}
				}
				else if (value2.Version != value.Version)
				{
					Append(ref builder, detailed ? ("  " + value.Name + ": they have " + value2.Version + ", this end has " + value.Version + ".") : ("  " + value.Name + " - " + value2.Version + " there, " + value.Version + " here"));
				}
				else if (CorePlugin.EnforceBuilds.Value && !string.IsNullOrEmpty(value.Fingerprint) && !string.IsNullOrEmpty(value2.Fingerprint))
				{
					if (value2.Fingerprint != value.Fingerprint)
					{
						Append(ref builder, detailed ? ("  " + value.Name + " " + value.Version + " is the same version on both ends but a different build (" + value2.Fingerprint + " there, " + value.Fingerprint + " here). Rebuild whichever is behind.") : ("  " + value.Name + " - different build"));
					}
					else if (!string.IsNullOrEmpty(value.Data) && !string.IsNullOrEmpty(value2.Data) && value2.Data != value.Data)
					{
						Append(ref builder, detailed ? ("  " + value.Name + " has a different data file (" + value2.Data + " there, " + value.Data + " here). Copy whichever is right to the other end.") : ("  " + value.Name + " - different data file"));
					}
				}
			}
			foreach (KeyValuePair<string, RemoteMod> their in theirs)
			{
				if (!Suite.Mods.ContainsKey(their.Key) && their.Value.Requirement != Requirement.HostOnly)
				{
					Append(ref builder, "  " + their.Key + " " + their.Value.Version + " is on the other end but not this one.");
				}
			}
			return builder?.ToString();
		}

		private static string Screen(Dictionary<string, RemoteMod> theirs)
		{
			string text = Compare(theirs, detailed: false);
			if (text == null)
			{
				return "";
			}
			return "This server does not match your mods:\n\n" + text + "\n\nThe full detail is in your log.";
		}

		private static void Append(ref StringBuilder builder, string line)
		{
			if (builder == null)
			{
				builder = new StringBuilder();
			}
			else
			{
				builder.Append('\n');
			}
			builder.Append(line);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static void PushConfigOnPeerInfo(ZNet __instance, ZRpc rpc)
		{
			if (__instance.IsServer() && CorePlugin.EnforceConfig.Value)
			{
				rpc.Invoke("Ezomic_Core_Config", new object[1] { ConfigSync.BuildConfig() });
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		private static void ForgetPeer(ZNetPeer peer)
		{
			if (peer != null && peer.m_rpc != null)
			{
				Received.Remove(peer.m_rpc);
			}
		}
	}
	internal static class ServerChat
	{
		internal const string Rpc = "Ezomic_Core_ChatLine";

		internal const string RpcSay = "Ezomic_Core_ChatSay";

		private static FieldRef<Chat, float> _hideTimer;

		private static bool _bound;

		internal static void Receive(ZRpc rpc, string title, string text)
		{
			Show(title, text, (Type)1);
		}

		internal static void ReceiveSay(ZRpc rpc, string title, string text, int type)
		{
			Show(title, text, (Type)(type switch
			{
				0 => 0, 
				2 => 2, 
				_ => 1, 
			}));
		}

		private static void Show(string title, string text, Type type)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
				{
					return;
				}
				Chat instance = Chat.instance;
				if (!((Object)(object)instance == (Object)null))
				{
					title = Clean(title);
					text = Clean(text);
					if (text.Length != 0)
					{
						((Terminal)instance).AddString((title.Length > 0) ? title : "Server", text, type, false);
						Open(instance);
					}
				}
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogWarning((object)("Could not show a chat line from the server: " + ex.Message));
			}
		}

		private static string Clean(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			return s.Replace('<', ' ').Replace('>', ' ').Replace('\r', ' ')
				.Replace('\n', ' ')
				.Trim();
		}

		private static void Open(Chat chat)
		{
			if (!_bound)
			{
				_bound = true;
				try
				{
					_hideTimer = AccessTools.FieldRefAccess<Chat, float>("m_hideTimer");
				}
				catch (Exception ex)
				{
					CorePlugin.Log.LogWarning((object)("Chat window timer not found, lines will not pop it open: " + ex.Message));
				}
			}
			if (_hideTimer != null)
			{
				_hideTimer.Invoke(chat) = 0f;
			}
		}
	}
	public static class Suite
	{
		internal static readonly Dictionary<string, ModEntry> Mods = new Dictionary<string, ModEntry>(StringComparer.Ordinal);

		private static string _lastRegistered;

		private static readonly Dictionary<string, List<string>> Early = new Dictionary<string, List<string>>(StringComparer.Ordinal);

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Register(string guid, string name, string version, ConfigFile config, Requirement requirement = Requirement.Everyone, Assembly owner = null)
		{
			if (string.IsNullOrEmpty(guid))
			{
				throw new ArgumentNullException("guid");
			}
			if (!Mods.TryGetValue(guid, out var value))
			{
				value = new ModEntry();
				Mods[guid] = value;
			}
			value.Guid = guid;
			value.Name = name;
			value.Version = version;
			value.Requirement = requirement;
			value.Config = config;
			value.Fingerprint = FingerprintOf(owner ?? Assembly.GetCallingAssembly());
			if (!string.IsNullOrEmpty(value.Fingerprint) && Early.TryGetValue(value.Fingerprint, out var value2))
			{
				foreach (string item in value2)
				{
					if (!value.Prefabs.Contains(item))
					{
						value.Prefabs.Add(item);
					}
				}
				Early.Remove(value.Fingerprint);
			}
			AbsorbConfig(value);
			_lastRegistered = guid;
			string text = (CorePlugin.GateWired ? "" : "  ** NOT ENFORCED, Core's version gate failed to apply **");
			CorePlugin.Log.LogInfo((object)("Registered " + name + " " + version + " (" + requirement.ToString() + ") build " + value.Fingerprint + text));
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Owns(params string[] names)
		{
			if (names == null || names.Length == 0)
			{
				return;
			}
			string text = FingerprintOf(Assembly.GetCallingAssembly());
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string[] array;
			foreach (ModEntry value2 in Mods.Values)
			{
				if (value2 == null || value2.Fingerprint != text)
				{
					continue;
				}
				array = names;
				foreach (string text2 in array)
				{
					if (!string.IsNullOrEmpty(text2) && !value2.Prefabs.Contains(text2))
					{
						value2.Prefabs.Add(text2);
					}
				}
				return;
			}
			if (!Early.TryGetValue(text, out var value))
			{
				value = new List<string>();
				Early[text] = value;
			}
			array = names;
			foreach (string text3 in array)
			{
				if (!string.IsNullOrEmpty(text3) && !value.Contains(text3))
				{
					value.Add(text3);
				}
			}
		}

		public static List<string> OwnedPrefabs(string guid)
		{
			if (!string.IsNullOrEmpty(guid) && Mods.TryGetValue(guid, out var value))
			{
				return new List<string>(value.Prefabs);
			}
			return new List<string>();
		}

		private static string FingerprintOf(Assembly assembly)
		{
			if (assembly == null)
			{
				return "";
			}
			try
			{
				return assembly.ManifestModule.ModuleVersionId.ToString("N").Substring(0, 12);
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogWarning((object)("Could not read a build id: " + ex.Message));
				return "";
			}
		}

		public static void Data(string contents, string guid = null)
		{
			guid = guid ?? _lastRegistered;
			if (!string.IsNullOrEmpty(guid) && Mods.TryGetValue(guid, out var value))
			{
				value.Data = HashOf(contents);
				CorePlugin.Log.LogInfo((object)(value.Name + " data " + value.Data + "."));
			}
		}

		private static string HashOf(string contents)
		{
			if (string.IsNullOrEmpty(contents))
			{
				return "";
			}
			contents = contents.Replace("\r\n", "\n").Replace("\r", "\n");
			uint num = 2166136261u;
			for (int i = 0; i < contents.Length; i++)
			{
				num ^= contents[i];
				num *= 16777619;
			}
			return num.ToString("x8");
		}

		public static void Sync(ConfigEntryBase entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			ModEntry modEntry = Current();
			string text = Key(entry);
			modEntry.Forced.Add(text);
			modEntry.Local.Remove(text);
			modEntry.Synced[text] = entry;
		}

		public static void Sync(params ConfigEntryBase[] entries)
		{
			for (int i = 0; i < entries.Length; i++)
			{
				Sync(entries[i]);
			}
		}

		public static void Local(ConfigEntryBase entry)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			ModEntry modEntry = Current();
			string text = Key(entry);
			modEntry.Local.Add(text);
			modEntry.Forced.Remove(text);
			modEntry.Synced.Remove(text);
		}

		public static void Local(params ConfigEntryBase[] entries)
		{
			for (int i = 0; i < entries.Length; i++)
			{
				Local(entries[i]);
			}
		}

		private static ModEntry Current()
		{
			if (_lastRegistered == null || !Mods.TryGetValue(_lastRegistered, out var value))
			{
				throw new InvalidOperationException("Suite.Sync and Suite.Local run against the mod that registered last. Call Suite.Register first.");
			}
			return value;
		}

		private static bool IsPersonal(ConfigEntryBase entry)
		{
			Type settingType = entry.SettingType;
			if (!(settingType == typeof(KeyboardShortcut)))
			{
				return settingType == typeof(KeyCode);
			}
			return true;
		}

		internal static void AbsorbConfig(ModEntry entry)
		{
			if (entry == null || entry.Config == null)
			{
				return;
			}
			foreach (ConfigDefinition key in entry.Config.Keys)
			{
				ConfigEntryBase val = entry.Config[key];
				if (val != null)
				{
					string text = key.Section + "." + key.Key;
					if (!entry.Local.Contains(text) && (!IsPersonal(val) || entry.Forced.Contains(text)))
					{
						entry.Synced[text] = val;
					}
				}
			}
		}

		internal static string Key(ConfigEntryBase entry)
		{
			return entry.Definition.Section + "." + entry.Definition.Key;
		}

		public static void ExplainRefusal(string reason)
		{
			ConnectError.Expect(reason);
		}

		public static ConfigurationManagerAttributes Display(int order = 0, bool advanced = false, string name = null)
		{
			return new ConfigurationManagerAttributes
			{
				Order = -order,
				IsAdvanced = advanced,
				DispName = name
			};
		}
	}
	public sealed class ConfigurationManagerAttributes
	{
		public int? Order;

		public bool? Browsable;

		public bool? IsAdvanced;

		public bool? HideDefaultButton;

		public string DispName;

		public bool? ReadOnly;
	}
	internal static class VanillaRows
	{
		internal const string RowsKey = "invrows";

		internal const int Ceiling = 9;
	}
	[HarmonyPatch(typeof(Player), "SetInventorySize")]
	internal static class VanillaRowsSize
	{
		[HarmonyPrefix]
		private static void Grow(Player __instance, ref int rows)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && __instance == Player.m_localPlayer)
				{
					InventoryRows.LearnBase(rows);
					int num = 9 - rows;
					if (num < 0)
					{
						num = 0;
					}
					int total = InventoryRows.Total;
					int num2 = ((total < num) ? total : num);
					if (num2 < total)
					{
						InventoryRows.SayTruncated(rows, total, num2);
					}
					rows += num2;
				}
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogError((object)("Core could not claim its inventory rows from SetInventorySize, so this login gets vanilla's height and anything in a granted row is about to be dropped. " + ex.Message));
			}
		}

		[HarmonyPostfix]
		private static void Restore(Player __instance)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && __instance == Player.m_localPlayer)
				{
					int num = InventoryRows.Base;
					if (num >= 0)
					{
						__instance.AddUniqueKeyValue("invrows", num.ToString());
					}
				}
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogError((object)("Core could not restore the vanilla row count in the character, so the saved value now includes Core's rows and will compound on the next login. " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class VanillaRowsSpawn
	{
		[HarmonyPostfix]
		private static void Learn(Player __instance)
		{
			try
			{
				string s = default(string);
				if (!((Object)(object)__instance == (Object)null) && __instance == Player.m_localPlayer && __instance.TryGetUniqueKeyValue("invrows", ref s) && int.TryParse(s, out var result) && result >= 0)
				{
					InventoryRows.LearnBase(result);
				}
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogError((object)("Core could not read the vanilla row count back out of the character, so this login falls back to measuring the grid. " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropInvalidItems")]
	internal static class VanillaRowsDrop
	{
		[HarmonyPrefix]
		private static void Hold(Humanoid __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || (object)__instance != Player.m_localPlayer)
				{
					return;
				}
				Inventory inventory = __instance.GetInventory();
				if (inventory == null)
				{
					return;
				}
				int num = 0;
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem != null && allItem.m_gridPos.y + 1 > num)
					{
						num = allItem.m_gridPos.y + 1;
					}
				}
				if (num > inventory.GetHeight())
				{
					CorePlugin.Log.LogWarning((object)("Vanilla was about to drop items from rows below its own count - holding the grid at " + num + " rows so nothing is thrown on the ground. Rows claimed: " + InventoryRows.Total + "."));
					inventory.SetHeight(num);
				}
			}
			catch (Exception ex)
			{
				CorePlugin.Log.LogError((object)("Core could not fence DropInvalidItems, so vanilla may drop items from rows it does not know about. " + ex.Message));
			}
		}
	}
}