Decompiled source of YggdrasilStorage v0.5.4

BepInEx/plugins/YggdrasilStorage/YggdrasilStorage.dll

Decompiled 2 months 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: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("YggdrasilStorage")]
[assembly: AssemblyDescription("Independent Valheim storage and crafting prototype, inspired by BlushieMagic's Terraria Magic Storage.")]
[assembly: AssemblyFileVersion("0.5.4.0")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.5.4.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, string outputStore = null)
		{
			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.Where((StoreState x) => outputStore == null || x.Id != outputStore).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 (item2.Key.StartsWith("protected/", StringComparison.Ordinal) || ((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, outputStore ?? Registry.Id(core), new StackState
			{
				Key = key,
				Count = val.m_amount,
				Limit = val4.m_shared.m_maxStackSize
			}, (outputStore == null) ? null : new HashSet<string>(from x in stores
				where x.Id != outputStore
				select x.Id));
		}
	}
}
namespace YggdrasilStorage.Backend
{
	public sealed class DeliveryReceipts
	{
		private readonly Dictionary<string, Tuple<long, bool>> entries = new Dictionary<string, Tuple<long, bool>>();

		public long LastSequence { get; private set; }

		public bool TryGet(string ticket, long sequence, out bool delivered)
		{
			delivered = false;
			if (!entries.TryGetValue(ticket, out var value))
			{
				return false;
			}
			if (value.Item1 != sequence)
			{
				throw new InvalidOperationException("Delivery ticket was reused for a different request.");
			}
			delivered = value.Item2;
			return true;
		}

		public void Record(string ticket, long sequence, bool delivered)
		{
			if (sequence <= LastSequence || entries.ContainsKey(ticket))
			{
				throw new InvalidOperationException("Delivery already decided.");
			}
			entries.Add(ticket, Tuple.Create(sequence, delivered));
			LastSequence = sequence;
			if (entries.Count > 256)
			{
				string[] array = (from x in entries
					where x.Value.Item1 < sequence - 128
					select x.Key).ToArray();
				foreach (string key in array)
				{
					entries.Remove(key);
				}
			}
		}
	}
}
namespace YggdrasilStorage
{
	internal sealed class PlayerSnapshot
	{
		internal int Width;

		internal int Height;

		internal byte[] Bytes;

		internal Inventory Inventory;

		private string identity;

		internal HashSet<int> Protected = new HashSet<int>();

		private static string Identity(Inventory inv)
		{
			return string.Join(";", (from i in inv.GetAllItems()
				orderby i.m_gridPos.y, i.m_gridPos.x
				select i.m_gridPos.x + "," + i.m_gridPos.y + "=" + Items.Key(i)).ToArray());
		}

		internal static PlayerSnapshot Capture(byte action)
		{
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			PlayerSnapshot playerSnapshot = new PlayerSnapshot();
			playerSnapshot.Width = inventory.GetWidth();
			playerSnapshot.Height = inventory.GetHeight();
			playerSnapshot.Inventory = inventory;
			PlayerSnapshot playerSnapshot2 = playerSnapshot;
			Items.Read(inventory, "player", null, Items.Key);
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_equipped || SmartCraftIntegration.IsLocked(allItem) || (action == 6 && allItem.m_gridPos.y == 0))
				{
					playerSnapshot2.Protected.Add(allItem.m_gridPos.y * playerSnapshot2.Width + allItem.m_gridPos.x);
				}
			}
			playerSnapshot2.identity = Identity(inventory);
			playerSnapshot2.Bytes = Items.Bytes(inventory);
			return playerSnapshot2;
		}

		internal void Write(ZPackage p)
		{
			p.Write(Width);
			p.Write(Height);
			p.Write(Encode(Inventory));
			p.Write(Inventory.GetAllItems().Count);
			foreach (ItemData allItem in Inventory.GetAllItems())
			{
				int num = allItem.m_gridPos.y * Width + allItem.m_gridPos.x;
				p.Write(num);
				p.Write(Items.Key(allItem));
				p.Write(Protected.Contains(num));
			}
		}

		internal static PlayerSnapshot Read(ZPackage p)
		{
			PlayerSnapshot playerSnapshot = new PlayerSnapshot();
			playerSnapshot.Width = p.ReadInt();
			playerSnapshot.Height = p.ReadInt();
			playerSnapshot.Bytes = p.ReadByteArray();
			PlayerSnapshot playerSnapshot2 = playerSnapshot;
			playerSnapshot2.Inventory = Decode(playerSnapshot2.Bytes, playerSnapshot2.Width, playerSnapshot2.Height);
			playerSnapshot2.Bytes = Items.Bytes(playerSnapshot2.Inventory);
			int num = p.ReadInt();
			StoreState storeState = Items.Read(playerSnapshot2.Inventory, "player", null, Items.Key);
			if (num != storeState.Items.Count)
			{
				throw new InvalidOperationException("Inventory serialization changed an item count.");
			}
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < num; i++)
			{
				int slot = p.ReadInt();
				string key = p.ReadString();
				bool flag = p.ReadBool();
				if (!hashSet.Add(slot) || !storeState.Items.Any((StackState stackState) => stackState.Slot == slot && stackState.Key == key))
				{
					throw new InvalidOperationException("An item mod did not preserve its identity during synchronization. No items moved.");
				}
				if (flag)
				{
					playerSnapshot2.Protected.Add(slot);
				}
			}
			return playerSnapshot2;
		}

		internal static byte[] Encode(Inventory inv)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			Items.Read(inv, "snapshot", null, Items.Key);
			ZPackage val = new ZPackage();
			val.Write(Items.Bytes(inv));
			val.Write(inv.GetAllItems().Count);
			foreach (ItemData allItem in inv.GetAllItems())
			{
				val.Write(allItem.m_gridPos.x);
				val.Write(allItem.m_gridPos.y);
				val.Write(allItem.m_durability);
				val.Write(allItem.m_crafterName ?? "");
				val.Write(Items.Key(allItem));
			}
			byte[] array = val.GetArray();
			if (array.Length > 786432)
			{
				throw new InvalidOperationException("Inventory snapshot is too large.");
			}
			return array;
		}

		internal static Inventory Decode(byte[] payload, int width, int height)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			if (width < 1 || height < 1 || width > 128 || height > 128 || width * height > 4096 || payload.Length > 786432)
			{
				throw new InvalidOperationException("Invalid inventory snapshot size.");
			}
			ZPackage val = new ZPackage(payload);
			byte[] array = val.ReadByteArray();
			if (array.Length > 262144)
			{
				throw new InvalidOperationException("Inventory save is too large.");
			}
			Inventory val2 = new Inventory("Yggdrasil transfer", (Sprite)null, width, height);
			val2.Load(new ZPackage(array));
			int num = val.ReadInt();
			if (num != val2.GetAllItems().Count)
			{
				throw new InvalidOperationException("Inventory synchronization changed an item count.");
			}
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < num; i++)
			{
				int x = val.ReadInt();
				int y = val.ReadInt();
				float num2 = val.ReadSingle();
				string crafterName = val.ReadString();
				string text = val.ReadString();
				if (x < 0 || y < 0 || x >= width || y >= height || !hashSet.Add(y * width + x) || float.IsNaN(num2) || float.IsInfinity(num2))
				{
					throw new InvalidOperationException("Invalid item snapshot.");
				}
				ItemData val3 = ((IEnumerable<ItemData>)val2.GetAllItems()).SingleOrDefault((Func<ItemData, bool>)((ItemData val4) => val4.m_gridPos.x == x && val4.m_gridPos.y == y));
				if (val3 == null)
				{
					throw new InvalidOperationException("Item position changed during synchronization.");
				}
				val3.m_durability = num2;
				val3.m_crafterName = crafterName;
				if (Items.Key(val3) != text)
				{
					throw new InvalidOperationException("Item metadata changed during synchronization: " + ((Object)val3.m_dropPrefab).name + ". No items moved.");
				}
			}
			Items.Read(val2, "snapshot", null, Items.Key);
			if (!Items.Bytes(val2).SequenceEqual(array))
			{
				throw new InvalidOperationException("Item save data changed during synchronization. No transfer performed.");
			}
			return val2;
		}

		internal bool Matches(byte action)
		{
			PlayerSnapshot playerSnapshot = Capture(action);
			if (identity == playerSnapshot.identity && Width == playerSnapshot.Width && Height == playerSnapshot.Height && Bytes.SequenceEqual(playerSnapshot.Bytes))
			{
				return Protected.SetEquals(playerSnapshot.Protected);
			}
			return false;
		}
	}
	internal static class DirectTransfers
	{
		internal sealed class Plan
		{
			internal List<Container> Chests;

			internal Player Player;

			internal PlayerSnapshot Snapshot;

			internal Dictionary<string, byte[]> Before;

			internal Dictionary<string, byte[]> After;

			internal Dictionary<string, List<ItemData>> Items;

			internal Dictionary<string, List<ItemData>> Originals;

			internal Dictionary<ItemData, int> Counts;

			internal byte[] PlayerAfter;

			internal byte[] PlayerDelivery;

			internal int Moved;

			internal string Journal;

			internal bool Committed;

			internal bool Resolved;
		}

		internal static Plan Prepare(List<Container> chests, Container core, Player player, PlayerSnapshot snapshot, byte action, string key, int amount)
		{
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0444: Expected O, but got Unknown
			Transactions.DemandWritable();
			foreach (Container chest in chests)
			{
				Transactions.Demand(chest, player.GetPlayerID());
			}
			Dictionary<string, ItemData> prototypes = new Dictionary<string, ItemData>();
			List<StoreState> list = chests.Select((Container c) => Items.Read(c, prototypes)).ToList();
			HashSet<string> hashSet = new HashSet<string>(list.Select((StoreState s) => s.Id));
			StoreState storeState = Items.Read(snapshot.Inventory, "player", prototypes, (ItemData i) => (((action == 2 || action == 6) && snapshot.Protected.Contains(i.m_gridPos.y * snapshot.Width + i.m_gridPos.x)) ? "protected/" : "") + Items.Key(i));
			list.Add(storeState);
			MovePlan movePlan;
			if (action == 6)
			{
				movePlan = Planner.QuickStack(list, "player", hashSet, matchingOnly: false, new HashSet<string>(from i in storeState.Items
					where !i.Key.StartsWith("protected/", StringComparison.Ordinal)
					select i.Key));
			}
			else if (action == 7)
			{
				Dictionary<string, int> dictionary = BulkDeposit.Parse(key);
				foreach (ItemData item in chests.SelectMany((Container c) => c.GetInventory().GetAllItems()))
				{
					if (dictionary.ContainsKey(Items.Key(item)) && !Items.MaterialEligible(item))
					{
						throw new InvalidOperationException("A required material is locked or special. Refresh and retry.");
					}
				}
				movePlan = Planner.BatchMove(list, hashSet, new HashSet<string> { "player" }, dictionary);
			}
			else if (action == 5)
			{
				movePlan = Crafting.Plan(list, prototypes, core, player, key, "player");
			}
			else
			{
				if (action != 1 && action != 2)
				{
					throw new InvalidOperationException("Unsupported direct transfer action.");
				}
				movePlan = Planner.Move(list, (action == 2) ? new HashSet<string> { "player" } : hashSet, (action == 2) ? hashSet : new HashSet<string> { "player" }, key, amount);
			}
			Plan plan = new Plan
			{
				Chests = chests,
				Player = player,
				Snapshot = snapshot,
				Moved = movePlan.Moved,
				Before = chests.ToDictionary(Registry.Id, (Container c) => Items.Bytes(c.GetInventory())),
				After = new Dictionary<string, byte[]>(),
				Items = new Dictionary<string, List<ItemData>>()
			};
			foreach (StoreState state in movePlan.After)
			{
				Inventory val = ((state.Id == "player") ? snapshot.Inventory : chests.Single((Container c) => Registry.Id(c) == state.Id).GetInventory());
				List<ItemData> source = Items.Materialize(state, val.GetWidth(), prototypes);
				Dictionary<Vector2i, ItemData> originals = val.GetAllItems().ToDictionary((ItemData i) => i.m_gridPos);
				source = ((IEnumerable<ItemData>)source).Select((Func<ItemData, ItemData>)delegate(ItemData i)
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					if (!originals.TryGetValue(i.m_gridPos, out var value) || Items.Key(value) != Items.Key(i))
					{
						return i;
					}
					ItemData val3 = Items.Clone(value);
					val3.m_stack = i.m_stack;
					return val3;
				}).ToList();
				plan.Items.Add(state.Id, source);
				Inventory val2 = new Inventory("Yggdrasil plan", (Sprite)null, val.GetWidth(), val.GetHeight());
				val2.GetAllItems().AddRange(source);
				byte[] array = Items.Bytes(val2);
				if (array.Length > 262144)
				{
					throw new InvalidOperationException("Inventory too large for a safe transfer.");
				}
				if (state.Id == "player")
				{
					plan.PlayerAfter = array;
					plan.PlayerDelivery = PlayerSnapshot.Encode(val2);
				}
				else
				{
					plan.After.Add(state.Id, array);
				}
			}
			plan.Chests = chests.Where((Container c) => !plan.Before[Registry.Id(c)].SequenceEqual(plan.After[Registry.Id(c)])).ToList();
			return plan;
		}

		internal static void Commit(Plan plan, bool local, string ticket)
		{
			Transactions.DemandWritable();
			foreach (Container chest in plan.Chests)
			{
				Transactions.Demand(chest, plan.Player.GetPlayerID());
				if (!Items.Bytes(chest.GetInventory()).SequenceEqual(plan.Before[Registry.Id(chest)]))
				{
					throw new InvalidOperationException("Chest changed; no transfer performed.");
				}
			}
			if (local && !Items.Bytes(((Humanoid)plan.Player).GetInventory()).SequenceEqual(plan.Snapshot.Bytes))
			{
				throw new InvalidOperationException("Your inventory changed. Retry the transfer.");
			}
			plan.Originals = plan.Chests.ToDictionary(Registry.Id, (Container c) => c.GetInventory().GetAllItems().ToList());
			if (local)
			{
				plan.Originals.Add("player", ((Humanoid)plan.Player).GetInventory().GetAllItems().ToList());
			}
			plan.Counts = plan.Originals.Values.SelectMany((List<ItemData> i) => i).ToDictionary((ItemData i) => i, (ItemData i) => i.m_stack);
			plan.Journal = WriteJournal(ticket, plan.Player.GetPlayerID(), plan.Snapshot.Bytes, plan.PlayerAfter, plan.Chests, plan.Before, plan.After);
			Transactions.Writing = true;
			try
			{
				foreach (Container chest2 in plan.Chests)
				{
					Transactions.Replace(chest2.GetInventory(), plan.Items[Registry.Id(chest2)]);
				}
				if (local)
				{
					Transactions.Replace(((Humanoid)plan.Player).GetInventory(), plan.Items["player"]);
				}
				foreach (Container chest3 in plan.Chests)
				{
					if (!Items.Bytes(chest3.GetInventory()).SequenceEqual(plan.After[Registry.Id(chest3)]))
					{
						throw new InvalidOperationException("A mod changed a chest during transfer.");
					}
					Registry.Save.Invoke(chest3, null);
				}
				if (local && !Items.Bytes(((Humanoid)plan.Player).GetInventory()).SequenceEqual(plan.PlayerAfter))
				{
					throw new InvalidOperationException("A mod changed the player inventory during transfer.");
				}
				plan.Committed = true;
				if (local)
				{
					Resolve(plan, delivered: true);
				}
			}
			catch
			{
				if (!plan.Resolved)
				{
					Restore(plan, local, requireAfter: false);
				}
				throw;
			}
			finally
			{
				Transactions.Writing = false;
			}
			if (local)
			{
				NotifyPlayer(plan.Player);
			}
		}

		internal static void Resolve(Plan plan, bool delivered)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (plan.Resolved)
			{
				return;
			}
			if (!delivered)
			{
				Restore(plan, local: false, requireAfter: true);
				return;
			}
			try
			{
				File.Move(plan.Journal, Path.ChangeExtension(plan.Journal, ".committed"));
				plan.Resolved = true;
			}
			catch
			{
				Transactions.Quarantine(plan.Chests);
				throw;
			}
			foreach (Container chest in plan.Chests)
			{
				try
				{
					Transactions.Notify(chest.GetInventory());
					ZDOMan.instance.ForceSendZDO(((Component)chest).GetComponent<ZNetView>().GetZDO().m_uid);
				}
				catch (Exception ex)
				{
					Plugin.Error(ex);
				}
			}
		}

		private static void Restore(Plan plan, bool local, bool requireAfter)
		{
			try
			{
				foreach (Container chest in plan.Chests)
				{
					if (!Registry.Valid(chest) || !((Component)chest).GetComponent<ZNetView>().IsOwner())
					{
						throw new InvalidOperationException("Cannot restore a chest whose ownership changed.");
					}
					if (requireAfter && !Items.Bytes(chest.GetInventory()).SequenceEqual(plan.After[Registry.Id(chest)]))
					{
						throw new InvalidOperationException("Chest changed before rollback.");
					}
				}
				foreach (KeyValuePair<ItemData, int> count in plan.Counts)
				{
					count.Key.m_stack = count.Value;
				}
				foreach (Container chest2 in plan.Chests)
				{
					RestoreInventory(chest2.GetInventory(), plan.Originals[Registry.Id(chest2)]);
					Registry.Save.Invoke(chest2, null);
				}
				if (local)
				{
					RestoreInventory(((Humanoid)plan.Player).GetInventory(), plan.Originals["player"]);
				}
				File.Move(plan.Journal, Path.ChangeExtension(plan.Journal, ".rolledback"));
				plan.Resolved = true;
			}
			catch
			{
				Transactions.Quarantine(plan.Chests);
				throw;
			}
		}

		internal static void Uncertain(Plan plan)
		{
			if (plan != null && plan.Committed && !plan.Resolved)
			{
				Transactions.Quarantine(plan.Chests);
			}
		}

		internal static bool ApplyPlayer(PlayerSnapshot original, byte[] delivery, string ticket)
		{
			Transactions.DemandWritable();
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			Inventory val = PlayerSnapshot.Decode(delivery, original.Width, original.Height);
			byte[] array = Items.Bytes(val);
			List<ItemData> list = inventory.GetAllItems().ToList();
			Dictionary<ItemData, int> dictionary = list.ToDictionary((ItemData i) => i, (ItemData i) => i.m_stack);
			string text = WriteJournal("client-" + ticket, Player.m_localPlayer.GetPlayerID(), original.Bytes, array, new List<Container>(), new Dictionary<string, byte[]>(), new Dictionary<string, byte[]>());
			Transactions.Writing = true;
			try
			{
				Transactions.Replace(inventory, val.GetAllItems().ToList());
				if (!Items.Bytes(inventory).SequenceEqual(array))
				{
					throw new InvalidOperationException("A mod changed the player inventory during delivery.");
				}
				File.Move(text, Path.ChangeExtension(text, ".committed"));
			}
			catch
			{
				try
				{
					foreach (KeyValuePair<ItemData, int> item in dictionary)
					{
						item.Key.m_stack = item.Value;
					}
					RestoreInventory(inventory, list);
					File.Move(text, Path.ChangeExtension(text, ".rolledback"));
				}
				catch
				{
					Transactions.Quarantine((IEnumerable<Container>)(object)new Container[0]);
					throw;
				}
				return false;
			}
			finally
			{
				Transactions.Writing = false;
			}
			NotifyPlayer(Player.m_localPlayer);
			return true;
		}

		private static void RestoreInventory(Inventory inv, List<ItemData> items)
		{
			inv.GetAllItems().Clear();
			inv.GetAllItems().AddRange(items);
			Transactions.Notify(inv);
		}

		private static void NotifyPlayer(Player player)
		{
			try
			{
				Transactions.Notify(((Humanoid)player).GetInventory());
			}
			catch (Exception ex)
			{
				Plugin.Error(ex);
			}
		}

		private static string WriteJournal(string ticket, long player, byte[] beforePlayer, byte[] afterPlayer, IEnumerable<Container> chests, Dictionary<string, byte[]> before, Dictionary<string, byte[]> after)
		{
			string text = Path.Combine(Transactions.DirectJournalDirectory, ticket + ".pending");
			try
			{
				using FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.Read);
				using (StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.UTF8, 4096, leaveOpen: true))
				{
					streamWriter.WriteLine("YggdrasilStorage direct transfer; UTC=" + DateTime.UtcNow.ToString("o") + "; player=" + player);
					streamWriter.WriteLine("player-before=" + Convert.ToBase64String(beforePlayer));
					streamWriter.WriteLine("player-after=" + Convert.ToBase64String(afterPlayer));
					foreach (Container chest in chests)
					{
						string text2 = Registry.Id(chest);
						streamWriter.WriteLine("container=" + text2);
						streamWriter.WriteLine("before=" + Convert.ToBase64String(before[text2]));
						streamWriter.WriteLine("after=" + Convert.ToBase64String(after[text2]));
					}
					streamWriter.Flush();
				}
				fileStream.Flush(flushToDisk: true);
				return text;
			}
			catch
			{
				Transactions.Quarantine(chests);
				throw;
			}
		}
	}
	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 PlayerSnapshot Snapshot;

			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 PlayerSnapshot Snapshot;

			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 DirectTransfers.Plan Plan;

			internal bool AwaitingPlayer;

			internal double Deadline;
		}

		private const string Hello = "Yggdrasil.HelloDirect2";

		private const string Welcome = "Yggdrasil.WelcomeDirect2";

		private const string Request = "Yggdrasil.RequestDirect2";

		private const string Reply = "Yggdrasil.ReplyDirect2";

		private const string Acquire = "Yggdrasil.Acquire2";

		private const string Granted = "Yggdrasil.Granted2";

		private const string Release = "Yggdrasil.ReleaseDirect2";

		private const string Freeze = "Yggdrasil.FreezeDirect2";

		private const string Frozen = "Yggdrasil.FrozenDirect2";

		private const string ApplyPlayer = "Yggdrasil.ApplyPlayer2";

		private const string AppliedPlayer = "Yggdrasil.AppliedPlayer2";

		private const string KeepArea = "Yggdrasil.KeepArea3";

		private static PlayerSnapshot clientSnapshot;

		private static byte clientAction;

		private static DeliveryReceipts deliveries = new DeliveryReceipts();

		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);
				}
				Transactions.CheckJournal();
				if (world.IsServer())
				{
					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.HelloDirect2", 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 = "Transfer response timed out. Do not repeat an uncertain transfer; check the host log.";
					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.ApplyPlayer2", (Action<ZRpc, ZPackage>)OnApplyPlayer);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.AppliedPlayer2", (Action<ZRpc, ZPackage>)OnAppliedPlayer);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.HelloDirect2", (Action<ZRpc, ZPackage>)OnHello);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.KeepArea3", (Action<ZRpc, ZPackage>)OnKeepArea);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.WelcomeDirect2", (Action<ZRpc, ZPackage>)OnWelcome);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.RequestDirect2", (Action<ZRpc, ZPackage>)OnRequest);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.ReplyDirect2", (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.ReleaseDirect2", (Action<ZRpc, ZPackage>)OnRelease);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.FreezeDirect2", (Action<ZRpc, ZPackage>)OnFreeze);
			peer.m_rpc.Register<ZPackage>("Yggdrasil.FrozenDirect2", (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.WelcomeDirect2", 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, value.Snapshot);
					}
				}
				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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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. All players and the host need the same mod version.";
				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);
			clientAction = action;
			clientSnapshot = ((action == 0) ? null : PlayerSnapshot.Capture(action));
			if (clientSnapshot != null)
			{
				clientSnapshot.Write(val);
			}
			waiting = sequence;
			clientDeadline = Now + 80.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.RequestDirect2", 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_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			long num = 0L;
			try
			{
				if (pkg.Size() > 1048576)
				{
					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 != 0 && b != 1 && b != 2 && b != 5 && b != 6 && 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 == 7)
				{
					BulkDeposit.Parse(text);
				}
				PlayerSnapshot snapshot = ((b == 0) ? null : PlayerSnapshot.Read(pkg));
				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,
							Snapshot = snapshot,
							Deadline = Now + 50.0
						};
						return;
					}
				}
				Process(who, num, Registry.Find(val), b, text, num2, snapshot);
			}
			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, PlayerSnapshot snapshot)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Expected O, but got Unknown
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: 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);
			if (list.Count == 0)
			{
				throw new InvalidOperationException("No accessible chests are connected.");
			}
			if (list.Any((Container c) => c.IsInUse() || Transactions.Blocked(c)))
			{
				throw new InvalidOperationException("Close nearby chests 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.Snapshot = snapshot;
			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 + 30.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.FreezeDirect2", 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 + 30.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.FrozenDirect2", 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 + 30.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_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Expected O, but got Unknown
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			//IL_053d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0544: Expected O, but got Unknown
			if (operation == null)
			{
				return;
			}
			Operation op = operation;
			try
			{
				if (op.AwaitingPlayer)
				{
					if (Now > op.Deadline || !world.GetConnectedPeers().Contains(op.Who.Peer))
					{
						throw new InvalidOperationException("Player delivery was not acknowledged. Storage is paused for recovery; do not repeat the transfer.");
					}
					return;
				}
				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> source = Registry.Discover(op.Core, playerID);
				if (!new HashSet<string>(source.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.");
					}
				}
				if (op.Who.Peer == null && !clientSnapshot.Matches(op.Action))
				{
					throw new InvalidOperationException("Your inventory changed. Retry the transfer.");
				}
				op.Plan = DirectTransfers.Prepare(op.Containers, op.Core, val, op.Snapshot, op.Action, op.Key, op.Amount);
				if (op.Plan.Moved == 0)
				{
					CompleteDirect(op);
					return;
				}
				DirectTransfers.Commit(op.Plan, op.Who.Peer == null, op.Ticket);
				if (op.Who.Peer == null)
				{
					CompleteDirect(op);
					return;
				}
				op.AwaitingPlayer = true;
				op.Deadline = Now + 15.0;
				ZPackage val3 = new ZPackage();
				val3.Write(op.Ticket);
				val3.Write(op.Sequence);
				val3.Write(op.Plan.PlayerDelivery);
				op.Who.Peer.m_rpc.Invoke("Yggdrasil.ApplyPlayer2", new object[1] { val3 });
			}
			catch (Exception ex)
			{
				Finish();
				Error(op.Who, op.Sequence, ex.Message);
				Plugin.Error(ex);
			}
		}

		private static void CompleteDirect(Operation op)
		{
			int moved = op.Plan.Moved;
			string text = ((op.Action != 6) ? (((op.Action == 2) ? "Deposited " : ((op.Action == 5) ? "Crafted " : "Collected ")) + moved + " items") : ((moved == 0) ? "No room in connected chests" : ("Deposited " + moved + " items")));
			Finish();
			try
			{
				Respond(op.Who, op.Sequence, op.Core, op.Plan.Player.GetPlayerID(), text, "", moved);
			}
			catch (Exception ex)
			{
				Error(op.Who, op.Sequence, text + ". Refresh to reload the list.");
				Plugin.Error(ex);
			}
		}

		private static void OnApplyPlayer(ZRpc rpc, ZPackage pkg)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			if (!FromServer(rpc) || pkg.Size() > 787000)
			{
				return;
			}
			try
			{
				string text = pkg.ReadString();
				long num = pkg.ReadLong();
				byte[] delivery = pkg.ReadByteArray();
				if (text.Length != 32)
				{
					return;
				}
				if (!deliveries.TryGet(text, num, out var delivered))
				{
					if (num <= deliveries.LastSequence)
					{
						return;
					}
					delivered = false;
					if (num == waiting && clientSnapshot != null && PlayerTray.Busy && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead() && clientSnapshot.Matches(clientAction))
					{
						delivered = DirectTransfers.ApplyPlayer(clientSnapshot, delivery, text);
					}
					deliveries.Record(text, num, delivered);
				}
				ZPackage val = new ZPackage();
				val.Write(text);
				val.Write(num);
				val.Write(delivered);
				rpc.Invoke("Yggdrasil.AppliedPlayer2", new object[1] { val });
			}
			catch (Exception ex)
			{
				StorageWindow.Status = "Delivery interrupted; storage is paused for recovery.";
				Plugin.Error(ex);
			}
		}

		private static void OnAppliedPlayer(ZRpc rpc, ZPackage pkg)
		{
			Operation operation = Network.operation;
			if (!Object.op_Implicit((Object)(object)world) || !world.IsServer() || operation == null || !operation.AwaitingPlayer || operation.Who.Peer == null || operation.Who.Peer.m_rpc != rpc || pkg.Size() > 128)
			{
				return;
			}
			try
			{
				if (!(pkg.ReadString() != operation.Ticket) && pkg.ReadLong() == operation.Sequence)
				{
					bool flag = pkg.ReadBool();
					DirectTransfers.Resolve(operation.Plan, flag);
					if (flag)
					{
						CompleteDirect(operation);
						return;
					}
					Finish();
					Error(operation.Who, operation.Sequence, "Your inventory changed before delivery. Nothing transferred; retry.");
				}
			}
			catch (Exception ex)
			{
				Finish();
				Error(operation.Who, operation.Sequence, "Transfer needs recovery. Do not repeat it.");
				Plugin.Error(ex);
			}
		}

		private static void Finish()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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_0129: Unknown result type (might be due to invalid IL or missing references)
			Operation op = operation;
			operation = null;
			if (op == null)
			{
				return;
			}
			DirectTransfers.Uncertain(op.Plan);
			bool flag = op.Containers.Any(Transactions.Blocked);
			foreach (ZRpc participant in op.Participants)
			{
				try
				{
					ZPackage val = new ZPackage();
					val.Write(op.Ticket);
					val.Write(flag);
					participant.Invoke("Yggdrasil.ReleaseDirect2", 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)
			{
				if (flag)
				{
					leases[key].Until = double.PositiveInfinity;
				}
				else
				{
					leases.Remove(key);
				}
			}
			SmartCraftIntegration.Invalidate();
		}

		private static void OnRelease(ZRpc rpc, ZPackage pkg)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if (!FromServer(rpc) || pkg.Size() > 128)
			{
				return;
			}
			try
			{
				string ticket = pkg.ReadString();
				bool flag = pkg.ReadBool();
				ZDOID[] array = (from x in leases
					where x.Value.Ticket == ticket
					select x.Key).ToArray();
				foreach (ZDOID key in array)
				{
					if (flag)
					{
						leases[key].Until = double.PositiveInfinity;
					}
					else
					{
						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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)world) && world.IsServer() && Transactions.Blocked(Registry.Find(zdo.m_uid)))
			{
				return owner == ZNet.GetUID();
			}
			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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			List<Container> list = Registry.Discover(core, pid);
			List<ItemRow> list2 = Items.Rows(list, tray: false);
			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);
			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);
			val.Write(0u);
			val.Write(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.ReplyDirect2", 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 || waiting == 0 || 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)
				{
					throw new InvalidOperationException("Invalid item count.");
				}
				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 the host log before repeating a transfer.";
				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;
			clientSnapshot = null;
			deliveries = new DeliveryReceipts();
			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 Container target;

		private static bool quick;

		internal static int QuickUseFrame = -1;

		internal static Container ActiveCore => target;

		internal static bool Busy => (Object)(object)target != (Object)null;

		internal static bool BlocksMovement => 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);
				}
				else
				{
					Start(val, 6, "", 0, isQuick: true);
					QuickUseFrame = Time.frameCount;
				}
			}
		}

		internal static void Deposit(Container core, ItemRow row, int count)
		{
			Start(core, 2, row.Key, count, isQuick: false);
		}

		internal static void Withdraw(Container core, ItemRow row, int count)
		{
			Start(core, 1, row.Key, count, isQuick: false);
		}

		internal static void CollectMaterials(Container core, Dictionary<string, int> quantities, List<ItemRow> rows)
		{
			string text = 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(text);
			Start(core, 7, text, 1, isQuick: false);
		}

		internal static void Craft(Container core, string recipe)
		{
			Start(core, 5, recipe, 1, isQuick: false);
		}

		private static void Start(Container core, byte action, string key, int amount, bool isQuick)
		{
			if (Busy || Network.Pending || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !SmartCraftIntegration.Ready || !EpicLootIntegration.Ready)
			{
				return;
			}
			target = core;
			quick = isQuick;
			try
			{
				Network.Send(core, action, key, amount);
				if (!Network.Pending)
				{
					Cancel();
				}
			}
			catch (Exception ex)
			{
				Cancel();
				StorageWindow.Status = ex.Message;
				Plugin.Error(ex);
			}
		}

		internal static void Completed(bool success, string outputKey, int outputAmount, uint revision = 0u, byte[] buffer = null)
		{
			if (Busy)
			{
				bool flag = quick;
				Cancel();
				if (flag && Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, StorageWindow.Status, 0, (Sprite)null, false);
				}
			}
		}

		internal static void Cancel()
		{
			target = null;
			quick = false;
			OpeningTray = false;
		}

		internal static void Tick()
		{
			if (Busy && !Network.Pending)
			{
				Cancel();
			}
		}
	}
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[BepInDependency("com.jotunn.jotunn", "2.30.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("org.yggdrasilstorage.valheim", "YggdrasilStorage", "0.5.4")]
	[BepInDependency(/*Could not decode attribute arguments