Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AdventureBackpackCompatibility v0.1.8
plugins/AdventureBackpackCompatibility/AdventureBackpackCompatibility.dll
Decompiled 13 hours agousing 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]); } } } }