Decompiled source of VikingCraftingAndQuickDeposit v0.0.4

BepInEx/plugins/VikingCraftingAndQuickDeposit/VikingCraftingAndQuickDeposit.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
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 Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace VikingCraftingQuickDeposit;

public static class StorageDefaults
{
	public const float DepositRadiusMetres = 250f;

	public const double DepositCooldownSeconds = 3.0;

	public const float MaxSerializableDurability = 21474830f;
}
public enum DepositAdmission
{
	Accepted,
	Cooldown,
	Pending
}
public sealed class DepositGate
{
	private sealed class Entry
	{
		public string Operation;

		public double NextAllowed;
	}

	private readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>(StringComparer.Ordinal);

	public DepositAdmission TryBegin(string player, string operation, double monotonicSeconds)
	{
		if (string.IsNullOrEmpty(player) || string.IsNullOrEmpty(operation) || double.IsNaN(monotonicSeconds) || double.IsInfinity(monotonicSeconds) || monotonicSeconds < 0.0)
		{
			throw new ArgumentException("Invalid deposit admission");
		}
		if (entries.TryGetValue(player, out var value))
		{
			if (value.Operation != null)
			{
				return DepositAdmission.Pending;
			}
			if (monotonicSeconds < value.NextAllowed)
			{
				return DepositAdmission.Cooldown;
			}
		}
		else
		{
			value = new Entry();
			entries.Add(player, value);
		}
		value.Operation = operation;
		value.NextAllowed = monotonicSeconds + 3.0;
		return DepositAdmission.Accepted;
	}

	public bool Complete(string player, string operation)
	{
		if (player == null || string.IsNullOrEmpty(operation) || !entries.TryGetValue(player, out var value) || value.Operation != operation)
		{
			return false;
		}
		value.Operation = null;
		return true;
	}

	public void PruneCompleted(double monotonicSeconds)
	{
		if (double.IsNaN(monotonicSeconds) || double.IsInfinity(monotonicSeconds) || monotonicSeconds < 0.0)
		{
			throw new ArgumentException();
		}
		foreach (string item in (from p in entries
			where p.Value.Operation == null && p.Value.NextAllowed <= monotonicSeconds
			select p.Key).ToList())
		{
			entries.Remove(item);
		}
	}
}
public enum MagicState
{
	Normal,
	Enchanted,
	Unknown
}
public sealed class ItemRecord
{
	private readonly byte[] payload;

	public readonly string Prefab;

	public readonly string Identity;

	public readonly int MaxStack;

	public readonly int Quality;

	public readonly bool Equipment;

	public readonly bool HasCustomData;

	public readonly bool QuestItem;

	public readonly float? ExactDurability;

	public readonly MagicState Magic;

	public ItemRecord(string prefab, string identity, int maxStack, byte[] nativePayload, bool equipment, bool hasCustomData, MagicState magic, bool questItem = false, float? exactDurability = null, int quality = 1)
	{
		if (string.IsNullOrEmpty(prefab) || string.IsNullOrEmpty(identity) || maxStack < 1 || nativePayload == null || nativePayload.Length == 0)
		{
			throw new ArgumentException("Invalid item snapshot");
		}
		if (quality < 1 || quality > 65535)
		{
			throw new ArgumentException("Invalid quality");
		}
		Prefab = prefab;
		Identity = identity;
		MaxStack = maxStack;
		Quality = quality;
		payload = (byte[])nativePayload.Clone();
		if (exactDurability.HasValue && (float.IsNaN(exactDurability.Value) || float.IsInfinity(exactDurability.Value) || exactDurability.Value < 0f || exactDurability.Value > 21474830f))
		{
			throw new ArgumentException("Invalid durability");
		}
		ExactDurability = exactDurability;
		Equipment = equipment;
		HasCustomData = hasCustomData;
		Magic = magic;
		QuestItem = questItem;
	}

	public byte[] Payload()
	{
		return (byte[])payload.Clone();
	}

	public bool StacksWith(ItemRecord other)
	{
		return other != null && MaxStack > 1 && MaxStack == other.MaxStack && Prefab == other.Prefab && Identity == other.Identity;
	}
}
public sealed class StackRecord
{
	public readonly ItemRecord Item;

	public readonly int Slot;

	public readonly int Count;

	public readonly int Reserve;

	public readonly bool Equipped;

	public readonly bool Locked;

	public readonly bool Hotbar;

	public int Available => Math.Max(0, Count - Reserve);

	public StackRecord(ItemRecord item, int slot, int count, int reserve = 0, bool equipped = false, bool locked = false, bool hotbar = false)
	{
		if (item == null || slot < 0 || count < 1 || count > item.MaxStack || reserve < 0)
		{
			throw new ArgumentException("Invalid stack");
		}
		Item = item;
		Slot = slot;
		Count = count;
		Reserve = reserve;
		Equipped = equipped;
		Locked = locked;
		Hotbar = hotbar;
	}
}
public sealed class InventoryRecord
{
	public readonly string Id;

	public readonly long Revision;

	public readonly int Capacity;

	public readonly bool QuickDepositEnabled;

	public readonly bool CraftEnabled;

	public readonly bool BuildEnabled;

	public readonly int Priority;

	public readonly ReadOnlyCollection<int> ReservedSlots;

	public readonly ReadOnlyCollection<StackRecord> Stacks;

	public InventoryRecord(string id, long revision, int capacity, IEnumerable<StackRecord> stacks, bool quickDepositEnabled = true, bool craftEnabled = true, bool buildEnabled = true, int priority = 0, IEnumerable<int> reservedSlots = null)
	{
		if (string.IsNullOrEmpty(id) || revision < 0 || capacity < 1 || stacks == null)
		{
			throw new ArgumentException("Invalid inventory");
		}
		List<StackRecord> list = stacks.ToList();
		if (list.Any((StackRecord x) => x == null || x.Slot >= capacity) || list.Select((StackRecord x) => x.Slot).Distinct().Count() != list.Count)
		{
			throw new ArgumentException("Invalid inventory slots");
		}
		List<int> list2 = (from s in (reservedSlots ?? Enumerable.Empty<int>()).Distinct()
			orderby s
			select s).ToList();
		if (priority < -10 || priority > 10 || list2.Any((int s) => s < 0 || s >= capacity))
		{
			throw new ArgumentException("Invalid inventory rules");
		}
		Id = id;
		Revision = revision;
		Capacity = capacity;
		Stacks = list.AsReadOnly();
		QuickDepositEnabled = quickDepositEnabled;
		CraftEnabled = craftEnabled;
		BuildEnabled = buildEnabled;
		Priority = priority;
		ReservedSlots = list2.AsReadOnly();
	}
}
public sealed class DepositPolicy
{
	public bool ProtectHotbar = true;

	public bool AllowEquipment = false;

	public bool AllowEnchanted = false;

	public bool MatchExistingPrefab = true;

	public readonly HashSet<string> Blacklist = new HashSet<string>(StringComparer.Ordinal);

	public readonly Dictionary<string, int> KeepCounts = new Dictionary<string, int>(StringComparer.Ordinal);

	public string Denial(StackRecord stack)
	{
		if (stack.Item.QuestItem)
		{
			return "quest item";
		}
		if (stack.Equipped)
		{
			return "equipped";
		}
		if (stack.Locked)
		{
			return "locked";
		}
		if (ProtectHotbar && stack.Hotbar)
		{
			return "hotbar";
		}
		if (Blacklist.Contains(stack.Item.Prefab))
		{
			return "blacklisted";
		}
		if (stack.Item.Magic == MagicState.Unknown)
		{
			return "unrecognized enchantment state";
		}
		if (stack.Item.Magic == MagicState.Enchanted && !AllowEnchanted)
		{
			return "enchanted item protected";
		}
		if (stack.Item.Equipment && !AllowEquipment)
		{
			return "equipment protected";
		}
		if (stack.Available == 0)
		{
			return "carried reserve";
		}
		return null;
	}
}
public sealed class TransferLine
{
	public readonly string Source;

	public readonly string Destination;

	public readonly long SourceRevision;

	public readonly long DestinationRevision;

	public readonly int SourceSlot;

	public readonly int DestinationSlot;

	public readonly int Count;

	public readonly ItemRecord Item;

	public TransferLine(InventoryRecord source, InventoryRecord destination, StackRecord item, int targetSlot, int count)
	{
		Source = source.Id;
		Destination = destination.Id;
		SourceRevision = source.Revision;
		DestinationRevision = destination.Revision;
		SourceSlot = item.Slot;
		DestinationSlot = targetSlot;
		Count = count;
		Item = item.Item;
	}
}
public sealed class DepositResult
{
	public readonly ReadOnlyCollection<TransferLine> Lines;

	public readonly ReadOnlyCollection<string> Skips;

	internal DepositResult(List<TransferLine> lines, List<string> skips)
	{
		Lines = lines.AsReadOnly();
		Skips = skips.AsReadOnly();
	}
}
public static class DepositPlanner
{
	private sealed class Working
	{
		public InventoryRecord Inventory;

		public Dictionary<int, ItemRecord> Items = new Dictionary<int, ItemRecord>();

		public Dictionary<int, int> Counts = new Dictionary<int, int>();
	}

	public static DepositResult Plan(InventoryRecord source, IEnumerable<InventoryRecord> destinations, DepositPolicy policy)
	{
		if (source == null || destinations == null || policy == null)
		{
			throw new ArgumentNullException();
		}
		List<InventoryRecord> list = destinations.ToList();
		if (list.Any((InventoryRecord b) => b == null || b.Id == source.Id) || list.Select((InventoryRecord b) => b.Id).Distinct().Count() != list.Count)
		{
			throw new ArgumentException("Duplicate/source inventory in destinations");
		}
		list = list.OrderByDescending((InventoryRecord b) => b.Priority).ToList();
		List<Working> list2 = list.Select((InventoryRecord b) => new Working
		{
			Inventory = b,
			Items = b.Stacks.ToDictionary((StackRecord s) => s.Slot, (StackRecord s) => s.Item),
			Counts = b.Stacks.ToDictionary((StackRecord s) => s.Slot, (StackRecord s) => s.Count)
		}).ToList();
		List<TransferLine> list3 = new List<TransferLine>();
		List<string> list4 = new List<string>();
		Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
		foreach (KeyValuePair<string, int> target in policy.KeepCounts)
		{
			KeyValuePair<string, int> keyValuePair = target;
			if (keyValuePair.Value < 0)
			{
				throw new ArgumentException("Negative carried target");
			}
			long num = source.Stacks.Where(delegate(StackRecord s)
			{
				string prefab = s.Item.Prefab;
				KeyValuePair<string, int> keyValuePair2 = target;
				return prefab == keyValuePair2.Key;
			}).Sum((Func<StackRecord, long>)((StackRecord s) => (policy.Denial(s) != null) ? s.Count : Math.Min(s.Count, s.Reserve)));
			keyValuePair = target;
			string key = keyValuePair.Key;
			long val = 0L;
			keyValuePair = target;
			dictionary[key] = (int)Math.Max(val, keyValuePair.Value - num);
		}
		foreach (StackRecord stack in source.Stacks.OrderBy((StackRecord s) => s.Slot))
		{
			string text = policy.Denial(stack);
			if (text != null)
			{
				list4.Add(stack.Slot + ": " + text);
				continue;
			}
			int num2 = stack.Available;
			if (dictionary.TryGetValue(stack.Item.Prefab, out var value))
			{
				int num3 = Math.Min(num2, value);
				num2 -= num3;
				dictionary[stack.Item.Prefab] -= num3;
			}
			foreach (Working item in list2)
			{
				if (!item.Inventory.QuickDepositEnabled || (policy.MatchExistingPrefab && !item.Inventory.Stacks.Any((StackRecord s) => s.Item.Prefab == stack.Item.Prefab)))
				{
					continue;
				}
				for (int num4 = 0; num4 < item.Inventory.Capacity; num4++)
				{
					if (num2 <= 0)
					{
						break;
					}
					if (item.Items.TryGetValue(num4, out var value2) && stack.Item.StacksWith(value2))
					{
						int num5 = Math.Min(num2, value2.MaxStack - item.Counts[num4]);
						if (num5 != 0)
						{
							list3.Add(new TransferLine(source, item.Inventory, stack, num4, num5));
							item.Counts[num4] += num5;
							num2 -= num5;
						}
					}
				}
				for (int num4 = 0; num4 < item.Inventory.Capacity; num4++)
				{
					if (num2 <= 0)
					{
						break;
					}
					if (!item.Items.ContainsKey(num4) && !item.Inventory.ReservedSlots.Contains(num4))
					{
						int num5 = Math.Min(num2, stack.Item.MaxStack);
						list3.Add(new TransferLine(source, item.Inventory, stack, num4, num5));
						item.Items.Add(num4, stack.Item);
						item.Counts.Add(num4, num5);
						num2 -= num5;
					}
				}
				if (num2 != 0)
				{
					continue;
				}
				break;
			}
			if (num2 > 0)
			{
				list4.Add(stack.Slot + ": " + num2 + " not placed (no matching capacity)");
			}
		}
		return new DepositResult(list3, list4);
	}
}
public sealed class Ingredient
{
	public readonly string Prefab;

	public readonly int Count;

	public readonly int Quality;

	public Ingredient(string prefab, int count, int quality = -1)
	{
		if (string.IsNullOrEmpty(prefab) || count <= 0 || quality == 0 || quality < -1 || quality > 65535)
		{
			throw new ArgumentException();
		}
		Prefab = prefab;
		Count = count;
		Quality = quality;
	}
}
public sealed class ConsumptionLine
{
	public readonly InventoryRecord Inventory;

	public readonly StackRecord Stack;

	public readonly int Count;

	public ConsumptionLine(InventoryRecord inventory, StackRecord stack, int count)
	{
		Inventory = inventory;
		Stack = stack;
		Count = count;
	}
}
public sealed class CraftResult
{
	public readonly bool Satisfied;

	public readonly ReadOnlyCollection<ConsumptionLine> Lines;

	public readonly ReadOnlyCollection<Ingredient> Missing;

	internal CraftResult(List<ConsumptionLine> lines, List<Ingredient> missing)
	{
		Satisfied = missing.Count == 0;
		Lines = (Satisfied ? lines : new List<ConsumptionLine>()).AsReadOnly();
		Missing = missing.AsReadOnly();
	}
}
public static class CraftPlanner
{
	public static bool CanConsume(StackRecord s)
	{
		return !s.Equipped && !s.Locked && !s.Item.QuestItem && !s.Item.Equipment && s.Item.Magic == MagicState.Normal && !s.Item.HasCustomData;
	}

	public static CraftResult Plan(IEnumerable<InventoryRecord> inventories, IEnumerable<Ingredient> requirements, bool building = false)
	{
		if (inventories == null || requirements == null)
		{
			throw new ArgumentNullException();
		}
		List<InventoryRecord> list = inventories.ToList();
		List<Ingredient> source = requirements.ToList();
		if (list.Any((InventoryRecord s) => s == null) || list.Select((InventoryRecord s) => s.Id).Distinct().Count() != list.Count || source.Any((Ingredient s) => s == null))
		{
			throw new ArgumentException();
		}
		list = list.Where((InventoryRecord s) => building ? s.BuildEnabled : s.CraftEnabled).ToList();
		List<ConsumptionLine> list2 = new List<ConsumptionLine>();
		List<Ingredient> list3 = new List<Ingredient>();
		foreach (var item in from r in source
			group r by new { r.Prefab, r.Quality } into g
			orderby (g.Key.Quality < 0) ? 1 : 0
			select g)
		{
			int num = item.Sum((Ingredient r) => r.Count);
			foreach (InventoryRecord inv in list)
			{
				foreach (StackRecord stack in inv.Stacks.OrderBy((StackRecord s) => s.Slot))
				{
					if (num != 0 && !(stack.Item.Prefab != item.Key.Prefab) && (item.Key.Quality <= 0 || stack.Item.Quality == item.Key.Quality) && CanConsume(stack))
					{
						int num2 = list2.Where((ConsumptionLine l) => l.Inventory.Id == inv.Id && l.Stack.Slot == stack.Slot).Sum((ConsumptionLine l) => l.Count);
						int num3 = Math.Min(num, stack.Available - num2);
						if (num3 > 0)
						{
							list2.Add(new ConsumptionLine(inv, stack, num3));
							num -= num3;
						}
					}
				}
			}
			if (num > 0)
			{
				list3.Add(new Ingredient(item.Key.Prefab, num, item.Key.Quality));
			}
		}
		return new CraftResult(list2, list3);
	}
}
public sealed class SupplyRule
{
	public readonly string Prefab;

	public readonly int Target;

	public readonly int MaxStack;

	public readonly bool Blacklisted;

	public SupplyRule(string prefab, int target, int maxStack, bool blacklisted)
	{
		if (string.IsNullOrEmpty(prefab) || maxStack < 1 || target < 0 || target > maxStack)
		{
			throw new ArgumentException("Invalid supply rule");
		}
		Prefab = prefab;
		Target = target;
		MaxStack = maxStack;
		Blacklisted = blacklisted;
	}
}
public sealed class SupplyPreferences
{
	private readonly Dictionary<string, SupplyRule> rules = new Dictionary<string, SupplyRule>(StringComparer.Ordinal);

	public IEnumerable<SupplyRule> Rules => rules.Values.OrderBy<SupplyRule, string>((SupplyRule r) => r.Prefab, StringComparer.Ordinal);

	public SupplyRule Get(string prefab, int currentMax)
	{
		SupplyRule value;
		return rules.TryGetValue(prefab, out value) ? new SupplyRule(prefab, Math.Min(value.Target, currentMax), currentMax, value.Blacklisted) : new SupplyRule(prefab, 0, currentMax, blacklisted: false);
	}

	public void Set(SupplyRule rule)
	{
		if (rule == null)
		{
			throw new ArgumentNullException();
		}
		if (rule.Target == 0 && !rule.Blacklisted)
		{
			rules.Remove(rule.Prefab);
		}
		else
		{
			rules[rule.Prefab] = rule;
		}
	}

	public void Apply(DepositPolicy policy)
	{
		foreach (SupplyRule rule in Rules)
		{
			if (rule.Blacklisted)
			{
				policy.Blacklist.Add(rule.Prefab);
			}
			if (rule.Target > 0)
			{
				policy.KeepCounts[rule.Prefab] = rule.Target;
			}
		}
	}

	public string Encode()
	{
		return string.Join(";", Rules.Select(delegate(SupplyRule r)
		{
			string[] array = new string[7]
			{
				Convert.ToBase64String(Encoding.UTF8.GetBytes(r.Prefab)),
				",",
				null,
				null,
				null,
				null,
				null
			};
			int target = r.Target;
			array[2] = target.ToString(CultureInfo.InvariantCulture);
			array[3] = ",";
			target = r.MaxStack;
			array[4] = target.ToString(CultureInfo.InvariantCulture);
			array[5] = ",";
			array[6] = (r.Blacklisted ? "1" : "0");
			return string.Concat(array);
		}).ToArray());
	}

	public static SupplyPreferences Decode(string value)
	{
		SupplyPreferences supplyPreferences = new SupplyPreferences();
		if (string.IsNullOrEmpty(value))
		{
			return supplyPreferences;
		}
		string[] array = value.Split(new char[1] { ';' });
		foreach (string text in array)
		{
			string[] array2 = text.Split(new char[1] { ',' });
			if (array2.Length != 4 || (array2[3] != "0" && array2[3] != "1"))
			{
				throw new FormatException("Invalid saved supply preferences");
			}
			string text2 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(array2[0]));
			if (supplyPreferences.rules.ContainsKey(text2))
			{
				throw new FormatException("Duplicate supply rule");
			}
			supplyPreferences.Set(new SupplyRule(text2, int.Parse(array2[1], CultureInfo.InvariantCulture), int.Parse(array2[2], CultureInfo.InvariantCulture), array2[3] == "1"));
		}
		return supplyPreferences;
	}
}
public sealed class RestockResult
{
	public readonly ReadOnlyCollection<TransferLine> Lines;

	public readonly ReadOnlyCollection<Ingredient> Missing;

	internal RestockResult(List<TransferLine> lines, List<Ingredient> missing)
	{
		Lines = lines.AsReadOnly();
		Missing = missing.AsReadOnly();
	}
}
public static class RestockPlanner
{
	public static RestockResult Plan(InventoryRecord player, IEnumerable<InventoryRecord> chests, IEnumerable<SupplyRule> rules)
	{
		if (player == null || chests == null || rules == null)
		{
			throw new ArgumentNullException();
		}
		List<InventoryRecord> list = chests.ToList();
		List<SupplyRule> list2 = rules.ToList();
		if (list.Any((InventoryRecord s) => s == null || s.Id == player.Id) || list.Select((InventoryRecord s) => s.Id).Distinct().Count() != list.Count || list2.Any((SupplyRule t) => t == null) || list2.Select((SupplyRule t) => t.Prefab).Distinct().Count() != list2.Count)
		{
			throw new ArgumentException("Duplicate/invalid restock input");
		}
		list = list.OrderByDescending((InventoryRecord s) => s.Priority).ToList();
		List<StackRecord> list3 = player.Stacks.ToList();
		List<TransferLine> list4 = new List<TransferLine>();
		List<Ingredient> list5 = new List<Ingredient>();
		foreach (SupplyRule target in list2)
		{
			Func<StackRecord, bool> predicate = (StackRecord s) => s.Item.Prefab == target.Prefab;
			long num = list3.Where(predicate).Sum((Func<StackRecord, long>)((StackRecord s) => s.Count));
			List<int> list6 = (from s in list3.Concat(list.SelectMany((InventoryRecord s) => s.Stacks))
				where s.Item.Prefab == target.Prefab
				select s.Item.MaxStack).ToList();
			int num2 = ((list6.Count == 0) ? target.Target : Math.Min(target.Target, list6.Min()));
			int num3 = (int)Math.Max(0L, num2 - num);
			foreach (InventoryRecord item in list)
			{
				foreach (StackRecord item2 in item.Stacks.OrderBy((StackRecord s) => s.Slot))
				{
					if (num3 == 0 || item2.Item.Prefab != target.Prefab || !CraftPlanner.CanConsume(item2) || item2.Available == 0)
					{
						continue;
					}
					int val = Math.Min(Math.Min(num3, item2.Available), item2.Item.MaxStack);
					int val2 = (int)Math.Max(0L, Math.Min(target.Target, item2.Item.MaxStack) - num);
					val = Math.Min(val, val2);
					if (val == 0)
					{
						continue;
					}
					InventoryRecord source = new InventoryRecord(item.Id, item.Revision, item.Capacity, new StackRecord[1]
					{
						new StackRecord(item2.Item, item2.Slot, val)
					});
					InventoryRecord inventoryRecord = new InventoryRecord(player.Id, player.Revision, player.Capacity, list3, quickDepositEnabled: true, craftEnabled: true, buildEnabled: true, 0, player.ReservedSlots);
					DepositResult depositResult = DepositPlanner.Plan(source, new InventoryRecord[1] { inventoryRecord }, new DepositPolicy
					{
						MatchExistingPrefab = false
					});
					foreach (TransferLine move in depositResult.Lines)
					{
						Func<StackRecord, bool> predicate2 = (StackRecord s) => s.Slot == move.DestinationSlot;
						StackRecord stackRecord = list3.FirstOrDefault(predicate2);
						if (stackRecord != null)
						{
							list3.Remove(stackRecord);
						}
						list3.Add(new StackRecord((stackRecord == null) ? move.Item : stackRecord.Item, move.DestinationSlot, move.Count + (stackRecord?.Count ?? 0), stackRecord?.Reserve ?? 0, stackRecord?.Equipped ?? false, stackRecord?.Locked ?? false, stackRecord?.Hotbar ?? false));
						list4.Add(move);
						num3 -= move.Count;
						num += move.Count;
					}
				}
			}
			if (num3 > 0)
			{
				list5.Add(new Ingredient(target.Prefab, num3));
			}
		}
		return new RestockResult(list4, list5);
	}
}
public sealed class TransferBatch
{
	public readonly ReadOnlyCollection<InventoryRecord> Before;

	public readonly ReadOnlyCollection<InventoryRecord> After;

	public readonly ReadOnlyCollection<TransferLine> Lines;

	public readonly int ItemCount;

	public string Purpose;

	private TransferBatch(List<InventoryRecord> before, List<InventoryRecord> after, List<TransferLine> lines)
	{
		Before = before.AsReadOnly();
		After = after.AsReadOnly();
		Lines = lines.AsReadOnly();
		ItemCount = lines.Sum((TransferLine l) => l.Count);
	}

	private static bool Same(ItemRecord a, ItemRecord b)
	{
		return a.Prefab == b.Prefab && a.Identity == b.Identity && a.MaxStack == b.MaxStack;
	}

	public static TransferBatch Prepare(IEnumerable<InventoryRecord> inventories, IEnumerable<TransferLine> transfers)
	{
		if (inventories == null || transfers == null)
		{
			throw new ArgumentNullException();
		}
		List<InventoryRecord> list = inventories.ToList();
		List<TransferLine> list2 = transfers.ToList();
		if (list.Any((InventoryRecord i) => i == null) || list.Select((InventoryRecord i) => i.Id).Distinct().Count() != list.Count || list2.Any((TransferLine l) => l == null))
		{
			throw new ArgumentException("Invalid batch");
		}
		Dictionary<string, InventoryRecord> dictionary = list.ToDictionary<InventoryRecord, string>((InventoryRecord i) => i.Id, StringComparer.Ordinal);
		Dictionary<string, Dictionary<int, int>> remaining = list.ToDictionary<InventoryRecord, string, Dictionary<int, int>>((InventoryRecord i) => i.Id, (InventoryRecord i) => i.Stacks.ToDictionary((StackRecord s) => s.Slot, (StackRecord s) => s.Count), StringComparer.Ordinal);
		foreach (TransferLine line in list2)
		{
			if ((line.Source == line.Destination && line.SourceSlot == line.DestinationSlot) || line.Count <= 0 || line.Item == null || !dictionary.TryGetValue(line.Source, out var value) || !dictionary.TryGetValue(line.Destination, out var value2))
			{
				throw new ArgumentException("Invalid transfer endpoints");
			}
			if (value.Revision != line.SourceRevision || value2.Revision != line.DestinationRevision)
			{
				throw new InvalidOperationException("Inventory changed; rebuild the plan");
			}
			if (line.DestinationSlot < 0 || line.DestinationSlot >= value2.Capacity)
			{
				throw new ArgumentException("Invalid destination slot");
			}
			StackRecord stackRecord = value.Stacks.FirstOrDefault((StackRecord s) => s.Slot == line.SourceSlot);
			if (stackRecord == null || !Same(stackRecord.Item, line.Item) || stackRecord.Equipped || stackRecord.Locked || stackRecord.Item.QuestItem)
			{
				throw new InvalidOperationException("Source item changed or protected");
			}
			int num = remaining[value.Id][stackRecord.Slot];
			if (line.Count > num - stackRecord.Reserve)
			{
				throw new InvalidOperationException("Insufficient unreserved source items");
			}
			remaining[value.Id][stackRecord.Slot] = num - line.Count;
		}
		Dictionary<string, Dictionary<int, StackRecord>> working = list.ToDictionary<InventoryRecord, string, Dictionary<int, StackRecord>>((InventoryRecord i) => i.Id, (InventoryRecord i) => i.Stacks.Where((StackRecord s) => remaining[i.Id][s.Slot] > 0).ToDictionary((StackRecord s) => s.Slot, (StackRecord s) => new StackRecord(s.Item, s.Slot, remaining[i.Id][s.Slot], s.Reserve, s.Equipped, s.Locked, s.Hotbar)), StringComparer.Ordinal);
		foreach (TransferLine item in list2)
		{
			InventoryRecord value2 = dictionary[item.Destination];
			Dictionary<int, StackRecord> dictionary2 = working[value2.Id];
			if (dictionary2.TryGetValue(item.DestinationSlot, out var value3))
			{
				if (!item.Item.StacksWith(value3.Item) || value3.Equipped || checked(value3.Count + item.Count) > value3.Item.MaxStack)
				{
					throw new InvalidOperationException("Destination stack changed or full");
				}
				dictionary2[item.DestinationSlot] = new StackRecord(value3.Item, value3.Slot, value3.Count + item.Count, value3.Reserve, value3.Equipped, value3.Locked, value3.Hotbar);
			}
			else
			{
				if (value2.ReservedSlots.Contains(item.DestinationSlot) || item.Count > item.Item.MaxStack)
				{
					throw new InvalidOperationException("Destination slot protected or full");
				}
				dictionary2.Add(item.DestinationSlot, new StackRecord(item.Item, item.DestinationSlot, item.Count));
			}
		}
		List<InventoryRecord> after = list.Select((InventoryRecord i) => new InventoryRecord(i.Id, i.Revision, i.Capacity, working[i.Id].Values.OrderBy((StackRecord s) => s.Slot), i.QuickDepositEnabled, i.CraftEnabled, i.BuildEnabled, i.Priority, i.ReservedSlots)).ToList();
		return new TransferBatch(list, after, list2);
	}
}
public sealed class TransferJournal
{
	public sealed class PendingRecord
	{
		public string Operation;

		public DateTime Utc;

		public int Items;

		public List<InventoryRecord> Before;

		public List<InventoryRecord> After;
	}

	private readonly string directory;

	private readonly string pending;

	private string operation;

	public bool HasPending => File.Exists(pending);

	public string PendingPath => pending;

	public bool TryResolve(IEnumerable<InventoryRecord> current, out bool committed)
	{
		if (!MatchPending(current, out committed))
		{
			return false;
		}
		Finish(committed);
		return true;
	}

	public bool MatchPending(IEnumerable<InventoryRecord> current, out bool committed)
	{
		committed = false;
		PendingRecord pendingRecord = InspectPending();
		List<InventoryRecord> actual = current.ToList();
		if (actual.Any((InventoryRecord i) => i == null) || actual.Count != pendingRecord.Before.Count || actual.Select((InventoryRecord i) => i.Id).Distinct().Count() != actual.Count)
		{
			return false;
		}
		Func<List<InventoryRecord>, bool> func = (List<InventoryRecord> expected) => expected.All((InventoryRecord e) => NativeTransfer.SameContents(e, actual.FirstOrDefault((InventoryRecord a) => a.Id == e.Id), e.Id.StartsWith("player:", StringComparison.Ordinal)));
		bool flag = func(pendingRecord.Before);
		bool flag2 = func(pendingRecord.After);
		if (flag == flag2)
		{
			return false;
		}
		operation = pendingRecord.Operation;
		committed = flag2;
		return true;
	}

	public TransferJournal(string directory)
	{
		this.directory = Path.GetFullPath(directory);
		pending = Path.Combine(this.directory, "pending.bin");
	}

	public PendingRecord InspectPending()
	{
		return ReadRecord(pending);
	}

	public static PendingRecord ReadRecord(string path)
	{
		using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
		using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8);
		if (fileStream.Length > 33554432 || binaryReader.ReadString() != "VCQD1")
		{
			throw new InvalidDataException("Invalid recovery record");
		}
		PendingRecord pendingRecord = new PendingRecord();
		pendingRecord.Operation = binaryReader.ReadString();
		pendingRecord.Utc = new DateTime(binaryReader.ReadInt64(), DateTimeKind.Utc);
		pendingRecord.Items = binaryReader.ReadInt32();
		PendingRecord pendingRecord2 = pendingRecord;
		if (!Guid.TryParseExact(pendingRecord2.Operation, "N", out var _) || pendingRecord2.Items < 0)
		{
			throw new InvalidDataException("Invalid recovery header");
		}
		pendingRecord2.Before = ReadInventories(binaryReader);
		pendingRecord2.After = ReadInventories(binaryReader);
		if (fileStream.Position != fileStream.Length || !(from i in pendingRecord2.Before
			select i.Id into s
			orderby s
			select s).SequenceEqual(from i in pendingRecord2.After
			select i.Id into s
			orderby s
			select s))
		{
			throw new InvalidDataException("Invalid recovery endpoints");
		}
		return pendingRecord2;
	}

	private static int Bounded(BinaryReader reader, int min, int max)
	{
		int num = reader.ReadInt32();
		if (num < min || num > max)
		{
			throw new InvalidDataException("Recovery field outside bounds");
		}
		return num;
	}

	private static List<InventoryRecord> ReadInventories(BinaryReader reader)
	{
		int num = Bounded(reader, 1, 512);
		List<InventoryRecord> list = new List<InventoryRecord>();
		for (int i = 0; i < num; i++)
		{
			string id = reader.ReadString();
			long revision = reader.ReadInt64();
			int num2 = Bounded(reader, 1, 65536);
			int num3 = Bounded(reader, 0, num2);
			List<StackRecord> list2 = new List<StackRecord>();
			for (int j = 0; j < num3; j++)
			{
				int slot = Bounded(reader, 0, num2 - 1);
				int count = Bounded(reader, 1, 65535);
				string prefab = reader.ReadString();
				string identity = reader.ReadString();
				int maxStack = Bounded(reader, 1, 65535);
				float? exactDurability = (reader.ReadBoolean() ? new float?(reader.ReadSingle()) : ((float?)null));
				int num4 = Bounded(reader, 1, 2097152);
				byte[] array = reader.ReadBytes(num4);
				if (array.Length != num4)
				{
					throw new EndOfStreamException();
				}
				list2.Add(new StackRecord(new ItemRecord(prefab, identity, maxStack, array, equipment: false, hasCustomData: true, MagicState.Unknown, questItem: false, exactDurability), slot, count));
			}
			list.Add(new InventoryRecord(id, revision, num2, list2));
		}
		if (list.Select((InventoryRecord inventoryRecord) => inventoryRecord.Id).Distinct().Count() != list.Count)
		{
			throw new InvalidDataException("Duplicate recovery endpoints");
		}
		return list;
	}

	public void Prepare(TransferBatch batch)
	{
		if (batch == null)
		{
			throw new ArgumentNullException();
		}
		Directory.CreateDirectory(directory);
		if (HasPending)
		{
			throw new InvalidOperationException("An earlier storage operation requires reconciliation");
		}
		operation = Guid.NewGuid().ToString("N");
		string text = Path.Combine(directory, operation + ".tmp");
		try
		{
			using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.WriteThrough))
			{
				using BinaryWriter binaryWriter = new BinaryWriter(fileStream, Encoding.UTF8);
				binaryWriter.Write("VCQD1");
				binaryWriter.Write(operation);
				binaryWriter.Write(DateTime.UtcNow.Ticks);
				binaryWriter.Write(batch.ItemCount);
				WriteInventories(binaryWriter, batch.Before);
				WriteInventories(binaryWriter, batch.After);
				binaryWriter.Flush();
				fileStream.Flush(flushToDisk: true);
			}
			File.Move(text, pending);
		}
		finally
		{
			if (File.Exists(text))
			{
				File.Delete(text);
			}
		}
	}

	private static void WriteInventories(BinaryWriter writer, IEnumerable<InventoryRecord> inventories)
	{
		List<InventoryRecord> list = inventories.ToList();
		writer.Write(list.Count);
		foreach (InventoryRecord item in list)
		{
			writer.Write(item.Id);
			writer.Write(item.Revision);
			writer.Write(item.Capacity);
			writer.Write(item.Stacks.Count);
			foreach (StackRecord stack in item.Stacks)
			{
				writer.Write(stack.Slot);
				writer.Write(stack.Count);
				writer.Write(stack.Item.Prefab);
				writer.Write(stack.Item.Identity);
				writer.Write(stack.Item.MaxStack);
				float? exactDurability = stack.Item.ExactDurability;
				writer.Write(exactDurability.HasValue);
				exactDurability = stack.Item.ExactDurability;
				if (exactDurability.HasValue)
				{
					exactDurability = stack.Item.ExactDurability;
					writer.Write(exactDurability.Value);
				}
				byte[] array = stack.Item.Payload();
				writer.Write(array.Length);
				writer.Write(array);
			}
		}
	}

	public void Finish(bool committed)
	{
		if (!HasPending || string.IsNullOrEmpty(operation))
		{
			throw new InvalidOperationException("Missing prepared storage record");
		}
		string path = Path.Combine(directory, DateTime.UtcNow.ToString("yyyyMMddTHHmmssfffffff") + "-" + operation + ".json");
		using (FileStream fileStream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.Read, 4096, FileOptions.WriteThrough))
		{
			byte[] bytes = Encoding.UTF8.GetBytes("{\"schema\":2,\"operation\":\"" + operation + "\",\"utc\":\"" + DateTime.UtcNow.ToString("o") + "\",\"state\":\"" + (committed ? "locally_applied" : "cancelled") + "\",\"remotePersistenceConfirmed\":false}\n");
			fileStream.Write(bytes, 0, bytes.Length);
			fileStream.Flush(flushToDisk: true);
		}
		File.Move(pending, Path.ChangeExtension(path, "local.bin"));
		operation = null;
		try
		{
			foreach (FileInfo item in new DirectoryInfo(directory).GetFiles("*.json").OrderByDescending<FileInfo, string>((FileInfo f) => f.Name, StringComparer.Ordinal).Skip(128))
			{
				item.Delete();
			}
		}
		catch (IOException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
		try
		{
			long num = 0L;
			int num2 = 0;
			foreach (FileInfo item2 in new DirectoryInfo(directory).GetFiles("*.local.bin").OrderByDescending<FileInfo, string>((FileInfo f) => f.Name, StringComparer.Ordinal))
			{
				if (num2 >= 128 || num + item2.Length > 33554432)
				{
					item2.Delete();
					continue;
				}
				num2++;
				num += item2.Length;
			}
		}
		catch (IOException)
		{
		}
		catch (UnauthorizedAccessException)
		{
		}
	}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("JacobsValheim.VikingCraftingAndQuickDeposit", "Viking Crafting & Quick Deposit", "0.0.4")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	private sealed class Observation
	{
		public ZRpc Rpc;

		public ZDOID Chest;

		public string Token;

		public string Hash;

		public float Deadline;
	}

	private sealed class AdmissionResult
	{
		public bool Granted;
	}

	private sealed class MaterialCount
	{
		public int Quality;

		public int World;

		public long Count;
	}

	private sealed class MaterialCache
	{
		public Player Player;

		public float Expires;

		public Vector3 Position;

		public readonly Dictionary<string, List<MaterialCount>> Items = new Dictionary<string, List<MaterialCount>>(StringComparer.Ordinal);
	}

	public sealed class CountScope
	{
		public int Depth;

		public bool Building;
	}

	private static class DisplayCounts
	{
		private static void Begin(out int __state)
		{
			__state = displayCountDepth;
			displayCountDepth++;
		}

		private static void BeginRefresh(out int __state)
		{
			Begin(out __state);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.InvalidateMaterials();
			}
		}

		private static void End(int __state)
		{
			displayCountDepth = __state;
		}
	}

	public sealed class EpicActionScope
	{
		public int Physical;

		public CountScope Count;
	}

	private sealed class Flight
	{
		public Vector3 From;

		public Transform To;

		public string Prefab;

		public int Variant;

		public float Start;

		public float Duration;
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass92
	{
		public Plugin <>4__this;

		public Player player;

		public Func<Container, bool> extraAccess;

		public bool <GatherMaterialsCore>b__8c()
		{
			return <>4__this.StorageReady && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead();
		}

		public InventoryRecord <GatherMaterialsCore>b__8d()
		{
			return <>4__this.CraftBag(player);
		}
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass95
	{
		private sealed class <>c__DisplayClass9d
		{
			public <>c__DisplayClass95 CS$<>8__locals96;

			public <>c__DisplayClass92 CS$<>8__locals93;

			public ItemData i;

			public bool <GatherMaterialsCore>b__8b(Ingredient r)
			{
				return r.Prefab == ((Object)i.m_dropPrefab).name && (r.Quality <= 0 || r.Quality == i.m_quality);
			}
		}

		public <>c__DisplayClass92 CS$<>8__locals93;

		public StorageUse use;

		public float range;

		public List<Ingredient> missing;

		public bool <GatherMaterialsCore>b__8a(ItemData i)
		{
			<>c__DisplayClass92 <>c__DisplayClass = CS$<>8__locals93;
			return (Object)(object)i.m_dropPrefab != (Object)null && missing.Any((Ingredient r) => r.Prefab == ((Object)i.m_dropPrefab).name && (r.Quality <= 0 || r.Quality == i.m_quality)) && i.m_worldLevel >= Game.m_worldLevel && CS$<>8__locals93.<>4__this.OrdinaryMaterial(i, player: false);
		}
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass98
	{
		public <>c__DisplayClass95 CS$<>8__locals96;

		public <>c__DisplayClass92 CS$<>8__locals93;

		public Container chest;

		public InventoryRecord <GatherMaterialsCore>b__8f()
		{
			return CS$<>8__locals93.<>4__this.CraftChest(chest, CS$<>8__locals93.player, CS$<>8__locals96.range, CS$<>8__locals96.use);
		}
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass9b
	{
		public <>c__DisplayClass98 CS$<>8__locals99;

		public <>c__DisplayClass95 CS$<>8__locals96;

		public <>c__DisplayClass92 CS$<>8__locals93;

		public ChestOwnership.Acquisition acquired;

		public bool <GatherMaterialsCore>b__8e()
		{
			return ChestOwnership.Holds(CS$<>8__locals99.chest, acquired.Operation) && ContainerStorage.Eligible(CS$<>8__locals99.chest, CS$<>8__locals93.player, CS$<>8__locals96.range, CS$<>8__locals96.use) && (CS$<>8__locals93.extraAccess == null || CS$<>8__locals93.extraAccess(CS$<>8__locals99.chest));
		}
	}

	[CompilerGenerated]
	private sealed class <GatherMaterialsCore>d__9f : IEnumerator<object>, IEnumerator, IDisposable
	{
		private object <>2__current;

		private int <>1__state;

		public Plugin <>4__this;

		public Player player;

		public List<Ingredient> requirements;

		public bool building;

		public Action complete;

		public string purpose;

		public Func<Container, bool> extraAccess;

		public TransferJournal <journal>5__a0;

		public string <error>5__a1;

		public bool <satisfied>5__a2;

		public bool <failed>5__a3;

		public int <moved>5__a4;

		public NativeTransferResult <result>5__a5;

		public Func<bool> CS$<>9__CachedAnonymousMethodDelegate9a;

		public <>c__DisplayClass9b CS$<>8__locals9c;

		public Func<InventoryRecord> CS$<>9__CachedAnonymousMethodDelegate97;

		public <>c__DisplayClass98 CS$<>8__locals99;

		public Func<ItemData, bool> CS$<>9__CachedAnonymousMethodDelegate94;

		public <>c__DisplayClass95 CS$<>8__locals96;

		public Func<bool> CS$<>9__CachedAnonymousMethodDelegate90;

		public Func<InventoryRecord> CS$<>9__CachedAnonymousMethodDelegate91;

		public <>c__DisplayClass92 CS$<>8__locals93;

		public List<Container>.Enumerator <>7__wrapa7;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		private bool MoveNext()
		{
			try
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 4)
					{
						if (num != 7)
						{
							goto IL_07f3;
						}
						<>1__state = 5;
						if (<result>5__a5.Committed)
						{
							<moved>5__a4 += <result>5__a5.Items;
							CS$<>8__locals96.missing = CraftPlanner.Plan(new InventoryRecord[1] { <>4__this.CraftBag(CS$<>8__locals93.player) }, requirements, building).Missing.ToList();
						}
						else if (<result>5__a5.NeedsRecovery)
						{
							<failed>5__a3 = true;
							<>m__Finallya9();
							goto IL_0719;
						}
						<>m__Finallya9();
					}
					else
					{
						<>1__state = 3;
						if (CS$<>8__locals9c.acquired.Granted)
						{
							<>1__state = 5;
							<result>5__a5 = null;
							try
							{
								InventoryRecord inventoryRecord = <>4__this.CraftChest(CS$<>8__locals99.chest, CS$<>8__locals93.player, CS$<>8__locals96.range, CS$<>8__locals96.use);
								if (inventoryRecord == null)
								{
									<>m__Finallya9();
									goto IL_0704;
								}
								InventoryRecord inventoryRecord2 = <>4__this.CraftBag(CS$<>8__locals93.player);
								TransferBatch transferBatch = MaterialPull.Plan(inventoryRecord2, inventoryRecord, requirements, building);
								if (transferBatch.Lines.Count == 0)
								{
									<>m__Finallya9();
									goto IL_0704;
								}
								NativeInventoryEndpoint[] array = new NativeInventoryEndpoint[2];
								string id = inventoryRecord2.Id;
								Inventory inventory = ((Humanoid)CS$<>8__locals93.player).GetInventory();
								if (CS$<>9__CachedAnonymousMethodDelegate90 == null)
								{
									CS$<>9__CachedAnonymousMethodDelegate90 = () => CS$<>8__locals93.<>4__this.StorageReady && (Object)(object)CS$<>8__locals93.player != (Object)null && (Object)(object)CS$<>8__locals93.player == (Object)(object)Player.m_localPlayer && !((Character)CS$<>8__locals93.player).IsDead();
								}
								Func<bool> authorized = CS$<>9__CachedAnonymousMethodDelegate90;
								if (CS$<>9__CachedAnonymousMethodDelegate91 == null)
								{
									CS$<>9__CachedAnonymousMethodDelegate91 = () => CS$<>8__locals93.<>4__this.CraftBag(CS$<>8__locals93.player);
								}
								array[0] = new NativeInventoryEndpoint(id, inventory, authorized, CS$<>9__CachedAnonymousMethodDelegate91, allowPickupMark: true);
								string id2 = inventoryRecord.Id;
								Inventory inventory2 = CS$<>8__locals99.chest.GetInventory();
								if (CS$<>9__CachedAnonymousMethodDelegate9a == null)
								{
									CS$<>9__CachedAnonymousMethodDelegate9a = () => ChestOwnership.Holds(CS$<>8__locals9c.CS$<>8__locals99.chest, CS$<>8__locals9c.acquired.Operation) && ContainerStorage.Eligible(CS$<>8__locals9c.CS$<>8__locals99.chest, CS$<>8__locals9c.CS$<>8__locals93.player, CS$<>8__locals9c.CS$<>8__locals96.range, CS$<>8__locals9c.CS$<>8__locals96.use) && (CS$<>8__locals9c.CS$<>8__locals93.extraAccess == null || CS$<>8__locals9c.CS$<>8__locals93.extraAccess(CS$<>8__locals9c.CS$<>8__locals99.chest));
								}
								Func<bool> authorized2 = CS$<>9__CachedAnonymousMethodDelegate9a;
								if (CS$<>9__CachedAnonymousMethodDelegate97 == null)
								{
									CS$<>9__CachedAnonymousMethodDelegate97 = () => CS$<>8__locals99.CS$<>8__locals93.<>4__this.CraftChest(CS$<>8__locals99.chest, CS$<>8__locals99.CS$<>8__locals93.player, CS$<>8__locals99.CS$<>8__locals96.range, CS$<>8__locals99.CS$<>8__locals96.use);
								}
								array[1] = new NativeInventoryEndpoint(id2, inventory2, authorized2, CS$<>9__CachedAnonymousMethodDelegate97);
								NativeInventoryEndpoint[] native = array;
								transferBatch.Purpose = purpose;
								<result>5__a5 = NativeTransfer.Execute(transferBatch, native, <journal>5__a0.Prepare, DeferCompletion(<journal>5__a0));
							}
							catch (Exception ex)
							{
								((BaseUnityPlugin)<>4__this).Logger.LogError((object)("Material pull: " + ex));
								<failed>5__a3 = true;
								<>m__Finallya9();
								goto IL_0719;
							}
							<>2__current = <>4__this.ConfirmTransfer(CS$<>8__locals93.player, CS$<>8__locals99.chest, <journal>5__a0, <result>5__a5);
							<>1__state = 7;
							return true;
						}
					}
				}
				else
				{
					<>1__state = -1;
					CS$<>9__CachedAnonymousMethodDelegate90 = null;
					CS$<>9__CachedAnonymousMethodDelegate91 = null;
					CS$<>8__locals93 = new <>c__DisplayClass92();
					CS$<>8__locals93.player = player;
					CS$<>8__locals93.extraAccess = extraAccess;
					CS$<>8__locals93.<>4__this = <>4__this;
					<journal>5__a0 = null;
					<error>5__a1 = null;
					try
					{
						<journal>5__a0 = <>4__this.Journal(CS$<>8__locals93.player);
						if (<journal>5__a0.HasPending)
						{
							<error>5__a1 = "An earlier transfer needs recovery.";
						}
					}
					catch (Exception ex)
					{
						<error>5__a1 = ex.Message;
					}
					if (<error>5__a1 != null)
					{
						<>4__this.actionRunning = false;
						<>4__this.Notify(<error>5__a1);
						goto IL_07f3;
					}
					<satisfied>5__a2 = false;
					<failed>5__a3 = false;
					<moved>5__a4 = 0;
					<>1__state = 2;
					CS$<>9__CachedAnonymousMethodDelegate94 = null;
					CS$<>8__locals96 = new <>c__DisplayClass95();
					CS$<>8__locals96.CS$<>8__locals93 = CS$<>8__locals93;
					CS$<>8__locals96.use = (building ? StorageUse.Build : StorageUse.Craft);
					CS$<>8__locals96.range = <>4__this.PullRadius(building);
					CS$<>8__locals96.missing = CraftPlanner.Plan(new InventoryRecord[1] { <>4__this.CraftBag(CS$<>8__locals93.player) }, requirements, building).Missing.ToList();
					<>7__wrapa7 = <>4__this.MaterialCandidates(CS$<>8__locals93.player, CS$<>8__locals96.range, CS$<>8__locals96.use, requirements).GetEnumerator();
					<>1__state = 3;
				}
				goto IL_0704;
				IL_07f3:
				return false;
				IL_0704:
				while (<>7__wrapa7.MoveNext())
				{
					CS$<>9__CachedAnonymousMethodDelegate97 = null;
					CS$<>8__locals99 = new <>c__DisplayClass98();
					CS$<>8__locals99.CS$<>8__locals96 = CS$<>8__locals96;
					CS$<>8__locals99.CS$<>8__locals93 = CS$<>8__locals93;
					CS$<>8__locals99.chest = <>7__wrapa7.Current;
					CS$<>9__CachedAnonymousMethodDelegate9a = null;
					CS$<>8__locals9c = new <>c__DisplayClass9b();
					CS$<>8__locals9c.CS$<>8__locals99 = CS$<>8__locals99;
					CS$<>8__locals9c.CS$<>8__locals96 = CS$<>8__locals96;
					CS$<>8__locals9c.CS$<>8__locals93 = CS$<>8__locals93;
					if ((Object)(object)CS$<>8__locals93.player == (Object)null || (Object)(object)CS$<>8__locals93.player != (Object)(object)Player.m_localPlayer || ((Character)CS$<>8__locals93.player).IsDead())
					{
						break;
					}
					if (CS$<>8__locals93.extraAccess != null && !CS$<>8__locals93.extraAccess(CS$<>8__locals99.chest))
					{
						continue;
					}
					if (CS$<>8__locals96.missing.Count == 0)
					{
						break;
					}
					List<ItemData> allItems = CS$<>8__locals99.chest.GetInventory().GetAllItems();
					if (CS$<>9__CachedAnonymousMethodDelegate94 == null)
					{
						CS$<>9__CachedAnonymousMethodDelegate94 = delegate(ItemData i)
						{
							<>c__DisplayClass95.<>c__DisplayClass9d CS$<>8__locals5 = new <>c__DisplayClass95.<>c__DisplayClass9d
							{
								CS$<>8__locals96 = CS$<>8__locals96,
								CS$<>8__locals93 = CS$<>8__locals96.CS$<>8__locals93,
								i = i
							};
							return (Object)(object)CS$<>8__locals5.i.m_dropPrefab != (Object)null && CS$<>8__locals96.missing.Any((Ingredient r) => r.Prefab == ((Object)CS$<>8__locals5.i.m_dropPrefab).name && (r.Quality <= 0 || r.Quality == CS$<>8__locals5.i.m_quality)) && CS$<>8__locals5.i.m_worldLevel >= Game.m_worldLevel && CS$<>8__locals96.CS$<>8__locals93.<>4__this.OrdinaryMaterial(CS$<>8__locals5.i, player: false);
						};
					}
					if (!allItems.Any(CS$<>9__CachedAnonymousMethodDelegate94))
					{
						continue;
					}
					CS$<>8__locals9c.acquired = new ChestOwnership.Acquisition();
					<>2__current = <>4__this.ownership.Acquire(CS$<>8__locals99.chest, CS$<>8__locals93.player, CS$<>8__locals9c.acquired);
					<>1__state = 4;
					return true;
				}
				goto IL_0719;
				IL_0719:
				<>m__Finallya8();
				<satisfied>5__a2 = !<failed>5__a3 && (Object)(object)CS$<>8__locals93.player != (Object)null && (Object)(object)CS$<>8__locals93.player == (Object)(object)Player.m_localPlayer && CraftPlanner.Plan(new InventoryRecord[1] { <>4__this.CraftBag(CS$<>8__locals93.player) }, requirements, building).Satisfied;
				<>m__Finallya6();
				if (<satisfied>5__a2)
				{
					complete();
				}
				else
				{
					<>4__this.Notify(<failed>5__a3 ? "Material transfer stopped; check the plugin log." : ("Could not gather every ingredient. Check free inventory space and storage access. " + <moved>5__a4 + " items gathered."));
				}
				goto IL_07f3;
			}
			catch
			{
				//try-fault
				((IDisposable)this).Dispose();
				throw;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}

		void IDisposable.Dispose()
		{
			switch (<>1__state)
			{
			case 6:
				break;
			case 2:
			case 3:
			case 4:
			case 5:
			case 7:
				try
				{
					switch (<>1__state)
					{
					case 6:
						break;
					case 3:
					case 4:
					case 5:
					case 7:
						try
						{
							switch (<>1__state)
							{
							case 6:
								break;
							case 5:
							case 7:
								try
								{
									break;
								}
								finally
								{
									<>m__Finallya9();
								}
							}
							break;
						}
						finally
						{
							<>m__Finallya8();
						}
					}
					break;
				}
				finally
				{
					<>m__Finallya6();
				}
			}
		}

		[DebuggerHidden]
		public <GatherMaterialsCore>d__9f(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		private void <>m__Finallya6()
		{
			<>1__state = -1;
			<>4__this.actionRunning = false;
			<>4__this.nextScan = 0f;
			<>4__this.InvalidateMaterials();
		}

		private void <>m__Finallya8()
		{
			<>1__state = 2;
			((IDisposable)<>7__wrapa7/*cast due to .constrained prefix*/).Dispose();
		}

		private void <>m__Finallya9()
		{
			<>1__state = 3;
			ChestOwnership.Release(CS$<>8__locals99.chest, CS$<>8__locals9c.acquired.Operation);
		}
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass146
	{
		public Player player;

		public Plugin <>4__this;

		public bool <RunStorageAction>b__140()
		{
			return <>4__this.StorageReady && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead();
		}

		public InventoryRecord <RunStorageAction>b__141()
		{
			return ContainerStorage.CapturePlayer(player, <>4__this.bridge, <>4__this.favorites);
		}
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass148
	{
		public StorageUse use;
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass14b
	{
		public <>c__DisplayClass148 CS$<>8__locals149;

		public <>c__DisplayClass146 CS$<>8__locals147;

		public Container chest;

		public InventoryRecord <RunStorageAction>b__143()
		{
			return ContainerStorage.Capture(chest, CS$<>8__locals147.player, CS$<>8__locals147.<>4__this.Radius(), CS$<>8__locals149.use, CS$<>8__locals147.<>4__this.bridge).Inventory;
		}
	}

	[CompilerGenerated]
	private sealed class <>c__DisplayClass14e
	{
		public <>c__DisplayClass14b CS$<>8__locals14c;

		public <>c__DisplayClass148 CS$<>8__locals149;

		public <>c__DisplayClass146 CS$<>8__locals147;

		public ChestOwnership.Acquisition acquired;

		public bool <RunStorageAction>b__142()
		{
			return ChestOwnership.Holds(CS$<>8__locals14c.chest, acquired.Operation) && ContainerStorage.Eligible(CS$<>8__locals14c.chest, CS$<>8__locals147.player, CS$<>8__locals147.<>4__this.Radius(), CS$<>8__locals149.use);
		}
	}

	[CompilerGenerated]
	private sealed class <RunStorageAction>d__150 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private object <>2__current;

		private int <>1__state;

		public Plugin <>4__this;

		public StorageAction action;

		public int <moved>5__151;

		public int <chests>5__152;

		public int <skipped>5__153;

		public bool <recovery>5__154;

		public TransferJournal <journal>5__155;

		public string <failure>5__156;

		public AdmissionResult <admission>5__157;

		public bool <candidate>5__158;

		public NativeTransferResult <result>5__159;

		public TransferBatch <batch>5__15a;

		public Func<bool> CS$<>9__CachedAnonymousMethodDelegate14d;

		public <>c__DisplayClass14e CS$<>8__locals14f;

		public Func<InventoryRecord> CS$<>9__CachedAnonymousMethodDelegate14a;

		public <>c__DisplayClass14b CS$<>8__locals14c;

		public <>c__DisplayClass148 CS$<>8__locals149;

		public Func<bool> CS$<>9__CachedAnonymousMethodDelegate144;

		public Func<InventoryRecord> CS$<>9__CachedAnonymousMethodDelegate145;

		public <>c__DisplayClass146 CS$<>8__locals147;

		public List<Container>.Enumerator <>7__wrap15c;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		private bool MoveNext()
		{
			try
			{
				switch (<>1__state)
				{
				case 0:
					<>1__state = -1;
					CS$<>9__CachedAnonymousMethodDelegate144 = null;
					CS$<>9__CachedAnonymousMethodDelegate145 = null;
					CS$<>8__locals147 = new <>c__DisplayClass146();
					CS$<>8__locals147.<>4__this = <>4__this;
					CS$<>8__locals147.player = Player.m_localPlayer;
					<moved>5__151 = 0;
					<chests>5__152 = 0;
					<skipped>5__153 = 0;
					<recovery>5__154 = false;
					<journal>5__155 = null;
					<failure>5__156 = null;
					try
					{
						<journal>5__155 = <>4__this.Journal(CS$<>8__locals147.player);
					}
					catch (Exception ex)
					{
						<failure>5__156 = ex.Message;
					}
					if (<failure>5__156 != null)
					{
						<>4__this.Notify(<failure>5__156);
						((BaseUnityPlugin)<>4__this).Logger.LogError((object)(<failure>5__156 + ((<journal>5__155 == null) ? "" : (" " + <journal>5__155.PendingPath))));
						<>4__this.actionRunning = false;
						break;
					}
					if (<journal>5__155.HasPending)
					{
						<>2__current = <>4__this.CheckRecovery(CS$<>8__locals147.player, <journal>5__155);
						<>1__state = 2;
						return true;
					}
					<admission>5__157 = new AdmissionResult();
					<>2__current = <>4__this.RequestAdmission(CS$<>8__locals147.player, <admission>5__157);
					<>1__state = 3;
					return true;
				case 2:
					<>1__state = -1;
					break;
				case 3:
					<>1__state = -1;
					if (!<admission>5__157.Granted)
					{
						<>4__this.actionRunning = false;
						<>4__this.Notify("Server cooldown or storage connection unavailable. No items moved.");
						break;
					}
					<>1__state = 4;
					CS$<>8__locals149 = new <>c__DisplayClass148();
					CS$<>8__locals149.use = ((action == StorageAction.QuickDeposit) ? StorageUse.QuickDeposit : StorageUse.Restock);
					<>7__wrap15c = <>4__this.AvailableStorage(CS$<>8__locals147.player, <>4__this.Radius(), CS$<>8__locals149.use).GetEnumerator();
					<>1__state = 6;
					goto IL_0917;
				case 8:
					<>1__state = 6;
					if (!CS$<>8__locals14f.acquired.Granted)
					{
						<skipped>5__153++;
						((BaseUnityPlugin)<>4__this).Logger.LogDebug((object)("Storage skipped: " + CS$<>8__locals14f.acquired.Error));
					}
					else
					{
						<>1__state = 9;
						<result>5__159 = null;
						<batch>5__15a = null;
						try
						{
							StorageLocation storageLocation2 = ContainerStorage.Capture(CS$<>8__locals14c.chest, CS$<>8__locals147.player, <>4__this.Radius(), CS$<>8__locals149.use, <>4__this.bridge);
							if (storageLocation2 == null || !ChestOwnership.Holds(CS$<>8__locals14c.chest, CS$<>8__locals14f.acquired.Operation))
							{
								<skipped>5__153++;
								<>m__Finally15e();
								goto IL_0917;
							}
							InventoryRecord inventoryRecord = ContainerStorage.CapturePlayer(CS$<>8__locals147.player, <>4__this.bridge, <>4__this.favorites);
							IEnumerable<TransferLine> enumerable = <>4__this.PlanStorage(action, inventoryRecord, storageLocation2.Inventory);
							if (!enumerable.Any())
							{
								<>m__Finally15e();
								goto IL_0917;
							}
							<batch>5__15a = TransferBatch.Prepare(new InventoryRecord[2] { inventoryRecord, storageLocation2.Inventory }, enumerable);
							NativeInventoryEndpoint[] array = new NativeInventoryEndpoint[2];
							string id = inventoryRecord.Id;
							Inventory inventory = ((Humanoid)CS$<>8__locals147.player).GetInventory();
							if (CS$<>9__CachedAnonymousMethodDelegate144 == null)
							{
								CS$<>9__CachedAnonymousMethodDelegate144 = () => CS$<>8__locals147.<>4__this.StorageReady && (Object)(object)CS$<>8__locals147.player != (Object)null && (Object)(object)CS$<>8__locals147.player == (Object)(object)Player.m_localPlayer && !((Character)CS$<>8__locals147.player).IsDead();
							}
							Func<bool> authorized = CS$<>9__CachedAnonymousMethodDelegate144;
							if (CS$<>9__CachedAnonymousMethodDelegate145 == null)
							{
								CS$<>9__CachedAnonymousMethodDelegate145 = () => ContainerStorage.CapturePlayer(CS$<>8__locals147.player, CS$<>8__locals147.<>4__this.bridge, CS$<>8__locals147.<>4__this.favorites);
							}
							array[0] = new NativeInventoryEndpoint(id, inventory, authorized, CS$<>9__CachedAnonymousMethodDelegate145, allowPickupMark: true);
							string id2 = storageLocation2.Inventory.Id;
							Inventory inventory2 = CS$<>8__locals14c.chest.GetInventory();
							if (CS$<>9__CachedAnonymousMethodDelegate14d == null)
							{
								CS$<>9__CachedAnonymousMethodDelegate14d = () => ChestOwnership.Holds(CS$<>8__locals14f.CS$<>8__locals14c.chest, CS$<>8__locals14f.acquired.Operation) && ContainerStorage.Eligible(CS$<>8__locals14f.CS$<>8__locals14c.chest, CS$<>8__locals14f.CS$<>8__locals147.player, CS$<>8__locals14f.CS$<>8__locals147.<>4__this.Radius(), CS$<>8__locals14f.CS$<>8__locals149.use);
							}
							Func<bool> authorized2 = CS$<>9__CachedAnonymousMethodDelegate14d;
							if (CS$<>9__CachedAnonymousMethodDelegate14a == null)
							{
								CS$<>9__CachedAnonymousMethodDelegate14a = () => ContainerStorage.Capture(CS$<>8__locals14c.chest, CS$<>8__locals14c.CS$<>8__locals147.player, CS$<>8__locals14c.CS$<>8__locals147.<>4__this.Radius(), CS$<>8__locals14c.CS$<>8__locals149.use, CS$<>8__locals14c.CS$<>8__locals147.<>4__this.bridge).Inventory;
							}
							array[1] = new NativeInventoryEndpoint(id2, inventory2, authorized2, CS$<>9__CachedAnonymousMethodDelegate14a);
							NativeInventoryEndpoint[] native = array;
							<result>5__159 = NativeTransfer.Execute(<batch>5__15a, native, <journal>5__155.Prepare, DeferCompletion(<journal>5__155));
						}
						catch (Exception ex)
						{
							<skipped>5__153++;
							((BaseUnityPlugin)<>4__this).Logger.LogError((object)("Storage action interrupted: " + ex));
							if (<journal>5__155.HasPending)
							{
								<recovery>5__154 = true;
								<>m__Finally15e();
								goto IL_092c;
							}
						}
						if (<result>5__159 != null)
						{
							<>2__current = <>4__this.ConfirmTransfer(CS$<>8__locals147.player, CS$<>8__locals14c.chest, <journal>5__155, <result>5__159);
							<>1__state = 11;
							return true;
						}
						<>m__Finally15e();
					}
					goto IL_0917;
				case 11:
					<>1__state = 9;
					if (<result>5__159.Committed)
					{
						<moved>5__151 += <result>5__159.Items;
						<chests>5__152++;
						if (action == StorageAction.QuickDeposit)
						{
							<>4__this.DepositFlight(<batch>5__15a, CS$<>8__locals147.player, CS$<>8__locals14c.chest);
						}
					}
					else
					{
						<skipped>5__153++;
						((BaseUnityPlugin)<>4__this).Logger.LogWarning((object)("Storage transfer stopped: " + <result>5__159.Error));
						if (<result>5__159.NeedsRecovery)
						{
							<recovery>5__154 = true;
							((BaseUnityPlugin)<>4__this).Logger.LogError((object)("Prepared recovery data: " + <journal>5__155.PendingPath));
							<>m__Finally15e();
							goto IL_092c;
						}
					}
					<>m__Finally15e();
					<>2__current = null;
					<>1__state = 12;
					return true;
				case 12:
					{
						<>1__state = 6;
						goto IL_0917;
					}
					IL_092c:
					<>m__Finally15d();
					<>m__Finally15b();
					break;
					IL_0917:
					while (<>7__wrap15c.MoveNext())
					{
						CS$<>9__CachedAnonymousMethodDelegate14a = null;
						CS$<>8__locals14c = new <>c__DisplayClass14b();
						CS$<>8__locals14c.CS$<>8__locals149 = CS$<>8__locals149;
						CS$<>8__locals14c.CS$<>8__locals147 = CS$<>8__locals147;
						CS$<>8__locals14c.chest = <>7__wrap15c.Current;
						CS$<>9__CachedAnonymousMethodDelegate14d = null;
						CS$<>8__locals14f = new <>c__DisplayClass14e();
						CS$<>8__locals14f.CS$<>8__locals14c = CS$<>8__locals14c;
						CS$<>8__locals14f.CS$<>8__locals149 = CS$<>8__locals149;
						CS$<>8__locals14f.CS$<>8__locals147 = CS$<>8__locals147;
						if ((Object)(object)CS$<>8__locals147.player == (Object)null || (Object)(object)CS$<>8__locals147.player != (Object)(object)Player.m_localPlayer || ((Character)CS$<>8__locals147.player).IsDead())
						{
							break;
						}
						<candidate>5__158 = true;
						try
						{
							StorageLocation storageLocation = ContainerStorage.Capture(CS$<>8__locals14c.chest, CS$<>8__locals147.player, <>4__this.Radius(), CS$<>8__locals149.use, <>4__this.bridge);
							<candidate>5__158 = storageLocation != null && <>4__this.PlanStorage(action, ContainerStorage.CapturePlayer(CS$<>8__locals147.player, <>4__this.bridge, <>4__this.favorites), storageLocation.Inventory).Any();
						}
						catch
						{
							<skipped>5__153++;
							<candidate>5__158 = false;
						}
						if (!<candidate>5__158)
						{
							continue;
						}
						CS$<>8__locals14f.acquired = new ChestOwnership.Acquisition();
						<>2__current = <>4__this.ownership.Acquire(CS$<>8__locals14c.chest, CS$<>8__locals147.player, CS$<>8__locals14f.acquired);
						<>1__state = 8;
						return true;
					}
					goto IL_092c;
				}
				return false;
			}
			catch
			{
				//try-fault
				((IDisposable)this).Dispose();
				throw;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}

		void IDisposable.Dispose()
		{
			switch (<>1__state)
			{
			case 5:
			case 7:
			case 10:
				break;
			case 4:
			case 6:
			case 8:
			case 9:
			case 11:
			case 12:
				try
				{
					switch (<>1__state)
					{
					case 7:
					case 10:
						break;
					case 6:
					case 8:
					case 9:
					case 11:
					case 12:
						try
						{
							switch (<>1__state)
							{
							case 10:
								break;
							case 9:
							case 11:
								try
								{
									break;
								}
								finally
								{
									<>m__Finally15e();
								}
							}
							break;
						}
						finally
						{
							<>m__Finally15d();
						}
					}
					break;
				}
				finally
				{
					<>m__Finally15b();
				}
			}
		}

		[DebuggerHidden]
		public <RunStorageAction>d__150(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		private void <>m__Finally15b()
		{
			<>1__state = -1;
			<>4__this.actionRunning = false;
			<>4__this.nextScan = 0f;
			<>4__this.InvalidateMaterials();
			if (<recovery>5__154)
			{
				<>4__this.Notify("Storage outcome needs recovery. Further transfers are blocked; see the plugin log.");
				return;
			}
			List<string> list = new List<string>();
			if (<skipped>5__153 > 0)
			{
				list.Add(<skipped>5__153 + " busy or unavailable chest(s)");
			}
			if (action == StorageAction.Restock && (Object)(object)CS$<>8__locals147.player != (Object)null && (Object)(object)CS$<>8__locals147.player == (Object)(object)Player.m_localPlayer)
			{
				foreach (Ingredient item in RestockPlanner.Plan(ContainerStorage.CapturePlayer(CS$<>8__locals147.player, <>4__this.bridge, <>4__this.favorites), new InventoryRecord[0], <>4__this.supplies.Rules).Missing)
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Prefab);
					string text = (((Object)(object)itemPrefab == (Object)null) ? item.Prefab : itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name);
					list.Add(item.Count + " " + ((Localization.instance == null) ? text : Localization.instance.Localize(text)));
				}
			}
			<>4__this.ShowStorageResult(action, (<moved>5__151 == 0) ? ResultState.NoChange : ((list.Count > 0) ? ResultState.Partial : ResultState.Complete), <moved>5__151, <chests>5__152, list);
		}

		private void <>m__Finally15d()
		{
			<>1__state = 4;
			((IDisposable)<>7__wrap15c/*cast due to .constrained prefix*/).Dispose();
		}

		private void <>m__Finally15e()
		{
			<>1__state = 6;
			ChestOwnership.Release(CS$<>8__locals14c.chest, CS$<>8__locals14f.acquired.Operation);
		}
	}

	private const string ConfirmRpc = "VCQD_ObserveV1";

	private const string ConfirmReplyRpc = "VCQD_ObservedV1";

	private const string HelloRpc = "VCQD_HelloV1";

	private const string SettingsRpc = "VCQD_SettingsV1";

	private const string AdmitRpc = "VCQD_AdmitV1";

	private const string AdmitReplyRpc = "VCQD_AdmitReplyV1";

	private const string AutoFuelKey = "VCQD_AutoFuel_v1";

	private const string AutoFuelRpc = "VCQD_AutoFuelToggle_v1";

	private const float SearchRowHeight = 112f;

	private readonly Dictionary<ZRpc, Observation> observations = new Dictionary<ZRpc, Observation>();

	private string confirmationToken;

	private bool confirmationReceived;

	private bool confirmationGranted;

	private bool serverConfirmation;

	private ConfigEntry<bool> sharedChests;

	private bool serverSharedChests;

	private bool? sharedConflict;

	private Container sharedView;

	private Container sharedSettingLease;

	private static Container renderingShared;

	private float sharedRefresh;

	private static readonly MethodInfo ClearDrag = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null);

	private readonly Dictionary<ZRpc, float> storageConnections = new Dictionary<ZRpc, float>();

	private ZRpc storageServer;

	private float[] serverRanges;

	private float nextNetworkScan;

	private readonly Dictionary<long, float> nextServerAction = new Dictionary<long, float>();

	private string admissionOperation;

	private bool admissionReceived;

	private bool admissionGranted;

	private ConfigEntry<float> craftRadius;

	private ConfigEntry<float> buildRadius;

	private static int countDepth;

	private static int physicalOnly;

	private static int displayCountDepth;

	private static bool countBuilding;

	private static int buildConsumptionFrame = -1;

	private readonly MaterialCache craftCounts = new MaterialCache();

	private readonly MaterialCache buildCounts = new MaterialCache();

	private static readonly FieldInfo CraftRecipe = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");

	private static readonly FieldInfo UpgradeItem = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem");

	private static readonly FieldInfo MultiCraft = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting");

	private static readonly MethodInfo DoCraft = AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null);

	private Type epicTableUi;

	private static Type epicListType;

	private static readonly Dictionary<Type, FieldInfo> epicCostFields = new Dictionary<Type, FieldInfo>();

	private bool epicCraftingReady;

	private ConfigEntry<bool> refillEnabled;

	private static int refillNative;

	private static readonly MethodInfo SmelterFuel = AccessTools.Method(typeof(Smelter), "OnAddFuel", (Type[])null, (Type[])null);

	private static readonly MethodInfo SmelterOre = AccessTools.Method(typeof(Smelter), "OnAddOre", (Type[])null, (Type[])null);

	private static readonly MethodInfo SmelterFuelCount = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null);

	private static readonly MethodInfo SmelterQueue = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null);

	private static readonly MethodInfo CookingFuel = AccessTools.Method(typeof(CookingStation), "OnAddFuelSwitch", (Type[])null, (Type[])null);

	private static readonly MethodInfo CookingInteract = AccessTools.Method(typeof(CookingStation), "OnInteract", (Type[])null, (Type[])null);

	private static readonly MethodInfo CookingDone = AccessTools.Method(typeof(CookingStation), "HaveDoneItem", (Type[])null, (Type[])null);

	private static readonly MethodInfo CookingSlot = AccessTools.Method(typeof(CookingStation), "GetFreeSlot", (Type[])null, (Type[])null);

	private static readonly MethodInfo CookingFire = AccessTools.Method(typeof(CookingStation), "IsFireLit", (Type[])null, (Type[])null);

	private static readonly MethodInfo CookingFuelCount = AccessTools.Method(typeof(CookingStation), "GetFuel", (Type[])null, (Type[])null);

	private static readonly MethodInfo ShieldFuel = AccessTools.Method(typeof(ShieldGenerator), "OnAddFuel", (Type[])null, (Type[])null);

	private static readonly MethodInfo ShieldFuelCount = AccessTools.Method(typeof(ShieldGenerator), "GetFuel", (Type[])null, (Type[])null);

	private static CookingStation cookingTarget;

	private static ItemData cookingChoice;

	private static readonly MethodInfo GridPosition = AccessTools.Method(typeof(InventoryGrid), "GetButtonPos", (Type[])null, (Type[])null);

	private static readonly MethodInfo GridElement = AccessTools.Method(typeof(InventoryGrid), "GetElement", (Type[])null, (Type[])null);

	private static readonly FieldInfo DragItem = AccessTools.Field(typeof(InventoryGui), "m_dragItem");

	private static readonly FieldInfo DragInventory = AccessTools.Field(typeof(InventoryGui), "m_dragInventory");

	private static readonly FieldInfo DragAmount = AccessTools.Field(typeof(InventoryGui), "m_dragAmount");

	private static readonly MethodInfo SetDrag = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null);

	private ConfigEntry<bool> trashEnabled;

	private GameObject trashObject;

	private InventoryGui trashGui;

	private Button trashButton;

	private TMP_Text trashLabel;

	private UITooltip trashTip;

	private readonly Dictionary<string, Sprite> itemIcons = new Dictionary<string, Sprite>();

	private readonly List<Flight> flights = new List<Flight>();

	private ConfigEntry<bool> depositEffects;

	private ConfigEntry<float> depositFlightSeconds;

	private readonly HashSet<Fireplace> fires = new HashSet<Fireplace>();

	private FuelOwnership fuelOwnership;

	private ConfigEntry<bool> automaticRefill;

	private float nextFuelScan;

	private float nextFireCleanup;

	private int fuelCursor;

	private readonly ItemLocationIndex materialIndex = new ItemLocationIndex();

	private ConfigEntry<KeyboardShortcut> depositKey;

	private ConfigEntry<KeyboardShortcut> restockKey;

	private ConfigEntry<float> noticeSeconds;

	private string notice;

	private float noticeUntil;

	private float nextActionTime;

	private bool actionRunning;

	private ConfigEntry<bool> depositEquipment;

	private ConfigEntry<bool> depositEnchanted;

	private ConfigEntry<bool> protectHotbar;

	private static readonly FieldInfo CurrentChest = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");

	private GameObject chestControls;

	private InventoryGui chestGui;

	private Container displayedChest;

	private Button depositButton;

	private Button craftButton;

	private Button buildButton;

	private Button priorityDown;

	private Button priorityUp;

	private Button sortButton;

	private TMP_Text depositLabel;

	private TMP_Text craftLabel;

	private TMP_Text buildLabel;

	private TMP_Text priorityLabel;

	private GameObject locatorObject;

	private LineRenderer locatorOutline;

	private Material locatorMaterial;

	private Minimap locatorMap;

	private PinData locatorPin;

	private Renderer[] locatorRenderers;

	private readonly Vector3[] locatorCorners = (Vector3[])(object)new Vector3[8];

	private static readonly int[] BoxPath = new int[16]
	{
		0, 1, 2, 3, 0, 4, 5, 1, 5, 6,
		2, 6, 7, 3, 7, 4
	};

	internal static Plugin Instance;

	internal readonly ModalInputState Modal = new ModalInputState();

	private readonly ChestScanner scanner = new ChestScanner();

	private readonly EpicLootBridge bridge = new EpicLootBridge();

	private readonly StorytellerBridge storyteller = new StorytellerBridge();

	private ConfigEntry<float> radius;

	private ConfigEntry<bool> discovery;

	private ConfigEntry<KeyboardShortcut> searchKey;

	private Harmony patches;

	private float nextScan;

	private float nextWarning;

	private Player lastPlayer;

	private Coroutine scan;

	private Coroutine discoveryScan;

	private bool ready;

	private bool searchRequested;

	private ChestOwnership ownership;

	private float nextStorageCleanup;

	private Rect window;

	private Vector2 scroll;

	private string query = "";

	private string rarity = "";

	private bool focusSearch;

	private int resultOrder;

	private int shownRevision = -1;

	private List<SearchRow> results = new List<SearchRow>();

	private Vector3 searchOrigin;

	private CursorLockMode cursorMode;

	private bool cursorVisible;

	private Texture2D background;

	private GUIStyle panel;

	private GUIStyle label;

	private GUIStyle muted;

	private GUIStyle button;

	private GUIStyle title;

	private string locateId;

	private float locateUntil;

	private float nextAccessCheck;

	private SupplyPreferences supplies = new SupplyPreferences();

	private ConfigEntry<string> supplySetting;

	private FavoriteSlots favorites = new FavoriteSlots();

	private ConfigEntry<string> favoriteSetting;

	private bool favoriteTab;

	private bool suppliesTab;

	private bool supplyReady;

	private Vector2 supplyScroll;

	private string supplyQuery = "";

	private string supplyError = "";

	private readonly Dictionary<string, string> targetText = new Dictionary<string, string>();

	private bool SharedAvailable
	{
		get
		{
			if (!sharedConflict.HasValue)
			{
				sharedConflict = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "MultiUserChest");
				if (sharedConflict.Value)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Built-in shared chest access is disabled while another shared chest handler is installed. Remove MultiUserChest before enabling built-in access.");
				}
			}
			return !sharedConflict.Value;
		}
	}

	private bool SharedEnabled => StorageReady && SharedAvailable && (ZNet.instance.IsServer() ? sharedChests.Value : serverSharedChests);

	private bool StorageReady => (Object)(object)ZNet.instance != (Object)null && (ZNet.instance.IsServer() || (serverConfirmation && storageServer != null && storageServer.IsConnected() && ZNet.instance.GetServerPeer() != null && ZNet.instance.GetServerPeer().m_rpc == storageServer));

	internal static bool BlockInput => (Object)(object)Instance != (Object)null && Instance.Modal.Blocks(Time.frameCount);

	private static string ItemHash(byte[] bytes)
	{
		if (bytes == null)
		{
			return null;
		}
		using SHA256 sHA = SHA256.Create();
		return Convert.ToBase64String(sHA.ComputeHash(bytes));
	}

	private static bool ObservationMatches(long owner, long sender, byte[] items, string hash)
	{
		return owner == sender && items != null && hash != null && ItemHash(items) == hash;
	}

	private void ReceiveObservation(ZRpc rpc, ZPackage package)
	{
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || package.Size() > 256)
		{
			return;
		}
		try
		{
			ZNetPeer val = ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_rpc == rpc && p.IsReady()));
			if (val == null)
			{
				return;
			}
			string text = package.ReadString();
			if (text.Length != 32 || !Guid.TryParseExact(text, "N", out var _))
			{
				return;
			}
			ZDOID val2 = package.ReadZDOID();
			string text2 = package.ReadString();
			if (text2.Length == 44)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(val2);
				if (zDO == null)
				{
					rpc.Invoke("VCQD_ObservedV1", new object[2] { text, false });
					return;
				}
				if (observations.ContainsKey(rpc) || observations.Count >= 64)
				{
					rpc.Invoke("VCQD_ObservedV1", new object[2] { text, false });
					return;
				}
				observations.Add(rpc, new Observation
				{
					Rpc = rpc,
					Chest = val2,
					Token = text,
					Hash = text2,
					Deadline = Time.realtimeSinceStartup + 4f
				});
			}
		}
		catch (Exception ex)
		{
			Warn("Storage observation rejected: " + ex.Message);
		}
	}

	private void TickObservations()
	{
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		if (observations.Count == 0)
		{
			return;
		}
		KeyValuePair<ZRpc, Observation>[] array = observations.ToArray();
		foreach (KeyValuePair<ZRpc, Observation> pair in array)
		{
			KeyValuePair<ZRpc, Observation> keyValuePair = pair;
			Observation value = keyValuePair.Value;
			ZNetPeer val = (((Object)(object)ZNet.instance == (Object)null) ? null : ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)delegate(ZNetPeer p)
			{
				ZRpc rpc = p.m_rpc;
				KeyValuePair<ZRpc, Observation> keyValuePair2 = pair;
				return rpc == keyValuePair2.Key && p.IsReady();
			}));
			if (val == null)
			{
				Dictionary<ZRpc, Observation> dictionary = observations;
				keyValuePair = pair;
				dictionary.Remove(keyValuePair.Key);
				continue;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(value.Chest);
			bool flag = zDO != null && zDO.GetOwner() == val.m_uid && ObservationMatches(zDO.GetOwner(), val.m_uid, zDO.GetByteArray(ZDOVars.s_items, (byte[])null), value.Hash);
			if (flag || Time.realtimeSinceStartup >= value.Deadline)
			{
				Dictionary<ZRpc, Observation> dictionary2 = observations;
				keyValuePair = pair;
				dictionary2.Remove(keyValuePair.Key);
				keyValuePair = pair;
				keyValuePair.Key.Invoke("VCQD_ObservedV1", new object[2] { value.Token, flag });
			}
		}
	}

	private void ReceiveObservationReply(ZRpc rpc, string token, bool accepted)
	{
		if (rpc == storageServer && !(token != confirmationToken))
		{
			confirmationReceived = true;
			confirmationGranted = accepted;
		}
	}

	private IEnumerator ConfirmTransfer(Player player, Container chest, TransferJournal journal, NativeTransferResult result, bool applied = true)
	{
		if (!result.Committed)
		{
			yield break;
		}
		result.Committed = false;
		result.NeedsRecovery = true;
		ZNetPeer peer = (((Object)(object)ZNet.instance == (Object)null) ? null : ZNet.instance.GetServerPeer());
		ZRpc connection = peer?.m_rpc;
		string error = null;
		string token = null;
		string hash = null;
		ZDOID id = ZDOID.None;
		try
		{
			ZNetView val = ContainerStorage.View(chest);
			if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner() || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead())
			{
				throw new InvalidOperationException("Storage session changed before confirmation");
			}
			id = val.GetZDO().m_uid;
			hash = ItemHash(val.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null));
			token = journal.InspectPending().Operation;
			if (hash == null)
			{
				throw new InvalidOperationException("Container save unavailable");
			}
			if (!ZNet.instance.IsServer() && (!StorageReady || !serverConfirmation || connection == null))
			{
				throw new InvalidOperationException("Server transfer confirmation unavailable");
			}
		}
		catch (Exception ex)
		{
			error = ex.Message;
		}
		if (error != null)
		{
			result.Error = error;
			yield break;
		}
		confirmationToken = token;
		confirmationReceived = (confirmationGranted = ZNet.instance.IsServer());
		try
		{
			if (!ZNet.instance.IsServer())
			{
				try
				{
					ZDOMan.instance.ForceSendZDO(peer.m_uid, id);
					ZPackage val2 = new ZPackage();
					val2.Write(token);
					val2.Write(id);
					val2.Write(hash);
					connection.Invoke("VCQD_ObserveV1", new object[1] { val2 });
				}
				catch (Exception ex)
				{
					error = ex.Message;
				}
				if (error != null)
				{
					result.Error = "Confirmation interrupted: " + error;
					yield break;
				}
				float deadline = Time.realtimeSinceStartup + 5f;
				while (!confirmationReceived && Time.realtimeSinceStartup < deadline && StorageReady && storageServer == connection && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead())
				{
					yield return null;
				}
			}
			bool session = (Object)(object)ZNet.instance != (Object)null && (ZNet.instance.IsServer() || (StorageReady && storageServer == connection));
			if (confirmationReceived && confirmationGranted && session && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead())
			{
				try
				{
					journal.Finish(applied);
					result.Committed = true;
					result.NeedsRecovery = false;
					yield break;
				}
				catch (Exception ex)
				{
					result.Error = ex.Message;
					yield break;
				}
			}
			result.Error = "Server did not confirm the chest update. Pending evidence retained; no automatic retry or refund.";
		}
		finally
		{
			confirmationToken = null;
		}
	}

	private static Action<bool> DeferCompletion(TransferJournal journal)
	{
		return delegate(bool applied)
		{
			if (!applied)
			{
				journal.Finish(committed: false);
			}
		};
	}

	private IEnumerator CheckRecovery(Player player, TransferJournal journal)
	{
		List<KeyValuePair<Container, string>> acquired = new List<KeyValuePair<Container, string>>();
		string outcome = "Recovery needs manual review; no items changed.";
		try
		{
			TransferJournal.PendingRecord record = journal.InspectPending();
			string playerId = "player:" + player.GetPlayerID();
			if (!record.Before.Any((InventoryRecord i) => i.Id == playerId))
			{
				Notify("Recovery belongs to another character.");
				yield break;
			}
			List<InventoryRecord> snapshots = new List<InventoryRecord>();
			bool available = true;
			foreach (InventoryRecord endpoint in record.Before)
			{
				if (!(endpoint.Id == playerId))
				{
					Container chest = ((IEnumerable<Container>)ChestScanner.Containers).FirstOrDefault((Func<Container, bool>)((Container c) => (Object)(object)c != (Object)null && ContainerStorage.Identity(c) == endpoint.Id && ChestScanner.Accessible(c, player, MaximumRadius())));
					if ((Object)(object)chest == (Object)null)
					{
						available = false;
						break;
					}
					ChestOwnership.Acquisition grant = new ChestOwnership.Acquisition();
					yield return ownership.Acquire(chest, player, grant);
					if (!grant.Granted)
					{
						available = false;
						break;
					}
					acquired.Add(new KeyValuePair<Container, string>(chest, grant.Operation));
				}
			}
			if (available && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead() && acquired.All((KeyValuePair<Container, string> p) => ChestOwnership.Holds(p.Key, p.Value) && ChestScanner.Accessible(p.Key, player, MaximumRadius())))
			{
				snapshots.Add(ContainerStorage.CapturePlayer(player, bridge, favorites));
				snapshots.AddRange(acquired.Select((KeyValuePair<Container, string> p) => NativeTransfer.Capture(ContainerStorage.Identity(p.Key), p.Key.GetInventory(), bridge, 0L)));
				if (acquired.Count == 1 && journal.MatchPending(snapshots, out var committed))
				{
					NativeTransferResult result = new NativeTransferResult
					{
						Committed = true
					};
					yield return ConfirmTransfer(player, acquired[0].Key, journal, result, committed);
					if (result.Committed)
					{
						outcome = (committed ? "Previous transfer verified complete. Press again for a new action." : "Previous transfer verified unchanged. Press again to try again.");
					}
				}
			}
			else
			{
				outcome = "Return within reach of the original chest to check the pending transfer.";
			}
		}
		finally
		{
			foreach (KeyValuePair<Container, string> item in acquired)
			{
				ChestOwnership.Release(item.Key, item.Value);
			}
			actionRunning = false;
			Notify(outcome);
		}
	}

	private bool ViewingShared(Container c)
	{
		return (Object)(object)c != (Object)null && (Object)(object)c == (Object)(object)sharedView && (Object)(object)InventoryGui.instance != (Object)null && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)(object)c;
	}

	internal bool CanEditShared(Container c)
	{
		return SharedEnabled && ViewingShared(c) && SharedAccess(c, Player.m_localPlayer) && !actionRunning;
	}

	internal bool SettingShared(Container c)
	{
		return (Object)(object)c != (Object)null && (Object)(object)sharedSettingLease == (Object)(object)c;
	}

	private void QueueSharedSetting(Container c, Func<Container, bool> change)
	{
		if (CanEditShared(c))
		{
			actionRunning = true;
			((MonoBehaviour)this).StartCoroutine(RunShared(c, null, "Chest setting updated.", change));
		}
	}

	private void SetupSharedChests()
	{
		sharedChests = ((BaseUnityPlugin)this).Config.Bind<bool>("Shared chests", "Enabled", true, "Allow simultaneous chest viewing. Every item change takes an exclusive short storage lease and validates the selected stacks. Server controls this setting.");
	}

	private void InstallSharedHooks()
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_004f: Expected O, but got Unknown
		patches.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "UpdateContainer", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeginSharedRender", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "EndSharedRender", (Type[])null), (HarmonyMethod)null);
		Hook(typeof(Container), "IsOwner", null, "SharedRenderOwner");
		Hook(typeof(Container), "SetInUse", "SharedSetInUse", null);
		Hook(typeof(InventoryGui), "OnSelectedItem", "SharedSelect", null);
		Hook(typeof(InventoryGui), "OnTakeAll", "SharedTakeAll", null);
		Hook(typeof(InventoryGui), "OnStackAll", "SharedStackAll", null);
		Hook(typeof(Container), "StackAll", "SharedHoldStack", null);
		Hook(typeof(InventoryGui), "OnDropOutside", "SharedDropOutside", null);
		Hook(typeof(InventoryGui), "OnRightClickItem", "SharedRightClick", null);
	}

	private bool OpenShared(Container chest, Humanoid user, bool hold, ref bool result)
	{
		if (!SharedEnabled || (Object)(object)user != (Object)(object)Player.m_localPlayer || (Object)(object)chest == (Object)null || (Object)(object)((Component)chest).GetComponent<TombStone>() != (Object)null || (Object)(object)((Component)chest).GetComponentInParent<Player>() != (Object)null)
		{
			return false;
		}
		result = false;
		if (hold)
		{
			return true;
		}
		if (!SharedAccess(chest, Player.m_localPlayer) || chest.IsInUse())
		{
			Notify("Chest busy or inaccessible.");
			return true;
		}
		if ((Object)(object)InventoryGui.instance == (Object)null)
		{
			return true;
		}
		try
		{
			sharedView = chest;
			sharedRefresh = 0f;
			ChestOwnership.Refresh(chest);
			InventoryGui.instance.Show(chest, 1);
			result = true;
		}
		catch (Exception ex)
		{
			sharedView = null;
			Notify("Unable to open shared storage.");
			Warn("Shared open failed: " + ex.Message);
		}
		return true;
	}

	private bool SharedAccess(Container chest, Player player)
	{
		return (Object)(object)player != (Object)null && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && ChestScanner.Accessible(chest, player, Math.Max(0f, player.m_maxInteractDistance) + Math.Max(0f, ((Object)(object)chest == (Object)null) ? 0f : chest.m_hoverOffset)) && (!((Object)(object)chest.m_wagon != (Object)null) || !chest.m_wagon.InUse());
	}

	private static void BeginSharedRender(InventoryGui __instance)
	{
		Plugin instance = Instance;
		renderingShared = null;
		if ((Object)(object)instance == (Object)null || !instance.ViewingShared(instance.sharedView))
		{
			return;
		}
		if (!instance.SharedEnabled || !instance.SharedAccess(instance.sharedView, Player.m_localPlayer))
		{
			AccessTools.Method(typeof(InventoryGui), "CloseContainer", (Type[])null, (Type[])null).Invoke(__instance, null);
			instance.sharedView = null;
			return;
		}
		renderingShared = instance.sharedView;
		if (!(Time.realtimeSinceStartup >= instance.sharedRefresh) || instance.actionRunning)
		{
			return;
		}
		instance.sharedRefresh = Time.realtimeSinceStartup + 0.25f;
		try
		{
			ChestOwnership.Refresh(instance.sharedView);
		}
		catch (Exception ex)
		{
			instance.Warn("Shared storage refresh failed: " + ex.Message);
			AccessTools.Method(typeof(InventoryGui), "CloseContainer", (Type[])null, (Type[])null).Invoke(__instance, null);
			instance.sharedView = null;
			renderingShared = null;
		}
	}

	private static Exception EndSharedRender(Exception __exception)
	{
		renderingShared = null;
		return __exception;
	}

	private static void SharedRenderOwner(Container __instance, ref bool __result)
	{
		if ((Object)(object)__instance == (Object)(object)renderingShared)
		{
			__result = true;
		}
	}

	private static bool SharedSetInUse(Container __instance)
	{
		return (Object)(object)Instance == (Object)null || !Instance.ViewingShared(__instance);
	}

	private static bool SharedSelect(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod)
	{
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Invalid comparison between Unknown and I4
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Invalid comparison between Unknown and I4
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		Plugin instance = Instance;
		if ((Object)(object)instance == (Object)null || !instance.ViewingShared(instance.sharedView))
		{
			return true;
		}
		Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
		Inventory inventory2 = instance.sharedView.GetInventory();
		object? value = DragItem.GetValue(__instance);
		ItemData val = (ItemData)((value is ItemData) ? value : null);
		object? value2 = DragInventory.GetValue(__instance);
		Inventory val2 = (Inventory)((value2 is Inventory) ? value2 : null);
		if (instance.actionRunning)
		{
			return false;
		}
		if (val != null && (val2 == inventory2 || grid.GetInventory() == inventory2))
		{
			instance.QueueSharedMove(val2, grid.GetInventory(), val, pos, (int)DragAmount.GetValue(__instance), drop: false);
			return false;
		}
		if (item == null)
		{
			return true;
		}
		if ((int)mod == 2 && (grid.GetInventory() == inventory2 || grid.GetInventory() == inventory))
		{
			instance.QueueSharedMove(grid.GetInventory(), (grid.GetInventory() == inventory2) ? inventory : inventory2, item, new Vector2i(-1, -1), item.m_stack, drop: false);
			return false;
		}
		if ((int)mod == 3 && grid.GetInventory() == inventory2)
		{
			instance.QueueSharedMove(inventory2, inventory, item, new Vector2i(-1, -1), item.m_stack, drop: true);
			return false;
		}
		return true;
	}

	private static bool SharedDropOutside(InventoryGui __instance)
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		Plugin instance = Instance;
		if ((Object)(object)instance == (Object)null || !instance.ViewingShared(instance.sharedView))
		{
			return true;
		}
		object? value = DragItem.GetValue(__instance);
		ItemData val = (ItemData)((value is ItemData) ? value : null);
		object? value2 = DragInventory.GetValue(__instance);
		Inventory val2 = (Inventory)((value2 is Inventory) ? value2 : null);
		if (val2 != instance.sharedView.GetInventory() || val == null)
		{
			return true;
		}
		instance.QueueSharedMove(val2, ((Humanoid)Player.m_localPlayer).GetInventory(), val, new Vector2i(-1, -1), (int)DragAmount.GetValue(__instance), drop: true);
		return false;
	}

	private static bool SharedRightClick(InventoryGrid grid, ItemData item)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		Plugin instance = Instance;
		if ((Object)(object)instance == (Object)null || !instance.ViewingShared(instance.sharedView) || grid.GetInventory() != instance.sharedView.GetInventory())
		{
			return true;
		}
		if (item != null)
		{
			instance.QueueSharedMove(grid.GetInventory(), ((Humanoid)Player.m_localPlayer).GetInventory(), item, new Vector2i(-1, -1), item.m_stack, drop: false, use: true);
		}
		return false;
	}

	private static bool SharedTakeAll()
	{
		Plugin instance = Instance;
		if ((Object)(object)instance == (Object)null || !instance.ViewingShared(instance.sharedView))
		{
			return true;
		}
		instance.QueueShared((InventoryRecord bag, InventoryRecord box) => SharedChestPlan.All(bag, box, take: true, existingOnly: false));
		return false;
	}

	private static bool SharedStackAll()
	{
		Plugin instance = Instance;
		if ((Object)(object)instance == (Object)null || !instance.ViewingShared(instance.sharedView))
		{
			return true;
		}
		instance.QueueShared((InventoryRecord bag, InventoryRecord box) => SharedChestPlan.All(bag, box, take: false, existingOnly: true));
		return false;
	}

	private static bool SharedHoldStack(Container __instance)
	{
		return (Object)(object)Instance == (Object)null || !Instance.ViewingShared(__instance) || SharedStackAll();
	}

	private void QueueSharedMove(Inventory source, Inventory target, ItemData item, Vector2i pos, int amount, bool drop, bool use = false)
	{
		if (actionRunning || item == null || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		try
		{
			Inventory inventory = sharedView.GetInventory();
			Inventory bag = ((Humanoid)Player.m_localPlayer).GetInventory();
			if ((source != inventory && source != bag) || (target != inventory && target != bag))
			{
				return;
			}
			int from = item.m_gridPos.y * source.GetWidth() + item.m_gridPos.x;
			int to = ((pos.x < 0) ? (-1) : (pos.y * target.GetWidth() + pos.x));
			ItemData val = ((to < 0) ? null : target.GetItemAt(pos.x, pos.y));
			string identity = NativeSnapshot.Capture(item, bridge).Identity;
			string targetId = ((val == null) ? null : NativeSnapshot.Capture(val, bridge).Identity);
			int count = item.m_stack;
			int targetCount = val?.m_stack ?? 0;
			bool fromBox = source == inventory;
			bool toBox = target == inventory;
			if (fromBox == toBox && from == to)
			{
				return;
			}
			QueueShared((InventoryRecord b, InventoryRecord c) => SharedChestPlan.Move(b, c, fromBox, toBox, from, to, amount, identity, count, targetId, targetCount), null, (drop || use) ? ((Action<TransferBatch>)delegate(TransferBatch batch)
			{
				Player player = Player.m_localPlayer;
				if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() != bag)
				{
					return;
				}
				foreach (TransferLine item2 in batch.Lines.Where((TransferLine l) => l.Destination == "player:" + player.GetPlayerID()))
				{
					ItemData itemAt = bag.GetItemAt(item2.DestinationSlot % bag.GetWidth(), item2.DestinationSlot / bag.GetWidth());
					if (itemAt != null)
					{
						if (!drop)
						{
							((Humanoid)player).UseItem(bag, itemAt, true);
							break;
						}
						((Humanoid)player).DropItem(bag, itemAt, item2.Count);
					}
				}
			}) : null);
		}
		catch (Exception ex)
		{
			Notify("Unable to prepare that item transfer.");
			Warn("Shared transfer preparation failed: " + ex.Message);
		}
	}

	private void QueueShared(Func<InventoryRecord, InventoryRecord, TransferBatch> plan, string message = null, Action<TransferBatch> after = null)
	{
		if (actionRunning)
		{
			Notify("Storage request pending…");
		}
		else if (SharedEnabled && ViewingShared(sharedView))
		{
			actionRunning = true;
			((MonoBehaviour)this).StartCoroutine(RunShared(sharedView, plan, message, null, after));
		}
	}

	private IEnumerator RunShared(Container chest, Func<InventoryRecord, InventoryRecord, TransferBatch> plan, string message, Func<Container, bool> setting = null, Action<TransferBatch> after = null)
	{
		Player player = Player.m_localPlayer;
		TransferJournal journal = null;
		string error = null;
		try
		{
			journal = Journal(player);
		}
		catch (Exception ex)
		{
			error = ex.Message;
		}
		if (error != null)
		{
			actionRunning = false;
			Notify(error);
			yield break;
		}
		if (journal.HasPending)
		{
			yield return CheckRecovery(player, journal);
			yield break;
		}
		ChestOwnership.Acquisition lease = new ChestOwnership.Acquisition();
		try
		{
			yield return ownership.Acquire(chest, player, lease);
			if (!lease.Granted)
			{
				Notify(lease.Error);
				yield break;
			}
			NativeTransferResult result = null;
			TransferBatch batch = null;
			try
			{
				if (!ViewingShared(chest) || !SharedAccess(chest, player) || (Object)(object)player != (Object)(object)Player.m_localPlayer)
				{
					yield break;
				}
				if (setting != null)
				{
					sharedSettingLease = chest;
					try
					{
						Notify(setting(chest) ? message : "Chest setting could not be changed.");
						yield break;
					}
					finally
					{
						sharedSettingLease = null;
					}
				}
				Func<InventoryRecord> func = () => NativeTransfer.Capture("player:" + player.GetPlayerID(), ((Humanoid)player).GetInventory(), bridge, 0L);
				Func<InventoryRecord> func2 = () => NativeTransfer.Capture(ContainerStorage.Identity(chest), chest.GetInventory(), bridge, 0L);
				batch = plan(func(), func2());
				if (batch.ItemCount == 0)
				{
					Notify("Nothing to move.");
					yield break;
				}
				result = NativeTransfer.Execute(batch, new NativeInventoryEndpoint[2]
				{
					new NativeInventoryEndpoint(batch.Before[0].Id, ((Humanoid)player).GetInventory(), () => StorageReady && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead() && !((Character)player).IsTeleporting(), func, allowPickupMark: true),
					new NativeInventoryEndpoint(batch.Before[1].Id, chest.GetInventory(), () => ChestOwnership.Holds(chest, lease.Operation) && SharedAccess(chest, player), func2)
				}, journal.Prepare, DeferCompletion(journal));
				if ((Object)(object)InventoryGui.instance != (Object)null)
				{
					ClearDrag.Invoke(InventoryGui.instance, new object[3] { null, null, 1 });
				}
			}
			catch (Exception ex)
			{
				if ((Object)(object)InventoryGui.instance != (Object)null)
				{
					ClearDrag.Invoke(InventoryGui.instance, new object[3] { null, null, 1 });
				}
				Notify(ex.Message);
			}
			if (result == null)
			{
				yield break;
			}
			yield return ConfirmTransfer(player, chest, journal, result);
			try
			{
				if (result.Committed)
				{
					InvalidateMaterials();
					scanner.Invalidate(chest);
					Notify(message ?? ("Moved " + result.Items + " items."));
					after?.Invoke(batch);
				}
				else
				{
					Notify(result.NeedsRecovery ? "Transfer outcome needs recovery. Do not retry; reopen storage after checking inventories." : result.Error);
				}
			}
			catch (Exception ex)
			{
				if ((Object)(object)InventoryGui.instance != (Object)null)
				{
					ClearDrag.Invoke(InventoryGui.instance, new object[3] { null, null, 1 });
				}
				Notify(ex.Message);
			}
		}
		finally
		{
			ChestOwnership.Release(chest, lease.Operation);
			actionRunning = false;
		}
	}

	private float ConfiguredRadius(int kind, float local)
	{
		return ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && StorageReady && serverRanges != null) ? serverRanges[kind] : local;
	}

	private void TickStorageNetwork()
	{
		TickObservations();
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			serverConfirmation = false;
			storageServer = null;
			serverRanges = null;
			storageConnections.Clear();
		}
		else
		{
			if (Time.realtimeSinceStartup < nextNetworkScan)
			{
				return;
			}
			nextNetworkScan = Time.realtimeSinceStartup + 0.5f;
			List<ZNetPeer> peers = ZNet.instance.GetPeers();
			bool flag = ZNet.instance.IsServer();
			ZRpc[] array = storageConnections.Keys.Where((ZRpc r) => !peers.Any((ZNetPeer p) => p.m_rpc == r)).ToArray();
			foreach (ZRpc key in array)
			{
				storageConnections.Remove(key);
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (!flag && (serverPeer == null || serverPeer.m_rpc != storageServer))
			{
				storageServer = null;
				serverRanges = null;
			}
			foreach (ZNetPeer item in peers)
			{
				if (item.m_rpc == null)
				{
					continue;
				}
				if (!storageConnections.ContainsKey(item.m_rpc))
				{
					storageConnections.Add(item.m_rpc, 0f);
					if (flag)
					{
						item.m_rpc.Register<int>("VCQD_HelloV1", (Action<ZRpc, int>)ReceiveStorageHello);
						item.m_rpc.Register<string>("