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 RunicEnchantingCompatibility v0.1.8
plugins/RunicEnchantingCompatibility/RunicEnchantingCompatibility.dll
Decompiled 21 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using EpicLoot; using EpicLoot_UnityLib; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("RunicEnchantingCompatibility")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.8.0")] [assembly: AssemblyInformationalVersion("0.1.8")] [assembly: AssemblyProduct("RunicEnchantingCompatibility")] [assembly: AssemblyTitle("RunicEnchantingCompatibility")] [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 Vercadi.RunicEnchantingCompatibility { 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[] array = new object[2] { player, null }; if (!(bool)canCraft.Invoke(null, array)) { return null; } object obj = equipped.Invoke(null, new object[1] { player }); object obj2 = array[1]; Inventory val = (Inventory)((obj2 is Inventory) ? obj2 : null); 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? obj3 = item.GetValue(obj); ItemData val2 = (ItemData)((obj3 is ItemData) ? obj3 : 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 CheckedTransfer { internal static void Match(string[] actual, string[] expected) { if (!actual.SequenceEqual<string>(expected, StringComparer.Ordinal)) { throw new InvalidOperationException("Inventory changed outside the planned enchanting 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) ? "Enchanting transfer stopped; original inventories verified. No automatic retry." : "Enchanting transfer stopped; recovery required. Unknown contents preserved. No automatic retry.", list); } } } internal static class ChestPreview { private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Type Reflection = ((object)Chainloader.PluginInfos["chazman.RunicCrafting"].Instance).GetType().Assembly.GetType("RunicCrafting.Integration.ValheimReflection", throwOnError: true); private static readonly MethodInfo View = Reflection.GetMethod("GetView", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null); private static readonly Assembly Crafting = Reflection.Assembly; private static readonly FieldInfo Loading = Crafting.GetType("RunicCrafting.Integration.PreviewRefreshRuntime", throwOnError: true).GetField("LoadingPreview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo Matches = Crafting.GetType("RunicCrafting.Integration.CraftingInventoryPayloadComparison", throwOnError: true).GetMethod("MatchesLoaded", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); internal static Inventory Read(Container chest) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown ZNetView val = (ZNetView)View.Invoke(null, new object[1] { chest }); ZDO val2 = ((Object.op_Implicit((Object)(object)val) && val.IsValid()) ? val.GetZDO() : null); Inventory inventory = chest.GetInventory(); if (val2 == null || !val2.IsValid() || inventory == null) { return null; } byte[] byteArray = val2.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null || byteArray.Length == 0 || byteArray.Length > 262144) { return null; } byte[] array = (byte[])byteArray.Clone(); Inventory val3 = new Inventory(inventory.GetName(), (Sprite)null, inventory.GetWidth(), inventory.GetHeight()); object value = Loading.GetValue(null); try { Loading.SetValue(null, val3); val3.Load(new ZPackage(array)); } finally { Loading.SetValue(null, value); } ZPackage val4 = new ZPackage(); val3.Save(val4); if (!(bool)Matches.Invoke(null, new object[2] { array, val4.GetArray() })) { return null; } byte[] byteArray2 = val2.GetByteArray(ZDOVars.s_items, (byte[])null); if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || val.GetZDO() != val2 || !val2.IsValid() || byteArray2 == null || byteArray2.Length != array.Length) { return null; } for (int i = 0; i < array.Length; i++) { if (array[i] != byteArray2[i]) { return null; } } return val3; } } internal sealed class PlayerSlotPolicy { internal const string Id = "shudnal.ExtraSlots"; internal const string Hash = "389A9DFC6D36BB8897D7B0991AA1E02B37DD55A95D944DF0EB7637954B79322B"; private readonly MethodInfo slot; internal PlayerSlotPolicy(Assembly assembly) { using (FileStream inputStream = File.OpenRead(assembly.Location)) { using SHA256 sHA = SHA256.Create(); if (BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "") != "389A9DFC6D36BB8897D7B0991AA1E02B37DD55A95D944DF0EB7637954B79322B") { throw new NotSupportedException("Unreviewed ExtraSlots inventory layout."); } } slot = assembly.GetType("ExtraSlots.Slots", throwOnError: true).GetMethod("GetSlotInGrid", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Vector2i) }, null) ?? throw new MissingMethodException("ExtraSlots grid contract changed."); } internal bool Ordinary(int x, int y) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) return slot.Invoke(null, new object[1] { (object)new Vector2i(x, y) }) == null; } } [BepInPlugin("vercadi.RunicEnchantingCompatibility", "Runic Enchanting Compatibility", "0.1.8")] [BepInProcess("valheim.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency("randyknapp.mods.epicloot", "0.14.13")] [BepInDependency("chazman.RunicCrafting", "1.1.11")] [BepInDependency("chazman.RunicStorage", "1.3.12")] public sealed class Plugin : BaseUnityPlugin { public const string Id = "vercadi.RunicEnchantingCompatibility"; internal const string EpicHash = "802790E0CBD8A6F951627619A488B84E6A406E8770E2687B2FFF8B63D2ADF9EA"; internal const string CraftingHash = "3D98A550F9A160F796AD3BDA3E9C47DE2E26211FB1449F53AB511FF883D041B0"; internal const string StorageHash = "C2EEE73F58A2F1AF7C19092FBE02AAC5D58A39BB92CA305997929F6AD0ACFEE2"; private static Plugin instance; private RunicBridge bridge; private Harmony harmony; private bool ready; private bool faulted; private bool provider; private FieldInfo selected; private MethodInfo inUseDistance; private TableActions actions; private int actionDepth; private float previewUntil; private Player previewPlayer; private EnchantingTable previewTable; private List<ItemData> storedPreview; private Dictionary<string, int> storedCounts; private EnchantingTableUIPanelBase pendingRefresh; [ThreadStatic] private static Dictionary<string, int> preview; private void Awake() { //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Expected O, but got Unknown //IL_021c: Expected O, but got Unknown //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Expected O, but got Unknown //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Expected O, but got Unknown //IL_02bd: Expected O, but got Unknown instance = this; try { Require("randyknapp.mods.epicloot", "802790E0CBD8A6F951627619A488B84E6A406E8770E2687B2FFF8B63D2ADF9EA"); bridge = new RunicBridge(Require("chazman.RunicCrafting", "3D98A550F9A160F796AD3BDA3E9C47DE2E26211FB1449F53AB511FF883D041B0"), Require("chazman.RunicStorage", "C2EEE73F58A2F1AF7C19092FBE02AAC5D58A39BB92CA305997929F6AD0ACFEE2")); if (Chainloader.PluginInfos.TryGetValue("shudnal.ExtraSlots", out var value)) { bridge.SetSlotPolicy(new PlayerSlotPolicy(((object)value.Instance).GetType().Assembly).Ordinary); } if (((BaseUnityPlugin)this).Config.Bind<bool>("General", "Use equipped backpack", true, "Use items and materials from the equipped closed AdventureBackpack at the enchanting table. Requires its server crafting setting.").Value && Chainloader.PluginInfos.TryGetValue("vapok.mods.adventurebackpacks", out var value2)) { try { bridge.SetBackpack(new BackpackAccess(((object)value2.Instance).GetType().Assembly)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Backpack materials unavailable; chest integration remains enabled. " + ex.Message)); } } selected = AccessTools.Field(typeof(UpgradeTableUI), "_selectedFeature") ?? throw new MissingFieldException("Selected feature unavailable."); inUseDistance = AccessTools.Method(typeof(EnchantingTable), "InUseDistance", new Type[1] { typeof(Humanoid) }, (Type[])null) ?? throw new MissingMethodException("Table use-distance contract changed."); actions = new TableActions(); provider = API.RegisterInventoryProvider("vercadi.RunicEnchantingCompatibility", (Func<List<ItemData>>)ListPreview, (Func<string, int>)CountPreview, (Func<string, int, int>)null, (Func<ItemData, int, int>)null); if (!provider) { throw new InvalidOperationException("Epic Loot inventory provider registration refused."); } harmony = new Harmony("vercadi.RunicEnchantingCompatibility"); harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(UpgradeTableUI), "Refresh", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeginPreview", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "EndPreview", (Type[])null), (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(UpgradeTableUI), "DoMainAction", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeUpgrade", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Type[] panels = TableActions.Panels; foreach (Type type in panels) { harmony.Patch((MethodBase)AccessTools.DeclaredMethod(type, "DoMainAction", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeAction", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterAction", (Type[])null), (HarmonyMethod)null); } ready = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"ACTIVE: reviewed EpicLoot 0.14.13 table items and materials with RunicCrafting 1.1.11 and RunicStorage 1.3.12. Identify, sacrifice, enchant, augment, disenchant, convert, runes and upgrades; equipped backpack and nearby chests; client-only. "); } catch (Exception ex2) { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if (provider) { API.UnregisterInventoryProvider("vercadi.RunicEnchantingCompatibility"); } provider = false; ((BaseUnityPlugin)this).Logger.LogError((object)("INACTIVE: original enchanting behavior retained. " + ex2)); } static Assembly Require(string id, string hash) { Assembly assembly = ((object)Chainloader.PluginInfos[id].Instance).GetType().Assembly; 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: " + id); } return assembly; } } private bool Context(UpgradeTableUI ui, out Player player, out EnchantingTable table) { if (TableContext(out player, out table) && Object.op_Implicit((Object)(object)ui)) { return ((Component)ui).gameObject.activeInHierarchy; } return false; } private bool TableContext(out Player player, out EnchantingTable table) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) player = Player.m_localPlayer; table = (Object.op_Implicit((Object)(object)EnchantingTableUI.instance) ? EnchantingTableUI.instance.SourceTable : null); if (ready && !faulted && bridge.Enabled && Object.op_Implicit((Object)(object)player) && ((Character)player).IsOwner() && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && Object.op_Implicit((Object)(object)table) && EnchantingTableUI.IsVisible() && (bool)inUseDistance.Invoke(table, new object[1] { player })) { return bridge.AllowsTable(((Component)table).transform.position); } return false; } private List<Cost> Costs(UpgradeTableUI ui, EnchantingTable table) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) int num = (int)selected.GetValue(ui); if (!Enum.IsDefined(typeof(EnchantingFeature), num)) { return null; } EnchantingFeature val = (EnchantingFeature)num; if (!table.IsFeatureAvailable(val) || table.IsFeatureMaxLevel(val)) { return null; } return Rules.Normalize((table.IsFeatureLocked(val) ? table.GetFeatureUnlockCost(val) : table.GetFeatureUpgradeCost(val)).Select(delegate(InventoryItemListElement row) { ItemData item = row.GetItem(); return new Cost(item?.m_shared?.m_name, bridge.Resource(item), item?.m_stack ?? 0); })); } private static List<ItemData> ListPreview() { Plugin plugin = instance; if (!Object.op_Implicit((Object)(object)plugin)) { return new List<ItemData>(); } try { if (!plugin.TableContext(out var player, out var table)) { return new List<ItemData>(); } if (plugin.storedPreview == null || (Object)(object)plugin.previewPlayer != (Object)(object)player || (Object)(object)plugin.previewTable != (Object)(object)table || Time.unscaledTime >= plugin.previewUntil) { plugin.storedPreview = plugin.bridge.ReadItems(player); plugin.storedCounts = (from i in plugin.storedPreview group i by i.m_shared.m_name).ToDictionary<IGrouping<string, ItemData>, string, int>((IGrouping<string, ItemData> g) => g.Key, (IGrouping<string, ItemData> g) => (int)Math.Min(2147483647L, ((IEnumerable<ItemData>)g).Sum((Func<ItemData, long>)((ItemData i) => i.m_stack))), StringComparer.Ordinal); plugin.previewPlayer = player; plugin.previewTable = table; plugin.previewUntil = Time.unscaledTime + 1f; } return plugin.storedPreview; } catch (Exception ex) { plugin.Fail(ex); return new List<ItemData>(); } } private static int CountPreview(string name) { Plugin plugin = instance; if (string.IsNullOrEmpty(name) || !Object.op_Implicit((Object)(object)plugin) || !plugin.ready || plugin.faulted || plugin.actionDepth > 0) { return 0; } if (preview != null) { if (!preview.TryGetValue(name, out var value)) { return 0; } return value; } ListPreview(); if (!plugin.TableContext(out var _, out var _) || plugin.storedCounts == null || !plugin.storedCounts.TryGetValue(name, out var value2)) { return 0; } return value2; } private static bool BeforeAction(EnchantingTableUIPanelBase __instance, out bool __state) { __state = false; Plugin plugin = instance; if (!Object.op_Implicit((Object)(object)plugin) || !plugin.ready) { return true; } try { if (!plugin.TableContext(out var player, out var table) || !Object.op_Implicit((Object)(object)__instance) || !((Component)__instance).gameObject.activeInHierarchy) { if (__instance != null) { __instance.Cancel(); } return false; } __state = true; plugin.actionDepth++; TableActions.Plan plan = plugin.actions.Create(__instance, table, plugin.bridge); if (plan == null) { __instance.Cancel(); return false; } if (!plugin.bridge.Pull(player, plan.Costs, plan.Selections, out var remapped, out var reason)) { __instance.Cancel(); ((Character)player).Message((MessageType)2, reason, 0, (Sprite)null, false); return false; } plugin.actions.Remap(__instance, plan, remapped); plugin.storedPreview = null; return true; } catch (Exception ex) { plugin.Fail(ex); __instance.Cancel(); if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Enchanting transfer stopped. Check the log before retrying.", 0, (Sprite)null, false); } return false; } } private static void AfterAction(EnchantingTableUIPanelBase __instance, bool __state, Exception __exception) { Plugin plugin = instance; if (Object.op_Implicit((Object)(object)plugin)) { if (__state) { plugin.actionDepth--; } plugin.storedPreview = null; plugin.pendingRefresh = __instance; if (__exception != null) { plugin.Fail(__exception); } } } private void LateUpdate() { if (Object.op_Implicit((Object)(object)pendingRefresh)) { EnchantingTableUIPanelBase panel = pendingRefresh; pendingRefresh = null; try { if (!faulted && ready) { actions.RefreshCosts(panel); } } catch (Exception ex) { Fail(ex); } } if (!EnchantingTableUI.IsVisible()) { storedPreview = null; previewPlayer = null; previewTable = null; } } private static void BeginPreview(UpgradeTableUI __instance, out Dictionary<string, int> __state) { __state = preview; preview = null; Plugin plugin = instance; if (!Object.op_Implicit((Object)(object)plugin)) { return; } try { if (plugin.Context(__instance, out var player, out var table)) { List<Cost> list = plugin.Costs(__instance, table); if (list != null) { preview = plugin.bridge.Preview(player, list); } } } catch (Exception ex) { plugin.Fail(ex); } } private static void EndPreview(Dictionary<string, int> __state) { preview = __state; } private static bool BeforeUpgrade(UpgradeTableUI __instance) { Plugin plugin = instance; if (!Object.op_Implicit((Object)(object)plugin)) { return true; } try { if (!plugin.Context(__instance, out var player, out var table) || player.NoCostCheat()) { return true; } List<Cost> list = plugin.Costs(__instance, table); if (list == null || list.Count == 0) { return true; } if (plugin.bridge.Pull(player, list, out var reason)) { return true; } ((EnchantingTableUIPanelBase)__instance).Cancel(); ((Character)player).Message((MessageType)2, reason, 0, (Sprite)null, false); __instance.Refresh(); return false; } catch (Exception ex) { plugin.Fail(ex); ((EnchantingTableUIPanelBase)__instance).Cancel(); if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Enchanting material pull stopped. Check the log before retrying.", 0, (Sprite)null, false); } return false; } } private void Fail(Exception ex) { if (faulted) { return; } faulted = true; storedPreview = null; ((BaseUnityPlugin)this).Logger.LogError((object)("External enchanting items disabled for this session after an error; no automatic retry. Restart the client before using the table again. " + ex)); try { string text = bridge?.RecoveryRecord(); if (text != null) { string text2 = Path.Combine(Paths.ConfigPath, "RunicEnchantingCompatibility-recovery"); Directory.CreateDirectory(text2); string text3 = Path.Combine(text2, DateTime.UtcNow.ToString("yyyyMMdd-HHmmss") + "-" + Guid.NewGuid().ToString("N") + ".txt"); File.WriteAllText(text3, "Recovery evidence only. Do not import automatically.\n" + text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); ((BaseUnityPlugin)this).Logger.LogError((object)("Preserved endpoint and staging contents for investigation: " + text3)); } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not write recovery evidence: " + ex2)); } } private void OnDestroy() { ready = false; if (provider) { API.UnregisterInventoryProvider("vercadi.RunicEnchantingCompatibility"); } Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)instance == (Object)(object)this) { instance = null; preview = null; } } } internal sealed class Cost { internal readonly string Name; internal readonly string Resource; internal readonly int Amount; internal Cost(string name, string resource, int amount) { Name = name; Resource = resource; Amount = amount; } } internal static class Rules { internal static List<Cost> Normalize(IEnumerable<Cost> rows) { Dictionary<string, Cost> dictionary = new Dictionary<string, Cost>(StringComparer.Ordinal); int num = 0; int num2 = 0; foreach (Cost row in rows) { if (++num > 32 || row == null || string.IsNullOrEmpty(row.Name) || string.IsNullOrEmpty(row.Resource) || row.Amount <= 0 || row.Amount > 100000 || (num2 += row.Amount) > 100000) { throw new NotSupportedException("Unsupported or excessive table upgrade cost."); } if (dictionary.TryGetValue(row.Name, out var value)) { if (value.Resource != row.Resource) { throw new NotSupportedException("Ambiguous material name in upgrade cost."); } dictionary[row.Name] = new Cost(row.Name, row.Resource, value.Amount + row.Amount); } else { dictionary.Add(row.Name, row); } } return dictionary.Values.OrderBy<Cost, string>((Cost c) => c.Name, StringComparer.Ordinal).ToList(); } internal static List<Cost> Missing(IEnumerable<Cost> costs, Func<string, int> carried) { return (from c in costs select new Cost(c.Name, c.Resource, Math.Max(0, c.Amount - Math.Max(0, carried(c.Name)))) into c where c.Amount > 0 select c).ToList(); } } internal sealed class RunicBridge { private sealed class Source { internal Container Chest; internal Inventory Inventory; internal List<ItemData> Items; internal BackpackAccess.Endpoint Bag; } private sealed class Line { internal Source Source; internal ItemData Item; internal int Quantity; internal int SlotX = -1; internal int SlotY; internal ItemData PreviewItem; } private sealed class Origin { internal Container Chest; internal Inventory BagInventory; internal ItemData Parent; internal ItemData Proof; } private sealed class TransferEndpoint { internal Inventory Live; internal Inventory Shadow; internal object Before; internal Func<bool> Authority; internal Source Source; internal string Name; } private sealed class NoCapacityException : Exception { } private readonly MethodInfo query; private readonly MethodInfo eligible; private readonly MethodInfo readPreview; private readonly MethodInfo previewCount; private readonly MethodInfo resourceId; private readonly MethodInfo begin; private readonly MethodInfo acquire; private readonly MethodInfo move; private readonly MethodInfo restore; private readonly MethodInfo save; private readonly MethodInfo protect; private readonly MethodInfo stateAt; private readonly MethodInfo isBlocked; private readonly MethodInfo block; private readonly MethodInfo captureAuthority; private readonly ConstructorInfo snapshot; private BackpackAccess backpack; private readonly MethodInfo shadow; private readonly PropertyInfo range; private readonly PropertyInfo candidates; private readonly PropertyInfo returned; private readonly PropertyInfo initialized; private readonly PropertyInfo enabled; private readonly PropertyInfo crafting; private readonly PropertyInfo wardAccess; private readonly PropertyInfo storageEnabled; private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private readonly MethodInfo addAt; private readonly MethodInfo sameItem; private readonly ConditionalWeakTable<ItemData, Origin> origins = new ConditionalWeakTable<ItemData, Origin>(); private Func<int, int, bool> ordinarySlot = (int x, int y) => true; private List<TransferEndpoint> recoveryEndpoints; internal bool Enabled { get { if (Flag(enabled) && Flag(crafting) && Flag(storageEnabled)) { return (bool)initialized.GetValue(null); } return false; } } private float Radius => (float)range.GetValue(null); internal void SetBackpack(BackpackAccess access) { backpack = access; } private static MethodInfo Method(Type t, string name, int args) { return t.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single((MethodInfo m) => m.Name == name && m.GetParameters().Length == args); } private static PropertyInfo Property(Type t, string name) { return t.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException(t.FullName, name); } private static object Call(MethodInfo m, object target, params object[] args) { return m.Invoke(target, args); } private static bool Flag(PropertyInfo f) { return ((ConfigEntry<bool>)f.GetValue(null)).Value; } internal RunicBridge(Assembly craftingAssembly, Assembly storageAssembly) { Assembly craftingAssembly2 = craftingAssembly; Assembly storageAssembly2 = storageAssembly; base..ctor(); query = Method(C("Integration.ContainerSpatialIndex"), "Query", 3); eligible = Method(C("Integration.ContainerQueryRuntime"), "IsEligibleContainer", 6); readPreview = Method(C("Integration.ValheimReflection"), "TryReadContainerInventory", 2); previewCount = Method(C("Integration.PreviewMaterialCounts"), "Count", 1); resourceId = C("Integration.ValheimReflection").GetMethod("ResourceId", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(ItemData) }, null) ?? throw new MissingMethodException("Material identity method unavailable."); range = Property(C("Configuration"), "SafeRangeCap"); candidates = Property(C("Configuration"), "SafeMaximumCandidates"); returned = Property(C("Configuration"), "SafeMaximumReturned"); initialized = Property(C("Integration.CraftingRuntime"), "IsInitialized"); enabled = Property(C("Configuration"), "Enabled"); crafting = Property(C("Configuration"), "CraftFromContainers"); wardAccess = Property(C("Configuration"), "RequireWardAccess"); storageEnabled = Property(S("PluginConfig"), "Enabled"); begin = Method(S("Runtime.StorageActions"), "TryBeginMutation", 4); acquire = Method(S("Runtime.StorageContainerAuthority"), "TryGetSynchronizedServerInventory", 2); move = Method(S("Runtime.ValheimContainerService"), "MoveUpTo", 9); restore = Method(S("Runtime.ValheimContainerService"), "RestoreInventory", 4); isBlocked = Method(storageAssembly2.GetType("RunicAutomation.MutationGate", throwOnError: true), "IsBlocked", 1); block = Method(storageAssembly2.GetType("RunicAutomation.MutationGate", throwOnError: true), "Block", 1); captureAuthority = Method(S("Runtime.StorageContainerAuthority"), "CaptureAuthority", 1); save = Method(S("Runtime.ValheimContainerService"), "SaveInventory", 1); protect = Method(S("Engine.StorageItemProtection"), "TryCapture", 4).MakeGenericMethod(typeof(ItemData)); stateAt = Method(S("Engine.StorageProtectionSnapshot"), "StateAt", 1); snapshot = S("Runtime.StorageInventorySnapshot").GetConstructor(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Inventory) }, null) ?? throw new MissingMethodException("Exact inventory snapshot unavailable."); shadow = Method(snapshot.DeclaringType, "CreateShadow", 0); addAt = Method(S("Runtime.ValheimContainerService"), "AddAt", 4); sameItem = Method(S("Runtime.ValheimContainerService"), "CanMergeExact", 3); Type C(string n) { return craftingAssembly2.GetType("RunicCrafting." + n, throwOnError: true); } Type S(string n) { return storageAssembly2.GetType("RunicStorage." + n, throwOnError: true); } } internal bool AllowsTable(Vector3 position) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Flag(wardAccess)) { return PrivateArea.CheckAccess(position, 0f, false, false); } return true; } internal string Resource(ItemData item) { return (string)Call(resourceId, null, item); } private bool Eligible(Container chest, Player player, bool writable) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) return (bool)Call(eligible, null, chest, player, ((Component)player).transform.position, Radius, null, writable); } private List<Container> Nearby(Player player, bool writable) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) List<Container> list = new List<Container>(); foreach (Container item in (IReadOnlyList<Container>)Call(query, null, ((Component)player).transform.position, Radius, (int)candidates.GetValue(null))) { if (list.Count >= (int)returned.GetValue(null)) { break; } if (Eligible(item, player, writable)) { list.Add(item); } } return list; } private Inventory Acquire(Container chest) { object[] array = new object[2] { chest, null }; if (!(bool)Call(acquire, null, array)) { return null; } object obj = array[1]; return (Inventory)((obj is Inventory) ? obj : 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(); } internal Dictionary<string, int> Preview(Player player, List<Cost> costs) { Dictionary<string, int> dictionary = costs.ToDictionary<Cost, string, int>((Cost c) => c.Name, (Cost c) => 0, StringComparer.Ordinal); BackpackAccess.Endpoint endpoint = backpack?.Current(player); if (endpoint != null) { foreach (Cost cost in costs) { dictionary[cost.Name] = Math.Min(cost.Amount, (from i in Usable(endpoint.Inventory) where i.m_shared.m_name == cost.Name && Resource(i) == cost.Resource select i).Sum((ItemData i) => i.m_stack)); } } foreach (Container item in Nearby(player, writable: false)) { object[] array = new object[2] { item, null }; if (!(bool)Call(readPreview, null, array)) { continue; } foreach (Cost cost2 in costs) { int val = (int)Call(previewCount, array[1], cost2.Resource); dictionary[cost2.Name] = (int)Math.Min(cost2.Amount, (long)dictionary[cost2.Name] + (long)Math.Max(0, val)); } } return dictionary; } private int Move(Inventory source, Inventory destination, ItemData item, int quantity, Player player, object lease, Container chest) { object[] args = new object[9] { source, destination, item, quantity, null, player, lease, chest, null }; return (int)Call(move, null, args); } private bool Valid(Source source, Player player) { if (source.Bag == null) { if (Eligible(source.Chest, player, writable: true)) { return Acquire(source.Chest) == source.Inventory; } return false; } return source.Bag.Valid(); } private List<ItemData> Usable(Inventory inventory) { List<ItemData> list = (from i in inventory.GetAllItems() orderby i.m_gridPos.y, i.m_gridPos.x select i).ToList(); object[] array = new object[4] { list, null, null, true }; if (list.Count > 1024 || !(bool)Call(protect, null, array)) { return new List<ItemData>(); } List<ItemData> list2 = new List<ItemData>(); for (int num = 0; num < list.Count; num++) { ItemData val = list[num]; if (val?.m_shared != null && val.m_stack > 0 && !val.m_equipped && !val.m_shared.m_questItem && val.m_worldLevel >= Game.m_worldLevel && Convert.ToInt32(Call(stateAt, array[1], num)) != 2) { list2.Add(val); } } return list2; } internal bool Pull(Player player, List<Cost> costs, out string reason) { Dictionary<ItemData, ItemData> remapped; return Pull(player, costs, new List<Selection>(), out remapped, out reason); } internal bool Pull(Player player, List<Cost> costs, List<Selection> selections, out Dictionary<ItemData, ItemData> remapped, out string reason) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown remapped = new Dictionary<ItemData, ItemData>(); reason = "Nearby materials are unavailable or busy. Close any open chests and try again."; List<Cost> list = Rules.Missing(costs, (string n) => ((Humanoid)player).GetInventory().CountItems(n, -1, true)); if (!ValidateCarried(player, selections, costs, remapped, out reason)) { return false; } if (list.Count == 0 && remapped.Count == selections.Count) { return true; } reason = "Nearby items or materials are unavailable or busy. Close chests and reselect the action."; object[] array = new object[4] { "Enchanting table upgrade", null, null, true }; if (!(bool)Call(begin, null, array)) { return false; } using IDisposable lease = (IDisposable)array[2]; if ((Object)(Player)array[1] != (Object)(object)player) { return false; } List<Source> list2 = new List<Source>(); BackpackAccess.Endpoint endpoint = backpack?.Current(player); if (endpoint != null) { list2.Add(new Source { Bag = endpoint, Inventory = endpoint.Inventory, Items = Usable(endpoint.Inventory) }); } foreach (Container item in Nearby(player, writable: true)) { Inventory val = Acquire(item); if (val != null && val != ((Humanoid)player).GetInventory() && val.GetAllItems().Count <= 1024) { list2.Add(new Source { Chest = item, Inventory = val, Items = Usable(val) }); } } List<Line> list3 = new List<Line>(); if (!PlanSelections(player, selections, list2, list3, remapped, out reason)) { return false; } foreach (Cost item2 in list) { int num = item2.Amount; foreach (Source item3 in list2) { foreach (ItemData item4 in item3.Items) { if (num > 0 && !(item4.m_shared.m_name != item2.Name) && !(Resource(item4) != item2.Resource)) { int num2 = Math.Min(num, item4.m_stack); list3.Add(new Line { Source = item3, Item = item4, Quantity = num2 }); num -= num2; } } } if (num > 0) { return false; } } if (list3.Count > 1024) { reason = "Too many material stacks for one upgrade."; return false; } if (!Transfer(player, lease, list3, costs, out reason)) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); foreach (Line item5 in list3.Where((Line l) => l.PreviewItem != null)) { remapped.Add(item5.PreviewItem, inventory.GetItemAt(item5.SlotX, item5.SlotY)); } reason = ""; return true; } 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 List<ItemData> ReadItems(Player player) { List<ItemData> result = new List<ItemData>(); HashSet<Container> hashSet = new HashSet<Container>(); BackpackAccess.Endpoint endpoint = backpack?.Current(player); if (endpoint != null) { Add(endpoint.Inventory, null, endpoint); } foreach (Container item in Nearby(player, writable: false)) { if (hashSet.Add(item)) { Add(ChestPreview.Read(item), item, null); } } return result; void Add(Inventory inventory, Container chest, BackpackAccess.Endpoint bag) { if (inventory == null || inventory.GetAllItems().Count > 1024) { return; } foreach (ItemData item2 in Usable(inventory)) { if (result.Count >= 4096) { break; } ItemData val = item2.Clone(); origins.Add(val, new Origin { Chest = chest, BagInventory = bag?.Inventory, Parent = bag?.Parent, Proof = item2.Clone() }); result.Add(val); } } } private bool Same(ItemData a, ItemData b) { return (bool)Call(sameItem, null, a, b, false); } private bool ValidateCarried(Player player, List<Selection> selections, List<Cost> costs, Dictionary<ItemData, ItemData> map, out string reason) { reason = "The selected items changed. Reopen the table and select them again."; if (selections.Count > 128 || selections.Any((Selection s) => s == null || s.Item == null || s.Amount <= 0 || s.Amount > s.Item.m_stack) || selections.Select((Selection s) => s.Item).Distinct().Count() != selections.Count) { return false; } if (selections.Any((Selection s) => costs.Any((Cost c) => c.Name == s.Item.m_shared.m_name))) { reason = "A selected item is also used as this action's currency. Separate the items and use the ordinary inventory flow."; return false; } foreach (Selection selection in selections) { Origin value; if (((Humanoid)player).GetInventory().GetAllItems().Contains(selection.Item)) { map.Add(selection.Item, selection.Item); } else if (!origins.TryGetValue(selection.Item, out value)) { return false; } } return true; } private bool PlanSelections(Player player, List<Selection> selections, List<Source> sources, List<Line> lines, Dictionary<ItemData, ItemData> map, out string reason) { reason = "A selected stored item changed or is ambiguous. Select it again."; foreach (Selection selection in selections) { if (!map.ContainsKey(selection.Item)) { if (!origins.TryGetValue(selection.Item, out var origin)) { return false; } Source source = sources.FirstOrDefault((Source s) => (!((Object)(object)origin.Chest != (Object)null)) ? (s.Bag != null && s.Inventory == origin.BagInventory && s.Bag.Parent == origin.Parent) : ((Object)(object)s.Chest == (Object)(object)origin.Chest)); if (source == null) { reason = "The selected item's storage is busy or still synchronizing. Close chests and try again."; return false; } List<ItemData> list = source.Items.Where((ItemData i) => Same(i, origin.Proof) && i.m_stack == origin.Proof.m_stack).Take(2).ToList(); if (list.Count != 1) { return false; } ItemData item = list[0]; if (selection.Amount > item.m_stack || selection.Amount > item.m_shared.m_maxStackSize || lines.Any((Line l) => l.Source.Inventory == source.Inventory && l.Item == item)) { return false; } lines.Add(new Line { Source = source, Item = item, Quantity = selection.Amount, PreviewItem = selection.Item }); } } return true; } private bool FindSlot(Inventory inventory, out int x, out int y) { for (y = 0; y < inventory.GetHeight(); y++) { for (x = 0; x < inventory.GetWidth(); x++) { if (ordinarySlot(x, y) && inventory.GetItemAt(x, y) == null) { return true; } } } x = (y = -1); return false; } internal void SetSlotPolicy(Func<int, int, bool> policy) { ordinarySlot = policy ?? throw new ArgumentNullException("policy"); } internal string RecoveryRecord() { if (recoveryEndpoints == null) { return null; } return string.Join("\n", recoveryEndpoints.Select((TransferEndpoint e) => e.Name + "\nbefore\n" + Serialized((Inventory)Call(shadow, e.Before)) + "\ncurrent\n" + Serialized(e.Live))); } private bool Transfer(Player player, object lease, List<Line> lines, List<Cost> costs, out string reason) { //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Expected O, but got Unknown //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Expected O, but got Unknown //IL_04e7: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Expected O, but got Unknown //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) reason = "Keep enough ordinary inventory space for the selected items and materials."; List<TransferEndpoint> endpoints = new List<TransferEndpoint>(); Inventory playerInventory = ((Humanoid)player).GetInventory(); TransferEndpoint carried = Endpoint("player", playerInventory, () => (Object)(object)player == (Object)(object)Player.m_localPlayer && ((Character)player).IsOwner() && ((Humanoid)player).GetInventory() == playerInventory); Dictionary<Source, TransferEndpoint> dictionary = new Dictionary<Source, TransferEndpoint>(); foreach (Source source in lines.Select((Line l) => l.Source).Distinct()) { Func<bool> authority = ((source.Bag == null) ? ((Func<bool>)Call(captureAuthority, null, source.Chest)) : new Func<bool>(source.Bag.Valid)); dictionary.Add(source, Endpoint("source-" + dictionary.Count, source.Inventory, () => authority() && Valid(source, player), source)); } Dictionary<Line, TransferEndpoint> dictionary2 = new Dictionary<Line, TransferEndpoint>(); foreach (Line item in lines.Where((Line l) => l.Source.Bag != null && l.PreviewItem == null)) { int num; checked { num = unchecked(checked(item.Quantity - 1) / Math.Max(1, item.Item.m_shared.m_maxStackSize)) + 1; if (num > 1024) { throw new NotSupportedException("Transfer staging limit exceeded."); } } dictionary2.Add(item, Endpoint("staging-" + dictionary2.Count, new Inventory("Compatibility material staging", (Sprite)null, 8, (num + 7) / 8), () => true)); } List<Action> steps = new List<Action>(); List<string[]> checkpoints = new List<string[]> { Shadows() }; long checkpointCharacters = ((IEnumerable<string>)checkpoints[0]).Sum((Func<string, long>)((string s) => s.Length)); try { foreach (Line line in lines) { TransferEndpoint source2 = dictionary[line.Source]; Inventory before = (Inventory)Call(shadow, snapshot.Invoke(new object[1] { carried.Shadow })); TransferEndpoint value; if (line.PreviewItem != null) { if (!FindSlot(carried.Shadow, out line.SlotX, out line.SlotY)) { return false; } Vector2i position = line.Item.m_gridPos; Vector2i slot = position; slot.x = line.SlotX; slot.y = line.SlotY; AddStep(delegate { //IL_007d: Unknown result type (might be due to invalid IL or missing references) Call(addAt, null, carried.Shadow, source2.Shadow.GetItemAt(position.x, position.y), line.Quantity, slot); }, delegate { //IL_0062: Unknown result type (might be due to invalid IL or missing references) Call(addAt, null, carried.Live, line.Item, line.Quantity, slot); }); AddStep(delegate { if (!source2.Shadow.RemoveItem(source2.Shadow.GetItemAt(position.x, position.y), line.Quantity)) { throw new InvalidOperationException("Selected shadow item changed."); } ProjectBag(source2); }, delegate { if (!source2.Live.RemoveItem(line.Item, line.Quantity)) { throw new InvalidOperationException("Selected source item changed."); } line.Source.Bag?.Persist(Serialized(source2.Live)); }); } else if (dictionary2.TryGetValue(line, out value)) { NativeMove(source2, value, line.Item, line.Quantity, null); before = (Inventory)Call(shadow, snapshot.Invoke(new object[1] { carried.Shadow })); ItemData[] array = value.Shadow.GetAllItems().ToArray(); foreach (ItemData val in array) { NativeMove(value, carried, val, val.m_stack, null); } } else { NativeMove(source2, carried, line.Item, line.Quantity, line.Source.Chest); } if (line.PreviewItem != null && line.Source.Bag != null) { line.Source.Bag.UpdateShadowParent(before, source2.Shadow, Serialized(source2.Shadow)); } if (!FitsPlayer(before)) { return false; } } } catch (NoCapacityException) { return false; } CheckedTransfer.Run(steps, checkpoints, Read, delegate { if (Rules.Missing(costs, (string n) => playerInventory.CountItems(n, -1, true)).Count != 0) { throw new InvalidOperationException("The complete table cost was not delivered."); } }, Recover, Quarantine); reason = ""; return true; void AddStep(Action project, Action execute) { project(); string[] array2 = Shadows(); checkpointCharacters += ((IEnumerable<string>)array2).Sum((Func<string, long>)((string s) => s.Length)); if (steps.Count >= 1024 || checkpointCharacters > 16777216) { throw new NotSupportedException("Enchanting transfer exceeds the bounded recovery-plan size."); } checkpoints.Add(array2); steps.Add(execute); } TransferEndpoint Endpoint(string name, Inventory live, Func<bool> authority2, Source source3 = null) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown if (endpoints.Any((TransferEndpoint e) => e.Live == live)) { throw new InvalidOperationException("Duplicate transfer inventory."); } object obj = snapshot.Invoke(new object[1] { live }); TransferEndpoint transferEndpoint = new TransferEndpoint { Name = name, Live = live, Before = obj, Shadow = (Inventory)Call(shadow, obj), Authority = authority2, Source = source3 }; endpoints.Add(transferEndpoint); return transferEndpoint; } bool FitsPlayer(Inventory val2) { foreach (ItemData allItem in carried.Shadow.GetAllItems()) { ItemData itemAt = val2.GetItemAt(allItem.m_gridPos.x, allItem.m_gridPos.y); if ((itemAt == null || !Same(allItem, itemAt) || allItem.m_stack != itemAt.m_stack) && !ordinarySlot(allItem.m_gridPos.x, allItem.m_gridPos.y)) { return false; } } return true; } void NativeMove(TransferEndpoint from, TransferEndpoint to, ItemData original, int amount, Container chest) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) Vector2i position2 = original.m_gridPos; AddStep(delegate { if (Move(from.Shadow, to.Shadow, from.Shadow.GetItemAt(position2.x, position2.y), amount, null, lease, chest) != amount) { throw new NoCapacityException(); } ProjectBag(from); }, delegate { if (Move(from.Live, to.Live, from.Live.GetItemAt(position2.x, position2.y), amount, player, lease, chest) != amount) { throw new InvalidOperationException("Material transfer was incomplete."); } from.Source?.Bag?.Persist(Serialized(from.Live)); }); } void ProjectBag(TransferEndpoint e) { e.Source?.Bag?.UpdateShadowParent(carried.Shadow, e.Shadow, Serialized(e.Shadow)); } void Quarantine() { recoveryEndpoints = endpoints; foreach (TransferEndpoint item2 in endpoints) { Call(block, null, item2.Live); } } string[] Read() { foreach (TransferEndpoint item3 in endpoints) { if (!item3.Authority() || (bool)Call(isBlocked, null, item3.Live)) { throw new InvalidOperationException("Enchanting endpoint ownership changed or requires recovery: " + item3.Name); } } return endpoints.Select((TransferEndpoint e) => Serialized(e.Live)).ToArray(); } void Recover(string[] array2) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown CheckedTransfer.Match(Read(), array2); foreach (TransferEndpoint item4 in endpoints) { item4.Shadow = (Inventory)Call(shadow, snapshot.Invoke(new object[1] { item4.Live })); } foreach (TransferEndpoint item5 in (from e in endpoints where e != carried orderby (e.Source?.Bag != null) ? 1 : 0 select e).Concat(new TransferEndpoint[1] { carried })) { CheckedTransfer.Match(Read(), array2); item5.Shadow = (Inventory)Call(shadow, item5.Before); ProjectBag(item5); string[] array3 = Shadows(); if (!array2.SequenceEqual<string>(array3, StringComparer.Ordinal)) { Restore(item5.Live, item5.Before, player, lease); item5.Source?.Bag?.Persist(Serialized(item5.Live)); CheckedTransfer.Match(Read(), array3); array2 = array3; } } } string[] Shadows() { return endpoints.Select((TransferEndpoint e) => Serialized(e.Shadow)).ToArray(); } } } internal sealed class Selection { internal readonly ItemData Item; internal readonly int Amount; internal Selection(ItemData item, int amount) { Item = item; Amount = amount; } } internal sealed class TableActions { internal sealed class Plan { internal List<Selection> Selections; internal List<Cost> Costs; internal List<InventoryItemListElement> Rows; } internal static readonly Type[] Panels = new Type[6] { typeof(SacrificeUI), typeof(EnchantUI), typeof(AugmentUI), typeof(DisenchantUI), typeof(ConvertUI), typeof(RuneUI) }; private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private readonly Dictionary<string, MethodInfo> costs = new Dictionary<string, MethodInfo>(); private readonly FieldInfo sacrificeMode; private readonly FieldInfo rarity; private readonly FieldInfo augmentIndex; private readonly FieldInfo runeMode; private readonly FieldInfo runeRarity; private readonly FieldInfo runeItem; private readonly FieldInfo overrideRune; private readonly FieldInfo locked; private readonly MethodInfo costReduction; internal TableActions() { Type type = typeof(SacrificeUI).Assembly.GetType("EpicLoot.CraftingV2.EnchantingUIController", throwOnError: true); string[] array = new string[6] { "GetIdentifyCostForCategory", "GetEnchantCost", "GetAugmentCost", "GetDisenchantCost", "GetRuneExtractCost", "GetRuneEtchCost" }; foreach (string text in array) { costs.Add(text, type.GetMethod(text, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMethodException(text)); } sacrificeMode = Field(typeof(SacrificeUI), "_sacrificeMode"); rarity = Field(typeof(EnchantUI), "_rarity"); augmentIndex = Field(typeof(AugmentUI), "_augmentIndex"); runeMode = Field(typeof(RuneUI), "_runeAction"); runeRarity = Field(typeof(RuneUI), "_selectedRarity"); runeItem = Field(typeof(RuneUI), "_selectedItem"); overrideRune = Field(typeof(RuneUI), "_selectedOverrideRune"); locked = Field(typeof(EnchantingTableUIPanelBase), "_locked"); costReduction = typeof(RuneUI).GetMethod("GetCostReduction", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMethodException("Rune cost reduction"); static FieldInfo Field(Type type2, string name) { return type2.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(type2.FullName, name); } } internal Plan Create(EnchantingTableUIPanelBase panel, EnchantingTable table, RunicBridge bridge) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) List<Tuple<IListElement, int>> list = ((panel is ConvertUI) ? new List<Tuple<IListElement, int>>() : panel.AvailableItems.GetSelectedItems<IListElement>()); if (!(panel is SacrificeUI) && list.Count > 1) { throw new NotSupportedException("Unexpected multiple table targets."); } if (list.Any((Tuple<IListElement, int> x) => !(x.Item1 is InventoryItemListElement))) { throw new NotSupportedException("Unsupported table item row."); } List<InventoryItemListElement> list2 = ((IEnumerable<Tuple<IListElement, int>>)list).Select((Func<Tuple<IListElement, int>, InventoryItemListElement>)((Tuple<IListElement, int> x) => (InventoryItemListElement)x.Item1)).ToList(); List<Selection> list3 = list.Select((Tuple<IListElement, int> x) => new Selection(x.Item1.GetItem(), x.Item2)).ToList(); ItemData val = list3.FirstOrDefault()?.Item; List<InventoryItemListElement> list4 = new List<InventoryItemListElement>(); SacrificeUI val2 = (SacrificeUI)(object)((panel is SacrificeUI) ? panel : null); EnchantingFeature val3; if (val2 != null) { val3 = (EnchantingFeature)0; if (sacrificeMode.GetValue(val2).ToString() == "Identify" && val != null) { list4 = Cost("GetIdentifyCostForCategory", new object[3] { val2.IdentifyStyle.options[val2.IdentifyStyle.value].text, list3.Select((Selection s) => Tuple.Create<ItemData, int>(s.Item, s.Amount)).ToList(), Reduction(val3) }); } } else { EnchantUI val4 = (EnchantUI)(object)((panel is EnchantUI) ? panel : null); if (val4 != null) { val3 = (EnchantingFeature)2; if (val != null) { list4 = Cost("GetEnchantCost", new object[2] { val, rarity.GetValue(val4) }); } } else { AugmentUI val5 = (AugmentUI)(object)((panel is AugmentUI) ? panel : null); if (val5 != null) { val3 = (EnchantingFeature)3; if (val != null) { list4 = Cost("GetAugmentCost", new object[2] { val, augmentIndex.GetValue(val5) }); } } else if (panel is DisenchantUI) { val3 = (EnchantingFeature)4; if (val != null) { list4 = Cost("GetDisenchantCost", new object[1] { val }); } } else { ConvertUI val6 = (ConvertUI)(object)((panel is ConvertUI) ? panel : null); if (val6 != null) { val3 = (EnchantingFeature)1; list4 = ConvertUI.GetConversionCost(((EnchantingTableUIPanelBase)val6).AvailableItems.GetSelectedItems<ConversionRecipeUnity>()); } else { RuneUI val7 = (RuneUI)(object)((panel is RuneUI) ? panel : null); if (val7 == null) { throw new NotSupportedException("Unsupported table panel."); } val3 = (EnchantingFeature)5; bool flag = runeMode.GetValue(val7).ToString() == "Etch"; if (val != null) { list4 = Cost(flag ? "GetRuneEtchCost" : "GetRuneExtractCost", new object[3] { val, runeRarity.GetValue(val7), Reduction(val3) }); } if (flag) { Tuple<InventoryItemListElement, int> singleSelectedItem = val7.AvailableRunes.GetSingleSelectedItem<InventoryItemListElement>(); if (singleSelectedItem != null) { list2.Add(singleSelectedItem.Item1); list3.Add(new Selection(singleSelectedItem.Item1.Item, 1)); } } } } } } if (!table.IsFeatureAvailable(val3) || !table.IsFeatureUnlocked(val3)) { return null; } if (Player.m_localPlayer.NoCostCheat()) { list4.Clear(); } return new Plan { Rows = list2, Selections = list3, Costs = Rules.Normalize(list4.Select((InventoryItemListElement r) => new Cost(r.Item?.m_shared?.m_name, bridge.Resource(r.Item), r.Item?.m_stack ?? 0))) }; List<InventoryItemListElement> Cost(string method, params object[] args) { return (List<InventoryItemListElement>)costs[method].Invoke(null, args); } float Reduction(EnchantingFeature f) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) return (float)costReduction.Invoke(null, new object[1] { table.GetFeatureCurrentValue(f).Item1 }); } } internal void Remap(EnchantingTableUIPanelBase panel, Plan plan, Dictionary<ItemData, ItemData> map) { foreach (InventoryItemListElement row in plan.Rows) { row.Item = map[row.Item]; } if (!(panel is RuneUI)) { return; } FieldInfo[] array = new FieldInfo[2] { runeItem, overrideRune }; foreach (FieldInfo fieldInfo in array) { object? value = fieldInfo.GetValue(panel); ItemData val = (ItemData)((value is ItemData) ? value : null); if (val != null && map.TryGetValue(val, out var value2)) { fieldInfo.SetValue(panel, value2); } } } internal void RefreshCosts(EnchantingTableUIPanelBase panel) { if (Object.op_Implicit((Object)(object)panel) && ((Component)panel).gameObject.activeInHierarchy && !(bool)locked.GetValue(panel)) { ((object)panel).GetType().GetMethod("OnSelectedItemsChanged", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(panel, null); } } } }