Decompiled source of Stackmaster v1.1.5

plugins/Stackmaster/Stackmaster.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Stackmaster.Core;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("JStack424")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Inventory sorting and nearby-storage planning for Valheim.")]
[assembly: AssemblyFileVersion("1.1.5.0")]
[assembly: AssemblyInformationalVersion("1.1.5+0412f1733398506fbf6f5d1c8daf54ef92a9cdfd")]
[assembly: AssemblyProduct("Stackmaster")]
[assembly: AssemblyTitle("Stackmaster")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/JStack424/Stackmaster")]
[assembly: AssemblyVersion("1.1.5.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Stackmaster
{
	internal static class ChestSortPreferences
	{
		private static readonly FieldInfo NetworkViewField = AccessTools.Field(typeof(Container), "m_nview");

		private static readonly HashSet<string> SessionDisabled = new HashSet<string>(StringComparer.Ordinal);

		private static bool _storageHealthy;

		private static bool _failureLogged;

		internal static void Initialize()
		{
			SessionDisabled.Clear();
			_storageHealthy = true;
			_failureLogged = false;
		}

		internal static bool TryGet(Container container, out string key, out bool enabled)
		{
			key = string.Empty;
			enabled = false;
			if (!TryCreateKey(container, out key))
			{
				LogFailureOnce("Opened chest has no stable local player/world/ZDO identity; chest auto-sort was skipped.");
				return false;
			}
			if (SessionDisabled.Contains(key))
			{
				return true;
			}
			if (!_storageHealthy)
			{
				return false;
			}
			try
			{
				bool num = PlayerPrefs.HasKey(key);
				int storedValue = ((!num) ? 1 : PlayerPrefs.GetInt(key, -1));
				if (!ChestSortPreferencePolicy.TryInterpretStoredValue(num, storedValue, out enabled))
				{
					DisableStorage("A saved chest auto-sort preference had an invalid value; chest auto-sort was disabled safely.");
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				DisableStorage("Local chest auto-sort preferences could not be read; chest auto-sort was disabled safely: " + ex.GetType().Name);
				return false;
			}
		}

		internal static bool TrySet(string key, bool enabled)
		{
			if (string.IsNullOrEmpty(key) || !_storageHealthy)
			{
				return false;
			}
			if (enabled)
			{
				SessionDisabled.Remove(key);
			}
			else
			{
				SessionDisabled.Add(key);
			}
			try
			{
				if (enabled)
				{
					PlayerPrefs.DeleteKey(key);
				}
				else
				{
					PlayerPrefs.SetInt(key, 0);
				}
				PlayerPrefs.Save();
				return true;
			}
			catch (Exception ex)
			{
				SessionDisabled.Add(key);
				DisableStorage("Local chest auto-sort preferences could not be saved; chest auto-sort was disabled safely: " + ex.GetType().Name);
				return false;
			}
		}

		internal static void Shutdown()
		{
			SessionDisabled.Clear();
			_storageHealthy = false;
		}

		private static bool TryCreateKey(Container container, out string key)
		{
			key = string.Empty;
			if ((Object)(object)container == (Object)null || ((object)container).GetType() != typeof(Container) || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || NetworkViewField == null)
			{
				return false;
			}
			object? value = NetworkViewField.GetValue(container);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			ZDO zDO = val.GetZDO();
			if (zDO == null || ((ZDOID)(ref zDO.m_uid)).IsNone())
			{
				return false;
			}
			return ChestSortPreferencePolicy.TryCreateKey(Player.m_localPlayer.GetPlayerID(), ZNet.instance.GetWorldUID(), ((object)Unsafe.As<ZDOID, ZDOID>(ref zDO.m_uid)/*cast due to .constrained prefix*/).ToString(), out key);
		}

		private static void DisableStorage(string message)
		{
			_storageHealthy = false;
			LogFailureOnce(message);
		}

		private static void LogFailureOnce(string message)
		{
			if (!_failureLogged)
			{
				_failureLogged = true;
				Plugin plugin = RuntimeContext.Plugin;
				if (plugin != null)
				{
					plugin.Log.LogWarning((object)message);
				}
			}
		}
	}
	internal sealed class CompatibilityResult
	{
		internal bool IsCompatible { get; }

		internal string Reason { get; }

		internal string Diagnostics { get; }

		internal CompatibilityResult(bool isCompatible, string reason, string diagnostics = "")
		{
			IsCompatible = isCompatible;
			Reason = reason;
			Diagnostics = diagnostics;
		}
	}
	internal static class CompatibilityGate
	{
		internal static CompatibilityResult Evaluate()
		{
			List<string> list = new List<string>();
			string diagnostics = DescribeRuntimeIdentity();
			Type[] array = new Type[35]
			{
				typeof(InventoryGui),
				typeof(Container),
				typeof(InventoryGrid),
				typeof(ItemData),
				typeof(Vector2i),
				typeof(Modifier),
				typeof(GameObject),
				typeof(Transform),
				typeof(Requirement),
				typeof(Player),
				typeof(Hud),
				typeof(BuildUi),
				typeof(ZDOID),
				typeof(ZDO),
				typeof(Inventory),
				typeof(ZPackage),
				typeof(ZNetView),
				typeof(Humanoid),
				typeof(Game),
				typeof(ZNet),
				typeof(ZDOMan),
				typeof(Character),
				typeof(TextViewer),
				typeof(GameCamera),
				typeof(PlayerPrefs),
				typeof(Recipe),
				typeof(RequirementMode),
				typeof(ZInput),
				typeof(SplitDialog),
				typeof(CraftingStation),
				typeof(ZNetScene),
				typeof(PrivateArea),
				typeof(Vector3),
				typeof(ZDOVars),
				typeof(TextInput)
			};
			foreach (Type type in array)
			{
				RuntimeContractValidator.RequireType(list, type);
			}
			HarmonyTargetManifest.Validate(list);
			RequireMethod(list, typeof(InventoryGui), "IsContainerOpen");
			RequireMethod(list, typeof(InventoryGui), "UpdateRecipe", typeof(Player), typeof(float));
			RequireStaticMethod(list, typeof(InventoryGui), "get_instance");
			RequireMethod(list, typeof(Container), "CheckAccess", typeof(long));
			RequireMethod(list, typeof(Container), "CheckForChanges");
			RequireMethod(list, typeof(Container), "GetInventory");
			RequireMethod(list, typeof(ZDOID), "IsNone");
			RequireMethod(list, typeof(ZDO), "GetByteArray", typeof(int), typeof(byte[]));
			RequireMethod(list, typeof(ZDO), "get_DataRevision");
			RequireMethod(list, typeof(ZDO), "get_OwnerRevision");
			RequireMethod(list, typeof(Inventory), "Load", typeof(ZPackage));
			RequireConstructor(list, typeof(Inventory), typeof(bool));
			RequireConstructor(list, typeof(ZPackage), typeof(byte[]));
			RequireMethod(list, typeof(ZNetView), "HasOwner");
			RequireMethod(list, typeof(Container), "IsOwner");
			RequireMethod(list, typeof(Container), "IsInUse");
			RequireMethod(list, typeof(Container), "SetInUse", typeof(bool));
			RequireMethod(list, typeof(Container), "StackAll");
			RequireMethod(list, typeof(Container), "RPC_RequestStack", typeof(long), typeof(long));
			RequireMethod(list, typeof(ZDO), "GetOwner");
			RequireMethod(list, typeof(ZDO), "SetOwner", typeof(long));
			RequireStaticMethod(list, typeof(ZDOMan), "GetSessionID");
			RequireMethod(list, typeof(ZDOMan), "GetZDO", typeof(ZDOID));
			RequireMethod(list, typeof(ZDOMan), "ForceSendZDO", typeof(ZDOID));
			RequireMethod(list, typeof(Character), "IsDead");
			RequireMethod(list, typeof(Character), "InCutscene");
			RequireMethod(list, typeof(Character), "IsTeleporting");
			RequireStaticMethod(list, typeof(TextViewer), "get_instance");
			RequireMethod(list, typeof(TextViewer), "IsVisible");
			RequireStaticMethod(list, typeof(GameCamera), "InFreeFly");
			RequireMethod(list, typeof(Inventory), "MoveItemToThis", typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int));
			RequireMethod(list, typeof(Inventory), "GetWidth");
			RequireMethod(list, typeof(Inventory), "GetHeight");
			RequireMethod(list, typeof(Inventory), "GetTotalWeight");
			RequireMethod(list, typeof(Inventory), "ContainsItem", typeof(ItemData));
			RequireMethod(list, typeof(Inventory), "GetAllItems");
			RequireMethod(list, typeof(Inventory), "GetItemAt", typeof(int), typeof(int));
			RequireMethod(list, typeof(Inventory), "RemoveAll");
			RequireMethod(list, typeof(Inventory), "CountItems", typeof(string), typeof(int), typeof(bool));
			RequireMethod(list, typeof(Inventory), "RemoveItem", typeof(ItemData), typeof(int));
			RequireMethod(list, typeof(Inventory), "RemoveItem", typeof(string), typeof(int), typeof(int), typeof(bool));
			RequireMethod(list, typeof(Inventory), "AddItem", typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool));
			RequireMethod(list, typeof(Player), "ConsumeResources", typeof(Requirement[]), typeof(int), typeof(int), typeof(int));
			RequireMethod(list, typeof(ItemData), "Clone");
			RequireMethod(list, typeof(ItemData), "GetWeight", typeof(int));
			RequireMethod(list, typeof(Requirement), "GetAmount", typeof(int));
			RequireMethod(list, typeof(Recipe), "GetAmount", typeof(int), typeof(int).MakeByRefType(), typeof(ItemData).MakeByRefType(), typeof(int));
			RequireMethod(list, typeof(Player), "GetHoverObject");
			RequireMethod(list, typeof(Player), "GetMaxCarryWeight");
			RequireMethod(list, typeof(Player), "GetPlayerID");
			RequireMethod(list, typeof(ZNet), "GetWorldUID");
			RequireStaticMethod(list, typeof(PlayerPrefs), "HasKey", typeof(string));
			RequireStaticMethod(list, typeof(PlayerPrefs), "GetInt", typeof(string), typeof(int));
			RequireStaticMethod(list, typeof(PlayerPrefs), "SetInt", typeof(string), typeof(int));
			RequireStaticMethod(list, typeof(PlayerPrefs), "DeleteKey", typeof(string));
			RequireStaticMethod(list, typeof(PlayerPrefs), "Save");
			RequireStaticMethod(list, typeof(ZInput), "ResetButtonStatus", typeof(string));
			RequireMethod(list, typeof(SplitDialog), "get_IsActive");
			RequireMethod(list, typeof(ZNetView), "IsOwner");
			RequireMethod(list, typeof(ZNetView), "IsValid");
			RequireMethod(list, typeof(ZNetView), "GetZDO");
			RequireStaticMethod(list, typeof(CraftingStation), "get_Instances");
			RequireMethod(list, typeof(CraftingStation), "GetStationBuildRange");
			RequireMethod(list, typeof(CraftingStation), "GetLevel", typeof(bool));
			RequireMethod(list, typeof(CraftingStation), "CheckUsable", typeof(Player), typeof(bool));
			RequireStaticMethod(list, typeof(ZNetScene), "get_instance");
			RequireMethod(list, typeof(ZNetScene), "GetPrefab", typeof(string));
			RequireMethod(list, typeof(ZNetScene), "GetPrefabHash", typeof(GameObject));
			RequireMethod(list, typeof(ZDO), "GetPrefab");
			RequireMethod(list, typeof(ZNetView), "InvokeRPC", typeof(string), typeof(object[]));
			RequireStaticMethod(list, typeof(PrivateArea), "CheckAccess", typeof(Vector3), typeof(float), typeof(bool), typeof(bool));
			RequireField(list, typeof(InventoryGui), "m_pvp");
			RequireField(list, typeof(InventoryGui), "m_container");
			RequireField(list, typeof(InventoryGui), "m_currentContainer");
			RequireField(list, typeof(InventoryGui), "m_craftTimer");
			RequireField(list, typeof(InventoryGui), "m_craftRecipe");
			RequireField(list, typeof(InventoryGui), "m_selectedRecipe");
			RequireField(list, typeof(InventoryGui), "m_reqList");
			RequireField(list, typeof(InventoryGui), "m_craftUpgradeItem");
			RequireField(list, typeof(InventoryGui), "m_selectedVariant");
			RequireField(list, typeof(InventoryGui), "m_craftVariant");
			RequireField(list, typeof(InventoryGui), "m_touchMultiCrafting");
			Type type2 = AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair");
			if (type2 == null)
			{
				list.Add("InventoryGui.RecipeDataPair missing");
			}
			else
			{
				RequireProperty(list, type2, "Recipe");
				RequireProperty(list, type2, "ItemData");
			}
			RequireField(list, typeof(InventoryGui), "m_multiCrafting");
			RequireField(list, typeof(InventoryGui), "m_multiCraftAmount");
			RequireField(list, typeof(InventoryGui), "m_dragItem");
			RequireField(list, typeof(InventoryGui), "m_dragInventory");
			RequireField(list, typeof(InventoryGui), "m_trophiesPanel");
			RequireField(list, typeof(InventoryGui), "m_achievementsPanel");
			RequireField(list, typeof(InventoryGui), "m_skillsDialog");
			RequireField(list, typeof(InventoryGui), "m_textsDialog");
			RequireField(list, typeof(InventoryGui), "m_splitDialog");
			RequireField(list, typeof(InventoryGui), "m_variantDialog");
			RequireField(list, typeof(Hud), "m_requirementItems");
			RequireField(list, typeof(Container), "m_nview");
			RequireField(list, typeof(Container), "m_wagon");
			RequireField(list, typeof(ZDO), "m_uid");
			RequireField(list, typeof(ZDOVars), "s_items");
			RequireField(list, typeof(Inventory), "m_onChanged");
			RequireField(list, typeof(Inventory), "m_inventory");
			RequireField(list, typeof(Player), "m_customData");
			RequireField(list, typeof(Player), "m_noPlacementCost");
			RequireField(list, typeof(TextInput), "m_inputField");
			RequireField(list, typeof(ItemData), "m_gridPos");
			RequireField(list, typeof(ItemData), "m_stack");
			RequireField(list, typeof(ItemData), "m_quality");
			RequireField(list, typeof(ItemData), "m_worldLevel");
			RequireField(list, typeof(ItemData), "m_equipped");
			RequireField(list, typeof(Piece), "m_resources");
			RequireField(list, typeof(Piece), "m_craftingStation");
			RequireField(list, typeof(Recipe), "m_resources");
			RequireField(list, typeof(Recipe), "m_requireOnlyOneIngredient");
			if (list.Count != 0)
			{
				return new CompatibilityResult(isCompatible: false, string.Join("; ", list), diagnostics);
			}
			return new CompatibilityResult(isCompatible: true, "verified runtime contract", diagnostics);
		}

		private static string DescribeRuntimeIdentity()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Assembly assembly = typeof(Player).Assembly;
			string text = ((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString();
			string unityVersion = Application.unityVersion;
			string text2 = typeof(BaseUnityPlugin).Assembly.GetName().Version?.ToString() ?? "unknown";
			string text3 = typeof(Harmony).Assembly.GetName().Version?.ToString() ?? "unknown";
			Guid moduleVersionId = assembly.ManifestModule.ModuleVersionId;
			string[] obj = new string[12]
			{
				"Valheim label ",
				Application.version,
				", API ",
				text,
				", Unity ",
				unityVersion,
				", BepInEx ",
				text2,
				", Harmony ",
				text3,
				", assembly_valheim MVID ",
				null
			};
			Guid guid = moduleVersionId;
			obj[11] = guid.ToString();
			return string.Concat(obj);
		}

		private static void RequireMethod(ICollection<string> failures, Type type, string name, params Type[] parameters)
		{
			RuntimeContractValidator.RequireMethod(failures, type, name, mustBeStatic: false, declaredOnly: false, parameters);
		}

		private static void RequireStaticMethod(ICollection<string> failures, Type type, string name, params Type[] parameters)
		{
			RuntimeContractValidator.RequireMethod(failures, type, name, mustBeStatic: true, declaredOnly: false, parameters);
		}

		private static void RequireConstructor(ICollection<string> failures, Type type, params Type[] parameters)
		{
			RuntimeContractValidator.RequireConstructor(failures, type, parameters);
		}

		private static void RequireProperty(ICollection<string> failures, Type type, string name)
		{
			RuntimeContractValidator.RequireProperty(failures, type, name);
		}

		private static void RequireField(ICollection<string> failures, Type type, string name)
		{
			RuntimeContractValidator.RequireField(failures, type, name);
		}
	}
	internal static class ConfigMigration
	{
		internal static ConfigEntry<bool> BindRenamedDefaultEnabledBoolean(ConfigFile config, string section, string legacyKey, string currentKey, string currentDescription)
		{
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				ConfigEntry<bool> val = config.Bind<bool>(section, legacyKey, true, "Legacy Stackmaster setting; migrated automatically and removed from the saved configuration.");
				ConfigEntry<bool> val2 = config.Bind<bool>(section, currentKey, true, currentDescription);
				val2.Value = val.Value && val2.Value;
				if (!config.Remove(((ConfigEntryBase)val).Definition))
				{
					throw new InvalidOperationException("Could not retire legacy Stackmaster configuration key: " + legacyKey);
				}
				config.Save();
				return val2;
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
		}
	}
	internal sealed class ContainerHandle
	{
		internal string Id { get; }

		internal Container Container { get; }

		internal ZNetView NetworkView { get; }

		internal ContainerSnapshot Snapshot { get; }

		internal Inventory ResourceInventory { get; }

		internal uint ResourceDataRevision { get; }

		internal ZDOID ResourceZdoId { get; }

		internal ushort ResourceOwnerRevision { get; }

		internal long ResourceOwner { get; }

		internal bool ResourceReadable { get; }

		internal ContainerHandle(string id, Container container, ZNetView networkView, ContainerSnapshot snapshot, Inventory resourceInventory, uint resourceDataRevision, ZDOID resourceZdoId, ushort resourceOwnerRevision, long resourceOwner, bool resourceReadable)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			Id = id;
			Container = container;
			NetworkView = networkView;
			Snapshot = snapshot;
			ResourceInventory = resourceInventory;
			ResourceDataRevision = resourceDataRevision;
			ResourceZdoId = resourceZdoId;
			ResourceOwnerRevision = resourceOwnerRevision;
			ResourceOwner = resourceOwner;
			ResourceReadable = resourceReadable;
		}
	}
	internal sealed class TargetDiscoveryDiagnostic
	{
		internal bool TargetPresent { get; }

		internal double Distance { get; }

		internal string Scope { get; }

		internal bool WithinScope { get; }

		internal bool Discovered { get; set; }

		internal string ObservedType { get; set; }

		internal bool? IsVanilla { get; set; }

		internal bool? HasNetworkView { get; set; }

		internal bool? NetworkViewValid { get; set; }

		internal bool? HasZdo { get; set; }

		internal bool? Refreshed { get; set; }

		internal bool? HasInventory { get; set; }

		internal bool? InUse { get; set; }

		internal bool? AccessGranted { get; set; }

		internal string RefreshFailure { get; set; }

		internal string AccessFailure { get; set; }

		internal TargetDiscoveryDiagnostic(bool targetPresent, double distance, StorageScope scope, Vector3 targetPosition)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			TargetPresent = targetPresent;
			Distance = distance;
			Scope = ((scope != null) ? scope.Description : "unavailable");
			WithinScope = targetPresent && scope != null && scope.Contains(targetPosition);
		}

		internal string Format(DiscoveryResult discovery)
		{
			return string.Format(CultureInfo.InvariantCulture, "targetPresent={0} discovered={1} distance={2} scope={3} withinScope={4} searchTruncated={5} truncationReason={6} searchMs={7:0.00} objectScanMs={8:0.00} inspectionMs={9:0.00} budgetMs={10:0.00} candidates={11} inspected={12} minimumBeforeBudget={13} maximumNearby={14} type={15} vanilla={16} nview={17} nviewValid={18} zdo={19} refresh={20} inventory={21} inUse={22} access={23} refreshError={24} accessError={25}", TargetPresent, Discovered, double.IsPositiveInfinity(Distance) ? "n/a" : Distance.ToString("0.0", CultureInfo.InvariantCulture), Scope, WithinScope, discovery.Truncated, discovery.TruncationReason ?? "none", discovery.SearchMilliseconds, discovery.ObjectScanMilliseconds, discovery.InspectionMilliseconds, 25.0, discovery.NearbyCandidates, discovery.InspectedNearby, 8, 128, ObservedType ?? "n/a", FormatNullable(IsVanilla), FormatNullable(HasNetworkView), FormatNullable(NetworkViewValid), FormatNullable(HasZdo), FormatNullable(Refreshed), FormatNullable(HasInventory), FormatNullable(InUse), FormatNullable(AccessGranted), Sanitize(RefreshFailure), Sanitize(AccessFailure));
		}

		private static string FormatNullable(bool? value)
		{
			if (!value.HasValue)
			{
				return "n/a";
			}
			return value.Value.ToString();
		}

		private static string Sanitize(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "none";
			}
			string text = value.Replace('\r', ' ').Replace('\n', ' ').Trim();
			if (text.Length > 160)
			{
				return text.Substring(0, 160) + "…";
			}
			return text;
		}
	}
	internal sealed class DiscoveryResult
	{
		internal IReadOnlyList<ContainerHandle> Containers { get; }

		internal bool Truncated { get; }

		internal double SearchMilliseconds { get; }

		internal double ObjectScanMilliseconds { get; }

		internal double InspectionMilliseconds { get; }

		internal int NearbyCandidates { get; }

		internal int InspectedNearby { get; }

		internal string TruncationReason { get; }

		internal TargetDiscoveryDiagnostic TargetDiagnostic { get; }

		internal StorageScope Scope { get; }

		internal DiscoveryResult(IReadOnlyList<ContainerHandle> containers, bool truncated, double searchMilliseconds, double objectScanMilliseconds, double inspectionMilliseconds, int nearbyCandidates, int inspectedNearby, string truncationReason, TargetDiscoveryDiagnostic targetDiagnostic, StorageScope scope)
		{
			Containers = containers;
			Truncated = truncated;
			SearchMilliseconds = searchMilliseconds;
			ObjectScanMilliseconds = objectScanMilliseconds;
			InspectionMilliseconds = inspectionMilliseconds;
			NearbyCandidates = nearbyCandidates;
			InspectedNearby = inspectedNearby;
			TruncationReason = truncationReason;
			TargetDiagnostic = targetDiagnostic;
			Scope = scope;
		}
	}
	internal static class ContainerDiscovery
	{
		internal const double SearchBudgetMilliseconds = 25.0;

		internal const int MinimumNearbyContainersBeforeBudget = 8;

		internal const int MaximumNearbyContainers = 128;

		private static readonly NearbyInspectionPolicy NearbyPolicy = new NearbyInspectionPolicy(8, 128, 25.0);

		private static readonly FieldInfo NetworkViewField = AccessTools.Field(typeof(Container), "m_nview");

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

		private static readonly MethodInfo CheckForChangesMethod = AccessTools.Method(typeof(Container), "CheckForChanges", (Type[])null, (Type[])null);

		internal static DiscoveryResult Discover(Player player, Container target, CompatibilityCatalog catalog, StorageScope scope, bool requireComplete = false, bool resourceReadOnly = false)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			List<ContainerHandle> list = new List<ContainerHandle>();
			scope = scope ?? StorageScopeProvider.Resolve(player);
			double distance = (((Object)(object)target != (Object)null) ? ((double)Vector3.Distance(((Component)player).transform.position, ((Component)target).transform.position)) : double.PositiveInfinity);
			TargetDiscoveryDiagnostic targetDiscoveryDiagnostic = new TargetDiscoveryDiagnostic((Object)(object)target != (Object)null, distance, scope, (Vector3)(((Object)(object)target != (Object)null) ? ((Component)target).transform.position : default(Vector3)));
			if ((Object)(object)target != (Object)null && scope.Contains(((Component)target).transform.position))
			{
				list.Add(Inspect(player, target, catalog, distance, isTarget: true, targetDiscoveryDiagnostic, resourceReadOnly));
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			Stopwatch stopwatch2 = Stopwatch.StartNew();
			var array = (from container in Object.FindObjectsByType<Container>((FindObjectsInactive)0, (FindObjectsSortMode)0)
				where (Object)(object)container != (Object)null && (Object)(object)container != (Object)(object)target
				select new
				{
					Container = container,
					Distance = (double)Vector3.Distance(((Component)player).transform.position, ((Component)container).transform.position)
				} into candidate
				where scope.Contains(((Component)candidate.Container).transform.position)
				orderby candidate.Distance, ((Object)candidate.Container).GetInstanceID()
				select candidate).ToArray();
			stopwatch2.Stop();
			Stopwatch stopwatch3 = Stopwatch.StartNew();
			int num = 0;
			string text = null;
			var array2 = array;
			foreach (var anon in array2)
			{
				double totalMilliseconds = stopwatch3.Elapsed.TotalMilliseconds;
				if (!requireComplete && !scope.RequiresCompleteDiscovery && !NearbyPolicy.CanInspectNext(num, totalMilliseconds))
				{
					text = NearbyPolicy.StopReason(num, totalMilliseconds);
					break;
				}
				list.Add(Inspect(player, anon.Container, catalog, anon.Distance, isTarget: false, null, resourceReadOnly));
				num++;
			}
			stopwatch3.Stop();
			stopwatch.Stop();
			bool flag = num < array.Length;
			if (flag && string.IsNullOrEmpty(text))
			{
				text = "responsiveness limit reached";
			}
			return new DiscoveryResult(list, flag, stopwatch.Elapsed.TotalMilliseconds, stopwatch2.Elapsed.TotalMilliseconds, stopwatch3.Elapsed.TotalMilliseconds, array.Length, num, text, targetDiscoveryDiagnostic, scope);
		}

		private static ContainerHandle Inspect(Player player, Container container, CompatibilityCatalog catalog, double distance, bool isTarget, TargetDiscoveryDiagnostic diagnostic, bool resourceReadOnly)
		{
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			object? value = NetworkViewField.GetValue(container);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			bool flag = (Object)(object)val != (Object)null && val.IsValid();
			ZDO val2 = (flag ? val.GetZDO() : null);
			string text = ((val2 != null) ? ((object)Unsafe.As<ZDOID, ZDOID>(ref val2.m_uid)/*cast due to .constrained prefix*/).ToString() : ("instance:" + ((Object)container).GetInstanceID()));
			Type type = ((object)container).GetType();
			bool flag2 = type == typeof(Container) && type.Assembly == typeof(Container).Assembly;
			string failure = null;
			bool flag3 = !resourceReadOnly && flag2 && flag && val2 != null && TryRefreshFromNetwork(container, out failure);
			Inventory val3 = (flag3 ? container.GetInventory() : null);
			bool flag4 = flag3 && val3 != null;
			bool flag5 = isTarget && StorageAction.IsLocalOpenTarget(container);
			bool flag6 = flag4 && ((!flag5 && container.IsInUse()) || ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse()));
			string failure2 = null;
			bool num;
			if (!resourceReadOnly)
			{
				num = flag4;
			}
			else
			{
				if (!(flag2 && flag))
				{
					goto IL_0140;
				}
				num = val2 != null;
			}
			if (!num)
			{
				goto IL_0140;
			}
			int num2 = (TryCheckAccess(player, container, out failure2) ? 1 : 0);
			goto IL_0141;
			IL_0140:
			num2 = 0;
			goto IL_0141;
			IL_0141:
			bool flag7 = (byte)num2 != 0;
			Inventory inventory = null;
			uint dataRevision = 0u;
			string failure3 = null;
			bool flag8 = resourceReadOnly && flag2 && flag && val2 != null && TryReadSerializedInventory(container, val2, out inventory, out dataRevision, out failure3);
			ResourceSnapshotReadPlan resourceSnapshotReadPlan = ResourceSnapshotPolicy.Evaluate(resourceReadOnly, flag4, flag7, flag6, flag8);
			bool captureLiveInventory = resourceSnapshotReadPlan.CaptureLiveInventory;
			int capacity = ((val3 != null) ? (val3.GetWidth() * val3.GetHeight()) : 0);
			IReadOnlyList<ItemStackSnapshot> readOnlyList2;
			if (!captureLiveInventory || val3 == null)
			{
				IReadOnlyList<ItemStackSnapshot> readOnlyList = Array.Empty<ItemStackSnapshot>();
				readOnlyList2 = readOnlyList;
			}
			else
			{
				readOnlyList2 = InventorySnapshots.CaptureInventory(text, val3, catalog).Items;
			}
			IReadOnlyList<ItemStackSnapshot> items = readOnlyList2;
			bool useDetachedInventory = resourceSnapshotReadPlan.UseDetachedInventory;
			if (!flag8 && string.IsNullOrEmpty(failure))
			{
				failure = failure3;
			}
			if (diagnostic != null)
			{
				diagnostic.Discovered = true;
				diagnostic.ObservedType = type.FullName;
				diagnostic.IsVanilla = flag2;
				diagnostic.HasNetworkView = (Object)(object)val != (Object)null;
				diagnostic.NetworkViewValid = flag;
				diagnostic.HasZdo = val2 != null;
				diagnostic.Refreshed = flag3;
				diagnostic.HasInventory = val3 != null;
				diagnostic.InUse = (flag4 ? new bool?(flag6) : ((bool?)null));
				diagnostic.AccessGranted = (flag4 ? new bool?(flag7) : ((bool?)null));
				diagnostic.RefreshFailure = failure;
				diagnostic.AccessFailure = failure2;
			}
			ContainerSnapshot snapshot = new ContainerSnapshot(text, distance, isTarget, flag4, captureLiveInventory, flag2, flag6, capacity, items);
			return new ContainerHandle(text, container, val, snapshot, useDetachedInventory ? inventory : null, dataRevision, (ZDOID)(((??)val2?.m_uid) ?? default(ZDOID)), (ushort)((val2 != null) ? val2.OwnerRevision : 0), (val2 != null) ? val2.GetOwner() : 0, useDetachedInventory);
		}

		private static bool TryReadSerializedInventory(Container container, ZDO zdo, out Inventory inventory, out uint dataRevision, out string failure)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			inventory = null;
			dataRevision = 0u;
			failure = null;
			try
			{
				uint dataRevision2 = zdo.DataRevision;
				byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null);
				uint dataRevision3 = zdo.DataRevision;
				if (dataRevision2 != dataRevision3)
				{
					failure = "container data revision changed while reading its serialized inventory";
					return false;
				}
				Inventory val = new Inventory(true);
				if (byteArray != null)
				{
					val.Load(new ZPackage(byteArray));
				}
				if (zdo.DataRevision != dataRevision3)
				{
					failure = "container data revision changed while decoding its serialized inventory";
					return false;
				}
				if (!DetachedItemHydrator.TryHydrate(val.GetAllItems(), delegate(ItemData item)
				{
					ItemDrop val2 = (((Object)(object)item.m_dropPrefab != (Object)null) ? item.m_dropPrefab.GetComponent<ItemDrop>() : null);
					return (!((Object)(object)val2 != (Object)null) || val2.m_itemData == null) ? null : val2.m_itemData.m_shared;
				}, delegate(ItemData item, SharedData shared)
				{
					item.m_shared = shared;
				}))
				{
					failure = "detached inventory item metadata could not be resolved";
					return false;
				}
				inventory = val;
				dataRevision = dataRevision3;
				return true;
			}
			catch (Exception ex)
			{
				failure = ex.Message;
				return false;
			}
		}

		internal static bool RefreshFromNetwork(Container container)
		{
			string failure;
			bool num = TryRefreshFromNetwork(container, out failure);
			if (!num && (Object)(object)RuntimeContext.Plugin != (Object)null)
			{
				RuntimeContext.Plugin.Log.LogWarning((object)("Container refresh failed safely: " + failure));
			}
			return num;
		}

		private static bool TryRefreshFromNetwork(Container container, out string failure)
		{
			failure = null;
			try
			{
				CheckForChangesMethod.Invoke(container, null);
				return true;
			}
			catch (Exception ex)
			{
				TargetInvocationException ex2 = ex as TargetInvocationException;
				failure = ((ex2 == null) ? ex.Message : (ex2.InnerException?.Message ?? ex2.Message));
				return false;
			}
		}

		internal static bool CheckAccess(Player player, Container container)
		{
			string failure;
			bool num = TryCheckAccess(player, container, out failure);
			if (!num && !string.IsNullOrEmpty(failure) && (Object)(object)RuntimeContext.Plugin != (Object)null)
			{
				RuntimeContext.Plugin.Log.LogWarning((object)("Container access check failed safely: " + failure));
			}
			return num;
		}

		private static bool TryCheckAccess(Player player, Container container, out string failure)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			failure = null;
			try
			{
				if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
				{
					return false;
				}
				return (bool)CheckAccessMethod.Invoke(container, new object[1] { player.GetPlayerID() });
			}
			catch (Exception ex)
			{
				TargetInvocationException ex2 = ex as TargetInvocationException;
				failure = ((ex2 == null) ? ex.Message : (ex2.InnerException?.Message ?? ex2.Message));
				return false;
			}
		}
	}
	internal sealed class SessionToken
	{
		internal ZNet Network { get; }

		internal long LocalSession { get; }

		internal SessionToken(ZNet network, long localSession)
		{
			Network = network;
			LocalSession = localSession;
		}

		internal static bool TryCapture(out SessionToken token)
		{
			token = null;
			if (!RuntimeContext.Compatibility.IsCompatible || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null)
			{
				return false;
			}
			token = new SessionToken(ZNet.instance, ZDOMan.GetSessionID());
			return true;
		}

		internal bool IsCurrent()
		{
			if (RuntimeContext.Compatibility.IsCompatible && Network == ZNet.instance && ZDOMan.instance != null)
			{
				return ZDOMan.GetSessionID() == LocalSession;
			}
			return false;
		}
	}
	internal sealed class CraftingIntent
	{
		internal InventoryGui Gui { get; }

		internal Player Player { get; }

		internal Recipe Recipe { get; }

		internal ItemData UpgradeItem { get; }

		internal int Quality { get; }

		internal int Variant { get; }

		internal bool MultiCrafting { get; }

		internal int Multiplier { get; }

		internal CraftingStation Station { get; }

		internal int StationLevel { get; }

		internal Vector3 PlayerPosition { get; }

		internal string ScopeSignature { get; }

		internal SessionToken Session { get; }

		internal CraftingIntent(InventoryGui gui, Player player, Recipe recipe, ItemData upgradeItem, int quality, int variant, bool multiCrafting, int multiplier, CraftingStation station, StorageScope scope, SessionToken session)
		{
			//IL_006d: 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)
			Gui = gui;
			Player = player;
			Recipe = recipe;
			UpgradeItem = upgradeItem;
			Quality = quality;
			Variant = variant;
			MultiCrafting = multiCrafting;
			Multiplier = multiplier;
			Station = station;
			StationLevel = ((!((Object)(object)station == (Object)null)) ? station.GetLevel(true) : 0);
			PlayerPosition = ((Component)player).transform.position;
			ScopeSignature = scope?.Signature;
			Session = session;
		}
	}
	internal static class CraftingPreflightAction
	{
		private const float OwnershipTimeoutSeconds = 2f;

		private static readonly CraftingActionLifecycle Lifecycle = new CraftingActionLifecycle();

		private static readonly MethodInfo OnCraftPressedMethod = AccessTools.Method(typeof(InventoryGui), "OnCraftPressed", Type.EmptyTypes, (Type[])null);

		private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

		private static readonly Type RecipeDataPairType = AccessTools.Inner(typeof(InventoryGui), "RecipeDataPair");

		private static readonly PropertyInfo SelectedRecipeProperty = AccessTools.Property(RecipeDataPairType, "Recipe");

		private static readonly PropertyInfo SelectedUpgradeItemProperty = AccessTools.Property(RecipeDataPairType, "ItemData");

		private static readonly FieldInfo SelectedVariantField = AccessTools.Field(typeof(InventoryGui), "m_selectedVariant");

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

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

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

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

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

		private static readonly FieldInfo TouchMultiCraftingField = AccessTools.Field(typeof(InventoryGui), "m_touchMultiCrafting");

		private static readonly FieldInfo CraftTimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer");

		private static CraftingIntent _intent;

		private static CraftingResourcePlan _plan;

		private static PreparedCraftingResources _prepared;

		private static OwnershipBatch _ownership;

		private static int _generation;

		private static bool _resuming;

		internal static bool Prefix(InventoryGui gui)
		{
			if (_resuming)
			{
				return true;
			}
			if (!RuntimeContext.Compatibility.IsCompatible || (Object)(object)RuntimeContext.Plugin == (Object)null || !RuntimeContext.Plugin.CraftingFromNearbyChestsEnabled.Value)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || localPlayer.NoCostCheat() || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)))
			{
				return true;
			}
			if (Lifecycle.IsActive)
			{
				if (_intent != null && IsSelectionCurrent(_intent, requireCraftFields: false))
				{
					Show("Stackmaster: securing the required nearby materials.");
					return false;
				}
				Cancel("a newer crafting intent replaced the pending craft", notify: false);
			}
			if (!TryCaptureIntent(gui, out var intent))
			{
				return true;
			}
			if (!NearbyResourceService.TryPlanCraftingResources(intent.Player, intent.Recipe, intent.Quality, intent.Multiplier, out var planned, out var failure))
			{
				return true;
			}
			if (planned.Plan.RequiredUnits == 0)
			{
				return true;
			}
			_intent = intent;
			_plan = planned;
			ContainerHandle[] array = planned.RequiredHandles.Where((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner()).ToArray();
			_generation = Lifecycle.Begin(array.Length != 0);
			if (array.Length == 0)
			{
				if (!TryPrepare(out failure))
				{
					Cancel(failure, notify: true);
					return false;
				}
				return true;
			}
			Show("Stackmaster: securing the required nearby materials.");
			((MonoBehaviour)RuntimeContext.Plugin).StartCoroutine(AcquireAndResume(_generation, intent.Session, array));
			return false;
		}

		internal static void AfterVanillaStart(InventoryGui gui)
		{
			if (!Lifecycle.Matches(_generation) || Lifecycle.Phase != CraftingActionPhase.Reserved || _intent == null || gui != _intent.Gui)
			{
				return;
			}
			if (_resuming)
			{
				MultiCraftingField.SetValue(gui, _intent.MultiCrafting);
				if (_intent.MultiCrafting)
				{
					MultiCraftAmountField.SetValue(gui, _intent.Multiplier);
				}
			}
			if (!IsSelectionCurrent(_intent, requireCraftFields: true) || GetCraftTimer(gui) < 0f || !Lifecycle.MarkCrafting(_generation))
			{
				Cancel("vanilla did not start the reserved craft", notify: true);
			}
		}

		internal static Exception Finalizer(Exception exception)
		{
			if (exception != null && Lifecycle.IsActive)
			{
				Cancel("craft start threw " + exception.GetType().Name, notify: true);
			}
			return exception;
		}

		internal static void Update(InventoryGui gui)
		{
			if (!Lifecycle.IsActive || _intent == null || gui != _intent.Gui)
			{
				return;
			}
			if (!RuntimeContext.Compatibility.IsCompatible || !_intent.Session.IsCurrent())
			{
				Cancel("the network session changed during crafting", notify: false);
				return;
			}
			bool flag = Lifecycle.Phase == CraftingActionPhase.Crafting;
			if (!IsSelectionCurrent(_intent, flag))
			{
				Cancel("the player, crafting selection, station, or storage scope changed", notify: true);
			}
			else if (flag && GetCraftTimer(gui) < 0f)
			{
				Cancel("the craft was canceled before completion", notify: false);
			}
		}

		internal static bool TryBeginPreparedTransaction(InventoryGui gui, Player player, Recipe recipe, int quality, int multiplier, out bool handled, out string failure)
		{
			handled = Lifecycle.IsActive;
			failure = null;
			if (!handled)
			{
				return false;
			}
			if (_intent == null || _prepared == null || gui != _intent.Gui || player != _intent.Player || recipe != _intent.Recipe || quality != _intent.Quality || multiplier != _intent.Multiplier || !IsSelectionCurrent(_intent, requireCraftFields: true) || !Lifecycle.TransferToTransaction(_generation))
			{
				failure = "the prepared crafting intent no longer matches the finishing craft";
				Cancel(failure, notify: true);
				return false;
			}
			PreparedCraftingResources prepared = _prepared;
			_prepared = null;
			ClearReferences();
			Lifecycle.FinishTransferred(_generation);
			if (!NearbyResourceService.TryBeginPreparedCraftingTransaction(player, prepared, out failure))
			{
				NearbyResourceService.ReleasePreparedCraftingResources(prepared);
				return false;
			}
			return true;
		}

		internal static void Cancel(string reason, bool notify)
		{
			if (Lifecycle.IsActive || _intent != null || _prepared != null || _ownership != null)
			{
				if (_intent != null && (Object)(object)_intent.Gui != (Object)null)
				{
					CraftTimerField.SetValue(_intent.Gui, -1f);
				}
				Lifecycle.Cancel();
				if (_prepared != null)
				{
					NearbyResourceService.ReleasePreparedCraftingResources(_prepared);
				}
				if (_ownership != null)
				{
					OwnershipCoordinator.Cancel(_ownership, reason);
				}
				ClearReferences();
				if (notify && !string.IsNullOrWhiteSpace(reason))
				{
					Show("Stackmaster: craft canceled — " + reason + ".");
				}
			}
		}

		internal static void Shutdown(string reason)
		{
			Cancel(reason, notify: false);
		}

		private static IEnumerator AcquireAndResume(int generation, SessionToken session, ContainerHandle[] unowned)
		{
			if (!Lifecycle.Matches(generation) || !session.IsCurrent())
			{
				yield break;
			}
			OwnershipBatch ownership;
			try
			{
				ownership = (_ownership = OwnershipCoordinator.Begin(unowned));
			}
			catch (Exception ex)
			{
				Cancel("ownership request failed: " + ex.GetType().Name, notify: true);
				yield break;
			}
			float deadline = Time.realtimeSinceStartup + 2f;
			while (Lifecycle.Matches(generation) && session.IsCurrent())
			{
				try
				{
					ownership.Refresh();
				}
				catch (Exception ex2)
				{
					Cancel("ownership refresh failed: " + ex2.GetType().Name, notify: true);
					yield break;
				}
				if (ownership.IsComplete)
				{
					break;
				}
				if (Time.realtimeSinceStartup >= deadline)
				{
					ownership.Timeout();
					break;
				}
				yield return null;
			}
			if (!Lifecycle.Matches(generation) || !session.IsCurrent())
			{
				if (_ownership == ownership)
				{
					OwnershipCoordinator.Cancel(ownership, "craft acquisition became stale");
					_ownership = null;
				}
				yield break;
			}
			try
			{
				ownership.Refresh();
				if (!ownership.IsComplete)
				{
					ownership.Timeout();
				}
				if (ownership.FailedContainerIds.Count != 0)
				{
					Cancel((ownership.OwnerRejectedContainerIds.Count != 0) ? "The required materials are currently in use" : "Stackmaster could not safely acquire the required materials", notify: true);
					yield break;
				}
				OwnershipLeaseManager.HoldForCrafting(ownership);
				OwnershipCoordinator.End(ownership);
				_ownership = null;
				if (!TryPrepare(out var failure))
				{
					OwnershipLeaseManager.ReleaseBatch(ownership, "craft reservation failed");
					Cancel(failure, notify: true);
					yield break;
				}
				if (!Lifecycle.MarkReserved(generation))
				{
					Cancel("the ownership callback was stale", notify: true);
					yield break;
				}
				TouchMultiCraftingField.SetValue(_intent.Gui, _intent.MultiCrafting);
				SelectedVariantField.SetValue(_intent.Gui, _intent.Variant);
				try
				{
					_resuming = true;
					OnCraftPressedMethod.Invoke(_intent.Gui, null);
				}
				finally
				{
					_resuming = false;
				}
			}
			catch (TargetInvocationException ex3)
			{
				Cancel("vanilla craft start threw " + (ex3.InnerException ?? ex3).GetType().Name, notify: true);
			}
			catch (Exception ex4)
			{
				Cancel("ownership preparation threw " + ex4.GetType().Name, notify: true);
			}
			finally
			{
				if (_ownership == ownership)
				{
					OwnershipCoordinator.Cancel(ownership, "craft acquisition ended before reservation");
					_ownership = null;
				}
			}
		}

		private static bool TryPrepare(out string failure)
		{
			failure = null;
			if (_intent == null || _plan == null || !IsSelectionCurrent(_intent, requireCraftFields: false))
			{
				failure = "the crafting selection changed before reservation";
				return false;
			}
			return NearbyResourceService.TryPrepareCraftingResources(_intent.Player, _plan, out _prepared, out failure);
		}

		private static bool TryCaptureIntent(InventoryGui gui, out CraftingIntent intent)
		{
			intent = null;
			Player localPlayer = Player.m_localPlayer;
			object value = SelectedRecipeField.GetValue(gui);
			Recipe val = (Recipe)((value == null) ? null : /*isinst with value type is only supported in some contexts*/);
			if ((Object)(object)localPlayer == (Object)null || value == null || (Object)(object)val == (Object)null)
			{
				return false;
			}
			object? value2 = SelectedUpgradeItemProperty.GetValue(value, null);
			ItemData val2 = (ItemData)((value2 is ItemData) ? value2 : null);
			int quality = ((val2 == null) ? 1 : (val2.m_quality + 1));
			bool flag = val2 == null && (ZInput.GetButton("AltPlace") || ZInput.GetButton("JoyLStick") || (bool)TouchMultiCraftingField.GetValue(gui));
			int multiplier = ((!flag) ? 1 : Math.Max(1, (int)MultiCraftAmountField.GetValue(gui)));
			StorageScope storageScope = StorageScopeProvider.Resolve(localPlayer);
			if (storageScope == null || !SessionToken.TryCapture(out var token))
			{
				return false;
			}
			intent = new CraftingIntent(gui, localPlayer, val, val2, quality, (int)SelectedVariantField.GetValue(gui), flag, multiplier, localPlayer.GetCurrentCraftingStation(), storageScope, token);
			return true;
		}

		private static bool IsSelectionCurrent(CraftingIntent intent, bool requireCraftFields)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (intent != null && !((Object)(object)intent.Player == (Object)null) && !((Object)(object)intent.Gui == (Object)null) && Player.m_localPlayer == intent.Player && !((Character)intent.Player).IsDead() && !((Character)intent.Player).IsTeleporting() && !((Character)intent.Player).InCutscene() && intent.Player.GetCurrentCraftingStation() == intent.Station && (!((Object)(object)intent.Station != (Object)null) || (intent.Station.CheckUsable(intent.Player, false) && intent.Station.GetLevel(true) == intent.StationLevel)))
			{
				Vector3 val = ((Component)intent.Player).transform.position - intent.PlayerPosition;
				if (!(((Vector3)(ref val)).sqrMagnitude > 0.0025f))
				{
					object value = SelectedRecipeField.GetValue(intent.Gui);
					if (value == null || SelectedRecipeProperty.GetValue(value, null) != intent.Recipe || SelectedUpgradeItemProperty.GetValue(value, null) != intent.UpgradeItem || (int)SelectedVariantField.GetValue(intent.Gui) != intent.Variant)
					{
						return false;
					}
					StorageScope storageScope = StorageScopeProvider.Resolve(intent.Player);
					if (storageScope == null || !string.Equals(storageScope.Signature, intent.ScopeSignature, StringComparison.Ordinal))
					{
						return false;
					}
					if (!requireCraftFields)
					{
						return true;
					}
					if (CraftRecipeField.GetValue(intent.Gui) == intent.Recipe && CraftUpgradeItemField.GetValue(intent.Gui) == intent.UpgradeItem && (int)CraftVariantField.GetValue(intent.Gui) == intent.Variant && (bool)MultiCraftingField.GetValue(intent.Gui) == intent.MultiCrafting)
					{
						if (intent.MultiCrafting)
						{
							return (int)MultiCraftAmountField.GetValue(intent.Gui) == intent.Multiplier;
						}
						return true;
					}
					return false;
				}
			}
			return false;
		}

		private static float GetCraftTimer(InventoryGui gui)
		{
			return (float)CraftTimerField.GetValue(gui);
		}

		private static void ClearReferences()
		{
			_intent = null;
			_plan = null;
			_prepared = null;
			_ownership = null;
		}

		private static void Show(string message)
		{
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false, true);
			}
		}
	}
	internal static class ExpeditionKitClickPatch
	{
		internal static bool Prefix([HarmonyArgument(0)] Piece piece)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			try
			{
				Plugin plugin = RuntimeContext.Plugin;
				Player localPlayer = Player.m_localPlayer;
				KeyCode[] source;
				if (!((Object)(object)plugin != (Object)null))
				{
					source = Array.Empty<KeyCode>();
				}
				else
				{
					KeyboardShortcut value = plugin.StorageActionShortcut.Value;
					source = ((KeyboardShortcut)(ref value)).Modifiers.ToArray();
				}
				bool[] configuredModifierStates = ((IEnumerable<KeyCode>)source).Select((Func<KeyCode, bool>)Input.GetKey).ToArray();
				bool hasClickedPiece = (Object)(object)piece != (Object)null && (piece.m_resources ?? Array.Empty<Requirement>()).Any((Requirement requirement) => requirement != null && (Object)(object)requirement.m_resItem != (Object)null && requirement.m_amount > 0);
				if (!ExpeditionClickPolicy.ShouldIntercept(RuntimeContext.Compatibility.IsCompatible, (Object)(object)localPlayer != (Object)null, hasClickedPiece, configuredModifierStates))
				{
					return true;
				}
				flag = true;
				ExpeditionKitAction.Begin(localPlayer, piece);
				return false;
			}
			catch (Exception exception)
			{
				NearbyHudFailOpen.ReportOnce("expedition-kit click", exception);
				return !flag;
			}
		}
	}
	internal sealed class ExpeditionInventoryBackup
	{
		private readonly IdentityPreservingInventoryBackup<ItemData, ItemData> _items;

		internal Inventory Inventory { get; }

		internal int TotalUnits { get; }

		internal ExpeditionInventoryBackup(Inventory inventory, bool preserveItemIdentity)
		{
			Inventory = inventory ?? throw new ArgumentNullException("inventory");
			ItemData[] array = (from item in inventory.GetAllItems()
				orderby item.m_gridPos.y, item.m_gridPos.x
				select item).ToArray();
			_items = IdentityPreservingInventoryBackup<ItemData, ItemData>.Capture(preserveItemIdentity ? ((IEnumerable<ItemData>)array) : ((IEnumerable<ItemData>)array.Select((ItemData item) => item.Clone()).ToArray()), (ItemData item) => item.Clone());
			TotalUnits = array.Sum((ItemData item) => item.m_stack);
		}

		internal ExpeditionSnapshotRestoreTarget PrepareRestore()
		{
			return new ExpeditionSnapshotRestoreTarget(Inventory, _items.PrepareRestore((ItemData snapshot) => snapshot.Clone()), TotalUnits);
		}
	}
	internal sealed class ExpeditionSnapshotRestoreTarget
	{
		internal Inventory Inventory { get; }

		internal IdentityPreservingInventoryRestorePlan<ItemData, ItemData> Items { get; }

		internal int TotalUnits { get; }

		internal ExpeditionSnapshotRestoreTarget(Inventory inventory, IdentityPreservingInventoryRestorePlan<ItemData, ItemData> items, int totalUnits)
		{
			Inventory = inventory;
			Items = items;
			TotalUnits = totalUnits;
		}
	}
	internal sealed class CompletedExpeditionMove
	{
		internal RuntimeResourceStack Source { get; }

		internal ItemData SourceClone { get; }

		internal Vector2i SourcePosition { get; }

		internal Vector2i DestinationPosition { get; }

		internal string CompatibilityKey { get; }

		internal int Quantity { get; }

		internal ContainerReservation Reservation { get; }

		internal CompletedExpeditionMove(RuntimeResourceStack source, ItemData sourceClone, Vector2i sourcePosition, Vector2i destinationPosition, string compatibilityKey, int quantity, ContainerReservation reservation)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Source = source;
			SourceClone = sourceClone;
			SourcePosition = sourcePosition;
			DestinationPosition = destinationPosition;
			CompatibilityKey = compatibilityKey;
			Quantity = quantity;
			Reservation = reservation;
		}
	}
	internal static class ExpeditionKitAction
	{
		private const float OwnershipTimeoutSeconds = 2f;

		private static readonly ExpeditionKitWithdrawalPlanner WithdrawalPlanner = new ExpeditionKitWithdrawalPlanner();

		private static readonly ExpeditionCapacityPlanner CapacityPlanner = new ExpeditionCapacityPlanner();

		private static readonly MethodInfo AddItemAtMethod = AccessTools.DeclaredMethod(typeof(Inventory), "AddItem", new Type[5]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		}, (Type[])null);

		private static readonly FieldInfo InventoryItemsField = AccessTools.Field(typeof(Inventory), "m_inventory");

		private static readonly Queue<Tuple<Player, Piece>> PendingRequests = new Queue<Tuple<Player, Piece>>();

		private static bool _running;

		private static int _generation;

		internal static void Begin(Player player, Piece piece)
		{
			if (!((Object)(object)player == (Object)null) && !((Object)(object)piece == (Object)null))
			{
				PendingRequests.Enqueue(Tuple.Create<Player, Piece>(player, piece));
				StartNext();
			}
		}

		private static void StartNext()
		{
			if (_running || !RuntimeContext.Compatibility.IsCompatible)
			{
				return;
			}
			Tuple<Player, Piece> tuple = null;
			while (PendingRequests.Count > 0)
			{
				Tuple<Player, Piece> tuple2 = PendingRequests.Dequeue();
				if (tuple2.Item1 == Player.m_localPlayer)
				{
					tuple = tuple2;
					break;
				}
			}
			if (tuple == null)
			{
				return;
			}
			Player item = tuple.Item1;
			Piece item2 = tuple.Item2;
			int generation = _generation;
			_running = true;
			try
			{
				if (!TryPrepare(item, item2, out var _, out var capture, out var plan, out var handles, out var failure))
				{
					ShowFailure(failure);
					Complete(generation);
					return;
				}
				if (!TryPlanCapacity(item, capture, plan, out var _, out failure))
				{
					ShowFailure(failure);
					Complete(generation);
					return;
				}
				if (handles.Any((ContainerHandle handle) => OwnershipLeaseManager.HasPotentialAcquisition(handle.Id)))
				{
					ShowFailure("a previous ownership transition is still pending");
					Complete(generation);
					return;
				}
				ContainerHandle[] array = handles.Where((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner()).ToArray();
				if (array.Length == 0)
				{
					Finish(item, item2, plan, null);
					Complete(generation);
				}
				else
				{
					RuntimeContext.ShowTopLeft("Stackmaster: checking expedition-kit storage…");
					((MonoBehaviour)RuntimeContext.Plugin).StartCoroutine(FinishAfterOwnership(item, item2, plan, array, generation));
				}
			}
			catch (Exception ex)
			{
				Plugin plugin = RuntimeContext.Plugin;
				if (plugin != null)
				{
					plugin.Log.LogError((object)("Expedition-kit action stopped safely: " + ex));
				}
				RuntimeContext.ShowCenter("Stackmaster stopped safely; no expedition kit was added.");
				Complete(generation);
			}
		}

		private static void Complete(int generation)
		{
			if (generation == _generation)
			{
				_running = false;
				StartNext();
			}
		}

		internal static void Shutdown()
		{
			_generation++;
			PendingRequests.Clear();
			_running = false;
		}

		internal static void RearmSession()
		{
			PendingRequests.Clear();
			_running = false;
		}

		private static IEnumerator FinishAfterOwnership(Player player, Piece piece, ResourceWithdrawalPlan expectedPlan, ContainerHandle[] unownedHandles, int generation)
		{
			if (generation != _generation || !RuntimeContext.Compatibility.IsCompatible)
			{
				yield break;
			}
			OwnershipBatch ownership;
			try
			{
				ownership = OwnershipCoordinator.Begin(unownedHandles);
			}
			catch (Exception ex)
			{
				Plugin plugin = RuntimeContext.Plugin;
				if (plugin != null)
				{
					plugin.Log.LogError((object)("Expedition-kit ownership setup failed safely: " + ex));
				}
				RuntimeContext.ShowCenter("Stackmaster could not safely acquire the expedition kit; nothing was moved.");
				Complete(generation);
				yield break;
			}
			try
			{
				bool refreshFailed = false;
				float deadline = Time.realtimeSinceStartup + 2f;
				while (generation == _generation && player == Player.m_localPlayer && !ownership.IsComplete && Time.realtimeSinceStartup < deadline)
				{
					try
					{
						ownership.Refresh();
					}
					catch (Exception ex2)
					{
						refreshFailed = true;
						Plugin plugin2 = RuntimeContext.Plugin;
						if (plugin2 != null)
						{
							plugin2.Log.LogError((object)("Expedition-kit ownership refresh failed safely: " + ex2));
						}
						RuntimeContext.ShowCenter("Stackmaster could not safely acquire the expedition kit; nothing was moved.");
					}
					if (refreshFailed)
					{
						yield break;
					}
					yield return null;
				}
				try
				{
					if (generation != _generation || !RuntimeContext.Compatibility.IsCompatible || player != Player.m_localPlayer)
					{
						yield break;
					}
					ownership.Refresh();
					if (!ownership.IsComplete)
					{
						ownership.Timeout();
					}
					if (ownership.FailedContainerIds.Count > 0)
					{
						ShowFailure((ownership.OwnerRejectedContainerIds.Count > 0 && unownedHandles.Where((ContainerHandle handle) => ownership.OwnerRejectedContainerIds.Contains(handle.Id)).All((ContainerHandle handle) => (Object)(object)handle.NetworkView != (Object)null && handle.NetworkView.IsValid() && handle.NetworkView.HasOwner() && ContainerDiscovery.CheckAccess(player, handle.Container))) ? "The required materials are currently in use" : "required storage ownership could not be acquired");
					}
					else
					{
						Finish(player, piece, expectedPlan, ownership);
					}
				}
				catch (Exception ex3)
				{
					Plugin plugin3 = RuntimeContext.Plugin;
					if (plugin3 != null)
					{
						plugin3.Log.LogError((object)("Expedition-kit ownership failed safely: " + ex3));
					}
					RuntimeContext.ShowCenter("Stackmaster could not safely acquire the expedition kit; nothing was moved.");
				}
			}
			finally
			{
				if (generation == _generation)
				{
					try
					{
						if (!ownership.IsComplete)
						{
							ownership.Timeout();
						}
					}
					catch (Exception ex4)
					{
						Plugin plugin4 = RuntimeContext.Plugin;
						if (plugin4 != null)
						{
							plugin4.Log.LogError((object)("Expedition-kit ownership timeout cleanup failed: " + ex4));
						}
						DisableAfterFatalFailure("Stackmaster could not finish expedition-kit ownership timeout cleanup.");
					}
					try
					{
						OwnershipLeaseManager.ReleaseBatch(ownership, "expedition-kit action ended");
					}
					catch (Exception ex5)
					{
						Plugin plugin5 = RuntimeContext.Plugin;
						if (plugin5 != null)
						{
							plugin5.Log.LogError((object)("Expedition-kit ownership release failed safely: " + ex5));
						}
						DisableAfterFatalFailure("Stackmaster could not hand expedition-kit ownership to cleanup.");
					}
					finally
					{
						try
						{
							OwnershipCoordinator.End(ownership);
						}
						catch (Exception ex)
						{
							Plugin plugin6 = RuntimeContext.Plugin;
							if (plugin6 != null)
							{
								plugin6.Log.LogError((object)("Expedition-kit ownership teardown failed safely: " + ex));
							}
							DisableAfterFatalFailure("Stackmaster could not finish expedition-kit ownership teardown.");
						}
						finally
						{
							Complete(generation);
						}
					}
				}
			}
		}

		private static void Finish(Player player, Piece piece, ResourceWithdrawalPlan expectedPlan, OwnershipBatch ownership)
		{
			if (!TryPrepare(player, piece, out var requirements, out var capture, out var plan, out var handles, out var failure) || !ExpeditionKitWithdrawalPlanner.PlansAreIdentical(expectedPlan, plan))
			{
				ShowFailure(failure ?? "nearby materials changed before transfer");
				return;
			}
			if (handles.Any((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner()))
			{
				ShowFailure("required storage ownership changed before transfer");
				return;
			}
			if (!NearbyResourceService.RevalidateContainers(player, plan, capture, out failure))
			{
				ShowFailure(failure);
				return;
			}
			if (!TryPrepare(player, piece, out requirements, out capture, out plan, out handles, out failure) || !ExpeditionKitWithdrawalPlanner.PlansAreIdentical(expectedPlan, plan) || handles.Any((ContainerHandle handle) => !handle.NetworkView.IsOwner() || !handle.Container.IsOwner()))
			{
				ShowFailure(failure ?? "nearby materials changed during transfer preparation");
				return;
			}
			if (!NearbyResourceService.TryReserveContainers(player, capture.Scope, handles, releaseMatchingOwnershipOnFailure: false, out var reservations, out failure))
			{
				ShowFailure(failure);
				return;
			}
			try
			{
				if (!NearbyResourceService.RevalidateReservedContainers(player, capture.Scope, reservations, out failure) || !NearbyResourceService.RevalidateStacks(plan, capture, matchWorldLevel: true, out failure) || !TryPlanCapacity(player, capture, plan, out var plan2, out failure))
				{
					ShowFailure(failure);
					return;
				}
				if (!ExecuteAtomic(player, capture, plan, plan2, reservations, out failure))
				{
					ShowFailure(failure);
					return;
				}
				try
				{
					NearbyResourceService.ResetCaches();
					NearbyBuildHudPatch.ResetCache();
				}
				catch (Exception ex)
				{
					Plugin plugin = RuntimeContext.Plugin;
					if (plugin != null)
					{
						plugin.Log.LogError((object)("Expedition-kit cache refresh failed after commit: " + ex));
					}
				}
				try
				{
					RuntimeContext.ShowTopLeft("Stackmaster: expedition kit added (" + plan.RequiredUnits.ToString(CultureInfo.InvariantCulture) + " items).");
				}
				catch (Exception ex2)
				{
					Plugin plugin2 = RuntimeContext.Plugin;
					if (plugin2 != null)
					{
						plugin2.Log.LogError((object)("Expedition-kit success notification failed after commit: " + ex2));
					}
				}
			}
			finally
			{
				bool flag = false;
				try
				{
					flag = NearbyResourceService.ReleaseReservations(reservations, releaseMatchingOwnership: false);
				}
				catch (Exception ex3)
				{
					Plugin plugin3 = RuntimeContext.Plugin;
					if (plugin3 != null)
					{
						plugin3.Log.LogError((object)("Expedition-kit reservation cleanup threw: " + ex3));
					}
				}
				if (!flag)
				{
					DisableAfterFatalFailure("Stackmaster could not fully release an expedition-kit reservation.");
				}
			}
		}

		private static bool TryPrepare(Player player, Piece piece, out IReadOnlyList<ResourceRequirement> requirements, out NearbyResourceCapture capture, out ResourceWithdrawalPlan plan, out ContainerHandle[] handles, out string failure)
		{
			requirements = Array.Empty<ResourceRequirement>();
			capture = null;
			plan = null;
			handles = Array.Empty<ContainerHandle>();
			failure = null;
			if ((Object)(object)player == (Object)null || (Object)(object)piece == (Object)null)
			{
				failure = "invalid build piece";
				return false;
			}
			requirements = NearbyResourceService.PieceRequirements(piece);
			if (requirements.Count == 0)
			{
				failure = "this build piece has no material recipe";
				return false;
			}
			capture = NearbyResourceService.CaptureForExpedition(player, matchWorldLevel: true, fresh: true);
			plan = WithdrawalPlanner.Plan(requirements, capture.Stacks.Where((ResourceStack stack) => !string.Equals(stack.InventoryId, "player", StringComparison.Ordinal)));
			if (!plan.IsSatisfiable || plan.PlannedUnits != plan.RequiredUnits)
			{
				failure = "nearby storage does not contain one complete additional kit";
				return false;
			}
			if (!NearbyResourceService.TryResolveRequiredContainers(player, plan, capture, out handles, out failure))
			{
				return false;
			}
			return true;
		}

		private static bool TryPlanCapacity(Player player, NearbyResourceCapture capture, ResourceWithdrawalPlan withdrawal, out ExpeditionCapacityPlan plan, out string failure)
		{
			failure = null;
			CompatibilityCatalog compatibilityCatalog = new CompatibilityCatalog();
			Inventory inventory = ((Humanoid)player).GetInventory();
			InventorySnapshot player2 = InventorySnapshots.CaptureInventory("player", inventory, compatibilityCatalog);
			List<ExpeditionCargoStack> list = new List<ExpeditionCargoStack>(withdrawal.Steps.Count);
			foreach (ResourceWithdrawalStep step in withdrawal.Steps)
			{
				if (!capture.RuntimeStacks.TryGetValue(step.StackId, out var value) || value.Item == null || value.Item.m_shared == null)
				{
					plan = null;
					failure = "planned expedition material disappeared";
					return false;
				}
				list.Add(new ExpeditionCargoStack(step.StackId, compatibilityCatalog.KeyFor(value.Item), step.Quantity, value.Item.m_shared.m_maxStackSize, value.Item.GetWeight(step.Quantity)));
			}
			plan = CapacityPlanner.Plan(player2, list, inventory.GetTotalWeight(), player.GetMaxCarryWeight());
			if (!plan.FitsWeight)
			{
				failure = "the complete expedition kit is too heavy";
				return false;
			}
			if (!plan.FitsSlots)
			{
				failure = "the complete expedition kit does not fit in inventory";
				return false;
			}
			return true;
		}

		private static bool ExecuteAtomic(Player player, NearbyResourceCapture capture, ResourceWithdrawalPlan withdrawal, ExpeditionCapacityPlan capacity, IReadOnlyList<ContainerReservation> reservations, out string failure)
		{
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Unknown result type (might be due to invalid IL or missing references)
			//IL_0407: Unknown result type (might be due to invalid IL or missing references)
			failure = null;
			Inventory inventory = ((Humanoid)player).GetInventory();
			CompatibilityCatalog compatibilityCatalog = new CompatibilityCatalog();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				compatibilityCatalog.KeyFor(allItem);
			}
			foreach (ResourceWithdrawalStep step in withdrawal.Steps)
			{
				compatibilityCatalog.KeyFor(capture.RuntimeStacks[step.StackId].Item);
			}
			Dictionary<Container, ContainerReservation> dictionary = reservations.ToDictionary((ContainerReservation reservation) => reservation.Container);
			List<ExpeditionInventoryBackup> list = new List<ExpeditionInventoryBackup>
			{
				new ExpeditionInventoryBackup(inventory, preserveItemIdentity: true)
			};
			list.AddRange(reservations.Select((ContainerReservation reservation) => new ExpeditionInventoryBackup(reservation.Container.GetInventory(), preserveItemIdentity: false)));
			List<CompletedExpeditionMove> list2 = new List<CompletedExpeditionMove>();
			Dictionary<string, int> dictionary2 = new Dictionary<string, int>(StringComparer.Ordinal);
			try
			{
				foreach (ExpeditionDestinationStep step2 in capacity.Steps)
				{
					if (!capture.RuntimeStacks.TryGetValue(step2.SourceStackId, out var value) || value.Container == null)
					{
						failure = "planned expedition source disappeared";
						return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
					}
					if (!dictionary.TryGetValue(value.Container.Container, out var value2) || !NearbyResourceService.ReservationMatches(player, capture.Scope, value2))
					{
						failure = "required storage reservation changed before transfer";
						return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
					}
					int value3;
					int num = (dictionary2.TryGetValue(step2.SourceStackId, out value3) ? value3 : 0);
					ItemData itemAt = value.Inventory.GetItemAt(value.Item.m_gridPos.x, value.Item.m_gridPos.y);
					if (itemAt != value.Item || itemAt.m_stack != value.Item.m_stack || itemAt.m_stack < step2.Quantity || !string.Equals(compatibilityCatalog.KeyFor(itemAt), step2.CompatibilityKey, StringComparison.Ordinal))
					{
						failure = "planned expedition source stack changed before transfer";
						return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
					}
					Vector2i val = InventorySnapshots.PositionForSlot(inventory, step2.DestinationSlot);
					ItemData itemAt2 = inventory.GetItemAt(val.x, val.y);
					if ((itemAt2 == null && step2.ExpectedDestinationQuantity != 0) || (itemAt2 != null && (itemAt2.m_stack != step2.ExpectedDestinationQuantity || !string.Equals(compatibilityCatalog.KeyFor(itemAt2), step2.CompatibilityKey, StringComparison.Ordinal))))
					{
						failure = "player inventory changed before expedition transfer";
						return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
					}
					ItemData val2 = itemAt.Clone();
					val2.m_stack = step2.Quantity;
					Vector2i gridPos = itemAt.m_gridPos;
					int num2 = TotalUnits(value.Inventory);
					int num3 = TotalUnits(inventory);
					bool flag;
					try
					{
						flag = inventory.MoveItemToThis(value.Inventory, itemAt, step2.Quantity, val.x, val.y);
					}
					catch (Exception ex)
					{
						Plugin plugin = RuntimeContext.Plugin;
						if (plugin != null)
						{
							plugin.Log.LogError((object)("Expedition-kit transfer primitive threw: " + ex));
						}
						throw;
					}
					int num4 = TotalUnits(value.Inventory);
					int num5 = TotalUnits(inventory);
					ItemData itemAt3 = inventory.GetItemAt(val.x, val.y);
					if (num4 == num2 - step2.Quantity && num5 == num3 + step2.Quantity && itemAt3 != null && itemAt3.m_stack == step2.ExpectedDestinationQuantity + step2.Quantity && string.Equals(compatibilityCatalog.KeyFor(itemAt3), step2.CompatibilityKey, StringComparison.Ordinal))
					{
						list2.Add(new CompletedExpeditionMove(value, val2, gridPos, val, step2.CompatibilityKey, step2.Quantity, value2));
						dictionary2[step2.SourceStackId] = num + step2.Quantity;
						if (!value2.AdvanceDataRevisionAfterMutation() || !NearbyResourceService.ReservationMatches(player, capture.Scope, value2))
						{
							failure = "required storage reservation changed after transfer";
							return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
						}
						continue;
					}
					if (!flag && num4 == num2 && num5 == num3)
					{
						failure = "game transfer primitive declined the expedition move";
						return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
					}
					failure = "an unexpected expedition transfer result required full rollback";
					if (!RestoreBackups(list, reservations))
					{
						RuntimeContext.Disable("Expedition-kit rollback could not restore every inventory.");
						failure += "; rollback could not restore every item";
					}
					else
					{
						RuntimeContext.Disable("Unexpected expedition-kit transfer result; inventories were restored.");
						failure += "; inventories were restored and Stackmaster was disabled";
					}
					return false;
				}
				int requiredUnits = withdrawal.RequiredUnits;
				if (list2.Sum((CompletedExpeditionMove move) => move.Quantity) != requiredUnits)
				{
					failure = "expedition transfer did not complete the exact kit";
					return RollbackOrDisable(player, capture.Scope, reservations, list2, list, failure, out failure);
				}
				return true;
			}
			catch (Exception ex2)
			{
				Plugin plugin2 = RuntimeContext.Plugin;
				if (plugin2 != null)
				{
					plugin2.Log.LogError((object)("Expedition-kit transaction threw after mutation began: " + ex2));
				}
				bool flag2 = false;
				try
				{
					flag2 = RestoreBackups(list, reservations);
				}
				catch (Exception ex3)
				{
					Plugin plugin3 = RuntimeContext.Plugin;
					if (plugin3 != null)
					{
						plugin3.Log.LogError((object)("Expedition-kit emergency snapshot rollback threw: " + ex3));
					}
				}
				if (flag2)
				{
					failure = "an expedition transfer exception required full rollback; inventories were restored and Stackmaster was disabled";
					RuntimeContext.Disable("Expedition-kit transfer exception; inventories were restored.");
				}
				else
				{
					failure = "an expedition transfer exception required full rollback; rollback could not restore every item";
					RuntimeContext.Disable("Expedition-kit rollback could not restore every inventory.");
				}
				NearbyResourceService.ResetCaches();
				NearbyBuildHudPatch.ResetCache();
				return false;
			}
		}

		private static bool RollbackOrDisable(Player player, StorageScope scope, IReadOnlyList<ContainerReservation> reservations, IList<CompletedExpeditionMove> completed, IReadOnlyList<ExpeditionInventoryBackup> backups, string reason, out string failure)
		{
			bool flag = false;
			try
			{
				flag = RollbackCompleted(player, scope, completed);
			}
			catch (Exception ex)
			{
				Plugin plugin = RuntimeContext.Plugin;
				if (plugin != null)
				{
					plugin.Log.LogError((object)("Expedition-kit reverse rollback threw; restoring snapshots: " + ex));
				}
			}
			if (!flag)
			{
				try
				{
					flag = RestoreBackups(backups, reservations);
				}
				catch (Exception ex2)
				{
					Plugin plugin2 = RuntimeContext.Plugin;
					if (plugin2 != null)
					{
						plugin2.Log.LogError((object)("Expedition-kit snapshot rollback threw: " + ex2));
					}
				}
			}
			failure = (flag ? (reason + "; all transfers were rolled back") : (reason + "; rollback could not restore every item"));
			if (!flag)
			{
				RuntimeContext.Disable("Expedition-kit rollback could not restore every inventory.");
			}
			NearbyResourceService.ResetCaches();
			NearbyBuildHudPatch.ResetCache();
			return false;
		}

		private static bool RollbackCompleted(Player player, StorageScope scope, IEnumerable<CompletedExpeditionMove> completed)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).GetInventory();
			CompatibilityCatalog compatibilityCatalog = new CompatibilityCatalog();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				compatibilityCatalog.KeyFor(allItem);
			}
			foreach (CompletedExpeditionMove item in completed)
			{
				compatibilityCatalog.KeyFor(item.SourceClone);
			}
			foreach (CompletedExpeditionMove item2 in completed.Reverse())
			{
				if (!NearbyResourceService.ReservationMatches(player, scope, item2.Reservation))
				{
					return false;
				}
				ItemData itemAt = inventory.GetItemAt(item2.DestinationPosition.x, item2.DestinationPosition.y);
				if (itemAt == null || itemAt.m_stack < item2.Quantity || !string.Equals(compatibilityCatalog.KeyFor(itemAt), item2.CompatibilityKey, StringComparison.Ordinal))
				{
					return false;
				}
				int num = TotalUnits(item2.Source.Inventory);
				item2.SourceClone.m_stack = item2.Quantity;
				item2.SourceClone.m_gridPos = item2.SourcePosition;
				if (!(AddItemAtMethod != null) || !(bool)AddItemAtMethod.Invoke(item2.Source.Inventory, new object[5]
				{
					item2.SourceClone,
					item2.Quantity,
					item2.SourcePosition.x,
					item2.SourcePosition.y,
					true
				}) || TotalUnits(item2.Source.Inventory) != num + item2.Quantity)
				{
					return false;
				}
				int num2 = TotalUnits(inventory);
				if (!inventory.RemoveItem(itemAt, item2.Quantity) || TotalUnits(inventory) != num2 - item2.Quantity || !item2.Reservation.AdvanceDataRevisionAfterMutation() || !NearbyResourceService.ReservationMatches(player, scope, item2.Reservation))
				{
					return false;
				}
			}
			return true;
		}

		private static bool RestoreBackups(IEnumerable<ExpeditionInventoryBackup> backups, IEnumerable<ContainerReservation> reservations)
		{
			if (InventoryItemsField == null)
			{
				return false;
			}
			ExpeditionSnapshotRestoreTarget[] array;
			List<ItemData>[] array2;
			try
			{
				array = backups.Select((ExpeditionInventoryBackup backup) => backup.PrepareRestore()).ToArray();
				array2 = array.Select((ExpeditionSnapshotRestoreTarget target) => target.Items.RebuildInventoryList()).ToArray();
				if (array2.Any((List<ItemData> items) => items.Any((ItemData item) => item == null)))
				{
					return false;
				}
			}
			catch (Exception ex)
			{
				Plugin plugin = RuntimeContext.Plugin;
				if (plugin != null)
				{
					plugin.Log.LogError((object)("Expedition-kit snapshot preparation failed: " + ex));
				}
				return false;
			}
			ExpeditionSnapshotRestoreTarget[] array3;
			try
			{
				array3 = array;
				foreach (ExpeditionSnapshotRestoreTarget expeditionSnapshotRestoreTarget in array3)
				{
					object value = InventoryItemsField.GetValue(expeditionSnapshotRestoreTarget.Inventory);
					InventoryItemsField.SetValue(expeditionSnapshotRestoreTarget.Inventory, value);
					if (value == null || InventoryItemsField.GetValue(expeditionSnapshotRestoreTarget.Inventory) != value)
					{
						return false;
					}
				}
			}
			catch (Exception ex2)
			{
				Plugin plugin2 = RuntimeContext.Plugin;
				if (plugin2 != null)
				{
					plugin2.Log.LogError((object)("Expedition-kit snapshot field preflight failed: " + ex2));
				}
				return false;
			}
			bool flag = true;
			for (int num2 = 0; num2 < array.Length; num2++)
			{
				ExpeditionSnapshotRestoreTarget expeditionSnapshotRestoreTarget2 = array[num2];
				try
				{
					expeditionSnapshotRestoreTarget2.Items.RestoreStates(RestoreItemDataState);
					InventoryItemsField.SetValue(expeditionSnapshotRestoreTarget2.Inventory, array2[num2]);
				}
				catch (Exception ex3)
				{
					flag = false;
					Plugin plugin3 = RuntimeContext.Plugin;
					if (plugin3 != null)
					{
						plugin3.Log.LogError((object)("Expedition-kit identity-preserving restore failed: " + ex3));
					}
				}
			}
			array3 = array;
			foreach (ExpeditionSnapshotRestoreTarget expeditionSnapshotRestoreTarget3 in array3)
			{
				try
				{
					flag &= expeditionSnapshotRestoreTarget3.Items.HasExactOriginalReferences(expeditionSnapshotRestoreTarget3.Inventory.GetAllItems()) && expeditionSnapshotRestoreTarget3.Items.StatesMatch(ItemDataStateMatches) && TotalUnits(expeditionSnapshotRestoreTarget3.Inventory) == expeditionSnapshotRestoreTarget3.TotalUnits;
					expeditionSnapshotRestoreTarget3.Inventory.m_onChanged?.Invoke();
				}
				catch (Exception ex4)
				{
					flag = false;
					Plugin plugin4 = RuntimeContext.Plugin;
					if (plugin4 != null)
					{
						plugin4.Log.LogError((object)("Expedition-kit restored-inventory notification failed: " + ex4));
					}
				}
			}
			foreach (ContainerReservation reservation in reservations)
			{
				try
				{
					flag &= reservation.AdvanceDataRevisionAfterMutation();
				}
				catch (Exception ex5)
				{
					flag = false;
					Plugin plugin5 = RuntimeContext.Plugin;
					if (plugin5 != null)
					{
						plugin5.Log.LogError((object)("Expedition-kit rollback revision update failed: " + ex5));
					}
				}
			}
			return flag;
		}

		private static void RestoreItemDataState(ItemData original, ItemData snapshot)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			original.m_shared = snapshot.m_shared;
			original.m_stack = snapshot.m_stack;
			original.m_durability = snapshot.m_durability;
			original.m_equipped = snapshot.m_equipped;
			original.m_quality = snapshot.m_quality;
			original.m_variant = snapshot.m_variant;
			original.m_crafterID = snapshot.m_crafterID;
			original.m_crafterName = snapshot.m_crafterName;
			original.m_worldLevel = snapshot.m_worldLevel;
			original.m_pickedUp = snapshot.m_pickedUp;
			original.m_cheated = snapshot.m_cheated;
			original.m_gridPos = snapshot.m_gridPos;
			original.m_dropPrefab = snapshot.m_dropPrefab;
			original.m_lastAttackTime = snapshot.m_lastAttackTime;
			original.m_lastProjectile = snapshot.m_lastProjectile;
			original.m_customData = snapshot.m_customData;
		}

		private static bool ItemDataStateMatches(ItemData original, ItemData snapshot)
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			if (original.m_shared != snapshot.m_shared || original.m_stack != snapshot.m_stack || !original.m_durability.Equals(snapshot.m_durability) || original.m_equipped != snapshot.m_equipped || original.m_quality != snapshot.m_quality || original.m_variant != snapshot.m_variant || original.m_crafterID != snapshot.m_crafterID || !string.Equals(original.m_crafterName, snapshot.m_crafterName, StringComparison.Ordinal) || original.m_worldLevel != snapshot.m_worldLevel || original.m_pickedUp != snapshot.m_pickedUp || original.m_cheated != snapshot.m_cheated || !((Vector2i)(ref original.m_gridPos)).Equals(snapshot.m_gridPos) || original.m_dropPrefab != snapshot.m_dropPrefab || !original.m_lastAttackTime.Equals(snapshot.m_lastAttackTime) || original.m_lastProjectile != snapshot.m_lastProjectile)
			{
				return false;
			}
			if (original.m_customData == null || snapshot.m_customData == null)
			{
				if (original.m_customData == null)
				{
					return snapshot.m_customData == null;
				}
				return false;
			}
			string value;
			if (original.m_customData.Count == snapshot.m_customData.Count)
			{
				return snapshot.m_customData.All((KeyValuePair<string, string> pair) => original.m_customData.TryGetValue(pair.Key, out value) && string.Equals(value, pair.Value, StringComparison.Ordinal));
			}
			return false;
		}

		private static int TotalUnits(Inventory inventory)
		{
			return inventory.GetAllItems().Sum((ItemData item) => item.m_stack);
		}

		private static void DisableAfterFatalFailure(string reason)
		{
			try
			{
				RuntimeContext.Disable(reason);
			}
			catch (Exception ex)
			{
				Plugin plugin = RuntimeContext.Plugin;
				if (plugin != null)
				{
					plugin.Log.LogError((object)("Stackmaster fatal-disable fallback failed: " + ex));
				}
			}
		}

		private static void ShowFailure(string failure)
		{
			string text = (string.IsNullOrWhiteSpace(failure) ? "the complete kit could not be transferred" : failure);
			Plugin plugin = RuntimeContext.Plugin;
			if (plugin != null)
			{
				plugin.Log.LogWarning((object)("Expedition-kit action rejected safely: " + text + "."));
			}
			RuntimeContext.ShowCenter("Stackmaster: " + text + ". Nothing was moved.");
		}
	}
	internal static class HarmonyTargetManifest
	{
		private static readonly IReadOnlyList<HarmonyTargetDescriptor> Targets = new HarmonyTargetDescriptor[32]
		{
			Postfix(typeof(InventoryGui), "Awake", isStatic: false, typeof(void), Type.EmptyTypes, typeof(InventoryGuiAwakePatch)),
			Both(typeof(InventoryGui), "Hide", isStatic: false, typeof(void), Type.EmptyTypes, typeof(InventoryGuiHidePatch)),
			Postfix(typeof(InventoryGui), "Show", isStatic: false, typeof(void), new Type[2]
			{
				typeof(Container),
				typeof(int)
			}, typeof(InventoryGuiShowPatch)),
			Both(typeof(InventoryGui), "Update", isStatic: false, typeof(void), Type.EmptyTypes, typeof(InventoryGuiStorageActionPatch)),
			Transactional(typeof(InventoryGui), "OnCraftPressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingStartPatch)),
			Prefix(typeof(InventoryGui), "OnCraftCancelPressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingCancelPatch)),
			Prefix(typeof(InventoryGui), "OnTabCraftPressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingSelectionPatch)),
			Prefix(typeof(InventoryGui), "OnTabUpgradePressed", isStatic: false, typeof(void), Type.EmptyTypes, typeof(CraftingSelectionPatch)),
			Prefix(typeof(InventoryGui), "OnSelectedRecipe", isStatic: false, typeof(void), new Type[1] { typeof(GameObject) }, typeof(CraftingSelectionPatch)),
			Postfix(typeof(InventoryGrid), "UpdateInventory", isStatic: false, typeof(void), new Type[3]
			{
				typeof(Inventory),
				typeof(Player),
				typeof(ItemData)
			}, typeof(InventoryGridUpdateInventoryPatch)),
			Prefix(typeof(InventoryGui), "OnSelectedItem", isStatic: false, typeof(void), new Type[4]
			{
				typeof(InventoryGrid),
				typeof(ItemData),
				typeof(Vector2i),
				typeof(Modifier)
			}, typeof(InventoryProtectionClickPatch)),
			Prefix(typeof(InventoryGui), "OnRightClickItem", isStatic: false, typeof(void), new Type[3]
			{
				typeof(InventoryGrid),
				typeof(ItemData),
				typeof(Vector2i)
			}, typeof(InventoryProtectionRightClickPatch)),
			Both(typeof(InventoryGui), "OnSelectedItem", isStatic: false, typeof(void), new Type[4]
			{
				typeof(InventoryGrid),
				typeof(ItemData),
				typeof(Vector2i),
				typeof(Modifier)
			}, typeof(ManualProtectionExitSelectionPatch)),
			Both(typeof(InventoryGui), "OnDropOutside", isStatic: false, typeof(void), Type.EmptyTypes, typeof(ManualProtectionExitDropOutsidePatch)),
			Prefix(typeof(Container), "Interact", isStatic: false, typeof(bool), new Type[3]
			{
				typeof(Humanoid),
				typeof(bool),
				typeof(bool)
			}, typeof(ContainerInteractPatch)),
			Postfix(typeof(Container), "GetHoverText", isStatic: false, typeof(string), Type.EmptyTypes, typeof(ContainerHoverTextPatch)),
			Postfix(typeof(Container), "RPC_RequestOpen", isStatic: false, typeof(void), new Type[2]
			{
				typeof(long),
				typeof(long)
			}, typeof(ContainerOpenRequestLeasePatch)),
			Prefix(typeof(Container), "RPC_StackResponse", isStatic: false, typeof(void), new Type[2]
			{
				typeof(long),
				typeof(bool)
			}, typeof(ContainerStackResponsePatch), preserveDuringCleanup: true),
			Prefix(typeof(Game), "Shutdown", isStatic: false, typeof(void), new Type[1] { typeof(bool) }, typeof(OwnershipLifecyclePatch)),
			Prefix(typeof(ZNet), "Shutdown", isStatic: false, typeof(void), new Type[1] { typeof(bool) }, typeof(OwnershipLifecyclePatch)),
			Prefix(typeof(ZNet), "ShutdownWithoutSave", isStatic: false, typeof(void), new Type[1] { typeof(bool) }, typeof(OwnershipLifecyclePatch)),
			Postfix(typeof(ZNet), "Update", isStatic: false, typeof(void), Type.EmptyTypes, typeof(OwnershipSafetyUpdatePatch), "Postfix", preserveDuringCleanup: true),
			Postfix(typeof(Player), "HaveRequirementItems", isStatic: false, typeof(bool), new Type[4]
			{
				typeof(Recipe),
				typeof(bool),
				typeof(int),
				typeof(int)
			}, typeof(NearbyRequirementPatches), "RecipePostfix"),
			Postfix(typeof(Player), "HaveRequirements", isStatic: false, typeof(bool), new Type[2]
			{
				typeof(Piece),
				typeof(RequirementMode)
			}, typeof(NearbyRequirementPatches), "PiecePostfix"),
			Postfix(typeof(Hud), "SetupPieceInfo", isStatic: false, typeof(void), new Type[1] { typeof(Piece) }, typeof(NearbyBuildHudPatch)),
			Prefix(typeof(BuildUi), "OnSelectPiece", isStatic: false, typeof(void), new Type[1] { typeof(Piece) }, typeof(ExpeditionKitClickPatch)),
			Both(typeof(InventoryGui), "SetupRequirement", isStatic: true, typeof(bool), new Type[6]
			{
				typeof(Transform),
				typeof(Requirement),
				typeof(Player),
				typeof(bool),
				typeof(int),
				typeof(int)
			}, typeof(NearbyCraftingHudPatch)),
			Postfix(typeof(Player), "GetFirstRequiredItem", isStatic: false, typeof(ItemData), new Type[6]
			{
				typeof(Inventory),
				typeof(Recipe),
				typeof(int),
				typeof(int).MakeByRefType(),
				typeof(int).MakeByRefType(),
				typeof(int)
			}, typeof(NearbyFirstRequiredItemPatch)),
			Transactional(typeof(InventoryGui), "DoCrafting", isStatic: false, typeof(void), new Type[1] { typeof(Player) }, typeof(NearbyCraftingActionPatch)),
			Transactional(typeof(Player), "UpdatePlacement", isStatic: false, typeof(void), new Type[2]
			{
				typeof(bool),
				typeof(float)
			}, typeof(NearbyBuildingActionPatch)),
			Both(typeof(Player), "TryPlacePiece", isStatic: false, typeof(bool), new Type[1] { typeof(Piece) }, typeof(NearbyTryPlacePiecePatch)),
			Prefix(typeof(Inventory), "RemoveItem", isStatic: false, typeof(void), new Type[4]
			{
				typeof(string),
				typeof(int),
				typeof(int),
				typeof(bool)
			}, typeof(NearbyResourceRemovalPatch))
		};

		internal static IReadOnlyList<HarmonyTargetDescriptor> Descriptors => Targets;

		internal static IReadOnlyList<PatchInstaller.PatchSpec> ResolveAll()
		{
			return Resolve(Targets);
		}

		internal static IReadOnlyList<PatchInstaller.PatchSpec> ResolveCleanupSafety()
		{
			return Resolve(Targets.Where((HarmonyTargetDescriptor target) => target.PreserveDuringCleanup).ToArray());
		}

		private static IReadOnlyList<PatchInstaller.PatchSpec> Resolve(IReadOnlyList<HarmonyTargetDescriptor> targets)
		{
			List<PatchInstaller.PatchSpec> list = new List<PatchInstaller.PatchSpec>(targets.Count);
			foreach (HarmonyTargetDescriptor target in targets)
			{
				list.Add(target.Resolve());
			}
			return list;
		}

		internal static void Validate(ICollection<string> failures)
		{
			try
			{
				ResolveAll();
			}
			catch (Exception ex)
			{
				failures.Add("Harmony target manifest failed: " + ex.Message);
			}
		}

		private static HarmonyTargetDescriptor Prefix(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType, bool preserveDuringCleanup = false)
		{
			return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, "Prefix", null, null, preserveDuringCleanup);
		}

		private static HarmonyTargetDescriptor Postfix(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType, string patchName = "Postfix", bool preserveDuringCleanup = false)
		{
			return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, null, patchName, null, preserveDuringCleanup);
		}

		private static HarmonyTargetDescriptor Both(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType)
		{
			return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, "Prefix", "Postfix", null);
		}

		private static HarmonyTargetDescriptor Transactional(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType)
		{
			return new HarmonyTargetDescriptor(targetType, targetName, isStatic, returnType, parameters, patchType, "Prefix", "Postfix", "Finalizer");
		}
	}
	internal sealed class HarmonyTargetDescriptor
	{
		internal Type TargetType { get; }

		internal string TargetName { get; }

		internal bool IsStatic { get; }

		internal Type ReturnType { get; }

		internal Type[] Parameters { get; }

		internal Type PatchType { get; }

		internal string PrefixName { get; }

		internal string PostfixName { get; }

		internal string FinalizerName { get; }

		internal bool PreserveDuringCleanup { get; }

		internal string Identity => TargetType.FullName + "." + TargetName + "(" + string.Join(",", Parameters.Select((Type type) => type.FullName).ToArray()) + ")";

		internal HarmonyTargetDescriptor(Type targetType, string targetName, bool isStatic, Type returnType, Type[] parameters, Type patchType, string prefixName, string postfixName, string finalizerName, bool preserveDuringCleanup = false)
		{
			TargetType = targetType;
			TargetName = targetName;
			IsStatic = isStatic;
			ReturnType = returnType;
			Parameters = parameters;
			PatchType = patchType;
			PrefixName = prefixName;
			PostfixName = postfixName;
			FinalizerName = finalizerName;
			PreserveDuringCleanup = preserveDuringCleanup;
		}

		internal PatchInstaller.PatchSpec Resolve()
		{
			MethodInfo original = RuntimeContractValidator.ResolveExactMethod(TargetType, TargetName, IsStatic, declaredOnly: true, ReturnType, Parameters);
			MethodInfo prefix = ResolvePatch(PrefixName);
			MethodInfo postfix = ResolvePatch(PostfixName);
			MethodInfo finalizer = ResolvePatch(FinalizerName);
			HarmonyPatchCompatibility.Validate(original, prefix, postfix, finalizer);
			return new PatchInstaller.PatchSpec(original, prefix, postfix, finalizer);
		}

		private MethodInfo ResolvePatch(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return RuntimeContractValidator.ResolveUniqueNamedMethod(PatchType, name, mustBeStatic: true);
			}
			return null;
		}
	}
	internal static class HarmonyPatchCompatibility
	{
		internal static void Validate(MethodInfo original, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer)
		{
			ValidatePatch(original, prefix, "prefix");
			ValidatePatch(original, postfix, "postfix");
			ValidatePatch(original, finalizer, "finalizer");
			ValidateReturns(prefix, postfix, finalizer);
			ValidateState(prefix, postfix, finalizer);
		}

		private static void ValidateReturns(MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer)
		{
			if (prefix != null && prefix.ReturnType != typeof(void) && prefix.ReturnType != typeof(bool))
			{
				throw new InvalidOperationException(prefix.DeclaringType.Name + "." + prefix.Name + " has an invalid Harmony prefix return type");
			}
			if (postfix != null && postfix.ReturnType != typeof(void))
			{
				throw new InvalidOperationException(postfix.DeclaringType.Name + "." + postfix.Name + " has an invalid Harmony postfix return type");
			}
			if (finalizer != null && finalizer.ReturnType != typeof(void) && finalizer.ReturnType != typeof(Exception))
			{
				throw new InvalidOperationException(finalizer.DeclaringType.Name + "." + finalizer.Name + " has an invalid Harmony finalizer return type");
			}
		}

		private static void ValidateState(MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer)
		{
			ParameterInfo parameterInfo = prefix?.GetParameters().FirstOrDefault((ParameterInfo parameter) => parameter.Name == "__state");
			ParameterInfo[] array = (from parameter in new MethodInfo[2] { postfix, finalizer }.Where((MethodInfo value) => value != null).SelectMany((MethodInfo value) => value.GetParameters())
				where parameter.Name == "__state"
				select parameter).ToArray();
			if (parameterInfo == null)
			{
				if (array.Length != 0)
				{
					throw new InvalidOperationException("Harmony __state is consumed without a prefix producer");
				}
				return;
			}
			if (!parameterInfo.ParameterType.IsByRef)
			{
				throw new InvalidOperationException(prefix.DeclaringType.Name + "." + prefix.Name + " must produce Harmony __state by ref or out");
			}
			Type type = ElementType(parameterInfo.ParameterType);
			ParameterInfo[] array2 = array;
			foreach (ParameterInfo parameterInfo2 in array2)
			{
				if (ElementType(parameterInfo2.ParameterType) != type)
				{
					throw new InvalidOperationException("Harmony __state types disagree for " + parameterInfo2.Member.DeclaringType.Name);
				}
			}
		}

		private static void ValidatePatch(MethodInfo original, MethodInfo patch, string role)
		{
			if (patch == null)
			{
				return;
			}
			if (!patch.IsStatic)
			{
				throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " " + role + " must be static");
			}
			ParameterInfo[] parameters = original.GetParameters();
			ParameterInfo[] parameters2 = patch.GetParameters();
			foreach (ParameterInfo parameterInfo in parameters2)
			{
				string name = parameterInfo.Name ?? string.Empty;
				if (name == "__instance")
				{
					if (original.IsStatic || ElementType(parameterInfo.ParameterType) != original.DeclaringType)
					{
						throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __instance");
					}
				}
				else if (name == "__result")
				{
					if (original.ReturnType == typeof(void) || ElementType(parameterInfo.ParameterType) != original.ReturnType)
					{
						throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __result");
					}
				}
				else
				{
					if (name == "__state")
					{
						continue;
					}
					if (name == "__runOriginal")
					{
						if (ElementType(parameterInfo.ParameterType) != typeof(bool))
						{
							throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __runOriginal");
						}
						continue;
					}
					if (name == "__exception")
					{
						if (role != "finalizer" || ElementType(parameterInfo.ParameterType) != typeof(Exception))
						{
							throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible __exception");
						}
						continue;
					}
					if (name.StartsWith("___", StringComparison.Ordinal))
					{
						if (original.IsStatic)
						{
							throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " cannot inject an instance field for a static original");
						}
						string fieldName = name.Substring(3);
						FieldInfo[] array = (from field in original.DeclaringType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
							where field.Name == fieldName
							select field).ToArray();
						if (array.Length != 1 || ElementType(parameterInfo.ParameterType) != array[0].FieldType)
						{
							throw new InvalidOperationException(patch.DeclaringType.Name + "." + patch.Name + " has incompatible field injection " + name);
						}
						continue;
					}
					HarmonyArgument[] array2 = parameterInfo.GetCustomAttributes(typeof(HarmonyArgument), inherit: false).Cast<HarmonyAr