Decompiled source of Crafting Storage Link v1.7.1

BepInEx/plugins/CraftingStorageLink/CraftingStorageLink.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[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("CraftingStorageLink")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyInformationalVersion("1.7.1")]
[assembly: AssemblyProduct("CraftingStorageLink")]
[assembly: AssemblyTitle("CraftingStorageLink")]
[assembly: AssemblyVersion("1.7.1.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 CraftingStorageLink
{
	[BepInPlugin("com.custom.valheim.craftingstoragelink", "CraftingStorageLink", "1.7.1")]
	public sealed class CraftingStorageLinkPlugin : BaseUnityPlugin
	{
		private sealed class CraftContext
		{
			public readonly Player Player;

			public readonly Inventory PlayerInventory;

			public readonly CraftingStation Station;

			public readonly Recipe Recipe;

			public CraftContext(Player player, CraftingStation station, Recipe recipe)
			{
				Player = player;
				PlayerInventory = ((Humanoid)player).GetInventory();
				Station = station;
				Recipe = recipe;
			}
		}

		private sealed class BuildContext
		{
			public readonly Player Player;

			public readonly Inventory PlayerInventory;

			public readonly CraftingStation Station;

			public readonly Piece Piece;

			public readonly Vector3 Origin;

			public BuildContext(Player player, CraftingStation station, Piece piece)
			{
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				Player = player;
				PlayerInventory = ((Humanoid)player).GetInventory();
				Station = station;
				Piece = piece;
				Origin = (((Object)(object)station != (Object)null) ? ((Component)station).transform.position : ((Component)player).transform.position);
			}
		}

		private sealed class PullItem
		{
			public readonly string Name;

			public readonly int Quality;

			public int Required;

			public int BeforeTotal;

			public PullItem(string name, int quality, int required)
			{
				Name = name;
				Quality = quality;
				Required = required;
			}
		}

		private sealed class PullPlan
		{
			public readonly Player Player;

			public readonly CraftingStation Station;

			public readonly Vector3 Origin;

			public readonly List<PullItem> Items = new List<PullItem>();

			public PullPlan(Player player, CraftingStation station, Vector3 origin)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				Player = player;
				Station = station;
				Origin = origin;
			}

			public void AddRequired(string name, int quality, int amount)
			{
				if (amount <= 0)
				{
					return;
				}
				for (int i = 0; i < Items.Count; i++)
				{
					PullItem pullItem = Items[i];
					if (pullItem.Name == name && pullItem.Quality == quality)
					{
						pullItem.Required += amount;
						return;
					}
				}
				Items.Add(new PullItem(name, quality, amount));
			}
		}

		private sealed class ContainerCandidate
		{
			public readonly Container Container;

			public readonly float DistanceSqr;

			public ContainerCandidate(Container container, float distanceSqr)
			{
				Container = container;
				DistanceSqr = distanceSqr;
			}
		}

		private sealed class ProcessorFeedTransaction
		{
			public readonly Player Player;

			public readonly Smelter Processor;

			public readonly Container SourceContainer;

			public readonly ItemData RestoreTemplate;

			public readonly string ItemName;

			public readonly int Quality;

			public readonly int PlayerCountBefore;

			public readonly string InteractionName;

			public ProcessorFeedTransaction(Player player, Smelter processor, Container sourceContainer, ItemData restoreTemplate, string itemName, int quality, int playerCountBefore, string interactionName)
			{
				Player = player;
				Processor = processor;
				SourceContainer = sourceContainer;
				RestoreTemplate = restoreTemplate;
				ItemName = itemName;
				Quality = quality;
				PlayerCountBefore = playerCountBefore;
				InteractionName = interactionName;
			}
		}

		private sealed class LinkedCountFrameEntry
		{
			public readonly int Count;

			public readonly bool Complete;

			public LinkedCountFrameEntry(int count, bool complete)
			{
				Count = count;
				Complete = complete;
			}
		}

		private enum LinkContextKind
		{
			Disabled,
			RecipeRequirement,
			RecipeDisplay,
			Crafting,
			BuildRequirement,
			BuildHudDisplay,
			Placement
		}

		private sealed class LinkContext
		{
			public readonly LinkContextKind Kind;

			public readonly Player Player;

			public readonly Inventory PlayerInventory;

			public readonly CraftingStation Station;

			public readonly Recipe Recipe;

			public readonly Piece Piece;

			public readonly Vector3 Origin;

			public readonly bool HasOrigin;

			public readonly bool Enabled;

			private LinkContext(LinkContextKind kind)
			{
				Kind = kind;
				Enabled = false;
			}

			public LinkContext(LinkContextKind kind, Player player, CraftingStation station, Recipe recipe)
			{
				//IL_0059: Unknown result type (might be due to invalid IL or missing references)
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				Kind = kind;
				Player = player;
				PlayerInventory = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
				Station = station;
				Recipe = recipe;
				Piece = null;
				Origin = (((Object)(object)station != (Object)null) ? ((Component)station).transform.position : Vector3.zero);
				HasOrigin = (Object)(object)station != (Object)null;
				Enabled = (Object)(object)player != (Object)null && PlayerInventory != null && HasOrigin;
			}

			public LinkContext(LinkContextKind kind, Player player, CraftingStation station, Piece piece)
			{
				//IL_006f: 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)
				//IL_0062: 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)
				Kind = kind;
				Player = player;
				PlayerInventory = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
				Station = station;
				Recipe = null;
				Piece = piece;
				Origin = (((Object)(object)station != (Object)null) ? ((Component)station).transform.position : (((Object)(object)player != (Object)null) ? ((Component)player).transform.position : Vector3.zero));
				HasOrigin = (Object)(object)player != (Object)null;
				Enabled = (Object)(object)player != (Object)null && PlayerInventory != null && (Object)(object)piece != (Object)null && HasOrigin;
			}

			public static LinkContext Disabled(LinkContextKind kind)
			{
				return new LinkContext(kind);
			}
		}

		private sealed class ContextGuard
		{
			private readonly LinkContext _context;

			private bool _disposed;

			public ContextGuard(LinkContext context)
			{
				_context = context;
			}

			public void Dispose()
			{
				if (_disposed)
				{
					return;
				}
				_disposed = true;
				Stack<LinkContext> contextStack = GetContextStack();
				if (contextStack.Count == 0)
				{
					return;
				}
				if (contextStack.Peek() == _context)
				{
					contextStack.Pop();
					return;
				}
				Stack<LinkContext> stack = new Stack<LinkContext>();
				while (contextStack.Count > 0 && contextStack.Peek() != _context)
				{
					stack.Push(contextStack.Pop());
				}
				if (contextStack.Count > 0)
				{
					contextStack.Pop();
				}
				while (stack.Count > 0)
				{
					contextStack.Push(stack.Pop());
				}
			}
		}

		[HarmonyPatch]
		private static class ContainerLifecycleRegistryPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				MethodInfo[] methods = typeof(Container).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				int i = 0;
				while (i < methods.Length)
				{
					MethodInfo methodInfo = methods[i];
					if (methodInfo.Name == "Awake" || methodInfo.Name == "OnDestroyed" || methodInfo.Name == "OnDestroy")
					{
						yield return methodInfo;
					}
					int num = i + 1;
					i = num;
				}
			}

			private static void Prefix(Container __instance, MethodBase __originalMethod)
			{
				if (!(__originalMethod == null) && !(__originalMethod.Name == "Awake"))
				{
					UnregisterContainer(__instance);
				}
			}

			private static void Postfix(Container __instance, MethodBase __originalMethod)
			{
				if (__originalMethod != null && __originalMethod.Name == "Awake")
				{
					RegisterContainer(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class ZNetOnNewConnectionPatch
		{
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				RegisterConfigRpc(__instance, peer);
			}
		}

		[HarmonyPatch]
		private static class PlayerHaveRequirementItemsPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				MethodInfo[] methods = typeof(Player).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				int i = 0;
				while (i < methods.Length)
				{
					MethodInfo methodInfo = methods[i];
					if (methodInfo.Name == "HaveRequirementItems" && methodInfo.ReturnType == typeof(bool))
					{
						yield return methodInfo;
					}
					int num = i + 1;
					i = num;
				}
			}

			private static void Prefix(Player __instance, MethodBase __originalMethod, object[] __args, ref ContextGuard __state)
			{
				try
				{
					Recipe val = null;
					bool discover = false;
					bool flag = false;
					ParameterInfo[] array = ((__originalMethod != null) ? __originalMethod.GetParameters() : null);
					if (__args != null)
					{
						for (int i = 0; i < __args.Length; i++)
						{
							object obj = __args[i];
							ParameterInfo parameterInfo = ((array != null && i < array.Length) ? array[i] : null);
							if ((Object)(object)val == (Object)null)
							{
								Recipe val2 = (Recipe)((obj is Recipe) ? obj : null);
								if (val2 != null)
								{
									val = val2;
								}
							}
							if (obj is bool flag2)
							{
								string a = parameterInfo?.Name;
								if (!flag || string.Equals(a, "discover", StringComparison.OrdinalIgnoreCase))
								{
									discover = flag2;
									flag = true;
									string.Equals(a, "discover", StringComparison.OrdinalIgnoreCase);
								}
							}
						}
					}
					__state = BeginRequirementCheck(__instance, val, discover);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-have-requirement-items", "CraftingStorageLink recovered while starting HaveRequirementItems context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.RecipeRequirement);
				}
			}

			private static void Postfix(ContextGuard __state)
			{
				__state?.Dispose();
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Recipe),
			typeof(bool),
			typeof(int),
			typeof(int)
		})]
		private static class PlayerHaveRecipeRequirementsPatch
		{
			private static void Prefix(Player __instance, Recipe recipe, bool discover, ref ContextGuard __state)
			{
				try
				{
					__state = BeginRequirementCheck(__instance, recipe, discover);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-recipe-requirements", "CraftingStorageLink recovered while starting a recipe requirement context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.RecipeRequirement);
				}
			}

			private static void Postfix(ContextGuard __state)
			{
				__state?.Dispose();
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement", new Type[]
		{
			typeof(Transform),
			typeof(Requirement),
			typeof(Player),
			typeof(bool),
			typeof(int),
			typeof(int)
		})]
		private static class InventoryGuiSetupRequirementPatch
		{
			private static void Prefix(InventoryGui __instance, object[] __args, ref ContextGuard __state)
			{
				try
				{
					Player player = (Player)((__args != null && __args.Length > 2) ? /*isinst with value type is only supported in some contexts*/: null);
					__state = BeginRequirementDisplay(__instance, player);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-recipe-display", "CraftingStorageLink recovered while starting a recipe display context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.RecipeDisplay);
				}
			}

			[HarmonyPriority(0)]
			private static void Postfix(InventoryGui __instance, object[] __args, ContextGuard __state)
			{
				__state?.Dispose();
				if (!ShowAvailableResourceCounts || __args == null || __args.Length < 6)
				{
					return;
				}
				try
				{
					object obj = __args[0];
					Transform row = (Transform)((obj is Transform) ? obj : null);
					object obj2 = __args[1];
					Requirement requirement = (Requirement)((obj2 is Requirement) ? obj2 : null);
					object obj3 = __args[2];
					Player player = (Player)((obj3 is Player) ? obj3 : null);
					int qualityLevel = ((__args[4] is int num) ? num : 0);
					int craftMultiplier = ((!(__args[5] is int num2)) ? 1 : num2);
					StabilizeCraftRequirementDisplay(__instance, row, requirement, player, qualityLevel, craftMultiplier);
				}
				catch (Exception ex)
				{
					DebugLog("Could not update crafting available-resource count: " + ex.GetType().Name + ": " + ex.Message);
				}
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
		private static class InventoryGuiDoCraftingPatch
		{
			private static void Prefix(InventoryGui __instance, Player player, ref ContextGuard __state)
			{
				try
				{
					__state = BeginCrafting(__instance, player);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-crafting", "CraftingStorageLink recovered while starting a crafting context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.Crafting);
				}
			}

			private static void Postfix(ContextGuard __state)
			{
				__state?.Dispose();
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static class PlayerHavePieceRequirementsPatch
		{
			private static void Prefix(Player __instance, Piece piece, RequirementMode mode, ref ContextGuard __state)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					__state = BeginBuildRequirementCheck(__instance, piece, mode);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-build-requirements", "CraftingStorageLink recovered while starting a build requirement context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.BuildRequirement);
				}
			}

			private static void Postfix(ContextGuard __state)
			{
				__state?.Dispose();
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement", new Type[]
		{
			typeof(bool),
			typeof(float)
		})]
		private static class PlayerUpdatePlacementPatch
		{
			private static void Prefix(Player __instance, ref ContextGuard __state)
			{
				try
				{
					__state = BeginPlacement(__instance);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-placement", "CraftingStorageLink recovered while starting a placement context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.Placement);
				}
			}

			private static void Postfix(ContextGuard __state)
			{
				__state?.Dispose();
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch]
		private static class HudSetupPieceInfoPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				MethodInfo[] methods = typeof(Hud).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				int i = 0;
				while (i < methods.Length)
				{
					if (methods[i].Name == "SetupPieceInfo")
					{
						yield return methods[i];
					}
					int num = i + 1;
					i = num;
				}
			}

			private static void Prefix(object[] __args, ref ContextGuard __state)
			{
				try
				{
					__state = BeginBuildHudDisplay(Player.m_localPlayer, __args);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("prefix-build-hud", "CraftingStorageLink recovered while starting a Hammer HUD context: " + ex.GetType().Name + ": " + ex.Message);
					__state = PushContext(null, LinkContextKind.BuildHudDisplay);
				}
			}

			private static void Postfix(Hud __instance, object[] __args, ContextGuard __state)
			{
				__state?.Dispose();
				try
				{
					Player localPlayer = Player.m_localPlayer;
					Piece piece = ResolveBuildHudPiece(localPlayer, __args);
					StabilizeBuildRequirementDisplay(__instance, localPlayer, piece);
				}
				catch (Exception ex)
				{
					DebugLog("Could not stabilize Hammer requirement display: " + ex.GetType().Name + ": " + ex.Message);
				}
			}

			private static Exception Finalizer(Exception __exception, ContextGuard __state)
			{
				__state?.Dispose();
				return __exception;
			}
		}

		[HarmonyPatch]
		private static class SmelterLinkedFeedingPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				MethodInfo[] methods = typeof(Smelter).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				int i = 0;
				while (i < methods.Length)
				{
					MethodInfo methodInfo = methods[i];
					if (methodInfo.Name == "OnAddFuel" || methodInfo.Name == "OnAddFuelSwitch" || methodInfo.Name == "OnAddOre")
					{
						yield return methodInfo;
					}
					int num = i + 1;
					i = num;
				}
			}

			[HarmonyPriority(800)]
			private static void Prefix(Smelter __instance, MethodBase __originalMethod, object[] __args, ref ProcessorFeedTransaction __state)
			{
				try
				{
					__state = BeginProcessorFeed(__instance, __originalMethod, __args);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("processor-feed-prefix-" + ((__originalMethod != null) ? __originalMethod.Name : "unknown"), "CraftingStorageLink recovered while staging a linked processor item: " + ex.GetType().Name + ": " + ex.Message);
					__state = null;
				}
			}

			[HarmonyPriority(0)]
			private static void Postfix(ProcessorFeedTransaction __state)
			{
				try
				{
					CompleteProcessorFeed(__state);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("processor-feed-postfix", "CraftingStorageLink recovered while finalizing a linked processor item: " + ex.GetType().Name + ": " + ex.Message);
				}
			}

			private static Exception Finalizer(Exception __exception, ProcessorFeedTransaction __state)
			{
				if (__exception != null)
				{
					try
					{
						CompleteProcessorFeed(__state);
					}
					catch
					{
					}
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
		{
			typeof(string),
			typeof(bool)
		})]
		private static class InventoryHaveItemPatch
		{
			private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
			{
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (!__result && __instance != null)
					{
						LinkContext currentContext = CurrentContext;
						if (currentContext != null && currentContext.Enabled && __instance == currentContext.PlayerInventory && (currentContext.Kind == LinkContextKind.BuildRequirement || currentContext.Kind == LinkContextKind.Placement))
						{
							__result = CountLinkedItem(currentContext.Player, currentContext.Station, currentContext.Origin, name, -1, matchWorldLevel, 1) > 0;
						}
					}
				}
				catch (Exception ex)
				{
					LogRuntimeFault("linked-haveitem-postfix", "CraftingStorageLink recovered from a HaveItem linked-storage failure: " + ex.GetType().Name + ": " + ex.Message);
					ClearContainerCache();
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "CountItems", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		})]
		private static class InventoryCountItemsPatch
		{
			private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
			{
				try
				{
					AddLinkedCount(__instance, name, quality, matchWorldLevel, ref __result);
				}
				catch (Exception ex)
				{
					LogRuntimeFault("inventory-countitems-postfix", "CraftingStorageLink recovered from Inventory.CountItems postfix: " + ex.GetType().Name + ": " + ex.Message);
					ClearContainerCache();
				}
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
		{
			typeof(Requirement[]),
			typeof(int),
			typeof(int),
			typeof(int)
		})]
		private static class PlayerConsumeResourcesPatch
		{
			private static void Prefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, ref PullPlan __state)
			{
				__state = CreatePullPlan(__instance, requirements, qualityLevel, itemQuality, multiplier);
			}

			[HarmonyPriority(0)]
			private static void Postfix(PullPlan __state)
			{
				PayPullPlan(__state);
			}
		}

		public const string PluginGuid = "com.custom.valheim.craftingstoragelink";

		public const string PluginName = "CraftingStorageLink";

		public const string PluginVersion = "1.7.1";

		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<bool> _craftingEnabled;

		private static ConfigEntry<bool> _hammerBuildingEnabled;

		private static ConfigEntry<bool> _processorFeedingEnabled;

		private static ConfigEntry<float> _radius;

		private static ConfigEntry<bool> _playerBuiltOnly;

		private static ConfigEntry<bool> _allowShipContainers;

		private static ConfigEntry<bool> _allowCartContainers;

		private static ConfigEntry<bool> _respectContainerAccess;

		private static ConfigEntry<bool> _respectWards;

		private static ConfigEntry<bool> _skipInUseContainers;

		private static ConfigEntry<bool> _claimOwnershipWhenPulling;

		private static ConfigEntry<bool> _respectMultiUserChestIgnore;

		private static ConfigEntry<bool> _showAvailableResourceCounts;

		private static ConfigEntry<KeyboardShortcut> _settingsMenuShortcut;

		private static ConfigEntry<bool> _clientMasterPreference;

		private static ConfigEntry<bool> _clientCraftingPreference;

		private static ConfigEntry<bool> _clientHammerPreference;

		private static ConfigEntry<bool> _clientProcessorPreference;

		private static ConfigEntry<bool> _debugLogging;

		private const string ConfigRpcName = "CSL_Config_v1";

		private const int ConfigProtocolVersion = 3;

		private static bool _serverConfigReceived;

		private static bool _serverEnabled;

		private static bool _serverCraftingEnabled;

		private static bool _serverHammerBuildingEnabled;

		private static bool _serverProcessorFeedingEnabled;

		private static float _serverRadius;

		private static bool _serverPlayerBuiltOnly;

		private static bool _serverAllowShipContainers;

		private static bool _serverAllowCartContainers;

		private static bool _serverRespectContainerAccess;

		private static bool _serverRespectWards;

		private static bool _serverSkipInUseContainers;

		private static bool _serverClaimOwnershipWhenPulling;

		private static bool _serverRespectMultiUserChestIgnore;

		private static ManualLogSource _log;

		private static FieldInfo _inventoryGuiCraftRecipeField;

		private static FieldInfo _inventoryGuiSelectedRecipeField;

		private static FieldInfo _inventoryGuiCurrentContainerField;

		private static FieldInfo _containerNViewField;

		private static MethodInfo _containerLoadMethod;

		private static MethodInfo _containerSaveMethod;

		private static MethodInfo _containerCheckAccessMethod;

		private static MethodInfo _inventoryAddItemDataMethod;

		private static MethodInfo _inventoryRemoveOneItemMethod;

		private static MethodInfo _itemDataCloneMethod;

		private static FieldInfo _smelterFuelItemField;

		private static FieldInfo _smelterFuelItemsField;

		private static FieldInfo _smelterConversionField;

		private static FieldInfo _smelterConversionsField;

		private static MethodInfo _znetGetPeersMethod;

		private bool _settingsWindowOpen;

		private Rect _settingsWindowRect = new Rect(48f, 48f, 500f, 475f);

		private Vector2 _settingsScrollPosition = Vector2.zero;

		private bool _savedCursorVisible;

		private CursorLockMode _savedCursorLockMode;

		[ThreadStatic]
		private static Stack<LinkContext> _contextStack;

		private static Player _cachedPlayer;

		private static CraftingStation _cachedStation;

		private static Vector3 _cachedOrigin;

		private static bool _cachedOriginSet;

		private static float _cachedContainersAtRealtime = -1000f;

		private static List<ContainerCandidate> _cachedContainers;

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

		private static bool _containerRegistryBootstrapped;

		private const float ContainerDiscoveryCacheSeconds = 0.25f;

		private const float ContainerDiscoveryOriginToleranceSqr = 1f;

		private static int _linkedCountCacheFrame = -1;

		private static readonly Dictionary<string, LinkedCountFrameEntry> _linkedCountFrameCache = new Dictionary<string, LinkedCountFrameEntry>();

		private Harmony _harmony;

		private int _lastLocalPlayerInstanceId = int.MinValue;

		private static readonly Dictionary<string, float> _runtimeFaultLogTimes = new Dictionary<string, float>();

		private const float RuntimeFaultLogIntervalSeconds = 10f;

		private static bool UseSyncedServerConfig
		{
			get
			{
				if (_serverConfigReceived && (Object)(object)ZNet.instance != (Object)null)
				{
					return !ZNet.instance.IsServer();
				}
				return false;
			}
		}

		private static bool ApplyClientPreferences
		{
			get
			{
				if (!((Object)(object)ZNet.instance == (Object)null))
				{
					return !ZNet.instance.IsServer();
				}
				return true;
			}
		}

		private static bool EffectiveEnabled
		{
			get
			{
				if (UseSyncedServerConfig ? _serverEnabled : _enabled.Value)
				{
					return ClientPreference(_clientMasterPreference);
				}
				return false;
			}
		}

		private static bool EffectiveCraftingEnabled
		{
			get
			{
				if (UseSyncedServerConfig ? _serverCraftingEnabled : _craftingEnabled.Value)
				{
					return ClientPreference(_clientCraftingPreference);
				}
				return false;
			}
		}

		private static bool EffectiveHammerBuildingEnabled
		{
			get
			{
				if (UseSyncedServerConfig ? _serverHammerBuildingEnabled : _hammerBuildingEnabled.Value)
				{
					return ClientPreference(_clientHammerPreference);
				}
				return false;
			}
		}

		private static bool EffectiveProcessorFeedingEnabled
		{
			get
			{
				if (UseSyncedServerConfig ? _serverProcessorFeedingEnabled : _processorFeedingEnabled.Value)
				{
					return ClientPreference(_clientProcessorPreference);
				}
				return false;
			}
		}

		private static float EffectiveRadius
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _radius.Value;
				}
				return _serverRadius;
			}
		}

		private static bool EffectivePlayerBuiltOnly
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _playerBuiltOnly.Value;
				}
				return _serverPlayerBuiltOnly;
			}
		}

		private static bool EffectiveAllowShipContainers
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _allowShipContainers.Value;
				}
				return _serverAllowShipContainers;
			}
		}

		private static bool EffectiveAllowCartContainers
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _allowCartContainers.Value;
				}
				return _serverAllowCartContainers;
			}
		}

		private static bool EffectiveRespectContainerAccess
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _respectContainerAccess.Value;
				}
				return _serverRespectContainerAccess;
			}
		}

		private static bool EffectiveRespectWards
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _respectWards.Value;
				}
				return _serverRespectWards;
			}
		}

		private static bool EffectiveSkipInUseContainers
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _skipInUseContainers.Value;
				}
				return _serverSkipInUseContainers;
			}
		}

		private static bool EffectiveClaimOwnershipWhenPulling
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _claimOwnershipWhenPulling.Value;
				}
				return _serverClaimOwnershipWhenPulling;
			}
		}

		private static bool EffectiveRespectMultiUserChestIgnore
		{
			get
			{
				if (!UseSyncedServerConfig)
				{
					return _respectMultiUserChestIgnore.Value;
				}
				return _serverRespectMultiUserChestIgnore;
			}
		}

		private static LinkContext CurrentContext
		{
			get
			{
				Stack<LinkContext> contextStack = GetContextStack();
				if (contextStack.Count <= 0)
				{
					return null;
				}
				return contextStack.Peek();
			}
		}

		private static bool ShowAvailableResourceCounts
		{
			get
			{
				if (_showAvailableResourceCounts != null)
				{
					return _showAvailableResourceCounts.Value;
				}
				return false;
			}
		}

		private static bool ClientPreference(ConfigEntry<bool> entry)
		{
			if (ApplyClientPreferences && entry != null)
			{
				return entry.Value;
			}
			return true;
		}

		private static Stack<LinkContext> GetContextStack()
		{
			if (_contextStack == null)
			{
				_contextStack = new Stack<LinkContext>();
			}
			return _contextStack;
		}

		private static ContextGuard PushContext(LinkContext context, LinkContextKind kind)
		{
			LinkContext linkContext = context ?? LinkContext.Disabled(kind);
			GetContextStack().Push(linkContext);
			return new ContextGuard(linkContext);
		}

		private void Awake()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Expected O, but got Unknown
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Expected O, but got Unknown
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Expected O, but got Unknown
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Expected O, but got Unknown
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ec: Expected O, but got Unknown
			//IL_056e: Unknown result type (might be due to invalid IL or missing references)
			_log = ((BaseUnityPlugin)this).Logger;
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "Enabled", true, new ConfigDescription("Master switch for CraftingStorageLink. When false, recipe crafting, Hammer/build-tool linking, and processor linked feeding are disabled.", (AcceptableValueBase)null, Array.Empty<object>()));
			_craftingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "CraftingEnabled", true, new ConfigDescription("Allow crafting and upgrading in crafting-station UIs to count and consume resources from linked storage. Set false while leaving HammerBuildingEnabled=true for Hammer-only mode.", (AcceptableValueBase)null, Array.Empty<object>()));
			_hammerBuildingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "HammerBuildingEnabled", true, new ConfigDescription("Allow Hammer/build-tool placement costs to count and consume resources from nearby storage. Station-required pieces link around that station; stationless pieces such as Workbenches and Campfires link around the player.", (AcceptableValueBase)null, Array.Empty<object>()));
			_processorFeedingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - General", "ProcessorFeedingEnabled", true, new ConfigDescription("Allow normal player interactions with Smelter-based processors (smelters, blast furnaces, kilns, windmills, spinning wheels, eitr refineries, and compatible modded derivatives) to source one valid fuel/input item from linked storage when the player is not carrying a valid item. Vanilla processor capacity, queueing and RPC behavior remain in control.", (AcceptableValueBase)null, Array.Empty<object>()));
			_radius = ((BaseUnityPlugin)this).Config.Bind<float>("02 - Linking", "Radius", 20f, new ConfigDescription("Radius in metres used to find storage containers. Crafting uses the active station; stationless Hammer pieces use the player position; processor feeding uses the processor position.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			_playerBuiltOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("02 - Linking", "PlayerBuiltContainersOnly", true, new ConfigDescription("Ignore world-generated loot containers and only use containers attached to player-built pieces.", (AcceptableValueBase)null, Array.Empty<object>()));
			_allowShipContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("02 - Linking", "AllowShipContainers", false, new ConfigDescription("Allow storage on ships to be linked. Disabled by default because moving networked containers are higher risk.", (AcceptableValueBase)null, Array.Empty<object>()));
			_allowCartContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("02 - Linking", "AllowCartContainers", false, new ConfigDescription("Allow cart storage to be linked. Disabled by default because moving networked containers are higher risk.", (AcceptableValueBase)null, Array.Empty<object>()));
			_respectContainerAccess = ((BaseUnityPlugin)this).Config.Bind<bool>("03 - Safety", "RespectContainerAccess", true, new ConfigDescription("Do not use private/personal containers that the crafting player cannot access.", (AcceptableValueBase)null, Array.Empty<object>()));
			_respectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("03 - Safety", "RespectWards", true, new ConfigDescription("Do not use containers blocked by ward/private-area access.", (AcceptableValueBase)null, Array.Empty<object>()));
			_skipInUseContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("03 - Safety", "SkipInUseContainers", true, new ConfigDescription("Ignore containers that are in use by another player. The local player's currently open container remains eligible. Set this false only when your shared-container setup is transaction-safe.", (AcceptableValueBase)null, Array.Empty<object>()));
			_claimOwnershipWhenPulling = ((BaseUnityPlugin)this).Config.Bind<bool>("03 - Safety", "ClaimOwnershipWhenPulling", true, new ConfigDescription("If required, claim ZNetView ownership immediately before consuming from a linked container. Disable only for troubleshooting; non-owned containers will then be ignored.", (AcceptableValueBase)null, Array.Empty<object>()));
			_respectMultiUserChestIgnore = ((BaseUnityPlugin)this).Config.Bind<bool>("03 - Safety", "RespectMultiUserChestIgnore", true, new ConfigDescription("Honor MultiUserChest's MUC_Ignore flag when present on a container.", (AcceptableValueBase)null, Array.Empty<object>()));
			_showAvailableResourceCounts = ((BaseUnityPlugin)this).Config.Bind<bool>("04 - UI", "ShowAvailableResourceCounts", true, new ConfigDescription("Show total available resources (player inventory + linked storage) as Available/Required in crafting and Hammer requirement rows. This is a local display preference and is not server-synced.", (AcceptableValueBase)null, Array.Empty<object>()));
			_settingsMenuShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("04 - UI", "SettingsMenuShortcut", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Open/close the built-in CraftingStorageLink settings window. The default is F8.");
			_clientMasterPreference = ((BaseUnityPlugin)this).Config.Bind<bool>("05 - Client Preferences", "UseCraftingStorageLink", true, "Local opt-out for this client. On a dedicated server this cannot override a server-side disabled setting.");
			_clientCraftingPreference = ((BaseUnityPlugin)this).Config.Bind<bool>("05 - Client Preferences", "UseLinkedCrafting", true, "Local preference for linked crafting/upgrading when the server allows it.");
			_clientHammerPreference = ((BaseUnityPlugin)this).Config.Bind<bool>("05 - Client Preferences", "UseLinkedBuilding", true, "Local preference for linked Hammer/build-tool costs when the server allows it.");
			_clientProcessorPreference = ((BaseUnityPlugin)this).Config.Bind<bool>("05 - Client Preferences", "UseLinkedProcessorFeeding", true, "Local preference for linked smelter/refinery/processor feeding when the server allows it.");
			_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("99 - Debug", "DebugLogging", false, "Log linked container discovery and resource pulls on this machine.");
			_inventoryGuiCraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");
			_inventoryGuiSelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");
			_inventoryGuiCurrentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");
			_containerNViewField = AccessTools.Field(typeof(Container), "m_nview");
			_containerLoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null);
			_containerSaveMethod = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null);
			_containerCheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);
			_inventoryAddItemDataMethod = ResolveSingleItemDataMethod(typeof(Inventory), "AddItem");
			_inventoryRemoveOneItemMethod = ResolveSingleItemDataMethod(typeof(Inventory), "RemoveOneItem");
			_itemDataCloneMethod = AccessTools.Method(typeof(ItemData), "Clone", Type.EmptyTypes, (Type[])null);
			_smelterFuelItemField = FindFieldNoWarning(typeof(Smelter), "m_fuelItem");
			_smelterFuelItemsField = FindFieldNoWarning(typeof(Smelter), "m_fuelItems");
			_smelterConversionField = FindFieldNoWarning(typeof(Smelter), "m_conversion");
			_smelterConversionsField = FindFieldNoWarning(typeof(Smelter), "m_conversions");
			_znetGetPeersMethod = AccessTools.Method(typeof(ZNet), "GetPeers", (Type[])null, (Type[])null);
			_harmony = new Harmony("com.custom.valheim.craftingstoragelink");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. BepInEx-only build; crafting linking {2}; hammer linking {3}; processor feeding {4}; configured radius {5:0.#}m; settings GUI {6}.", "CraftingStorageLink", "1.7.1", EffectiveCraftingEnabled ? "on" : "off", EffectiveHammerBuildingEnabled ? "on" : "off", EffectiveProcessorFeedingEnabled ? "on" : "off", EffectiveRadius, _settingsMenuShortcut.Value));
			if (_inventoryAddItemDataMethod == null || _itemDataCloneMethod == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Processor linked-feeding compatibility is incomplete: Inventory.AddItem(ItemData) or ItemData.Clone() could not be resolved. Crafting/Hammer linking still works normally; processor staging will safely no-op.");
			}
		}

		private void Update()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Application.isBatchMode || _settingsMenuShortcut == null)
			{
				return;
			}
			try
			{
				KeyboardShortcut value = _settingsMenuShortcut.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					SetSettingsWindowOpen(!_settingsWindowOpen);
				}
			}
			catch (Exception ex)
			{
				LogRuntimeFault("settings-gui-hotkey", "CraftingStorageLink settings hotkey failed safely: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private void OnGUI()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//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 (Application.isBatchMode || !_settingsWindowOpen)
			{
				return;
			}
			try
			{
				_settingsWindowRect = GUI.Window(1129532439, _settingsWindowRect, new WindowFunction(DrawSettingsWindow), "CraftingStorageLink 1.7.1");
			}
			catch (Exception ex)
			{
				SetSettingsWindowOpen(open: false);
				LogRuntimeFault("settings-gui-draw", "CraftingStorageLink settings GUI closed after a draw error: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private void DrawSettingsWindow(int windowId)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				wordWrap = true
			};
			GUIStyle val2 = new GUIStyle(GUI.skin.box);
			bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
			bool flag2 = (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && _serverConfigReceived;
			_settingsScrollPosition = GUILayout.BeginScrollView(_settingsScrollPosition, false, true, Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.Label(flag ? "Host / single-player controls" : "Client preferences", val2, Array.Empty<GUILayoutOption>());
			if (flag2)
			{
				GUILayout.Label("The server remains authoritative. These switches only let this client opt out of features the server allows; they cannot enable a feature the server has disabled.", val, Array.Empty<GUILayoutOption>());
			}
			else if (flag)
			{
				GUILayout.Label("Changes below are applied immediately, saved to the local config, and pushed to connected modded clients.", val, Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.Label("Changes below are saved immediately. Server policy will be applied automatically after joining a dedicated server.", val, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(6f);
			if (flag)
			{
				DrawServerGameplayToggle("Enable CraftingStorageLink", _enabled);
				DrawServerGameplayToggle("Linked crafting & upgrading", _craftingEnabled);
				DrawServerGameplayToggle("Linked Hammer / build costs", _hammerBuildingEnabled);
				DrawServerGameplayToggle("Linked smelter / processor feeding", _processorFeedingEnabled);
				GUILayout.Space(6f);
				GUILayout.Label($"Linked-storage radius: {_radius.Value:0.#} m", val2, Array.Empty<GUILayoutOption>());
				float num = GUILayout.HorizontalSlider(_radius.Value, 1f, 100f, Array.Empty<GUILayoutOption>());
				num = Mathf.Round(num);
				if (Math.Abs(num - _radius.Value) >= 0.5f)
				{
					_radius.Value = num;
					SaveGuiConfigAndRefresh(broadcastServerConfig: true);
				}
			}
			else
			{
				DrawClientPreferenceToggle("Use CraftingStorageLink on this client", _clientMasterPreference, flag2 ? new bool?(_serverEnabled) : ((bool?)null));
				DrawClientPreferenceToggle("Use linked crafting & upgrading", _clientCraftingPreference, flag2 ? new bool?(_serverEnabled && _serverCraftingEnabled) : ((bool?)null));
				DrawClientPreferenceToggle("Use linked Hammer / build costs", _clientHammerPreference, flag2 ? new bool?(_serverEnabled && _serverHammerBuildingEnabled) : ((bool?)null));
				DrawClientPreferenceToggle("Use linked smelter / processor feeding", _clientProcessorPreference, flag2 ? new bool?(_serverEnabled && _serverProcessorFeedingEnabled) : ((bool?)null));
				if (flag2)
				{
					GUILayout.Space(4f);
					GUILayout.Label($"Server linked-storage radius: {_serverRadius:0.#} m", val, Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.Space(8f);
			GUILayout.Label("Local display / diagnostics", val2, Array.Empty<GUILayoutOption>());
			DrawLocalToggle("Show Available/Required resource counters", _showAvailableResourceCounts);
			DrawLocalToggle("Debug logging", _debugLogging);
			GUILayout.Space(8f);
			GUILayout.Label($"Open / close: {_settingsMenuShortcut.Value}. This menu does not pause the game.", val, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (!flag && GUILayout.Button("Reset local preferences", Array.Empty<GUILayoutOption>()))
			{
				_clientMasterPreference.Value = true;
				_clientCraftingPreference.Value = true;
				_clientHammerPreference.Value = true;
				_clientProcessorPreference.Value = true;
				_showAvailableResourceCounts.Value = true;
				_debugLogging.Value = false;
				SaveGuiConfigAndRefresh(broadcastServerConfig: false);
			}
			GUILayout.FlexibleSpace();
			if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }))
			{
				SetSettingsWindowOpen(open: false);
			}
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			GUILayout.EndScrollView();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 28f));
		}

		private void DrawServerGameplayToggle(string label, ConfigEntry<bool> entry)
		{
			if (entry != null)
			{
				bool flag = GUILayout.Toggle(entry.Value, label, Array.Empty<GUILayoutOption>());
				if (flag != entry.Value)
				{
					entry.Value = flag;
					SaveGuiConfigAndRefresh(broadcastServerConfig: true);
				}
			}
		}

		private void DrawClientPreferenceToggle(string label, ConfigEntry<bool> entry, bool? serverAllowed)
		{
			if (entry != null)
			{
				string text = ((!serverAllowed.HasValue) ? string.Empty : (serverAllowed.Value ? "  [server allows]" : "  [server disabled]"));
				bool flag = GUILayout.Toggle(entry.Value, label + text, Array.Empty<GUILayoutOption>());
				if (flag != entry.Value)
				{
					entry.Value = flag;
					SaveGuiConfigAndRefresh(broadcastServerConfig: false);
				}
			}
		}

		private void DrawLocalToggle(string label, ConfigEntry<bool> entry)
		{
			if (entry != null)
			{
				bool flag = GUILayout.Toggle(entry.Value, label, Array.Empty<GUILayoutOption>());
				if (flag != entry.Value)
				{
					entry.Value = flag;
					SaveGuiConfigAndRefresh(broadcastServerConfig: false);
				}
			}
		}

		private void SaveGuiConfigAndRefresh(bool broadcastServerConfig)
		{
			try
			{
				((BaseUnityPlugin)this).Config.Save();
				ClearContainerCache();
				if (broadcastServerConfig)
				{
					BroadcastServerConfig();
				}
			}
			catch (Exception ex)
			{
				LogRuntimeFault("settings-gui-save", "CraftingStorageLink could not persist a GUI setting: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private void SetSettingsWindowOpen(bool open)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (_settingsWindowOpen != open)
			{
				_settingsWindowOpen = open;
				if (open)
				{
					_savedCursorVisible = Cursor.visible;
					_savedCursorLockMode = Cursor.lockState;
					Cursor.visible = true;
					Cursor.lockState = (CursorLockMode)0;
				}
				else
				{
					Cursor.visible = _savedCursorVisible;
					Cursor.lockState = _savedCursorLockMode;
				}
			}
		}

		private static FieldInfo FindFieldNoWarning(Type type, string name)
		{
			if (type == null || string.IsNullOrEmpty(name))
			{
				return null;
			}
			return type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static void BroadcastServerConfig()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer() || _znetGetPeersMethod == null)
			{
				return;
			}
			try
			{
				if (!(_znetGetPeersMethod.Invoke(instance, null) is IEnumerable enumerable))
				{
					return;
				}
				int num = 0;
				foreach (object item in enumerable)
				{
					ZNetPeer val = (ZNetPeer)((item is ZNetPeer) ? item : null);
					if (val != null && val.m_rpc != null)
					{
						val.m_rpc.Invoke("CSL_Config_v1", new object[1] { BuildServerConfigPackage() });
						num++;
					}
				}
				DebugLog($"Broadcast updated CraftingStorageLink server settings to {num} connected peer(s).");
			}
			catch (Exception ex)
			{
				LogRuntimeFault("config-broadcast", "CraftingStorageLink could not broadcast updated server settings: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private void LateUpdate()
		{
			Stack<LinkContext> contextStack = _contextStack;
			if (contextStack != null && contextStack.Count > 0)
			{
				int count = contextStack.Count;
				contextStack.Clear();
				ClearContainerCache();
				LogRuntimeFault("context-leak", $"CraftingStorageLink recovered {count} stale runtime context frame(s) at end of frame. " + "This usually indicates another Harmony patch interrupted a crafting/build UI call.");
			}
			Player localPlayer = Player.m_localPlayer;
			int num = (((Object)(object)localPlayer != (Object)null) ? ((Object)localPlayer).GetInstanceID() : 0);
			if (num != _lastLocalPlayerInstanceId)
			{
				_lastLocalPlayerInstanceId = num;
				ResetContainerRegistry();
				if (_contextStack != null)
				{
					_contextStack.Clear();
				}
				DebugLog($"Runtime state and live-container registry reset for local-player lifecycle change (instance {num}).");
			}
		}

		private static void LogRuntimeFault(string key, string message)
		{
			try
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!_runtimeFaultLogTimes.TryGetValue(key, out var value) || !(realtimeSinceStartup - value < 10f))
				{
					_runtimeFaultLogTimes[key] = realtimeSinceStartup;
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)message);
					}
				}
			}
			catch
			{
			}
		}

		private static string SafeContainerName(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return "<null>";
			}
			try
			{
				return string.IsNullOrEmpty(((Object)container).name) ? "<unnamed container>" : ((Object)container).name;
			}
			catch
			{
				return "<unavailable container>";
			}
		}

		private void OnDestroy()
		{
			if (_settingsWindowOpen)
			{
				SetSettingsWindowOpen(open: false);
			}
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
				_harmony = null;
			}
			ResetContainerRegistry();
		}

		private static bool IsLocalEnabled(Player player)
		{
			if (_enabled == null || !EffectiveEnabled || (Object)(object)player == (Object)null)
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				return (Object)(object)Player.m_localPlayer == (Object)(object)player;
			}
			return false;
		}

		private static Recipe GetCraftRecipe(InventoryGui gui)
		{
			if ((Object)(object)gui == (Object)null)
			{
				return null;
			}
			if (_inventoryGuiCraftRecipeField != null)
			{
				object? value = _inventoryGuiCraftRecipeField.GetValue(gui);
				Recipe val = (Recipe)((value is Recipe) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			if (_inventoryGuiSelectedRecipeField == null)
			{
				return null;
			}
			try
			{
				object value2 = _inventoryGuiSelectedRecipeField.GetValue(gui);
				if (value2 == null)
				{
					return null;
				}
				Recipe val2 = (Recipe)((value2 is Recipe) ? value2 : null);
				if (val2 != null)
				{
					return val2;
				}
				Type type = value2.GetType();
				string[] array = new string[5] { "Recipe", "recipe", "m_recipe", "Key", "Item1" };
				foreach (string name in array)
				{
					PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && typeof(Recipe).IsAssignableFrom(property.PropertyType))
					{
						object? value3 = property.GetValue(value2, null);
						Recipe val3 = (Recipe)((value3 is Recipe) ? value3 : null);
						if ((Object)(object)val3 != (Object)null)
						{
							return val3;
						}
					}
					FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && typeof(Recipe).IsAssignableFrom(field.FieldType))
					{
						object? value4 = field.GetValue(value2);
						Recipe val4 = (Recipe)((value4 is Recipe) ? value4 : null);
						if ((Object)(object)val4 != (Object)null)
						{
							return val4;
						}
					}
				}
				PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (PropertyInfo propertyInfo in properties)
				{
					if (typeof(Recipe).IsAssignableFrom(propertyInfo.PropertyType) && propertyInfo.GetIndexParameters().Length == 0)
					{
						object? value5 = propertyInfo.GetValue(value2, null);
						Recipe val5 = (Recipe)((value5 is Recipe) ? value5 : null);
						if ((Object)(object)val5 != (Object)null)
						{
							return val5;
						}
					}
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (typeof(Recipe).IsAssignableFrom(fieldInfo.FieldType))
					{
						object? value6 = fieldInfo.GetValue(value2);
						Recipe val6 = (Recipe)((value6 is Recipe) ? value6 : null);
						if ((Object)(object)val6 != (Object)null)
						{
							return val6;
						}
					}
				}
				return null;
			}
			catch (Exception ex)
			{
				DebugLog("Could not resolve InventoryGui.m_selectedRecipe: " + ex.Message);
				return null;
			}
		}

		private static CraftingStation GetActiveStation(Player player, Recipe recipe)
		{
			if (!IsLocalEnabled(player) || !EffectiveCraftingEnabled || (Object)(object)recipe == (Object)null)
			{
				return null;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation == (Object)null)
			{
				return null;
			}
			return currentCraftingStation;
		}

		private static CraftingStation GetBuildStation(Player player, Piece piece)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!IsLocalEnabled(player) || !EffectiveHammerBuildingEnabled || (Object)(object)piece == (Object)null || (Object)(object)piece.m_craftingStation == (Object)null)
			{
				return null;
			}
			return CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)player).transform.position);
		}

		private static bool SupportsPiece(Piece piece)
		{
			if ((Object)(object)piece != (Object)null && piece.m_resources != null)
			{
				return piece.m_resources.Length != 0;
			}
			return false;
		}

		private static bool SupportsRecipe(Recipe recipe)
		{
			if ((Object)(object)recipe != (Object)null)
			{
				return !recipe.m_requireOnlyOneIngredient;
			}
			return false;
		}

		private static ContextGuard BeginRequirementCheck(Player player, Recipe recipe, bool discover)
		{
			LinkContext context = null;
			if (!discover && SupportsRecipe(recipe))
			{
				CraftingStation activeStation = GetActiveStation(player, recipe);
				if ((Object)(object)activeStation != (Object)null)
				{
					context = new LinkContext(LinkContextKind.RecipeRequirement, player, activeStation, recipe);
				}
			}
			return PushContext(context, LinkContextKind.RecipeRequirement);
		}

		private static ContextGuard BeginRequirementDisplay(InventoryGui gui, Player player)
		{
			LinkContext context = null;
			if (IsLocalEnabled(player) && EffectiveCraftingEnabled)
			{
				Recipe craftRecipe = GetCraftRecipe(gui);
				if ((Object)(object)craftRecipe == (Object)null || SupportsRecipe(craftRecipe))
				{
					CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
					if ((Object)(object)currentCraftingStation != (Object)null)
					{
						context = new LinkContext(LinkContextKind.RecipeDisplay, player, currentCraftingStation, craftRecipe);
					}
				}
			}
			return PushContext(context, LinkContextKind.RecipeDisplay);
		}

		private static ContextGuard BeginCrafting(InventoryGui gui, Player player)
		{
			LinkContext context = null;
			Recipe craftRecipe = GetCraftRecipe(gui);
			if (EffectiveCraftingEnabled && SupportsRecipe(craftRecipe))
			{
				CraftingStation activeStation = GetActiveStation(player, craftRecipe);
				if ((Object)(object)activeStation != (Object)null)
				{
					context = new LinkContext(LinkContextKind.Crafting, player, activeStation, craftRecipe);
				}
			}
			return PushContext(context, LinkContextKind.Crafting);
		}

		private static LinkContext CreateBuildContext(Player player, Piece piece, LinkContextKind kind, RequirementMode? mode = null)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			if (!IsLocalEnabled(player) || !EffectiveHammerBuildingEnabled || !SupportsPiece(piece))
			{
				return null;
			}
			if (mode.HasValue && (int)mode.Value == 1)
			{
				return null;
			}
			CraftingStation buildStation = GetBuildStation(player, piece);
			if ((Object)(object)piece.m_craftingStation != (Object)null && (Object)(object)buildStation == (Object)null)
			{
				return null;
			}
			return new LinkContext(kind, player, buildStation, piece);
		}

		private static ContextGuard BeginBuildRequirementCheck(Player player, Piece piece, RequirementMode mode)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			return PushContext(CreateBuildContext(player, piece, LinkContextKind.BuildRequirement, mode), LinkContextKind.BuildRequirement);
		}

		private static ContextGuard BeginPlacement(Player player)
		{
			PieceTable val = (((Object)(object)player != (Object)null) ? player.GetBuildTool() : null);
			Piece piece = (((Object)(object)val != (Object)null) ? val.GetSelectedPiece() : null);
			return PushContext(CreateBuildContext(player, piece, LinkContextKind.Placement), LinkContextKind.Placement);
		}

		private static Piece ResolveBuildHudPiece(Player player, object[] args)
		{
			Piece val = null;
			if (args != null)
			{
				foreach (object obj in args)
				{
					val = (Piece)((obj is Piece) ? obj : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			if ((Object)(object)player != (Object)null)
			{
				PieceTable buildTool = player.GetBuildTool();
				val = (((Object)(object)buildTool != (Object)null) ? buildTool.GetSelectedPiece() : null);
			}
			return val;
		}

		private static ContextGuard BeginBuildHudDisplay(Player player, object[] args)
		{
			Piece piece = ResolveBuildHudPiece(player, args);
			return PushContext(CreateBuildContext(player, piece, LinkContextKind.BuildHudDisplay), LinkContextKind.BuildHudDisplay);
		}

		private static string FormatAvailableRequirementCount(int available, int required)
		{
			return Math.Max(0, available) + "/" + Math.Max(0, required);
		}

		private static TMP_Text FindRequirementAmountText(Transform row)
		{
			if ((Object)(object)row == (Object)null)
			{
				return null;
			}
			Transform val = row.Find("res_amount");
			TMP_Text val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			TMP_Text[] componentsInChildren = ((Component)row).GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val3 in componentsInChildren)
			{
				if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val3).gameObject != (Object)null && string.Equals(((Object)((Component)val3).gameObject).name, "res_amount", StringComparison.OrdinalIgnoreCase))
				{
					return val3;
				}
			}
			return null;
		}

		private static void SetRequirementCounterText(TMP_Text amountText, int available, int required)
		{
			if (!((Object)(object)amountText == (Object)null))
			{
				amountText.text = FormatAvailableRequirementCount(available, required);
				amountText.enableAutoSizing = true;
				amountText.fontSizeMin = Mathf.Min((amountText.fontSizeMin > 0f) ? amountText.fontSizeMin : 10f, 10f);
				amountText.fontSizeMax = Mathf.Max(amountText.fontSize, amountText.fontSizeMax);
				amountText.overflowMode = (TextOverflowModes)0;
			}
		}

		private static void StabilizeCraftRequirementDisplay(InventoryGui gui, Transform row, Requirement requirement, Player player, int qualityLevel, int craftMultiplier)
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			if (!ShowAvailableResourceCounts || (Object)(object)gui == (Object)null || (Object)(object)row == (Object)null || !IsLocalEnabled(player) || !EffectiveCraftingEnabled || requirement == null || (Object)(object)requirement.m_resItem == (Object)null || requirement.m_resItem.m_itemData == null || requirement.m_resItem.m_itemData.m_shared == null)
			{
				return;
			}
			Recipe craftRecipe = GetCraftRecipe(gui);
			if ((Object)(object)craftRecipe != (Object)null && !SupportsRecipe(craftRecipe))
			{
				return;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return;
			}
			int num = requirement.GetAmount(qualityLevel) * craftMultiplier;
			if (num <= 0)
			{
				return;
			}
			string name = requirement.m_resItem.m_itemData.m_shared.m_name;
			int num2 = inventory.CountItems(name, -1, true);
			int num3 = CountLinkedItem(player, currentCraftingStation, ((Component)currentCraftingStation).transform.position, name, -1, matchWorldLevel: true, int.MaxValue);
			int num4 = num2 + num3;
			TMP_Text val = FindRequirementAmountText(row);
			if ((Object)(object)val != (Object)null)
			{
				SetRequirementCounterText(val, num4, num);
				if (num4 >= num)
				{
					((Graphic)val).color = Color.white;
				}
			}
			if (num4 >= num)
			{
				Transform val2 = row.Find("res_icon");
				Image val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null);
				if ((Object)(object)val3 != (Object)null)
				{
					((Behaviour)val3).enabled = true;
					((Graphic)val3).color = Color.white;
				}
			}
		}

		private static void StabilizeBuildRequirementDisplay(Hud hud, Player player, Piece piece)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hud == (Object)null || !IsLocalEnabled(player) || !EffectiveHammerBuildingEnabled || !SupportsPiece(piece))
			{
				return;
			}
			CraftingStation buildStation = GetBuildStation(player, piece);
			if ((Object)(object)piece.m_craftingStation != (Object)null && (Object)(object)buildStation == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || hud.m_requirementItems == null)
			{
				return;
			}
			Vector3 origin = (((Object)(object)buildStation != (Object)null) ? ((Component)buildStation).transform.position : ((Component)player).transform.position);
			int num = Math.Min(piece.m_resources.Length, hud.m_requirementItems.Length);
			for (int i = 0; i < num; i++)
			{
				Requirement val = piece.m_resources[i];
				if (val == null || (Object)(object)val.m_resItem == (Object)null || val.m_resItem.m_itemData == null || val.m_resItem.m_itemData.m_shared == null)
				{
					continue;
				}
				int amount = val.GetAmount(0);
				if (amount <= 0)
				{
					continue;
				}
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				int num2 = inventory.CountItems(name, -1, true);
				if (ShowAvailableResourceCounts)
				{
					num2 += CountLinkedItem(player, buildStation, origin, name, -1, matchWorldLevel: true, int.MaxValue);
				}
				else if (num2 < amount)
				{
					num2 += CountLinkedItem(player, buildStation, origin, name, -1, matchWorldLevel: true, amount - num2);
				}
				GameObject val2 = hud.m_requirementItems[i];
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				TMP_Text val3 = FindRequirementAmountText(val2.transform);
				if ((Object)(object)val3 != (Object)null && ShowAvailableResourceCounts)
				{
					SetRequirementCounterText(val3, num2, amount);
				}
				if (num2 >= amount)
				{
					if ((Object)(object)val3 != (Object)null)
					{
						((Graphic)val3).color = Color.white;
					}
					Transform val4 = val2.transform.Find("res_icon");
					Image val5 = (((Object)(object)val4 != (Object)null) ? ((Component)val4).GetComponent<Image>() : null);
					if ((Object)(object)val5 != (Object)null)
					{
						((Behaviour)val5).enabled = true;
						((Graphic)val5).color = Color.white;
					}
				}
			}
		}

		private static bool IsBuildContext(LinkContext context)
		{
			if (context == null)
			{
				return false;
			}
			if (context.Kind != LinkContextKind.BuildRequirement && context.Kind != LinkContextKind.BuildHudDisplay)
			{
				return context.Kind == LinkContextKind.Placement;
			}
			return true;
		}

		private static bool ContextAllowsLinkedCounting(LinkContext context)
		{
			if (context == null || !context.Enabled)
			{
				return false;
			}
			if (context.Kind != LinkContextKind.RecipeRequirement && context.Kind != LinkContextKind.RecipeDisplay)
			{
				return context.Kind == LinkContextKind.BuildRequirement;
			}
			return true;
		}

		private static void AddLinkedCount(Inventory inventory, string itemName, int quality, bool matchWorldLevel, ref int result)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				LinkContext currentContext = CurrentContext;
				if (ContextAllowsLinkedCounting(currentContext) && !((Object)(object)currentContext.Player == (Object)null) && currentContext.HasOrigin && inventory != null && inventory == currentContext.PlayerInventory)
				{
					result += CountLinkedItem(currentContext.Player, currentContext.Station, currentContext.Origin, itemName, quality, matchWorldLevel, int.MaxValue);
				}
			}
			catch (Exception ex)
			{
				LogRuntimeFault("linked-count-postfix", "CraftingStorageLink recovered from a linked-count failure: " + ex.GetType().Name + ": " + ex.Message);
				ClearContainerCache();
			}
		}

		private static int CountLinkedItem(Player player, CraftingStation station, Vector3 origin, string itemName, int quality, bool matchWorldLevel, int stopAt)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(itemName) || !IsLocalEnabled(player))
			{
				return 0;
			}
			List<ContainerCandidate> list;
			try
			{
				list = FindContainers(player, station, origin);
			}
			catch (Exception ex)
			{
				LogRuntimeFault("container-discovery-count", "CraftingStorageLink recovered from container discovery while counting " + itemName + ": " + ex.GetType().Name + ": " + ex.Message);
				ClearContainerCache();
				return 0;
			}
			if (_linkedCountCacheFrame != Time.frameCount)
			{
				_linkedCountCacheFrame = Time.frameCount;
				_linkedCountFrameCache.Clear();
			}
			string key = itemName + "\u001f" + quality + "\u001f" + (matchWorldLevel ? "1" : "0");
			if (_linkedCountFrameCache.TryGetValue(key, out var value))
			{
				if (value.Complete)
				{
					if (stopAt != int.MaxValue)
					{
						return Math.Min(value.Count, stopAt);
					}
					return value.Count;
				}
				if (value.Count >= stopAt)
				{
					return stopAt;
				}
			}
			int num = 0;
			bool flag = true;
			for (int i = 0; i < list.Count; i++)
			{
				Container container = list[i].Container;
				try
				{
					Inventory loadedInventory = GetLoadedInventory(container);
					if (loadedInventory == null)
					{
						DebugLog("Linked container '" + SafeContainerName(container) + "' had no readable inventory while counting " + itemName + ".");
						continue;
					}
					int num2 = loadedInventory.CountItems(itemName, quality, matchWorldLevel);
					if (num2 > 0)
					{
						DebugLog($"Counted {num2}x {itemName} in '{SafeContainerName(container)}'.");
					}
					num += num2;
					if (stopAt != int.MaxValue && num >= stopAt)
					{
						flag = false;
						break;
					}
				}
				catch (Exception ex2)
				{
					LogRuntimeFault("count-container-" + SafeContainerName(container), "CraftingStorageLink skipped a transient container while counting " + itemName + ": " + ex2.GetType().Name + ": " + ex2.Message);
				}
			}
			if (_linkedCountFrameCache.TryGetValue(key, out var value2) && (value2.Complete || (!flag && value2.Count >= num)))
			{
				if (stopAt != int.MaxValue)
				{
					return Math.Min(value2.Count, stopAt);
				}
				return value2.Count;
			}
			_linkedCountFrameCache[key] = new LinkedCountFrameEntry(num, flag);
			if (stopAt != int.MaxValue)
			{
				return Math.Min(num, stopAt);
			}
			return num;
		}

		private static bool RequirementAppliesToTransaction(Requirement requirement, LinkContext context)
		{
			if (requirement == null || context == null)
			{
				return false;
			}
			if (context.Kind == LinkContextKind.Crafting && ((Object)(object)context.Station != (Object)null && context.Station.m_upgrader) != requirement.m_upgraderResource)
			{
				return false;
			}
			return true;
		}

		private static int RawCountItems(Inventory inventory, string itemName, int quality, bool matchWorldLevel)
		{
			if (inventory == null || string.IsNullOrEmpty(itemName))
			{
				return 0;
			}
			int num = 0;
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return 0;
			}
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (val != null && val.m_shared != null && !(val.m_shared.m_name != itemName) && (quality < 0 || val.m_quality == quality) && (!matchWorldLevel || val.m_worldLevel >= Game.m_worldLevel))
				{
					num += Math.Max(0, val.m_stack);
				}
			}
			return num;
		}

		private static int CountTransactionTotal(Player player, CraftingStation station, Vector3 origin, string itemName, int quality)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return 0;
			}
			int num = RawCountItems(((Humanoid)player).GetInventory(), itemName, quality, matchWorldLevel: true);
			List<ContainerCandidate> list = FindContainers(player, station, origin);
			for (int i = 0; i < list.Count; i++)
			{
				Container container = list[i].Container;
				if (!IsEligibleContainer(player, container))
				{
					continue;
				}
				float num2 = Mathf.Max(1f, EffectiveRadius);
				Vector3 val = ((Component)container).transform.position - origin;
				if (!(((Vector3)(ref val)).sqrMagnitude > num2 * num2))
				{
					try
					{
						Inventory loadedInventory = GetLoadedInventory(container);
						num += RawCountItems(loadedInventory, itemName, quality, matchWorldLevel: true);
					}
					catch (Exception ex)
					{
						LogRuntimeFault("transaction-count-" + SafeContainerName(container), "CraftingStorageLink skipped a transient container while auditing " + itemName + ": " + ex.GetType().Name + ": " + ex.Message);
					}
				}
			}
			return num;
		}

		private static int RemoveFromPlayer(Player player, string itemName, int quality, int requested)
		{
			if ((Object)(object)player == (Object)null || requested <= 0)
			{
				return 0;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return 0;
			}
			int num = RawCountItems(inventory, itemName, quality, matchWorldLevel: true);
			int num2 = Math.Min(num, requested);
			if (num2 <= 0)
			{
				return 0;
			}
			inventory.RemoveItem(itemName, num2, quality, true);
			int num3 = RawCountItems(inventory, itemName, quality, matchWorldLevel: true);
			return Math.Max(0, num - num3);
		}

		private static PullPlan CreatePullPlan(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			LinkContext currentContext = CurrentContext;
			if (currentContext == null || !currentContext.Enabled || (Object)(object)currentContext.Player != (Object)(object)player || !currentContext.HasOrigin || requirements == null || requirements.Length == 0)
			{
				return null;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return null;
			}
			PullPlan pullPlan = new PullPlan(player, currentContext.Station, currentContext.Origin);
			foreach (Requirement val in requirements)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null) && RequirementAppliesToTransaction(val, currentContext))
				{
					int num = val.GetAmount(qualityLevel) * multiplier;
					if (num > 0)
					{
						string name = val.m_resItem.m_itemData.m_shared.m_name;
						pullPlan.AddRequired(name, itemQuality, num);
					}
				}
			}
			bool flag = false;
			for (int j = 0; j < pullPlan.Items.Count; j++)
			{
				PullItem pullItem = pullPlan.Items[j];
				int num2 = RawCountItems(inventory, pullItem.Name, pullItem.Quality, matchWorldLevel: true);
				if (num2 < pullItem.Required)
				{
					flag = true;
				}
				pullItem.BeforeTotal = CountTransactionTotal(player, currentContext.Station, currentContext.Origin, pullItem.Name, pullItem.Quality);
				DebugLog($"Transaction start {pullItem.Name}: required={pullItem.Required}, player={num2}, combined={pullItem.BeforeTotal}, multiplier={multiplier}.");
			}
			if (!flag || pullPlan.Items.Count <= 0)
			{
				return null;
			}
			return pullPlan;
		}

		private static void PayPullPlan(PullPlan plan)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			if (plan == null || !IsLocalEnabled(plan.Player))
			{
				return;
			}
			List<ContainerCandidate> list = FindContainers(plan.Player, plan.Station, plan.Origin);
			for (int i = 0; i < plan.Items.Count; i++)
			{
				PullItem pullItem = plan.Items[i];
				int num = CountTransactionTotal(plan.Player, plan.Station, plan.Origin, pullItem.Name, pullItem.Quality);
				int num2 = Math.Max(0, pullItem.BeforeTotal - num);
				if (num2 > pullItem.Required)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)($"CraftingStorageLink detected over-consumption of {pullItem.Name}: required={pullItem.Required}, " + $"already removed={num2}. Another mod may also be modifying Player.ConsumeResources. " + "CraftingStorageLink will not remove any additional items for this resource."));
					}
					continue;
				}
				int num3 = pullItem.Required - num2;
				if (num3 <= 0)
				{
					DebugLog($"Transaction {pullItem.Name}: another path already paid {num2}/{pullItem.Required}; no linked pull needed.");
					continue;
				}
				int num4 = RemoveFromPlayer(plan.Player, pullItem.Name, pullItem.Quality, num3);
				num3 -= num4;
				int num5 = 0;
				for (int j = 0; j < list.Count; j++)
				{
					if (num3 <= 0)
					{
						break;
					}
					int num6 = PullFromContainer(plan.Player, plan.Station, plan.Origin, list[j].Container, pullItem.Name, pullItem.Quality, num3);
					num5 += num6;
					num3 -= num6;
				}
				int num7 = CountTransactionTotal(plan.Player, plan.Station, plan.Origin, pullItem.Name, pullItem.Quality);
				int num8 = Math.Max(0, pullItem.BeforeTotal - num7);
				DebugLog($"Transaction end {pullItem.Name}: required={pullItem.Required}, pre-paid={num2}, " + $"player-paid={num4}, linked-paid={num5}, observed-total={num8}, remaining={num3}.");
				if (num8 > pullItem.Required)
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogWarning((object)($"CraftingStorageLink detected that {num8}x {pullItem.Name} disappeared during a transaction requiring only {pullItem.Required}. " + "This strongly indicates another Player.ConsumeResources patch or inventory mod is also consuming the resource."));
					}
				}
				else if (num3 > 0)
				{
					ManualLogSource log3 = _log;
					if (log3 != null)
					{
						log3.LogWarning((object)$"CraftingStorageLink could not pull {num3}x {pullItem.Name}; a linked container may have changed during crafting.");
					}
				}
			}
			ClearContainerCache();
		}

		private static List<ContainerCandidate> FindContainers(Player player, CraftingStation station, Vector3 origin)
		{
			//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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			if (!EnsureContainerRegistry())
			{
				return new List<ContainerCandidate>();
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Vector3 val;
			if ((Object)(object)_cachedPlayer == (Object)(object)player && (Object)(object)_cachedStation == (Object)(object)station && _cachedOriginSet)
			{
				val = _cachedOrigin - origin;
				if (((Vector3)(ref val)).sqrMagnitude <= 1f && _cachedContainers != null && realtimeSinceStartup - _cachedContainersAtRealtime <= 0.25f)
				{
					return _cachedContainers;
				}
			}
			_linkedCountCacheFrame = Time.frameCount;
			_linkedCountFrameCache.Clear();
			List<ContainerCandidate> list = new List<ContainerCandidate>();
			if (!IsLocalEnabled(player))
			{
				return list;
			}
			float num = Mathf.Max(1f, EffectiveRadius);
			float num2 = num * num;
			List<Container> list2 = null;
			try
			{
				foreach (Container item in _containerRegistry)
				{
					try
					{
						if ((Object)(object)item == (Object)null)
						{
							if (list2 == null)
							{
								list2 = new List<Container>();
							}
							list2.Add(item);
							continue;
						}
						Transform transform = ((Component)item).transform;
						if (!((Object)(object)transform == (Object)null))
						{
							val = transform.position - origin;
							float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
							if (!(sqrMagnitude > num2) && IsEligibleContainer(player, item))
							{
								list.Add(new ContainerCandidate(item, sqrMagnitude));
							}
						}
					}
					catch (Exception ex)
					{
						LogRuntimeFault("scan-container-" + SafeContainerName(item), "CraftingStorageLink skipped a transient container during registry discovery: " + ex.GetType().Name + ": " + ex.Message);
					}
				}
			}
			catch (InvalidOperationException ex2)
			{
				LogRuntimeFault("container-registry-enumeration", "CraftingStorageLink live-container registry changed during enumeration; it will rebuild automatically: " + ex2.Message);
				ResetContainerRegistry();
				return list;
			}
			if (list2 != null && list2.Count > 0)
			{
				for (int i = 0; i < list2.Count; i++)
				{
					_containerRegistry.Remove(list2[i]);
				}
			}
			list.Sort(delegate(ContainerCandidate a, ContainerCandidate b)
			{
				float distanceSqr = a.DistanceSqr;
				return distanceSqr.CompareTo(b.DistanceSqr);
			});
			_cachedPlayer = player;
			_cachedStation = station;
			_cachedOrigin = origin;
			_cachedOriginSet = true;
			_cachedContainersAtRealtime = realtimeSinceStartup;
			_cachedContainers = list;
			string arg = (((Object)(object)station != (Object)null) ? station.m_name : "builder");
			DebugLog($"Found {list.Count} linked containers within {num:0.#}m of {arg} " + $"from {_containerRegistry.Count} registered loaded containers.");
			return list;
		}

		private static bool EnsureContainerRegistry()
		{
			if (_containerRegistryBootstrapped)
			{
				return true;
			}
			try
			{
				Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
				_containerRegistry.Clear();
				foreach (Container val in array)
				{
					if ((Object)(object)val != (Object)null)
					{
						_containerRegistry.Add(val);
					}
				}
				_containerRegistryBootstrapped = true;
				ClearContainerCache();
				DebugLog($"Bootstrapped live-container registry with {_containerRegistry.Count} loaded containers. " + "Recurring scene-wide Container scans are now disabled for this player/world lifecycle.");
				return true;
			}
			catch (Exception ex)
			{
				LogRuntimeFault("container-registry-bootstrap", "CraftingStorageLink could not bootstrap the live-container registry; it will retry on the next linked-storage query: " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static void RegisterContainer(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return;
			}
			try
			{
				if (_containerRegistry.Add(container))
				{
					ClearContainerCache();
					DebugLog("Registered live container '" + SafeContainerName(container) + "' " + $"(registry now {_containerRegistry.Count}).");
				}
			}
			catch (Exception ex)
			{
				LogRuntimeFault("container-registry-add", "CraftingStorageLink could not register a live container: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private static void UnregisterContainer(Container container)
		{
			if (container == null)
			{
				return;
			}
			try
			{
				if (_containerRegistry.Remove(container))
				{
					ClearContainerCache();
					DebugLog("Unregistered container from live registry " + $"(registry now {_containerRegistry.Count}).");
				}
			}
			catch (Exception ex)
			{
				LogRuntimeFault("container-registry-remove", "CraftingStorageLink could not unregister a container: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private static void ResetContainerRegistry()
		{
			_containerRegistry.Clear();
			_containerRegistryBootstrapped = false;
			ClearContainerCache();
		}

		private static bool IsEligibleContainer(Player player, Container container)
		{
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)container == (Object)null || (Object)(object)player == (Object)null)
				{
					return false;
				}
				if ((Object)(object)((Component)container).GetComponentInParent<TombStone>() != (Object)null)
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': tombstone.");
					return false;
				}
				if (!EffectiveAllowShipContainers && (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': ship containers disabled.");
					return false;
				}
				if (!EffectiveAllowCartContainers && (Object)(object)container.m_wagon != (Object)null)
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': cart containers disabled.");
					return false;
				}
				if (EffectivePlayerBuiltOnly)
				{
					long num = 0L;
					Piece componentInParent = ((Component)container).GetComponentInParent<Piece>();
					if ((Object)(object)componentInParent != (Object)null)
					{
						num = componentInParent.GetCreator();
					}
					if (num == 0L)
					{
						ZNetView containerView = GetContainerView(container);
						if ((Object)(object)containerView != (Object)null && containerView.IsValid() && containerView.GetZDO() != null)
						{
							num = containerView.GetZDO().GetLong(StringExtensionMethods.GetStableHashCode("creator"), 0L);
						}
					}
					if (num == 0L)
					{
						DebugLog("Rejected container '" + ((Object)container).name + "': no player creator on Piece or container ZDO.");
						return false;
					}
				}
				bool flag = container.IsInUse() || ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse());
				if (EffectiveSkipInUseContainers && flag && !IsLocallyOpenContainer(container))
				{
					DebugLog("Ignoring in-use linked container '" + ((Object)container).name + "': it is not the local player's currently open container.");
					return false;
				}
				if (EffectiveSkipInUseContainers && flag)
				{
					DebugLog("Allowing locally open linked container '" + ((Object)container).name + "'.");
				}
				if (EffectiveRespectContainerAccess && !CheckContainerAccess(container, player.GetPlayerID()))
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': access check failed.");
					return false;
				}
				if (EffectiveRespectWards && container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': ward/private-area access denied.");
					return false;
				}
				ZNetView containerView2 = GetContainerView(container);
				if ((Object)(object)containerView2 == (Object)null || !containerView2.IsValid() || containerView2.GetZDO() == null)
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': invalid or unavailable ZNetView/ZDO.");
					return false;
				}
				if (EffectiveRespectMultiUserChestIgnore && containerView2.GetZDO().GetBool("MUC_Ignore", false))
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': MUC_Ignore is set.");
					return false;
				}
				if (!EffectiveClaimOwnershipWhenPulling && !containerView2.IsOwner())
				{
					DebugLog("Rejected container '" + ((Object)container).name + "': local peer is not owner and ClaimOwnershipWhenPulling=false.");
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				LogRuntimeFault("eligible-container-" + SafeContainerName(container), "CraftingStorageLink skipped an unavailable container during eligibility checks: " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static bool CheckContainerAccess(Container container, long playerId)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			if (_containerCheckAccessMethod == null)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)"CraftingStorageLink could not resolve Container.CheckAccess; refusing to pull from this container while RespectContainerAccess is enabled.");
				}
				return false;
			}
			try
			{
				object obj = _containerCheckAccessMethod.Invoke(container, new object[1] { playerId });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				DebugLog("Container access check failed for '" + ((Object)container).name + "': " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static bool IsLocallyOpenContainer(Container container)
		{
			if ((Object)(object)container == (Object)null || (Object)(object)InventoryGui.instance == (Object)null || _inventoryGuiCurrentContainerField == null)
			{
				return false;
			}
			try
			{
				return _inventoryGuiCurrentContainerField.GetValue(InventoryGui.instance) == container;
			}
			catch
			{
				return false;
			}
		}

		private static ZNetView GetContainerView(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			try
			{
				if (_containerNViewField != null)
				{
					object? value = _containerNViewField.GetValue(container);
					ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			return ((Component)container).GetComponentInParent<ZNetView>();
		}

		private static Inventory GetLoadedInventory(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			try
			{
				Inventory inventory = container.GetInventory();
				if (inventory != null)
				{
					return inventory;
				}
				_containerLoadMethod?.Invoke(container, null);
				return container.GetInventory();
			}
			catch (Exception ex)
			{
				DebugLog("Could not load linked container '" + ((Object)container).name + "': " + ex.GetType().Name + ": " + ex.Message);
				return null;
			}
		}

		private static void SaveContainer(Container container)
		{
			if ((Object)(object)container == (Object)null || _containerSaveMethod == null)
			{
				return;
			}
			try
			{
				_containerSaveMethod.Invoke(container, null);
			}
			catch (Exception ex)
			{
				DebugLog("Could not explicitly save linked container '" + ((Object)container).name + "': " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private static int PullFromContainer(Player player, CraftingStation station, Vector3 origin, Container container, string itemName, int quality, int requested)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (requested <= 0 || !IsEligibleContainer(player, container))
			{
				return 0;
			}
			float num = Mathf.Max(1f, EffectiveRadius);
			Vector3 val = ((Component)container).transform.position - origin;
			if (((Vector3)(ref val)).sqrMagnitude > num * num)
			{
				return 0;
			}
			try
			{
				ZNetView containerView = GetContainerView(container);
				if ((Object)(object)containerView == (Object)null || !containerView.IsValid())
				{
					return 0;
				}
				if (!containerView.IsOwner())
				{
					if (!EffectiveClaimOwnershipWhenPulling)
					{
						return 0;
					}
					containerView.ClaimOwnership();
				}
				Inventory loadedInventory = GetLoadedInventory(container);
				if (loadedInventory == null)
				{
					return 0;
				}
				int num2 = RawCountItems(loadedInventory, itemName, quality, matchWorldLevel: true);
				int num3 = Math.Min(num2, requested);
				if (num3 <= 0)
				{
					return 0;
				}
				loadedInventory.RemoveItem(itemName, num3, quality, true);
				int num4 = RawCountItems(loadedInventory, itemName, quality, matchWorldLevel: true);
				int num5 = Math.Max(0, num2 - num4);
				if (num5 > 0)
				{
					SaveContainer(container);
					DebugLog($"Pulled {num5}x {itemName} from '{((Object)container).name}'.");
				}
				return num5;
			}
			catch (Exception ex)
			{
				LogRuntimeFault("pull-" + SafeContainerName(container), "CraftingStorageLink skipped container '" + SafeContainerName(container) + "' during pull: " + ex.GetType().Name + ": " + ex.Message);
				return 0;
			}
		}

		private static MethodInfo ResolveSingleItemDataMethod(Type type, string methodName)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != methodName))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType == typeof(ItemData))
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		private static ItemData CloneSingleItem(ItemData source)
		{
			if (source == null || _itemDataCloneMethod == null)
			{
				return null;
			}
			try
			{
				object? obj = _itemDataCloneMethod.Invoke(source, null);
				ItemData val = (ItemData)((obj is ItemData) ? obj : null);
				if (val != null)
				{
					val.m_stack = 1;
				}
				return val;
			}
			catch (Exception ex)
			{
				LogRuntimeFault("processor-clone-item", "CraftingStorageLink could not clone a processor input item: " + ex.GetType().Name + ": " + ex.Message);
				return null;
			}
		}

		private static bool TryAddItemData(Inventory inventory, ItemData item)
		{
			if (inventory == null || item == null || _inventoryAddItemDataMethod == null)
			{
				return false;
			}
			try
			{
				string text = ((item.m_shared != null) ? item.m_shared.m_name : null);
				int quality = item.m_quality;
				int num = ((!string.IsNullOrEmpty(text)) ? RawCountItems(inventory, text, quality, matchWorldLevel: false) : 0);
				object obj = _inventoryAddItemDataMethod.Invoke(inventory, new object[1] { item });
				int num2 = ((!string.IsNullOrEmpty(text)) ? RawCountItems(inventory, text, quality, matchWorldLevel: false) : num);
				if (obj is bool)
				{
					return (bool)obj && num2 > num;
				}
				return num2 > num;
			}
			catch (Exception ex)
			{
				LogRuntimeFault("processor-add-item", "CraftingStorageLink could not stage a processor input item in the player inventory: " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static bool RemoveOneItemData(Inventory inventory, ItemData item, string itemName, int quality)
		{
			if (inventory == null || string.IsNullOrEmpty(itemName))
			{
				return false;
			}
			try
			{
				int num = RawCountItems(inventory, itemName, quality, matchWorldLevel: false);
				if (num <= 0)
				{
					return false;
				}
				if (_inventoryRemoveOneItemMethod != null && item != null)
				{
					_inventoryRemoveOneItemMethod.Invoke(inventory, new object[1] { item });
				}
				else
				{
					inventory.RemoveItem(itemName, 1, quality, false);
				}
				return RawCountItems(inventory, itemName, quality, matchWorldLevel: false) < num;
			}
			catch (Exception ex)
			{
				LogRuntimeFault("processor-remove-item", "CraftingStorageLink could not remove a staged processor input item: " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static string GetItemPrefabName(ItemData item)
		{
			try
			{
				if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
				{
					return null;
				}
				return ((Object)item.m_dropPrefab).name;
			}
			catch
			{
				return null;
			}
		}

		private static void AddUniquePrefabName(List<string> names, ItemDrop itemDrop)
		{
			if (names != null && !((Object)(object)itemDrop == (Object)null) && !((Object)(object)((Component)itemDrop).gameObject == (Object)null))
			{
				string name = ((Object)((Component)itemDrop).gameObject).name;
				if (!string.IsNullOrEmpty(name) && !names.Contains(name))
				{
					names.Add(name);
				}
			}
		}

		private static void CollectItemDropsFromValue(object value, List<string> names, bool conversionMode)
		{
			if (value == null || names == null)
			{
				return;
			}
			ItemDrop val = (ItemDrop)((value is ItemDrop) ? value : null);
			if (val != null)
			{
				AddUniquePrefabName(names, val);
				return;
			}
			if (value is IEnumerable enumerable && !(value is string))
			{
				foreach (object item in enumerable)
				{
					CollectItemDropsFromValue(item, names, conversionMode);
				}
				return;
			}
			Type type = value.GetType();
			string[] array = ((!conversionMode) ? new string[5] { "m_item", "item", "m_fuelItem", "fuelItem", "m_from" } : new string[4] { "m_from", "from", "m_item", "item" });
			for (int i = 0; i < array.Length; i++)
			{
				FieldInfo fieldInfo = AccessTools.Field(type, array[i]);
				if (fieldInfo == null)
				{
					continue;
				}
				try
				{
					object? value2 = fieldInfo.GetValue(value);
					ItemDrop val2 = (ItemDrop)((value2 is ItemDrop) ? value2 : null);
					if (val2 != null)
					{
						AddUniquePrefabName(names, val2);
						break;
					}
				}
				catch
				{
				}
			}
		}

		private static List<string> GetProcessorAcceptedPrefabNames(Smelter processor, bool fuelInteraction)
		{
			List<string> list = new List<string>();
			if ((Object)(object)processor == (Object)null)
			{
				return list;
			}
			try
			{
				if (fuelInteraction)
				{
					if (_smelterFuelItemField != null)
					{
						CollectItemDropsFromValue(_smelterFuelItemField.GetValue(processor), list, conversionMode: false);
					}
					if (_smelterFuelItemsField != null)
					{
						CollectItemDropsFromValue(_smelterFuelItemsField.GetValue(processor), list, conversionMode: false);
					}
				}
				else
				{
					if (_smelterConversionField != null)
					{
						CollectItemDropsFromValue(_smelterConversionField.GetValue(processor), list, conversionMode: true);
					}
					if (_smelterConversionsField != null)
					{
						CollectItemDropsFromValue(_smelterConversionsField.GetValue(processor), list, conversionMode: true);
					}
				}
			}
			catch (Exception ex)
			{
				LogRuntimeFault("processor-accepted-items-" + (fuelInteraction ? "fuel" : "input"), "CraftingStorageLink could not inspect processor accepted items: " + ex.GetType().Name + ": " + ex.Message);
			}
			return list;
		}

		private static bool InventoryHasAnyProcessorItem(Inventory inventory, List<string> acceptedP