Decompiled source of AzuCraftyBoxesBuildingOnly v1.6.4

BepInEx/plugins/AzuCraftyBoxesBuildingOnly/AzuCraftyBoxesBuildingOnly.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using MonoMod.Cil;
using MonoMod.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("AzuCraftyBoxes Fixes & Config")]
[assembly: AssemblyProduct("AzuCraftyBoxes Fixes & Config")]
[assembly: AssemblyFileVersion("1.6.3.0")]
[assembly: AssemblyVersion("1.6.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AzuCraftyBoxesBuildingOnly
{
	[BepInPlugin("local.valheim.azucraftyboxesbuildingonly", "AzuCraftyBoxes Fixes & Config", "1.6.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("local.valheim.compatibilityfixes")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private enum LegacyMode
		{
			Full,
			BuildingOnly,
			Disabled
		}

		private static class AzuShouldPreventPatch
		{
			public static void Postfix(ref bool __result)
			{
				if (IsReady && IsReady && ModEnabled.Value && !AzuCallContext.InInfrastructure && !FeaturePolicy.Allowed(AzuCallContext.InCrafting, AzuCallContext.InBuilding || PlacementConsumptionMarker.Active, AzuCallContext.InMachine, CraftTransaction.Active, CraftTransaction.AllowNearbyContainers, BuildingFromContainers.Value, CraftingFromContainers.Value, MachinesFromContainers.Value))
				{
					__result = true;
				}
			}
		}

		public const string PluginGuid = "local.valheim.azucraftyboxesbuildingonly";

		public const string PluginName = "AzuCraftyBoxes Fixes & Config";

		public const string PluginVersion = "1.6.3";

		public const string AzuCraftyBoxesGuid = "Azumatt.AzuCraftyBoxes";

		public const string CombinedPluginGuid = "local.valheim.compatibilityfixes";

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<bool> BuildingFromContainers;

		internal static ConfigEntry<bool> CraftingFromContainers;

		internal static ConfigEntry<bool> MachinesFromContainers;

		internal static ConfigEntry<KeyboardShortcut> ToggleBuilding;

		internal static ConfigEntry<KeyboardShortcut> ToggleCrafting;

		internal static ConfigEntry<KeyboardShortcut> ToggleMachines;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private PatchSession _patches;

		private StartupSequence _startup;

		private static StartupSequence _currentStartup;

		private bool _versionRegistrationStarted;

		private FileSystemWatcher _configWatcher;

		private readonly object _configReloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private const long ConfigReloadDelayTicks = 5000000L;

		internal static bool IsReady
		{
			get
			{
				if (_currentStartup != null)
				{
					return _currentStartup.Ready;
				}
				return false;
			}
		}

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			_startup = new StartupSequence(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)message);
			});
			_currentStartup = _startup;
			Assembly azu = null;
			if (_startup.Run(delegate
			{
				azu = BaseModHealth.RequireInitialized();
			}, delegate
			{
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: Expected O, but got Unknown
				_startup.Step("binding local configuration", BindConfiguration);
				_startup.Step("validating game contracts", RuntimeContract.Validate);
				List<Tuple<MethodBase, bool>> targets = AzuGameApiAdapter.Prepare(azu, _startup);
				_startup.Step("initializing the Azu runtime bridge", delegate
				{
					AzuRuntimeBridge.Initialize(((BaseUnityPlugin)this).Logger);
				});
				_harmony = new Harmony("local.valheim.azucraftyboxesbuildingonly");
				_patches = new PatchSession(_harmony, _startup, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)message);
				});
				AzuGameApiAdapter.Install(_patches, targets);
				foreach (Type item in IntegrationPatchTypes(azu.GetName()))
				{
					_patches.ApplyClass(item);
				}
				InstallAzuCraftyBoxesGate();
				_startup.Step("starting the config watcher", SetupConfigWatcher);
				_startup.Step("registering multiplayer version checks", delegate
				{
					_versionRegistrationStarted = true;
					AzuServerSyncBridge.Initialize(((BaseUnityPlugin)this).Logger);
				});
			}, () => Cleanup(startupFailure: true)))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded successfully: AzuCraftyBoxes Fixes & Config 1.6.3. All required initialization and " + _patches.InstalledCount + " patch installations completed. Base profile=" + BaseModCompatibility.Resolve(azu.GetName()).ToString() + ". Per-player settings: building=" + BuildingFromContainers.Value + ", crafting=" + CraftingFromContainers.Value + ", machines=" + MachinesFromContainers.Value + "."));
			}
		}

		internal static IEnumerable<Type> IntegrationPatchTypes(AssemblyName baseAssembly)
		{
			bool replaceFermenter = BaseModCompatibility.RequiresFermenterReplacement(baseAssembly);
			Type[] array = new Type[18]
			{
				typeof(MachineItemRemovalMetadataPatch),
				typeof(MachineSingleItemMetadataPatch),
				typeof(CookingMetadataPatch),
				typeof(FermenterTransferCompatibilityPatch),
				typeof(BuildingOperationContextPatch),
				typeof(PlacementConsumptionMarkerPatch),
				typeof(BuildingConsumptionSafetyPatch),
				typeof(UpgraderRequirementFilterPatch),
				typeof(UpgraderRecipeFilterPatch),
				typeof(UpgraderRecipeAmountFilterPatch),
				typeof(CraftingOperationContextPatch),
				typeof(CraftTransactionStartPatch),
				typeof(CraftTransactionFinishPatch),
				typeof(CraftTransactionCancelPatch),
				typeof(AzuCraftingStateCleanupPatch),
				typeof(MachineOperationContextPatch),
				typeof(AzuInfrastructureContextPatch),
				typeof(ContainerCheatedResourcesPatch)
			};
			foreach (Type type in array)
			{
				if (!(type == typeof(FermenterTransferCompatibilityPatch)) || replaceFermenter)
				{
					yield return type;
				}
			}
		}

		private void BindConfiguration()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Mod Enabled", true, "Master switch for this add-on on this player. When off, this add-on stops filtering AzuCraftyBoxes and AzuCraftyBoxes behaves normally. Local player only; not synchronized.");
			BuildingFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Per-Player Features", "Building From Nearby Chests", true, "Allow this player to count and consume nearby-container materials while placing building pieces. Changes take effect immediately. Local player only; not synchronized.");
			CraftingFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Per-Player Features", "Crafting From Nearby Chests", false, "Allow this player to count and consume nearby-container materials for crafting, upgrading, and supported crafting-like interfaces. Changes take effect immediately. Local player only; not synchronized.");
			MachinesFromContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Per-Player Features", "Machines From Nearby Chests", false, "Allow this player to fill smelters, cooking stations, fires, fermenters, shield generators, turrets, and other AzuCraftyBoxes-supported machines from nearby containers. Changes take effect immediately. Local player only; not synchronized.");
			ToggleBuilding = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3 - Per-Player Hotkeys", "Toggle Building", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Toggle Building From Nearby Chests for this player. Rebind freely or set to None to disable the hotkey. Local player only; not synchronized.");
			ToggleCrafting = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3 - Per-Player Hotkeys", "Toggle Crafting", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Toggle Crafting From Nearby Chests for this player. Rebind freely or set to None to disable the hotkey. Local player only; not synchronized.");
			ToggleMachines = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("3 - Per-Player Hotkeys", "Toggle Machines", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Toggle Machines From Nearby Chests for this player. Rebind freely or set to None to disable the hotkey. Local player only; not synchronized.");
			MigrateLegacyModeConfig();
		}

		private void Update()
		{
			if (IsReady)
			{
				UpdatePlayerInput();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void UpdatePlayerInput()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !Console.IsVisible() && !TextInput.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()))
			{
				HandleToggle(ToggleBuilding, BuildingFromContainers, "Building from nearby chests");
				HandleToggle(ToggleCrafting, CraftingFromContainers, "Crafting from nearby chests");
				HandleToggle(ToggleMachines, MachinesFromContainers, "Machines from nearby chests");
			}
		}

		private static void HandleToggle(ConfigEntry<KeyboardShortcut> shortcut, ConfigEntry<bool> setting, string label)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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)
			if (shortcut == null || setting == null)
			{
				return;
			}
			KeyboardShortcut value = shortcut.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey != 0)
			{
				value = shortcut.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					setting.Value = !setting.Value;
					ShowFeatureMessage(label + ": " + (setting.Value ? "ON" : "OFF"));
				}
			}
		}

		private void SetupConfigWatcher()
		{
			_configWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
			_configWatcher.Changed += ReloadConfig;
			_configWatcher.Created += ReloadConfig;
			_configWatcher.Renamed += ReloadConfig;
			_configWatcher.IncludeSubdirectories = false;
			_configWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_configWatcher.EnableRaisingEvents = true;
		}

		private void ReloadConfig(object sender, FileSystemEventArgs args)
		{
			if (!IsReady)
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow.Ticks - _lastConfigReloadTime.Ticks < 5000000)
			{
				return;
			}
			_lastConfigReloadTime = utcNow;
			lock (_configReloadLock)
			{
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Reloaded local per-player configuration: building=" + BuildingFromContainers.Value + ", crafting=" + CraftingFromContainers.Value + ", machines=" + MachinesFromContainers.Value + "."));
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Failed to reload configuration: " + ex.Message));
				}
			}
		}

		private void MigrateLegacyModeConfig()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			try
			{
				if (!(typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(((BaseUnityPlugin)this).Config, null) is IDictionary<ConfigDefinition, string> dictionary))
				{
					return;
				}
				ConfigDefinition key = new ConfigDefinition("2 - AzuCraftyBoxes", "Mode");
				bool flag = false;
				if (dictionary.TryGetValue(key, out var value) && Enum.TryParse<LegacyMode>(value.Trim(), ignoreCase: true, out var result))
				{
					bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
					((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
					try
					{
						BuildingFromContainers.Value = result != LegacyMode.Disabled;
						CraftingFromContainers.Value = result == LegacyMode.Full;
						MachinesFromContainers.Value = result == LegacyMode.Full;
					}
					finally
					{
						((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
					}
					dictionary.Remove(key);
					flag = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Migrated legacy shared Mode=" + result.ToString() + " to the new local per-player feature toggles."));
				}
				flag |= dictionary.Remove(new ConfigDefinition("1 - General", "Lock Configuration"));
				if (flag | dictionary.Remove(new ConfigDefinition("2 - AzuCraftyBoxes", "Toggle Full and Building Only")))
				{
					((BaseUnityPlugin)this).Config.Save();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not clean up legacy 1.1.0 config entries: " + ex.Message));
			}
		}

		private bool Cleanup(bool startupFailure)
		{
			bool result = true;
			if (_configWatcher != null)
			{
				try
				{
					_configWatcher.Dispose();
					_configWatcher = null;
				}
				catch (Exception ex)
				{
					result = false;
					((BaseUnityPlugin)this).Logger.LogError((object)("Separate config-watcher cleanup failure: " + ex));
				}
			}
			if (_patches != null && !_patches.Cleanup())
			{
				result = false;
			}
			if (_versionRegistrationStarted && startupFailure)
			{
				result = false;
				((BaseUnityPlugin)this).Logger.LogError((object)"Version registration was reached. Bundled ServerSync has no supported unregister operation; restart to clear that registration.");
			}
			return result;
		}

		private void OnDestroy()
		{
			_startup?.Stop();
			if (_startup == null)
			{
				return;
			}
			try
			{
				if (!Cleanup(startupFailure: false))
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"Add-on shutdown cleanup is incomplete or unverified; restart before playing.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Separate add-on shutdown cleanup failure: " + ex));
			}
		}

		private static void ShowFeatureMessage(string message)
		{
			Log.LogInfo((object)message);
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		private void InstallAzuCraftyBoxesGate()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method("AzuCraftyBoxes.Util.Functions.MiscFunctions:ShouldPrevent", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException("AzuCraftyBoxes ShouldPrevent was not found.");
			}
			HarmonyMethod val = new HarmonyMethod(typeof(AzuShouldPreventPatch), "Postfix", (Type[])null);
			val.priority = 0;
			val.after = new string[1] { "Azumatt.AzuCraftyBoxes" };
			HarmonyMethod postfix = val;
			_patches.Apply(methodInfo, null, postfix);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Installed the local per-player AzuCraftyBoxes building/crafting/machine gate.");
		}
	}
	internal static class FeaturePolicy
	{
		internal static bool Allowed(bool crafting, bool building, bool machine, bool activeCraft, bool craftSnapshot, bool buildingChoice, bool craftingChoice, bool machineChoice)
		{
			if (crafting)
			{
				if (!activeCraft)
				{
					return craftingChoice;
				}
				return craftSnapshot;
			}
			if (building)
			{
				return buildingChoice;
			}
			if (machine)
			{
				return machineChoice;
			}
			if (!activeCraft)
			{
				return craftingChoice;
			}
			return craftSnapshot;
		}
	}
	internal static class RuntimeContract
	{
		internal static void Validate()
		{
			Require(typeof(Player), "UpdatePlacement", typeof(bool), typeof(float));
			Require(typeof(Player), "TryPlacePiece", typeof(Piece));
			Require(typeof(Player), "ConsumeResources", typeof(Requirement[]), typeof(int), typeof(int), typeof(int));
			Require(typeof(InventoryGui), "OnCraftPressed");
			Require(typeof(InventoryGui), "DoCrafting", typeof(Player));
			Require(typeof(InventoryGui), "OnCraftCancelPressed");
			Require(typeof(Inventory), "ItemCheated", typeof(Requirement[]), typeof(int), typeof(bool));
			Require(typeof(Smelter), "RPC_AddOre", typeof(long), typeof(string), typeof(bool));
			Require(typeof(Fermenter), "RPC_AddItem", typeof(long), typeof(int), typeof(bool));
			Require(typeof(Fermenter), "GetStatus");
			Require(typeof(Fermenter), "IsItemAllowed", typeof(ItemData));
			Require(typeof(Character), "Message", typeof(MessageType), typeof(string), typeof(int), typeof(Sprite), typeof(bool));
			Require(typeof(Inventory), "Changed", typeof(bool), typeof(bool));
			Require(typeof(SEMan), "AddStatusEffect", typeof(StatusEffect), typeof(bool), typeof(int), typeof(float), typeof(short));
			if (AccessTools.Field(typeof(InventoryGui), "m_craftTimer")?.FieldType != typeof(float))
			{
				throw new MissingFieldException("InventoryGui.m_craftTimer float field is required.");
			}
		}

		private static void Require(Type type, string method, params Type[] parameters)
		{
			if (AccessTools.Method(type, method, parameters, (Type[])null) == null)
			{
				throw new MissingMethodException(type.FullName, method);
			}
		}
	}
	internal static class AzuGameApiAdapter
	{
		internal static List<Tuple<MethodBase, bool>> Prepare(Assembly azu, StartupSequence startup)
		{
			List<Tuple<MethodBase, bool>> targets = new List<Tuple<MethodBase, bool>>();
			startup.Step("discovering Azu compatibility targets", delegate
			{
				targets.AddRange(Discover(azu));
			});
			if (Chainloader.PluginInfos.TryGetValue("Azumatt.BuildCameraCHE", out var camera) && startup.TryOptionalCheck("checking optional Build Camera compatibility", delegate
			{
				if (camera == null || camera.Instance == null)
				{
					throw new InvalidOperationException("Build Camera is present but has no initialized instance.");
				}
				Assembly cameraAssembly = ((object)camera.Instance).GetType().Assembly;
				startup.Step("discovering optional Build Camera targets", delegate
				{
					if (Discover(cameraAssembly).Count != 0)
					{
						throw new NotSupportedException("Optional camera validation must not install dependency adapters.");
					}
				});
				startup.Step("checking optional Build Camera hooks", delegate
				{
					BaseModHealth.RequireInstalledCallbacks(cameraAssembly, "Azumatt.BuildCameraCHE", new string[3] { "Player_Update_Patch", "PlayerController_TakeInput_Patch", "GameCamera_UpdateCamera_Patch" });
				});
			}, delegate(string message)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)message);
				}
			}))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Optional Build Camera API and installed hooks checked. Placement uses the normal Player material safeguards.");
				}
			}
			List<Exception> failures = new List<Exception>();
			foreach (Tuple<MethodBase, bool> target in targets)
			{
				startup.Step("preflighting compatibility target " + PatchSession.Describe(target.Item1), delegate
				{
					try
					{
						ProbeTarget(target.Item1, target.Item2);
					}
					catch (Exception innerException)
					{
						failures.Add(new NotSupportedException("Cannot read/adapt the original method " + PatchSession.Describe(target.Item1) + " with " + typeof(Harmony).Assembly.GetName()?.ToString() + " and " + typeof(DynamicMethodDefinition).Assembly.GetName()?.ToString() + ". A later ILManipulator cannot repair a failure that occurs before it runs.", innerException));
					}
				});
			}
			if (failures.Count != 0)
			{
				throw new AggregateException("Compatibility preflight rejected " + failures.Count + " method(s). No add-on patches or ServerSync registrations were attempted. The installed dependency must have runtime-readable methods before this adapter can be used.", failures);
			}
			return targets;
		}

		internal static void ProbeTarget(MethodBase target, bool ore)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//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)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			DynamicMethodDefinition val = new DynamicMethodDefinition(target);
			try
			{
				Enumerator<Instruction> enumerator = val.Definition.Body.Instructions.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						Instruction current = enumerator.Current;
						OpCode opCode = current.OpCode;
						if ((int)((OpCode)(ref opCode)).OperandType != 4)
						{
							opCode = current.OpCode;
							if ((int)((OpCode)(ref opCode)).OperandType != 1)
							{
								continue;
							}
						}
						if (current.Operand == null)
						{
							throw new MissingMemberException("MonoMod left an unresolved member at IL_" + current.Offset.ToString("x4") + ".");
						}
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
				ILContext val2 = new ILContext(val.Definition);
				try
				{
					val2.Invoke((Manipulator)delegate(ILContext context)
					{
						AdaptCore(context, ore);
					});
				}
				finally
				{
					((IDisposable)val2)?.Dispose();
				}
				val.Generate();
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
		}

		internal static void Install(PatchSession patches, List<Tuple<MethodBase, bool>> targets)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			foreach (Tuple<MethodBase, bool> target in targets)
			{
				patches.Apply(target.Item1, null, null, null, null, new HarmonyMethod(typeof(AzuGameApiAdapter), target.Item2 ? "AdaptOre" : "Adapt", (Type[])null));
			}
		}

		private static List<Tuple<MethodBase, bool>> Discover(Assembly azu)
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			bool flag = azu.GetName().Name == "AzuCraftyBoxes";
			if (!flag && azu.GetName().Name != "Build Camera")
			{
				throw new NotSupportedException("Unknown compatibility assembly " + azu.GetName().Name);
			}
			if (flag)
			{
				BaseModCompatibility.Resolve(azu.GetName());
			}
			else if (azu.GetName().Version != new Version(1, 2, 10, 0))
			{
				throw new NotSupportedException("This compatibility profile requires Build Camera 1.2.10.0; detected " + azu.GetName().Version?.ToString() + ".");
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			HashSet<MethodBase> hashSet = new HashSet<MethodBase>();
			List<Tuple<MethodBase, bool>> list = new List<Tuple<MethodBase, bool>>();
			AssemblyDefinition val = AssemblyDefinition.ReadAssembly(azu.Location);
			try
			{
				foreach (TypeDefinition item2 in AllTypes((IEnumerable<TypeDefinition>)val.MainModule.Types))
				{
					Enumerator<MethodDefinition> enumerator2 = item2.Methods.GetEnumerator();
					try
					{
						while (enumerator2.MoveNext())
						{
							MethodDefinition current2 = enumerator2.Current;
							if (!current2.HasBody)
							{
								continue;
							}
							bool flag2 = false;
							bool flag3 = ((MemberReference)item2).FullName == "AzuCraftyBoxes.Patches.SmelterOnAddOrePatch";
							Enumerator<Instruction> enumerator3 = current2.Body.Instructions.GetEnumerator();
							MetadataToken metadataToken;
							try
							{
								while (enumerator3.MoveNext())
								{
									Instruction current3 = enumerator3.Current;
									object operand = current3.Operand;
									int num5 = Classify((MethodReference)((operand is MethodReference) ? operand : null));
									if (num5 >= 1 && num5 <= 3)
									{
										throw new NotSupportedException("Obsolete game API in " + ((MemberReference)current2).FullName + ". Replace the original dependency with the supported Valheim 1.0 rebuild; this add-on does not patch unreadable legacy methods.");
									}
									object operand2 = current3.Operand;
									MethodReference val2 = (MethodReference)((operand2 is MethodReference) ? operand2 : null);
									if (((val2 != null) ? ((MemberReference)((MemberReference)val2).DeclaringType).FullName : null) == "Character" && ((MemberReference)val2).Name == "Message" && val2.Parameters.Count == 5)
									{
										num3++;
										if (!flag)
										{
											Module manifestModule = azu.ManifestModule;
											metadataToken = ((MemberReference)current2).MetadataToken;
											hashSet.Add(manifestModule.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32()));
										}
									}
									if (!flag && ((val2 != null) ? ((MemberReference)((MemberReference)val2).DeclaringType).FullName : null) == "Player" && ((MemberReference)val2).Name == "UpdatePlacement" && val2.Parameters.Count == 2 && ((MemberReference)((ParameterReference)val2.Parameters[0]).ParameterType).FullName == "System.Boolean" && ((MemberReference)((ParameterReference)val2.Parameters[1]).ParameterType).FullName == "System.Single")
									{
										num4++;
										Module manifestModule2 = azu.ManifestModule;
										metadataToken = ((MemberReference)current2).MetadataToken;
										hashSet.Add(manifestModule2.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32()));
									}
									if (num5 == 4 && flag3)
									{
										flag2 = true;
										num2++;
									}
								}
							}
							finally
							{
								((IDisposable)enumerator3/*cast due to .constrained prefix*/).Dispose();
							}
							if (flag2)
							{
								Module manifestModule3 = azu.ManifestModule;
								metadataToken = ((MemberReference)current2).MetadataToken;
								MethodBase item = manifestModule3.ResolveMethod(((MetadataToken)(ref metadataToken)).ToInt32());
								list.Add(Tuple.Create(item, flag3));
								num++;
							}
						}
					}
					finally
					{
						((IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose();
					}
				}
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
			if (num2 != (flag ? 2 : 0) || num != (flag ? 1 : 0) || (!flag && (num3 != 2 || num4 != 1 || hashSet.Count != 3)))
			{
				throw new InvalidOperationException("Unexpected " + azu.GetName().Name + " API call-site counts; no partial adaptation is accepted.");
			}
			foreach (MethodBase item3 in hashSet)
			{
				ProbeTarget(item3, ore: false);
			}
			return list;
		}

		private static IEnumerable<TypeDefinition> AllTypes(IEnumerable<TypeDefinition> roots)
		{
			foreach (TypeDefinition type in roots)
			{
				yield return type;
				foreach (TypeDefinition item in AllTypes((IEnumerable<TypeDefinition>)type.NestedTypes))
				{
					yield return item;
				}
			}
		}

		private static int Classify(MethodReference method)
		{
			if (method == null)
			{
				return 0;
			}
			string fullName = ((MemberReference)((MemberReference)method).DeclaringType).FullName;
			if (fullName == "Character" && ((MemberReference)method).Name == "Message" && method.Parameters.Count == 4)
			{
				return 1;
			}
			if (fullName == "Inventory" && ((MemberReference)method).Name == "Changed" && method.Parameters.Count == 0)
			{
				return 2;
			}
			if (fullName == "SEMan" && ((MemberReference)method).Name == "AddStatusEffect" && method.Parameters.Count == 4 && ((MemberReference)((ParameterReference)method.Parameters[0]).ParameterType).FullName == "StatusEffect")
			{
				return 3;
			}
			if (fullName == "ZNetView" && ((MemberReference)method).Name == "InvokeRPC" && method.Parameters.Count == 2 && ((MemberReference)((ParameterReference)method.Parameters[0]).ParameterType).FullName == "System.String" && ((MemberReference)((ParameterReference)method.Parameters[1]).ParameterType).FullName == "System.Object[]")
			{
				return 4;
			}
			return 0;
		}

		private static void Adapt(ILContext il)
		{
			AdaptCore(il, ore: false);
		}

		private static void AdaptOre(ILContext il)
		{
			AdaptCore(il, ore: true);
		}

		private static void AdaptCore(ILContext il, bool ore)
		{
			//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)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (!ore)
			{
				return;
			}
			Enumerator<Instruction> enumerator = il.Body.Instructions.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					Instruction current = enumerator.Current;
					object operand = current.Operand;
					if (Classify((MethodReference)((operand is MethodReference) ? operand : null)) == 4)
					{
						current.OpCode = OpCodes.Call;
						current.Operand = il.Import((MethodBase)typeof(MachineTransferMetadata).GetMethod("InvokeRPC", new Type[3]
						{
							typeof(ZNetView),
							typeof(string),
							typeof(object[])
						}));
					}
				}
			}
			finally
			{
				((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
		}
	}
	internal static class MachineTransferMetadata
	{
		[ThreadStatic]
		private static HashSet<string> _cheatedNames;

		internal static void Clear()
		{
			if (_cheatedNames == null)
			{
				_cheatedNames = new HashSet<string>(StringComparer.Ordinal);
			}
			else
			{
				_cheatedNames.Clear();
			}
		}

		internal static bool RemovedCheated(string name)
		{
			if (name != null && _cheatedNames != null)
			{
				return _cheatedNames.Contains(name);
			}
			return false;
		}

		internal static void Record(string name, bool cheated)
		{
			if (AzuCallContext.InMachine && cheated && name != null)
			{
				_cheatedNames.Add(name);
			}
		}

		public static void InvokeRPC(ZNetView view, string method, object[] parameters)
		{
			if (AzuCallContext.InMachine && method == "RPC_AddOre" && parameters != null && parameters.Length == 2 && parameters[0] is string && parameters[1] is bool)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab((string)parameters[0]);
				string name = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name);
				parameters = WithRemovedMetadata(parameters, RemovedCheated(name));
			}
			view.InvokeRPC(method, parameters);
		}

		internal static object[] WithRemovedMetadata(object[] parameters, bool removedCheated)
		{
			if (parameters == null || parameters.Length != 2 || !(parameters[0] is string) || !(parameters[1] is bool) || !removedCheated || (bool)parameters[1])
			{
				return parameters;
			}
			return new object[2]
			{
				parameters[0],
				true
			};
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class MachineItemRemovalMetadataPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Inventory __instance, string name, int itemQuality, bool worldLevelBased)
		{
			if (Plugin.IsReady && AzuCallContext.InMachine)
			{
				MachineTransferMetadata.Record(name, __instance.ItemCheated(name, itemQuality, worldLevelBased));
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveOneItem", new Type[] { typeof(ItemData) })]
	internal static class MachineSingleItemMetadataPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ItemData item)
		{
			if (Plugin.IsReady && item != null)
			{
				MachineTransferMetadata.Record(item.m_shared?.m_name, item.m_cheated);
			}
		}
	}
	[HarmonyPatch(typeof(CookingStation), "FindCookableItem")]
	internal static class CookingMetadataPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "Azumatt.AzuCraftyBoxes" })]
		private static void Postfix(ItemData __result)
		{
			if (Plugin.IsReady && __result != null && MachineTransferMetadata.RemovedCheated(__result.m_shared?.m_name))
			{
				__result.m_cheated = true;
			}
		}
	}
	[HarmonyPatch]
	internal static class FermenterTransferCompatibilityPatch
	{
		private static readonly MethodInfo Status = typeof(Fermenter).GetMethod("GetStatus", BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly MethodInfo Allowed = typeof(Fermenter).GetMethod("IsItemAllowed", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(ItemData) }, null);

		private static MethodBase TargetMethod()
		{
			return AccessTools.Method("AzuCraftyBoxes.Patches.SearchContainersAsWell:Postfix", (Type[])null, (Type[])null);
		}

		[HarmonyPrefix]
		private static bool Prefix(object[] __args)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			if (!Plugin.IsReady)
			{
				return true;
			}
			Fermenter val = (Fermenter)__args[0];
			Inventory val2 = (Inventory)__args[1];
			Player localPlayer = Player.m_localPlayer;
			if (__args[2] != null || (Object)(object)localPlayer == (Object)null || val2 != ((Humanoid)localPlayer).GetInventory() || AzuRuntimeBridge.PullingIsPrevented() || Convert.ToInt32(Status.Invoke(val, null)) != 0)
			{
				return false;
			}
			foreach (object nearbyContainer in AzuRuntimeBridge.GetNearbyContainers((Component)(object)val))
			{
				Inventory inventory = AzuRuntimeBridge.GetInventory(nearbyContainer);
				if (inventory == null || inventory == val2)
				{
					continue;
				}
				foreach (ItemConversion item2 in val.m_conversion)
				{
					if ((Object)(object)item2.m_from == (Object)null)
					{
						continue;
					}
					string name = item2.m_from.m_itemData.m_shared.m_name;
					if (!AzuRuntimeBridge.CanPullItem(nearbyContainer, ((Object)item2.m_from).name) || AzuRuntimeBridge.GetAllowedRemoval(inventory.CountItems(name, -1, true)) <= 0)
					{
						continue;
					}
					ItemData item = inventory.GetItem(name, -1, false);
					if (item != null && !((Object)(object)item.m_dropPrefab == (Object)null) && (bool)Allowed.Invoke(val, new object[1] { item }))
					{
						ZNetView component = ((Component)val).GetComponent<ZNetView>();
						if ((Object)(object)component == (Object)null || !component.IsValid() || !inventory.RemoveOneItem(item))
						{
							return false;
						}
						component.InvokeRPC("RPC_AddItem", new object[2]
						{
							StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name),
							item.m_cheated
						});
						__args[2] = null;
						return false;
					}
				}
			}
			return false;
		}
	}
	internal static class AzuServerSyncBridge
	{
		private static object _configSync;

		internal static void Initialize(ManualLogSource log)
		{
			try
			{
				Type type = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin");
				if (type == null)
				{
					throw new TypeLoadException("AzuCraftyBoxes.AzuCraftyBoxesPlugin was not found.");
				}
				Type type2 = type.Assembly.GetType("ServerSync.ConfigSync", throwOnError: false);
				if (type2 == null)
				{
					throw new TypeLoadException("AzuCraftyBoxes' bundled ServerSync.ConfigSync was not found.");
				}
				_configSync = Activator.CreateInstance(type2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[1] { "local.valheim.azucraftyboxesbuildingonly" }, null);
				if (_configSync == null)
				{
					throw new InvalidOperationException("ServerSync.ConfigSync could not be created.");
				}
				SetMember(type2, "DisplayName", "AzuCraftyBoxes Fixes & Config");
				SetMember(type2, "CurrentVersion", "1.6.3");
				SetMember(type2, "MinimumRequiredVersion", "1.6.3");
				SetMember(type2, "ModRequired", true);
				log.LogInfo((object)"Registered required-mod version checking only; per-player gameplay settings are intentionally not synchronized.");
			}
			catch (Exception innerException)
			{
				throw new InvalidOperationException("Add-on version-check registration failed.", innerException);
			}
		}

		private static void SetMember(Type configSyncType, string name, object value)
		{
			FieldInfo field = configSyncType.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(_configSync, value);
				return;
			}
			PropertyInfo? property = configSyncType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null)
			{
				throw new MissingMemberException(configSyncType.FullName, name);
			}
			property.SetValue(_configSync, value, null);
		}
	}
	internal static class AzuRuntimeBridge
	{
		private sealed class ContainerMethods
		{
			internal readonly MethodInfo ItemCount;

			internal readonly MethodInfo GetPrefabName;

			internal readonly MethodInfo ProcessContainerInventory;

			internal readonly MethodInfo GetInventory;

			internal bool IsComplete
			{
				get
				{
					if (ItemCount != null && GetPrefabName != null)
					{
						return ProcessContainerInventory != null;
					}
					return false;
				}
			}

			internal ContainerMethods(Type type)
			{
				ItemCount = type.GetMethod("ItemCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
				GetPrefabName = type.GetMethod("GetPrefabName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				GetInventory = type.GetMethod("GetInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				ProcessContainerInventory = type.GetMethod("ProcessContainerInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
				{
					typeof(string),
					typeof(int),
					typeof(int)
				}, null);
			}
		}

		private static MethodInfo _shouldPrevent;

		private static MethodInfo _getNearbyContainers;

		private static MethodInfo _canItemBePulled;

		private static MethodInfo _checkAndDecrement;

		private static FieldInfo _rangeField;

		private static FieldInfo _skipField;

		private static PropertyInfo _pendingConsumptionsProperty;

		private static readonly Dictionary<Type, ContainerMethods> ContainerMethodCache = new Dictionary<Type, ContainerMethods>();

		private static ManualLogSource _log;

		internal static void Initialize(ManualLogSource log)
		{
			_log = log;
			try
			{
				Type type = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin");
				Type type2 = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.MiscFunctions");
				Type type3 = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.Boxes");
				if (type == null || type2 == null || type3 == null)
				{
					throw new TypeLoadException("One or more AzuCraftyBoxes runtime types are missing.");
				}
				_shouldPrevent = type2.GetMethod("ShouldPrevent", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				_canItemBePulled = type3.GetMethod("CanItemBePulled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
				{
					typeof(string),
					typeof(string),
					typeof(string)
				}, null);
				_checkAndDecrement = type3.GetMethod("CheckAndDecrement", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null);
				_rangeField = type.GetField("mRange", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_skipField = type.GetField("skip", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				MethodInfo[] methods = type3.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo.Name == "GetNearbyContainers" && methodInfo.IsGenericMethodDefinition && methodInfo.GetParameters().Length == 2)
					{
						_getNearbyContainers = methodInfo.MakeGenericMethod(typeof(Component));
						break;
					}
				}
				_pendingConsumptionsProperty = AccessTools.TypeByName("AzuCraftyBoxes.Util.Functions.ConsumptionManager")?.GetProperty("PendingConsumptions", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (_shouldPrevent == null || _getNearbyContainers == null || _canItemBePulled == null || _checkAndDecrement == null || _rangeField == null || _skipField == null || _pendingConsumptionsProperty == null)
				{
					throw new MissingMemberException("A required member of the supported AzuCraftyBoxes runtime contract is missing.");
				}
			}
			catch (Exception innerException)
			{
				throw new InvalidOperationException("AzuCraftyBoxes runtime bridge initialization failed.", innerException);
			}
		}

		internal static bool PullingIsPrevented()
		{
			try
			{
				return (bool)_shouldPrevent.Invoke(null, null);
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Could not read AzuCraftyBoxes pulling state: " + ex.Message));
				return true;
			}
		}

		internal static List<object> GetNearbyContainers(Component player)
		{
			List<object> list = new List<object>();
			try
			{
				object value = _rangeField.GetValue(null);
				float num = Convert.ToSingle((value?.GetType().GetProperty("Value"))?.GetValue(value, null));
				if (!(_getNearbyContainers.Invoke(null, new object[2] { player, num }) is IEnumerable enumerable))
				{
					return list;
				}
				foreach (object item in enumerable)
				{
					if (item != null)
					{
						list.Add(item);
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Could not query nearby AzuCraftyBoxes containers: " + ex.Message));
			}
			return list;
		}

		internal static int GetItemCount(object container, string sharedName)
		{
			try
			{
				ContainerMethods containerMethods = GetContainerMethods(container);
				return (containerMethods != null) ? Convert.ToInt32(containerMethods.ItemCount.Invoke(container, new object[1] { sharedName })) : 0;
			}
			catch
			{
				return 0;
			}
		}

		internal static Inventory GetInventory(object container)
		{
			object? obj = GetContainerMethods(container)?.GetInventory?.Invoke(container, null);
			return (Inventory)((obj is Inventory) ? obj : null);
		}

		internal static bool CanPullItem(object container, string itemPrefabName)
		{
			try
			{
				ContainerMethods containerMethods = GetContainerMethods(container);
				if (containerMethods == null)
				{
					return false;
				}
				string text = containerMethods.GetPrefabName.Invoke(container, null) as string;
				if (string.IsNullOrWhiteSpace(text))
				{
					return false;
				}
				return (bool)_canItemBePulled.Invoke(null, new object[3]
				{
					text,
					itemPrefabName,
					string.Empty
				});
			}
			catch
			{
				return false;
			}
		}

		internal static int GetAllowedRemoval(int itemCount)
		{
			try
			{
				return Convert.ToInt32(_checkAndDecrement.Invoke(null, new object[1] { itemCount }));
			}
			catch
			{
				return 0;
			}
		}

		internal static int RemoveFromContainer(object container, string sharedName, int amount)
		{
			if (amount <= 0)
			{
				return 0;
			}
			try
			{
				ContainerMethods containerMethods = GetContainerMethods(container);
				if (containerMethods == null)
				{
					return 0;
				}
				return Convert.ToInt32(containerMethods.ProcessContainerInventory.Invoke(container, new object[3] { sharedName, 0, amount }));
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Could not remove a building material from a nearby container: " + ex.Message));
				return 0;
			}
		}

		internal static void ResetTransientCraftingState()
		{
			try
			{
				_skipField?.SetValue(null, false);
			}
			catch
			{
			}
		}

		internal static void ClearPendingConsumptions()
		{
			try
			{
				object obj = _pendingConsumptionsProperty?.GetValue(null, null);
				if (obj == null)
				{
					return;
				}
				MethodInfo method = obj.GetType().GetMethod("TryTake");
				if (!(method == null))
				{
					object[] array = new object[1];
					while ((bool)method.Invoke(obj, array))
					{
						array[0] = null;
					}
				}
			}
			catch
			{
			}
		}

		private static ContainerMethods GetContainerMethods(object container)
		{
			if (container == null)
			{
				return null;
			}
			Type type = container.GetType();
			if (!ContainerMethodCache.TryGetValue(type, out var value))
			{
				value = new ContainerMethods(type);
				ContainerMethodCache[type] = value;
			}
			if (!value.IsComplete)
			{
				return null;
			}
			return value;
		}
	}
	internal static class AzuCallContext
	{
		[ThreadStatic]
		private static int _buildingDepth;

		[ThreadStatic]
		private static int _machineDepth;

		[ThreadStatic]
		private static int _craftingDepth;

		[ThreadStatic]
		private static int _infrastructureDepth;

		internal static bool InBuilding => _buildingDepth > 0;

		internal static bool InMachine => _machineDepth > 0;

		internal static bool InCrafting => _craftingDepth > 0;

		internal static bool InInfrastructure => _infrastructureDepth > 0;

		internal static void EnterBuilding()
		{
			_buildingDepth++;
		}

		internal static void ExitBuilding()
		{
			if (_buildingDepth > 0)
			{
				_buildingDepth--;
			}
		}

		internal static void EnterMachine()
		{
			if (_machineDepth == 0)
			{
				MachineTransferMetadata.Clear();
			}
			_machineDepth++;
		}

		internal static void ExitMachine()
		{
			if (_machineDepth > 0)
			{
				_machineDepth--;
			}
		}

		internal static void EnterCrafting()
		{
			_craftingDepth++;
		}

		internal static void ExitCrafting()
		{
			if (_craftingDepth > 0)
			{
				_craftingDepth--;
			}
		}

		internal static void EnterInfrastructure()
		{
			_infrastructureDepth++;
		}

		internal static void ExitInfrastructure()
		{
			if (_infrastructureDepth > 0)
			{
				_infrastructureDepth--;
			}
		}
	}
	[HarmonyPatch]
	internal static class BuildingOperationContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			HashSet<MethodBase> hashSet = new HashSet<MethodBase>();
			AddDeclaredMethods(hashSet, typeof(Player), "UpdatePlacement", "TryPlacePiece", "PlacePiece");
			MethodBase methodBase = AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2]
			{
				typeof(Piece),
				typeof(RequirementMode)
			}, (Type[])null);
			if (methodBase != null)
			{
				hashSet.Add(methodBase);
			}
			AddDeclaredMethods(hashSet, typeof(Hud), "SetupPieceInfo");
			return hashSet;
		}

		private static void AddDeclaredMethods(ISet<MethodBase> methods, Type type, params string[] methodNames)
		{
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				foreach (string text in methodNames)
				{
					if (declaredMethod.Name == text)
					{
						methods.Add(declaredMethod);
						break;
					}
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			if (Plugin.IsReady)
			{
				AzuCallContext.EnterBuilding();
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuCallContext.ExitBuilding();
			return __exception;
		}
	}
	internal static class PlacementConsumptionMarker
	{
		[ThreadStatic]
		private static Requirement[] _requirements;

		[ThreadStatic]
		private static int _frame;

		internal static bool Active
		{
			get
			{
				if (_requirements != null)
				{
					return _frame == Time.frameCount;
				}
				return false;
			}
		}

		internal static void Mark(Piece piece)
		{
			_requirements = piece?.m_resources;
			_frame = Time.frameCount;
		}

		internal static bool Matches(Requirement[] requirements)
		{
			if (Active)
			{
				return _requirements == requirements;
			}
			return false;
		}

		internal static void ClearIfMatches(Requirement[] requirements)
		{
			if (Matches(requirements))
			{
				_requirements = null;
				_frame = -1;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece", new Type[] { typeof(Piece) })]
	internal static class PlacementConsumptionMarkerPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(Player __instance, Piece piece, bool __result)
		{
			if (Plugin.IsReady && __result && (Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				PlacementConsumptionMarker.Mark(piece);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class BuildingConsumptionSafetyPatch
	{
		private static bool _reportedFallback;

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Player __instance, Requirement[] requirements, int qualityLevel, out BuildingConsumptionSnapshot __state, int itemQuality = -1, int multiplier = 1)
		{
			if (!Plugin.IsReady)
			{
				__state = null;
				return;
			}
			__state = null;
			if ((AzuCallContext.InBuilding || PlacementConsumptionMarker.Matches(requirements)) && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.ModEnabled.Value && Plugin.BuildingFromContainers.Value && !AzuRuntimeBridge.PullingIsPrevented())
			{
				__state = BuildingConsumptionSnapshot.Capture(__instance, requirements, qualityLevel, itemQuality, multiplier);
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception, BuildingConsumptionSnapshot __state, Requirement[] requirements)
		{
			try
			{
				if (__exception == null && __state != null && __state.RepairMissingConsumption() > 0 && !_reportedFallback)
				{
					_reportedFallback = true;
					Plugin.Log.LogWarning((object)"The building-consumption safety fallback was needed and removed the missing nearby-chest materials. This message is shown once.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Building material consumption could not be verified safely: " + ex));
			}
			finally
			{
				PlacementConsumptionMarker.ClearIfMatches(requirements);
			}
			return __exception;
		}
	}
	internal sealed class BuildingConsumptionSnapshot
	{
		private sealed class ResourceAudit
		{
			internal string SharedName;

			internal string PrefabName;

			internal int RequiredAmount;

			internal int CountBefore;
		}

		private readonly Player _player;

		private readonly Inventory _inventory;

		private readonly List<object> _containers;

		private readonly List<ResourceAudit> _resources;

		private readonly int _itemQuality;

		private BuildingConsumptionSnapshot(Player player, List<object> containers, List<ResourceAudit> resources, int itemQuality)
		{
			_player = player;
			_inventory = ((Humanoid)player).GetInventory();
			_containers = containers;
			_resources = resources;
			_itemQuality = itemQuality;
		}

		internal static BuildingConsumptionSnapshot Capture(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			if ((Object)(object)player == (Object)null || requirements == null || multiplier <= 0)
			{
				return null;
			}
			List<object> nearbyContainers = AzuRuntimeBridge.GetNearbyContainers((Component)(object)player);
			Dictionary<string, ResourceAudit> dictionary = new Dictionary<string, ResourceAudit>(StringComparer.Ordinal);
			foreach (Requirement val in requirements)
			{
				if ((Object)(object)val?.m_resItem == (Object)null || val.m_resItem.m_itemData?.m_shared == null || !UpgraderRequirements.Matches(val, player))
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * multiplier;
				if (num > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					if (!dictionary.TryGetValue(name, out var value))
					{
						value = new ResourceAudit
						{
							SharedName = name,
							PrefabName = GetPrefabName(((Object)val.m_resItem).name)
						};
						dictionary.Add(name, value);
					}
					value.RequiredAmount += num;
				}
			}
			List<ResourceAudit> list = new List<ResourceAudit>(dictionary.Values);
			BuildingConsumptionSnapshot buildingConsumptionSnapshot = new BuildingConsumptionSnapshot(player, nearbyContainers, list, itemQuality);
			foreach (ResourceAudit item in list)
			{
				item.CountBefore = buildingConsumptionSnapshot.CountTotal(item.SharedName);
			}
			return buildingConsumptionSnapshot;
		}

		internal int RepairMissingConsumption()
		{
			if ((Object)(object)_player == (Object)null || (Object)(object)_player != (Object)(object)Player.m_localPlayer || _inventory == null)
			{
				return 0;
			}
			int num = 0;
			foreach (ResourceAudit resource in _resources)
			{
				int num2 = CountTotal(resource.SharedName);
				int num3 = Math.Max(0, resource.CountBefore - num2);
				int num4 = resource.RequiredAmount - num3;
				if (num4 > 0)
				{
					int num5 = RemoveMissing(resource, num4);
					num += num5;
					if (num5 < num4)
					{
						Plugin.Log.LogError((object)("A placed piece still has " + (num4 - num5) + " unconsumed " + resource.SharedName + " after the AzuCraftyBoxes safety fallback. Check the BepInEx log and nearby-container permissions."));
					}
				}
			}
			return num;
		}

		private int CountTotal(string sharedName)
		{
			Inventory inventory = _inventory;
			int num = ((inventory != null) ? inventory.CountItems(sharedName, -1, true) : 0);
			foreach (object container in _containers)
			{
				num += Math.Max(0, AzuRuntimeBridge.GetItemCount(container, sharedName));
			}
			return num;
		}

		private int RemoveMissing(ResourceAudit resource, int missing)
		{
			int num = 0;
			int num2 = _inventory.CountItems(resource.SharedName, -1, true);
			int num3 = Math.Min(missing, num2);
			if (num3 > 0)
			{
				_inventory.RemoveItem(resource.SharedName, num3, _itemQuality, true);
				num += Math.Max(0, num2 - _inventory.CountItems(resource.SharedName, -1, true));
			}
			foreach (object container in _containers)
			{
				int num4 = missing - num;
				if (num4 <= 0)
				{
					break;
				}
				if (AzuRuntimeBridge.CanPullItem(container, resource.PrefabName))
				{
					int allowedRemoval = AzuRuntimeBridge.GetAllowedRemoval(AzuRuntimeBridge.GetItemCount(container, resource.SharedName));
					int num5 = Math.Min(num4, Math.Max(0, allowedRemoval));
					num += Math.Min(num5, AzuRuntimeBridge.RemoveFromContainer(container, resource.SharedName, num5));
				}
			}
			return num;
		}

		private static string GetPrefabName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			int num = name.IndexOfAny(new char[2] { '(', ' ' });
			if (num >= 0)
			{
				return name.Substring(0, num);
			}
			return name;
		}
	}
	internal static class UpgraderRequirements
	{
		internal static bool Matches(Requirement requirement, Player player)
		{
			CraftingStation val = (((Object)(object)player == (Object)null) ? null : player.GetCurrentCraftingStation());
			if (requirement != null)
			{
				return requirement.m_upgraderResource == ((Object)(object)val != (Object)null && val.m_upgrader);
			}
			return false;
		}

		internal static Requirement[] Filter(Requirement[] requirements, Player player)
		{
			if (requirements == null || (Object)(object)player == (Object)null)
			{
				return requirements;
			}
			if (requirements.Length == 0)
			{
				return requirements;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool flag = (Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader;
			int num = 0;
			Requirement[] array = requirements;
			foreach (Requirement val in array)
			{
				if (val != null && val.m_upgraderResource == flag)
				{
					num++;
				}
			}
			if (num == requirements.Length)
			{
				return requirements;
			}
			if (num == 0)
			{
				return Array.Empty<Requirement>();
			}
			Requirement[] array2 = (Requirement[])(object)new Requirement[num];
			int num2 = 0;
			array = requirements;
			foreach (Requirement val2 in array)
			{
				if (val2 != null && val2.m_upgraderResource == flag)
				{
					array2[num2++] = val2;
				}
			}
			return array2;
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class UpgraderRequirementFilterPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "Azumatt.AzuCraftyBoxes" })]
		private static void Prefix(Player __instance, ref Requirement[] requirements)
		{
			if (Plugin.IsReady)
			{
				requirements = UpgraderRequirements.Filter(requirements, __instance);
			}
		}
	}
	[HarmonyPatch]
	internal static class UpgraderRecipeFilterPatch
	{
		private sealed class State
		{
			internal Recipe Recipe;

			internal Requirement[] Resources;
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Player)))
			{
				if (declaredMethod.Name != "HaveRequirements" && declaredMethod.Name != "HaveRequirementItems" && declaredMethod.Name != "GetFirstRequiredItem")
				{
					continue;
				}
				ParameterInfo[] parameters = declaredMethod.GetParameters();
				for (int i = 0; i < parameters.Length; i++)
				{
					if (parameters[i].ParameterType == typeof(Recipe))
					{
						yield return declaredMethod;
						break;
					}
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "Azumatt.AzuCraftyBoxes" })]
		private static void Prefix(Player __instance, object[] __args, out State __state)
		{
			if (!Plugin.IsReady)
			{
				__state = null;
				return;
			}
			__state = null;
			foreach (object obj in __args)
			{
				Recipe val = (Recipe)((obj is Recipe) ? obj : null);
				if (!((Object)(object)val == (Object)null))
				{
					__state = new State
					{
						Recipe = val,
						Resources = val.m_resources
					};
					val.m_resources = UpgraderRequirements.Filter(__state.Resources, __instance);
					break;
				}
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(State __state, Exception __exception)
		{
			if (__state != null && (Object)(object)__state.Recipe != (Object)null)
			{
				__state.Recipe.m_resources = __state.Resources;
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Recipe), "GetAmount")]
	internal static class UpgraderRecipeAmountFilterPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Recipe __instance, out Requirement[] __state)
		{
			if (!Plugin.IsReady)
			{
				__state = null;
				return;
			}
			__state = __instance.m_resources;
			__instance.m_resources = UpgraderRequirements.Filter(__state, Player.m_localPlayer);
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Recipe __instance, Requirement[] __state, Exception __exception)
		{
			if (__state != null)
			{
				__instance.m_resources = __state;
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(bool)
	})]
	internal static class ContainerCheatedResourcesPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Inventory __instance, Requirement[] resources, int quality, bool matchWorldLevel, ref bool __result)
		{
			if (!Plugin.IsReady)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (__result || (Object)(object)localPlayer == (Object)null || __instance != ((Humanoid)localPlayer).GetInventory() || resources == null || AzuRuntimeBridge.PullingIsPrevented())
			{
				return;
			}
			List<object> nearbyContainers = AzuRuntimeBridge.GetNearbyContainers((Component)(object)localPlayer);
			foreach (Requirement val in resources)
			{
				if (!UpgraderRequirements.Matches(val, localPlayer) || (Object)(object)val.m_resItem == (Object)null)
				{
					continue;
				}
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				int num = (AzuCallContext.InCrafting ? CraftTransaction.QualityLevel : 0);
				int num2 = ((!AzuCallContext.InCrafting) ? 1 : CraftTransaction.Multiplier);
				int num3 = val.GetAmount(num) * num2 - __instance.CountItems(name, quality, matchWorldLevel);
				if (num3 <= 0)
				{
					continue;
				}
				string itemPrefabName = ((Object)val.m_resItem).name.Replace("(Clone)", "").Trim();
				foreach (object item in nearbyContainers)
				{
					if (!AzuRuntimeBridge.CanPullItem(item, itemPrefabName))
					{
						continue;
					}
					Inventory inventory = AzuRuntimeBridge.GetInventory(item);
					if (inventory == null || inventory == __instance)
					{
						continue;
					}
					int allowedRemoval = AzuRuntimeBridge.GetAllowedRemoval(inventory.CountItems(name, quality, matchWorldLevel));
					if (allowedRemoval > 0)
					{
						if (inventory.ItemCheated(name, quality, matchWorldLevel))
						{
							__result = true;
							return;
						}
						num3 -= allowedRemoval;
						if (num3 <= 0)
						{
							break;
						}
					}
				}
			}
		}
	}
	internal static class CraftTransaction
	{
		internal static bool Active { get; private set; }

		internal static bool AllowNearbyContainers { get; private set; }

		internal static int QualityLevel { get; private set; } = 1;

		internal static int Multiplier { get; private set; } = 1;

		internal static void ReadAmounts(InventoryGui gui)
		{
			object? obj = typeof(InventoryGui).GetField("m_craftUpgradeItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gui);
			ItemData val = (ItemData)((obj is ItemData) ? obj : null);
			QualityLevel = ((val == null) ? 1 : (val.m_quality + 1));
			Multiplier = ((!(bool)(typeof(InventoryGui).GetField("m_multiCrafting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gui) ?? ((object)false))) ? 1 : ((int)(typeof(InventoryGui).GetField("m_multiCraftAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gui) ?? ((object)1))));
		}

		internal static void Begin(bool allowNearbyContainers)
		{
			Active = true;
			AllowNearbyContainers = allowNearbyContainers;
		}

		internal static void Clear()
		{
			Active = false;
			AllowNearbyContainers = false;
		}
	}
	[HarmonyPatch]
	internal static class CraftingOperationContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			HashSet<MethodBase> hashSet = new HashSet<MethodBase>();
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(Player)))
			{
				if ((!(declaredMethod.Name != "HaveRequirements") || !(declaredMethod.Name != "HaveRequirementItems") || !(declaredMethod.Name != "GetFirstRequiredItem") || !(declaredMethod.Name != "UpdateKnownRecipesList")) && (declaredMethod.Name == "UpdateKnownRecipesList" || HasRecipeParameter(declaredMethod)))
				{
					hashSet.Add(declaredMethod);
				}
			}
			string[] array = new string[8] { "SetupCrafting", "UpdateCraftingPanel", "UpdateRecipeList", "AddRecipeToList", "OnSelectedRecipe", "UpdateRecipe", "SetupRequirementList", "SetupRequirement" };
			foreach (MethodInfo declaredMethod2 in AccessTools.GetDeclaredMethods(typeof(InventoryGui)))
			{
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (declaredMethod2.Name == text)
					{
						hashSet.Add(declaredMethod2);
						break;
					}
				}
			}
			return hashSet;
		}

		private static bool HasRecipeParameter(MethodInfo method)
		{
			ParameterInfo[] parameters = method.GetParameters();
			for (int i = 0; i < parameters.Length; i++)
			{
				if (parameters[i].ParameterType == typeof(Recipe))
				{
					return true;
				}
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			if (Plugin.IsReady)
			{
				AzuCallContext.EnterCrafting();
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuCallContext.ExitCrafting();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	internal static class CraftTransactionStartPatch
	{
		private struct State
		{
			internal float TimerBefore;

			internal bool AllowNearbyContainers;
		}

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

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(InventoryGui __instance, out State __state)
		{
			if (!Plugin.IsReady)
			{
				__state = default(State);
				return;
			}
			AzuCallContext.EnterCrafting();
			AzuRuntimeBridge.ClearPendingConsumptions();
			__state = new State
			{
				TimerBefore = ReadCraftTimer(__instance),
				AllowNearbyContainers = Plugin.CraftingFromContainers.Value
			};
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(InventoryGui __instance, State __state)
		{
			if (Plugin.IsReady)
			{
				float num = ReadCraftTimer(__instance);
				if (num >= 0f && (__state.TimerBefore < 0f || !CraftTransaction.Active))
				{
					CraftTransaction.Begin(__state.AllowNearbyContainers);
				}
				else if (num < 0f)
				{
					AzuRuntimeBridge.ClearPendingConsumptions();
					CraftTransaction.Clear();
				}
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuCallContext.ExitCrafting();
			return __exception;
		}

		private static float ReadCraftTimer(InventoryGui inventoryGui)
		{
			try
			{
				return (CraftTimerField == null) ? (-1f) : Convert.ToSingle(CraftTimerField.GetValue(inventoryGui));
			}
			catch
			{
				return -1f;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
	internal static class CraftTransactionFinishPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(InventoryGui __instance)
		{
			if (Plugin.IsReady)
			{
				AzuCallContext.EnterCrafting();
				CraftTransaction.ReadAmounts(__instance);
				if (!CraftTransaction.Active)
				{
					CraftTransaction.Begin(Plugin.CraftingFromContainers.Value);
				}
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuRuntimeBridge.ResetTransientCraftingState();
			AzuRuntimeBridge.ClearPendingConsumptions();
			CraftTransaction.Clear();
			AzuCallContext.ExitCrafting();
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class CraftTransactionCancelPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodBase methodBase = AccessTools.Method(typeof(InventoryGui), "OnCraftCancelPressed", (Type[])null, (Type[])null);
			MethodBase hide = AccessTools.Method(typeof(InventoryGui), "Hide", (Type[])null, (Type[])null);
			if (methodBase != null)
			{
				yield return methodBase;
			}
			if (hide != null)
			{
				yield return hide;
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix()
		{
			if (Plugin.IsReady)
			{
				AzuRuntimeBridge.ResetTransientCraftingState();
				AzuRuntimeBridge.ClearPendingConsumptions();
				CraftTransaction.Clear();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")]
	internal static class AzuCraftingStateCleanupPatch
	{
		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuRuntimeBridge.ResetTransientCraftingState();
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class MachineOperationContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			HashSet<MethodBase> hashSet = new HashSet<MethodBase>();
			AddDeclaredMethods(hashSet, typeof(CookingStation), "OnAddFuelSwitch", "FindCookableItem");
			AddDeclaredMethods(hashSet, typeof(Fermenter), "GetHoverText", "FindCookableItem");
			AddDeclaredMethods(hashSet, typeof(Fireplace), "Interact", "GetHoverText");
			AddDeclaredMethods(hashSet, typeof(Smelter), "OnHoverAddOre", "OnHoverAddFuel", "OnAddOre", "OnAddFuel");
			AddDeclaredMethods(hashSet, typeof(ShieldGenerator), "OnHoverAddFuel", "OnAddFuel");
			AddDeclaredMethods(hashSet, typeof(Turret), "UseItem", "GetHoverText");
			return hashSet;
		}

		private static void AddDeclaredMethods(ISet<MethodBase> methods, Type type, params string[] methodNames)
		{
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				foreach (string text in methodNames)
				{
					if (declaredMethod.Name == text)
					{
						methods.Add(declaredMethod);
						break;
					}
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			if (Plugin.IsReady)
			{
				AzuCallContext.EnterMachine();
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuCallContext.ExitMachine();
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class AzuInfrastructureContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodBase[] array = new MethodBase[9]
			{
				AccessTools.Method(typeof(Container), "Awake", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Container), "OnDestroyed", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(WearNTear), "OnDestroy", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "UpdateTeleport", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(ObjectDB), "Awake", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "SetLocalPlayer", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "OnSpawned", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "OnRespawn", (Type[])null, (Type[])null)
			};
			MethodBase[] array2 = array;
			foreach (MethodBase methodBase in array2)
			{
				if (methodBase != null)
				{
					yield return methodBase;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			if (Plugin.IsReady)
			{
				AzuCallContext.EnterInfrastructure();
			}
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			if (!Plugin.IsReady)
			{
				return __exception;
			}
			AzuCallContext.ExitInfrastructure();
			return __exception;
		}
	}
	internal enum BaseModProfile
	{
		LegacyFork1818,
		Official1819
	}
	internal static class BaseModCompatibility
	{
		internal static BaseModProfile Resolve(AssemblyName assembly)
		{
			if (assembly != null && assembly.Name == "AzuCraftyBoxes")
			{
				if (assembly.Version == new Version(1, 8, 19, 0))
				{
					return BaseModProfile.Official1819;
				}
				if (assembly.Version == new Version(1, 8, 18, 0))
				{
					return BaseModProfile.LegacyFork1818;
				}
			}
			throw new NotSupportedException("No verified AzuCraftyBoxes compatibility profile exists for " + ((assembly == null) ? "an unknown assembly" : assembly.FullName) + ". This release supports official Azumatt AzuCraftyBoxes 1.8.19 and the legacy fedorovdgap 1.8.18 rebuild.");
		}

		internal static bool RequiresFermenterReplacement(AssemblyName assembly)
		{
			return Resolve(assembly) == BaseModProfile.LegacyFork1818;
		}
	}
	internal sealed class StartupSequence
	{
		private readonly Action<string> _error;

		internal bool Ready { get; private set; }

		internal string Stage { get; private set; } = "dependency health check";

		internal StartupSequence(Action<string> error)
		{
			_error = error;
		}

		internal void Step(string stage, Action action)
		{
			Stage = stage;
			action();
		}

		internal void Stop()
		{
			Ready = false;
		}

		internal bool TryOptionalCheck(string stage, Action check, Action<string> warning)
		{
			string stage2 = Stage;
			try
			{
				Step(stage, check);
				return true;
			}
			catch (Exception ex)
			{
				warning("Optional check failed during " + Stage + ". Required CraftyBoxes initialization will continue. Build Camera compatibility is not validated; this add-on has neither repaired nor disabled that camera. Keep a working compatible camera copy, or disable the incompatible camera in your mod manager. Original optional-check exception:\n" + ex);
				return false;
			}
			finally
			{
				Stage = stage2;
			}
		}

		internal bool Run(Action checkDependency, Action initialize, Func<bool> cleanup)
		{
			Ready = false;
			try
			{
				Step("dependency health check", checkDependency);
				initialize();
				Ready = true;
				Stage = "ready";
				return true;
			}
			catch (Exception ex)
			{
				_error("Add-on startup stopped during " + Stage + ". ORIGINAL EXCEPTION:\n" + ex);
				bool flag = false;
				try
				{
					flag = cleanup();
				}
				catch (Exception ex2)
				{
					_error("Separate cleanup failure; the original exception is above:\n" + ex2);
				}
				if (!flag)
				{
					_error("Cleanup could not be verified. The add-on is inactive; partial hooks may remain. Restart before playing.");
				}
				return false;
			}
		}
	}
	internal static class BaseModHealth
	{
		private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const string Explanation = "AzuCraftyBoxes did not establish a complete, supported startup. This add-on will not register ServerSync or install integration patches. Check the earlier AzuCraftyBoxes/ServerSync error in the client or server log. Use official Azumatt-AzuCraftyBoxes-1.8.19 (recommended), or the supported legacy fedorovdgap-AzuCraftyBoxes-1.8.18, never both. This add-on does not repair a failed base mod or treat a version registration as startup success.";

		internal static Assembly RequireInitialized()
		{
			try
			{
				if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuCraftyBoxes", out var value) || value == null || value.Instance == null)
				{
					throw new InvalidOperationException("The dependency has no loaded plug-in instance.");
				}
				Type type = ((object)value.Instance).GetType();
				if (type.FullName != "AzuCraftyBoxes.AzuCraftyBoxesPlugin")
				{
					throw new NotSupportedException("No startup-completion contract has been verified for " + type.Assembly.GetName()?.ToString() + ".");
				}
				BaseModCompatibility.Resolve(type.Assembly.GetName());
				RequireWatcher(type, value.Instance, "_watcher");
				RequireWatcher(type, value.Instance, "_yamlwatcher");
				object? value2 = RequiredField(type.Assembly.GetType("AzuCraftyBoxes.Patches.SE_ContainerPull", throwOnError: true), "SE_ContainerPulling").GetValue(null);
				StatusEffect val = (StatusEffect)((value2 is StatusEffect) ? value2 : null);
				if (val == null || val.m_name != "Preventing Pulling" || val.m_tooltip != "Prevents pulling from nearby containers & backpacks")
				{
					throw new InvalidOperationException("The final Awake status-effect initialization was not completed.");
				}
				RequireInstalledCallbacks(type.Assembly, "Azumatt.AzuCraftyBoxes", new string[8] { "ContainerAwakePatch", "ContainerLoadPatch", "PlayerHaveRequirementsPatch", "ConsumeResourcesPatch", "ConsumeLaterConsumptionItemsInventoryGuiDoCraftingPatch", "SmelterOnAddOrePatch", "SmelterOnAddFuelPatch", "SearchContainersAsWell" });
				return type.Assembly;
			}
			catch (Exception innerException)
			{
				throw new InvalidOperationException("AzuCraftyBoxes did not establish a complete, supported startup. This add-on will not register ServerSync or install integration patches. Check the earlier AzuCraftyBoxes/ServerSync error in the client or server log. Use official Azumatt-AzuCraftyBoxes-1.8.19 (recommended), or the supported legacy fedorovdgap-AzuCraftyBoxes-1.8.18, never both. This add-on does not repair a failed base mod or treat a version registration as startup success.", innerException);
			}
		}

		private static FieldInfo RequiredField(Type type, string name)
		{
			return type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.FullName, name);
		}

		private static void RequireWatcher(Type type, object instance, string name)
		{
			if (!(RequiredField(type, name).GetValue(instance) is FileSystemWatcher { EnableRaisingEvents: not false }))
			{
				throw new InvalidOperationException("Base startup marker " + name + " is absent or not running.");
			}
		}

		internal static void RequireInstalledCallbacks(Assembly assembly, string owner, IEnumerable<string> required)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (MethodBase allPatchedMethod in Harmony.GetAllPatchedMethods())
			{
				Patches patchInfo = Harmony.GetPatchInfo(allPatchedMethod);
				if (patchInfo == null)
				{
					continue;
				}
				List<Patch> list = new List<Patch>();
				list.AddRange(patchInfo.Prefixes);
				list.AddRange(patchInfo.Postfixes);
				list.AddRange(patchInfo.Transpilers);
				list.AddRange(patchInfo.Finalizers);
				foreach (Patch item in list)
				{
					if (item.owner == owner && item.PatchMethod.DeclaringType?.Assembly == assembly)
					{
						hashSet.Add(item.PatchMethod.DeclaringType.Name);
					}
				}
			}
			foreach (string item2 in required)
			{
				if (!hashSet.Contains(item2))
				{
					throw new InvalidOperationException("Required base callback " + item2 + " is not installed.");
				}
			}
		}
	}
	internal sealed class PatchSession
	{
		private readonly Harmony _harmony;

		private readonly StartupSequence _startup;

		private readonly Action<string> _error;

		private readonly List<MethodBase> _attempted = new List<MethodBase>();

		private bool _uncertainDetour;

		internal int InstalledCount { get; private set; }

		internal PatchSession(Harmony harmony, StartupSequence startup, Action<string> error)
		{
			_harmony = harmony;
			_startup = startup;
			_error = error;
		}

		internal void Apply(MethodBase target, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null, HarmonyMethod finalizer = null, HarmonyMethod ilmanipulator = null)
		{
			if (target == null)
			{
				throw new MissingMethodException("A required patch target was not found.");
			}
			_startup.Step("reading patch target " + Describe(target), delegate
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Expected O, but got Unknown
				DynamicMethodDefinition val = new DynamicMethodDefinition(target);
				try
				{
				}
				finally
				{
					((IDisposable)val)?.Dispose();
				}
			});
			_startup.Step("installing patch on " + Describe(target), delegate
			{
				if (!_attempted.Contains(target))
				{
					_attempted.Add(target);
				}
				try
				{
					if (_harmony.Patch(target, prefix, postfix, transpiler, finalizer, ilmanipulator) == null || !HasOwner(target))
					{
						throw new InvalidOperationException("Harmony did not confirm the installed patch.");
					}
					int installedCount = InstalledCount;
					InstalledCount = installedCount + 1;
				}
				catch
				{
					_uncertainDetour = true;
					throw;
				}
			});
		}

		internal void ApplyClass(Type patchType)
		{
			_startup.Step("resolving patch class " + patchType.FullName, delegate
			{
				ApplyResolvedClass(patchType);
			});
		}

		private void ApplyResolvedClass(Type type)
		{
			List<MethodBase> list = ResolveTargets(type);
			HarmonyMethod prefix = Callback<HarmonyPrefix>(type);
			HarmonyMethod postfix = Callback<HarmonyPostfix>(type);
			HarmonyMethod transpiler = Callback<HarmonyTranspiler>(type);
			HarmonyMethod finalizer = Callback<HarmonyFinalizer>(type);
			foreach (MethodBase item in list)
			{
				Apply(item, prefix, postfix, transpiler, finalizer);
			}
		}

		internal static List<MethodBase> ResolveTargets(Type type)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo method = type.GetMethod("TargetMethods", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo method2 = type.GetMethod("TargetMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			List<MethodBase> list = new List<MethodBase>();
			if (method != null)
			{
				list.AddRange((IEnumerable<MethodBase>)method.Invoke(null, null));
			}
			else if (method2 != null)
			{
				list.Add((MethodBase)method2.Invoke(null, null));
			}
			else
			{
				object[] customAttributes = type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true);
				for (int i = 0; i < customAttributes.Length; i++)
				{
					HarmonyMethod info = ((HarmonyAttribute)(HarmonyPatch)customAttributes[i]).info;
					if (info.declaringType != null && info.methodName != null)
					{
						list.Add((info.argumentTypes == null) ? info.declaringType.GetMethod(info.methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : info.declaringType.GetMethod(info.methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, info.argumentTypes, null));
					}
				}
			}
			if (list.Count == 0)
			{
				throw new MissingMethodException("No targets resolved for " + type.FullName);
			}
			if (list.Contains(null))
			{
				throw new MissingMethodException("A required target did not resolve for " + type.FullName);
			}
			return list;
		}

		private static HarmonyMethod Callback<T>(Type type) where T : Attribute
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.IsDefined(typeof(T), inherit: true))
				{
					return new HarmonyMethod(methodInfo);
				}
			}
			return null;
		}

		private bool HasOwner(MethodBase target)
		{
			Patches patchInfo = Harmony.GetPatchInfo(target);
			if (patchInfo == null)
			{
				return false;
			}
			return patchInfo.Owners.Contains(_harmony.Id);
		}

		internal static string Describe(MethodBase target)
		{
			return target.DeclaringType?.FullName + "." + target;
		}

		internal bool Cleanup()
		{
			bool flag = true;
			for (int num = _attempted.Count - 1; num >= 0; num--)
			{
				MethodBase methodBase = _attempted[num];
				try
				{
					_harmony.Unpatch(methodBase, (HarmonyPatchType)0, _harmony.Id);
					if (HasOwner(methodBase))
					{
						throw new InvalidOperationException("Our Harmony registration remains after unpatching.");
					}
				}
				catch (Exception ex)
				{
					flag = false;
					_error("Separate cleanup failure for " + Describe(methodBase) + "; original startup error is above:\n" + ex);
				}
			}
			if (_uncertainDetour)
			{
				flag = false;
				_error("A detour installation failed. Removing its Harmony registration alone cannot prove native rollback; a restart is required.");
			}
			if (flag)
			{
				_attempted.Clear();
			}
			return flag;
		}
	}
}