Decompiled source of Stackable Items v0.1.5

plugins/com.atomic.stackableitems.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Object.Synchronizing;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using SaveBackups;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.atomic.stackableitems")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyInformationalVersion("0.1.5+831e86f97f144393d6b28deac7ad3f90753dd12b")]
[assembly: AssemblyProduct("com.atomic.stackableitems")]
[assembly: AssemblyTitle("StackableItems")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.5.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace StackableItems
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.atomic.stackableitems", "StackableItems", "0.1.5")]
	public class Plugin : BaseUnityPlugin
	{
		private Harmony harmony;

		public static ConfigEntry<int> MaxStackSize;

		public const string Id = "com.atomic.stackableitems";

		internal static ManualLogSource Log { get; private set; }

		public static string Name => "StackableItems";

		public static string Version => "0.1.5";

		private void Awake()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			MaxStackSize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Max Stack Size", 64, new ConfigDescription("The maximum amount of objects you can have in a given slot. The more you have, the more data can be tracked per player.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			harmony = new Harmony("com.atomic.stackableitems");
			harmony.PatchAll();
			if (ModInstalled("dazed.howtofish.savebackups"))
			{
				TryRegister();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		private void TryRegister()
		{
			try
			{
				Companions.Register("stackableitems", "Stackable Items", (Func<string, string>)((string saveName) => Path.Combine(Paths.ConfigPath, "StackableItems", "Saves", Companions.SanitiseSaveName(saveName) + ".json")));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to register with SaveBackups: " + ex.Message));
			}
		}

		public static bool ModInstalled(string GUID)
		{
			bool flag = Chainloader.PluginInfos.ContainsKey(GUID);
			if (!flag)
			{
				Log.LogWarning((object)(GUID + " is not enabled."));
			}
			return flag;
		}
	}
	public static class StackManager
	{
		public static Item ItemBeingStored;

		public static byte LastRemovedSlot = byte.MaxValue;

		public static Dictionary<ulong, Dictionary<byte, List<Item>>> ServerStacks = new Dictionary<ulong, Dictionary<byte, List<Item>>>();

		public static Dictionary<ulong, Dictionary<byte, List<Item>>> ClientStacks = new Dictionary<ulong, Dictionary<byte, List<Item>>>();

		public static int MaxStackSize => Plugin.MaxStackSize.Value;

		private static ulong GetSteamID(PlayerInventory inv)
		{
			Player player = inv._player;
			if (player == null)
			{
				return 0uL;
			}
			return player.SteamID;
		}

		public static int GetStackCount(PlayerInventory inv, byte index)
		{
			ulong steamID = GetSteamID(inv);
			if (steamID == 0L)
			{
				return 0;
			}
			if (((NetworkBehaviour)inv).IsServerInitialized && ServerStacks.TryGetValue(steamID, out Dictionary<byte, List<Item>> value) && value.TryGetValue(index, out var value2))
			{
				return value2.Count;
			}
			if (((NetworkBehaviour)inv).Owner != (NetworkConnection)null && ((NetworkBehaviour)inv).Owner.IsLocalClient && ClientStacks.TryGetValue(steamID, out Dictionary<byte, List<Item>> value3) && value3.TryGetValue(index, out var value4))
			{
				return value4.Count;
			}
			return 0;
		}

		public static bool PushServerStack(PlayerInventory inv, byte index, Item item)
		{
			ulong steamID = GetSteamID(inv);
			if (steamID == 0L)
			{
				return false;
			}
			if (!ServerStacks.TryGetValue(steamID, out Dictionary<byte, List<Item>> value))
			{
				value = new Dictionary<byte, List<Item>>();
				ServerStacks[steamID] = value;
			}
			if (!value.TryGetValue(index, out var value2))
			{
				value2 = (value[index] = new List<Item>());
			}
			if (value2.Count < MaxStackSize - 1 && (Object)(object)item.Weapon == (Object)null)
			{
				value2.Add(item);
				return true;
			}
			return false;
		}

		public static Item PopServerStack(PlayerInventory inv, byte index)
		{
			ulong steamID = GetSteamID(inv);
			if (steamID == 0L)
			{
				return null;
			}
			if (!ServerStacks.TryGetValue(steamID, out Dictionary<byte, List<Item>> value) || !value.TryGetValue(index, out var value2))
			{
				return null;
			}
			while (value2.Count > 0)
			{
				Item val = value2[value2.Count - 1];
				value2.RemoveAt(value2.Count - 1);
				if (!((Object)(object)val == (Object)null) && !((NetworkBehaviour)val).IsDeinitializing)
				{
					if (!((Object)(object)val.SyncedHolder != (Object)(object)inv._player))
					{
						return val;
					}
					Plugin.Log.LogWarning((object)$"Discarded a stacked '{((Object)val).name}' in slot {index} that no longer belongs to this player. It is loose in the world rather than in the stack.");
				}
			}
			return null;
		}

		public static void Forget(Item item)
		{
			if (!((Object)(object)item == (Object)null))
			{
				Purge(ServerStacks, item);
				Purge(ClientStacks, item);
			}
		}

		private static void Purge(Dictionary<ulong, Dictionary<byte, List<Item>>> source, Item item)
		{
			foreach (Dictionary<byte, List<Item>> value in source.Values)
			{
				foreach (List<Item> value2 in value.Values)
				{
					for (int num = value2.Count - 1; num >= 0; num--)
					{
						if ((Object)(object)value2[num] == (Object)(object)item)
						{
							value2.RemoveAt(num);
						}
					}
				}
			}
		}

		public static void SyncClientStack(PlayerInventory inv, byte index, Item newItem, Item oldItem)
		{
			ulong steamID = GetSteamID(inv);
			if (steamID != 0L)
			{
				if (!ClientStacks.TryGetValue(steamID, out Dictionary<byte, List<Item>> value))
				{
					value = new Dictionary<byte, List<Item>>();
					ClientStacks[steamID] = value;
				}
				if (!value.TryGetValue(index, out var value2))
				{
					value2 = (value[index] = new List<Item>());
				}
				if ((Object)(object)newItem == (Object)null)
				{
					value2.Clear();
				}
				else if (value2.Count > 0 && (Object)(object)value2[value2.Count - 1] == (Object)(object)newItem)
				{
					value2.RemoveAt(value2.Count - 1);
				}
				else if ((Object)(object)oldItem != (Object)null && (Object)(object)oldItem != (Object)(object)newItem)
				{
					value2.Add(oldItem);
				}
			}
		}
	}
	[Serializable]
	public class SavedPlayerStacks
	{
		public ulong SteamID { get; set; }

		public List<SavedItem> StackedItems { get; set; } = new List<SavedItem>();
	}
	[Serializable]
	public class StackSaveFile
	{
		public List<SavedPlayerStacks> Players { get; set; } = new List<SavedPlayerStacks>();
	}
	public static class StackSaveManager
	{
		private static readonly string SaveFolder = Path.Combine(Paths.ConfigPath, "StackableItems", "Saves");

		private static Dictionary<ulong, List<SavedItem>> PendingRestores = new Dictionary<ulong, List<SavedItem>>();

		public static void Save()
		{
			if (SaveManager.CurServerSave == null)
			{
				return;
			}
			foreach (Player player in PlayerManager.Players)
			{
				SavePlayer(player);
			}
		}

		public static void SavePlayer(Player player)
		{
			if (SaveManager.CurServerSave == null)
			{
				return;
			}
			StackSaveFile stackSaveFile = ReadFile(SaveManager.CurServerSave.Name);
			stackSaveFile.Players.RemoveAll((SavedPlayerStacks playerStacks) => playerStacks.SteamID == player.SteamID);
			SavedPlayerStacks savedPlayerStacks = new SavedPlayerStacks
			{
				SteamID = player.SteamID
			};
			if (StackManager.ServerStacks.TryGetValue(player.SteamID, out Dictionary<byte, List<Item>> value))
			{
				foreach (KeyValuePair<byte, List<Item>> item in value)
				{
					foreach (Item item2 in item.Value)
					{
						if ((Object)(object)item2 != (Object)null)
						{
							savedPlayerStacks.StackedItems.Add(SaveManager.ItemToSavedItem(item.Key, item2));
						}
					}
				}
			}
			if (savedPlayerStacks.StackedItems.Count > 0)
			{
				stackSaveFile.Players.Add(savedPlayerStacks);
			}
			Write(SaveManager.CurServerSave.Name, stackSaveFile);
		}

		public static void Load()
		{
			PendingRestores.Clear();
			if (SaveManager.CurServerSave == null)
			{
				return;
			}
			StackSaveFile stackSaveFile = ReadFile(SaveManager.CurServerSave.Name);
			foreach (SavedPlayerStacks player in stackSaveFile.Players)
			{
				PendingRestores[player.SteamID] = new List<SavedItem>(player.StackedItems);
			}
		}

		private static StackSaveFile ReadFile(string serverName)
		{
			string path = GetPath(serverName);
			if (!File.Exists(path))
			{
				return new StackSaveFile();
			}
			try
			{
				string text = File.ReadAllText(path);
				StackSaveFile stackSaveFile = JsonConvert.DeserializeObject<StackSaveFile>(text);
				return stackSaveFile ?? new StackSaveFile();
			}
			catch
			{
				return new StackSaveFile();
			}
		}

		public static List<SavedItem> GetPending(ulong steamID)
		{
			if (!PendingRestores.TryGetValue(steamID, out List<SavedItem> value))
			{
				return null;
			}
			PendingRestores.Remove(steamID);
			return value;
		}

		public static void SetStats(Item item, SavedItem savedItem)
		{
			item._cookness.Value = savedItem.Cookness;
			item._bettingMultiplier.Value = savedItem.BettingMultiplier;
			item._killScoreMultiplier.Value = savedItem.KillScoreMultiplier;
			item._curSkin.Value = savedItem.SkinIndex;
			if ((Object)(object)item._weapon != (Object)null)
			{
				item._weapon._attachments._syncedSight.Value = savedItem.Sight;
				item._weapon._attachments._syncedBarrelAttachment.Value = savedItem.BarrelAttachment;
				item._weapon._attachments._syncedBulletIndex.Value = savedItem.AmmoType;
				item._weapon._attachments._syncedExtendedMag.Value = savedItem.ExtendedMag;
				item._weapon._attachments._syncedLaserSight.Value = savedItem.LaserSight;
			}
			if ((Object)(object)item._melee != (Object)null)
			{
				item._melee._syncedSharpnessIndex.Value = savedItem.Sharpness;
			}
			if ((Object)(object)item._creature != (Object)null)
			{
				((Item)item._creature)._syncedRandomWeight.Value = savedItem.Weight;
			}
		}

		public static void Delete(string serverName)
		{
			string path = GetPath(serverName);
			if (File.Exists(path))
			{
				File.Delete(path);
			}
		}

		private static void Write(string serverName, StackSaveFile saveFile)
		{
			try
			{
				Directory.CreateDirectory(SaveFolder);
				string path = GetPath(serverName);
				string contents = JsonConvert.SerializeObject((object)saveFile, (Formatting)1);
				File.WriteAllText(path, contents);
			}
			catch
			{
			}
		}

		public static string GetPath(string serverName)
		{
			string text = serverName;
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				text = text.Replace(oldChar, '_');
			}
			return Path.Combine(SaveFolder, text + ".json");
		}
	}
	[HarmonyPatch]
	public static class StackableItemsPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "ServerTryStoreHeldItem")]
		public static void ServerTryStoreHeldItem_Prefix(Item item, out Item __state)
		{
			__state = StackManager.ItemBeingStored;
			StackManager.ItemBeingStored = item;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "ServerTryStoreHeldItem")]
		public static void ServerTryStoreHeldItem_Postfix(Item __state)
		{
			StackManager.ItemBeingStored = __state;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "ResolveHeldItemReplacement")]
		public static void ResolveHeldItemReplacement_Prefix(Item item, out Item __state)
		{
			__state = StackManager.ItemBeingStored;
			StackManager.ItemBeingStored = item;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "ResolveHeldItemReplacement")]
		public static void ResolveHeldItemReplacement_Postfix(Item __state)
		{
			StackManager.ItemBeingStored = __state;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "LocalTrySelectSlot")]
		public static void LocalTrySelectSlot_Prefix(PlayerInventory __instance, out Item __state)
		{
			__state = StackManager.ItemBeingStored;
			Player player = __instance._player;
			object itemBeingStored;
			if (player == null)
			{
				itemBeingStored = null;
			}
			else
			{
				PlayerHolding holding = player.Holding;
				itemBeingStored = ((holding != null) ? holding.HeldItem : null);
			}
			StackManager.ItemBeingStored = (Item)itemBeingStored;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "LocalTrySelectSlot")]
		public static void LocalTrySelectSlot_Postfix(Item __state)
		{
			StackManager.ItemBeingStored = __state;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "UpdateHeldItem")]
		public static void UpdateHeldItem_Prefix(PlayerInventory __instance, out Item __state)
		{
			__state = StackManager.ItemBeingStored;
			Player player = __instance._player;
			object itemBeingStored;
			if (player == null)
			{
				itemBeingStored = null;
			}
			else
			{
				PlayerHolding holding = player.Holding;
				itemBeingStored = ((holding != null) ? holding.HeldItem : null);
			}
			StackManager.ItemBeingStored = (Item)itemBeingStored;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "UpdateHeldItem")]
		public static void UpdateHeldItem_Postfix(Item __state)
		{
			StackManager.ItemBeingStored = __state;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "GetOpenSlot")]
		public static bool GetOpenSlot(PlayerInventory __instance, int selected, ref int __result)
		{
			if ((Object)(object)StackManager.ItemBeingStored != (Object)null)
			{
				string text = ((Object)StackManager.ItemBeingStored).name.Replace("(Clone)", "").Trim();
				for (byte b = 0; b < __instance._availableSlots.Count; b++)
				{
					Item val = __instance._items[b];
					if ((Object)(object)val != (Object)null)
					{
						string text2 = ((Object)val).name.Replace("(Clone)", "").Trim();
						int num = 1 + StackManager.GetStackCount(__instance, b);
						if (text2 == text && num < StackManager.MaxStackSize && (Object)(object)val.Weapon == (Object)null)
						{
							__result = b;
							return false;
						}
					}
				}
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "AddItem")]
		public static bool AddItem(PlayerInventory __instance, byte index, Item item)
		{
			if (!((NetworkBehaviour)__instance).IsServerInitialized || (Object)(object)item == (Object)null)
			{
				return true;
			}
			if (!__instance._items.ContainsKey(index))
			{
				return true;
			}
			if ((Object)(object)item.SyncedHolder != (Object)(object)__instance._player)
			{
				return true;
			}
			foreach (KeyValuePair<byte, Item> item2 in __instance._items)
			{
				if ((Object)(object)item2.Value == (Object)(object)item)
				{
					return false;
				}
			}
			Item val = __instance._items[index];
			if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)item && !StackManager.PushServerStack(__instance, index, val))
			{
				return false;
			}
			__instance._items[index] = item;
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "LoadFromSave")]
		public static void LoadFromSave(PlayerInventory __instance)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsServerInitialized || (Object)(object)__instance._player == (Object)null)
			{
				return;
			}
			ulong steamID = __instance._player.SteamID;
			List<SavedItem> pending = StackSaveManager.GetPending(steamID);
			if (pending == null || pending.Count == 0)
			{
				return;
			}
			if (!StackManager.ServerStacks.TryGetValue(steamID, out Dictionary<byte, List<Item>> value))
			{
				value = new Dictionary<byte, List<Item>>();
				StackManager.ServerStacks[steamID] = value;
			}
			foreach (SavedItem item in pending)
			{
				Item spawnable = GameInfo.GetSpawnable(item.ItemID);
				if (!Object.op_Implicit((Object)(object)spawnable))
				{
					continue;
				}
				Item val = ItemManager.Instance.SpawnNewItem(spawnable, SpawnManager._mainPlayerSpawnPoint.position, Quaternion.identity);
				StackSaveManager.SetStats(val, item);
				if ((Object)(object)val.Creature != (Object)null)
				{
					val.Creature.ServerKillOnSpawn();
					if (item.IsDripCreature)
					{
						val.Creature.SetDrip();
					}
				}
				if ((Object)(object)val.Weapon != (Object)null)
				{
					val.SetSyncedHolder(__instance._player, true);
					byte? b = null;
					for (byte b2 = 0; b2 < __instance._items.Count; b2++)
					{
						if ((Object)(object)__instance._items[b2] == (Object)null)
						{
							b = b2;
							break;
						}
					}
					if (b.HasValue)
					{
						__instance._items[b.Value] = val;
						continue;
					}
					val.Drop(false, default(Vector3), default(Vector3));
					Plugin.Log.LogWarning((object)("Dropped stacked weapon for " + ((Object)__instance._player).name + " because inventory was full."));
				}
				else
				{
					val.SetSyncedHolder(__instance._player, true);
					val.PutInInventory();
					if (!value.TryGetValue(item.InventorySlot, out var value2))
					{
						value2 = new List<Item>();
						value[item.InventorySlot] = value2;
					}
					value2.Add(val);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "RemoveItem")]
		public static bool RemoveItem(PlayerInventory __instance, Item item, out int __state)
		{
			__state = -1;
			if (!((NetworkBehaviour)__instance).IsServerInitialized || (Object)(object)item == (Object)null)
			{
				return true;
			}
			byte b = byte.MaxValue;
			foreach (KeyValuePair<byte, Item> item2 in __instance._items)
			{
				if ((Object)(object)item2.Value == (Object)(object)item)
				{
					b = item2.Key;
					break;
				}
			}
			StackManager.LastRemovedSlot = b;
			if (b != byte.MaxValue)
			{
				Item val = StackManager.PopServerStack(__instance, b);
				if ((Object)(object)val != (Object)null)
				{
					__instance._items[b] = val;
					__state = b;
					return false;
				}
			}
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "RemoveItem")]
		public static void RemoveItem_Postfix(PlayerInventory __instance, int __state)
		{
			StackManager.LastRemovedSlot = byte.MaxValue;
			if (__state >= 0 && !(((NetworkBehaviour)__instance).Owner == (NetworkConnection)null) && ((NetworkBehaviour)__instance).Owner.IsLocalClient && __instance._localCurSlot != __state)
			{
				__instance.ApplySlot(__state);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Item), "DestroyByEating")]
		public static void EatItem(Item __instance)
		{
			Player lastHolder = __instance.LastHolder;
			if (!((Object)(object)lastHolder == (Object)null) && !((Object)(object)Player.LocalPlayer != (Object)(object)lastHolder))
			{
				PlayerInventory inventory = lastHolder.Inventory;
				if (!((Object)(object)inventory == (Object)null) && !(((NetworkBehaviour)inventory).Owner == (NetworkConnection)null) && ((NetworkBehaviour)inventory).Owner.IsLocalClient)
				{
					inventory.ApplySlot(inventory._localCurSlot);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Item), "SetSyncedHolder")]
		public static void Item_SetSyncedHolder_Postfix(Item __instance)
		{
			if ((Object)(object)__instance.SyncedHolder == (Object)null)
			{
				StackManager.Forget(__instance);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Item), "OnStopServer")]
		public static void Item_OnStopServer_Postfix(Item __instance)
		{
			StackManager.Forget(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "ServerDropAll")]
		public static void ServerDropAll(PlayerInventory __instance, Vector3 pos, Quaternion rot)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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_00e2: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsServerInitialized || Plugin.ModInstalled("kvasir.keepinventory"))
			{
				return;
			}
			Player player = __instance._player;
			ulong num = ((player != null) ? player.SteamID : 0);
			if (num == 0L || !StackManager.ServerStacks.TryGetValue(num, out Dictionary<byte, List<Item>> value))
			{
				return;
			}
			Vector3 val = Vector3.zero;
			foreach (KeyValuePair<byte, List<Item>> item in value)
			{
				Item[] array = item.Value.ToArray();
				item.Value.Clear();
				Item[] array2 = array;
				foreach (Item val2 in array2)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						val += Vector3.up * val2.ModelHeight;
						val2.SetSyncedHolder((Player)null, false);
						if (!val2.RigidbodySync.IsSimulatedLocal)
						{
							val2.RigidbodySync.StartSimulateLocal(pos + val, rot);
						}
						else
						{
							((Component)val2).transform.position = pos + val;
							val2.Rig.linearVelocity = Vector3.zero;
							val2.Rig.angularVelocity = Vector3.zero;
						}
						val += Vector3.up * val2.ModelHeight;
					}
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerInventory), "OnItemsChange")]
		public static void OnItemsChange(PlayerInventory __instance, SyncDictionaryOperation op, byte index, Item item, bool asServer)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if (!asServer && (int)op == 3 && ((NetworkBehaviour)__instance).Owner != (NetworkConnection)null && ((NetworkBehaviour)__instance).Owner.IsLocalClient)
			{
				Item item2 = __instance._itemSlots[index].Item;
				StackManager.SyncClientStack(__instance, index, item, item2);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "OnStopServer")]
		public static void OnStopServer(PlayerInventory __instance)
		{
			if ((Object)(object)__instance._player != (Object)null)
			{
				StackSaveManager.SavePlayer(__instance._player);
				ulong steamID = __instance._player.SteamID;
				StackManager.ServerStacks.Remove(steamID);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerInventory), "OnStopClient")]
		public static void OnStopClient(PlayerInventory __instance)
		{
			Player player = __instance._player;
			ulong num = ((player != null) ? player.SteamID : 0);
			if (num != 0L)
			{
				StackManager.ClientStacks.Remove(num);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventorySlot), "SetItem")]
		public static void SetItem(InventorySlot __instance, Item item)
		{
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item == (Object)null)
			{
				((TMP_Text)__instance._itemText).text = "";
				return;
			}
			PlayerInventory inventory = Player.LocalPlayer.Inventory;
			if ((Object)(object)inventory == (Object)null)
			{
				return;
			}
			byte b = byte.MaxValue;
			for (byte b2 = 0; b2 < inventory._itemSlots.Count; b2++)
			{
				if ((Object)(object)inventory._itemSlots[b2] == (Object)(object)__instance)
				{
					b = b2;
					break;
				}
			}
			if (b == byte.MaxValue)
			{
				return;
			}
			int num = StackManager.GetStackCount(inventory, b) + 1;
			if (num > 1)
			{
				if ((Object)(object)item.Mesh == (Object)null)
				{
					((TMP_Text)__instance._itemText).text = $"{((Object)item).name} ({num})";
				}
				else
				{
					((TMP_Text)__instance._itemText).text = num.ToString();
				}
				((Component)__instance._itemText).gameObject.SetActive(true);
			}
			else if ((Object)(object)item.Mesh != (Object)null)
			{
				((TMP_Text)__instance._itemText).text = "";
			}
			((TMP_Text)__instance._itemText).verticalAlignment = (VerticalAlignmentOptions)1024;
			((TMP_Text)__instance._itemText).horizontalAlignment = (HorizontalAlignmentOptions)1;
			((TMP_Text)__instance._itemText).fontSize = 16f;
			((TMP_Text)__instance._itemText).fontSizeMax = 18f;
			((TMP_Text)__instance._itemText).fontSizeMin = 10f;
			((Graphic)__instance._itemText).color = Color.white;
			((TMP_Text)__instance._itemText).transform.localPosition = new Vector3(7.25f, 5f, 0f);
			((TMP_Text)__instance._itemText).transform.SetAsLastSibling();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SaveManager), "SaveServer")]
		public static void SaveServer()
		{
			StackSaveManager.Save();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SaveManager), "OnServerLoaded")]
		public static void OnServerLoaded()
		{
			StackSaveManager.Load();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SaveManager), "DeleteServer")]
		public static void DeleteServer()
		{
			if (SaveManager.CurServerSave != null)
			{
				StackSaveManager.Delete(SaveManager.CurServerSave.Name);
			}
		}
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ConstantExpectedAttribute : Attribute
	{
		public object? Min { get; set; }

		public object? Max { get; set; }
	}
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ExperimentalAttribute : Attribute
	{
		public string DiagnosticId { get; }

		public string? UrlFormat { get; set; }

		public ExperimentalAttribute(string diagnosticId)
		{
			DiagnosticId = diagnosticId;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullAttribute : Attribute
	{
		public string[] Members { get; }

		public MemberNotNullAttribute(string member)
		{
			Members = new string[1] { member };
		}

		public MemberNotNullAttribute(params string[] members)
		{
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public string[] Members { get; }

		public MemberNotNullWhenAttribute(bool returnValue, string member)
		{
			ReturnValue = returnValue;
			Members = new string[1] { member };
		}

		public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
		{
			ReturnValue = returnValue;
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SetsRequiredMembersAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class StringSyntaxAttribute : Attribute
	{
		public const string CompositeFormat = "CompositeFormat";

		public const string DateOnlyFormat = "DateOnlyFormat";

		public const string DateTimeFormat = "DateTimeFormat";

		public const string EnumFormat = "EnumFormat";

		public const string GuidFormat = "GuidFormat";

		public const string Json = "Json";

		public const string NumericFormat = "NumericFormat";

		public const string Regex = "Regex";

		public const string TimeOnlyFormat = "TimeOnlyFormat";

		public const string TimeSpanFormat = "TimeSpanFormat";

		public const string Uri = "Uri";

		public const string Xml = "Xml";

		public string Syntax { get; }

		public object?[] Arguments { get; }

		public StringSyntaxAttribute(string syntax)
		{
			Syntax = syntax;
			Arguments = new object[0];
		}

		public StringSyntaxAttribute(string syntax, params object?[] arguments)
		{
			Syntax = syntax;
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class UnscopedRefAttribute : Attribute
	{
	}
}
namespace System.Runtime.Versioning
{
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresPreviewFeaturesAttribute : Attribute
	{
		public string? Message { get; }

		public string? Url { get; set; }

		public RequiresPreviewFeaturesAttribute()
		{
		}

		public RequiresPreviewFeaturesAttribute(string? message)
		{
			Message = message;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CallerArgumentExpressionAttribute : Attribute
	{
		public string ParameterName { get; }

		public CallerArgumentExpressionAttribute(string parameterName)
		{
			ParameterName = parameterName;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CollectionBuilderAttribute : Attribute
	{
		public Type BuilderType { get; }

		public string MethodName { get; }

		public CollectionBuilderAttribute(Type builderType, string methodName)
		{
			BuilderType = builderType;
			MethodName = methodName;
		}
	}
	[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CompilerFeatureRequiredAttribute : Attribute
	{
		public const string RefStructs = "RefStructs";

		public const string RequiredMembers = "RequiredMembers";

		public string FeatureName { get; }

		public bool IsOptional { get; set; }

		public CompilerFeatureRequiredAttribute(string featureName)
		{
			FeatureName = featureName;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
	{
		public string[] Arguments { get; }

		public InterpolatedStringHandlerArgumentAttribute(string argument)
		{
			Arguments = new string[1] { argument };
		}

		public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
		{
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerAttribute : Attribute
	{
	}
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal static class IsExternalInit
	{
	}
	[AttributeUsage(AttributeTargets.Method, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ModuleInitializerAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class OverloadResolutionPriorityAttribute : Attribute
	{
		public int Priority { get; }

		public OverloadResolutionPriorityAttribute(int priority)
		{
			Priority = priority;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ParamCollectionAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiredMemberAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresLocationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SkipLocalsInitAttribute : Attribute
	{
	}
}