Decompiled source of CraftFromContainers v0.1.4

BepInEx/plugins/CraftFromContainers/CraftFromContainers.dll

Decompiled 6 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("CraftFromContainers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.4.0")]
[assembly: AssemblyInformationalVersion("0.1.4+1e02ab00b7a5cdc2c410824a9588f86bf4118091")]
[assembly: AssemblyProduct("CraftFromContainers")]
[assembly: AssemblyTitle("CraftFromContainers")]
[assembly: AssemblyVersion("0.1.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CraftFromContainers
{
	internal static class ContainerIndex
	{
		private sealed class Entry
		{
			internal readonly Container Container;

			internal readonly Inventory Inventory;

			internal readonly ZNetView View;

			internal readonly Action OnChanged;

			internal readonly Dictionary<ItemKey, int> Amounts = new Dictionary<ItemKey, int>();

			internal readonly Dictionary<ItemKey, ItemData> Items = new Dictionary<ItemKey, ItemData>();

			internal readonly Dictionary<QueryKey, LinkedListNode<ItemData>> Nodes = new Dictionary<QueryKey, LinkedListNode<ItemData>>();

			internal bool Registered = true;

			internal bool NeedsRebuild = true;

			internal bool Active;

			internal bool Queued;

			internal bool AccessErrorReported;

			internal bool LoadErrorReported;

			internal Entry(Container container, Inventory inventory, ZNetView view)
			{
				Container = container;
				Inventory = inventory;
				View = view;
				OnChanged = delegate
				{
					MarkDirty(this);
				};
			}
		}

		private readonly struct QueryKey : IEquatable<QueryKey>
		{
			private readonly string? _name;

			private readonly int _quality;

			internal QueryKey(string? name, int quality)
			{
				_name = name;
				_quality = ((quality < 0) ? (-1) : quality);
			}

			public bool Equals(QueryKey other)
			{
				if (_quality == other._quality)
				{
					return _name == other._name;
				}
				return false;
			}

			public override bool Equals(object? other)
			{
				if (other is QueryKey other2)
				{
					return Equals(other2);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return ((_name?.GetHashCode() ?? 0) * 397) ^ _quality;
			}
		}

		private readonly struct ItemKey : IEquatable<ItemKey>
		{
			internal readonly string Name;

			internal readonly int Quality;

			internal readonly int WorldLevel;

			internal ItemKey(string name, int quality, int worldLevel)
			{
				Name = name;
				Quality = quality;
				WorldLevel = worldLevel;
			}

			public bool Equals(ItemKey other)
			{
				if (Quality == other.Quality && WorldLevel == other.WorldLevel)
				{
					return Name == other.Name;
				}
				return false;
			}

			public override bool Equals(object? other)
			{
				if (other is ItemKey other2)
				{
					return Equals(other2);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (((Name.GetHashCode() * 397) ^ Quality) * 397) ^ WorldLevel;
			}
		}

		private static readonly Func<Container, long, bool> CheckAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Container, bool> LoadInventory = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldRef<Container, ZNetView> GetView = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private static readonly FieldRef<Container, bool> IsLoading = AccessTools.FieldRefAccess<Container, bool>("m_loading");

		private static readonly Dictionary<Container, Entry> Lookup = new Dictionary<Container, Entry>();

		private static readonly List<Entry> Entries = new List<Entry>();

		private static readonly List<Entry> NearbyEntries = new List<Entry>();

		private static readonly List<Entry> Dirty = new List<Entry>();

		private static readonly Dictionary<QueryKey, int> AllWorldCounts = new Dictionary<QueryKey, int>();

		private static readonly Dictionary<QueryKey, int> CurrentWorldCounts = new Dictionary<QueryKey, int>();

		private static readonly Dictionary<QueryKey, LinkedList<ItemData>> Candidates = new Dictionary<QueryKey, LinkedList<ItemData>>();

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

		private static int _preparedFrame = -1;

		private static int _worldLevel = int.MinValue;

		private static Player? _player;

		private static float _nextRefresh;

		private static float _lastRadius = -1f;

		private static bool _refreshRequested = true;

		private static bool _preparing;

		private static int _activeCount;

		private static long _queries;

		private static long _refreshes;

		private static long _rebuilds;

		internal static long Revision { get; private set; }

		internal static string Diagnostics => $"containers={Entries.Count}, nearby={_activeCount}, queries={_queries}, " + $"refreshes={_refreshes}, inventoryRebuilds={_rebuilds}, revision={Revision}";

		internal static void Register(Container container)
		{
			if (Object.op_Implicit((Object)(object)container) && !Lookup.ContainsKey(container) && !container.m_autoDestroyEmpty && !Object.op_Implicit((Object)(object)((Component)container).GetComponent<TombStone>()))
			{
				Inventory inventory = container.GetInventory();
				ZNetView val = GetView.Invoke(container);
				if (inventory != null && Object.op_Implicit((Object)(object)val) && val.IsValid())
				{
					Entry entry = new Entry(container, inventory, val);
					Lookup.Add(container, entry);
					Entries.Add(entry);
					inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, entry.OnChanged);
					_refreshRequested = true;
				}
			}
		}

		internal static void Unregister(Container container)
		{
			if (container != null && Lookup.TryGetValue(container, out Entry value))
			{
				RemoveEntry(value);
				Entries.Remove(value);
			}
		}

		internal static void Reset()
		{
			foreach (Entry entry in Entries)
			{
				Inventory inventory = entry.Inventory;
				inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, entry.OnChanged);
			}
			Entries.Clear();
			NearbyEntries.Clear();
			Lookup.Clear();
			Dirty.Clear();
			AllWorldCounts.Clear();
			CurrentWorldCounts.Clear();
			Candidates.Clear();
			SourcesByName.Clear();
			_player = null;
			_activeCount = 0;
			_worldLevel = int.MinValue;
			_preparedFrame = -1;
			_nextRefresh = 0f;
			_refreshRequested = true;
			_preparing = false;
			Revision++;
		}

		internal static void Tick()
		{
			Prepare();
		}

		internal static int Count(string? name, int quality = -1, bool matchWorldLevel = true)
		{
			Prepare();
			_queries++;
			QueryKey key = new QueryKey(name, quality);
			if (!(matchWorldLevel ? CurrentWorldCounts : AllWorldCounts).TryGetValue(key, out var value))
			{
				return 0;
			}
			return value;
		}

		internal static ItemData? Find(string name, int quality = -1)
		{
			Prepare();
			_queries++;
			if (!Candidates.TryGetValue(new QueryKey(name, quality), out LinkedList<ItemData> value))
			{
				return null;
			}
			return value.First?.Value;
		}

		internal static List<Container> GetNearby()
		{
			Prepare();
			List<Container> list = new List<Container>(_activeCount);
			foreach (Entry nearbyEntry in NearbyEntries)
			{
				if (nearbyEntry.Active && CanAccess(nearbyEntry.Container))
				{
					list.Add(nearbyEntry.Container);
				}
			}
			return list;
		}

		internal static List<Container> GetSourcesFor(IEnumerable<string> names)
		{
			Prepare();
			List<Container> list = new List<Container>();
			HashSet<Entry> hashSet = new HashSet<Entry>();
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			foreach (string name in names)
			{
				if (name == null || !hashSet2.Add(name) || !SourcesByName.TryGetValue(name, out HashSet<Entry> value))
				{
					continue;
				}
				foreach (Entry item in value)
				{
					if (hashSet.Add(item) && item.Active && CanAccess(item.Container))
					{
						list.Add(item.Container);
					}
				}
			}
			return list;
		}

		internal static void ForceRefresh()
		{
			_refreshRequested = true;
			Prepare();
		}

		internal static bool CanAccess(Container container, bool ignoreInUse = false)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)container) || !Lookup.TryGetValue(container, out Entry value))
			{
				return false;
			}
			return CanAccess(value, localPlayer, ((Component)localPlayer).transform.position, RadiusSquared(), ignoreInUse);
		}

		private static float RadiusSquared()
		{
			float num = Mathf.Max(0f, Plugin.SearchRadius.Value);
			return num * num;
		}

		private static bool CanAccess(Entry entry, Player player, Vector3 position, float radiusSquared, bool ignoreInUse)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			Container container = entry.Container;
			if (Object.op_Implicit((Object)(object)container) && Object.op_Implicit((Object)(object)entry.View) && entry.View.IsValid() && ((Component)container).gameObject.activeInHierarchy)
			{
				Vector3 val = ((Component)container).transform.position - position;
				if (!(((Vector3)(ref val)).sqrMagnitude > radiusSquared))
				{
					if (!ignoreInUse && (container.IsInUse() || entry.View.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0 || ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse())))
					{
						return false;
					}
					try
					{
						return CheckAccess(container, player.GetPlayerID()) && (!container.m_checkGuardStone || PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false));
					}
					catch (Exception ex)
					{
						if (!entry.AccessErrorReported)
						{
							entry.AccessErrorReported = true;
							Plugin.Log.LogWarning((object)("Skipping container '" + ((Object)container).name + "': access check failed: " + ex.Message));
						}
						return false;
					}
				}
			}
			return false;
		}

		private static void Prepare()
		{
			if (_preparing)
			{
				return;
			}
			int frameCount = Time.frameCount;
			if (_preparedFrame == frameCount && !_refreshRequested && Dirty.Count == 0)
			{
				return;
			}
			_preparing = true;
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)(object)_player)
				{
					_player = localPlayer;
					_refreshRequested = true;
				}
				if (_worldLevel != Game.m_worldLevel)
				{
					_worldLevel = Game.m_worldLevel;
					AllWorldCounts.Clear();
					CurrentWorldCounts.Clear();
					Candidates.Clear();
					SourcesByName.Clear();
					foreach (Entry entry in Entries)
					{
						entry.Nodes.Clear();
						if (entry.Active)
						{
							AddContributions(entry);
						}
					}
					Revision++;
				}
				float value = Plugin.SearchRadius.Value;
				if (value != _lastRadius)
				{
					_lastRadius = value;
					_refreshRequested = true;
				}
				if (_refreshRequested || Time.unscaledTime >= _nextRefresh)
				{
					_refreshRequested = false;
					_nextRefresh = Time.unscaledTime + Mathf.Max(0.1f, Plugin.RefreshInterval.Value);
					RefreshNearby(localPlayer);
				}
				DrainDirty();
				_preparedFrame = frameCount;
			}
			finally
			{
				_preparing = false;
			}
		}

		private static void RefreshNearby(Player player)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			_refreshes++;
			Vector3 position = (Object.op_Implicit((Object)(object)player) ? ((Component)player).transform.position : Vector3.zero);
			float radiusSquared = RadiusSquared();
			for (int num = Entries.Count - 1; num >= 0; num--)
			{
				Entry entry = Entries[num];
				if (!Object.op_Implicit((Object)(object)entry.Container) || !Object.op_Implicit((Object)(object)entry.View) || !entry.View.IsValid())
				{
					RemoveEntry(entry);
					Entries.RemoveAt(num);
					continue;
				}
				if (!Object.op_Implicit((Object)(object)player) || !CanAccess(entry, player, position, radiusSquared, ignoreInUse: false))
				{
					if (entry.Active)
					{
						RemoveContributions(entry);
						entry.Active = false;
						NearbyEntries.Remove(entry);
						_activeCount--;
						Revision++;
					}
					continue;
				}
				try
				{
					LoadInventory(entry.Container);
				}
				catch (Exception ex)
				{
					if (!entry.LoadErrorReported)
					{
						entry.LoadErrorReported = true;
						Plugin.Log.LogWarning((object)("Could not refresh '" + ((Object)entry.Container).name + "': " + ex.Message));
					}
					if (entry.Active)
					{
						RemoveContributions(entry);
						entry.Active = false;
						NearbyEntries.Remove(entry);
						_activeCount--;
						Revision++;
					}
					continue;
				}
				if (!entry.Active && !IsLoading.Invoke(entry.Container))
				{
					if (entry.NeedsRebuild)
					{
						Rebuild(entry);
					}
					entry.Active = true;
					NearbyEntries.Add(entry);
					_activeCount++;
					AddContributions(entry);
					Revision++;
				}
			}
		}

		private static void MarkDirty(Entry entry)
		{
			entry.NeedsRebuild = true;
			if (entry.Registered && entry.Active && !entry.Queued)
			{
				entry.Queued = true;
				Dirty.Add(entry);
			}
		}

		private static void DrainDirty()
		{
			for (int num = Dirty.Count - 1; num >= 0; num--)
			{
				Entry entry = Dirty[num];
				if (!entry.Registered || !entry.Active || !Object.op_Implicit((Object)(object)entry.Container) || !IsLoading.Invoke(entry.Container))
				{
					Dirty.RemoveAt(num);
					entry.Queued = false;
					if (entry.Registered && entry.Active && Object.op_Implicit((Object)(object)entry.Container) && entry.NeedsRebuild)
					{
						Rebuild(entry);
					}
				}
			}
		}

		private static void Rebuild(Entry entry)
		{
			if (entry.Active)
			{
				RemoveContributions(entry);
			}
			entry.Amounts.Clear();
			entry.Items.Clear();
			foreach (ItemData allItem in entry.Inventory.GetAllItems())
			{
				if (allItem != null && allItem.m_shared != null)
				{
					ItemKey key = new ItemKey(allItem.m_shared.m_name, allItem.m_quality, allItem.m_worldLevel);
					entry.Amounts.TryGetValue(key, out var value);
					entry.Amounts[key] = value + allItem.m_stack;
					if (!entry.Items.ContainsKey(key))
					{
						entry.Items.Add(key, allItem);
					}
				}
			}
			entry.NeedsRebuild = false;
			_rebuilds++;
			if (entry.Active)
			{
				AddContributions(entry);
			}
			Revision++;
		}

		private static void AddContributions(Entry entry)
		{
			foreach (KeyValuePair<ItemKey, int> amount in entry.Amounts)
			{
				AddAmount(AllWorldCounts, amount.Key, amount.Value);
				if (amount.Key.WorldLevel >= _worldLevel)
				{
					AddAmount(CurrentWorldCounts, amount.Key, amount.Value);
				}
			}
			foreach (KeyValuePair<ItemKey, ItemData> item in entry.Items)
			{
				if (item.Key.WorldLevel >= _worldLevel)
				{
					if (!SourcesByName.TryGetValue(item.Key.Name, out HashSet<Entry> value))
					{
						value = new HashSet<Entry>();
						SourcesByName.Add(item.Key.Name, value);
					}
					value.Add(entry);
					AddCandidate(entry, new QueryKey(item.Key.Name, item.Key.Quality), item.Value);
					AddCandidate(entry, new QueryKey(item.Key.Name, -1), item.Value);
				}
			}
		}

		private static void RemoveContributions(Entry entry)
		{
			foreach (KeyValuePair<ItemKey, int> amount in entry.Amounts)
			{
				AddAmount(AllWorldCounts, amount.Key, -amount.Value);
				if (amount.Key.WorldLevel >= _worldLevel)
				{
					AddAmount(CurrentWorldCounts, amount.Key, -amount.Value);
				}
			}
			foreach (KeyValuePair<QueryKey, LinkedListNode<ItemData>> node in entry.Nodes)
			{
				LinkedList<ItemData>? list = node.Value.List;
				list.Remove(node.Value);
				if (list.Count == 0)
				{
					Candidates.Remove(node.Key);
				}
			}
			entry.Nodes.Clear();
			foreach (ItemKey key in entry.Items.Keys)
			{
				if (SourcesByName.TryGetValue(key.Name, out HashSet<Entry> value))
				{
					value.Remove(entry);
					if (value.Count == 0)
					{
						SourcesByName.Remove(key.Name);
					}
				}
			}
		}

		private static void AddAmount(Dictionary<QueryKey, int> counts, ItemKey key, int delta)
		{
			AddAmount(counts, new QueryKey(key.Name, key.Quality), delta);
			AddAmount(counts, new QueryKey(key.Name, -1), delta);
			AddAmount(counts, new QueryKey(null, key.Quality), delta);
			AddAmount(counts, new QueryKey(null, -1), delta);
		}

		private static void AddAmount(Dictionary<QueryKey, int> counts, QueryKey key, int delta)
		{
			counts.TryGetValue(key, out var value);
			int num = value + delta;
			if (num == 0)
			{
				counts.Remove(key);
			}
			else
			{
				counts[key] = num;
			}
		}

		private static void AddCandidate(Entry entry, QueryKey key, ItemData item)
		{
			if (!entry.Nodes.ContainsKey(key))
			{
				if (!Candidates.TryGetValue(key, out LinkedList<ItemData> value))
				{
					value = new LinkedList<ItemData>();
					Candidates.Add(key, value);
				}
				entry.Nodes.Add(key, value.AddLast(item));
			}
		}

		private static void RemoveEntry(Entry entry)
		{
			Inventory inventory = entry.Inventory;
			inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, entry.OnChanged);
			entry.Registered = false;
			if (entry.Active)
			{
				RemoveContributions(entry);
				entry.Active = false;
				NearbyEntries.Remove(entry);
				_activeCount--;
				Revision++;
			}
			Lookup.Remove(entry.Container);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")]
	internal static class CookingStationFuelPatch
	{
		private static readonly Func<CookingStation, Switch, Humanoid, ItemData, bool> Add = AccessTools.MethodDelegate<Func<CookingStation, Switch, Humanoid, ItemData, bool>>(AccessTools.Method(typeof(CookingStation), "OnAddFuelSwitch", (Type[])null, (Type[])null), (object)null, true);

		private static bool Prefix(CookingStation __instance, Switch sw, Humanoid user, ItemData item, ZNetView ___m_nview, ref bool __result)
		{
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			if (item == null)
			{
				Player player = (Player)(object)((user is Player) ? user : null);
				if (player != null && ResourceActions.Local(player) && !InventoryBridge.Executing)
				{
					CookingStation station = __instance;
					ItemDrop fuel = station.m_fuelItem;
					ZNetView view = ___m_nview;
					if (!station.m_useFuel || !Object.op_Implicit((Object)(object)sw) || (Object)(object)station.m_addFuelSwitch != (Object)(object)sw || !Object.op_Implicit((Object)(object)fuel) || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || view.GetZDO() == null)
					{
						return true;
					}
					string name = fuel.m_itemData.m_shared.m_name;
					if (view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > (float)(station.m_maxFuel - 1) || ((Humanoid)player).GetInventory().HaveItem(name, true) || ContainerIndex.Count(name) <= 0)
					{
						return true;
					}
					Vector3 start = ((Component)station).transform.InverseTransformPoint(((Component)player).transform.position);
					bool num = ResourceActions.Begin(player, () => new List<ResourcePlanner.Demand>
					{
						new ResourcePlanner.Demand(name, 1)
					}, delegate
					{
						//IL_0102: Unknown result type (might be due to invalid IL or missing references)
						//IL_0107: Unknown result type (might be due to invalid IL or missing references)
						//IL_010d: Unknown result type (might be due to invalid IL or missing references)
						//IL_0112: Unknown result type (might be due to invalid IL or missing references)
						//IL_0117: Unknown result type (might be due to invalid IL or missing references)
						if (Object.op_Implicit((Object)(object)station) && Object.op_Implicit((Object)(object)sw) && station.m_useFuel && (Object)(object)station.m_addFuelSwitch == (Object)(object)sw && (Object)(object)station.m_fuelItem == (Object)(object)fuel && Object.op_Implicit((Object)(object)fuel) && fuel.m_itemData.m_shared.m_name == name && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.GetZDO() != null && view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) <= (float)(station.m_maxFuel - 1))
						{
							Vector3 val = ((Component)station).transform.InverseTransformPoint(((Component)player).transform.position) - start;
							return ((Vector3)(ref val)).sqrMagnitude < 1f;
						}
						return false;
					}, delegate
					{
						Add(station, sw, (Humanoid)(object)player, null);
					});
					if (!num)
					{
						__result = false;
					}
					return num;
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")]
	internal static class CraftingPanelRefresh
	{
		private static readonly Action<InventoryGui, bool> Rebuild = AccessTools.MethodDelegate<Action<InventoryGui, bool>>(AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null), (object)null, true);

		private static InventoryGui? _gui;

		private static long _builtRevision = -1L;

		private static void Prefix()
		{
			ContainerIndex.Tick();
		}

		private static void Postfix(InventoryGui __instance)
		{
			_gui = __instance;
			_builtRevision = ContainerIndex.Revision;
		}

		internal static void Tick()
		{
			InventoryGui instance = InventoryGui.instance;
			if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && InventoryGui.IsVisible() && (instance != _gui || _builtRevision != ContainerIndex.Revision))
			{
				Rebuild(instance, arg2: false);
			}
		}

		internal static void Reset()
		{
			_gui = null;
			_builtRevision = -1L;
		}
	}
	[HarmonyPatch(typeof(Fireplace), "Interact")]
	internal static class FireplaceActionPatch
	{
		private static bool Prefix(Fireplace __instance, Humanoid user, bool hold, bool alt, ZNetView ___m_nview, float ___m_lastUseTime, ref bool __result)
		{
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			Player player = (Player)(object)((user is Player) ? user : null);
			if (player == null || !ResourceActions.Local(player) || InventoryBridge.Executing)
			{
				return true;
			}
			Fireplace fireplace = __instance;
			if (hold && (fireplace.m_holdRepeatInterval <= 0f || Time.time - ___m_lastUseTime < fireplace.m_holdRepeatInterval))
			{
				return true;
			}
			ZNetView view = ___m_nview;
			if (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || view.GetZDO() == null)
			{
				__result = false;
				return false;
			}
			if (WouldToggle(fireplace, view, hold, alt) || !fireplace.m_canRefill || fireplace.m_infiniteFuel)
			{
				return true;
			}
			ItemDrop fuel = fireplace.m_fuelItem;
			if (!Object.op_Implicit((Object)(object)fuel))
			{
				__result = false;
				return false;
			}
			string name = fuel.m_itemData.m_shared.m_name;
			if (Full(fireplace, view) || ((Humanoid)player).GetInventory().HaveItem(name, true) || ContainerIndex.Count(name) <= 0)
			{
				return true;
			}
			Vector3 start = ((Component)fireplace).transform.InverseTransformPoint(((Component)player).transform.position);
			bool num = ResourceActions.Begin(player, () => new List<ResourcePlanner.Demand>
			{
				new ResourcePlanner.Demand(name, 1)
			}, delegate
			{
				//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)fireplace) && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.GetZDO() != null && fireplace.m_canRefill && !fireplace.m_infiniteFuel && (Object)(object)fireplace.m_fuelItem == (Object)(object)fuel && Object.op_Implicit((Object)(object)fuel) && fuel.m_itemData.m_shared.m_name == name)
				{
					Vector3 val = ((Component)fireplace).transform.InverseTransformPoint(((Component)player).transform.position) - start;
					if (((Vector3)(ref val)).sqrMagnitude < 1f && !Full(fireplace, view))
					{
						return !WouldToggle(fireplace, view, hold, alt);
					}
				}
				return false;
			}, delegate
			{
				fireplace.Interact((Humanoid)(object)player, hold, alt);
			});
			if (!num)
			{
				__result = false;
			}
			return num;
		}

		private static bool WouldToggle(Fireplace fireplace, ZNetView view, bool hold, bool alt)
		{
			if (fireplace.m_canTurnOff && !hold && !alt)
			{
				return view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > 0f;
			}
			return false;
		}

		private static bool Full(Fireplace fireplace, ZNetView view)
		{
			return (float)Mathf.CeilToInt(view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f)) >= fireplace.m_maxFuel;
		}
	}
	internal static class InventoryBridge
	{
		private static int _depth;

		internal static ResourceTransaction? Transaction;

		internal static List<Container>? RestrictedSources;

		internal static bool Executing;

		internal static bool IsActive(Inventory inventory)
		{
			if (_depth > 0 && Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return inventory == ((Humanoid)Player.m_localPlayer).GetInventory();
			}
			return false;
		}

		internal static int Enter()
		{
			int depth = _depth;
			_depth++;
			return depth;
		}

		internal static void Leave(int previous)
		{
			_depth = previous;
		}

		internal static void Reset()
		{
			_depth = 0;
			Transaction = null;
			RestrictedSources = null;
			Executing = false;
		}

		internal static int ChestCount(string name, int quality, bool worldLevel)
		{
			if (RestrictedSources == null)
			{
				return ContainerIndex.Count(name, quality, worldLevel);
			}
			int num = 0;
			foreach (Container restrictedSource in RestrictedSources)
			{
				if (Object.op_Implicit((Object)(object)restrictedSource) && restrictedSource.GetInventory() != null)
				{
					num += restrictedSource.GetInventory().CountItems(name, quality, worldLevel);
				}
			}
			return num;
		}

		internal static ItemData? Find(string name, int quality, bool prefab = false)
		{
			if (RestrictedSources == null && !prefab)
			{
				return ContainerIndex.Find(name, quality);
			}
			foreach (Container item in RestrictedSources ?? ContainerIndex.GetNearby())
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					Inventory inventory = item.GetInventory();
					ItemData val = ((inventory != null) ? inventory.GetItem(name, quality, prefab) : null);
					if (val != null)
					{
						return val;
					}
				}
			}
			return null;
		}
	}
	[HarmonyPatch]
	internal static class ResourceReadScopePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Player), "HaveRequirementItems", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2]
			{
				typeof(Piece),
				typeof(RequirementMode)
			}, (Type[])null);
			yield return AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null);
		}

		private static void Prefix(out int __state)
		{
			__state = InventoryBridge.Enter();
		}

		private static void Finalizer(int __state)
		{
			InventoryBridge.Leave(__state);
		}
	}
	[HarmonyPatch(typeof(Inventory), "CountItems")]
	internal static class CountItemsPatch
	{
		private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
		{
			if (InventoryBridge.IsActive(__instance))
			{
				__result += InventoryBridge.ChestCount(name, quality, matchWorldLevel);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
	{
		typeof(string),
		typeof(bool)
	})]
	internal static class HaveItemPatch
	{
		private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
		{
			if (!__result && InventoryBridge.IsActive(__instance))
			{
				__result = InventoryBridge.ChestCount(name, -1, matchWorldLevel) > 0;
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "GetItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(bool)
	})]
	internal static class GetItemPatch
	{
		private static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result)
		{
			if (__result == null && InventoryBridge.IsActive(__instance))
			{
				__result = InventoryBridge.Find(name, quality, isPrefabName);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class RemoveByNamePatch
	{
		private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			if (InventoryBridge.Transaction == null || !InventoryBridge.IsActive(__instance))
			{
				return true;
			}
			InventoryBridge.Transaction.Consume(name, amount, itemQuality, worldLevelBased);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	internal static class RemoveByItemPatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, int amount, ref bool __result)
		{
			if (InventoryBridge.Transaction == null || !InventoryBridge.IsActive(__instance) || __instance.ContainsItem(item))
			{
				return true;
			}
			InventoryBridge.Transaction.Consume(item.m_shared.m_name, amount, item.m_quality, worldLevelBased: true);
			__result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(bool)
	})]
	internal static class CheatedResourcePatch
	{
		private static void Postfix(Inventory __instance, ref bool __result)
		{
			if (InventoryBridge.IsActive(__instance) && InventoryBridge.Transaction != null)
			{
				__result |= InventoryBridge.Transaction.Cheated;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	internal static class RequirementTextPatch
	{
		private static readonly Dictionary<Transform, TMP_Text> Labels = new Dictionary<Transform, TMP_Text>();

		private static void Postfix(Transform elementRoot, Requirement req, Player player, int quality, int craftMultiplier, bool __result)
		{
			if (!Plugin.ShowAvailable.Value || !__result || !Object.op_Implicit((Object)(object)req.m_resItem) || !Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			int num = req.GetAmount(quality) * craftMultiplier;
			if (num <= 0)
			{
				return;
			}
			if (!Labels.TryGetValue(elementRoot, out TMP_Text value) || !Object.op_Implicit((Object)(object)value))
			{
				Transform obj = elementRoot.Find("res_amount");
				value = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
				if (!Object.op_Implicit((Object)(object)value))
				{
					return;
				}
				Labels[elementRoot] = value;
			}
			int num2 = ((Humanoid)player).GetInventory().CountItems(req.m_resItem.m_itemData.m_shared.m_name, -1, true);
			string text = num + "/" + num2;
			if (value.text != text)
			{
				value.text = text;
			}
		}

		internal static void Clear()
		{
			Labels.Clear();
		}
	}
	internal sealed class InventorySnapshot
	{
		private sealed class InventoryState
		{
			internal readonly Inventory Inventory;

			internal readonly Container? Container;

			internal readonly List<ItemState> Items = new List<ItemState>();

			internal InventoryState(Inventory inventory, Container? container)
			{
				Inventory = inventory;
				Container = container;
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					Items.Add(new ItemState(allItem));
				}
			}

			internal void RestoreItems()
			{
				List<ItemData> allItems = Inventory.GetAllItems();
				foreach (ItemState item in Items)
				{
					item.Restore();
				}
				allItems.Clear();
				foreach (ItemState item2 in Items)
				{
					allItems.Add(item2.Original);
				}
			}
		}

		private sealed class ItemState
		{
			internal readonly ItemData Original;

			internal readonly ItemData Copy;

			private readonly Dictionary<string, string> _customData;

			private readonly FieldInfo[] _fields;

			internal ItemState(ItemData item)
			{
				Original = item;
				Copy = item.Clone();
				_customData = item.m_customData;
				_fields = FieldsFor(((object)item).GetType());
			}

			internal void Restore()
			{
				FieldInfo[] fields = _fields;
				foreach (FieldInfo fieldInfo in fields)
				{
					fieldInfo.SetValue(Original, fieldInfo.GetValue(Copy));
				}
				_customData.Clear();
				foreach (KeyValuePair<string, string> customDatum in Copy.m_customData)
				{
					_customData.Add(customDatum.Key, customDatum.Value);
				}
				Original.m_customData = _customData;
			}
		}

		private static readonly Action<Inventory, bool, bool> NotifyChanged = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null), (object)null, true);

		private static readonly Action<Container> SaveContainer = AccessTools.MethodDelegate<Action<Container>>(AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<Humanoid> SetupEquipment = AccessTools.MethodDelegate<Action<Humanoid>>(AccessTools.Method(typeof(Humanoid), "SetupEquipment", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldInfo[] EquipmentFields = GetEquipmentFields();

		private static readonly FieldInfo HiddenLeft = AccessTools.Field(typeof(Humanoid), "m_hiddenLeftItem");

		private static readonly FieldInfo HiddenRight = AccessTools.Field(typeof(Humanoid), "m_hiddenRightItem");

		private static readonly Dictionary<Type, FieldInfo[]> ItemFields = new Dictionary<Type, FieldInfo[]>();

		private readonly Player _player;

		private readonly List<InventoryState> _inventories = new List<InventoryState>();

		private readonly ItemData?[] _equipment = (ItemData?[])(object)new ItemData[EquipmentFields.Length];

		private readonly ItemData? _hiddenLeft;

		private readonly ItemData? _hiddenRight;

		private bool _restored;

		internal InventorySnapshot(Player player, IEnumerable<Container> sources)
		{
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)player))
			{
				throw new ArgumentNullException("player");
			}
			_player = player;
			HashSet<Inventory> hashSet = new HashSet<Inventory>();
			Inventory inventory = ((Humanoid)player).GetInventory();
			_inventories.Add(new InventoryState(inventory, null));
			hashSet.Add(inventory);
			foreach (Container source in sources)
			{
				if (!Object.op_Implicit((Object)(object)source) || !NetworkLeases.Owns(source))
				{
					throw new InvalidOperationException("Cannot snapshot a container without its current synchronized lease.");
				}
				Inventory inventory2 = source.GetInventory();
				if (inventory2 == null)
				{
					throw new InvalidOperationException("A source inventory disappeared before snapshot.");
				}
				if (hashSet.Add(inventory2))
				{
					_inventories.Add(new InventoryState(inventory2, source));
				}
			}
			for (int i = 0; i < EquipmentFields.Length; i++)
			{
				_equipment[i] = (ItemData)EquipmentFields[i].GetValue(player);
			}
			_hiddenLeft = (ItemData)HiddenLeft.GetValue(player);
			_hiddenRight = (ItemData)HiddenRight.GetValue(player);
		}

		internal void Restore()
		{
			if (_restored)
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)_player) || ((Humanoid)_player).GetInventory() != _inventories[0].Inventory)
			{
				Fail("The player inventory changed before rollback.");
			}
			foreach (InventoryState inventory in _inventories)
			{
				if (inventory.Container != null && (!Object.op_Implicit((Object)(object)inventory.Container) || !NetworkLeases.Owns(inventory.Container)))
				{
					Fail("A container lease expired before rollback; refusing to overwrite another owner's inventory.");
				}
			}
			List<Exception> list = new List<Exception>();
			Attempt(delegate
			{
				((Humanoid)_player).UnequipAllItems();
			}, list);
			Attempt(delegate
			{
				HiddenLeft.SetValue(_player, null);
			}, list);
			Attempt(delegate
			{
				HiddenRight.SetValue(_player, null);
			}, list);
			foreach (InventoryState inventory2 in _inventories)
			{
				Attempt(inventory2.RestoreItems, list);
			}
			ItemData[] equipment = _equipment;
			foreach (ItemData item in equipment)
			{
				if (item != null)
				{
					Attempt(delegate
					{
						((Humanoid)_player).EquipItem(item, false);
					}, list);
				}
			}
			int index;
			for (index = 0; index < EquipmentFields.Length; index++)
			{
				Attempt(delegate
				{
					EquipmentFields[index].SetValue(_player, _equipment[index]);
				}, list);
			}
			Attempt(delegate
			{
				HiddenLeft.SetValue(_player, _hiddenLeft);
			}, list);
			Attempt(delegate
			{
				HiddenRight.SetValue(_player, _hiddenRight);
			}, list);
			foreach (ItemState item2 in _inventories[0].Items)
			{
				item2.Original.m_equipped = item2.Copy.m_equipped;
			}
			Attempt(delegate
			{
				SetupEquipment((Humanoid)(object)_player);
			}, list);
			foreach (InventoryState state in _inventories)
			{
				if (state.Container != null && (!Object.op_Implicit((Object)(object)state.Container) || !NetworkLeases.Owns(state.Container)))
				{
					list.Add(new InvalidOperationException("Container ownership changed during rollback; saving was refused."));
					continue;
				}
				Attempt(delegate
				{
					NotifyChanged(state.Inventory, arg2: false, arg3: false);
				}, list);
				if (state.Container == null)
				{
					continue;
				}
				if (Object.op_Implicit((Object)(object)state.Container) && NetworkLeases.Owns(state.Container))
				{
					Attempt(delegate
					{
						SaveContainer(state.Container);
					}, list);
				}
				else
				{
					list.Add(new InvalidOperationException("Container ownership changed before rollback save."));
				}
			}
			if (list.Count != 0)
			{
				AggregateException ex = new AggregateException("Inventory rollback could not complete every restoration step.", list);
				Plugin.Log.LogError((object)ex);
				throw ex;
			}
			_restored = true;
		}

		private static void Attempt(Action operation, List<Exception> failures)
		{
			try
			{
				operation();
			}
			catch (Exception item)
			{
				failures.Add(item);
			}
		}

		private static void Fail(string message)
		{
			InvalidOperationException ex = new InvalidOperationException(message);
			Plugin.Log.LogError((object)ex);
			throw ex;
		}

		private static FieldInfo[] GetEquipmentFields()
		{
			string[] array = new string[9] { "m_rightItem", "m_leftItem", "m_chestItem", "m_legItem", "m_helmetItem", "m_ammoItem", "m_shoulderItem", "m_utilityItem", "m_trinketItem" };
			FieldInfo[] array2 = new FieldInfo[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array2[i] = AccessTools.Field(typeof(Humanoid), array[i]) ?? throw new MissingFieldException(typeof(Humanoid).FullName, array[i]);
			}
			return array2;
		}

		private static FieldInfo[] FieldsFor(Type type)
		{
			if (ItemFields.TryGetValue(type, out FieldInfo[] value))
			{
				return value;
			}
			List<FieldInfo> list = new List<FieldInfo>();
			Type type2 = type;
			while (type2 != null && type2 != typeof(object))
			{
				FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (!fieldInfo.IsInitOnly && (!(fieldInfo.DeclaringType == typeof(ItemData)) || !(fieldInfo.Name == "m_customData")))
					{
						list.Add(fieldInfo);
					}
				}
				type2 = type2.BaseType;
			}
			value = list.ToArray();
			ItemFields.Add(type, value);
			return value;
		}
	}
	internal static class NetworkLeases
	{
		private sealed class Entry
		{
			internal Container Container;

			internal ZNetView View;

			internal long ExpectedOwner;

			internal byte[]? Snapshot;

			internal bool Synchronized;

			internal bool Requested;

			internal float RetryAfter;
		}

		private sealed class Session
		{
			internal readonly string Token = Guid.NewGuid().ToString("N");

			internal readonly List<Entry> Entries = new List<Entry>();

			internal Player Player;

			internal Action Ready;

			internal Action<string> Failure;

			internal float Started;

			internal bool Executing;

			internal bool AllowOwnOpen;
		}

		[HarmonyPatch]
		private static class NativeRequestPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Container), "RPC_RequestOpen", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Container), "RPC_RequestStack", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Container), "RPC_RequestTakeAll", (Type[])null, (Type[])null);
			}

			[HarmonyPriority(800)]
			[HarmonyBefore(new string[] { "com.maxsch.valheim.MultiUserChest" })]
			private static void Prefix(Container __instance)
			{
				YieldToContainerUse(__instance);
			}
		}

		[HarmonyPatch(typeof(Container), "RPC_TakeAllResponse")]
		private static class TakeAllResponsePatch
		{
			private static readonly Action<Container, long, bool> Respond = AccessTools.MethodDelegate<Action<Container, long, bool>>(AccessTools.Method(typeof(Container), "RPC_TakeAllResponse", (Type[])null, (Type[])null), (object)null, true);

			private static bool Prefix(Container __instance, long uid, bool granted)
			{
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				if (!granted || (Object)(object)replayingTakeAll == (Object)(object)__instance)
				{
					return true;
				}
				Player player = Player.m_localPlayer;
				if (!Object.op_Implicit((Object)(object)player))
				{
					return false;
				}
				Vector3 startedAt = ((Component)player).transform.position;
				bool startedOpen = Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)OpenContainerField.Invoke(InventoryGui.instance) == (Object)(object)__instance;
				if (current != null && current.AllowOwnOpen && current.Entries.Count == 1 && (Object)(object)current.Entries[0].Container == (Object)(object)__instance)
				{
					return false;
				}
				if (current == null || !current.Executing)
				{
					YieldToContainerUse(__instance);
				}
				if (current != null && !current.Executing)
				{
					Fail(current, "TakeAll replaced the pending resource operation. No resources were consumed.");
				}
				Acquire(player, new List<Container> { __instance }, delegate
				{
					//IL_0018: Unknown result type (might be due to invalid IL or missing references)
					//IL_001e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_0028: Unknown result type (might be due to invalid IL or missing references)
					if (Object.op_Implicit((Object)(object)player))
					{
						Vector3 val = ((Component)player).transform.position - startedAt;
						if (!(((Vector3)(ref val)).sqrMagnitude > 9f) && (!startedOpen || (Object.op_Implicit((Object)(object)InventoryGui.instance) && !((Object)(object)OpenContainerField.Invoke(InventoryGui.instance) != (Object)(object)__instance))))
						{
							replayingTakeAll = __instance;
							try
							{
								Respond(__instance, uid, arg3: true);
							}
							finally
							{
								replayingTakeAll = null;
							}
						}
					}
				}, delegate
				{
					if (Object.op_Implicit((Object)(object)player))
					{
						((Character)player).Message((MessageType)2, "$msg_cantopen", 0, (Sprite)null, false);
					}
				}, allowOwnOpen: true);
				return false;
			}
		}

		private const string RequestRpc = "CFC_RequestLease_v1";

		private const string TakeAllRequestRpc = "CFC_RequestTakeAllLease_v1";

		private const string ResponseRpc = "CFC_LeaseResponse_v1";

		private const string TokenKey = "cfc_lease_token_v1";

		private const string PeerKey = "cfc_lease_peer_v1";

		private const string UntilKey = "cfc_lease_until_v1";

		private const string MultiUserChestGuid = "com.maxsch.valheim.MultiUserChest";

		private const float AcquireTimeout = 8f;

		private const double LeaseSeconds = 15.0;

		private const long CommitMargin = 20000000L;

		private static readonly FieldRef<Container, ZNetView> ViewField = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private static readonly FieldRef<Container, bool> LoadingField = AccessTools.FieldRefAccess<Container, bool>("m_loading");

		private static readonly FieldRef<InventoryGui, Container> OpenContainerField = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

		private static readonly Func<Container, bool> LoadContainer = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<Container> SaveContainer = AccessTools.MethodDelegate<Action<Container>>(AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<Container> UpdateRows = AccessTools.MethodDelegate<Action<Container>>(AccessTools.Method(typeof(Container), "UpdateRows", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Container, long, bool> CheckPrivateAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldRef<PrivateArea, Piece> WardPiece = AccessTools.FieldRefAccess<PrivateArea, Piece>("m_piece");

		private static readonly Func<PrivateArea, bool> WardEnabled = AccessTools.MethodDelegate<Func<PrivateArea, bool>>(AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<PrivateArea, long, bool> WardPermitted = AccessTools.MethodDelegate<Func<PrivateArea, long, bool>>(AccessTools.Method(typeof(PrivateArea), "IsPermitted", (Type[])null, (Type[])null), (object)null, true);

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

		private static readonly Dictionary<Container, ZNetView> Registered = new Dictionary<Container, ZNetView>();

		private static readonly Dictionary<Inventory, Container> InventoryContainers = new Dictionary<Inventory, Container>();

		private static readonly HashSet<ZNetView> RegisteredViews = new HashSet<ZNetView>();

		private static readonly List<Container> RemovedContainers = new List<Container>();

		private static readonly List<Session> Abandoned = new List<Session>();

		private static Session? current;

		private static Container? replayingTakeAll;

		private static float nextPrune;

		internal static bool IsBusy => current != null;

		private static long Now
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)ZNet.instance))
				{
					return 0L;
				}
				return ZNet.instance.GetTime().Ticks;
			}
		}

		internal static void Register(Container container)
		{
			if (!Object.op_Implicit((Object)(object)container) || Registered.ContainsKey(container))
			{
				return;
			}
			ZNetView val = ViewField.Invoke(container);
			if (Object.op_Implicit((Object)(object)val) && val.IsValid() && container.GetInventory() != null && !RegisteredViews.Contains(val))
			{
				val.Register<long, string>("CFC_RequestLease_v1", (Action<long, long, string>)delegate(long sender, long playerId, string token)
				{
					Request(container, sender, playerId, token);
				});
				val.Register<long, string>("CFC_RequestTakeAllLease_v1", (Action<long, long, string>)delegate(long sender, long playerId, string token)
				{
					Request(container, sender, playerId, token, allowOwnOpen: true);
				});
				val.Register<string, bool, ZPackage>("CFC_LeaseResponse_v1", (Action<long, string, bool, ZPackage>)delegate(long sender, string token, bool granted, ZPackage snapshot)
				{
					Response(container, sender, token, granted, snapshot);
				});
				Registered.Add(container, val);
				InventoryContainers[container.GetInventory()] = container;
				RegisteredViews.Add(val);
			}
		}

		internal static void Unregister(Container container)
		{
			if (Registered.TryGetValue(container, out ZNetView value))
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					value.Unregister("CFC_RequestLease_v1");
					value.Unregister("CFC_RequestTakeAllLease_v1");
					value.Unregister("CFC_LeaseResponse_v1");
				}
				Registered.Remove(container);
				InventoryContainers.Remove(container.GetInventory());
				RegisteredViews.Remove(value);
			}
		}

		internal static bool IsLeased(Container container)
		{
			ZNetView view = GetView(container);
			if ((Object)(object)view != (Object)null)
			{
				return LeaseActive(view.GetZDO());
			}
			return false;
		}

		internal static bool Owns(Container container)
		{
			Session session = current;
			if (session == null)
			{
				return false;
			}
			foreach (Entry entry in session.Entries)
			{
				if ((Object)(object)entry.Container == (Object)(object)container)
				{
					return entry.Synchronized && Matches(entry, session, 20000000L) && CanUse(container, session.Player, ZNet.GetUID(), session.AllowOwnOpen);
				}
			}
			return false;
		}

		internal static void Acquire(Player player, List<Container> containers, Action onReady, Action<string> onFailure)
		{
			Acquire(player, containers, onReady, onFailure, allowOwnOpen: false);
		}

		private static void Acquire(Player player, List<Container> containers, Action onReady, Action<string> onFailure, bool allowOwnOpen)
		{
			if (current != null)
			{
				onFailure("Another container operation is still pending.");
				return;
			}
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				onFailure("The local player is not ready.");
				return;
			}
			Session session = new Session
			{
				Player = player,
				Ready = onReady,
				Failure = onFailure,
				Started = Time.realtimeSinceStartup,
				AllowOwnOpen = allowOwnOpen
			};
			foreach (Container container in containers)
			{
				if (!session.Entries.Exists((Entry entry) => (Object)(object)entry.Container == (Object)(object)container))
				{
					Register(container);
					ZNetView view = GetView(container);
					if ((Object)(object)view == (Object)null || !Registered.ContainsKey(container) || !CanUse(container, player, ZNet.GetUID(), allowOwnOpen))
					{
						onFailure("A required container is busy, unavailable, or outside your access range.");
						return;
					}
					session.Entries.Add(new Entry
					{
						Container = container,
						View = view,
						ExpectedOwner = view.GetZDO().GetOwner()
					});
				}
			}
			session.Entries.Sort(delegate(Entry left, Entry right)
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				ZDOID uid = left.View.GetZDO().m_uid;
				ZDOID uid2 = right.View.GetZDO().m_uid;
				int num = ((ZDOID)(ref uid)).UserID.CompareTo(((ZDOID)(ref uid2)).UserID);
				return (num != 0) ? num : ((ZDOID)(ref uid)).ID.CompareTo(((ZDOID)(ref uid2)).ID);
			});
			current = session;
		}

		private static void Request(Container container, long sender, long playerId, string token, bool allowOwnOpen = false)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Expected O, but got Unknown
			ZNetView view = GetView(container);
			if ((Object)(object)view == (Object)null)
			{
				return;
			}
			if (!view.IsOwner())
			{
				view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3]
				{
					token ?? "",
					false,
					(object)new ZPackage()
				});
				return;
			}
			bool flag = false;
			ZPackage val = new ZPackage();
			try
			{
				Player player = Player.GetPlayer(playerId);
				if (token == null || token.Length != 32 || LeaseActive(view.GetZDO()) || !CanUse(container, player, sender, allowOwnOpen))
				{
					view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3]
					{
						token ?? "",
						false,
						val
					});
					return;
				}
				LoadContainer(container);
				if (sender != ZNet.GetUID())
				{
					SaveContainer(container);
					val = new ZPackage(view.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null));
				}
				ZDO zDO = view.GetZDO();
				zDO.Set("cfc_lease_token_v1", token);
				zDO.Set("cfc_lease_peer_v1", sender);
				zDO.Set("cfc_lease_until_v1", Now + 150000000);
				zDO.SetOwner(sender);
				ZDOMan.instance.ForceSendZDO(sender, zDO.m_uid);
				flag = true;
				view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3] { token, true, val });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Container owner could not grant lease: " + ex.Message));
				if (!flag && view.IsOwner())
				{
					view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3]
					{
						token ?? "",
						false,
						(object)new ZPackage()
					});
				}
			}
		}

		private static void Response(Container container, long sender, string token, bool granted, ZPackage snapshot)
		{
			Session session = ((current != null && current.Token == token) ? current : Abandoned.Find((Session item) => item.Token == token));
			if (session == null)
			{
				return;
			}
			Entry entry = session.Entries.Find((Entry item) => (Object)(object)item.Container == (Object)(object)container);
			if (entry != null && entry.Requested && entry.ExpectedOwner == sender && entry.Snapshot == null)
			{
				if (!granted)
				{
					entry.Requested = false;
					entry.RetryAfter = Time.realtimeSinceStartup + Random.Range(0.12f, 0.3f);
				}
				else
				{
					entry.Snapshot = snapshot.GetArray();
				}
			}
		}

		internal static void Tick()
		{
			PruneDestroyed();
			Session session = current;
			if (session != null && !session.Executing)
			{
				if (Time.realtimeSinceStartup - session.Started >= 8f || !Object.op_Implicit((Object)(object)session.Player) || (Object)(object)session.Player != (Object)(object)Player.m_localPlayer || ((Character)session.Player).IsDead())
				{
					Fail(session, "Container request timed out or the player is unavailable. No resources were consumed.");
				}
				else
				{
					try
					{
						bool flag = true;
						foreach (Entry entry3 in session.Entries)
						{
							if (!Object.op_Implicit((Object)(object)entry3.Container) || !Object.op_Implicit((Object)(object)entry3.View) || !entry3.View.IsValid())
							{
								throw new InvalidOperationException("A required container disappeared.");
							}
							if (!CanUse(entry3.Container, session.Player, ZNet.GetUID(), session.AllowOwnOpen))
							{
								throw new InvalidOperationException("A required container is no longer accessible.");
							}
							if (!entry3.Requested && Time.realtimeSinceStartup >= entry3.RetryAfter && !LeaseActive(entry3.View.GetZDO()))
							{
								long owner = entry3.View.GetZDO().GetOwner();
								if (owner != 0L)
								{
									entry3.ExpectedOwner = owner;
									entry3.Requested = true;
									entry3.View.InvokeRPC(owner, session.AllowOwnOpen ? "CFC_RequestTakeAllLease_v1" : "CFC_RequestLease_v1", new object[2]
									{
										session.Player.GetPlayerID(),
										session.Token
									});
								}
							}
							if (!entry3.Synchronized && entry3.Snapshot != null && Matches(entry3, session, 20000000L))
							{
								Synchronize(entry3);
							}
							if (!entry3.Synchronized)
							{
								flag = false;
								break;
							}
						}
						if (flag)
						{
							foreach (Entry entry4 in session.Entries)
							{
								if (!Owns(entry4.Container))
								{
									throw new InvalidOperationException("Container access or ownership changed.");
								}
							}
							session.Executing = true;
							try
							{
								session.Ready();
							}
							finally
							{
								Release(session);
								if (current == session)
								{
									current = null;
								}
							}
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("Container operation failed: " + ex));
						if (session == current)
						{
							Fail(session, "Container operation failed: " + ex.Message);
						}
						else
						{
							session.Failure("Container operation failed: " + ex.Message);
						}
					}
				}
			}
			for (int num = Abandoned.Count - 1; num >= 0; num--)
			{
				Session session2 = Abandoned[num];
				foreach (Entry entry5 in session2.Entries)
				{
					if (entry5.Snapshot != null && !entry5.Synchronized && Matches(entry5, session2, 0L))
					{
						try
						{
							Synchronize(entry5);
						}
						catch (Exception ex2)
						{
							Plugin.Log.LogWarning((object)("Late container synchronization failed: " + ex2.Message));
						}
					}
				}
				Release(session2);
				if ((double)(Time.realtimeSinceStartup - session2.Started) > 23.0)
				{
					Abandoned.RemoveAt(num);
				}
			}
		}

		private static void Synchronize(Entry entry)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			if (entry.ExpectedOwner == ZNet.GetUID())
			{
				entry.Synchronized = true;
				return;
			}
			LoadingField.Invoke(entry.Container) = true;
			try
			{
				entry.Container.GetInventory().Load(new ZPackage(entry.Snapshot));
			}
			finally
			{
				LoadingField.Invoke(entry.Container) = false;
			}
			UpdateRows(entry.Container);
			SaveContainer(entry.Container);
			entry.Synchronized = true;
		}

		private static void Fail(Session session, string message)
		{
			if (current == session)
			{
				Release(session);
				current = null;
				Abandoned.Add(session);
				session.Failure(message);
			}
		}

		internal static void CancelPending()
		{
			Session session = current;
			if (session != null && !session.Executing)
			{
				Release(session);
				current = null;
				Abandoned.Add(session);
			}
		}

		private static void Release(Session session)
		{
			foreach (Entry entry in session.Entries)
			{
				if (entry.Synchronized && Matches(entry, session, 0L))
				{
					ZDO zDO = entry.View.GetZDO();
					zDO.Set("cfc_lease_until_v1", 0L);
					zDO.Set("cfc_lease_peer_v1", 0L);
					zDO.Set("cfc_lease_token_v1", "");
				}
			}
		}

		internal static void Reset()
		{
			if (current != null)
			{
				Release(current);
			}
			current = null;
			foreach (Session item in Abandoned)
			{
				Release(item);
			}
			Abandoned.Clear();
			replayingTakeAll = null;
			foreach (KeyValuePair<Container, ZNetView> item2 in Registered)
			{
				if (Object.op_Implicit((Object)(object)item2.Value))
				{
					item2.Value.Unregister("CFC_RequestLease_v1");
					item2.Value.Unregister("CFC_RequestTakeAllLease_v1");
					item2.Value.Unregister("CFC_LeaseResponse_v1");
				}
			}
			Registered.Clear();
			InventoryContainers.Clear();
			RegisteredViews.Clear();
			RemovedContainers.Clear();
			nextPrune = 0f;
		}

		private static void PruneDestroyed()
		{
			if (Time.realtimeSinceStartup < nextPrune)
			{
				return;
			}
			nextPrune = Time.realtimeSinceStartup + 2f;
			foreach (KeyValuePair<Container, ZNetView> item in Registered)
			{
				if (!Object.op_Implicit((Object)(object)item.Key) || !Object.op_Implicit((Object)(object)item.Value) || !item.Value.IsValid())
				{
					RemovedContainers.Add(item.Key);
				}
			}
			foreach (Container removedContainer in RemovedContainers)
			{
				Unregister(removedContainer);
			}
			RemovedContainers.Clear();
		}

		private static bool LeaseActive(ZDO zdo)
		{
			if (zdo.GetLong("cfc_lease_peer_v1", 0L) != 0L)
			{
				return zdo.GetLong("cfc_lease_until_v1", 0L) > Now;
			}
			return false;
		}

		private static bool Matches(Entry entry, Session session, long margin)
		{
			if (!Object.op_Implicit((Object)(object)entry.Container) || !Object.op_Implicit((Object)(object)entry.View) || !entry.View.IsValid() || !entry.View.IsOwner())
			{
				return false;
			}
			ZDO zDO = entry.View.GetZDO();
			if (zDO.GetLong("cfc_lease_peer_v1", 0L) == ZNet.GetUID() && zDO.GetString("cfc_lease_token_v1", "") == session.Token)
			{
				return zDO.GetLong("cfc_lease_until_v1", 0L) > Now + margin;
			}
			return false;
		}

		private static ZNetView? GetView(Container container)
		{
			if (!Object.op_Implicit((Object)(object)container))
			{
				return null;
			}
			ZNetView val = ViewField.Invoke(container);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid())
			{
				return null;
			}
			return val;
		}

		private static bool CanUse(Container container, Player player, long peer, bool allowOwnOpen = false)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)container) || !Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead() || container.GetInventory() == null)
			{
				return false;
			}
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			ZNetView view = GetView(container);
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || component.GetZDO().GetOwner() != peer || (Object)(object)view == (Object)null)
			{
				return false;
			}
			float value = Plugin.SearchRadius.Value;
			Vector3 val = ((Component)container).transform.position - ((Component)player).transform.position;
			if (((Vector3)(ref val)).sqrMagnitude > value * value)
			{
				return false;
			}
			bool flag = allowOwnOpen && peer == ZNet.GetUID() && view.IsOwner() && Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)OpenContainerField.Invoke(InventoryGui.instance) == (Object)(object)container;
			if (((container.IsInUse() || view.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0) && !flag) || (Object.op_Implicit((Object)(object)container.m_wagon) && container.m_wagon.InUse()))
			{
				return false;
			}
			if (!CheckPrivateAccess(container, player.GetPlayerID()))
			{
				return false;
			}
			if (!container.m_checkGuardStone)
			{
				return true;
			}
			bool flag2 = false;
			foreach (PrivateArea item in (List<PrivateArea>)AllWards.GetValue(null))
			{
				if (!Object.op_Implicit((Object)(object)item) || !WardEnabled(item))
				{
					continue;
				}
				Vector3 val3 = ((Component)item).transform.position - ((Component)container).transform.position;
				if (!(val3.x * val3.x + val3.z * val3.z >= item.m_radius * item.m_radius))
				{
					Piece val4 = WardPiece.Invoke(item);
					if ((Object.op_Implicit((Object)(object)val4) && val4.GetCreator() == player.GetPlayerID()) || WardPermitted(item, player.GetPlayerID()))
					{
						return true;
					}
					flag2 = true;
				}
			}
			return !flag2;
		}

		internal static void YieldToContainerUse(Container container)
		{
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			ZNetView view = GetView(container);
			if ((Object)(object)view == (Object)null || !view.IsOwner())
			{
				return;
			}
			ZDO zDO = view.GetZDO();
			string text = zDO.GetString("cfc_lease_token_v1", "");
			if (zDO.GetLong("cfc_lease_peer_v1", 0L) == 0L || string.IsNullOrEmpty(text))
			{
				return;
			}
			Session session = current;
			if (session != null && session.Executing && session.Token == text)
			{
				throw new InvalidOperationException("A container mutation reentered a synchronous resource transaction.");
			}
			Entry entry = ((session != null && session.Token == text) ? session.Entries.Find((Entry item) => (Object)(object)item.Container == (Object)(object)container) : null);
			if (entry == null || (!entry.Synchronized && entry.ExpectedOwner != ZNet.GetUID()))
			{
				byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, (byte[])null);
				if (byteArray == null)
				{
					throw new InvalidOperationException("The container handoff has no authoritative inventory snapshot.");
				}
				LoadingField.Invoke(container) = true;
				try
				{
					container.GetInventory().Load(new ZPackage(byteArray));
				}
				finally
				{
					LoadingField.Invoke(container) = false;
				}
				UpdateRows(container);
				SaveContainer(container);
			}
			zDO.Set("cfc_lease_until_v1", 0L);
			zDO.Set("cfc_lease_peer_v1", 0L);
			zDO.Set("cfc_lease_token_v1", "");
			if (session != null && session.Token == text)
			{
				Fail(session, "Container use interrupted the pending resource operation. No resources were consumed.");
			}
		}

		internal static void InitializeCompatibility(Harmony harmony)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("MultiUserChest.ContainerRPCHandler");
			if (type == null)
			{
				return;
			}
			HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(NetworkLeases), "MultiUserMutationPrefix", (Type[])null, (Type[])null))
			{
				priority = 800
			};
			string[] array = new string[5] { "RequestItemAdd", "RequestItemRemove", "RequestItemConsume", "RequestItemMove", "RequestDrop" };
			foreach (string text in array)
			{
				MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null);
				if (methodInfo == null || methodInfo.GetParameters().Length != 2 || methodInfo.GetParameters()[0].ParameterType != typeof(Inventory))
				{
					throw new MissingMethodException(type.FullName, text);
				}
				harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Plugin.Log.LogInfo((object)"MultiUserChest compatibility enabled: chest interaction takes priority over pending crafting.");
		}

		private static void MultiUserMutationPrefix(Inventory __0)
		{
			if (InventoryContainers.TryGetValue(__0, out Container value))
			{
				Session session = current;
				if (!((Object)(object)replayingTakeAll == (Object)(object)value) || session == null || !session.Executing || !session.AllowOwnOpen || !Owns(value))
				{
					YieldToContainerUse(value);
				}
			}
		}
	}
	[BepInPlugin("local.craftfromcontainers", "Craft From Containers", "0.1.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("toxo.craftfromchests")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "local.craftfromcontainers";

		public const string ModVersion = "0.1.4";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> SearchRadius;

		internal static ConfigEntry<float> RefreshInterval;

		internal static ConfigEntry<bool> ShowAvailable;

		internal static ConfigEntry<string> KilnAllowedResources;

		private ConfigEntry<bool> _diagnostics;

		private float _nextDiagnostics;

		private Harmony? _harmony;

		private ZNetScene? _scene;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SearchRadius", 40f, new ConfigDescription("Resource search radius in metres. Use the same value on every client and server.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			RefreshInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "RefreshInterval", 0.5f, new ConfigDescription("Seconds between proximity/access/network revision checks. Inventory changes update the resource cache immediately.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			ShowAvailable = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowAvailable", true, "Display required/available resources.");
			KilnAllowedResources = ((BaseUnityPlugin)this).Config.Bind<string>("Refueling", "KilnAllowedResources", "*", "Allowed input prefab names for recipes producing Coal, separated by commas. * allows every existing recipe; empty blocks all coal inputs. Set Wood to protect FineWood and RoundLog. Applies to backpack, containers and explicit item use; does not add recipes.");
			_diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Enabled", false, "Log cache counters every 30 seconds for performance checks.");
			_harmony = new Harmony("local.craftfromcontainers");
			try
			{
				_harmony.PatchAll();
				NetworkLeases.InitializeCompatibility(_harmony);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Craft From Containers 0.1.4; Valheim " + Version.GetVersionString(false) + "; multiplayer protocol 1."));
			}
			catch (Exception arg)
			{
				_harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)$"Unsupported game API: mod disabled. {arg}");
				((Behaviour)this).enabled = false;
			}
		}

		private void Update()
		{
			ZNetScene instance = ZNetScene.instance;
			if (instance != _scene)
			{
				NetworkLeases.Reset();
				ContainerIndex.Reset();
				InventoryBridge.Reset();
				RequirementTextPatch.Clear();
				CraftingPanelRefresh.Reset();
				ResourceActions.Cancel();
				_scene = instance;
				if (Object.op_Implicit((Object)(object)instance))
				{
					Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
					for (int i = 0; i < array.Length; i++)
					{
						Register(array[i]);
					}
				}
			}
			NetworkLeases.Tick();
			ContainerIndex.Tick();
			CraftingPanelRefresh.Tick();
			if (_diagnostics.Value && Time.unscaledTime >= _nextDiagnostics)
			{
				_nextDiagnostics = Time.unscaledTime + 30f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)ContainerIndex.Diagnostics);
			}
		}

		internal static void Register(Container container)
		{
			if (Object.op_Implicit((Object)(object)container) && container.GetInventory() != null)
			{
				ContainerIndex.Register(container);
				NetworkLeases.Register(container);
			}
		}

		private void OnDestroy()
		{
			NetworkLeases.Reset();
			ContainerIndex.Reset();
			InventoryBridge.Reset();
			RequirementTextPatch.Clear();
			CraftingPanelRefresh.Reset();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class ContainerAwakePatch
	{
		private static void Postfix(Container __instance)
		{
			Plugin.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	internal static class OreActionPatch
	{
		private static readonly Func<Smelter, Switch, Humanoid, ItemData, bool> Add = AccessTools.MethodDelegate<Func<Smelter, Switch, Humanoid, ItemData, bool>>(AccessTools.Method(typeof(Smelter), "OnAddOre", (Type[])null, (Type[])null), (object)null, true);

		private static bool Prefix(Smelter __instance, Switch sw, Humanoid user, ref ItemData item, ref bool __result)
		{
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			Player player = (Player)(object)((user is Player) ? user : null);
			if (player == null || !ResourceActions.Local(player))
			{
				return true;
			}
			Smelter smelter = __instance;
			if (item != null)
			{
				if (!RefuelingPolicy.AllowsOre(smelter, item))
				{
					return RefuelingPolicy.Refuse(player, ref __result);
				}
				return true;
			}
			if (InventoryBridge.Executing)
			{
				return true;
			}
			foreach (ItemConversion item4 in smelter.m_conversion)
			{
				if (RefuelingPolicy.AllowsConversion(item4))
				{
					ItemData item2 = ((Humanoid)player).GetInventory().GetItem(item4.m_from.m_itemData.m_shared.m_name, -1, false);
					if (item2 != null && RefuelingPolicy.AllowsOre(smelter, item2))
					{
						item = item2;
						return true;
					}
				}
			}
			foreach (ItemConversion conversion in smelter.m_conversion)
			{
				if (!RefuelingPolicy.AllowsConversion(conversion))
				{
					continue;
				}
				string name = conversion.m_from.m_itemData.m_shared.m_name;
				if (ContainerIndex.Count(name) <= 0)
				{
					continue;
				}
				Vector3 start = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position);
				if (ResourceActions.Begin(player, () => (!RefuelingPolicy.AllowsConversion(conversion)) ? null : new List<ResourcePlanner.Demand>
				{
					new ResourcePlanner.Demand(name, 1)
				}, delegate
				{
					//IL_0044: Unknown result type (might be due to invalid IL or missing references)
					//IL_0049: Unknown result type (might be due to invalid IL or missing references)
					//IL_004f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0054: Unknown result type (might be due to invalid IL or missing references)
					//IL_0059: Unknown result type (might be due to invalid IL or missing references)
					if (Object.op_Implicit((Object)(object)smelter) && Object.op_Implicit((Object)(object)sw))
					{
						Vector3 val = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position) - start;
						if (((Vector3)(ref val)).sqrMagnitude < 1f)
						{
							return RefuelingPolicy.AllowsConversion(conversion);
						}
					}
					return false;
				}, delegate
				{
					ItemData item3 = ((Humanoid)player).GetInventory().GetItem(name, -1, false);
					if (item3 != null && RefuelingPolicy.AllowsOre(smelter, item3))
					{
						Add(smelter, sw, (Humanoid)(object)player, item3);
					}
				}))
				{
					item = ((Humanoid)player).GetInventory().GetItem(name, -1, false);
					if (item != null && RefuelingPolicy.AllowsOre(smelter, item))
					{
						return true;
					}
				}
				__result = false;
				return false;
			}
			((Character)player).Message((MessageType)2, "$msg_noprocessableitems", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
	internal static class FuelActionPatch
	{
		private static readonly Func<Smelter, Switch, Humanoid, ItemData, bool> Add = AccessTools.MethodDelegate<Func<Smelter, Switch, Humanoid, ItemData, bool>>(AccessTools.Method(typeof(Smelter), "OnAddFuel", (Type[])null, (Type[])null), (object)null, true);

		private static bool Prefix(Smelter __instance, Switch sw, Humanoid user, ItemData item, ref bool __result)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (item == null)
			{
				Player player = (Player)(object)((user is Player) ? user : null);
				if (player != null && ResourceActions.Local(player) && !InventoryBridge.Executing)
				{
					Smelter smelter = __instance;
					string name = smelter.m_fuelItem.m_itemData.m_shared.m_name;
					if (((Humanoid)player).GetInventory().HaveItem(name, true))
					{
						return true;
					}
					Vector3 start = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position);
					bool num = ResourceActions.Begin(player, () => new List<ResourcePlanner.Demand>
					{
						new ResourcePlanner.Demand(name, 1)
					}, delegate
					{
						//IL_0030: Unknown result type (might be due to invalid IL or missing references)
						//IL_0035: Unknown result type (might be due to invalid IL or missing references)
						//IL_003b: Unknown result type (might be due to invalid IL or missing references)
						//IL_0040: Unknown result type (might be due to invalid IL or missing references)
						//IL_0045: Unknown result type (might be due to invalid IL or missing references)
						if (Object.op_Implicit((Object)(object)smelter) && Object.op_Implicit((Object)(object)sw))
						{
							Vector3 val = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position) - start;
							return ((Vector3)(ref val)).sqrMagnitude < 1f;
						}
						return false;
					}, delegate
					{
						Add(smelter, sw, (Humanoid)(object)player, null);
					});
					if (!num)
					{
						__result = false;
					}
					return num;
				}
			}
			return true;
		}
	}
	internal sealed class ResourceAllowList
	{
		private string? _configuration;

		private readonly HashSet<string> _names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private bool _all;

		internal bool Allows(string configuration, string prefabName)
		{
			if (_configuration != configuration)
			{
				_names.Clear();
				_all = false;
				string[] array = configuration.Split(',');
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text == "*")
					{
						_all = true;
					}
					else if (text.Length > 0)
					{
						_names.Add(text);
					}
				}
				_configuration = configuration;
			}
			if (!_all)
			{
				return _names.Contains(prefabName);
			}
			return true;
		}
	}
	internal static class RefuelingPolicy
	{
		private static readonly ResourceAllowList Kiln = new ResourceAllowList();

		internal static bool AllowsConversion(ItemConversion conversion)
		{
			if (Object.op_Implicit((Object)(object)conversion.m_from))
			{
				if (Object.op_Implicit((Object)(object)conversion.m_to) && !(((Object)((Component)conversion.m_to).gameObject).name != "Coal"))
				{
					return Kiln.Allows(Plugin.KilnAllowedResources.Value, ((Object)((Component)conversion.m_from).gameObject).name);
				}
				return true;
			}
			return false;
		}

		internal static bool AllowsOre(Smelter smelter, ItemData item)
		{
			foreach (ItemConversion item2 in smelter.m_conversion)
			{
				if (Object.op_Implicit((Object)(object)item2.m_from) && ((Object)((Component)item2.m_from).gameObject).name == ((Object)item.m_dropPrefab).name)
				{
					return AllowsConversion(item2);
				}
			}
			return true;
		}

		internal static bool Refuse(Player player, ref bool result)
		{
			((Character)player).Message((MessageType)2, "$msg_wontwork", 0, (Sprite)null, false);
			result = false;
			return false;
		}
	}
	internal static class ResourceActions
	{
		private static int _generation;

		internal static bool Pending;

		internal static void Cancel()
		{
			_generation++;
			Pending = false;
			NetworkLeases.CancelPending();
		}

		internal static bool Local(Player player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				return (Object)(object)player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		internal static List<ResourcePlanner.Demand> Costs(Player player, Requirement[] requirements, int quality, int multiplier)
		{
			List<ResourcePlanner.Demand> list = new List<ResourcePlanner.Demand>();
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			foreach (Requirement val in requirements)
			{
				if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_upgraderResource == (Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader))
				{
					int num = checked(val.GetAmount(quality) * multiplier);
					if (num > 0)
					{
						list.Add(new ResourcePlanner.Demand(val.m_resItem.m_itemData.m_shared.m_name, num));
					}
				}
			}
			return list;
		}

		internal static List<ResourcePlanner.Demand>? RecipeCosts(Player player, Recipe recipe, int quality, int multiplier)
		{
			if (!recipe.m_requireOnlyOneIngredient)
			{
				return Costs(player, recipe.m_resources, quality, multiplier);
			}
			int previous = InventoryBridge.Enter();
			try
			{
				int amount = default(int);
				int num = default(int);
				ItemData firstRequiredItem = player.GetFirstRequiredItem(((Humanoid)player).GetInventory(), recipe, quality, ref amount, ref num, multiplier);
				return (firstRequiredItem == null) ? null : new List<ResourcePlanner.Demand>
				{
					new ResourcePlanner.Demand(firstRequiredItem.m_shared.m_name, amount, firstRequiredItem.m_quality)
				};
			}
			finally
			{
				InventoryBridge.Leave(previous);
			}
		}

		internal static bool Begin(Player player, Func<List<ResourcePlanner.Demand>?> costs, Func<bool> stillValid, Action action)
		{
			if (InventoryBridge.Executing)
			{
				return true;
			}
			if (Pending || NetworkLeases.IsBusy)
			{
				return false;
			}
			List<ResourcePlanner.Demand> list = costs();
			List<string> list2 = new List<string>();
			if (list != null)
			{
				foreach (ResourcePlanner.Demand item in list)
				{
					list2.Add(item.Name);
				}
			}
			if (list == null || !ResourceTransaction.TryCreate(player, ContainerIndex.GetSourcesFor(list2), list, requireOwnership: false, out ResourceTransaction preview))
			{
				Fail(player, "$msg_missingrequirement");
				return false;
			}
			if (preview.Sources.Count == 0)
			{
				return true;
			}
			int generation = _generation;
			Pending = true;
			NetworkLeases.Acquire(player, preview.Sources, delegate
			{
				Pending = false;
				if (generation != _generation || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || !stillValid())
				{
					return;
				}
				foreach (Container source in preview.Sources)
				{
					if (!Object.op_Implicit((Object)(object)source) || !NetworkLeases.Owns(source) || !ContainerIndex.CanAccess(source, ignoreInUse: true))
					{
						Fail(player, "$msg_cantopen");
						return;
					}
				}
				InventoryBridge.RestrictedSources = preview.Sources;
				try
				{
					List<ResourcePlanner.Demand> list3 = costs();
					if (list3 == null || !ResourceTransaction.TryCreate(player, preview.Sources, list3, requireOwnership: true, out ResourceTransaction transaction))
					{
						Fail(player, "$msg_missingrequirement");
						return;
					}
					transaction.Validate();
					transaction.Capture(player);
					InventoryBridge.Transaction = transaction;
					InventoryBridge.Executing = true;
					int previous = InventoryBridge.Enter();
					try
					{
						action();
					}
					finally
					{
						try
						{
							transaction.Finish();
						}
						finally
						{
							InventoryBridge.Leave(previous);
						}
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Resource operation failed: " + ex));
					Fail(player, "Craft From Containers: operation failed; see BepInEx log.");
				}
				finally
				{
					InventoryBridge.Transaction = null;
					InventoryBridge.RestrictedSources = null;
					InventoryBridge.Executing = false;
				}
			}, delegate(string reason)
			{
				Pending = false;
				Plugin.Log.LogDebug((object)("Resource request cancelled: " + reason));
				if (Object.op_Implicit((Object)(object)player) && generation == _generation)
				{
					Fail(player, "Storage is busy or unavailable. Please try again.");
				}
			});
			return false;
		}

		private static void Fail(Player player, string message)
		{
			((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class CraftActionPatch
	{
		private static readonly FieldRef<InventoryGui, Recipe> RecipeField = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe");

		private static readonly FieldRef<InventoryGui, ItemData> UpgradeField = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_craftUpgradeItem");

		private static readonly FieldRef<InventoryGui, int> VariantField = AccessTools.FieldRefAccess<InventoryGui, int>("m_craftVariant");

		private static readonly FieldRef<InventoryGui, bool> MultiField = AccessTools.FieldRefAccess<InventoryGui, bool>("m_multiCrafting");

		private static readonly FieldRef<InventoryGui, int> AmountField = AccessTools.FieldRefAccess<InventoryGui, int>("m_multiCraftAmount");

		private static readonly Action<InventoryGui, Player> Craft = AccessTools.MethodDelegate<Action<InventoryGui, Player>>(AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null), (object)null, true);

		private static bool Prefix(InventoryGui __instance, Player player)
		{
			if (!ResourceActions.Local(player) || InventoryBridge.Executing || player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25))
			{
				return true;
			}
			InventoryGui gui = __instance;
			Recipe recipe = RecipeField.Invoke(gui);
			if (!Object.op_Implicit((Object)(object)recipe))
			{
				return true;
			}
			ItemData upgrade = UpgradeField.Invoke(gui);
			int quality = ((upgrade == null) ? 1 : (upgrade.m_quality + 1));
			int variant = VariantField.Invoke(gui);
			bool multi = MultiField.Invoke(gui);
			int amount = ((!multi) ? 1 : AmountField.Invoke(gui));
			CraftingStation station = player.GetCurrentCraftingStation();
			return ResourceActions.Begin(player, () => ResourceActions.RecipeCosts(player, recipe, quality, amount), () => Object.op_Implicit((Object)(object)gui) && InventoryGui.IsVisible() && (Object)(object)RecipeField.Invoke(gui) == (Object)(object)recipe && UpgradeField.Invoke(gui) == upgrade && VariantField.Invoke(gui) == variant && MultiField.Invoke(gui) == multi && (!multi || AmountField.Invoke(gui) == amount) && (Object)(object)player.GetCurrentCraftingStation() == (Object)(object)station && (!Object.op_Implicit((Object)(object)station) || station.CheckUsable(player, false)), delegate
			{
				Craft(gui, player);
			});
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			FieldInfo objB = AccessTools.Field(typeof(InventoryGui), "m_craftVariant");
			int num = -1;
			for (int i = 1; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Ldfld && object.Equals(list[i].operand, objB))
				{
					num = i - 1;
					break;
				}
			}
			if (num < 0 || list[num].opcode != OpCodes.Ldarg_0)
			{
				throw new InvalidOperationException("Unsupported DoCrafting layout: cannot place debit before item creation.");
			}
			CodeInstruction val = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CraftActionPatch), "PayBeforeOutput", (Type[])null, (Type[])null));
			val.labels.AddRange(list[num].labels);
			list[num].labels.Clear();
			list.Insert(num, val);
			return list;
		}

		private static void PayBeforeOutput()
		{
			InventoryBridge.Transaction?.Pay();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	internal static class PendingCraftPatch
	{
		private static bool Prefix()
		{
			return !ResourceActions.Pending;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")]
	internal static class CancelCraftPatch
	{
		private static void Prefix()
		{
			ResourceActions.Cancel();
		}
	}
	[HarmonyPatch(typeof(Recipe), "GetAmount")]
	internal static class MissingAlternativePatch
	{
		private static bool Prefix(Recipe __instance, int quality, int craftMultiplier, ref int need, ref ItemData singleReqItem, ref int __result)
		{
			if (!__instance.m_requireOnlyOneIngredient || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return true;
			}
			int num = default(int);
			int num2 = default(int);
			if (Player.m_localPlayer.GetFirstRequiredItem(((Humanoid)Player.m_localPlayer).GetInventory(), __instance, quality, ref num, ref num2, craftMultiplier) != null)
			{
				return true;
			}
			need = 0;
			singleReqItem = null;
			__result = __instance.m_amount * craftMultiplier;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class BuildActionPatch
	{
		private static readonly FieldRef<Player, GameObject> Ghost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");

		private static readonly FieldRef<Player, float> LastUse = AccessTools.FieldRefAccess<Player, float>("m_lastToolUseTime");

		private static readonly FieldRef<Player, int> Debt = AccessTools.FieldRefAccess<Player, int>("m_buildRemoveDebt");

		private static readonly Func<Player, float> Stamina = AccessTools.MethodDelegate<Func<Player, float>>(AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Player, ItemData, float> Durability = AccessTools.MethodDelegate<Func<Player, ItemData, float>>(AccessTools.Method(typeof(Player), "GetPlaceDurability", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Humanoid, ItemData> RightItem = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetRightItem", (Type[])null, (Type[])null), (object)null, true);

		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (!ResourceActions.Local(__instance) || InventoryBridge.Executing || __instance.NoCostCheat() || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
			{
				return true;
			}
			GameObject val = Ghost.Invoke(__instance);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return true;
			}
			Vector3 position = val.transform.position;
			Quaternion rotation = val.transform.rotation;
			ItemData tool = RightItem((Humanoid)(object)__instance);
			bool num = ResourceActions.Begin(__instance, () => ResourceActions.Costs(__instance, piece.m_resources, 0, 1), delegate
			{
				//IL_0074: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				if (((Character)__instance).InPlaceMode() && (Object)(object)__instance.GetSelectedPiece() == (Object)(object)piece && RightItem((Humanoid)(object)__instance) == tool && Object.op_Implicit((Object)(object)Ghost.Invoke(__instance)))
				{
					Vector3 val2 = Ghost.Invoke(__instance).transform.position - position;
					if (((Vector3)(ref val2)).sqrMagnitude < 0.01f && Quaternion.Angle(Ghost.Invoke(__instance).transform.rotation, rotation) < 1f)
					{
						return ((Character)__instance).HaveStamina(Stamina(__instance));
					}
				}
				return false;
			}, delegate
			{
				//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: Invalid comparison between Unknown and I4
				//IL_0190: Unknown result type (might be due to invalid IL or missing references)
				//IL_0195: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_010f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0116: Invalid comparison between Unknown and I4
				if (__instance.HaveRequirements(piece, (RequirementMode)0))
				{
					InventoryBridge.Transaction.Pay();
					if (__instance.TryPlacePiece(piece))
					{
						InventoryBridge.Transaction.CompletePayment();
						Hud.instance.m_buildUi.AddRecentPiece(piece);
						LastUse.Invoke(__instance) = Time.time;
						((Character)__instance).UseStamina(Stamina(__instance));
						PieceTable val2 = tool?.m_shared.m_buildPieces;
						if (Object.op_Implicit((Object)(object)val2) && (int)val2.m_skill != 0)
						{
							if ((int)val2.m_skill == 107)
							{
								Game.instance.IncrementPlayerStat((PlayerStatType)168, 1f, false);
							}
							if (Debt.Invoke(__instance) > 0)
							{
								Debt.Invoke(__instance)--;
							}
							else
							{
								((Character)__instance).RaiseSkill(val2.m_skill, 1f);
								if ((int)val2.m_skill == 107)
								{
									Game.instance.IncrementPlayerStat((PlayerStatType)169, 1f, false);
								}
							}
						}
						if (tool != null)
						{
							if (tool.m_shared.m_useDurability)
							{
								ItemData obj = tool;
								obj.m_durability -= Durability(__instance, tool) * Game.m_durabilityRate;
							}
							tool.m_shared.m_buildEffect.Create(((Component)__instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)__instance).GetZDOID());
						}
					}
				}
			});
			if (!num)
			{
				__result = false;
			}
			return num;
		}
	}
	internal static class ResourcePlanner
	{
		internal readonly struct Stock
		{
			internal readonly string Name;

			internal readonly int Quality;

			internal readonly int WorldLevel;

			internal readonly int Amount;

			internal Stock(string name, int quality, int worldLevel, int amount)
			{
				Name = name;
				Quality = quality;
				WorldLevel = worldLevel;
				Amount = amount;
			}
		}

		internal readonly struct Demand
		{
			internal readonly string Name;

			internal readonly int Amount;

			internal readonly int Quality;

			internal Demand(string name, int amount, int quality = -1)
			{
				Name = name;
				Amount = amount;
				Quality = quality;
			}
		}

		internal readonly struct Take
		{
			internal readonly int StockIndex;

			internal readonly int Amount;

			internal Take(int stockIndex, int amount)
			{
				StockIndex = stockIndex;
				Amount = amount;
			}
		}

		internal static bool TryPlan(IReadOnlyList<Stock> stock, IReadOnlyList<Demand> demands, int worldLevel, out List<Take> plan)
		{
			plan = new List<Take>();
			int[] array = new int[stock.Count];
			for (int i = 0; i < stock.Count; i++)
			{
				array[i] = Math.Max(0, stock[i].Amount);
			}
			for (int j = 0; j < 2; j++)
			{
				foreach (Demand demand in demands)
				{
					if (demand.Amount < 0 || string.IsNullOrEmpty(demand.Name))
					{
						plan.Clear();
						return false;
					}
					if (demand.Quality >= 0 != (j == 0))
					{
						continue;
					}
					int num = demand.Amount;
					for (int k = 0; k < stock.Count; k++)
					{
						if (num <= 0)
						{
							break;
						}
						Stock stock2 = stock[k];
						if (!(stock2.Name != demand.Name) && stock2.WorldLevel >= worldLevel && (demand.Quality < 0 || stock2.Quality == demand.Quality))
						{
							int num2 = Math.Min(num, array[k]);
							if (num2 != 0)
							{
								array[k] -= num2;
								num -= num2;
								plan.Add(new Take(k, num2));
							}
						}
					}
					if (num != 0)
					{
						plan.Clear();
						return false;
					}
				}
			}
			return true;
		}
	}
	internal sealed class ResourceTransaction
	{
		private sealed class Entry
		{
			internal Inventory Inventory;

			internal ItemData Item;

			internal Container? Container;

			internal int Reserved;
		}

		private readonly List<Entry> _entries = new List<Entry>();

		private InventorySnapshot? _snapshot;

		private readonly List<Inventory> _inventories = new List<Inventory>();

		private static readonly Action<Inventory, bool, bool> Changed = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), (object)null, true);

		internal readonly List<Container> Sources = new List<Container>();

		internal bool Paid { get; private set; }

		internal bool Completed
		{
			get
			{
				if (!Paid)
				{
					return false;
				}
				foreach (Entry entry in _entries)
				{
					if (entry.Reserved != 0)
					{
						return false;
					}
				}
				return true;
			}
		}

		internal bool Cheated { get; private set; }

		internal static bool TryCreate(Player player, List<Container> candidates, List<ResourcePlanner.Demand> costs, bool requireOwnership, out ResourceTransaction transaction)
		{
			transaction = new ResourceTransaction();
			List<ResourcePlanner.Stock> stock = new List<ResourcePlanner.Stock>();
			List<Entry> list = new List<Entry>();
			AddInventory(((Humanoid)player).GetInventory(), null, stock, list);
			foreach (Container candidate in candidates)
			{
				if (Object.op_Implicit((Object)(object)candidate) && candidate.GetIn