Decompiled source of AdventureBackpackCompatibility v0.1.8

plugins/AdventureBackpackCompatibility/AdventureBackpackCompatibility.dll

Decompiled 13 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using AdventureBackpacks.API;
using AdventureBackpacks.Components;
using AdventureBackpacks.Extensions;
using AdventureBackpacks.Features;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using ConditionalConfigSync;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RunicCrafting.Domain;
using UnityEngine;
using Vapok.Common.Managers;

[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("AdventureBackpackCompatibility")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.8.0")]
[assembly: AssemblyInformationalVersion("0.1.8")]
[assembly: AssemblyProduct("AdventureBackpackCompatibility")]
[assembly: AssemblyTitle("AdventureBackpackCompatibility")]
[assembly: AssemblyVersion("0.1.8.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 AdventureBackpackCompatibility
{
	internal sealed class BackpackAccess
	{
		internal sealed class Endpoint
		{
			private readonly BackpackAccess access;

			private readonly Player player;

			private readonly object bag;

			internal readonly Inventory Inventory;

			internal readonly ItemData Parent;

			internal readonly string Key;

			internal Endpoint(BackpackAccess access, Player player, object bag, Inventory inventory, ItemData parent, string key)
			{
				this.access = access;
				this.player = player;
				this.bag = bag;
				Inventory = inventory;
				Parent = parent;
				Key = key;
			}

			internal bool Valid()
			{
				Endpoint endpoint = access.Current(player);
				if (endpoint != null && endpoint.bag == bag && endpoint.Inventory == Inventory && endpoint.Parent == Parent)
				{
					return endpoint.Key == Key;
				}
				return false;
			}

			internal void Persist(string expected)
			{
				if (!Valid())
				{
					throw new InvalidOperationException("Equipped backpack changed during transfer.");
				}
				access.save.Invoke(bag, new object[1] { Inventory });
				if ((string)access.value.GetValue(bag) != expected || !Parent.m_customData.TryGetValue(Key, out var value) || value != expected)
				{
					throw new InvalidOperationException("Backpack material changes did not persist exactly.");
				}
			}

			internal void UpdateShadowParent(Inventory shadow, Inventory contents, string expected)
			{
				ItemData itemAt = shadow.GetItemAt(Parent.m_gridPos.x, Parent.m_gridPos.y);
				if (shadow == ((Humanoid)player).GetInventory() || contents == Inventory || itemAt == null || itemAt == Parent || (Object)(object)itemAt.m_dropPrefab != (Object)(object)Parent.m_dropPrefab || itemAt.m_quality != Parent.m_quality)
				{
					throw new InvalidOperationException("Backpack parent moved in the staged inventory.");
				}
				object obj = access.itemData.Invoke(null, new object[1] { itemAt });
				object obj2 = access.getComponent.Invoke(obj, new object[1] { "" });
				if (obj2 == null || obj2 == bag || access.item.GetValue(obj2) != itemAt || (string)access.key.GetValue(obj2) != Key || access.getInventory.Invoke(obj2, null) == Inventory)
				{
					throw new InvalidOperationException("Backpack shadow component is not isolated from the live item.");
				}
				access.save.Invoke(obj2, new object[1] { contents });
				if (access.getInventory.Invoke(obj2, null) != contents || (string)access.value.GetValue(obj2) != expected || !itemAt.m_customData.TryGetValue(Key, out var value) || value != expected)
				{
					throw new InvalidOperationException("Backpack shadow component did not retain the projected contents.");
				}
			}
		}

		internal const string Id = "vapok.mods.adventurebackpacks";

		internal const string Hash = "9F0B0FD4D1FE3B1406ECD39BEAA166C2E36F26CC012EA63812732F62AFA5BDA0";

		internal const string PreviousHash = "5773E28A4E8A7F27B839F1FBC4AB2272C5349E7E38D671F683950E30D47D4541";

		private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private readonly MethodInfo canCraft;

		private readonly MethodInfo equipped;

		private readonly MethodInfo getInventory;

		private readonly MethodInfo save;

		private readonly MethodInfo itemData;

		private readonly MethodInfo getComponent;

		private readonly FieldInfo open;

		private readonly FieldInfo loading;

		private readonly FieldInfo emptying;

		private readonly PropertyInfo alive;

		private readonly PropertyInfo item;

		private readonly PropertyInfo key;

		private readonly PropertyInfo value;

		private static MethodInfo Method(Type t, string n, Type[] args)
		{
			return t.GetMethod(n, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
		}

		private static FieldInfo Field(Type t, string n)
		{
			return t.GetField(n, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static PropertyInfo Property(Type t, string n)
		{
			while (t != null)
			{
				PropertyInfo property = t.GetProperty(n, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property;
				}
				t = t.BaseType;
			}
			return null;
		}

		internal BackpackAccess(Assembly assembly)
		{
			using (FileStream inputStream = File.OpenRead(assembly.Location))
			{
				using SHA256 sHA = SHA256.Create();
				string text = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "");
				if (text != "9F0B0FD4D1FE3B1406ECD39BEAA166C2E36F26CC012EA63812732F62AFA5BDA0" && text != "5773E28A4E8A7F27B839F1FBC4AB2272C5349E7E38D671F683950E30D47D4541")
				{
					throw new NotSupportedException("Unreviewed AdventureBackpacks binary.");
				}
			}
			Type type = assembly.GetType("AdventureBackpacks.Features.CraftFromBackpack", throwOnError: true);
			Type type2 = assembly.GetType("AdventureBackpacks.Extensions.PlayerExtensions", throwOnError: true);
			Type type3 = assembly.GetType("AdventureBackpacks.Components.BackpackComponent", throwOnError: true);
			itemData = Method(assembly.GetType("Vapok.Common.Managers.ItemExtensions", throwOnError: true), "Data", new Type[1] { typeof(ItemData) });
			getComponent = assembly.GetType("Vapok.Common.Managers.ItemInfo", throwOnError: true).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single((MethodInfo m) => m.Name == "Get" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string))
				.MakeGenericMethod(type3);
			canCraft = Method(type, "CanCraftFromBackpack", new Type[2]
			{
				typeof(Player),
				typeof(Inventory).MakeByRefType()
			});
			equipped = Method(type2, "GetEquippedBackpack", new Type[1] { typeof(Player) });
			getInventory = Method(type3, "GetInventory", Type.EmptyTypes);
			save = Method(type3, "Save", new Type[1] { typeof(Inventory) });
			open = Field(assembly.GetType("AdventureBackpacks.Patches.InventoryGuiPatches", throwOnError: true), "BackpackIsOpen");
			alive = Property(type3, "IsAlive");
			loading = Field(type3, "IsLoadingInventory");
			emptying = Field(type3, "IsEmptyingBackpack");
			item = Property(type3, "Item");
			key = Property(type3, "CustomDataKey");
			value = Property(type3, "Value");
			if (canCraft == null || equipped == null || getInventory == null || save == null || open == null || alive == null || loading == null || emptying == null || item == null || key == null || value == null || itemData == null || getComponent == null)
			{
				throw new MissingMemberException("Backpack material endpoint contract changed.");
			}
		}

		internal Endpoint Current(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner() || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || (bool)open.GetValue(null))
			{
				return null;
			}
			object obj = equipped.Invoke(null, new object[1] { player });
			Inventory val = (Inventory)((obj == null) ? null : /*isinst with value type is only supported in some contexts*/);
			if (obj == null || val == null || val == ((Humanoid)player).GetInventory() || val.GetAllItems().Count > 1024 || !(bool)alive.GetValue(obj) || (bool)loading.GetValue(obj) || (bool)emptying.GetValue(obj) || getInventory.Invoke(obj, null) != val)
			{
				return null;
			}
			object? obj2 = item.GetValue(obj);
			ItemData val2 = (ItemData)((obj2 is ItemData) ? obj2 : null);
			if (val2 == null || !((Humanoid)player).GetInventory().GetAllItems().Contains(val2))
			{
				return null;
			}
			return new Endpoint(this, player, obj, val, val2, (string)key.GetValue(obj));
		}
	}
	internal static class BuildingAdapter
	{
		private sealed class PreviewSource : IMutableMaterialSource
		{
			private readonly IMutableMaterialSource inner;

			public string SourceId => inner.SourceId;

			internal PreviewSource(IMutableMaterialSource inner)
			{
				this.inner = inner;
			}

			public MaterialSourceSnapshot Snapshot()
			{
				return inner.Snapshot();
			}

			public bool TryTake(string resource, int quantity, out IMaterialRestoreToken token)
			{
				token = null;
				return false;
			}
		}

		private static ConstructorInfo sourceConstructor;

		private static MethodInfo watch;

		private static MethodInfo count;

		private static MethodInfo resource;

		private static MethodInfo eligible;

		private static FieldInfo bagOpen;

		[ThreadStatic]
		private static Stack<bool> rows;

		private static readonly NativeCraftingScope nativeScope = new NativeCraftingScope(delegate
		{
			ABAPI.SuppressNativeCrafting("vercadi.AdventureBackpackCompatibility.Runic");
		}, delegate
		{
			ABAPI.UnsuppressNativeCrafting("vercadi.AdventureBackpackCompatibility.Runic");
		});

		internal static Action Install(Assembly runic)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Expected O, but got Unknown
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Expected O, but got Unknown
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Expected O, but got Unknown
			//IL_0328: Expected O, but got Unknown
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Expected O, but got Unknown
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0431: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Unknown result type (might be due to invalid IL or missing references)
			//IL_0460: Expected O, but got Unknown
			//IL_0460: Expected O, but got Unknown
			Harmony harmony = new Harmony("vercadi.AdventureBackpackCompatibility.Building");
			try
			{
				Type type = Type("ValheimMaterialSource");
				sourceConstructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[7]
				{
					typeof(string),
					typeof(Inventory),
					typeof(MaterialSourceKind),
					typeof(float),
					typeof(IEnumerable<string>),
					typeof(Func<bool>),
					typeof(Func<bool>)
				}, null) ?? throw new MissingMethodException("Runic material source constructor changed.");
				eligible = AccessTools.PropertyGetter(type, "IsEligible") ?? throw new MissingMethodException("Native source eligibility missing.");
				watch = AccessTools.DeclaredMethod(Type("UiPreviewCache"), "Watch", new Type[1] { typeof(object) }, (Type[])null);
				count = AccessTools.DeclaredMethod(Type("ValheimReflection"), "CountRequirementItems", new Type[2]
				{
					typeof(Inventory),
					typeof(string)
				}, (Type[])null);
				resource = AccessTools.DeclaredMethod(Type("ValheimReflection"), "ResourceId", new Type[1] { typeof(ItemDrop) }, (Type[])null);
				bagOpen = AccessTools.Field(typeof(CraftFromBackpack).Assembly.GetType("AdventureBackpacks.Patches.InventoryGuiPatches", throwOnError: true), "BackpackIsOpen");
				if (watch == null || count == null || resource == null || bagOpen == null)
				{
					throw new MissingMemberException("Backpack building reflection contract changed.");
				}
				MethodInfo methodInfo = AccessTools.DeclaredMethod(Type("ContainerQueryRuntime"), "ResolveSources", (Type[])null, (Type[])null);
				if (methodInfo == null || methodInfo.GetParameters().Length != 10 || methodInfo.ReturnType != typeof(IReadOnlyList<IMutableMaterialSource>))
				{
					throw new MissingMethodException("Runic source query changed.");
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(BuildingAdapter), "AddBackpack", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.DeclaredMethod(Type("CraftingRuntime"), "TryGetAvailabilityBreakdown", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(BuildingAdapter), "AddCarriedCount", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.DeclaredMethod(Type("CraftingRuntime"), "TryGetBuildAvailabilityBreakdown", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(BuildingAdapter), "AddCarriedCount", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null), new HarmonyMethod(typeof(BuildingAdapter), "BeginRow", (Type[])null)
				{
					priority = 800
				}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BuildingAdapter), "EndRow", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null);
				Type type2 = typeof(CraftFromBackpack).Assembly.GetType("AdventureBackpacks.Patches.InventoryGuiPatches+InventoryGuiSetupRequirementPatch", throwOnError: true);
				harmony.Patch((MethodBase)AccessTools.DeclaredMethod(type2, "Postfix", (Type[])null, (Type[])null), new HarmonyMethod(typeof(BuildingAdapter), "AllowNativeBagRow", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				string[] array = new string[11]
				{
					"BeforeCraft", "AfterCraftCapacityCheck", "BeforePlacePiece", "BeforeHaveRecipeRequirements", "AddNearbyRecipeAvailability", "AddNearbyPieceAvailability", "BeforeVanillaConsume", "TryGetAvailabilityBreakdown", "TryGetBuildAvailabilityBreakdown", "FinishCraft",
					"FinishPlacementScope"
				};
				foreach (string text in array)
				{
					MethodInfo methodInfo2 = AccessTools.DeclaredMethod(Type("CraftingRuntime"), text, (Type[])null, (Type[])null) ?? throw new MissingMethodException("Runic material entry point changed: " + text);
					harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(BuildingAdapter), "BeginNativeScope", (Type[])null)
					{
						priority = 800
					}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BuildingAdapter), "EndNativeScope", (Type[])null)
					{
						priority = 0
					}, (HarmonyMethod)null);
				}
				return delegate
				{
					nativeScope.Reset();
					harmony.UnpatchSelf();
				};
			}
			catch
			{
				nativeScope.Reset();
				harmony.UnpatchSelf();
				throw;
			}
			Type Type(string name)
			{
				return runic.GetType("RunicCrafting.Integration." + name, throwOnError: true);
			}
		}

		private static void BeginNativeScope(out bool __state)
		{
			__state = false;
			if (Plugin.BuildingEnabled)
			{
				nativeScope.Enter();
				__state = true;
			}
		}

		private static void EndNativeScope(bool __state)
		{
			if (__state)
			{
				nativeScope.Exit();
			}
		}

		private static bool CurrentBag(Player player, out BackpackComponent bag, out Inventory inventory)
		{
			bag = null;
			inventory = null;
			if (!Plugin.BuildingEnabled || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !((Character)player).IsOwner() || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || (bool)bagOpen.GetValue(null) || !CraftFromBackpack.CanCraftFromBackpack(player, ref inventory))
			{
				return false;
			}
			bag = PlayerExtensions.GetEquippedBackpack(player);
			if (bag != null && ((CustomItemData)bag).IsAlive && !bag.IsLoadingInventory && !bag.IsEmptyingBackpack && bag.GetInventory() == inventory && inventory != ((Humanoid)player).GetInventory() && ((Humanoid)player).GetInventory().GetAllItems().Contains(((CustomItemData)bag).Item))
			{
				return inventory.GetAllItems().Count <= 128;
			}
			return false;
		}

		private static bool BuildingQuery(string purpose)
		{
			if (!(purpose == "runic.crafting.build") && !(purpose == "runic.crafting.build-ui"))
			{
				return purpose == "runic.crafting.build-preview";
			}
			return true;
		}

		private static bool CraftingQuery(string purpose)
		{
			if (!(purpose == "runic.crafting.recipe") && !(purpose == "runic.crafting.ui"))
			{
				return purpose == "runic.crafting.recipe-preview";
			}
			return true;
		}

		private static bool MaterialQuery(string purpose)
		{
			if (!BuildingQuery(purpose))
			{
				return CraftingQuery(purpose);
			}
			return true;
		}

		private static void AddBackpack(Player player, IEnumerable<MaterialRequirement> requirements, string purposeId, string reasonCode, bool requireWritable, ref IReadOnlyList<IMutableMaterialSource> __result)
		{
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			if (!MaterialQuery(purposeId) || (reasonCode != "guarded-native-ownership" && reasonCode != "read-only-preview") || __result == null)
			{
				return;
			}
			BackpackComponent bag;
			Inventory inventory;
			IMutableMaterialSource nativePlayer;
			try
			{
				if (!CurrentBag(player, out bag, out inventory))
				{
					return;
				}
				string id = "vercadi.backpack:" + player.GetPlayerID();
				if (__result.Any((IMutableMaterialSource s) => s.SourceId == id) || __result.Count >= 128)
				{
					return;
				}
				string[] array = requirements.Select((MaterialRequirement r) => r.ResourceId).Distinct<string>(StringComparer.Ordinal).Take(129)
					.ToArray();
				if (array.Length > 128)
				{
					return;
				}
				nativePlayer = ((IEnumerable<IMutableMaterialSource>)__result).FirstOrDefault((Func<IMutableMaterialSource, bool>)((IMutableMaterialSource s) => s.SourceId.StartsWith("valheim.player:", StringComparison.Ordinal)));
				if (requireWritable && (nativePlayer == null || !eligible.DeclaringType.IsInstanceOfType(nativePlayer)))
				{
					return;
				}
				IMutableMaterialSource inner = (IMutableMaterialSource)sourceConstructor.Invoke(new object[7]
				{
					id,
					inventory,
					(object)(MaterialSourceKind)0,
					0f,
					array,
					new Func<bool>(IsEligible),
					new Func<bool>(IsEligible)
				});
				IMutableMaterialSource obj;
				if (!requireWritable)
				{
					IMutableMaterialSource val = (IMutableMaterialSource)(object)new PreviewSource(inner);
					obj = val;
				}
				else
				{
					IMutableMaterialSource val = (IMutableMaterialSource)(object)new PersistedMaterialSource(inner, delegate
					{
						Persist(bag, inventory);
					}, Plugin.BuildingFailed);
					obj = val;
				}
				IMutableMaterialSource val2 = obj;
				watch.Invoke(null, new object[1] { inventory });
				__result = __result.Concat((IEnumerable<IMutableMaterialSource>)(object)new IMutableMaterialSource[1] { val2 }).ToArray();
			}
			catch (Exception error)
			{
				Plugin.BuildingFailed(error);
			}
			bool IsEligible()
			{
				if (CurrentBag(player, out var bag2, out var inventory2) && bag2 == bag && inventory2 == inventory)
				{
					if (requireWritable)
					{
						return (bool)eligible.Invoke(nativePlayer, null);
					}
					return true;
				}
				return false;
			}
		}

		private static void Persist(BackpackComponent bag, Inventory inventory)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if (!((CustomItemData)bag).IsAlive || bag.IsLoadingInventory || bag.IsEmptyingBackpack || bag.GetInventory() != inventory)
			{
				throw new InvalidOperationException("Backpack changed during material transaction.");
			}
			ZPackage val = new ZPackage();
			inventory.Save(val);
			string @base = val.GetBase64();
			bag.Save(inventory);
			if (((CustomItemData)bag).Value != @base || !((CustomItemData)bag).Item.m_customData.TryGetValue(((CustomItemData)bag).CustomDataKey, out var value) || value != @base)
			{
				throw new InvalidOperationException("Backpack crafting/building materials were not persisted correctly.");
			}
		}

		private static void AddCarriedCount(Player player, Requirement requirement, ref int carried, NearbyCraftingFeatureState state, bool __result)
		{
			if (!__result || !state.IsReady || (Object)(object)requirement?.m_resItem == (Object)null)
			{
				return;
			}
			try
			{
				if (CurrentBag(player, out var _, out var inventory))
				{
					string text = (string)resource.Invoke(null, new object[1] { requirement.m_resItem });
					int num = (int)count.Invoke(null, new object[2] { inventory, text });
					carried = (int)Math.Min(2147483647L, (long)carried + (long)num);
					watch.Invoke(null, new object[1] { inventory });
					if (rows != null && rows.Count > 0)
					{
						rows.Pop();
						rows.Push(item: true);
					}
				}
			}
			catch (Exception error)
			{
				Plugin.BuildingFailed(error);
			}
		}

		private static void BeginRow()
		{
			if (rows == null)
			{
				rows = new Stack<bool>();
			}
			rows.Push(item: false);
		}

		private static void EndRow()
		{
			if (rows != null && rows.Count > 0)
			{
				rows.Pop();
			}
		}

		private static bool AllowNativeBagRow(bool craft)
		{
			if (rows != null && rows.Count != 0)
			{
				return !rows.Peek();
			}
			return true;
		}
	}
	internal static class CheckedTransfer
	{
		internal static void Match(string[] actual, string[] expected)
		{
			if (!actual.SequenceEqual<string>(expected, StringComparer.Ordinal))
			{
				throw new InvalidOperationException("Inventory changed outside the planned material transfer.");
			}
		}

		internal static void Run(IReadOnlyList<Action> steps, IReadOnlyList<string[]> checkpoints, Func<string[]> read, Action verify, Action<string[]> recover, Action quarantine)
		{
			if (checkpoints.Count != steps.Count + 1)
			{
				throw new ArgumentException("Incomplete transfer plan.");
			}
			Match(read(), checkpoints[0]);
			int num = 0;
			try
			{
				for (int i = 0; i < steps.Count; i++)
				{
					Match(read(), checkpoints[i]);
					num = i + 1;
					steps[i]();
					Match(read(), checkpoints[i + 1]);
				}
				verify();
				Match(read(), checkpoints[checkpoints.Count - 1]);
			}
			catch (Exception item)
			{
				List<Exception> list = new List<Exception> { item };
				try
				{
					string[] current = read();
					if (!checkpoints.Take(num + 1).Any((string[] c) => current.SequenceEqual<string>(c, StringComparer.Ordinal)))
					{
						throw new InvalidOperationException("Recovery would overwrite an unrecognized inventory change; nothing restored.");
					}
					if (!current.SequenceEqual<string>(checkpoints[0], StringComparer.Ordinal))
					{
						recover(current);
					}
					Match(read(), checkpoints[0]);
				}
				catch (Exception item2)
				{
					list.Add(item2);
					try
					{
						quarantine();
					}
					catch (Exception item3)
					{
						list.Add(item3);
					}
				}
				throw new AggregateException((list.Count == 1) ? "Material transfer stopped; original inventories verified. No automatic retry." : "Material transfer stopped; recovery required. Unknown contents preserved. No automatic retry.", list);
			}
		}
	}
	internal sealed class NativeCraftingScope
	{
		private readonly Action suppress;

		private readonly Action release;

		private int depth;

		internal NativeCraftingScope(Action suppress, Action release)
		{
			this.suppress = suppress;
			this.release = release;
		}

		internal void Enter()
		{
			if (depth == 0)
			{
				suppress();
			}
			depth++;
		}

		internal void Exit()
		{
			if (depth != 0 && --depth == 0)
			{
				release();
			}
		}

		internal void Reset()
		{
			if (depth != 0)
			{
				depth = 0;
				release();
			}
		}
	}
	internal sealed class PersistedMaterialSource : IMutableMaterialSource
	{
		private sealed class RestoreToken : IMaterialRestoreToken
		{
			private readonly IMaterialRestoreToken inner;

			private readonly Action persist;

			private readonly Action<Exception> fault;

			private bool done;

			internal RestoreToken(IMaterialRestoreToken inner, Action persist, Action<Exception> fault)
			{
				this.inner = inner;
				this.persist = persist;
				this.fault = fault;
			}

			public bool Restore()
			{
				if (done)
				{
					return true;
				}
				try
				{
					if (!inner.Restore())
					{
						throw new InvalidOperationException("RunicCrafting could not restore backpack materials.");
					}
					persist();
					done = true;
					return true;
				}
				catch (Exception obj)
				{
					fault(obj);
					return false;
				}
			}
		}

		private readonly IMutableMaterialSource inner;

		private readonly Action persist;

		private readonly Action<Exception> fault;

		public string SourceId => inner.SourceId;

		internal PersistedMaterialSource(IMutableMaterialSource inner, Action persist, Action<Exception> fault)
		{
			this.inner = inner;
			this.persist = persist;
			this.fault = fault;
		}

		public MaterialSourceSnapshot Snapshot()
		{
			return inner.Snapshot();
		}

		public bool TryTake(string resourceId, int quantity, out IMaterialRestoreToken restoreToken)
		{
			restoreToken = null;
			IMaterialRestoreToken val = null;
			try
			{
				if (!inner.TryTake(resourceId, quantity, ref val))
				{
					return false;
				}
				if (val == null)
				{
					throw new InvalidOperationException("Material source returned no restore token.");
				}
				persist();
				restoreToken = (IMaterialRestoreToken)(object)new RestoreToken(val, persist, fault);
				return true;
			}
			catch (Exception innerException)
			{
				bool flag = val != null && new RestoreToken(val, persist, fault).Restore();
				InvalidOperationException ex = new InvalidOperationException("Backpack material reservation failed; rollback " + (flag ? "verified." : "could not be verified."), innerException);
				fault(ex);
				throw ex;
			}
		}
	}
	[BepInPlugin("vercadi.AdventureBackpackCompatibility", "Adventure Backpack Compatibility", "0.1.8")]
	[BepInDependency("vapok.mods.adventurebackpacks", "2.2.1")]
	[BepInDependency("chazman.RunicStorage", "1.3.12")]
	[BepInDependency("_shudnal.ConditionalConfigSync", "1.0.9")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "vercadi.AdventureBackpackCompatibility";

		public const string Version = "0.1.8";

		internal const string PortalId = "ArgusMagnus.ServersideQoL.PortalProgression";

		internal const string BackpackHash = "9F0B0FD4D1FE3B1406ECD39BEAA166C2E36F26CC012EA63812732F62AFA5BDA0";

		internal const string StorageHash = "C2EEE73F58A2F1AF7C19092FBE02AAC5D58A39BB92CA305997929F6AD0ACFEE2";

		internal const string PortalHash = "751CF513FCEA4593C7F363F5A401E1E9A22F93582FD72F567DC08AC52E9601CF";

		private static Plugin instance;

		private Harmony harmony;

		private ConfigSync sync;

		private CustomSyncedValue<string> portalItems;

		private ConfigEntry<bool> portals;

		private ConfigEntry<bool> quickStack;

		private ConfigEntry<bool> building;

		private Action removeBuilding;

		private bool buildingFault;

		private HashSet<string> allowed = new HashSet<string>(StringComparer.Ordinal);

		private RunicAdapter storage;

		private bool ready;

		private bool storageFault;

		private bool busy;

		private bool policyWarning;

		private bool portalWarning;

		private bool policyWaiting;

		private string loggedPolicy;

		private float nextPolicy;

		private Assembly portalAssembly;

		private Type portalConfigType;

		internal static bool BuildingEnabled
		{
			get
			{
				if (Object.op_Implicit((Object)(object)instance) && instance.ready && instance.Synced && instance.building.Value)
				{
					return !instance.buildingFault;
				}
				return false;
			}
		}

		private bool Synced
		{
			get
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance))
				{
					if (!ZNet.instance.IsServer())
					{
						if (((ConditionalConfigSync)sync).InitialSyncDone)
						{
							return !((ConditionalConfigSync)sync).IsSourceOfTruth;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Expected O, but got Unknown
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Expected O, but got Unknown
			//IL_0245: Expected O, but got Unknown
			instance = this;
			portals = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Backpack portal progression", true, "Use the server's enabled PortalProgression item rules and world boss keys for carried AdventureBackpacks. Unknown and locked items stay restricted.");
			quickStack = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Quick stack equipped backpack", true, "After RunicStorage Quick Stack, also route matching items from the equipped AdventureBackpack. Uses RunicStorage range, rules and ownership checks. Backpack must be closed.");
			building = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Build from equipped backpack", true, "Include the equipped, closed AdventureBackpack in reviewed RunicCrafting crafting, upgrading and hammer-building checks and exact consumption. The legacy setting name is retained for configuration compatibility.");
			sync = new ConfigSync("vercadi.AdventureBackpackCompatibility")
			{
				DisplayName = "Adventure Backpack Compatibility",
				CurrentVersion = "0.1.8",
				MinimumRequiredVersion = "0.1.8",
				ModRequired = true
			};
			((ConditionalConfigSync)sync).AddLockingConfigEntry<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock server settings", true, "Only server administrators may change shared settings."));
			((ConditionalConfigSync)sync).AddConfigEntry<bool>(portals, (ConfigSyncMode)0);
			((ConditionalConfigSync)sync).AddConfigEntry<bool>(quickStack, (ConfigSyncMode)0);
			((ConditionalConfigSync)sync).AddConfigEntry<bool>(building, (ConfigSyncMode)0);
			portalItems = new CustomSyncedValue<string>((ConditionalConfigSync)(object)sync, "Eligible backpack portal items", "", 0, (IEqualityComparer<string>)null);
			((CustomSyncedValueBase)portalItems).ValueChanged += delegate
			{
				allowed = Rules.Decode(portalItems.Value);
			};
			try
			{
				Verify(((object)Chainloader.PluginInfos["vapok.mods.adventurebackpacks"].Instance).GetType().Assembly, "9F0B0FD4D1FE3B1406ECD39BEAA166C2E36F26CC012EA63812732F62AFA5BDA0");
				Assembly assembly = ((object)Chainloader.PluginInfos["chazman.RunicStorage"].Instance).GetType().Assembly;
				Verify(assembly, "C2EEE73F58A2F1AF7C19092FBE02AAC5D58A39BB92CA305997929F6AD0ACFEE2");
				storage = new RunicAdapter(assembly);
				harmony = new Harmony("vercadi.AdventureBackpackCompatibility");
				harmony.Patch((MethodBase)AccessTools.Method(typeof(Inventory), "IsTeleportable", new Type[1] { typeof(bool) }, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "TeleportTranspiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)storage.QuickStackMethod, new HarmonyMethod(typeof(Plugin), "BeforeQuickStack", (Type[])null)
				{
					priority = 800
				}, new HarmonyMethod(typeof(Plugin), "AfterQuickStack", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				ready = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"ACTIVE: reviewed AdventureBackpacks 2.2.1 / RunicStorage 1.3.12 adapters. Portal eligibility waits for server policy.");
			}
			catch (Exception ex)
			{
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("INACTIVE: compatibility contracts failed; original mod behavior retained. " + ex));
			}
			if (ready && Chainloader.PluginInfos.TryGetValue("chazman.RunicCrafting", out var value))
			{
				try
				{
					Verify(((object)value.Instance).GetType().Assembly, "3D98A550F9A160F796AD3BDA3E9C47DE2E26211FB1449F53AB511FF883D041B0");
					Type type = typeof(Plugin).Assembly.GetType("AdventureBackpackCompatibility.BuildingAdapter", throwOnError: true);
					removeBuilding = (Action)type.GetMethod("Install", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[1] { ((object)value.Instance).GetType().Assembly });
					((BaseUnityPlugin)this).Logger.LogInfo((object)"ACTIVE: RunicCrafting 1.1.11 equipped-backpack crafting/building integration; exact material reservation and rollback enabled.");
				}
				catch (Exception ex2)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Backpack crafting/building integration not applied; existing portal and stacking adapters remain active. " + ex2));
				}
			}
		}

		internal static void BuildingFailed(Exception error)
		{
			if (Object.op_Implicit((Object)(object)instance) && !instance.buildingFault)
			{
				instance.buildingFault = true;
				((BaseUnityPlugin)instance).Logger.LogError((object)("Backpack crafting/building stopped for this session after a material error. " + error));
			}
		}

		internal static void Verify(Assembly assembly, string hash)
		{
			using FileStream inputStream = File.OpenRead(assembly.Location);
			using SHA256 sHA = SHA256.Create();
			if (BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "") != hash)
			{
				throw new NotSupportedException("Unreviewed dependency binary: " + assembly.GetName().Name);
			}
		}

		private void Update()
		{
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			if (!ready || Time.realtimeSinceStartup < nextPolicy)
			{
				return;
			}
			nextPolicy = Time.realtimeSinceStartup + 1f;
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				List<string> list = new List<string>();
				if (portals.Value && Object.op_Implicit((Object)(object)ZoneSystem.instance) && Object.op_Implicit((Object)(object)ObjectDB.instance) && Chainloader.PluginInfos.TryGetValue("ArgusMagnus.ServersideQoL.PortalProgression", out var value))
				{
					Assembly assembly = ((object)value.Instance).GetType().Assembly;
					if (portalAssembly != assembly)
					{
						Verify(assembly, "751CF513FCEA4593C7F363F5A401E1E9A22F93582FD72F567DC08AC52E9601CF");
						portalConfigType = assembly.GetType("ServersideQoL.PortalProgression.Config", throwOnError: true);
						portalAssembly = assembly;
					}
					object value2 = AccessTools.Property(portalConfigType, "Instance").GetValue(null, null);
					bool flag = value2 != null && ((ConfigEntry<bool>)AccessTools.Property(portalConfigType, "Enabled").GetValue(value2, null)).Value;
					ConfigEntry<bool> val = default(ConfigEntry<bool>);
					flag &= Chainloader.PluginInfos.TryGetValue("ArgusMagnus.ServersideQoL", out var value3) && value3.Instance.Config.TryGetEntry<bool>(new ConfigDefinition("General", "Enabled"), ref val) && val.Value;
					if (flag)
					{
						IEnumerable obj = (IEnumerable)AccessTools.Property(portalConfigType, "Entries").GetValue(value2, null);
						int num = 0;
						foreach (object item in obj)
						{
							if (++num > 512)
							{
								throw new NotSupportedException("Portal rule bound exceeded.");
							}
							Type type = item.GetType();
							ItemDrop val2 = (ItemDrop)AccessTools.Property(type, "ItemDrop").GetValue(item, null);
							string value4 = ((ConfigEntry<string>)AccessTools.Property(type, "Config").GetValue(item, null)).Value;
							if (Object.op_Implicit((Object)(object)val2) && Rules.Unlocked(flag, value4, (Func<string, bool>)ZoneSystem.instance.GetGlobalKey))
							{
								list.Add(Rules.Prefab(((Object)((Component)val2).gameObject).name));
							}
						}
					}
				}
				string text = Rules.Encode(list);
				portalItems.AssignLocalValueIfChanged(text);
				if (text != loggedPolicy)
				{
					loggedPolicy = text;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("SERVER POLICY READY: " + list.Count + " backpack item prefabs permitted by the current server world-boss rules."));
				}
				policyWaiting = false;
			}
			catch (TargetInvocationException ex) when (ex.InnerException is InvalidOperationException && ex.InnerException.Message == "Config has not been initialized yet")
			{
				portalItems.AssignLocalValueIfChanged("");
				if (!policyWaiting)
				{
					policyWaiting = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Waiting for ServersideQoL portal configuration to initialize; bag restrictions remain in force.");
				}
			}
			catch (Exception ex2)
			{
				portalItems.AssignLocalValueIfChanged("");
				if (!policyWarning)
				{
					policyWarning = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Portal policy unavailable: bag restrictions retained. " + ex2));
				}
			}
		}

		internal static IEnumerable<CodeInstruction> TeleportTranspiler(IEnumerable<CodeInstruction> source)
		{
			List<CodeInstruction> code = source.ToList();
			FieldInfo shared = typeof(ItemData).GetField("m_shared");
			FieldInfo flag = typeof(SharedData).GetField("m_teleportable");
			int[] array = (from i in Enumerable.Range(0, Math.Max(0, code.Count - 1))
				where code[i].opcode == OpCodes.Ldfld && object.Equals(code[i].operand, shared) && code[i + 1].opcode == OpCodes.Ldfld && object.Equals(code[i + 1].operand, flag)
				select i).ToArray();
			if (array.Length != 1)
			{
				throw new NotSupportedException("Expected one item teleportability field pair.");
			}
			int num = array[0];
			code[num].opcode = OpCodes.Ldarg_0;
			code[num].operand = null;
			code[num + 1].opcode = OpCodes.Call;
			code[num + 1].operand = typeof(Plugin).GetMethod("ItemTeleportable", BindingFlags.Static | BindingFlags.NonPublic);
			return code;
		}

		private static bool ItemTeleportable(ItemData item, Inventory inventory)
		{
			bool valueOrDefault = item?.m_shared?.m_teleportable == true;
			if (valueOrDefault || item?.m_shared == null || (Object)(object)instance == (Object)null || !instance.ready || !instance.portals.Value || !instance.Synced || !Object.op_Implicit((Object)(object)item.m_dropPrefab))
			{
				return valueOrDefault;
			}
			try
			{
				string text = Rules.Prefab(((Object)item.m_dropPrefab).name);
				if (!instance.allowed.Contains(text))
				{
					return valueOrDefault;
				}
				return Rules.AllowItem(valueOrDefault, ready: true, IsCarriedBackpack(inventory), text, instance.allowed);
			}
			catch (Exception ex)
			{
				if (!instance.portalWarning)
				{
					instance.portalWarning = true;
					((BaseUnityPlugin)instance).Logger.LogWarning((object)("Backpack identity unavailable; original teleport restriction retained. " + ex));
				}
				return valueOrDefault;
			}
		}

		private static bool IsCarriedBackpack(Inventory inventory)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || !((Character)localPlayer).IsOwner() || inventory == null || inventory == ((Humanoid)localPlayer).GetInventory())
			{
				return false;
			}
			if (ABAPI.GetEquippedBackpackInventory(localPlayer) == inventory)
			{
				return true;
			}
			List<ItemData> allItems = ((Humanoid)localPlayer).GetInventory().GetAllItems();
			if (allItems.Count > 128)
			{
				return false;
			}
			foreach (ItemData item in allItems)
			{
				if (ABAPI.IsBackpack(item) && ABAPI.GetBackpackInventory(item) == inventory)
				{
					return true;
				}
			}
			return false;
		}

		private static void BeforeQuickStack(object __instance, out bool __state)
		{
			__state = Object.op_Implicit((Object)(object)instance) && instance.ready && instance.storage.IsWholeInventoryQuickStack(__instance);
		}

		private static void AfterQuickStack(object __instance, bool __state)
		{
			if (!__state)
			{
				return;
			}
			Plugin plugin = instance;
			if (!Object.op_Implicit((Object)(object)plugin) || !plugin.ready || !plugin.Synced || !plugin.quickStack.Value || plugin.storageFault || plugin.busy)
			{
				return;
			}
			plugin.busy = true;
			try
			{
				int num = plugin.storage.Stack(__instance);
				if (num > 0)
				{
					((Character)Player.m_localPlayer).Message((MessageType)1, "Backpack: stashed " + num + " item(s).", 0, (Sprite)null, false);
					((BaseUnityPlugin)plugin).Logger.LogInfo((object)("Backpack quick-stack committed: moved=" + num + "; bag persistence verified."));
				}
			}
			catch (Exception ex)
			{
				plugin.storageFault = true;
				try
				{
					string text = plugin.storage.RecoveryRecord();
					if (text != null)
					{
						string text2 = Path.Combine(Paths.ConfigPath, "AdventureBackpackCompatibility-recovery");
						Directory.CreateDirectory(text2);
						File.WriteAllText(Path.Combine(text2, DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fffffff") + ".txt"), text);
					}
				}
				catch (Exception ex2)
				{
					((BaseUnityPlugin)plugin).Logger.LogError((object)("Could not write recovery evidence: " + ex2));
				}
				((BaseUnityPlugin)plugin).Logger.LogError((object)("Backpack quick-stack stopped for this session after a transfer error; no automatic retry. " + ex));
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, "Backpack stacking stopped. Check the log before trying again.", 0, (Sprite)null, false);
				}
			}
			finally
			{
				plugin.busy = false;
			}
		}

		private void OnDestroy()
		{
			ready = false;
			removeBuilding?.Invoke();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
		}
	}
	internal static class Rules
	{
		internal const int MaximumStacks = 128;

		internal const int MaximumRules = 512;

		internal static string Prefab(string value)
		{
			string text;
			if (value == null || !value.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				text = value;
				if (text == null)
				{
					return "";
				}
			}
			else
			{
				text = value.Substring(0, value.Length - 7);
			}
			return text;
		}

		internal static bool Unlocked(bool enabled, string key, Func<string, bool> worldKey)
		{
			if (enabled && !string.IsNullOrEmpty(key) && key != "--")
			{
				return worldKey(key);
			}
			return false;
		}

		internal static string Encode(IEnumerable<string> names)
		{
			return "v1\n" + string.Join("\n", names.Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string x) => x, StringComparer.Ordinal));
		}

		internal static HashSet<string> Decode(string value)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (value == null || value.Length > 65536 || !value.StartsWith("v1\n", StringComparison.Ordinal))
			{
				return hashSet;
			}
			string[] array = value.Substring(3).Split(new char[1] { '\n' });
			if (array.Length > 512)
			{
				return hashSet;
			}
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (text.Length != 0)
				{
					if (text.Length > 256 || text.Any(char.IsControl))
					{
						return new HashSet<string>(StringComparer.Ordinal);
					}
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		internal static bool AllowItem(bool original, bool ready, bool carriedBag, string prefab, HashSet<string> allowed)
		{
			if (!original)
			{
				if (ready && carriedBag)
				{
					return allowed.Contains(prefab);
				}
				return false;
			}
			return true;
		}

		internal static bool KeepItem(bool equipped, bool quest, bool backpack, int protection)
		{
			if (!(equipped || quest || backpack))
			{
				if (protection != 0)
				{
					return protection != 1;
				}
				return false;
			}
			return true;
		}
	}
	internal sealed class RunicAdapter
	{
		private sealed class Destination
		{
			internal Container Chest;

			internal Inventory Inventory;

			internal object Rules;
		}

		internal readonly MethodInfo QuickStackMethod;

		private readonly FieldInfo retry;

		private readonly FieldInfo singleStackInventory;

		private readonly FieldInfo backpackOpen;

		private readonly MethodInfo begin;

		private readonly MethodInfo nearby;

		private readonly MethodInfo acquire;

		private readonly MethodInfo eligible;

		private readonly MethodInfo learn;

		private readonly MethodInfo readRules;

		private readonly MethodInfo score;

		private readonly MethodInfo facts;

		private readonly MethodInfo resource;

		private readonly MethodInfo contains;

		private readonly MethodInfo protection;

		private readonly MethodInfo stateAt;

		private readonly MethodInfo isBlocked;

		private readonly MethodInfo move;

		private readonly MethodInfo restore;

		private readonly MethodInfo save;

		private readonly MethodInfo canDiscover;

		private readonly MethodInfo shadow;

		private readonly MethodInfo block;

		private readonly MethodInfo captureAuthority;

		private readonly ConstructorInfo snapshot;

		private readonly BackpackAccess bagAccess = new BackpackAccess(typeof(BackpackComponent).Assembly);

		private Func<string> recoveryRecord;

		private static MethodInfo Method(Type type, string name, int arguments)
		{
			return type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single((MethodInfo x) => x.Name == name && x.GetParameters().Length == arguments);
		}

		private static object Call(MethodInfo method, object target, params object[] args)
		{
			return method.Invoke(target, args);
		}

		internal RunicAdapter(Assembly assembly)
		{
			Assembly assembly2 = assembly;
			base..ctor();
			backpackOpen = typeof(ABAPI).Assembly.GetType("AdventureBackpacks.Patches.InventoryGuiPatches", throwOnError: true).GetField("BackpackIsOpen", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException("Backpack UI state unavailable.");
			Type type = Get("RunicStorage.Runtime.StorageActions");
			Type type2 = Get("RunicStorage.Runtime.ValheimContainerService");
			Type type3 = Get("RunicStorage.Runtime.ChestRuleStore");
			QuickStackMethod = Method(type, "ExecuteQuickStack", 0);
			retry = type.GetField("_quickStackRetryPending", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.FullName, "_quickStackRetryPending");
			singleStackInventory = type.GetField("_singleStackInventory", BindingFlags.Instance | BindingFlags.NonPublic);
			if (singleStackInventory == null || singleStackInventory.FieldType != typeof(Inventory))
			{
				throw new MissingFieldException(type.FullName, "_singleStackInventory");
			}
			begin = Method(type, "TryBeginMutation", 4);
			nearby = Method(type, "Nearby", 4);
			contains = Method(type, "ContainsResource", 2);
			acquire = Method(Get("RunicStorage.Runtime.StorageContainerAuthority"), "TryGetSynchronizedServerInventory", 2);
			eligible = Method(type3, "Eligible", 1);
			learn = Method(type3, "Learn", 1);
			readRules = Method(type3, "Read", 2);
			score = Method(Get("RunicStorage.Engine.ChestRules"), "RoutingScore", 2);
			facts = Method(Get("RunicStorage.Runtime.ChestGroupRuntime"), "Facts", 1);
			resource = Method(Get("RunicStorage.Runtime.ValheimContainerIdentity"), "ResourceId", 1);
			protection = Method(Get("RunicStorage.Engine.StorageItemProtection"), "TryCapture", 4).MakeGenericMethod(typeof(ItemData));
			stateAt = Method(Get("RunicStorage.Engine.StorageProtectionSnapshot"), "StateAt", 1);
			move = Method(type2, "MoveUpTo", 9);
			restore = Method(type2, "RestoreInventory", 4);
			isBlocked = Method(assembly2.GetType("RunicAutomation.MutationGate", throwOnError: true), "IsBlocked", 1);
			block = Method(assembly2.GetType("RunicAutomation.MutationGate", throwOnError: true), "Block", 1);
			captureAuthority = Method(Get("RunicStorage.Runtime.StorageContainerAuthority"), "CaptureAuthority", 1);
			save = Method(type2, "SaveInventory", 1);
			canDiscover = Method(type2, "CanDiscover", 4);
			snapshot = Get("RunicStorage.Runtime.StorageInventorySnapshot").GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Inventory) }, null) ?? throw new MissingMethodException("Storage snapshot constructor unavailable.");
			shadow = Method(snapshot.DeclaringType, "CreateShadow", 0);
			Type Get(string name)
			{
				return assembly2.GetType(name, throwOnError: true);
			}
		}

		internal bool IsWholeInventoryQuickStack(object actions)
		{
			return singleStackInventory.GetValue(actions) == null;
		}

		private string Serialized(Inventory inventory)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			return ((ZPackage)Call(save, null, inventory)).GetBase64();
		}

		private bool Accessible(Container chest, Player player)
		{
			return (bool)Call(canDiscover, null, chest, player.GetPlayerID(), true, false);
		}

		private bool TryAcquire(Container chest, out Inventory inventory)
		{
			object[] array = new object[2] { chest, null };
			bool num = (bool)Call(acquire, null, array);
			object obj = array[1];
			inventory = (Inventory)((obj is Inventory) ? obj : null);
			if (num)
			{
				return inventory != null;
			}
			return false;
		}

		private bool OwnedBag(Player player, BackpackComponent bag, Inventory inventory)
		{
			if (Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && ((Character)player).IsOwner() && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && bag != null && ((CustomItemData)bag).IsAlive && !bag.IsLoadingInventory && !bag.IsEmptyingBackpack && PlayerExtensions.GetEquippedBackpack(player) == bag && bag.GetInventory() == inventory && ((Humanoid)player).GetInventory().GetAllItems().Contains(((CustomItemData)bag).Item))
			{
				return !(bool)backpackOpen.GetValue(null);
			}
			return false;
		}

		private void Persist(BackpackComponent bag, Inventory inventory)
		{
			if (!((CustomItemData)bag).IsAlive || bag.GetInventory() != inventory)
			{
				throw new InvalidOperationException("Backpack endpoint changed before persistence.");
			}
			string text = Serialized(inventory);
			bag.Save(inventory);
			if (!string.Equals(((CustomItemData)bag).Value, text, StringComparison.Ordinal) || !((CustomItemData)bag).Item.m_customData.TryGetValue(((CustomItemData)bag).CustomDataKey, out var value) || value != text)
			{
				throw new InvalidOperationException("Backpack item custom data did not match its inventory.");
			}
		}

		private void VerifyDestination(Destination target, string expected)
		{
			if (!TryAcquire(target.Chest, out var inventory) || inventory != target.Inventory || Serialized(inventory) != expected)
			{
				throw new InvalidOperationException("Destination persistence or ownership could not be verified.");
			}
		}

		private int Move(Player player, object lease, BackpackComponent bag, Inventory source, Destination target, ItemData item)
		{
			if (!OwnedBag(player, bag, source) || !Accessible(target.Chest, player))
			{
				return 0;
			}
			return SafeStackMove(player, lease, bag, source, target, item);
		}

		internal int Stack(object actions)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			if ((bool)retry.GetValue(actions))
			{
				return 0;
			}
			object[] array = new object[4] { "Backpack Quick Stack", null, null, true };
			if (!(bool)Call(begin, null, array))
			{
				return 0;
			}
			Player val = (Player)array[1];
			using IDisposable lease = (IDisposable)array[2];
			BackpackComponent equippedBackpack = PlayerExtensions.GetEquippedBackpack(val);
			Inventory val2 = ((equippedBackpack != null) ? equippedBackpack.GetInventory() : null);
			if (!OwnedBag(val, equippedBackpack, val2) || val2 == ((Humanoid)val).GetInventory())
			{
				return 0;
			}
			List<ItemData> list = (from x in val2.GetAllItems()
				orderby x.m_gridPos.y, x.m_gridPos.x
				select x).ToList();
			if (list.Count == 0 || list.Count > 128)
			{
				return 0;
			}
			object[] array2 = new object[4] { list, null, null, true };
			if (!(bool)Call(protection, null, array2))
			{
				throw new InvalidOperationException("Backpack item protection unavailable: " + array2[2]);
			}
			object target = array2[1];
			IReadOnlyList<Container> obj = (IReadOnlyList<Container>)Call(nearby, actions, val, true, "quick-stack", false);
			if (obj.Count > 256)
			{
				throw new NotSupportedException("Destination bound exceeded.");
			}
			List<Destination> list2 = new List<Destination>();
			foreach (Container item in obj)
			{
				if (!TryAcquire(item, out var inventory) || inventory == val2 || inventory == ((Humanoid)val).GetInventory())
				{
					continue;
				}
				object rules = null;
				if ((bool)Call(eligible, null, item))
				{
					Call(learn, null, item);
					object[] array3 = new object[2] { item, null };
					if (!(bool)Call(readRules, null, array3))
					{
						continue;
					}
					rules = array3[1];
				}
				list2.Add(new Destination
				{
					Chest = item,
					Inventory = inventory,
					Rules = rules
				});
			}
			int num = 0;
			for (int num2 = 0; num2 < list.Count; num2++)
			{
				ItemData val3 = list[num2];
				int num3 = Convert.ToInt32(Call(stateAt, target, num2));
				if (val3?.m_shared == null || Rules.KeepItem(val3.m_equipped || ((Humanoid)val).IsItemEquiped(val3), val3.m_shared.m_questItem, ABAPI.IsBackpack(val3), num3))
				{
					continue;
				}
				string text = (string)Call(resource, null, val3);
				if (text.Length == 0)
				{
					continue;
				}
				object obj2 = Call(facts, null, val3);
				for (int num4 = 0; num4 <= 9; num4++)
				{
					if (val3.m_stack <= 0)
					{
						break;
					}
					if (!val2.GetAllItems().Contains(val3))
					{
						break;
					}
					foreach (Destination item2 in list2)
					{
						if (val3.m_stack <= 0 || !val2.GetAllItems().Contains(val3))
						{
							break;
						}
						bool flag = (bool)Call(contains, null, item2.Inventory, text);
						if (((item2.Rules != null) ? ((int)Call(score, item2.Rules, obj2, flag)) : (flag ? 9 : int.MaxValue)) == num4)
						{
							num = checked(num + Move(val, lease, equippedBackpack, val2, item2, val3));
						}
					}
				}
			}
			return num;
		}

		private object Restore(Inventory inventory, object backup, Player player, object lease)
		{
			if ((bool)Call(isBlocked, null, inventory))
			{
				throw new InvalidOperationException("Runic marked this inventory for recovery; automatic rollback is blocked.");
			}
			return Call(restore, null, inventory, backup, player, lease);
		}

		internal string RecoveryRecord()
		{
			return recoveryRecord?.Invoke();
		}

		private int SafeStackMove(Player player, object lease, BackpackComponent bag, Inventory source, Destination target, ItemData item)
		{
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			BackpackAccess.Endpoint bagEndpoint = bagAccess.Current(player);
			if (bagEndpoint == null || bagEndpoint.Inventory != source || bagEndpoint.Parent != ((CustomItemData)bag).Item)
			{
				return 0;
			}
			Inventory carried = ((Humanoid)player).GetInventory();
			Inventory[] inventories = (Inventory[])(object)new Inventory[3] { source, target.Inventory, carried };
			object[] before = inventories.Select((Inventory i) => snapshot.Invoke(new object[1] { i })).ToArray();
			Inventory[] shadows = ((IEnumerable<object>)before).Select((Func<object, Inventory>)((object s) => (Inventory)Call(shadow, s))).ToArray();
			string[] checkpoint0 = Projected();
			Func<bool> authority = (Func<bool>)Call(captureAuthority, null, target.Chest);
			Vector2i gridPos = item.m_gridPos;
			ItemData itemAt = shadows[0].GetItemAt(gridPos.x, gridPos.y);
			object[] args = new object[9]
			{
				shadows[0],
				shadows[1],
				itemAt,
				item.m_stack,
				null,
				null,
				lease,
				null,
				target.Chest
			};
			int amount = (int)Call(move, null, args);
			if (amount == 0)
			{
				CheckedTransfer.Match(Projected(), checkpoint0);
				return 0;
			}
			bagEndpoint.UpdateShadowParent(shadows[2], shadows[0], Serialized(shadows[0]));
			string[] checkpoint1 = Projected();
			CheckedTransfer.Run(new Action[1]
			{
				delegate
				{
					object[] args2 = new object[9] { source, target.Inventory, item, amount, null, player, lease, null, target.Chest };
					if ((int)Call(move, null, args2) != amount)
					{
						throw new InvalidOperationException("Backpack stack transfer was incomplete.");
					}
					bagEndpoint.Persist(Serialized(source));
				}
			}, new string[2][] { checkpoint0, checkpoint1 }, Read, delegate
			{
				VerifyDestination(target, checkpoint1[1]);
			}, Recover, Quarantine);
			return amount;
			string[] Projected()
			{
				return shadows.Select(Serialized).ToArray();
			}
			void Quarantine()
			{
				recoveryRecord = () => string.Join("\n", inventories.Select((Inventory inv, int n) => (new string[3] { "bag", "destination", "player" })[n] + "\nbefore\n" + checkpoint0[n] + "\ncurrent\n" + Serialized(inv)));
				Inventory[] array = inventories;
				foreach (Inventory val in array)
				{
					Call(block, null, val);
				}
			}
			string[] Read()
			{
				if (!authority() || !OwnedBag(player, bag, source) || !bagEndpoint.Valid() || ((Humanoid)player).GetInventory() != carried || !Accessible(target.Chest, player) || inventories.Any((Inventory i) => (bool)Call(isBlocked, null, i)))
				{
					throw new InvalidOperationException("Backpack transfer endpoint changed or requires recovery.");
				}
				return inventories.Select(Serialized).ToArray();
			}
			void Recover(string[] current)
			{
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_007e: Expected O, but got Unknown
				CheckedTransfer.Match(Read(), current);
				shadows = ((IEnumerable<Inventory>)inventories).Select((Func<Inventory, Inventory>)((Inventory i) => (Inventory)Call(shadow, snapshot.Invoke(new object[1] { i })))).ToArray();
				int[] array = new int[3] { 0, 1, 2 };
				foreach (int num2 in array)
				{
					CheckedTransfer.Match(Read(), current);
					shadows[num2] = (Inventory)Call(shadow, before[num2]);
					if (num2 == 0)
					{
						bagEndpoint.UpdateShadowParent(shadows[2], shadows[0], Serialized(shadows[0]));
					}
					string[] array2 = Projected();
					if (!current.SequenceEqual<string>(array2, StringComparer.Ordinal))
					{
						Restore(inventories[num2], before[num2], player, lease);
						if (num2 == 0)
						{
							bagEndpoint.Persist(Serialized(source));
						}
						CheckedTransfer.Match(Read(), array2);
						current = array2;
					}
				}
				VerifyDestination(target, checkpoint0[1]);
			}
		}
	}
}