Decompiled source of WiresBalancedCraftFromNearbyChests v0.1.1

WiresBalancedCraftFromNearbyChests.dll

Decompiled 13 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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WiresBalancedCraftFromNearbyChests")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WiresBalancedCraftFromNearbyChests")]
[assembly: AssemblyTitle("WiresBalancedCraftFromNearbyChests")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 WiresBalancedCraftFromNearbyChests
{
	[HarmonyPatch(typeof(ZDO), "Set", new Type[]
	{
		typeof(int),
		typeof(string)
	})]
	internal static class ChestTitleZdoSetPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ZDO __instance, int hash)
		{
			if (__instance != null && hash == ZDOVars.s_text)
			{
				ContainerRegistry.NotifyTitleChanged(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetView), "OnDestroy")]
	internal static class RegistryObjectDestroyPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetView __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Container[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Container>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if ((Object)(object)componentsInChildren[i] != (Object)null)
				{
					ContainerRegistry.Unregister(componentsInChildren[i]);
				}
			}
			Smelter componentInChildren = ((Component)__instance).GetComponentInChildren<Smelter>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren);
			}
			Fireplace componentInChildren2 = ((Component)__instance).GetComponentInChildren<Fireplace>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren2);
			}
			CookingStation componentInChildren3 = ((Component)__instance).GetComponentInChildren<CookingStation>(true);
			if ((Object)(object)componentInChildren3 != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren3);
			}
			Fermenter componentInChildren4 = ((Component)__instance).GetComponentInChildren<Fermenter>(true);
			if ((Object)(object)componentInChildren4 != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren4);
			}
			Turret componentInChildren5 = ((Component)__instance).GetComponentInChildren<Turret>(true);
			if ((Object)(object)componentInChildren5 != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren5);
			}
			ShieldGenerator componentInChildren6 = ((Component)__instance).GetComponentInChildren<ShieldGenerator>(true);
			if ((Object)(object)componentInChildren6 != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren6);
			}
			Catapult componentInChildren7 = ((Component)__instance).GetComponentInChildren<Catapult>(true);
			if ((Object)(object)componentInChildren7 != (Object)null)
			{
				RefillTargetRegistry.Unregister((Component)(object)componentInChildren7);
			}
		}
	}
	[HarmonyPatch(typeof(ZDO), "Set", new Type[]
	{
		typeof(string),
		typeof(string)
	})]
	internal static class ChestTitleZdoStringSetPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ZDO __instance, string name)
		{
			if (__instance != null && string.Equals(name, "text", StringComparison.Ordinal))
			{
				ContainerRegistry.NotifyTitleChanged(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Start")]
	internal static class CraftingStationStartGraphPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			ConnectionVisuals.NotifyGraphChanged();
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "OnDestroy")]
	internal static class CraftingStationDestroyGraphPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			ConnectionVisuals.NotifyGraphChanged();
		}
	}
	internal static class ConnectionVisuals
	{
		private enum VisualMode
		{
			None,
			Station,
			RefillTarget,
			ChestStation,
			ChestRefill,
			Tool
		}

		private struct BeamKey : IEquatable<BeamKey>
		{
			internal int ContainerId;

			internal int TargetId;

			internal bool FromPlayer;

			public bool Equals(BeamKey other)
			{
				if (ContainerId == other.ContainerId && TargetId == other.TargetId)
				{
					return FromPlayer == other.FromPlayer;
				}
				return false;
			}

			public override bool Equals(object obj)
			{
				if (obj is BeamKey)
				{
					return Equals((BeamKey)obj);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (((ContainerId * 397) ^ TargetId) * 397) ^ (FromPlayer ? 1 : 0);
			}
		}

		private sealed class Beam
		{
			internal BeamKey Key;

			internal Container Container;

			internal Component Target;

			internal bool FromPlayer;

			internal GameObject Prefab;

			internal GameObject Instance;
		}

		private static readonly Dictionary<BeamKey, Beam> Beams = new Dictionary<BeamKey, Beam>();

		private static readonly HashSet<BeamKey> Desired = new HashSet<BeamKey>();

		private static readonly List<BeamKey> RemoveKeys = new List<BeamKey>();

		private static readonly Dictionary<int, Stack<GameObject>> Pool = new Dictionary<int, Stack<GameObject>>();

		private static readonly FieldInfo AllExtensionsField = AccessTools.Field(typeof(StationExtension), "m_allExtensions");

		private static readonly string[] KnownExtensionPrefabs = new string[10] { "piece_workbench_ext1", "piece_workbench_ext2", "piece_workbench_ext3", "piece_workbench_ext4", "forge_ext1", "forge_ext2", "forge_ext3", "forge_ext4", "forge_ext5", "forge_ext6" };

		private const float ContextPollSeconds = 0.08f;

		private const float LazyGraphDelay = 0.12f;

		private const float MovingGraphRefreshSeconds = 0.25f;

		private const int BeamCreatesPerFrame = 1;

		private static GameObject _genericConnectionPrefab;

		private static VisualMode _mode;

		private static int _focusId;

		private static string _routeId;

		private static CraftingStation _station;

		private static Component _refillTarget;

		private static Container _chest;

		private static ItemData _tool;

		private static bool _dirty = true;

		private static float _rebuildNotBefore;

		private static float _nextContextPoll;

		private static float _nextMovingGraphRefresh;

		internal static void Update()
		{
			ContainerRegistry.Tick();
			if (!VisualsEnabled())
			{
				CleanupActive();
				ResetContext();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				CleanupActive();
				ResetContext();
				return;
			}
			if (Time.time >= _nextContextPoll)
			{
				_nextContextPoll = Time.time + 0.08f;
				PollContext(localPlayer);
			}
			if (Time.time >= _nextMovingGraphRefresh && ContextUsesMovingContainers())
			{
				_nextMovingGraphRefresh = Time.time + 0.25f;
				_dirty = true;
				_rebuildNotBefore = 0f;
			}
			if (_dirty && Time.time >= _rebuildNotBefore)
			{
				Rebuild(localPlayer);
				_dirty = false;
			}
			ProcessBeamCreationBudget(localPlayer);
			if (Beams.Count > 0 && (_mode == VisualMode.Tool || HasMovingBeam()))
			{
				UpdateBeamTransforms(localPlayer);
			}
		}

		private static bool VisualsEnabled()
		{
			if (ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.ShowConnections != null)
			{
				return ModConfig.ShowConnections.Value;
			}
			return false;
		}

		private static bool ContextUsesMovingContainers()
		{
			if ((Object)(object)_chest != (Object)null && ContainerRegistry.IsMoving(_chest))
			{
				return true;
			}
			string text = _routeId;
			if (string.IsNullOrEmpty(text) && _mode == VisualMode.Station && (Object)(object)_station != (Object)null)
			{
				text = RouteRegistry.EnsureStation(_station)?.Id;
			}
			return ContainerRegistry.HasMovingForRoute(text);
		}

		private static bool HasMovingBeam()
		{
			foreach (Beam value in Beams.Values)
			{
				if (value != null && ContainerRegistry.IsMoving(value.Container))
				{
					return true;
				}
			}
			return false;
		}

		private static void PollContext(Player player)
		{
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if ((Object)(object)hoverObject != (Object)null)
			{
				CraftingStation componentInParent = hoverObject.GetComponentInParent<CraftingStation>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					SetContext(VisualMode.Station, ((Object)componentInParent).GetInstanceID(), null, componentInParent, null, null, null);
					return;
				}
				Component val = RefillTargetRegistry.FindHoveredTarget(hoverObject);
				if ((Object)(object)val != (Object)null)
				{
					RouteDefinition routeDefinition = RefillRouting.Resolve(val);
					if (routeDefinition != null)
					{
						SetContext(VisualMode.RefillTarget, ((Object)val).GetInstanceID(), routeDefinition.Id, null, val, null, null);
						return;
					}
				}
				Container componentInParent2 = hoverObject.GetComponentInParent<Container>();
				if ((Object)(object)componentInParent2 != (Object)null)
				{
					RouteDefinition routeDefinition2 = RouteRegistry.Get(ContainerRegistry.GetAssignedRouteId(componentInParent2));
					if (routeDefinition2 != null && routeDefinition2.Kind == RouteKind.Station)
					{
						SetContext(VisualMode.ChestStation, ((Object)componentInParent2).GetInstanceID(), routeDefinition2.Id, null, null, componentInParent2, null);
						return;
					}
					if (routeDefinition2 != null && routeDefinition2.Kind == RouteKind.Refill)
					{
						SetContext(VisualMode.ChestRefill, ((Object)componentInParent2).GetInstanceID(), routeDefinition2.Id, null, null, componentInParent2, null);
						return;
					}
				}
			}
			ItemData rightItem = ((Humanoid)player).RightItem;
			if (Hud.InBuildUi() && rightItem != null && rightItem.m_shared != null && (Object)(object)rightItem.m_shared.m_buildPieces != (Object)null)
			{
				RouteDefinition routeDefinition3 = RouteRegistry.EnsureTool(rightItem);
				if (routeDefinition3 != null)
				{
					int focusId = (((Object)(object)rightItem.m_dropPrefab != (Object)null) ? ((Object)rightItem.m_dropPrefab).GetInstanceID() : ((object)rightItem).GetHashCode());
					SetContext(VisualMode.Tool, focusId, routeDefinition3.Id, null, null, null, rightItem);
					return;
				}
			}
			SetContext(VisualMode.None, 0, null, null, null, null, null);
		}

		private static void SetContext(VisualMode mode, int focusId, string routeId, CraftingStation station, Component refillTarget, Container chest, ItemData tool)
		{
			bool num = _mode != mode || _focusId != focusId || !string.Equals(_routeId, routeId, StringComparison.Ordinal);
			_mode = mode;
			_focusId = focusId;
			_routeId = routeId;
			_station = station;
			_refillTarget = refillTarget;
			_chest = chest;
			_tool = tool;
			if (num)
			{
				_dirty = true;
				_rebuildNotBefore = 0f;
			}
		}

		private static void Rebuild(Player player)
		{
			Desired.Clear();
			switch (_mode)
			{
			case VisualMode.Station:
				BuildStation(_station);
				break;
			case VisualMode.RefillTarget:
				BuildRefillTarget(_refillTarget);
				break;
			case VisualMode.ChestStation:
				BuildChestStation(_chest);
				break;
			case VisualMode.ChestRefill:
				BuildChestRefill(_chest);
				break;
			case VisualMode.Tool:
				BuildTool(player, _tool);
				break;
			}
			RemoveUndesired();
			UpdateBeamTransforms(player);
		}

		private static void BuildStation(CraftingStation station)
		{
			//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)
			if ((Object)(object)station == (Object)null)
			{
				return;
			}
			RouteDefinition routeDefinition = RouteRegistry.EnsureStation(station);
			if (routeDefinition != null)
			{
				float stationBuildRange = station.GetStationBuildRange();
				List<Container> matching = ContainerRegistry.GetMatching(new RouteContext
				{
					Definition = routeDefinition,
					Origin = ((Component)station).transform.position,
					Range = stationBuildRange,
					Station = station
				});
				GameObject prefab = FindConnectionPrefab(station);
				for (int i = 0; i < matching.Count; i++)
				{
					WantBeam(matching[i], (Component)(object)station, fromPlayer: false, prefab);
				}
			}
		}

		private static void BuildRefillTarget(Component target)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			RouteDefinition routeDefinition = RefillRouting.Resolve(target);
			if (routeDefinition != null)
			{
				float sharedStationRadius = RangeProvider.GetSharedStationRadius();
				List<Container> matching = ContainerRegistry.GetMatching(new RouteContext
				{
					Definition = routeDefinition,
					Origin = target.transform.position,
					Range = sharedStationRadius
				});
				GameObject prefab = FindGenericConnectionPrefab();
				for (int i = 0; i < matching.Count; i++)
				{
					WantBeam(matching[i], target, fromPlayer: false, prefab);
				}
			}
		}

		private static void BuildChestStation(Container chest)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)chest == (Object)null || !ContainerRegistry.IsUsable(chest))
			{
				return;
			}
			RouteDefinition routeDefinition = RouteRegistry.Get(ContainerRegistry.GetAssignedRouteId(chest));
			if (routeDefinition == null)
			{
				return;
			}
			foreach (CraftingStation instance in CraftingStation.Instances)
			{
				CraftingStation val = instance;
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				RouteDefinition routeDefinition2 = RouteRegistry.EnsureStation(val);
				if (routeDefinition2 != null && !(routeDefinition2.Id != routeDefinition.Id))
				{
					float stationBuildRange = val.GetStationBuildRange();
					if (WithinHorizontalRange(((Component)chest).transform.position, ((Component)val).transform.position, stationBuildRange))
					{
						WantBeam(chest, (Component)(object)val, fromPlayer: false, FindConnectionPrefab(val));
					}
				}
			}
		}

		private static void BuildChestRefill(Container chest)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)chest == (Object)null || !ContainerRegistry.IsUsable(chest))
			{
				return;
			}
			RouteDefinition routeDefinition = RouteRegistry.Get(ContainerRegistry.GetAssignedRouteId(chest));
			if (routeDefinition == null)
			{
				return;
			}
			float sharedStationRadius = RangeProvider.GetSharedStationRadius();
			foreach (Component target in RefillTargetRegistry.GetTargets(routeDefinition))
			{
				if (!((Object)(object)target == (Object)null) && WithinHorizontalRange(((Component)chest).transform.position, target.transform.position, sharedStationRadius))
				{
					WantBeam(chest, target, fromPlayer: false, FindGenericConnectionPrefab());
				}
			}
		}

		private static void BuildTool(Player player, ItemData tool)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || tool == null)
			{
				return;
			}
			RouteDefinition routeDefinition = RouteRegistry.EnsureTool(tool);
			if (routeDefinition != null)
			{
				float sharedStationRadius = RangeProvider.GetSharedStationRadius();
				List<Container> matching = ContainerRegistry.GetMatching(new RouteContext
				{
					Definition = routeDefinition,
					Origin = ((Component)player).transform.position,
					Range = sharedStationRadius,
					Tool = tool
				});
				GameObject prefab = FindGenericConnectionPrefab();
				for (int i = 0; i < matching.Count; i++)
				{
					WantBeam(matching[i], null, fromPlayer: true, prefab);
				}
			}
		}

		private static bool WithinHorizontalRange(Vector3 a, Vector3 b, float range)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = a - b;
			val.y = 0f;
			return ((Vector3)(ref val)).sqrMagnitude <= range * range;
		}

		private static void WantBeam(Container chest, Component target, bool fromPlayer, GameObject prefab)
		{
			if (!((Object)(object)chest == (Object)null) && !((Object)(object)prefab == (Object)null))
			{
				BeamKey beamKey = new BeamKey
				{
					ContainerId = ((Object)chest).GetInstanceID(),
					TargetId = (((Object)(object)target != (Object)null) ? ((Object)target).GetInstanceID() : 0),
					FromPlayer = fromPlayer
				};
				Desired.Add(beamKey);
				EnsureBeam(beamKey, chest, target, fromPlayer, prefab);
			}
		}

		private static void EnsureBeam(BeamKey key, Container chest, Component target, bool fromPlayer, GameObject prefab)
		{
			if (Beams.TryGetValue(key, out var value))
			{
				if (value != null && (Object)(object)value.Prefab == (Object)(object)prefab)
				{
					value.Container = chest;
					value.Target = target;
					value.FromPlayer = fromPlayer;
					return;
				}
				ReleaseBeam(value);
				Beams.Remove(key);
			}
			Beams.Add(key, new Beam
			{
				Key = key,
				Container = chest,
				Target = target,
				FromPlayer = fromPlayer,
				Prefab = prefab,
				Instance = null
			});
		}

		private static GameObject AcquireInstance(GameObject prefab)
		{
			//IL_003d: 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)
			int instanceID = ((Object)prefab).GetInstanceID();
			GameObject val = null;
			if (Pool.TryGetValue(instanceID, out var value))
			{
				while (value.Count > 0 && (Object)(object)val == (Object)null)
				{
					val = value.Pop();
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				val = Object.Instantiate<GameObject>(prefab, Vector3.zero, Quaternion.identity);
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			val.SetActive(true);
			return val;
		}

		private static void ReleaseBeam(Beam beam)
		{
			if (beam == null || (Object)(object)beam.Instance == (Object)null)
			{
				return;
			}
			beam.Instance.SetActive(false);
			if ((Object)(object)beam.Prefab == (Object)null)
			{
				Object.Destroy((Object)(object)beam.Instance);
				return;
			}
			int instanceID = ((Object)beam.Prefab).GetInstanceID();
			if (!Pool.TryGetValue(instanceID, out var value))
			{
				value = new Stack<GameObject>();
				Pool.Add(instanceID, value);
			}
			if (value.Count < 64)
			{
				value.Push(beam.Instance);
			}
			else
			{
				Object.Destroy((Object)(object)beam.Instance);
			}
		}

		private static void RemoveUndesired()
		{
			RemoveKeys.Clear();
			foreach (KeyValuePair<BeamKey, Beam> beam in Beams)
			{
				if (!Desired.Contains(beam.Key) || beam.Value == null || (Object)(object)beam.Value.Container == (Object)null)
				{
					RemoveKeys.Add(beam.Key);
				}
			}
			for (int i = 0; i < RemoveKeys.Count; i++)
			{
				if (Beams.TryGetValue(RemoveKeys[i], out var value))
				{
					ReleaseBeam(value);
				}
				Beams.Remove(RemoveKeys[i]);
			}
		}

		private static void ProcessBeamCreationBudget(Player player)
		{
			int num = 0;
			foreach (Beam value in Beams.Values)
			{
				if (num >= 1)
				{
					break;
				}
				if (value != null && !((Object)(object)value.Instance != (Object)null) && !((Object)(object)value.Prefab == (Object)null) && !((Object)(object)value.Container == (Object)null))
				{
					value.Instance = AcquireInstance(value.Prefab);
					if (!((Object)(object)value.Instance == (Object)null))
					{
						UpdateBeamTransform(value, player);
						num++;
					}
				}
			}
		}

		private static void UpdateBeamTransforms(Player player)
		{
			foreach (Beam value in Beams.Values)
			{
				UpdateBeamTransform(value, player);
			}
		}

		private static void UpdateBeamTransform(Beam beam, Player player)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00a0: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (beam == null || (Object)(object)beam.Container == (Object)null || (Object)(object)beam.Instance == (Object)null)
			{
				return;
			}
			Vector3 val;
			Vector3 val2;
			if (beam.FromPlayer)
			{
				if ((Object)(object)player == (Object)null)
				{
					return;
				}
				val = ((Component)player).transform.position + Vector3.up;
				val2 = GetContainerPoint(beam.Container);
			}
			else
			{
				if ((Object)(object)beam.Target == (Object)null)
				{
					return;
				}
				val = GetContainerPoint(beam.Container);
				val2 = GetTargetPoint(beam.Target);
			}
			Vector3 val3 = val2 - val;
			if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0001f))
			{
				Transform transform = beam.Instance.transform;
				transform.position = val;
				transform.rotation = Quaternion.LookRotation(((Vector3)(ref val3)).normalized);
				transform.localScale = new Vector3(1f, 1f, ((Vector3)(ref val3)).magnitude);
			}
		}

		private static Vector3 GetTargetPoint(Component target)
		{
			//IL_0011: 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)
			//IL_002d: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			CraftingStation val = (CraftingStation)(object)((target is CraftingStation) ? target : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.GetConnectionEffectPoint();
			}
			Collider componentInChildren = target.GetComponentInChildren<Collider>();
			Bounds bounds;
			if ((Object)(object)componentInChildren != (Object)null)
			{
				bounds = componentInChildren.bounds;
				return ((Bounds)(ref bounds)).center;
			}
			Piece componentInParent = target.GetComponentInParent<Piece>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				componentInChildren = ((Component)componentInParent).GetComponentInChildren<Collider>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					bounds = componentInChildren.bounds;
					return ((Bounds)(ref bounds)).center;
				}
			}
			return target.transform.position + Vector3.up * 0.5f;
		}

		private static Vector3 GetContainerPoint(Container container)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Collider componentInChildren = ((Component)container).GetComponentInChildren<Collider>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				Bounds bounds = componentInChildren.bounds;
				return ((Bounds)(ref bounds)).center;
			}
			return ((Component)container).transform.position + Vector3.up * 0.5f;
		}

		private static GameObject FindConnectionPrefab(CraftingStation station)
		{
			if ((Object)(object)station != (Object)null)
			{
				List<StationExtension> loadedExtensions = GetLoadedExtensions();
				if (loadedExtensions != null)
				{
					for (int i = 0; i < loadedExtensions.Count; i++)
					{
						StationExtension val = loadedExtensions[i];
						if (!((Object)(object)val == (Object)null) && !((Object)(object)val.m_connectionPrefab == (Object)null) && !((Object)(object)val.m_craftingStation == (Object)null) && val.m_craftingStation.m_name == station.m_name)
						{
							return val.m_connectionPrefab;
						}
					}
				}
			}
			return FindGenericConnectionPrefab();
		}

		private static List<StationExtension> GetLoadedExtensions()
		{
			if (!(AllExtensionsField != null))
			{
				return null;
			}
			return AllExtensionsField.GetValue(null) as List<StationExtension>;
		}

		private static GameObject FindGenericConnectionPrefab()
		{
			if ((Object)(object)_genericConnectionPrefab != (Object)null)
			{
				return _genericConnectionPrefab;
			}
			List<StationExtension> loadedExtensions = GetLoadedExtensions();
			if (loadedExtensions != null)
			{
				for (int i = 0; i < loadedExtensions.Count; i++)
				{
					StationExtension val = loadedExtensions[i];
					if ((Object)(object)val != (Object)null && (Object)(object)val.m_connectionPrefab != (Object)null)
					{
						_genericConnectionPrefab = val.m_connectionPrefab;
						return _genericConnectionPrefab;
					}
				}
			}
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			for (int j = 0; j < KnownExtensionPrefabs.Length; j++)
			{
				GameObject prefab = instance.GetPrefab(KnownExtensionPrefabs[j]);
				if (!((Object)(object)prefab == (Object)null))
				{
					StationExtension componentInChildren = prefab.GetComponentInChildren<StationExtension>(true);
					if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren.m_connectionPrefab == (Object)null))
					{
						_genericConnectionPrefab = componentInChildren.m_connectionPrefab;
						return _genericConnectionPrefab;
					}
				}
			}
			return null;
		}

		internal static void NotifyGraphChanged()
		{
			_dirty = true;
			_rebuildNotBefore = Mathf.Max(_rebuildNotBefore, Time.time + 0.12f);
		}

		internal static void NotifyChestChanged(Container chest)
		{
			NotifyGraphChanged();
			if (!((Object)(object)chest == (Object)null) && !string.IsNullOrEmpty(ContainerRegistry.GetAssignedRouteId(chest)))
			{
				SpawnRenameBurst(chest);
			}
		}

		private static void SpawnRenameBurst(Container chest)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = FindGenericConnectionPrefab();
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = Object.Instantiate<GameObject>(val, GetContainerPoint(chest), Quaternion.identity);
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.transform.localScale = Vector3.one * 0.3f;
					val2.SetActive(true);
					Object.Destroy((Object)(object)val2, 0.55f);
				}
			}
		}

		private static void CleanupActive()
		{
			foreach (Beam value in Beams.Values)
			{
				ReleaseBeam(value);
			}
			Beams.Clear();
			Desired.Clear();
			RemoveKeys.Clear();
		}

		private static void ResetContext()
		{
			_mode = VisualMode.None;
			_focusId = 0;
			_routeId = null;
			_station = null;
			_refillTarget = null;
			_chest = null;
			_tool = null;
			_dirty = true;
			_rebuildNotBefore = 0f;
			_nextMovingGraphRefresh = 0f;
		}

		internal static void Cleanup()
		{
			CleanupActive();
			foreach (Stack<GameObject> value in Pool.Values)
			{
				while (value.Count > 0)
				{
					GameObject val = value.Pop();
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
			Pool.Clear();
			_genericConnectionPrefab = null;
			ResetContext();
		}
	}
	internal static class ContainerRegistry
	{
		private const float CacheSeconds = 0.25f;

		private const float ReconcileIntervalSeconds = 0.05f;

		private static readonly HashSet<Container> Known = new HashSet<Container>();

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

		private static readonly Queue<Container> PendingIndex = new Queue<Container>();

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

		private static readonly Dictionary<int, string> RouteByContainer = new Dictionary<int, string>();

		private static readonly Dictionary<int, string> TitleByContainer = new Dictionary<int, string>();

		private static readonly Dictionary<ZDOID, Container> ByZdo = new Dictionary<ZDOID, Container>();

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

		private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);

		private static float _nextRefresh;

		private static float _nextReconcile;

		private static int _reconcileCursor;

		private static bool _fullReindexPending;

		private static int _fullReindexCursor;

		private static string _cachedRouteId;

		private static Vector3 _cachedOrigin;

		private static float _cachedRange;

		internal static void Register(Container container)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)container == (Object)null) && Known.Add(container))
			{
				Reconcile.Add(container);
				PendingIndex.Enqueue(container);
				ZNetView netView = GetNetView(container);
				if ((Object)(object)netView != (Object)null && netView.IsValid() && netView.GetZDO() != null)
				{
					ByZdo[netView.GetZDO().m_uid] = container;
				}
			}
		}

		internal static void Unregister(Container container)
		{
			if (!((Object)(object)container == (Object)null))
			{
				Known.Remove(container);
				Reconcile.Remove(container);
				RemoveIndex(container);
				Invalidate();
				ConnectionVisuals.NotifyGraphChanged();
			}
		}

		internal static void Clear()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			Known.Clear();
			Reconcile.Clear();
			PendingIndex.Clear();
			ByRoute.Clear();
			RouteByContainer.Clear();
			TitleByContainer.Clear();
			ByZdo.Clear();
			Matching.Clear();
			_nextRefresh = 0f;
			_nextReconcile = 0f;
			_reconcileCursor = 0;
			_fullReindexPending = false;
			_fullReindexCursor = 0;
			_cachedRouteId = null;
			_cachedOrigin = Vector3.zero;
			_cachedRange = 0f;
		}

		internal static void Invalidate()
		{
			_nextRefresh = 0f;
			_cachedRouteId = null;
		}

		internal static void ReindexAll()
		{
			ByRoute.Clear();
			RouteByContainer.Clear();
			TitleByContainer.Clear();
			_fullReindexPending = Reconcile.Count > 0;
			_fullReindexCursor = 0;
			Invalidate();
			ConnectionVisuals.NotifyGraphChanged();
		}

		internal static void Tick()
		{
			while (PendingIndex.Count > 0)
			{
				Container val = PendingIndex.Dequeue();
				if (!((Object)(object)val == (Object)null))
				{
					Reindex(val, force: false);
					return;
				}
			}
			if (_fullReindexPending)
			{
				while (_fullReindexCursor < Reconcile.Count)
				{
					Container val2 = Reconcile[_fullReindexCursor++];
					if (!((Object)(object)val2 == (Object)null))
					{
						Reindex(val2, force: true);
						return;
					}
				}
				_fullReindexPending = false;
				_fullReindexCursor = 0;
			}
			else
			{
				if (Reconcile.Count == 0 || Time.time < _nextReconcile)
				{
					return;
				}
				_nextReconcile = Time.time + 0.05f;
				if (_reconcileCursor >= Reconcile.Count)
				{
					_reconcileCursor = 0;
				}
				int reconcileCursor = _reconcileCursor;
				Container val3 = Reconcile[reconcileCursor];
				if ((Object)(object)val3 == (Object)null)
				{
					Reconcile.RemoveAt(reconcileCursor);
					if (_reconcileCursor >= Reconcile.Count)
					{
						_reconcileCursor = 0;
					}
				}
				else
				{
					_reconcileCursor++;
					Reindex(val3, force: false);
				}
			}
		}

		internal static void NotifyTitleChanged(Container container)
		{
			if (!((Object)(object)container == (Object)null))
			{
				int instanceID = ((Object)container).GetInstanceID();
				TitleByContainer.TryGetValue(instanceID, out var value);
				string b = GetCustomTitle(container) ?? "";
				if (!string.Equals(value, b, StringComparison.Ordinal))
				{
					Reindex(container, force: true);
					Invalidate();
					ConnectionVisuals.NotifyChestChanged(container);
				}
			}
		}

		internal static void NotifyTitleChanged(ZDO zdo)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (zdo != null && ByZdo.TryGetValue(zdo.m_uid, out var value) && (Object)(object)value != (Object)null)
			{
				NotifyTitleChanged(value);
			}
		}

		internal static string GetAssignedRouteId(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)container).GetInstanceID();
			if (!TitleByContainer.ContainsKey(instanceID))
			{
				Reindex(container, force: true);
			}
			else
			{
				Reindex(container, force: false);
			}
			if (!RouteByContainer.TryGetValue(((Object)container).GetInstanceID(), out var value))
			{
				return null;
			}
			return value;
		}

		internal static List<Container> GetMatching(RouteContext context)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (context == null || context.Definition == null)
			{
				Matching.Clear();
				return Matching;
			}
			if (_cachedRouteId == context.Definition.Id && Mathf.Abs(_cachedRange - context.Range) < 0.01f)
			{
				Vector3 val = _cachedOrigin - context.Origin;
				if (((Vector3)(ref val)).sqrMagnitude < 0.0625f && Time.time < _nextRefresh)
				{
					return Matching;
				}
			}
			_cachedRouteId = context.Definition.Id;
			_cachedOrigin = context.Origin;
			_cachedRange = context.Range;
			_nextRefresh = Time.time + 0.25f;
			Refresh(context);
			return Matching;
		}

		internal static bool IsAssignedTo(Container container, RouteDefinition route)
		{
			if ((Object)(object)container == (Object)null || route == null)
			{
				return false;
			}
			ZNetView netView = GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid() || netView.GetZDO() == null)
			{
				return false;
			}
			return GetAssignedRouteId(container) == route.Id;
		}

		internal static bool IsUsable(Container container)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)container == (Object)null || (Object)(object)localPlayer == (Object)null || (Object)(object)Game.instance == (Object)null || container.GetInventory() == null)
			{
				return false;
			}
			ZNetView netView = GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return false;
			}
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			if (CheckAccessMethod != null)
			{
				object obj = CheckAccessMethod.Invoke(container, new object[1] { playerID });
				if (!(obj is bool) || !(bool)obj)
				{
					return false;
				}
			}
			if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
			{
				return false;
			}
			ZDO zDO = netView.GetZDO();
			if (!MultiUserChestCompat.Active && zDO != null && !netView.IsOwner() && zDO.GetInt(ZDOVars.s_inUse, 0) == 1)
			{
				return false;
			}
			return true;
		}

		internal static bool IsMoving(Container container)
		{
			if ((Object)(object)container != (Object)null)
			{
				return (Object)(object)container.m_rootObjectOverride != (Object)null;
			}
			return false;
		}

		internal static bool HasMovingForRoute(string routeId)
		{
			if (string.IsNullOrEmpty(routeId))
			{
				return false;
			}
			if (!ByRoute.TryGetValue(routeId, out var value))
			{
				return false;
			}
			foreach (Container item in value)
			{
				if (IsMoving(item))
				{
					return true;
				}
			}
			return false;
		}

		internal static ZNetView GetNetView(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			if (!((Object)(object)container.m_rootObjectOverride != (Object)null))
			{
				return ((Component)container).GetComponent<ZNetView>();
			}
			return container.m_rootObjectOverride;
		}

		internal static bool TryTakeOwnership(Container container)
		{
			ZNetView netView = GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid())
			{
				return false;
			}
			if (!netView.IsOwner())
			{
				netView.ClaimOwnership();
			}
			return netView.IsOwner();
		}

		private static void Reindex(Container container, bool force)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null)
			{
				return;
			}
			ZNetView netView = GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid() || netView.GetZDO() == null)
			{
				return;
			}
			ZDO zDO = netView.GetZDO();
			ByZdo[zDO.m_uid] = container;
			string text = zDO.GetString(ZDOVars.s_text, "") ?? "";
			int instanceID = ((Object)container).GetInstanceID();
			if (!force && TitleByContainer.TryGetValue(instanceID, out var value) && string.Equals(value, text, StringComparison.Ordinal))
			{
				return;
			}
			RouteByContainer.TryGetValue(instanceID, out var value2);
			string text2 = RouteRegistry.ClassifyTitle(text);
			if (!string.IsNullOrEmpty(value2) && !string.Equals(value2, text2, StringComparison.Ordinal) && ByRoute.TryGetValue(value2, out var value3))
			{
				value3.Remove(container);
				if (value3.Count == 0)
				{
					ByRoute.Remove(value2);
				}
			}
			if (!string.IsNullOrEmpty(text2))
			{
				if (!ByRoute.TryGetValue(text2, out var value4))
				{
					value4 = new HashSet<Container>();
					ByRoute.Add(text2, value4);
				}
				value4.Add(container);
				RouteByContainer[instanceID] = text2;
			}
			else
			{
				RouteByContainer.Remove(instanceID);
			}
			TitleByContainer[instanceID] = text;
			if (!string.Equals(value2, text2, StringComparison.Ordinal))
			{
				Invalidate();
				ConnectionVisuals.NotifyGraphChanged();
			}
		}

		private static void RemoveIndex(Container container)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)container).GetInstanceID();
			if (RouteByContainer.TryGetValue(instanceID, out var value) && ByRoute.TryGetValue(value, out var value2))
			{
				value2.Remove(container);
				if (value2.Count == 0)
				{
					ByRoute.Remove(value);
				}
			}
			RouteByContainer.Remove(instanceID);
			TitleByContainer.Remove(instanceID);
			ZNetView netView = GetNetView(container);
			if ((Object)(object)netView != (Object)null && netView.GetZDO() != null)
			{
				ByZdo.Remove(netView.GetZDO().m_uid);
			}
		}

		private static void Refresh(RouteContext context)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			Matching.Clear();
			float num = context.Range * context.Range;
			if (!ByRoute.TryGetValue(context.Definition.Id, out var value))
			{
				return;
			}
			foreach (Container item in value)
			{
				if (!((Object)(object)item == (Object)null))
				{
					Vector3 val = ((Component)item).transform.position - context.Origin;
					val.y = 0f;
					if (!(((Vector3)(ref val)).sqrMagnitude > num) && IsUsable(item))
					{
						Matching.Add(item);
					}
				}
			}
			Matching.Sort(delegate(Container a, Container b)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: 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)
				string customTitle = GetCustomTitle(a);
				string customTitle2 = GetCustomTitle(b);
				int num2 = WindowsLogicalCompare(customTitle, customTitle2);
				if (num2 != 0)
				{
					return num2;
				}
				Vector3 val2 = ((Component)a).transform.position - context.Origin;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				val2 = ((Component)b).transform.position - context.Origin;
				float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude;
				return sqrMagnitude.CompareTo(sqrMagnitude2);
			});
		}

		internal static string GetCustomTitle(Container container)
		{
			ZNetView netView = GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid() || netView.GetZDO() == null)
			{
				return "";
			}
			return netView.GetZDO().GetString(ZDOVars.s_text, "");
		}

		private static int WindowsLogicalCompare(string left, string right)
		{
			left = left ?? "";
			right = right ?? "";
			if (left.Length != right.Length)
			{
				if (left.StartsWith(right, StringComparison.OrdinalIgnoreCase))
				{
					return -1;
				}
				if (right.StartsWith(left, StringComparison.OrdinalIgnoreCase))
				{
					return 1;
				}
			}
			int i = 0;
			int j = 0;
			while (i < left.Length && j < right.Length)
			{
				char c = left[i];
				char c2 = right[j];
				if (char.IsDigit(c) && char.IsDigit(c2))
				{
					int num = i;
					int num2 = j;
					for (; i < left.Length && left[i] == '0'; i++)
					{
					}
					for (; j < right.Length && right[j] == '0'; j++)
					{
					}
					int num3 = i;
					int num4 = j;
					for (; i < left.Length && char.IsDigit(left[i]); i++)
					{
					}
					for (; j < right.Length && char.IsDigit(right[j]); j++)
					{
					}
					int num5 = i - num3;
					int num6 = j - num4;
					if (num5 != num6)
					{
						return num5.CompareTo(num6);
					}
					for (int k = 0; k < num5; k++)
					{
						int num7 = left[num3 + k].CompareTo(right[num4 + k]);
						if (num7 != 0)
						{
							return num7;
						}
					}
					int num8 = i - num;
					int num9 = j - num2;
					if (num8 != num9)
					{
						return num8.CompareTo(num9);
					}
				}
				else
				{
					char c3 = char.ToUpperInvariant(c);
					char c4 = char.ToUpperInvariant(c2);
					if (c3 != c4)
					{
						return c3.CompareTo(c4);
					}
					i++;
					j++;
				}
			}
			return (left.Length - i).CompareTo(right.Length - j);
		}
	}
	internal static class MethodSwaps
	{
		internal static readonly MethodInfo CountItemsFrom = AccessTools.Method(typeof(Inventory), "CountItems", new Type[3]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		}, (Type[])null);

		internal static readonly MethodInfo CountItemsTo = AccessTools.Method(typeof(InventoryBridge), "CountItems", (Type[])null, (Type[])null);

		internal static readonly MethodInfo HaveItemFrom = AccessTools.Method(typeof(Inventory), "HaveItem", new Type[2]
		{
			typeof(string),
			typeof(bool)
		}, (Type[])null);

		internal static readonly MethodInfo HaveItemTo = AccessTools.Method(typeof(InventoryBridge), "HaveItem", (Type[])null, (Type[])null);

		internal static readonly MethodInfo GetItemFrom = AccessTools.Method(typeof(Inventory), "GetItem", new Type[3]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		}, (Type[])null);

		internal static readonly MethodInfo GetItemTo = AccessTools.Method(typeof(InventoryBridge), "GetItem", (Type[])null, (Type[])null);

		internal static readonly MethodInfo RemoveItemFrom = AccessTools.Method(typeof(Inventory), "RemoveItem", new Type[4]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		}, (Type[])null);

		internal static readonly MethodInfo RemoveItemTo = AccessTools.Method(typeof(InventoryBridge), "RemoveItem", (Type[])null, (Type[])null);
	}
	internal static class Redirect
	{
		internal static IEnumerable<CodeInstruction> Calls(IEnumerable<CodeInstruction> instructions, params KeyValuePair<MethodInfo, MethodInfo>[] swaps)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				for (int i = 0; i < swaps.Length; i++)
				{
					if (CodeInstructionExtensions.Calls(instruction, swaps[i].Key))
					{
						instruction.opcode = OpCodes.Call;
						instruction.operand = swaps[i].Value;
						break;
					}
				}
				yield return instruction;
			}
		}

		internal static KeyValuePair<MethodInfo, MethodInfo> Swap(MethodInfo from, MethodInfo to)
		{
			return new KeyValuePair<MethodInfo, MethodInfo>(from, to);
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class ContainerAwakePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirementItems", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class HaveRequirementItemsPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo));
		}
	}
	[HarmonyPatch]
	internal static class GetFirstRequiredItemPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo), Redirect.Swap(MethodSwaps.GetItemFrom, MethodSwaps.GetItemTo));
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources")]
	internal static class ConsumeResourcesPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.RemoveItemFrom, MethodSwaps.RemoveItemTo));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
	internal static class DoCraftingPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.RemoveItemFrom, MethodSwaps.RemoveItemTo));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	internal static class SetupRequirementPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo));
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Piece),
		typeof(RequirementMode)
	})]
	internal static class HavePieceRequirementsPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			RoutingContext.EnterBuild();
		}

		[HarmonyFinalizer]
		private static void Finalizer()
		{
			RoutingContext.ExitBuild();
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.HaveItemFrom, MethodSwaps.HaveItemTo), Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo));
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacement", new Type[]
	{
		typeof(bool),
		typeof(float)
	})]
	internal static class UpdatePlacementPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			RoutingContext.EnterBuild();
		}

		[HarmonyFinalizer]
		private static void Finalizer()
		{
			RoutingContext.ExitBuild();
		}
	}
	[HarmonyPatch(typeof(Hud), "SetupPieceInfo", new Type[] { typeof(Piece) })]
	internal static class SetupPieceInfoPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			RoutingContext.EnterBuild();
		}

		[HarmonyFinalizer]
		private static void Finalizer()
		{
			RoutingContext.ExitBuild();
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return Redirect.Calls(instructions, Redirect.Swap(MethodSwaps.CountItemsFrom, MethodSwaps.CountItemsTo));
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ZNetShutdownPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			RefillTargetRegistry.Clear();
			ContainerRegistry.Clear();
			RoutingContext.Reset();
			ConnectionVisuals.Cleanup();
			KeywordSync.Reset();
			MultiUserChestCompat.Reset();
			RangeProvider.Invalidate();
		}
	}
	internal static class InventoryBridge
	{
		private static bool TryGetContext(Inventory inventory, out RouteContext context)
		{
			context = null;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || inventory == null || inventory != ((Humanoid)localPlayer).GetInventory())
			{
				return false;
			}
			return RoutingContext.TryGet(out context);
		}

		public static int CountItems(Inventory inventory, string name, int quality, bool matchWorldLevel)
		{
			int num = inventory.CountItems(name, quality, matchWorldLevel);
			if (!TryGetContext(inventory, out var context))
			{
				return num;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				Inventory inventory2 = matching[i].GetInventory();
				if (inventory2 != null)
				{
					num += inventory2.CountItems(name, quality, matchWorldLevel);
				}
			}
			return num;
		}

		public static bool HaveItem(Inventory inventory, string name, bool matchWorldLevel)
		{
			if (inventory.HaveItem(name, matchWorldLevel))
			{
				return true;
			}
			if (!TryGetContext(inventory, out var context))
			{
				return false;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				Inventory inventory2 = matching[i].GetInventory();
				if (inventory2 != null && inventory2.HaveItem(name, matchWorldLevel))
				{
					return true;
				}
			}
			return false;
		}

		public static ItemData GetItem(Inventory inventory, string name, int quality, bool isPrefabName)
		{
			ItemData item = inventory.GetItem(name, quality, isPrefabName);
			if (item != null)
			{
				return item;
			}
			if (!TryGetContext(inventory, out var context))
			{
				return null;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				Inventory inventory2 = matching[i].GetInventory();
				if (inventory2 != null)
				{
					item = inventory2.GetItem(name, quality, isPrefabName);
					if (item != null)
					{
						return item;
					}
				}
			}
			return null;
		}

		public static void RemoveItem(Inventory inventory, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			if (!TryGetContext(inventory, out var context))
			{
				inventory.RemoveItem(name, amount, itemQuality, worldLevelBased);
				return;
			}
			if (context.Definition != null && context.Definition.Kind == RouteKind.Refill && MultiUserChestCompat.Active)
			{
				MultiUserChestCompat.PendingAction pendingAction = MultiUserChestCompat.PrepareName(context, name, amount, itemQuality, worldLevelBased);
				if (pendingAction != null)
				{
					throw new MucPendingException(pendingAction);
				}
			}
			int num = amount;
			int num2 = inventory.CountItems(name, itemQuality, worldLevelBased);
			int num3 = Mathf.Min(num, num2);
			if (num3 > 0)
			{
				inventory.RemoveItem(name, num3, itemQuality, worldLevelBased);
				num -= num3;
			}
			if (num <= 0)
			{
				return;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				if (num <= 0)
				{
					break;
				}
				Container val = matching[i];
				Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(val);
				if (effectiveInventory == null)
				{
					continue;
				}
				int num4 = effectiveInventory.CountItems(name, itemQuality, worldLevelBased);
				if (num4 > 0)
				{
					Inventory inventory2 = val.GetInventory();
					if (effectiveInventory != inventory2 || ContainerRegistry.TryTakeOwnership(val))
					{
						int num5 = Mathf.Min(num4, num);
						effectiveInventory.RemoveItem(name, num5, itemQuality, worldLevelBased);
						num -= num5;
					}
				}
			}
			if (num > 0 && Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)("Could not consume " + num + "x " + name + " after checking the player and " + matching.Count + " matching chest(s)."));
			}
		}

		public static int CountItemsByName(Inventory inventory, string[] names, int quality, bool matchWorldLevel, bool stacksOnly)
		{
			int num = inventory.CountItemsByName(names, quality, matchWorldLevel, stacksOnly);
			if (!TryGetContext(inventory, out var context))
			{
				return num;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(matching[i]);
				if (effectiveInventory != null)
				{
					num += effectiveInventory.CountItemsByName(names, quality, matchWorldLevel, stacksOnly);
				}
			}
			return num;
		}

		public static ItemData GetAmmoItem(Inventory inventory, string ammoName, string matchPrefabName)
		{
			ItemData ammoItem = inventory.GetAmmoItem(ammoName, matchPrefabName);
			if (ammoItem != null)
			{
				return ammoItem;
			}
			if (!TryGetContext(inventory, out var context))
			{
				return null;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(matching[i]);
				if (effectiveInventory != null)
				{
					ammoItem = effectiveInventory.GetAmmoItem(ammoName, matchPrefabName);
					if (ammoItem != null)
					{
						return ammoItem;
					}
				}
			}
			return null;
		}

		public static bool RemoveItemData(Inventory inventory, ItemData item, int amount)
		{
			if (inventory == null || item == null)
			{
				return false;
			}
			if (inventory.ContainsItem(item))
			{
				return inventory.RemoveItem(item, amount);
			}
			if (!TryGetContext(inventory, out var context))
			{
				return inventory.RemoveItem(item, amount);
			}
			if (context.Definition != null && context.Definition.Kind == RouteKind.Refill && MultiUserChestCompat.Active)
			{
				MultiUserChestCompat.PendingAction pendingAction = MultiUserChestCompat.PrepareExactItem(context, item, amount);
				if (pendingAction != null)
				{
					throw new MucPendingException(pendingAction);
				}
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			for (int i = 0; i < matching.Count; i++)
			{
				Container val = matching[i];
				Inventory effectiveInventory = MultiUserChestCompat.GetEffectiveInventory(val);
				if (effectiveInventory != null && effectiveInventory.ContainsItem(item))
				{
					Inventory inventory2 = val.GetInventory();
					if (effectiveInventory == inventory2 && !ContainerRegistry.TryTakeOwnership(val))
					{
						return false;
					}
					return effectiveInventory.RemoveItem(item, amount);
				}
			}
			return false;
		}

		public static bool RemoveOneItem(Inventory inventory, ItemData item)
		{
			return RemoveItemData(inventory, item, 1);
		}
	}
	internal static class KeywordSync
	{
		private const string RequestRpc = "WBCFNC_KeywordRequest";

		private const string ConfigRpc = "WBCFNC_Keywords";

		private static readonly Dictionary<string, string[]> ServerKeywords = new Dictionary<string, string[]>(StringComparer.Ordinal);

		private static bool _registered;

		internal static bool ServerEnforced { get; private set; }

		internal static string[] GetKeywords(string routeId, string[] localValues)
		{
			if (ServerEnforced && routeId != null && ServerKeywords.TryGetValue(routeId, out var value))
			{
				return value;
			}
			return localValues ?? new string[0];
		}

		internal static void Register()
		{
			if (!_registered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register("WBCFNC_KeywordRequest", (Action<long>)OnConfigRequested);
				ZRoutedRpc.instance.Register<ZPackage>("WBCFNC_Keywords", (Action<long, ZPackage>)OnConfigReceived);
				_registered = true;
			}
		}

		internal static void RequestFromServer()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC("WBCFNC_KeywordRequest", Array.Empty<object>());
			}
		}

		internal static void NotifyLocalKeywordChanged()
		{
			ContainerRegistry.Invalidate();
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZPackage val = BuildPackage();
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "WBCFNC_Keywords", new object[1] { val });
			}
		}

		internal static void Reset()
		{
			_registered = false;
			ServerEnforced = false;
			ServerKeywords.Clear();
		}

		private static void OnConfigRequested(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "WBCFNC_Keywords", new object[1] { BuildPackage() });
			}
		}

		private static ZPackage BuildPackage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			List<RouteDefinition> list = new List<RouteDefinition>(RouteRegistry.All);
			val.Write(list.Count);
			for (int i = 0; i < list.Count; i++)
			{
				RouteDefinition routeDefinition = list[i];
				val.Write(routeDefinition.Id ?? "");
				for (int j = 0; j < 3; j++)
				{
					string text = "";
					if (routeDefinition.KeywordEntries != null && j < routeDefinition.KeywordEntries.Length && routeDefinition.KeywordEntries[j] != null)
					{
						text = routeDefinition.KeywordEntries[j].Value ?? "";
					}
					val.Write(text);
				}
			}
			return val;
		}

		private static void OnConfigReceived(long sender, ZPackage package)
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || package == null)
			{
				return;
			}
			ServerKeywords.Clear();
			int num = package.ReadInt();
			for (int i = 0; i < num; i++)
			{
				string text = package.ReadString();
				string[] array = new string[3];
				for (int j = 0; j < array.Length; j++)
				{
					array[j] = package.ReadString() ?? "";
				}
				if (!string.IsNullOrEmpty(text))
				{
					ServerKeywords[text] = array;
				}
			}
			ServerEnforced = true;
			ContainerRegistry.ReindexAll();
			if (Plugin.Log != null)
			{
				Plugin.Log.LogInfo((object)("Using " + ServerKeywords.Count + " station/tool chest keyword(s) from the server."));
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Awake")]
	internal static class ZNetAwakeKeywordSyncPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			KeywordSync.Reset();
			KeywordSync.Register();
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class ZNetPeerInfoKeywordSyncPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			KeywordSync.Register();
			KeywordSync.RequestFromServer();
		}
	}
	internal enum ChestNameHover
	{
		Vanilla,
		CustomName,
		TypeThenCustomName,
		CustomNameThenType
	}
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> ShowConnections;

		internal static ConfigEntry<bool> EnableCustomNames;

		internal static ConfigEntry<ChestNameHover> HoverNameFormat;

		internal static ConfigEntry<bool> ShowRenameHint;

		internal static ConfigFile File;

		internal static void Bind(ConfigFile config)
		{
			File = config;
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable crafting and building from correctly named nearby chests.");
			ShowConnections = config.Bind<bool>("Visuals", "Show connections", true, "Show vanilla-style connection effects for linked chests.");
			EnableCustomNames = config.Bind<bool>("Chest Renaming", "Enable custom names", true, "Enable the standalone chest renaming fallback when another compatible chest renamer is not installed.");
			HoverNameFormat = config.Bind<ChestNameHover>("Chest Renaming", "Hover name format", ChestNameHover.TypeThenCustomName, "Chest name format shown in hover when the standalone rename fallback is active.");
			ShowRenameHint = config.Bind<bool>("Chest Renaming", "Show rename hint in hover", true, "Show the Alt+Use rename hint when the standalone rename fallback is active.");
		}

		internal static ConfigEntry<string>[] BindStationKeywords(string key, params string[] defaults)
		{
			return BindKeywordSlots("Station Keywords", key, defaults, "Chest titles containing this alternative are assigned to this crafting station. Matching is case-insensitive; spaces are literal. Leave blank to disable this alternative. If another route already uses the same alias, this field is automatically cleared.");
		}

		internal static ConfigEntry<string>[] BindToolKeywords(string key, params string[] defaults)
		{
			return BindKeywordSlots("Build Tool Keywords", key, defaults, "Chest titles containing this alternative are assigned to this build tool. Matching is case-insensitive; spaces are literal. Leave blank to disable this alternative. If another route already uses the same alias, this field is automatically cleared.");
		}

		internal static ConfigEntry<string>[] BindRefillKeywords(string key, params string[] defaults)
		{
			return BindKeywordSlots("Refill Keywords", key, defaults, "Chest titles containing this alternative are assigned to this refill/feed route. Matching is case-insensitive; spaces are literal. Leave blank to disable this alternative. If another route already uses the same alias, this field is automatically cleared.");
		}

		private static ConfigEntry<string>[] BindKeywordSlots(string section, string key, string[] defaults, string description)
		{
			ConfigEntry<string>[] array = new ConfigEntry<string>[3];
			for (int i = 0; i < array.Length; i++)
			{
				string text = ((defaults != null && i < defaults.Length) ? (defaults[i] ?? "") : "");
				array[i] = File.Bind<string>(section, key + " " + (i + 1), text, description);
			}
			return array;
		}
	}
	internal static class MucActionPreflight
	{
		internal static readonly FieldInfo CraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");

		internal static readonly FieldInfo CraftUpgradeItemField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem");

		internal static readonly FieldInfo CraftVariantField = AccessTools.Field(typeof(InventoryGui), "m_craftVariant");

		internal static readonly FieldInfo MultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting");

		internal static readonly FieldInfo MultiCraftAmountField = AccessTools.Field(typeof(InventoryGui), "m_multiCraftAmount");

		internal static List<MultiUserChestCompat.Demand> RequirementDemands(Requirement[] requirements, int quality, int multiplier, CraftingStation station)
		{
			List<MultiUserChestCompat.Demand> list = new List<MultiUserChestCompat.Demand>();
			if (requirements == null)
			{
				return list;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			foreach (Requirement val in requirements)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null) && (!((Object)(object)station != (Object)null) || station.m_upgrader == val.m_upgraderResource) && (!((Object)(object)station == (Object)null) || !val.m_upgraderResource))
				{
					int num = val.GetAmount(quality) * multiplier;
					if (num > 0)
					{
						string name = val.m_resItem.m_itemData.m_shared.m_name;
						dictionary.TryGetValue(name, out var value);
						dictionary[name] = value + num;
					}
				}
			}
			foreach (KeyValuePair<string, int> item in dictionary)
			{
				list.Add(new MultiUserChestCompat.Demand
				{
					Name = item.Key,
					Amount = item.Value,
					Quality = -1,
					MatchWorldLevel = true
				});
			}
			return list;
		}

		internal static void Missing(Player player)
		{
			if ((Object)(object)player != (Object)null)
			{
				((Character)player).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
	internal static class MucCraftActionPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(InventoryGui __instance, Player player, MethodBase __originalMethod)
		{
			if (!MultiUserChestCompat.Active || MultiUserChestCompat.Executing)
			{
				return true;
			}
			if (MultiUserChestCompat.Busy)
			{
				return false;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)player == (Object)null || player.NoCostCheat() || (Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25))
			{
				return true;
			}
			Recipe recipe = default(Recipe);
			ref Recipe reference = ref recipe;
			object? value = MucActionPreflight.CraftRecipeField.GetValue(__instance);
			reference = (Recipe)((value is Recipe) ? value : null);
			if ((Object)(object)recipe == (Object)null)
			{
				return true;
			}
			ItemData upgrade = default(ItemData);
			ref ItemData reference2 = ref upgrade;
			object? value2 = MucActionPreflight.CraftUpgradeItemField.GetValue(__instance);
			reference2 = (ItemData)((value2 is ItemData) ? value2 : null);
			int num = ((upgrade == null) ? 1 : (upgrade.m_quality + 1));
			bool multi = (bool)MucActionPreflight.MultiCraftingField.GetValue(__instance);
			int multiplier = ((!multi) ? 1 : ((int)MucActionPreflight.MultiCraftAmountField.GetValue(__instance)));
			int variant = (int)MucActionPreflight.CraftVariantField.GetValue(__instance);
			CraftingStation station = player.GetCurrentCraftingStation();
			if (!RoutingContext.TryGet(out var context))
			{
				return true;
			}
			MultiUserChestCompat.PendingAction pendingAction;
			if (recipe.m_requireOnlyOneIngredient)
			{
				int num2 = default(int);
				ItemData val = default(ItemData);
				recipe.GetAmount(num, ref num2, ref val, multiplier);
				pendingAction = ((val != null && num2 > 0) ? MultiUserChestCompat.PrepareExactItem(context, val, num2) : null);
			}
			else
			{
				List<MultiUserChestCompat.Demand> demands = MucActionPreflight.RequirementDemands(recipe.m_resources, num, multiplier, station);
				pendingAction = MultiUserChestCompat.Prepare(context, demands);
			}
			if (pendingAction == null)
			{
				return true;
			}
			Func<bool> stillValid = delegate
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || !InventoryGui.IsVisible())
				{
					return false;
				}
				if (MucActionPreflight.CraftRecipeField.GetValue(__instance) != recipe)
				{
					return false;
				}
				if (MucActionPreflight.CraftUpgradeItemField.GetValue(__instance) != upgrade)
				{
					return false;
				}
				if ((int)MucActionPreflight.CraftVariantField.GetValue(__instance) != variant)
				{
					return false;
				}
				if ((bool)MucActionPreflight.MultiCraftingField.GetValue(__instance) != multi)
				{
					return false;
				}
				if (multi && (int)MucActionPreflight.MultiCraftAmountField.GetValue(__instance) != multiplier)
				{
					return false;
				}
				if ((Object)(object)player.GetCurrentCraftingStation() != (Object)(object)station)
				{
					return false;
				}
				return (Object)(object)station == (Object)null || station.CheckUsable(player, false);
			};
			Action ready = delegate
			{
				__originalMethod.Invoke(__instance, new object[1] { player });
			};
			MultiUserChestCompat.Attach(pendingAction, stillValid, ready, delegate
			{
				MucActionPreflight.Missing(player);
			});
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class MucBuildActionPatch
	{
		private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static readonly FieldInfo LastToolUseField = AccessTools.Field(typeof(Player), "m_lastToolUseTime");

		private static readonly FieldInfo BuildRemoveDebtField = AccessTools.Field(typeof(Player), "m_buildRemoveDebt");

		private static readonly MethodInfo GetBuildStaminaMethod = AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null);

		private static readonly MethodInfo GetPlaceDurabilityMethod = AccessTools.Method(typeof(Player), "GetPlaceDurability", new Type[1] { typeof(ItemData) }, (Type[])null);

		[HarmonyPrefix]
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			if (!MultiUserChestCompat.Active || MultiUserChestCompat.Executing)
			{
				return true;
			}
			if (MultiUserChestCompat.Busy)
			{
				__result = false;
				return false;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)piece == (Object)null || __instance.NoCostCheat() || (Object)(object)ZoneSystem.instance == (Object)null || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
			{
				return true;
			}
			GameObject val = (GameObject)((PlacementGhostField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			ItemData tool = ((Humanoid)__instance).RightItem;
			if (tool == null || tool.m_shared == null || (Object)(object)tool.m_shared.m_buildPieces == (Object)null)
			{
				return true;
			}
			Vector3 position = val.transform.position;
			Quaternion rotation = val.transform.rotation;
			RoutingContext.EnterBuild();
			RouteContext context;
			try
			{
				if (!RoutingContext.TryGet(out context))
				{
					return true;
				}
			}
			finally
			{
				RoutingContext.ExitBuild();
			}
			List<MultiUserChestCompat.Demand> demands = MucActionPreflight.RequirementDemands(piece.m_resources, 0, 1, null);
			MultiUserChestCompat.PendingAction pendingAction = MultiUserChestCompat.Prepare(context, demands);
			if (pendingAction == null)
			{
				return true;
			}
			Func<bool> stillValid = delegate
			{
				//IL_009e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || !((Character)__instance).InPlaceMode() || (Object)(object)__instance.GetSelectedPiece() != (Object)(object)piece || ((Humanoid)__instance).RightItem != tool)
				{
					return false;
				}
				GameObject val2 = (GameObject)((PlacementGhostField != null) ? /*isinst with value type is only supported in some contexts*/: null);
				if ((Object)(object)val2 == (Object)null)
				{
					return false;
				}
				Vector3 val3 = val2.transform.position - position;
				if (((Vector3)(ref val3)).sqrMagnitude >= 0.01f || Quaternion.Angle(val2.transform.rotation, rotation) >= 1f)
				{
					return false;
				}
				float buildStamina = GetBuildStamina(__instance);
				return ((Character)__instance).HaveStamina(buildStamina);
			};
			Action ready = delegate
			{
				CompleteBuild(__instance, piece, tool);
			};
			MultiUserChestCompat.Attach(pendingAction, stillValid, ready, delegate
			{
				MucActionPreflight.Missing(__instance);
			});
			__result = false;
			return false;
		}

		private static void CompleteBuild(Player player, Piece piece, ItemData tool)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: 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_00e7: Invalid comparison between Unknown and I4
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Invalid comparison between Unknown and I4
			if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || tool == null)
			{
				return;
			}
			RoutingContext.EnterBuild();
			try
			{
				if (!player.HaveRequirements(piece, (RequirementMode)0))
				{
					MucActionPreflight.Missing(player);
				}
				else
				{
					if (!player.TryPlacePiece(piece))
					{
						return;
					}
					if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null)
					{
						Hud.instance.m_buildUi.AddRecentPiece(piece);
					}
					if (LastToolUseField != null)
					{
						LastToolUseField.SetValue(player, Time.time);
					}
					if (!ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
					{
						player.ConsumeResources(piece.m_resources, 0, -1, 1);
					}
					((Character)player).UseStamina(GetBuildStamina(player));
					PieceTable buildPieces = tool.m_shared.m_buildPieces;
					if ((Object)(object)buildPieces != (Object)null && (int)buildPieces.m_skill != 0)
					{
						if ((int)buildPieces.m_skill == 107)
						{
							Game.instance.IncrementPlayerStat((PlayerStatType)168, 1f, false);
						}
						int num = ((BuildRemoveDebtField != null) ? ((int)BuildRemoveDebtField.GetValue(player)) : 0);
						if (num > 0)
						{
							BuildRemoveDebtField.SetValue(player, num - 1);
						}
						else
						{
							((Character)player).RaiseSkill(buildPieces.m_skill, 1f);
							if ((int)buildPieces.m_skill == 107)
							{
								Game.instance.IncrementPlayerStat((PlayerStatType)169, 1f, false);
							}
						}
					}
					if (tool.m_shared.m_useDurability)
					{
						tool.m_durability -= GetPlaceDurability(player, tool) * Game.m_durabilityRate;
					}
					tool.m_shared.m_buildEffect.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)player).GetZDOID());
				}
			}
			finally
			{
				RoutingContext.ExitBuild();
			}
		}

		private static float GetBuildStamina(Player player)
		{
			if ((Object)(object)player == (Object)null || GetBuildStaminaMethod == null)
			{
				return 0f;
			}
			object obj = GetBuildStaminaMethod.Invoke(player, null);
			if (!(obj is float))
			{
				return 0f;
			}
			return (float)obj;
		}

		private static float GetPlaceDurability(Player player, ItemData tool)
		{
			if ((Object)(object)player == (Object)null || tool == null || GetPlaceDurabilityMethod == null)
			{
				return 0f;
			}
			object obj = GetPlaceDurabilityMethod.Invoke(player, new object[1] { tool });
			if (!(obj is float))
			{
				return 0f;
			}
			return (float)obj;
		}
	}
	internal sealed class MucPendingException : Exception
	{
		internal readonly MultiUserChestCompat.PendingAction Pending;

		internal MucPendingException(MultiUserChestCompat.PendingAction pending)
		{
			Pending = pending;
		}
	}
	internal static class MultiUserChestCompat
	{
		internal sealed class Demand
		{
			internal string Name;

			internal int Amount;

			internal int Quality = -1;

			internal bool MatchWorldLevel = true;

			internal string PrefabName;

			internal Container SourceOnly;

			internal bool IgnorePlayer;
		}

		internal sealed class PendingAction
		{
			internal RouteContext Context;

			internal readonly List<Demand> Demands = new List<Demand>();

			internal readonly Dictionary<Container, Inventory> Reserves = new Dictionary<Container, Inventory>();

			internal readonly HashSet<Container> Concurrent = new HashSet<Container>();

			internal readonly List<int> RequestIds = new List<int>();

			internal Func<bool> StillValid;

			internal Action Ready;

			internal Action Failed;

			internal float Started;

			internal bool Blocked;
		}

		private sealed class RefundJob
		{
			internal Container Container;

			internal Inventory Reserve;

			internal readonly List<int> RequestIds = new List<int>();

			internal float NextTry;
		}

		internal const string PluginGuid = "com.maxsch.valheim.MultiUserChest";

		private static bool _initialized;

		private static bool _available;

		private static MethodInfo _removeItemFromChest;

		private static MethodInfo _addItemToChest;

		private static MethodInfo _getPackageGeneric;

		private static Type _iPackageType;

		private static PendingAction _pending;

		private static PendingAction _active;

		private static readonly List<RefundJob> Refunds = new List<RefundJob>();

		internal static bool Executing => _active != null;

		internal static bool Busy => _pending != null;

		internal static bool Active
		{
			get
			{
				EnsureInitialized();
				return _available;
			}
		}

		private static void EnsureInitialized()
		{
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			if (!Chainloader.PluginInfos.ContainsKey("com.maxsch.valheim.MultiUserChest"))
			{
				return;
			}
			try
			{
				Assembly assembly = ((object)Chainloader.PluginInfos["com.maxsch.valheim.MultiUserChest"].Instance).GetType().Assembly;
				Type type = assembly.GetType("MultiUserChest.ContainerHandler");
				Type type2 = assembly.GetType("MultiUserChest.PackageHandler");
				_iPackageType = assembly.GetType("MultiUserChest.IPackage");
				if (type != null)
				{
					MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
					foreach (MethodInfo methodInfo in methods)
					{
						if (methodInfo.Name == "RemoveItemFromChest" && methodInfo.GetParameters().Length == 7)
						{
							_removeItemFromChest = methodInfo;
						}
						if (methodInfo.Name == "AddItemToChest" && methodInfo.GetParameters().Length == 6)
						{
							_addItemToChest = methodInfo;
						}
					}
				}
				if (type2 != null)
				{
					MethodInfo[] methods2 = type2.GetMethods(BindingFlags.Static | BindingFlags.Public);
					foreach (MethodInfo methodInfo2 in methods2)
					{
						if (methodInfo2.Name == "GetPackage" && methodInfo2.IsGenericMethodDefinition && methodInfo2.GetParameters().Length == 1)
						{
							_getPackageGeneric = methodInfo2;
							break;
						}
					}
				}
				_available = _removeItemFromChest != null && _addItemToChest != null && _getPackageGeneric != null && _iPackageType != null;
				if (_available && Plugin.Log != null)
				{
					Plugin.Log.LogInfo((object)"MultiUserChest compatibility enabled.");
				}
			}
			catch (Exception ex)
			{
				_available = false;
				if (Plugin.Log != null)
				{
					Plugin.Log.LogWarning((object)("Could not initialize MultiUserChest compatibility: " + ex.Message));
				}
			}
		}

		internal static bool IsConcurrentRemote(Container container)
		{
			if (!Active || (Object)(object)container == (Object)null)
			{
				return false;
			}
			ZNetView netView = ContainerRegistry.GetNetView(container);
			if ((Object)(object)netView == (Object)null || !netView.IsValid() || !netView.HasOwner() || netView.IsOwner())
			{
				return false;
			}
			ZDO zDO = netView.GetZDO();
			if (!container.IsInUse())
			{
				if (zDO != null)
				{
					return zDO.GetInt(ZDOVars.s_inUse, 0) == 1;
				}
				return false;
			}
			return true;
		}

		internal static Inventory GetEffectiveInventory(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			if (_active != null)
			{
				if (_active.Reserves.TryGetValue(container, out var value))
				{
					return value;
				}
				if (_active.Concurrent.Contains(container))
				{
					return null;
				}
			}
			return container.GetInventory();
		}

		internal static void Update()
		{
			UpdatePending();
			UpdateRefunds();
		}

		private static void UpdatePending()
		{
			PendingAction pending = _pending;
			if (pending == null)
			{
				return;
			}
			if (pending.Blocked)
			{
				if (pending.Ready != null || pending.Failed != null)
				{
					_pending = null;
					FailPending(pending);
				}
				return;
			}
			bool flag = false;
			for (int i = 0; i < pending.RequestIds.Count; i++)
			{
				if (RequestExists(pending.RequestIds[i]))
				{
					flag = true;
					break;
				}
			}
			if (flag)
			{
				if (!(Time.realtimeSinceStartup - pending.Started < 8f))
				{
					_pending = null;
					FailPending(pending);
				}
			}
			else
			{
				if (pending.Ready == null && pending.Failed == null)
				{
					return;
				}
				if (CanSatisfy(pending) && (pending.StillValid == null || pending.StillValid()))
				{
					_pending = null;
					_active = pending;
					try
					{
						if (pending.Ready != null)
						{
							pending.Ready();
						}
						return;
					}
					catch (Exception ex)
					{
						if (Plugin.Log != null)
						{
							Plugin.Log.LogWarning((object)("MultiUserChest routed action failed: " + ex.Message));
						}
						if (pending.Failed != null)
						{
							pending.Failed();
						}
						return;
					}
					finally
					{
						_active = null;
						RefundLeftovers(pending);
					}
				}
				_pending = null;
				FailPending(pending);
			}
		}

		private static void FailPending(PendingAction pending)
		{
			RefundLeftovers(pending);
			if (pending != null && pending.Failed != null)
			{
				try
				{
					pending.Failed();
				}
				catch
				{
				}
			}
		}

		internal static void Attach(PendingAction pending, Func<bool> stillValid, Action ready, Action failed)
		{
			if (pending != null && pending.Ready == null && pending.Failed == null)
			{
				pending.StillValid = stillValid;
				pending.Ready = ready;
				pending.Failed = failed;
			}
		}

		internal static PendingAction PrepareName(RouteContext context, string name, int amount, int quality, bool matchWorldLevel)
		{
			Demand item = new Demand
			{
				Name = name,
				Amount = amount,
				Quality = quality,
				MatchWorldLevel = matchWorldLevel
			};
			return Prepare(context, new List<Demand> { item });
		}

		internal static PendingAction PrepareExactItem(RouteContext context, ItemData item, int amount)
		{
			if (context == null || item == null || item.m_shared == null || amount <= 0)
			{
				return null;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			Container val = null;
			for (int i = 0; i < matching.Count; i++)
			{
				Inventory val2 = (((Object)(object)matching[i] != (Object)null) ? matching[i].GetInventory() : null);
				if (val2 != null && val2.ContainsItem(item))
				{
					val = matching[i];
					break;
				}
			}
			if ((Object)(object)val == (Object)null || !IsConcurrentRemote(val))
			{
				return null;
			}
			Demand item2 = new Demand
			{
				Name = item.m_shared.m_name,
				Amount = amount,
				Quality = item.m_quality,
				MatchWorldLevel = false,
				PrefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null),
				SourceOnly = val,
				IgnorePlayer = true
			};
			return Prepare(context, new List<Demand> { item2 });
		}

		internal static PendingAction Prepare(RouteContext context, List<Demand> demands)
		{
			if (!Active || Executing || context == null || demands == null || demands.Count == 0)
			{
				return null;
			}
			if (_pending != null)
			{
				return _pending;
			}
			List<Container> matching = ContainerRegistry.GetMatching(context);
			if (matching.Count == 0)
			{
				return null;
			}
			PendingAction pendingAction = new PendingAction
			{
				Context = context,
				Started = Time.realtimeSinceStartup
			};
			for (int i = 0; i < matching.Count; i++)
			{
				Container val = matching[i];
				if (IsConcurrentRemote(val))
				{
					pendingAction.Concurrent.Add(val);
				}
			}
			if (pendingAction.Concurrent.Count == 0)
			{
				return null;
			}
			bool flag = false;
			bool flag2 = true;
			for (int j = 0; j < demands.Count; j++)
			{
				Demand demand = demands[j];
				if (demand == null || string.IsNullOrEmpty(demand.Name) || demand.Amount <= 0)
				{
					continue;
				}
				pendingAction.Demands.Add(demand);
				int num = demand.Amount;
				if (!demand.IgnorePlayer)
				{
					Player localPlayer = Player.m_localPlayer;
					Inventory val2 = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null);
					if (val2 != null)
					{
						num -= Mathf.Min(num, Count(val2, demand));
					}
				}
				if (num <= 0)
				{
					continue;
				}
				for (int k = 0; k < matching.Count; k++)
				{
					if (num <= 0)
					{
						break;
					}
					Container val3 = matching[k];
					if ((Object)(object)val3 == (Object)null || ((Object)(object)demand.SourceOnly != (Object)null && (Object)(object)val3 != (Object)(object)demand.SourceOnly))
					{
						continue;
					}
					Inventory inventory = val3.GetInventory();
					if (inventory == null)
					{
						continue;
					}
					if (!pendingAction.Concurrent.Contains(val3))
					{
						int num2 = Count(inventory, demand);
						num -= Mathf.Min(num, num2);
						continue;
					}
					int num3 = RequestFromConcurrent(pendingAction, val3, inventory, demand, num);
					if (num3 > 0)
					{
						flag = true;
						num -= num3;
					}
				}
				if (num > 0)
				{
					flag2 = false;
				}
			}
			if (!flag)
			{
				return null;
			}
			if (!flag2 && pendingAction.RequestIds.Count == 0)
			{
				pendingAction.Blocked = true;
			}
			_pending = pendingAction;
			return pendingAction;
		}

		private static int RequestFromConcurrent(PendingAction pending, Container container, Inventory source, Demand demand, int amount)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			if (pending == null || (Object)(object)container == (Object)null || source == null || demand == null || amount <= 0)
			{
				return 0;
			}
			if (!pending.Reserves.TryGetValue(container, out var value))
			{
				value = new Inventory("Wire MUC Reservation", (Sprite)null, 20, 20);
				pending.Reserves.Add(container, value);
			}
			int num = 0;
			List<ItemData> list = RefillSelection.BottomLeftFirst(source);
			for (int i = 0; i < list.Count; i++)
			{
				if (num >= amount)
				{
					break;
				}
				ItemData val = list[i];
				if (!Matches(val, demand))
				{
					continue;
				}
				int amount2 = Mathf.Min(val.m_stack, amount - num);
				int requestId;
				int num2 = RequestRemove(container, val, value, amount2, out requestId);
				if (num2 > 0)
				{
					num += num2;
					if (requestId != 0)
					{
						pending.RequestIds.Add(requestId);
					}
				}
			}
			return num;
		}

		private static int RequestRemove(Container container, ItemData item, Inventory reserve, int amount, out int requestId)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			requestId = 0;
			if (!_available || (Object)(object)container == (Object)null || item == null || reserve == null || amount <= 0)
			{
				return 0;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return 0;
			}
			int num = reserve.CountItems(item.m_shared.m_name, item.m_quality, false);
			try
			{
				object obj = _removeItemFromChest.Invoke(null, new object[7]
				{
					container,
					item,
					reserve,
					(object)new Vector2i(-1, -1),
					((Character)localPlayer).GetZDOID(),
					amount,
					null
				});
				if (obj == null)
				{
					int num2 = reserve.CountItems(item.m_shared.m_name, item.m_quality, false);
					return Mathf.Max(0, num2 - num);
				}
				Type type = obj.GetType();
				PropertyInfo property = type.GetProperty("RequestID");
				FieldInfo field = type.GetField("dragAmount");
				if (property != null)
				{
					requestId = (int)property.GetValue(obj, null);
				}
				return (field != null) ? Mathf.Max(0, (int)field.GetValue(obj)) : amount;
			}
			catch (Exception ex)
			{
				if (Plugin.Log != null)
				{
					Plugin.Log.LogWarning((object)("MultiUserChest reservation failed: " + ex.Message));
				}
				return 0;
			}
		}

		private static bool CanSatisfy(PendingAction pending)
		{
			if (pending == null || pending.Context == null)
			{
				return false;
			}
			List<Container> matching = ContainerRegistry.GetMatching(pending.Context);
			for (int i = 0; i < pending.Demands.Count; i++)
			{
				Demand demand = pending.Demands[i];
				int num = demand.Amount;
				if (!demand.IgnorePlayer)
				{
					Player localPlayer = Player.m_localPlayer;
					Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null);
					if (val != null)
					{
						num -= Mathf.Min(num, Count(val, demand));
					}
				}
				for (int j = 0; j < matching.Count && num > 0; j++)
				{
					Container val2 = matching[j];
					if ((Object)(object)demand.SourceOnly != (Object)null && (Object)(object)val2 != (Object)(object)demand.SourceOnly)
					{
						continue;
					}
					Inventory value = null;
					if (!pending.Reserves.TryGetValue(val2, out value))
					{
						if (pending.Concurrent.Contains(val2))
						{
							continue;
						}
						value = (((Object)(object)val2 != (Object)null) ? val2.GetInventory() : null);
					}
					if (value != null)
					{
						num -= Mathf.Min(num, Count(value, demand));
					}
				}
				if (num > 0)
				{
					return false;
				}
			}
			return true;
		}

		private static int Count(Inventory inventory, Demand demand)
		{
			if (inventory == null || demand == null)
			{
				return 0;
			}
			int num = 0;
			List<ItemData> allItems = inventory.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (Matches(val, demand))
				{
					num += val.m_stack;
				}
			}
			return num;
		}

		private static bool Matches(ItemData item, Demand demand)
		{
			if (item == null || item.m_shared == null || demand == null || item.m_shared.m_name != demand.Name)
			{
				return false;
			}
			if (demand.Quality >= 0 && item.m_quality != demand.Quality)
			{
				return false;
			}
			if (demand.MatchWorldLevel && item.m_worldLevel < Game.m_worldLevel)
			{
				return false;
			}
			if (!string.IsNullOrEmpty(demand.PrefabName) && ((Object)(object)item.m_dropPrefab == (Object)null || ((Object)item.m_dropPrefab).name != demand.PrefabName))
			{
				return false;
			}
			return true;
		}

		private static bool RequestExists(int id)
		{
			if (!_available || id == 0)
			{
				return false;
			}
			try
			{
				return _getPackageGeneric.MakeGenericMethod(_iPackageType).Invoke(null, new object[1] { id }) != null;
			}
			catch
			{
				return false;
			}
		}

		internal static ItemData FindReservedItem(string name, int quality, string prefabName)
		{
			if (_active == null)
			{
				return null;
			}
			List<Container> matching = ContainerRegistry.GetMatching(_active.Context);
			for (int i = 0; i < matching.Count; i++)
			{
				if (!_active.Reserves.TryGetValue(matching[i], out var value))
				{
					continue;
				}
				List<ItemData> list = RefillSelection.BottomLeftFirst(value);
				for (int j = 0; j < list.Count; j++)
				{
					ItemData val = list[j];
					if (val != null && val.m_shared != null && !(val.m_shared.m_name != name) && (quality < 0 || val.m_quality == quality) && (string.IsNullOrEmpty(prefabName) || (!((Object)(object)val.m_dropPrefab == (Object)null) && !(((Object)val.m_dropPrefab).name != prefabName))))
					{
						return val;
					}
				}
			}
			return null;
		}

		internal static object[] RefreshItemArgs(object[] args)
		{
			if (args == null || _active == null)
			{
				return args;
			}
			object[] array = (object[])args.Clone();
			for (int i = 0; i < array.Length; i++)
			{
				object obj = array[i];
				ItemData val = (ItemData)((obj is ItemData) ? obj : null);
				if (val == null || val.m_shared == null)
				{
					continue;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer != (Object)null) || !((Humanoid)localPlayer).GetInventory().ContainsItem(val))
				{
					string prefabName = (((Object)(object)val.m_dropPrefab != (Object)null) ? ((Object)val.m_dropPrefab).name : null);
					ItemData val2 = FindReservedItem(val.m_shared.m_name, val.m_quality, prefabName);
					if (val2 != null)
					{
						array[i] = val2;
					}
				}
			}
			return array;
		}

		private static void RefundLeftovers(PendingAction pending)
		{
			if (pending == null || pending.Reserves.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Container, Inventory> reserf in pending.Reserves)
			{
				if (!((Object)(object)reserf.Key == (Object)null) && reserf.Value != null && reserf.Value.NrOfItems() != 0)
				{
					RefundJob item = new RefundJob
					{
						Container = reserf.Key,
						Reserve = reserf.Value,
						NextTry = Time.realtimeSinceStartup
					};
					Refunds.Add(item);
				}
			}
		}

		private static void UpdateRefunds()
		{
			if (!Active || Refunds.Count == 0)
			{
				return;
			}
			for (int num = Refunds.Count - 1; num >= 0; num--)
			{
				RefundJob refundJob = Refunds[num];
				if (refundJob == null || refundJob.Reserve == null)
				{
					Refunds.RemoveAt(num);
				}
				else
				{
					bool flag = false;
					for (int num2 = refundJob.RequestIds.Count - 1; num2 >= 0; num2--)
					{
						if (RequestExists(refundJob.RequestIds[num2]))
						{
							flag = true;
						}
						else
						{
							refundJob.RequestIds.RemoveAt(num2);
						}
					}
					if (!flag)
					{
						if (refundJob.Reserve.NrOfItems() == 0)
						{
							Refunds.RemoveAt(num);
						}
						else if ((Object)(object)refundJob.Container == (Object)null)
						{
							ReturnReserveToPlayer(refundJob.Reserve);
							Refunds.RemoveAt(num);
						}
						else if (!(Time.realtimeSinceStartup < refundJob.NextTry))
						{
							refundJob.NextTry = Time.realtimeSinceStartup + 0.5f;
							StartRefund(refundJob);
						}
					}
				}
			}
		}

		private static void StartRefund(RefundJob job)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (job == null || (Object)(object)job.Container == (Object)null || job.Reserve == null || (Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			List<ItemData> list = new List<ItemData>(job.Reserve.GetAllItems());
			for (int i = 0; i < list.Count; i++)
			{
				ItemData val = list[i];
				if (val == null || val.m_stack <= 0)
				{
					continue;
				}
				try
				{
					object obj = _addItemToChest.Invoke(null, new object[6]
					{
						job.Container,
						val,
						job.Reserve,
						(object)new Vector2i(-1, -1),
						((Character)localPlayer).GetZDOID(),
						val.m_stack
					});
					if (obj == null)
					{
						continue;
					}
					PropertyInfo property = obj.GetType().GetProperty("RequestID");
					if (property != null)
					{
						int num = (int)property.GetValue(obj, null);
						if (num != 0)
						{
							job.RequestIds.Add(num);
						}
					}
				}
				catch (Exception ex)
				{
					if (Plugin.Log != null)
					{
						Plugin.Log.LogWarning((object)("MultiUserChest refund failed: " + ex.Message));
					}
				}
			}
		}

		private static void ReturnReserveToPlayer(Inventory reserve)
		{
			Player localPlayer = Player.m_localPlayer;
			if (reserve == null || (Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			List<ItemData> list = new List<ItemData>(reserve.GetAllItems());
			for (int i = 0; i < list.Count; i++)
			{
				ItemData val = list[i];
				if (val != null)
				{
					inventory.MoveItemToThis(reserve, val);
				}
			}
		}

		internal static void Reset()
		{
			if (_pending != null)
			{
				RefundLeftovers(_pending);
			}
			if (_active != null)
			{
				RefundLeftovers(_active);
			}
			_pending = null;
			_active = null;
			_initialized = false;
			_available = false;
			_removeItemFromChest = null;
			_addItemToChest = null;
			_getPackageGeneric = null;
			_iPackageType = null;
		}
	}
	[BepInPlugin("wire.valheim.balancedcraftfromnearbychests", "Wire's Balanced Craft From Nearby Chests", "0.1.1")]
	[BepInProcess("valheim.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "wire.valheim.balancedcraftfromnearbychests";

		public const string Name = "Wire's Balanced Craft From Nearby Chests";

		public const string Version = "0.1.1";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL