Decompiled source of YggdrasilStorage v0.5.2

BepInEx/plugins/YggdrasilStorage/YggdrasilStorage.dll

Decompiled 6 hours ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;
using YggdrasilStorage.Backend;

[assembly: AssemblyTitle("YggdrasilStorage")]
[assembly: AssemblyDescription("Independent Valheim storage and crafting prototype, inspired by BlushieMagic's Terraria Magic Storage.")]
[assembly: AssemblyFileVersion("0.5.2.0")]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.5.2.0")]
namespace YggdrasilStorage.Backend
{
	public static class BulkDeposit
	{
		public static Dictionary<string, int> Parse(string manifest)
		{
			if (string.IsNullOrEmpty(manifest) || manifest.Length > 1800)
			{
				throw new ArgumentException("Invalid deposit manifest.");
			}
			string[] array = manifest.Split(new char[1] { ';' });
			if (array.Length > 20)
			{
				throw new ArgumentException("Too many deposit entries.");
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			string[] array2 = array;
			foreach (string text in array2)
			{
				string[] array3 = text.Split(new char[1] { '=' });
				if (array3.Length != 2 || array3[0].Length != 64 || array3[0].Any((char c) => !Uri.IsHexDigit(c)) || !int.TryParse(array3[1], out var result) || result < 1 || result > 10000 || dictionary.ContainsKey(array3[0]))
				{
					throw new ArgumentException("Invalid deposit entry.");
				}
				dictionary.Add(array3[0], result);
			}
			return dictionary;
		}

		public static MovePlan Plan(IEnumerable<StoreState> stores, string source, ISet<string> destinations, IDictionary<string, int> quantities)
		{
			List<StoreState> list = stores.Select((StoreState s) => s.Copy()).ToList();
			List<StoreState> list2 = list.Select((StoreState s) => s.Copy()).ToList();
			int num = 0;
			foreach (KeyValuePair<string, int> pair in quantities)
			{
				List<StackState> list3 = list2.Single((StoreState s) => s.Id == source).Items.Where(delegate(StackState i)
				{
					string key = i.Key;
					KeyValuePair<string, int> keyValuePair4 = pair;
					return key == keyValuePair4.Key;
				}).ToList();
				long num2 = ((IEnumerable<StackState>)list3).Sum((Func<StackState, long>)((StackState i) => i.Count));
				KeyValuePair<string, int> keyValuePair = pair;
				if (num2 < keyValuePair.Value)
				{
					throw new InvalidOperationException("Staged deposit changed; no transfer performed.");
				}
				int limit = list3[0].Limit;
				long val = list2.Where((StoreState s) => destinations.Contains(s.Id)).Sum((StoreState s) => (long)(s.Capacity - s.Items.Count) * (long)limit + s.Items.Where(delegate(StackState i)
				{
					string key = i.Key;
					KeyValuePair<string, int> keyValuePair4 = pair;
					return key == keyValuePair4.Key;
				}).Sum((StackState i) => (long)i.Limit - (long)i.Count));
				KeyValuePair<string, int> keyValuePair2 = pair;
				int num3 = (int)Math.Min(keyValuePair2.Value, val);
				if (num3 != 0)
				{
					List<StoreState> stores2 = list2;
					HashSet<string> sources = new HashSet<string> { source };
					ISet<string> destinations2 = destinations;
					KeyValuePair<string, int> keyValuePair3 = pair;
					list2 = Planner.Move(stores2, sources, destinations2, keyValuePair3.Key, num3).After;
					num += num3;
				}
			}
			MovePlan movePlan = new MovePlan();
			movePlan.Before = list;
			movePlan.After = list2;
			movePlan.Moved = num;
			return movePlan;
		}
	}
}
namespace YggdrasilStorage
{
	public static class Compatibility
	{
		private static readonly SortedDictionary<string, Func<ItemData, string>> identities = new SortedDictionary<string, Func<ItemData, string>>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Func<Container, long, bool>> access = new Dictionary<string, Func<Container, long, bool>>();

		public static void RegisterItemIdentity(string modId, Func<ItemData, string> provider)
		{
			if (string.IsNullOrEmpty(modId) || provider == null)
			{
				throw new ArgumentException("A mod ID and provider are required.");
			}
			if (Object.op_Implicit((Object)(object)ZNet.instance))
			{
				throw new InvalidOperationException("Register integrations before entering a world.");
			}
			identities.Add(modId, provider);
		}

		public static void RegisterContainerAccess(string modId, Func<Container, long, bool> allowed)
		{
			if (string.IsNullOrEmpty(modId) || allowed == null)
			{
				throw new ArgumentException("A mod ID and provider are required.");
			}
			if (Object.op_Implicit((Object)(object)ZNet.instance))
			{
				throw new InvalidOperationException("Register integrations before entering a world.");
			}
			access.Add(modId, allowed);
		}

		internal static IDictionary<string, string> Identity(ItemData item)
		{
			return identities.ToDictionary((KeyValuePair<string, Func<ItemData, string>> x) => x.Key, (KeyValuePair<string, Func<ItemData, string>> x) => x.Value(item));
		}

		internal static bool Allowed(Container container, long playerId)
		{
			return access.Values.All((Func<Container, long, bool> check) => check(container, playerId));
		}
	}
	internal sealed class RecipeRow
	{
		internal string Id;

		internal string Name;

		internal string Station;

		internal string Requirements;

		internal string Reason;

		internal int Amount;

		internal bool Ready;
	}
	internal static class Crafting
	{
		private static readonly FieldInfo StationsField = AccessTools.Field(typeof(CraftingStation), "m_allStations");

		internal static List<CraftingStation> Stations(Container core, long playerId)
		{
			return ((List<CraftingStation>)StationsField.GetValue(null)).Where(delegate(CraftingStation s)
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)s) && ((Behaviour)s).isActiveAndEnabled && s.m_hasCraftTab && Object.op_Implicit((Object)(object)((Component)s).GetComponent<ZNetView>()) && ((Component)s).GetComponent<ZNetView>().IsValid())
				{
					Vector3 val = ((Component)s).transform.position - ((Component)core).transform.position;
					if (((Vector3)(ref val)).sqrMagnitude <= Plugin.Radius.Value * Plugin.Radius.Value)
					{
						return Registry.WardAccess(((Component)s).transform.position, playerId);
					}
				}
				return false;
			}).ToList();
		}

		private static string Unsupported(Recipe recipe)
		{
			if (!Object.op_Implicit((Object)(object)recipe) || !recipe.m_enabled || !Object.op_Implicit((Object)(object)recipe.m_item))
			{
				return "Recipe unavailable";
			}
			if (recipe.m_noCraftOnlyUpgrade || (Object.op_Implicit((Object)(object)recipe.GetRequiredStation(1)) && recipe.GetRequiredStation(1).m_upgrader))
			{
				return "Use the station for upgrades";
			}
			if (recipe.m_requireOnlyOneIngredient)
			{
				return "Use the station for alternative-ingredient recipes";
			}
			if (!string.IsNullOrEmpty(recipe.m_item.m_itemData.m_shared.m_dlc))
			{
				return "Use the station for DLC recipes";
			}
			if (recipe.m_resources == null || recipe.m_amount < 1 || recipe.m_amount > 10000)
			{
				return "Unsupported recipe";
			}
			return "";
		}

		private static Dictionary<CraftingStation, int> UsableStations(Container core, Player player)
		{
			return (from s in Stations(core, player.GetPlayerID())
				where s.CheckUsable(player, false)
				select s).ToDictionary((CraftingStation s) => s, (CraftingStation s) => s.GetLevel(false));
		}

		private static CraftingStation ResolveStation(Recipe recipe, Dictionary<CraftingStation, int> stations)
		{
			CraftingStation required = recipe.GetRequiredStation(1);
			if (!Object.op_Implicit((Object)(object)required))
			{
				return null;
			}
			return (from s in stations
				where s.Key.m_name == required.m_name && s.Value >= recipe.GetRequiredStationLevel(1)
				orderby s.Value descending
				select s.Key).FirstOrDefault();
		}

		private static Dictionary<string, int> Requirements(Recipe recipe)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			if (recipe.m_resources == null)
			{
				return dictionary;
			}
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val != null)
				{
					int amount = val.GetAmount(1);
					if (Object.op_Implicit((Object)(object)val.m_resItem) && amount > 0)
					{
						string name = ((Object)((Component)val.m_resItem).gameObject).name;
						dictionary.TryGetValue(name, out var value);
						dictionary[name] = checked(value + amount);
					}
				}
			}
			return dictionary;
		}

		internal static List<RecipeRow> Rows(Container core, Player player, IEnumerable<Container> containers)
		{
			Dictionary<CraftingStation, int> stations = UsableStations(core, player);
			Dictionary<string, long> dictionary = (from i in containers.Where((Container c) => !c.IsInUse()).SelectMany((Container c) => c.GetInventory().GetAllItems())
				where Object.op_Implicit((Object)(object)i.m_dropPrefab) && !i.m_equipped && !SmartCraftIntegration.IsLocked(i) && !EpicLootIntegration.IsMagic(i)
				group i by ((Object)i.m_dropPrefab).name).ToDictionary((IGrouping<string, ItemData> g) => g.Key, (IGrouping<string, ItemData> g) => ((IEnumerable<ItemData>)g).Sum((Func<ItemData, long>)((ItemData i) => i.m_stack)));
			List<RecipeRow> list = new List<RecipeRow>();
			foreach (Recipe item in from r in ObjectDB.instance.m_recipes
				where Object.op_Implicit((Object)(object)r) && r.m_enabled && Object.op_Implicit((Object)(object)r.m_item)
				group r by ((Object)r).name into g
				where g.Count() == 1
				select g.First())
			{
				if (((Object)item).name.Length > 128)
				{
					continue;
				}
				string text = Unsupported(item);
				if (text.Length != 0)
				{
					continue;
				}
				CraftingStation requiredStation = item.GetRequiredStation(1);
				CraftingStation val = ResolveStation(item, stations);
				if (Object.op_Implicit((Object)(object)requiredStation) && !Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				Dictionary<string, int> dictionary2 = Requirements(item);
				List<string> list2 = new List<string>();
				foreach (KeyValuePair<string, int> item2 in dictionary2)
				{
					dictionary.TryGetValue(item2.Key, out var value);
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item2.Key);
					string text2 = ((Object.op_Implicit((Object)(object)itemPrefab) && Object.op_Implicit((Object)(object)itemPrefab.GetComponent<ItemDrop>())) ? itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name : item2.Key);
					list2.Add(text2 + " " + item2.Value + " (" + value + " stored)");
					if (value < item2.Value && text.Length == 0)
					{
						text = "Missing materials";
					}
				}
				list.Add(new RecipeRow
				{
					Id = ((Object)item).name,
					Name = item.m_item.m_itemData.m_shared.m_name,
					Station = (Object.op_Implicit((Object)(object)requiredStation) ? (requiredStation.m_name + " level " + item.GetRequiredStationLevel(1)) : "Hand crafting"),
					Requirements = string.Join(", ", list2.ToArray()),
					Amount = item.m_amount,
					Ready = (text.Length == 0),
					Reason = text
				});
				if (list.Count >= 2048)
				{
					break;
				}
			}
			return list;
		}

		internal static MovePlan Plan(List<StoreState> stores, Dictionary<string, ItemData> prototypes, Container core, Player player, string recipeId)
		{
			List<Recipe> list = ObjectDB.instance.m_recipes.Where((Recipe r) => Object.op_Implicit((Object)(object)r) && ((Object)r).name == recipeId).ToList();
			if (list.Count != 1)
			{
				throw new InvalidOperationException("Recipe missing or ambiguous.");
			}
			Recipe val = list[0];
			string text = Unsupported(val);
			if (text.Length > 0)
			{
				throw new InvalidOperationException(text);
			}
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && !player.IsRecipeKnown(val.m_item.m_itemData.m_shared.m_name))
			{
				throw new InvalidOperationException("Recipe not yet known.");
			}
			CraftingStation val2 = ResolveStation(val, UsableStations(core, player));
			if (Object.op_Implicit((Object)(object)val.GetRequiredStation(1)) && !Object.op_Implicit((Object)(object)val2))
			{
				throw new InvalidOperationException("Required station is missing, inaccessible, under-level, or needs shelter/fire.");
			}
			Dictionary<string, long> dictionary = (from x in stores.SelectMany((StoreState x) => x.Items)
				group x by x.Key).ToDictionary((IGrouping<string, StackState> g) => g.Key, (IGrouping<string, StackState> g) => ((IEnumerable<StackState>)g).Sum((Func<StackState, long>)((StackState x) => x.Count)));
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
			bool flag = false;
			foreach (KeyValuePair<string, int> item in Requirements(val))
			{
				int num = item.Value;
				foreach (KeyValuePair<string, ItemData> item2 in prototypes.OrderBy<KeyValuePair<string, ItemData>, string>((KeyValuePair<string, ItemData> x) => x.Key, StringComparer.Ordinal))
				{
					ItemData value = item2.Value;
					if (((Object)value.m_dropPrefab).name != item.Key || value.m_equipped || SmartCraftIntegration.IsLocked(value) || EpicLootIntegration.IsMagic(value))
					{
						continue;
					}
					dictionary.TryGetValue(item2.Key, out var value2);
					int num2 = (int)Math.Min(num, value2);
					if (num2 > 0)
					{
						dictionary2[item2.Key] = num2;
						dictionary[item2.Key] -= num2;
						num -= num2;
						flag |= value.m_cheated;
						if (num == 0)
						{
							break;
						}
					}
				}
				if (num != 0)
				{
					throw new InvalidOperationException("Not enough " + item.Key + "; nothing consumed.");
				}
			}
			if (Object.op_Implicit((Object)(object)val2))
			{
				flag |= ((Component)val2).GetComponent<ZNetView>().GetZDO().GetBool(ZDOVars.s_cheated, false);
			}
			ItemData val3 = val.m_item.m_itemData.Clone();
			val3.m_dropPrefab = ((Component)val.m_item).gameObject;
			ItemData val4 = Items.Clone(val3);
			val4.m_dropPrefab = ((Component)val.m_item).gameObject;
			val4.m_quality = 1;
			val4.m_variant = 0;
			val4.m_crafterID = player.GetPlayerID();
			val4.m_crafterName = player.GetPlayerName();
			val4.m_durability = val4.GetMaxDurability();
			val4.m_equipped = false;
			val4.m_cheated = flag;
			string key = Items.Key(val4);
			prototypes[key] = val4;
			return Planner.Craft(stores, dictionary2, Registry.Id(core), new StackState
			{
				Key = key,
				Count = val.m_amount,
				Limit = val4.m_shared.m_maxStackSize
			});
		}
	}
	internal static class EpicLootIntegration
	{
		internal const string ModId = "randyknapp.mods.epicloot";

		internal static bool Ready = true;

		private static MethodInfo save;

		private static MethodInfo magic;

		private static MethodInfo displayName;

		internal static void Install()
		{
			if (!Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("EpicLoot.Data.ItemInfo");
				Type type2 = AccessTools.TypeByName("EpicLoot.ItemDataExtensions");
				save = AccessTools.Method(type, "SaveItem", new Type[1] { typeof(ItemData) }, (Type[])null);
				magic = AccessTools.Method(type2, "IsMagic", new Type[1] { typeof(ItemData) }, (Type[])null);
				displayName = AccessTools.Method(type2, "GetDisplayName", new Type[1] { typeof(ItemData) }, (Type[])null);
				if (save == null || magic == null || displayName == null)
				{
					throw new MissingMethodException("Unsupported EpicLoot item-data API.");
				}
				Plugin.Warn("EpicLoot integration active: cached item data flushed before snapshots; named enchanted items searchable; magic items protected from automatic ingredient consumption.");
			}
			catch (Exception ex)
			{
				Ready = false;
				Plugin.Error(ex);
				Plugin.Warn("EpicLoot integration could not load; Yggdrasil writes disabled to protect custom item data.");
			}
		}

		internal static void Prepare(ItemData item)
		{
			if (!Ready)
			{
				throw new InvalidOperationException("EpicLoot integration unavailable; item snapshots disabled.");
			}
			if (save != null)
			{
				save.Invoke(null, new object[1] { item });
			}
		}

		internal static bool IsMagic(ItemData item)
		{
			if (magic != null)
			{
				return (bool)magic.Invoke(null, new object[1] { item });
			}
			return false;
		}

		internal static string Name(ItemData item)
		{
			if (!(displayName == null))
			{
				return (string)displayName.Invoke(null, new object[1] { item });
			}
			return item.m_shared.m_name;
		}
	}
}
namespace YggdrasilStorage.Backend
{
	public sealed class ItemIdentity
	{
		public string Prefab;

		public string RuntimeType;

		public string CrafterName;

		public int Quality;

		public int Variant;

		public int WorldLevel;

		public float Durability;

		public long Crafter;

		public bool Cheated;

		public IDictionary<string, string> CustomData = new Dictionary<string, string>();

		public IDictionary<string, string> Extensions = new Dictionary<string, string>();

		public string Key()
		{
			if (string.IsNullOrEmpty(Prefab))
			{
				throw new ArgumentException("Item has no registered prefab identity.");
			}
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(Prefab);
				binaryWriter.Write(RuntimeType ?? "");
				binaryWriter.Write(Quality);
				binaryWriter.Write(Variant);
				binaryWriter.Write(WorldLevel);
				binaryWriter.Write(Durability);
				binaryWriter.Write(Crafter);
				binaryWriter.Write(CrafterName ?? "");
				binaryWriter.Write(Cheated);
				WriteMap(binaryWriter, CustomData);
				WriteMap(binaryWriter, Extensions);
			}
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(memoryStream.ToArray())).Replace("-", "");
		}

		private static void WriteMap(BinaryWriter w, IDictionary<string, string> data)
		{
			if (data == null)
			{
				w.Write(0);
				return;
			}
			w.Write(data.Count);
			foreach (KeyValuePair<string, string> item in data.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> x) => x.Key, StringComparer.Ordinal))
			{
				w.Write(item.Key);
				w.Write(item.Value != null);
				if (item.Value != null)
				{
					w.Write(item.Value);
				}
			}
		}
	}
	public static class ItemOrdering
	{
		public static string Group(string category, string prefab, string name)
		{
			string text = category ?? "";
			string text2 = ((prefab ?? "") + " " + (name ?? "")).ToLowerInvariant();
			if (text == "Material")
			{
				string text3 = (prefab ?? "").ToLowerInvariant();
				if (Has(text2, "nail"))
				{
					return "03 Nails";
				}
				if ((text3.EndsWith("ore", StringComparison.Ordinal) && !text3.EndsWith("core", StringComparison.Ordinal)) || text3.StartsWith("ore", StringComparison.Ordinal) || Has(text2, " ore", "scrapiron", "ironscrap", "copperscrap", "blackmetalscrap", "scrapmetal"))
				{
					return "02 Ores and scrap";
				}
				if (Has(text2, "wood", "roundlog", "elderbark", "ancientbark", "timber"))
				{
					return "01 Wood";
				}
				if (Has(text2, "copper", "bronze", "iron", "silver", "blackmetal", "flametal", "ingot") || prefab == "Tin")
				{
					return "03 Ingots";
				}
				if (Has(text2, "stone", "flint", "obsidian", "crystal", "marble", "grausten"))
				{
					return "04 Stone and minerals";
				}
				if (Has(text2, "coal", "resin", "tar"))
				{
					return "05 Fuel and resin";
				}
				if (Has(text2, "hide", "leather", "pelt", "scale", "feather", "bone", "chitin", "carapace"))
				{
					return "06 Hides and animal materials";
				}
				if (Has(text2, "seed", "cone", "sapling"))
				{
					return "07 Seeds";
				}
				if (Has(text2, "meat", "berry", "berries", "mushroom", "thistle", "dandelion", "honey", "carrot", "turnip", "onion", "barley", "flax", "sap", "egg", "fish", "flour"))
				{
					return "08 Plants and ingredients";
				}
				if (Has(text2, "coin", "amber", "ruby", "pearl", "gem"))
				{
					return "09 Valuables";
				}
				return "10 Other materials";
			}
			switch (text)
			{
			case "Consumable":
				return "11 Food and consumables";
			case "Ammo":
			case "AmmoNonEquipable":
				return "12 Ammunition";
			case "OneHandedWeapon":
			case "TwoHandedWeapon":
			case "TwoHandedWeaponLeft":
			case "Bow":
				return "13 Weapons";
			case "Shield":
				return "14 Shields";
			case "Helmet":
			case "Chest":
			case "Legs":
			case "Shoulder":
				return "15 Armor";
			case "Utility":
				return "16 Accessories";
			case "Tool":
			case "Torch":
				return "17 Tools";
			case "Trophy":
				return "18 Trophies";
			case "Fish":
				return "08 Plants and ingredients";
			default:
				return "19 " + ((text.Length == 0) ? "Other items" : text);
			}
		}

		private static bool Has(string text, params string[] values)
		{
			foreach (string value in values)
			{
				if (text.IndexOf(value, StringComparison.Ordinal) >= 0)
				{
					return true;
				}
			}
			return false;
		}
	}
}
namespace YggdrasilStorage
{
	internal sealed class ItemRow
	{
		internal string Key;

		internal string Name;

		internal string Category;

		internal string Prefab;

		internal int Variant;

		internal long Count;

		internal long MaterialCount;

		internal int Limit;

		internal bool Tray;
	}
	internal static class Items
	{
		internal static bool MaterialEligible(ItemData item)
		{
			if (!item.m_equipped && item.m_quality == 1 && !SmartCraftIntegration.IsLocked(item))
			{
				return !EpicLootIntegration.IsMagic(item);
			}
			return false;
		}

		internal static List<ItemRow> CarriedRows()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return new List<ItemRow>();
			}
			return (from i in ((Humanoid)localPlayer).GetInventory().GetAllItems()
				where !i.m_equipped && !SmartCraftIntegration.IsLocked(i)
				select i).GroupBy(Key).Select(delegate(IGrouping<string, ItemData> g)
			{
				//IL_0095: Unknown result type (might be due to invalid IL or missing references)
				ItemData val = g.First();
				return new ItemRow
				{
					Key = g.Key,
					Name = EpicLootIntegration.Name(val) + ((val.m_quality > 1) ? (" [Q" + val.m_quality + "]") : ""),
					Count = ((IEnumerable<ItemData>)g).Sum((Func<ItemData, long>)((ItemData i) => i.m_stack)),
					Limit = val.m_shared.m_maxStackSize,
					Category = ((object)val.m_shared.m_itemType).ToString(),
					Prefab = ((Object)val.m_dropPrefab).name,
					Variant = val.m_variant
				};
			}).ToList();
		}

		internal static string Key(ItemData item)
		{
			if (!Object.op_Implicit((Object)(object)item.m_dropPrefab))
			{
				throw new InvalidOperationException("Item has no registered prefab.");
			}
			EpicLootIntegration.Prepare(item);
			ItemIdentity itemIdentity = new ItemIdentity();
			itemIdentity.Prefab = ((Object)item.m_dropPrefab).name;
			itemIdentity.RuntimeType = ((object)item).GetType().FullName;
			itemIdentity.Quality = item.m_quality;
			itemIdentity.Variant = item.m_variant;
			itemIdentity.Durability = item.m_durability;
			itemIdentity.WorldLevel = item.m_worldLevel;
			itemIdentity.Crafter = item.m_crafterID;
			itemIdentity.CrafterName = item.m_crafterName;
			itemIdentity.Cheated = item.m_cheated;
			itemIdentity.CustomData = item.m_customData;
			itemIdentity.Extensions = Compatibility.Identity(item);
			return itemIdentity.Key();
		}

		internal static ItemData Clone(ItemData item)
		{
			ItemData val = item.Clone();
			if (object.ReferenceEquals(val, item))
			{
				throw new InvalidOperationException("An item mod returned the original from Clone; unsafe transfer refused.");
			}
			val.m_customData = new Dictionary<string, string>(item.m_customData ?? new Dictionary<string, string>());
			if (Key(val) != Key(item))
			{
				throw new InvalidOperationException("An item mod did not preserve metadata when cloning; unsafe transfer refused.");
			}
			return val;
		}

		internal static byte[] Bytes(Inventory inventory)
		{
			//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();
			inventory.Save(val);
			return val.GetArray();
		}

		internal static StoreState Read(Container c, Dictionary<string, ItemData> prototypes)
		{
			return Read(c.GetInventory(), Registry.Id(c), prototypes, Key);
		}

		internal static StoreState Read(Inventory inv, string id, Dictionary<string, ItemData> prototypes, Func<ItemData, string> keyFor)
		{
			StoreState storeState = new StoreState();
			storeState.Id = id;
			storeState.Capacity = inv.GetWidth() * inv.GetHeight();
			StoreState storeState2 = storeState;
			foreach (ItemData allItem in inv.GetAllItems())
			{
				string key = keyFor(allItem);
				if (allItem.m_gridPos.x < 0 || allItem.m_gridPos.x >= inv.GetWidth() || allItem.m_gridPos.y < 0 || allItem.m_gridPos.y >= inv.GetHeight())
				{
					throw new InvalidOperationException("Invalid inventory coordinates.");
				}
				storeState2.Items.Add(new StackState
				{
					Key = key,
					Count = allItem.m_stack,
					Limit = allItem.m_shared.m_maxStackSize,
					Slot = allItem.m_gridPos.y * inv.GetWidth() + allItem.m_gridPos.x
				});
				if (prototypes != null && !prototypes.ContainsKey(key))
				{
					prototypes.Add(key, Clone(allItem));
				}
			}
			storeState2.Validate();
			return storeState2;
		}

		internal static List<ItemData> Materialize(StoreState state, int width, Dictionary<string, ItemData> prototypes)
		{
			return state.Items.Select(delegate(StackState x)
			{
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				ItemData val = Clone(prototypes[x.Key]);
				val.m_stack = x.Count;
				val.m_gridPos = new Vector2i(x.Slot % width, x.Slot / width);
				return val;
			}).ToList();
		}

		internal static List<ItemRow> Rows(IEnumerable<Container> containers, bool tray)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, ItemRow> dictionary = new Dictionary<string, ItemRow>();
			checked
			{
				foreach (Container container in containers)
				{
					if (container.IsInUse())
					{
						continue;
					}
					Registry.Load.Invoke(container, null);
					foreach (ItemData allItem in container.GetInventory().GetAllItems())
					{
						string key = Key(allItem);
						if (!dictionary.TryGetValue(key, out var value))
						{
							ItemRow itemRow = new ItemRow();
							itemRow.Key = key;
							itemRow.Name = EpicLootIntegration.Name(allItem) + ((allItem.m_quality > 1) ? (" [Q" + allItem.m_quality + "]") : "");
							itemRow.Category = ((object)allItem.m_shared.m_itemType).ToString();
							itemRow.Limit = allItem.m_shared.m_maxStackSize;
							itemRow.Tray = tray;
							itemRow.Prefab = ((Object)allItem.m_dropPrefab).name;
							itemRow.Variant = allItem.m_variant;
							value = itemRow;
							dictionary.Add(key, value);
						}
						value.Count += allItem.m_stack;
						if (MaterialEligible(allItem))
						{
							value.MaterialCount += allItem.m_stack;
						}
					}
				}
				return dictionary.Values.OrderBy<ItemRow, string>((ItemRow x) => x.Name, StringComparer.Ordinal).ToList();
			}
		}
	}
	internal sealed class MaterialRecipe
	{
		internal string Id;

		internal string Name;

		internal string Kind;

		internal int Amount;

		internal readonly Dictionary<string, int> Needs = new Dictionary<string, int>(StringComparer.Ordinal);

		internal readonly Dictionary<string, string> Labels = new Dictionary<string, string>(StringComparer.Ordinal);
	}
	internal static class MaterialRecipes
	{
		internal static List<MaterialRecipe> Catalog()
		{
			List<MaterialRecipe> list = new List<MaterialRecipe>();
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				return list;
			}
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (Object.op_Implicit((Object)(object)recipe) && recipe.m_enabled && Object.op_Implicit((Object)(object)recipe.m_item) && !recipe.m_noCraftOnlyUpgrade && !recipe.m_requireOnlyOneIngredient && recipe.m_amount >= 1)
				{
					Add(list, "item:" + ((Object)recipe).name, recipe.m_item.m_itemData.m_shared.m_name, "Item recipe", recipe.m_amount, recipe.m_resources);
				}
			}
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				ItemDrop val = (Object.op_Implicit((Object)(object)item) ? item.GetComponent<ItemDrop>() : null);
				PieceTable val2 = (Object.op_Implicit((Object)(object)val) ? val.m_itemData.m_shared.m_buildPieces : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					continue;
				}
				foreach (GameObject piece in val2.m_pieces)
				{
					if (Object.op_Implicit((Object)(object)piece) && hashSet.Add(piece))
					{
						Piece component = piece.GetComponent<Piece>();
						if (Object.op_Implicit((Object)(object)component) && component.m_enabled)
						{
							Add(list, "build:" + ((Object)piece).name, component.m_name, "Building piece", 1, component.m_resources);
						}
					}
				}
			}
			return (from r in list
				group r by r.Id into g
				select g.First()).ToList();
		}

		private static void Add(List<MaterialRecipe> rows, string id, string name, string kind, int output, Requirement[] requirements)
		{
			if (requirements == null)
			{
				return;
			}
			MaterialRecipe materialRecipe = new MaterialRecipe();
			materialRecipe.Id = id;
			materialRecipe.Name = name;
			materialRecipe.Kind = kind;
			materialRecipe.Amount = output;
			MaterialRecipe materialRecipe2 = materialRecipe;
			foreach (Requirement val in requirements)
			{
				if (val == null || !Object.op_Implicit((Object)(object)val.m_resItem))
				{
					continue;
				}
				int amount = val.GetAmount(1);
				if (amount > 0)
				{
					string name2 = ((Object)((Component)val.m_resItem).gameObject).name;
					materialRecipe2.Needs.TryGetValue(name2, out var value);
					long num = (long)value + (long)amount;
					if (num > 10000)
					{
						return;
					}
					materialRecipe2.Needs[name2] = (int)num;
					materialRecipe2.Labels[name2] = val.m_resItem.m_itemData.m_shared.m_name;
				}
			}
			if (materialRecipe2.Needs.Count > 0 && materialRecipe2.Needs.Count <= 20)
			{
				rows.Add(materialRecipe2);
			}
		}

		internal static Dictionary<string, int> Quantities(MaterialRecipe recipe, IEnumerable<ItemRow> rows)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, int> need in recipe.Needs)
			{
				KeyValuePair<string, int> keyValuePair = need;
				int num = keyValuePair.Value;
				Func<ItemRow, bool> predicate = delegate(ItemRow r)
				{
					if (!r.Tray)
					{
						string prefab = r.Prefab;
						KeyValuePair<string, int> keyValuePair2 = need;
						if (prefab == keyValuePair2.Key)
						{
							return r.MaterialCount > 0;
						}
					}
					return false;
				};
				foreach (ItemRow item in rows.Where(predicate).OrderBy<ItemRow, string>((ItemRow r) => r.Key, StringComparer.Ordinal))
				{
					int num2 = (int)Math.Min(num, item.MaterialCount);
					if (num2 > 0)
					{
						dictionary.Add(item.Key, num2);
						num -= num2;
					}
					if (num == 0)
					{
						break;
					}
				}
				if (num > 0)
				{
					throw new InvalidOperationException("Missing materials. Nothing withdrawn.");
				}
			}
			if (dictionary.Count > 20)
			{
				throw new InvalidOperationException("Too many different item variants for one material set.");
			}
			return dictionary;
		}
	}
	internal static class Network
	{
		private sealed class PeerState
		{
			internal ZNetPeer Peer;

			internal readonly RequestGate Gate = new RequestGate();

			internal double NextHello;

			internal double NextKeep;
		}

		private sealed class DeferredRequest
		{
			internal PeerState Who;

			internal long Sequence;

			internal ZDOID Core;

			internal byte Action;

			internal string Key;

			internal int Amount;

			internal double Deadline;
		}

		private sealed class Lease
		{
			internal string Ticket;

			internal long Server;

			internal double Until;
		}

		private sealed class Expected
		{
			internal ZRpc Owner;

			internal byte[] Bytes;

			internal uint Revision;

			internal bool Acknowledged;
		}

		private sealed class Operation
		{
			internal PeerState Who;

			internal long Sequence;

			internal string Ticket;

			internal string Key;

			internal byte Action;

			internal int Amount;

			internal Container Core;

			internal List<Container> Containers;

			internal Dictionary<ZDOID, Expected> Expected = new Dictionary<ZDOID, Expected>();

			internal Dictionary<ZDOID, long> Owners = new Dictionary<ZDOID, long>();

			internal HashSet<ZRpc> FrozenPeers = new HashSet<ZRpc>();

			internal List<ZRpc> Participants;

			internal bool Acquiring;

			internal double Deadline;
		}

		private const string Hello = "Yggdrasil.Hello2";

		private const string Welcome = "Yggdrasil.Welcome2";

		private const string Request = "Yggdrasil.Request2";

		private const string Reply = "Yggdrasil.Reply2";

		private const string Acquire = "Yggdrasil.Acquire2";

		private const string Granted = "Yggdrasil.Granted2";

		private const string Release = "Yggdrasil.Release2";

		private const string Freeze = "Yggdrasil.Freeze2";

		private const string Frozen = "Yggdrasil.Frozen3";

		private const string KeepArea = "Yggdrasil.KeepArea3";

		private static readonly Dictionary<ZRpc, DeferredRequest> deferred = new Dictionary<ZRpc, DeferredRequest>();

		private static double nextKeepArea;

		private static readonly Dictionary<ZRpc, PeerState> peers = new Dictionary<ZRpc, PeerState>();

		private static readonly Dictionary<ZDOID, Lease> leases = new Dictionary<ZDOID, Lease>();

		private static PeerState local = new PeerState();

		private static Operation operation;

		private static ZNet world;

		private static string clientToken;

		private static long sequence;

		private static long waiting;

		private static double clientDeadline;

		private static double nextHello;

		private static double nextRequest;

		internal static bool Pending
		{
			get
			{
				if (waiting == 0)
				{
					return Now < nextRequest;
				}
				return true;
			}
		}

		private static double Now => Time.realtimeSinceStartup;

		internal static void Tick()
		{
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: 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)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance != (Object)(object)world)
			{
				Reset();
				world = ZNet.instance;
			}
			if (!Object.op_Implicit((Object)(object)world) || !Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				return;
			}
			try
			{
				List<ZNetPeer> live = world.GetConnectedPeers();
				ZRpc[] array = peers.Keys.Where((ZRpc r) => !live.Any((ZNetPeer p) => p.m_rpc == r)).ToArray();
				foreach (ZRpc key in array)
				{
					peers.Remove(key);
				}
				foreach (ZNetPeer item in live)
				{
					if (!peers.ContainsKey(item.m_rpc))
					{
						Attach(item);
					}
				}
				ZDOID[] array2 = (from x in leases
					where x.Value.Until < Now
					select x.Key).ToArray();
				foreach (ZDOID key2 in array2)
				{
					leases.Remove(key2);
				}
				if (world.IsServer())
				{
					Transactions.CheckJournal();
					RemoteAreas.Tick();
					PollDeferred();
					PollOperation();
				}
				else if (string.IsNullOrEmpty(clientToken) && Now >= nextHello)
				{
					nextHello = Now + 2.0;
					ZNetPeer serverPeer = world.GetServerPeer();
					if (serverPeer != null)
					{
						serverPeer.m_rpc.Invoke("Yggdrasil.Hello2", new object[1] { (object)new ZPackage() });
					}
				}
				if (!world.IsServer() && !string.IsNullOrEmpty(clientToken) && Now >= nextKeepArea)
				{
					nextKeepArea = Now + 5.0;
					Container val = (Object.op_Implicit((Object)(object)PlayerTray.ActiveCore) ? PlayerTray.ActiveCore : StorageWindow.ActiveCore);
					if (Registry.Valid(val))
					{
						ZPackage val2 = new ZPackage();
						val2.Write(clientToken);
						val2.Write(((Component)val).GetComponent<ZNetView>().GetZDO().m_uid);
						ZNetPeer serverPeer2 = world.GetServerPeer();
						if (serverPeer2 != null)
						{
							serverPeer2.m_rpc.Invoke("Yggdrasil.KeepArea3", new object[1] { val2 });
						}
					}
				}
				if (waiting != 0 && Now > clientDeadline)
				{
					waiting = 0L;
					StorageWindow.Status = "No response. Refresh and check the tray before trying another transfer.";
					PlayerTray.Completed(success: false, null, 0);
				}
			}
			catch (Exception ex)
			{
				Plugin.Error(ex);
			}
		}

		private static void Attach(ZNetPeer peer)
		{
			peers.Add(peer.m_rpc, new PeerState
			{
				Peer = peer
			});
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Hello2", (Action<ZRpc, ZPackage>)OnHello);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.KeepArea3", (Action<ZRpc, ZPackage>)OnKeepArea);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Welcome2", (Action<ZRpc, ZPackage>)OnWelcome);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Request2", (Action<ZRpc, ZPackage>)OnRequest);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Reply2", (Action<ZRpc, ZPackage>)OnReply);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Acquire2", (Action<ZRpc, ZPackage>)OnAcquire);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Granted2", (Action<ZRpc, ZPackage>)OnGranted);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Release2", (Action<ZRpc, ZPackage>)OnRelease);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Freeze2", (Action<ZRpc, ZPackage>)OnFreeze);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.Frozen3", (Action<ZRpc, ZPackage>)OnFrozen);
		}

		private static bool FromServer(ZRpc rpc)
		{
			if (Object.op_Implicit((Object)(object)world) && !world.IsServer() && world.GetServerPeer() != null)
			{
				return world.GetServerPeer().m_rpc == rpc;
			}
			return false;
		}

		private static void OnHello(ZRpc rpc, ZPackage ignored)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)world) && world.IsServer() && peers.TryGetValue(rpc, out var value) && !(Now < value.NextHello))
			{
				value.NextHello = Now + 1.0;
				ZPackage val = new ZPackage();
				val.Write(value.Gate.Token);
				rpc.Invoke("Yggdrasil.Welcome2", new object[1] { val });
			}
		}

		private static void OnKeepArea(ZRpc rpc, ZPackage pkg)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || pkg.Size() > 128 || !peers.TryGetValue(rpc, out var value) || Now < value.NextKeep)
			{
				return;
			}
			value.NextKeep = Now + 1.0;
			try
			{
				if (!(pkg.ReadString() != value.Gate.Token))
				{
					RemoteAreas.Touch(value.Peer, pkg.ReadZDOID());
				}
			}
			catch
			{
			}
		}

		private static void PollDeferred()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			KeyValuePair<ZRpc, DeferredRequest>[] array = deferred.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<ZRpc, DeferredRequest> keyValuePair = array[i];
				DeferredRequest value = keyValuePair.Value;
				try
				{
					if (Now > value.Deadline)
					{
						throw new InvalidOperationException("The host could not finish loading this storage area. No transfer was performed; retry shortly.");
					}
					RemoteAreas.Touch(value.Who.Peer, value.Core);
					if (RemoteAreas.Ready(value.Who.Peer, value.Core))
					{
						deferred.Remove(keyValuePair.Key);
						Process(value.Who, value.Sequence, Registry.Find(value.Core), value.Action, value.Key, value.Amount);
					}
				}
				catch (Exception ex)
				{
					deferred.Remove(keyValuePair.Key);
					if (operation != null && operation.Who == value.Who && operation.Sequence == value.Sequence)
					{
						Finish();
					}
					Error(value.Who, value.Sequence, ex.Message);
				}
			}
		}

		private static void OnWelcome(ZRpc rpc, ZPackage pkg)
		{
			if (!FromServer(rpc) || pkg.Size() > 128)
			{
				return;
			}
			try
			{
				string text = pkg.ReadString();
				if (text.Length == 32)
				{
					clientToken = text;
				}
			}
			catch (Exception ex)
			{
				Plugin.Error(ex);
			}
		}

		internal static void Send(Container core, byte action, string key, int amount)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			if (Pending || !Object.op_Implicit((Object)(object)world) || !Registry.Valid(core))
			{
				return;
			}
			if (!world.IsServer() && string.IsNullOrEmpty(clientToken))
			{
				StorageWindow.Status = "Connecting to storage service...";
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(world.IsServer() ? local.Gate.Token : clientToken);
			val.Write(++sequence);
			val.Write(action);
			val.Write(((Component)core).GetComponent<ZNetView>().GetZDO().m_uid);
			val.Write(key ?? "");
			val.Write(amount);
			waiting = sequence;
			clientDeadline = Now + 65.0;
			StorageWindow.Status = ((action == 0) ? "Loading storage..." : "Preparing transfer...");
			if (world.IsServer())
			{
				Handle(local, new ZPackage(val.GetArray()));
				return;
			}
			world.GetServerPeer().m_rpc.Invoke("Yggdrasil.Request2", new object[1] { val });
		}

		private static void OnRequest(ZRpc rpc, ZPackage pkg)
		{
			if (Object.op_Implicit((Object)(object)world) && world.IsServer() && peers.TryGetValue(rpc, out var value))
			{
				Handle(value, pkg);
			}
		}

		private static Player PlayerFor(PeerState p)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if (p.Peer == null)
			{
				if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsDead())
				{
					throw new InvalidOperationException("Player unavailable.");
				}
				return Player.m_localPlayer;
			}
			if (!world.GetConnectedPeers().Contains(p.Peer))
			{
				throw new InvalidOperationException("Player disconnected.");
			}
			GameObject val = ZNetScene.instance.FindInstance(p.Peer.m_characterID);
			Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Player>() : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				throw new InvalidOperationException("Your character has not finished loading on the host. Refresh and retry.");
			}
			ZNetView component = ((Component)val2).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || ((Character)val2).IsDead() || val2.GetPlayerID() == 0)
			{
				throw new InvalidOperationException("Your character is still spawning or unavailable. Retry when ready.");
			}
			if (p.Peer.m_uid == 0 || component.GetZDO().GetOwner() != p.Peer.m_uid)
			{
				throw new InvalidOperationException("Your character's network ownership is not ready. Retry shortly.");
			}
			return val2;
		}

		private static void Handle(PeerState who, ZPackage pkg)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			long num = 0L;
			try
			{
				if (pkg.Size() > 2048)
				{
					return;
				}
				string token = pkg.ReadString();
				num = pkg.ReadLong();
				if (!who.Gate.Accept(token, num, Now))
				{
					Error(who, num, "Repeated request or too many requests; retry shortly.");
					return;
				}
				byte b = pkg.ReadByte();
				ZDOID val = pkg.ReadZDOID();
				string text = pkg.ReadString();
				int num2 = pkg.ReadInt();
				if (b > 7 || ((b == 1 || b == 2) && (text.Length != 64 || num2 < 1 || num2 > 10000)) || (b == 5 && (text.Length < 1 || text.Length > 128)))
				{
					throw new InvalidOperationException("Invalid storage request.");
				}
				if (b == 6 || b == 7)
				{
					BulkDeposit.Parse(text);
				}
				if (who.Peer != null)
				{
					if (deferred.ContainsKey(who.Peer.m_rpc))
					{
						throw new InvalidOperationException("A storage request is already waiting for this area.");
					}
					RemoteAreas.Touch(who.Peer, val);
					if (!RemoteAreas.Ready(who.Peer, val))
					{
						deferred[who.Peer.m_rpc] = new DeferredRequest
						{
							Who = who,
							Sequence = num,
							Core = val,
							Action = b,
							Key = text,
							Amount = num2,
							Deadline = Now + 50.0
						};
						return;
					}
				}
				Process(who, num, Registry.Find(val), b, text, num2);
			}
			catch (Exception ex)
			{
				if (operation != null && operation.Who == who && operation.Sequence == num)
				{
					Finish();
				}
				Error(who, num, ex.Message);
			}
		}

		private static void Process(PeerState who, long seq, Container core, byte action, string key, int amount)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Expected O, but got Unknown
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			Player val = PlayerFor(who);
			long playerID = val.GetPlayerID();
			Registry.DemandCore(core, playerID, ((Component)val).transform.position, action == 6);
			if (action == 0)
			{
				Respond(who, seq, core, playerID, "Ready. Counts update after transfers. Use Refresh to check other changes.");
				return;
			}
			if (!SmartCraftIntegration.Ready || !EpicLootIntegration.Ready)
			{
				throw new InvalidOperationException("A required optional-mod integration is unavailable; transfers disabled.");
			}
			if (Network.operation != null)
			{
				throw new InvalidOperationException("Another transfer is in progress. Retry shortly.");
			}
			List<Container> list = Registry.Discover(core, playerID);
			list.Add(core);
			if (list.Any((Container c) => c.IsInUse() || Transactions.Blocked(c)))
			{
				throw new InvalidOperationException("Close nearby chests and the transfer tray before moving items.");
			}
			Operation operation = new Operation();
			operation.Who = who;
			operation.Sequence = seq;
			operation.Core = core;
			operation.Containers = list;
			operation.Key = key;
			operation.Amount = amount;
			operation.Action = action;
			operation.Ticket = Guid.NewGuid().ToString("N");
			operation.Deadline = Now + 8.0;
			operation.Participants = (from p in world.GetConnectedPeers()
				select p.m_rpc).ToList();
			Operation operation2 = (Network.operation = operation);
			foreach (Container item in list)
			{
				ZNetView component = ((Component)item).GetComponent<ZNetView>();
				ZDO zDO = component.GetZDO();
				if (IsLeased(item))
				{
					throw new InvalidOperationException("Container is reserved by another transfer.");
				}
				leases[zDO.m_uid] = new Lease
				{
					Ticket = operation2.Ticket,
					Server = ZNet.GetUID(),
					Until = Now + 12.0
				};
				operation2.Owners.Add(zDO.m_uid, zDO.GetOwner());
				if (component.IsOwner())
				{
					Registry.Load.Invoke(item, null);
					continue;
				}
				ZNetPeer val2 = liveOwner(zDO.GetOwner());
				if (val2 == null)
				{
					throw new InvalidOperationException("Container owner unavailable; retry once the area has settled.");
				}
				operation2.Expected.Add(zDO.m_uid, new Expected
				{
					Owner = val2.m_rpc
				});
			}
			foreach (ZRpc participant in operation2.Participants)
			{
				ZPackage val3 = new ZPackage();
				val3.Write(operation2.Ticket);
				val3.Write(operation2.Owners.Count);
				foreach (ZDOID key2 in operation2.Owners.Keys)
				{
					val3.Write(key2);
				}
				participant.Invoke("Yggdrasil.Freeze2", new object[1] { val3 });
			}
			SmartCraftIntegration.Invalidate();
			PollOperation();
		}

		private static ZNetPeer liveOwner(long uid)
		{
			return ((IEnumerable<ZNetPeer>)world.GetConnectedPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_uid == uid));
		}

		private static void OnFreeze(ZRpc rpc, ZPackage pkg)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			if (!FromServer(rpc) || pkg.Size() > 4096 || !SmartCraftIntegration.Ready || !EpicLootIntegration.Ready)
			{
				return;
			}
			try
			{
				string text = pkg.ReadString();
				int num = pkg.ReadInt();
				if (text.Length != 32 || num < 1 || num > 129)
				{
					return;
				}
				ZPackage val = new ZPackage();
				val.Write(text);
				val.Write(num);
				for (int i = 0; i < num; i++)
				{
					ZDOID val2 = pkg.ReadZDOID();
					ZDO zDO = ZDOMan.instance.GetZDO(val2);
					Container val3 = Registry.Find(val2);
					leases[val2] = new Lease
					{
						Ticket = text,
						Server = world.GetServerPeer().m_uid,
						Until = Now + 12.0
					};
					val.Write(val2);
					val.Write(zDO != null);
					if (zDO != null)
					{
						val.Write(zDO.GetOwner());
						val.Write(Object.op_Implicit((Object)(object)val3) && val3.IsInUse());
						val.Write(Registry.Valid(val3));
					}
				}
				rpc.Invoke("Yggdrasil.Frozen3", new object[1] { val });
				SmartCraftIntegration.Invalidate();
			}
			catch (Exception ex)
			{
				Plugin.Error(ex);
			}
		}

		private static void OnFrozen(ZRpc rpc, ZPackage pkg)
		{
			//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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			Operation operation = Network.operation;
			if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || operation == null || !operation.Participants.Contains(rpc) || pkg.Size() > 8192)
			{
				return;
			}
			try
			{
				string text = pkg.ReadString();
				int num = pkg.ReadInt();
				if (text != operation.Ticket || num != operation.Owners.Count || operation.FrozenPeers.Contains(rpc))
				{
					return;
				}
				HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
				for (int i = 0; i < num; i++)
				{
					ZDOID val = pkg.ReadZDOID();
					if (!hashSet.Add(val) || !operation.Owners.ContainsKey(val))
					{
						throw new InvalidOperationException("Invalid freeze acknowledgement.");
					}
					if (pkg.ReadBool())
					{
						long observedOwner = pkg.ReadLong();
						bool flag = pkg.ReadBool();
						bool loaded = pkg.ReadBool();
						ZNetPeer val2 = ((IEnumerable<ZNetPeer>)world.GetConnectedPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_rpc == rpc));
						if (val2 == null)
						{
							throw new InvalidOperationException("Player disconnected during storage handoff.");
						}
						if (flag)
						{
							throw new InvalidOperationException("A player is using a connected chest. Close it and retry.");
						}
						if (!OwnershipAgreement.Accept(operation.Owners[val], val2.m_uid, observedOwner, loaded, flag))
						{
							throw new InvalidOperationException("The chest owner is still synchronizing. Retry shortly.");
						}
					}
				}
				operation.FrozenPeers.Add(rpc);
			}
			catch (Exception ex)
			{
				Finish();
				Error(operation.Who, operation.Sequence, ex.Message);
			}
		}

		private static void OnAcquire(ZRpc rpc, ZPackage pkg)
		{
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Expected O, but got Unknown
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			if (!FromServer(rpc) || pkg.Size() > 128)
			{
				return;
			}
			string text = "";
			ZDOID val = default(ZDOID);
			ZPackage val2 = new ZPackage();
			try
			{
				text = pkg.ReadString();
				val = pkg.ReadZDOID();
				if (text.Length != 32)
				{
					return;
				}
				Container val3 = Registry.Find(val);
				if (!Registry.Valid(val3) || val3.IsInUse() || Transactions.Blocked(val3) || !((Component)val3).GetComponent<ZNetView>().IsOwner() || !leases.TryGetValue(val, out var value) || value.Ticket != text || value.Until < Now)
				{
					throw new InvalidOperationException("Container busy or not owned.");
				}
				Piece component = ((Component)val3).GetComponent<Piece>();
				if (!Object.op_Implicit((Object)(object)component) || component.GetCreator() == 0 || Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<Ship>()) || Object.op_Implicit((Object)(object)((Component)val3).GetComponentInParent<Vagon>()) || Object.op_Implicit((Object)(object)((Component)val3).GetComponent<TombStone>()))
				{
					throw new InvalidOperationException("Ineligible container.");
				}
				Registry.Load.Invoke(val3, null);
				ZDO zDO = ((Component)val3).GetComponent<ZNetView>().GetZDO();
				byte[] array = Items.Bytes(val3.GetInventory());
				if (array.Length > 262144)
				{
					throw new InvalidOperationException("Inventory is too large for a handoff.");
				}
				Registry.Save.Invoke(val3, null);
				leases[val] = new Lease
				{
					Ticket = text,
					Server = world.GetServerPeer().m_uid,
					Until = Now + 12.0
				};
				zDO.SetOwner(world.GetServerPeer().m_uid);
				ZDOMan.instance.ForceSendZDO(world.GetServerPeer().m_uid, val);
				val2.Write(text);
				val2.Write(val);
				val2.Write(true);
				val2.Write(zDO.DataRevision);
				val2.Write(array);
			}
			catch (Exception)
			{
				val2 = new ZPackage();
				val2.Write(text);
				val2.Write(val);
				val2.Write(false);
			}
			rpc.Invoke("Yggdrasil.Granted2", new object[1] { val2 });
		}

		private static void OnGranted(ZRpc rpc, ZPackage pkg)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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 (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || Network.operation == null || pkg.Size() > 263000)
			{
				return;
			}
			try
			{
				string text = pkg.ReadString();
				ZDOID key = pkg.ReadZDOID();
				if (!(text != Network.operation.Ticket) && Network.operation.Expected.TryGetValue(key, out var value) && value.Owner == rpc && !value.Acknowledged)
				{
					if (!pkg.ReadBool())
					{
						throw new InvalidOperationException("A chest is being used or changed owner. Close chests and retry.");
					}
					value.Revision = pkg.ReadUInt();
					value.Bytes = pkg.ReadByteArray();
					if (value.Bytes.Length > 262144)
					{
						throw new InvalidOperationException("Invalid handoff size.");
					}
					value.Acknowledged = true;
				}
			}
			catch (Exception ex)
			{
				Operation operation = Network.operation;
				Finish();
				if (operation != null)
				{
					Error(operation.Who, operation.Sequence, ex.Message);
				}
			}
		}

		private static void PollOperation()
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Expected O, but got Unknown
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			if (operation == null)
			{
				return;
			}
			Operation op = operation;
			try
			{
				if (Now > op.Deadline)
				{
					throw new InvalidOperationException("Ownership handoff timed out. No transfer performed.");
				}
				if (op.Who.Peer != null)
				{
					RemoteAreas.Touch(op.Who.Peer, ((Component)op.Core).GetComponent<ZNetView>().GetZDO().m_uid);
					if (!RemoteAreas.Ready(op.Who.Peer, ((Component)op.Core).GetComponent<ZNetView>().GetZDO().m_uid))
					{
						return;
					}
				}
				Player val = PlayerFor(op.Who);
				long playerID = val.GetPlayerID();
				Registry.DemandCore(op.Core, playerID, ((Component)val).transform.position, op.Action == 6);
				if (!op.Participants.All((ZRpc r) => world.GetConnectedPeers().Any((ZNetPeer p) => p.m_rpc == r)) || world.GetConnectedPeers().Any((ZNetPeer p) => !op.Participants.Contains(p.m_rpc)))
				{
					throw new InvalidOperationException("Player connections changed; retry the transfer.");
				}
				if (op.FrozenPeers.Count != op.Participants.Count)
				{
					return;
				}
				if (!op.Acquiring)
				{
					op.Acquiring = true;
					foreach (KeyValuePair<ZDOID, Expected> item in op.Expected)
					{
						ZPackage val2 = new ZPackage();
						val2.Write(op.Ticket);
						val2.Write(item.Key);
						item.Value.Owner.Invoke("Yggdrasil.Acquire2", new object[1] { val2 });
					}
				}
				List<Container> list = Registry.Discover(op.Core, playerID);
				list.Add(op.Core);
				if (!new HashSet<string>(list.Select(Registry.Id)).SetEquals(op.Containers.Where(Registry.Valid).Select(Registry.Id)) || op.Containers.Any((Container c) => !Registry.Valid(c)))
				{
					throw new InvalidOperationException("Storage network changed; refresh and retry.");
				}
				foreach (Container container in op.Containers)
				{
					if (!Registry.Access(container, playerID) || container.IsInUse())
					{
						throw new InvalidOperationException("Access or chest usage changed.");
					}
					ZDO zDO = ((Component)container).GetComponent<ZNetView>().GetZDO();
					if (op.Expected.TryGetValue(zDO.m_uid, out var value))
					{
						if (!value.Acknowledged || !((Component)container).GetComponent<ZNetView>().IsOwner() || zDO.DataRevision < value.Revision)
						{
							return;
						}
						byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, (byte[])null);
						if (byteArray == null || !byteArray.SequenceEqual(value.Bytes))
						{
							throw new InvalidOperationException("Chest contents changed during handoff; retry.");
						}
					}
					else if (!((Component)container).GetComponent<ZNetView>().IsOwner())
					{
						throw new InvalidOperationException("Ownership changed; retry.");
					}
				}
				string outputKey;
				int num = Transactions.Execute(op.Containers, op.Core, val, op.Action, op.Key, op.Amount, out outputKey);
				Finish();
				string text = ((op.Action == 5) ? "Crafted " : "Moved ") + num + (((op.Action >= 2 && op.Action <= 4) || op.Action == 6) ? " into nearby chests." : "; collecting into your inventory...");
				try
				{
					Respond(op.Who, op.Sequence, op.Core, playerID, text, outputKey, num);
				}
				catch (Exception ex)
				{
					Error(op.Who, op.Sequence, text + " Refresh to reload the list.");
					Plugin.Error(ex);
				}
			}
			catch (Exception ex2)
			{
				Finish();
				Error(op.Who, op.Sequence, ex2.Message);
				Plugin.Error(ex2);
			}
		}

		private static void Finish()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			Operation op = operation;
			operation = null;
			if (op == null)
			{
				return;
			}
			foreach (ZRpc participant in op.Participants)
			{
				try
				{
					ZPackage val = new ZPackage();
					val.Write(op.Ticket);
					participant.Invoke("Yggdrasil.Release2", new object[1] { val });
				}
				catch (Exception ex)
				{
					Plugin.Error(ex);
				}
			}
			ZDOID[] array = (from x in leases
				where x.Value.Ticket == op.Ticket
				select x.Key).ToArray();
			foreach (ZDOID key in array)
			{
				leases.Remove(key);
			}
			SmartCraftIntegration.Invalidate();
		}

		private static void OnRelease(ZRpc rpc, ZPackage pkg)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (!FromServer(rpc) || pkg.Size() > 128)
			{
				return;
			}
			try
			{
				string ticket = pkg.ReadString();
				ZDOID[] array = (from x in leases
					where x.Value.Ticket == ticket
					select x.Key).ToArray();
				foreach (ZDOID key in array)
				{
					leases.Remove(key);
				}
				SmartCraftIntegration.Invalidate();
			}
			catch (Exception ex)
			{
				Plugin.Error(ex);
			}
		}

		internal static bool IsLeased(Container c)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (Transactions.Blocked(c))
			{
				return true;
			}
			if (!Registry.Valid(c))
			{
				return false;
			}
			if (leases.TryGetValue(((Component)c).GetComponent<ZNetView>().GetZDO().m_uid, out var value))
			{
				return value.Until >= Now;
			}
			return false;
		}

		internal static bool AllowOwner(ZDO zdo, long owner)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (leases.TryGetValue(zdo.m_uid, out var value) && !(value.Until < Now))
			{
				return owner == value.Server;
			}
			return true;
		}

		private static void Respond(PeerState who, long seq, Container core, long pid, string message, string outputKey = "", int outputAmount = 0)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			List<Container> list = Registry.Discover(core, pid);
			List<ItemRow> list2 = Items.Rows(list, tray: false);
			list2.AddRange(Items.Rows((IEnumerable<Container>)(object)new Container[1] { core }, tray: true));
			if (list2.Count > 2048)
			{
				throw new InvalidOperationException("Too many distinct items for this prototype; reduce the Core radius.");
			}
			ZPackage val = new ZPackage();
			val.Write(seq);
			val.Write(true);
			val.Write(message);
			val.Write(Plugin.Radius.Value);
			val.Write(list.Count);
			val.Write(list2.Count);
			foreach (ItemRow item in list2)
			{
				val.Write(item.Key);
				val.Write((item.Name.Length > 128) ? item.Name.Substring(0, 128) : item.Name);
				val.Write(item.Category);
				val.Write(item.Count);
				val.Write(item.Limit);
				val.Write(item.Tray);
				val.Write(item.Prefab);
				val.Write(item.Variant);
				val.Write(item.MaterialCount);
			}
			List<RecipeRow> list3 = Crafting.Rows(core, PlayerFor(who), list.Concat((IEnumerable<Container>)(object)new Container[1] { core }));
			val.Write(list3.Count);
			foreach (RecipeRow item2 in list3)
			{
				val.Write(item2.Id);
				val.Write(item2.Name);
				val.Write(item2.Station);
				val.Write((item2.Requirements.Length > 1024) ? item2.Requirements.Substring(0, 1024) : item2.Requirements);
				val.Write(item2.Amount);
				val.Write(item2.Ready);
				val.Write(item2.Reason);
			}
			val.Write(outputKey ?? "");
			val.Write(outputAmount);
			ZDO zDO = ((Component)core).GetComponent<ZNetView>().GetZDO();
			val.Write(zDO.DataRevision);
			val.Write(string.IsNullOrEmpty(outputKey) ? new byte[0] : zDO.GetByteArray(ZDOVars.s_items, new byte[0]));
			if (val.Size() > 1048576)
			{
				throw new InvalidOperationException("Storage snapshot too large; reduce radius or recipe count.");
			}
			Deliver(who, val);
		}

		private static void Error(PeerState who, long seq, string message)
		{
			//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(seq);
			val.Write(false);
			val.Write((message.Length > 512) ? message.Substring(0, 512) : message);
			Deliver(who, val);
		}

		private static void Deliver(PeerState who, ZPackage p)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			if (who.Peer == null)
			{
				ReadReply(new ZPackage(p.GetArray()));
				return;
			}
			who.Peer.m_rpc.Invoke("Yggdrasil.Reply2", new object[1] { p });
		}

		private static void OnReply(ZRpc rpc, ZPackage p)
		{
			if (FromServer(rpc))
			{
				ReadReply(p);
			}
		}

		private static void ReadReply(ZPackage p)
		{
			try
			{
				if (p.Size() > 1048576 || p.ReadLong() != waiting)
				{
					return;
				}
				waiting = 0L;
				nextRequest = Now + 0.3;
				bool flag = p.ReadBool();
				StorageWindow.Status = p.ReadString();
				if (!flag)
				{
					PlayerTray.Completed(success: false, null, 0);
					StorageWindow.RefreshSoon();
					return;
				}
				float range = p.ReadSingle();
				int count = p.ReadInt();
				int num = p.ReadInt();
				if (num >= 0 && num <= 2048)
				{
					List<ItemRow> list = new List<ItemRow>();
					for (int i = 0; i < num; i++)
					{
						list.Add(new ItemRow
						{
							Key = p.ReadString(),
							Name = p.ReadString(),
							Category = p.ReadString(),
							Count = p.ReadLong(),
							Limit = p.ReadInt(),
							Tray = p.ReadBool(),
							Prefab = p.ReadString(),
							Variant = p.ReadInt(),
							MaterialCount = p.ReadLong()
						});
					}
					int num2 = p.ReadInt();
					if (num2 < 0 || num2 > 2048)
					{
						throw new InvalidOperationException("Invalid recipe count.");
					}
					List<RecipeRow> list2 = new List<RecipeRow>();
					for (int j = 0; j < num2; j++)
					{
						list2.Add(new RecipeRow
						{
							Id = p.ReadString(),
							Name = p.ReadString(),
							Station = p.ReadString(),
							Requirements = p.ReadString(),
							Amount = p.ReadInt(),
							Ready = p.ReadBool(),
							Reason = p.ReadString()
						});
					}
					string text = p.ReadString();
					int num3 = p.ReadInt();
					if (text.StartsWith("batch:", StringComparison.Ordinal))
					{
						BulkDeposit.Parse(text.Substring(6));
					}
					if ((!text.StartsWith("batch:", StringComparison.Ordinal) && text.Length != 0 && text.Length != 64) || num3 < 0 || num3 > 1000000)
					{
						throw new InvalidOperationException("Invalid collection output.");
					}
					uint revision = p.ReadUInt();
					byte[] array = p.ReadByteArray();
					if (array.Length > 262144)
					{
						throw new InvalidOperationException("Collection snapshot is too large.");
					}
					StorageWindow.SetRows(list, range, count);
					StorageWindow.SetRecipes(list2);
					PlayerTray.Completed(success: true, text, num3, revision, array);
				}
			}
			catch (Exception ex)
			{
				waiting = 0L;
				StorageWindow.ClearRows();
				PlayerTray.Completed(success: false, null, 0);
				StorageWindow.Status = "Invalid server response. Check Core contents before retrying.";
				Plugin.Error(ex);
			}
		}

		internal static void Reset()
		{
			deferred.Clear();
			RemoteAreas.Reset();
			nextKeepArea = 0.0;
			peers.Clear();
			leases.Clear();
			operation = null;
			local = new PeerState();
			clientToken = null;
			sequence = 0L;
			waiting = 0L;
			nextHello = 0.0;
			nextRequest = 0.0;
			Transactions.Reset();
			StorageWindow.Close();
			PlayerTray.Cancel();
		}
	}
	[HarmonyPatch(typeof(ZDO), "SetOwner")]
	internal static class HoldOwnership
	{
		private static bool Prefix(ZDO __instance, long __0)
		{
			return Network.AllowOwner(__instance, __0);
		}
	}
	[HarmonyPatch]
	internal static class LeaseContainerRequests
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Container), "RPC_RequestOpen", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Container), "RPC_RequestStack", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Container), "RPC_RequestTakeAll", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Container), "RPC_OpenResponse", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Container), "RPC_StackResponse", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Container), "RPC_TakeAllResponse", (Type[])null, (Type[])null);
		}

		private static bool Prefix(Container __instance)
		{
			return !Network.IsLeased(__instance);
		}
	}
}
namespace YggdrasilStorage.Backend
{
	public static class OwnershipAgreement
	{
		public static bool Accept(long expectedOwner, long reportingPeer, long observedOwner, bool loaded, bool busy)
		{
			if (busy)
			{
				return false;
			}
			if (reportingPeer == expectedOwner || (loaded && observedOwner == reportingPeer))
			{
				return observedOwner == expectedOwner;
			}
			return true;
		}
	}
}
namespace YggdrasilStorage
{
	internal static class PlayerTray
	{
		internal static bool OpeningTray;

		private static int stage;

		private static Container target;

		private static string key;

		private static int amount;

		private static bool granted;

		private static float deadline;

		private static uint expectedRevision;

		private static byte[] expectedBuffer;

		private static bool depositing;

		private static float distributeAt;

		private static bool quick;

		private static int deposited;

		internal static int QuickUseFrame = -1;

		internal static Container ActiveCore
		{
			get
			{
				if (!Busy)
				{
					return null;
				}
				return target;
			}
		}

		internal static bool Busy => stage != 0;

		internal static bool BlocksMovement
		{
			get
			{
				if (Busy)
				{
					return !quick;
				}
				return false;
			}
		}

		internal static void TryQuickDeposit()
		{
			Player p = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)p) || Busy || Network.Pending || StorageWindow.Visible || InventoryGui.IsVisible() || !SmartCraftIntegration.Ready || !EpicLootIntegration.Ready || !(bool)AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null).Invoke(p, null) || (!Input.GetKey((KeyCode)304) && !Input.GetKey((KeyCode)303)) || !ZInput.GetButtonDown("Use"))
			{
				return;
			}
			Container val = Registry.All.Where(delegate(Container c)
			{
				//IL_002e: 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_0043: 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)
				if (Registry.Valid(c) && Object.op_Implicit((Object)(object)((Component)c).GetComponent<StorageCore>()) && Registry.Access(c, p.GetPlayerID()))
				{
					Vector3 val2 = ((Component)c).transform.position - ((Component)p).transform.position;
					return ((Vector3)(ref val2)).sqrMagnitude <= Plugin.Radius.Value * Plugin.Radius.Value;
				}
				return false;
			}).OrderBy(delegate(Container c)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: 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)
				Vector3 val2 = ((Component)c).transform.position - ((Component)p).transform.position;
				return ((Vector3)(ref val2)).sqrMagnitude;
			}).ThenBy(Registry.Id)
				.FirstOrDefault();
			if (Object.op_Implicit((Object)(object)val))
			{
				if (!((Humanoid)p).GetInventory().GetAllItems().Any((ItemData i) => i.m_gridPos.y != 0 && !i.m_equipped && !SmartCraftIntegration.IsLocked(i)))
				{
					QuickUseFrame = Time.frameCount;
					StorageWindow.Status = "Nothing to store";
					((Character)p).Message((MessageType)2, StorageWindow.Status, 0, (Sprite)null, false);
					return;
				}
				Start(val);
				quick = true;
				depositing = true;
				deposited = 0;
				stage = 2;
				QuickUseFrame = Time.frameCount;
			}
		}

		internal static void Deposit(Container core, ItemRow row, int count)
		{
			if (!Busy && !Network.Pending && Object.op_Implicit((Object)(object)Player.m_localPlayer) && SmartCraftIntegration.Ready && EpicLootIntegration.Ready)
			{
				Start(core);
				depositing = true;
				key = row.Key;
				amount = count;
				stage = 2;
			}
		}

		internal static void Withdraw(Container core, ItemRow row, int count)
		{
			if (Busy || Network.Pending || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			try
			{
				Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
				StoreState storeState = Items.Read(inventory, "player", null, Items.Key);
				long num = (long)(storeState.Capacity - storeState.Items.Count) * (long)row.Limit + storeState.Items.Where((StackState x) => x.Key == row.Key).Sum((StackState x) => (long)x.Limit - (long)x.Count);
				if (num < count)
				{
					StorageWindow.Status = "Not enough inventory space for that quantity.";
					return;
				}
				Start(core);
				key = row.Key;
				amount = count;
				if (row.Tray)
				{
					stage = 2;
				}
				else
				{
					Network.Send(core, 1, key, count);
				}
			}
			catch (Exception ex)
			{
				Cancel();
				StorageWindow.Status = ex.Message;
				Plugin.Error(ex);
			}
		}

		internal static void CollectMaterials(Container core, Dictionary<string, int> quantities, List<ItemRow> rows)
		{
			if (Busy || Network.Pending || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			try
			{
				string manifest = string.Join(";", (from p in quantities.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.Ordinal)
					select p.Key + "=" + p.Value).ToArray());
				BulkDeposit.Parse(manifest);
				StoreState storeState = new StoreState();
				storeState.Id = "materials";
				storeState.Capacity = 4096;
				StoreState storeState2 = storeState;
				foreach (KeyValuePair<string, int> pair in quantities)
				{
					Func<ItemRow, bool> predicate = delegate(ItemRow r)
					{
						if (!r.Tray)
						{
							string text = r.Key;
							KeyValuePair<string, int> keyValuePair3 = pair;
							return text == keyValuePair3.Key;
						}
						return false;
					};
					ItemRow itemRow = rows.First(predicate);
					KeyValuePair<string, int> keyValuePair = pair;
					int num = keyValuePair.Value;
					while (num > 0)
					{
						int num2 = Math.Min(num, itemRow.Limit);
						List<StackState> items = storeState2.Items;
						StackState stackState = new StackState();
						KeyValuePair<string, int> keyValuePair2 = pair;
						stackState.Key = keyValuePair2.Key;
						stackState.Count = num2;
						stackState.Limit = itemRow.Limit;
						stackState.Slot = storeState2.Items.Count;
						items.Add(stackState);
						num -= num2;
					}
				}
				StoreState storeState3 = Items.Read(((Humanoid)Player.m_localPlayer).GetInventory(), "player", null, Items.Key);
				Planner.BatchMove(new StoreState[2] { storeState2, storeState3 }, new HashSet<string> { "materials" }, new HashSet<string> { "player" }, quantities);
				Start(core);
				Network.Send(core, 7, manifest, 1);
			}
			catch (Exception ex)
			{
				Cancel();
				StorageWindow.Status = ex.Message;
			}
		}

		internal static void Craft(Container core, string recipe)
		{
			if (!Busy && !Network.Pending)
			{
				Start(core);
				Network.Send(core, 5, recipe, 1);
			}
		}

		private static void Start(Container core)
		{
			target = core;
			key = null;
			amount = 0;
			granted = false;
			stage = 1;
			expectedBuffer = null;
			expectedRevision = 0u;
			depositing = false;
			quick = false;
			deadline = Time.unscaledTime + 80f;
		}

		internal static void Completed(bool success, string outputKey, int outputAmount, uint revision = 0u, byte[] buffer = null)
		{
			if (stage == 5)
			{
				if (quick && success && outputAmount > 0)
				{
					deposited += outputAmount;
					stage = 2;
					granted = false;
					deadline = Time.unscaledTime + 80f;
					return;
				}
				bool flag = quick;
				Cancel();
				StorageWindow.RefreshSoon();
				if (!success)
				{
					StorageWindow.Status += " Staged deposit items remain in Core contents; check there before retrying.";
				}
				if (flag && Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, success ? "Storage full. Remaining items are inside the Core (Alt+Use)." : StorageWindow.Status, 0, (Sprite)null, false);
				}
			}
			else if (stage == 1)
			{
				if (!success)
				{
					Cancel();
					return;
				}
				if (string.IsNullOrEmpty(outputKey) || outputAmount <= 0)
				{
					Cancel();
					StorageWindow.Status = "Output remains in Core contents. Use Alt+Use on the Core to collect it.";
					return;
				}
				key = outputKey;
				amount = outputAmount;
				expectedRevision = revision;
				expectedBuffer = buffer;
				stage = 2;
			}
		}

		internal static bool CaptureOpen(Container core)
		{
			if (stage != 3 || (Object)(object)core != (Object)(object)target)
			{
				return false;
			}
			granted = true;
			return true;
		}

		internal static void Cancel()
		{
			stage = 0;
			target = null;
			key = null;
			amount = 0;
			granted = false;
			OpeningTray = false;
			expectedBuffer = null;
			expectedRevision = 0u;
		}

		internal static void Tick()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (!Busy)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			try
			{
				float num = (quick ? Plugin.Radius.Value : 5f);
				string text = " Any staged items remain inside the Core (Alt+Use).";
				if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead())
				{
					throw new InvalidOperationException("Transfer stopped: character unavailable." + text);
				}
				if (!Registry.Valid(target))
				{
					throw new InvalidOperationException("Transfer stopped: Core unavailable." + text);
				}
				if (!quick && !StorageWindow.Visible)
				{
					throw new InvalidOperationException("Transfer stopped: storage menu closed." + text);
				}
				Vector3 val = ((Component)target).transform.position - ((Component)localPlayer).transform.position;
				if (((Vector3)(ref val)).sqrMagnitude > num * num)
				{
					throw new InvalidOperationException("Transfer stopped: you moved out of Core range." + text);
				}
				if (Time.unscaledTime > deadline)
				{
					throw new InvalidOperationException("Transfer timed out while waiting for the server or chest ownership." + text);
				}
				if (stage == 1 || stage == 5 || Network.Pending || Network.IsLeased(target))
				{
					return;
				}
				if (!Registry.Access(target, localPlayer.GetPlayerID()))
				{
					throw new InvalidOperationException("Core access changed. Output remains in Core contents.");
				}
				if (stage == 4)
				{
					if (!(Time.unscaledTime < distributeAt))
					{
						stage = 5;
						Network.Send(target, (byte)(quick ? 6 : 2), key, amount);
					}
					return;
				}
				if (stage == 2)
				{
					stage = 3;
					OpeningTray = true;
					try
					{
						target.Interact((Humanoid)(object)localPlayer, false, false);
					}
					finally
					{
						OpeningTray = false;
					}
					StorageWindow.Status = (depositing ? "Depositing from your inventory..." : "Collecting into your inventory...");
				}
				if (!granted || !((Component)target).GetComponent<ZNetView>().IsOwner())
				{
					return;
				}
				if (target.IsInUse())
				{
					throw new InvalidOperationException("Core is in use. Output remains in Core contents.");
				}
				ZDO zDO = ((Component)target).GetComponent<ZNetView>().GetZDO();
				if (expectedBuffer != null)
				{
					if (zDO.DataRevision < expectedRevision)
					{
						return;
					}
					byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, new byte[0]);
					if (!byteArray.SequenceEqual(expectedBuffer))
					{
						throw new InvalidOperationException("Core contents changed before collection. Check Core contents; no automatic retry.");
					}
				}
				Registry.Load.Invoke(target, null);
				target.SetInUse(true);
				int num2;
				try
				{
					num2 = MovePlayerItems(localPlayer, target, key, amount, depositing);
				}
				finally
				{
					target.SetInUse(false);
				}
				if (quick && num2 == 0)
				{
					int num3 = deposited;
					bool flag = ((Humanoid)localPlayer).GetInventory().GetAllItems().Any((ItemData i) => i.m_gridPos.y != 0 && !i.m_equipped && !SmartCraftIntegration.IsLocked(i));
					Cancel();
					StorageWindow.Status = ((num3 > 0) ? ("Deposited " + num3 + " items") : (flag ? "Storage full" : "Nothing to store"));
					((Character)localPlayer).Message((MessageType)2, StorageWindow.Status, 0, (Sprite)null, false);
				}
				else if (depositing)
				{
					stage = 4;
					distributeAt = Time.unscaledTime + 0.4f;
					StorageWindow.Status = "Sending deposited items to connected storage...";
				}
				else
				{
					Cancel();
					StorageWindow.Status = "Collected " + num2 + " into your inventory.";
					StorageWindow.RefreshSoon();
				}
			}
			catch (Exception ex)
			{
				bool flag2 = quick;
				Cancel();
				StorageWindow.Status = ex.Message;
				if (flag2 && Object.op_Implicit((Object)(object)localPlayer))
				{
					((Character)localPlayer).Message((MessageType)2, ex.Message, 0, (Sprite)null, false);
				}
				Plugin.Error(ex);
			}
		}

		private static int MovePlayerItems(Player player, Container core, string itemKey, int count, bool deposit)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			Inventory inventory2 = core.GetInventory();
			Dictionary<string, ItemData> prototypes = new Dictionary<string, ItemData>();
			StoreState from = Items.Read(inventory2, "buffer", prototypes, Items.Key);
			StoreState storeState = Items.Read(inventory, "player", prototypes, (ItemData item) => ((deposit && (item.m_equipped || SmartCraftIntegration.IsLocked(item) || (quick && item.m_gridPos.y == 0))) ? "protected/" : "") + Items.Key(item));
			MovePlan movePlan;
			try
			{
				movePlan = (quick ? Planner.QuickStack(new StoreState[2] { from, storeState }, "player", new HashSet<string> { "buffer" }, matchingOnly: false, new HashSet<string>(from i in storeState.Items
					where !i.Key.StartsWith("protected/", StringComparison.Ordinal)
					select i.Key)) : ((deposit || itemKey == null || !itemKey.StartsWith("batch:", StringComparison.Ordinal)) ? Planner.Move(new StoreState[2] { from, storeState }, new HashSet<string> { deposit ? "player" : "buffer" }, new HashSet<string> { deposit ? "buffer" : "player" }, itemKey, count) : Planner.BatchMove(new StoreState[2] { from, storeState }, new HashSet<string> { "buffer" }, new HashSet<string> { "player" }, BulkDeposit.Parse(itemKey.Substring(6)))));
			}
			catch (Exception ex)
			{
				throw new InvalidOperationException("Could not transfer: " + ex.Message + " Items remain in their current inventory.");
			}
			if (movePlan.Moved == 0)
			{
				return 0;
			}
			List<ItemData> items = Items.Materialize(movePlan.After.Single((StoreState x) => x.Id == "player"), inventory.GetWidth(), prototypes);
			List<ItemData> items2 = Items.Materialize(movePlan.After.Single((StoreState x) => x.Id == "buffer"), inventory2.GetWidth(), prototypes);
			List<ItemData> list = inventory.GetAllItems().ToList();
			List<ItemData> list2 = inventory2.GetAllItems().ToList();
			Dictionary<ItemData, int> dictionary = list.Concat(list2).ToDictionary((ItemData x) => x, (ItemData x) => x.m_stack);
			try
			{
				Transactions.Replace(inventory, items);
				Transactions.Replace(inventory2, items2);
				Registry.Save.Invoke(core, null);
			}
			catch
			{
				foreach (KeyValuePair<ItemData, int> item in dictionary)
				{
					item.Key.m_stack = item.Value;
				}
				inventory.GetAllItems().Clear();
				inventory.GetAllItems().AddRange(list);
				inventory2.GetAllItems().Clear();
				inventory2.GetAllItems().AddRange(list2);
				Transactions.Notify(inventory);
				Transactions.Notify(inventory2);
				Registry.Save.Invoke(core, null);
				throw;
			}
			try
			{
				Transactions.Notify(inventory);
				Transactions.Notify(inventory2);
			}
			catch (Exception ex2)
			{
				Plugin.Error(ex2);
			}
			if (quick)
			{
				key = string.Join(";", (from i in movePlan.After.Single((StoreState s) => s.Id == "buffer").Items
					group i by i.Key into g
					select new
					{
						Key = g.Key,
						Count = g.Sum((StackState i) => i.Count) - @from.Items.Where((StackState i) => i.Key == g.Key).Sum((StackState i) => i.Count)
					} into g
					where g.Count > 0
					select g.Key + "=" + g.Count).ToArray());
			}
			return movePlan.Moved;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	internal static class AutomaticCollectionPanel
	{
		private static bool Prefix(Container __0)
		{
			return !PlayerTray.CaptureOpen(__0);
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency("com.jotunn.jotunn", "2.30.0")]
	[BepInPlugin("org.yggdrasilstorage.valheim", "YggdrasilStorage", "0.5.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "org.yggdrasilstorage.valheim";

		public const string Version = "0.5.2";

		public const string CorePrefab = "YggdrasilStorage_Core";

		internal static Plugin Instance;

		internal static ConfigEntry<float> Radius;

		internal static ConfigEntry<string> Eligible;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			Instance = this;
			Radius = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "Radius", 20f, new ConfigDescription("Storage and crafting-station radius in metres. Server value is authoritative.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 50f), new object[0]));
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Migration", "Radius20Applied", false, "Internal: old default radius migrated once to 20 m.");
			if (!val.Value)
			{
				if (Mathf.Approximately(Radius.Value, 15f))
				{
					Radius.Value = 20f;
				}
				val.Value = true;
			}
			Eligible = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "EligiblePrefabs", "piece_chest_wood,piece_chest,piece_chest_blackmetal,piece_chest_private", "Exact prefab allowlist. Only placed stationary containers with a creator are eligible.");
			harmony = new Harmony("org.yggdrasilstorage.valheim");
			harmony.PatchAll();
			SmartCraftIntegration.Install(harmony);
			EpicLootIntegration.Install();
			PrefabManager.OnVanillaPrefabsAvailable += RegisterCore;
			((Component)this).gameObject.AddComponent<StorageWindow>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Inspired by Magic Storage for Terraria, created by BlushieMagic. Independent; not affiliated with or endorsed by BlushieMagic.");
		}

		private void RegisterCore()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			PieceConfig val = new PieceConfig();
			val.Name = "Storage Core";
			val.Description = "Yggdrasil storage network and transfer tray";
			val.PieceTable = "Hammer";
			val.Category = "Furniture";
			val.CraftingStation = "piece_workbench";
			val.Requirements = (RequirementConfig[])(object)new RequirementConfig[2]
			{
				new RequirementConfig("Wood", 10, 0, true),
				new RequirementConfig("SurtlingCore", 1, 0, true)
			};
			CustomPiece val2 = new CustomPiece("YggdrasilStorage_Core", "piece_chest_wood", val);
			Container component = val2.PiecePrefab.GetComponent<Container>();
			component.m_name = "Storage Core transfer tray";
			component.m_width = 5;
			component.m_height = 4;
			val2.PiecePrefab.AddComponent<StorageCore>();
			RuneforgedModel.Install(val2.PiecePrefab);
			RuneforgedModel.InstallIcon(val2.PiecePrefab);
			if (!PieceManager.Instance.AddPiece(val2))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not register Storage Core.");
			}
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterCore;
		}

		private void Update()
		{
			Network.Tick();
			PlayerTray.Tick();
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterCore;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Network.Reset();
		}

		internal static void Error(Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
		}

		internal static void Warn(string text)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)text);
		}
	}
	public sealed class StorageCore : MonoBehaviour
	{
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class TrackContainer
	{
		private static void Postfix(Container __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<ContainerTracker>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<ContainerTracker>();
			}
		}
	}
	public sealed class ContainerTracker : MonoBehaviour
	{
		private Container container;

		private void Start()
		{
			container = ((Component)this).GetComponent<Container>();
			Registry.All.Add(container);
		}

		private void OnDestroy()
		{
			if (!object.ReferenceEquals(container, null))
			{
				Registry.All.Remove(container);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	internal static class CoreHover
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			if ((Object)(object)((Component)__instance).GetComponent<StorageCore>() != (Object)null)
			{
				__result = Localization.instance.Localize("Storage Core\n[<color=yellow><b>$KEY_Use</b></color>] Storage & crafting\n[<color=yellow>Alt + $KEY_Use</color>] Core contents");
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Interact")]
	internal static class CoreInteract
	{
		private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result)
		{
			if (!PlayerTray.OpeningTray && PlayerTray.QuickUseFrame == Time.frameCount)
			{
				__result = false;
				return false;
			}
			if (Network.IsLeased(__instance))
			{
				__result = false;
				return false;
			}
			if ((Object)(object)((Component)__instance).GetComponent<StorageCore>() == (Object)null || Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307) || PlayerTray.OpeningTray)
			{
				return true;
			}
			if (alt || Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303) || Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305))
			{
				__result = false;
				return false;
			}
			__result = false;
			if (!hold && (Object)(object)character == (Object)(object)Player.m_localPlayer)
			{
				StorageWindow.Open(__instance);
				__result = true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class CoreQuickDepositInput
	{
		[HarmonyPriority(800)]
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				PlayerTray.TryQuickDeposit();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class BlockMovement
	{
		[HarmonyPriority(0)]
		private static void Postfix(ref bool __result)
		{
			if (StorageWindow.BlockGameInput)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
	internal static class WindowCursor
	{
		private static bool Prefix()
		{
			if (!StorageWindow.BlockGameInput)
			{
				return true;
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerController), "TakeInput")]
	internal static class BlockControllerInput
	{
		[HarmonyPriority(0)]
		private static void Postfix(ref bool __result)
		{
			if (StorageWindow.BlockGameInput)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")]
	internal static class BlockGameScroll
	{
		[HarmonyPriority(0)]
		private static void Postfix(ref float __result)
		{
			if (StorageWindow.BlockGameInput)
			{
				__result = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(ZInput), "GetMouseDelta")]
	internal static class BlockGameMouseLook
	{
		[HarmonyPriority(0)]
		private static void Postfix(ref Vector2 __result)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (StorageWindow.BlockGameInput)
			{
				__result = Vector2.zero;
			}
		}
	}
	[HarmonyPatch(typeof(Menu), "Update")]
	internal static class StorageEscape
	{
		private static bool Prefix()
		{
			if (StorageWindow.Visible && StorageWindow.EscapePressed())
			{
				StorageWindow.Close();
			}
			return !StorageWindow.BlockGameInput;
		}
	}
	internal static class Registry
	{
		internal static readonly HashSet<Container> All = new HashSet<Container>();

		internal static readonly MethodInfo Load = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null);

		internal static readonly MethodInfo Save = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null);

		private static readonly MethodInfo CheckAccess = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null);

		private static readonly MethodInfo IsPermitted = AccessTools.Method(typeof(PrivateArea), "IsPermitted", (Type[])null, (Type[])null);

		private static readonly MethodInfo IsEnabled = AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null);

		private static readonly MethodInfo IsInside = AccessTools.Method(typeof(PrivateArea), "IsInside", (Type[])null, (Type[])null);

		private static readonly FieldInfo Wards = AccessTools.Field(typeof(PrivateArea), "m_allAreas");

		internal static string Id(Container c)
		{
			return ((object)Unsafe.As<ZDOID, ZDOID>(ref ((Component)c).GetComponent<ZNetView>().GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString();
		}

		internal static string Prefab(Container c)
		{
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			GameObject val = ((Object.op_Implicit((Object)(object)component) && component.GetZDO() != null && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.GetPrefab(component.GetZDO().GetPrefab()) : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return "";
			}
			return ((Object)val).name;
		}

		internal static bool Valid(Container c)
		{
			if (!Object.op_Implicit((Object)(object)c) || !((Behaviour)c).isActiveAndEnabled)
			{
				return false;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid())
			{
				return c.GetInventory() != null;
			}
			return false;
		}

		internal static bool Access(Container c, long playerId)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (Valid(c) && playerId != 0 && (bool)CheckAccess.Invoke(c, new object[1] { playerId }) && WardAccess(((Component)c).transform.position, playerId))
			{
				return Compatibility.Allowed(c, playerId);
			}
			return false;
		}

		internal static bool WardAccess(Vector3 point, long playerId)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			List<PrivateArea> list = (List<PrivateArea>)Wards.GetValue(null);
			foreach (PrivateArea item in list)
			{
				if (Object.op_Implicit((Object)(object)item) && (bool)IsEnabled.Invoke(item, null) && (bool)IsInside.Invoke(item, new object[2] { point, 0f }))
				{
					Piece component = ((Component)item).GetComponent<Piece>();
					if ((!Object.op_Implicit((Object)(object)component) || component.GetCreator() != playerId) && !(bool)IsPermitted.Invoke(item, new object[1] { playerId }))
					{
						return false;
					}
				}
			}
			return true;
		}

		internal static List<Container> Discover(Container core, long playerId)
		{
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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)
			HashSet<string> hashSet = new HashSet<string>(from x in Plugin.Eligible.Value.Split(new char[1] { ',' })
				select x.Trim(), StringComparer.Ordinal);
			float value = Plugin.Radius.Value;
			List<Container> list = new List<Container>();
			Container[] array = All.ToArray();
			foreach (Container val in array)
			{
				if (!Valid(val) || (Object)(object)val == (Object)(object)core || Object.op_Implicit((Object)(object)((Component)val).GetComponent<StorageCore>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<Ship>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<Vagon>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<TombStone>()))
				{
					continue;
				}
				Piece component = ((Component)val).GetComponent<Piece>();
				if (!Object.op_Implicit((Object)(object)component) || component.GetCreator() == 0 || !hashSet.Contains(Prefab(val)))
				{
					continue;
				}
				Vector3 val2 = ((Component)val).transform.position - ((Component)core).transform.position;
				if (!(((Vector3)(ref val2)).sqrMagnitude > value * value) && Access(val, playerId))
				{
					list.Add(val);
					if (list.Count > 128)
					{
						throw new InvalidOperationException("More than 128 eligible chests; reduce the radius.");
					}
				}
			}
			return list.OrderBy<Container, string>(Id, StringComparer.Ordinal).ToList();
		}

		internal static Container Find(ZDOID id)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(id) : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			return val.GetComponent<Container>();
		}

		internal static void DemandCore(Container core