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 CraftFromContainers v0.1.4
BepInEx/plugins/CraftFromContainers/CraftFromContainers.dll
Decompiled 6 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")] [assembly: AssemblyCompany("CraftFromContainers")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.4.0")] [assembly: AssemblyInformationalVersion("0.1.4+1e02ab00b7a5cdc2c410824a9588f86bf4118091")] [assembly: AssemblyProduct("CraftFromContainers")] [assembly: AssemblyTitle("CraftFromContainers")] [assembly: AssemblyVersion("0.1.4.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CraftFromContainers { internal static class ContainerIndex { private sealed class Entry { internal readonly Container Container; internal readonly Inventory Inventory; internal readonly ZNetView View; internal readonly Action OnChanged; internal readonly Dictionary<ItemKey, int> Amounts = new Dictionary<ItemKey, int>(); internal readonly Dictionary<ItemKey, ItemData> Items = new Dictionary<ItemKey, ItemData>(); internal readonly Dictionary<QueryKey, LinkedListNode<ItemData>> Nodes = new Dictionary<QueryKey, LinkedListNode<ItemData>>(); internal bool Registered = true; internal bool NeedsRebuild = true; internal bool Active; internal bool Queued; internal bool AccessErrorReported; internal bool LoadErrorReported; internal Entry(Container container, Inventory inventory, ZNetView view) { Container = container; Inventory = inventory; View = view; OnChanged = delegate { MarkDirty(this); }; } } private readonly struct QueryKey : IEquatable<QueryKey> { private readonly string? _name; private readonly int _quality; internal QueryKey(string? name, int quality) { _name = name; _quality = ((quality < 0) ? (-1) : quality); } public bool Equals(QueryKey other) { if (_quality == other._quality) { return _name == other._name; } return false; } public override bool Equals(object? other) { if (other is QueryKey other2) { return Equals(other2); } return false; } public override int GetHashCode() { return ((_name?.GetHashCode() ?? 0) * 397) ^ _quality; } } private readonly struct ItemKey : IEquatable<ItemKey> { internal readonly string Name; internal readonly int Quality; internal readonly int WorldLevel; internal ItemKey(string name, int quality, int worldLevel) { Name = name; Quality = quality; WorldLevel = worldLevel; } public bool Equals(ItemKey other) { if (Quality == other.Quality && WorldLevel == other.WorldLevel) { return Name == other.Name; } return false; } public override bool Equals(object? other) { if (other is ItemKey other2) { return Equals(other2); } return false; } public override int GetHashCode() { return (((Name.GetHashCode() * 397) ^ Quality) * 397) ^ WorldLevel; } } private static readonly Func<Container, long, bool> CheckAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Container, bool> LoadInventory = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<Container, ZNetView> GetView = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview"); private static readonly FieldRef<Container, bool> IsLoading = AccessTools.FieldRefAccess<Container, bool>("m_loading"); private static readonly Dictionary<Container, Entry> Lookup = new Dictionary<Container, Entry>(); private static readonly List<Entry> Entries = new List<Entry>(); private static readonly List<Entry> NearbyEntries = new List<Entry>(); private static readonly List<Entry> Dirty = new List<Entry>(); private static readonly Dictionary<QueryKey, int> AllWorldCounts = new Dictionary<QueryKey, int>(); private static readonly Dictionary<QueryKey, int> CurrentWorldCounts = new Dictionary<QueryKey, int>(); private static readonly Dictionary<QueryKey, LinkedList<ItemData>> Candidates = new Dictionary<QueryKey, LinkedList<ItemData>>(); private static readonly Dictionary<string, HashSet<Entry>> SourcesByName = new Dictionary<string, HashSet<Entry>>(StringComparer.Ordinal); private static int _preparedFrame = -1; private static int _worldLevel = int.MinValue; private static Player? _player; private static float _nextRefresh; private static float _lastRadius = -1f; private static bool _refreshRequested = true; private static bool _preparing; private static int _activeCount; private static long _queries; private static long _refreshes; private static long _rebuilds; internal static long Revision { get; private set; } internal static string Diagnostics => $"containers={Entries.Count}, nearby={_activeCount}, queries={_queries}, " + $"refreshes={_refreshes}, inventoryRebuilds={_rebuilds}, revision={Revision}"; internal static void Register(Container container) { if (Object.op_Implicit((Object)(object)container) && !Lookup.ContainsKey(container) && !container.m_autoDestroyEmpty && !Object.op_Implicit((Object)(object)((Component)container).GetComponent<TombStone>())) { Inventory inventory = container.GetInventory(); ZNetView val = GetView.Invoke(container); if (inventory != null && Object.op_Implicit((Object)(object)val) && val.IsValid()) { Entry entry = new Entry(container, inventory, val); Lookup.Add(container, entry); Entries.Add(entry); inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, entry.OnChanged); _refreshRequested = true; } } } internal static void Unregister(Container container) { if (container != null && Lookup.TryGetValue(container, out Entry value)) { RemoveEntry(value); Entries.Remove(value); } } internal static void Reset() { foreach (Entry entry in Entries) { Inventory inventory = entry.Inventory; inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, entry.OnChanged); } Entries.Clear(); NearbyEntries.Clear(); Lookup.Clear(); Dirty.Clear(); AllWorldCounts.Clear(); CurrentWorldCounts.Clear(); Candidates.Clear(); SourcesByName.Clear(); _player = null; _activeCount = 0; _worldLevel = int.MinValue; _preparedFrame = -1; _nextRefresh = 0f; _refreshRequested = true; _preparing = false; Revision++; } internal static void Tick() { Prepare(); } internal static int Count(string? name, int quality = -1, bool matchWorldLevel = true) { Prepare(); _queries++; QueryKey key = new QueryKey(name, quality); if (!(matchWorldLevel ? CurrentWorldCounts : AllWorldCounts).TryGetValue(key, out var value)) { return 0; } return value; } internal static ItemData? Find(string name, int quality = -1) { Prepare(); _queries++; if (!Candidates.TryGetValue(new QueryKey(name, quality), out LinkedList<ItemData> value)) { return null; } return value.First?.Value; } internal static List<Container> GetNearby() { Prepare(); List<Container> list = new List<Container>(_activeCount); foreach (Entry nearbyEntry in NearbyEntries) { if (nearbyEntry.Active && CanAccess(nearbyEntry.Container)) { list.Add(nearbyEntry.Container); } } return list; } internal static List<Container> GetSourcesFor(IEnumerable<string> names) { Prepare(); List<Container> list = new List<Container>(); HashSet<Entry> hashSet = new HashSet<Entry>(); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); foreach (string name in names) { if (name == null || !hashSet2.Add(name) || !SourcesByName.TryGetValue(name, out HashSet<Entry> value)) { continue; } foreach (Entry item in value) { if (hashSet.Add(item) && item.Active && CanAccess(item.Container)) { list.Add(item.Container); } } } return list; } internal static void ForceRefresh() { _refreshRequested = true; Prepare(); } internal static bool CanAccess(Container container, bool ignoreInUse = false) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)container) || !Lookup.TryGetValue(container, out Entry value)) { return false; } return CanAccess(value, localPlayer, ((Component)localPlayer).transform.position, RadiusSquared(), ignoreInUse); } private static float RadiusSquared() { float num = Mathf.Max(0f, Plugin.SearchRadius.Value); return num * num; } private static bool CanAccess(Entry entry, Player player, Vector3 position, float radiusSquared, bool ignoreInUse) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) Container container = entry.Container; if (Object.op_Implicit((Object)(object)container) && Object.op_Implicit((Object)(object)entry.View) && entry.View.IsValid() && ((Component)container).gameObject.activeInHierarchy) { Vector3 val = ((Component)container).transform.position - position; if (!(((Vector3)(ref val)).sqrMagnitude > radiusSquared)) { if (!ignoreInUse && (container.IsInUse() || entry.View.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0 || ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse()))) { return false; } try { return CheckAccess(container, player.GetPlayerID()) && (!container.m_checkGuardStone || PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false)); } catch (Exception ex) { if (!entry.AccessErrorReported) { entry.AccessErrorReported = true; Plugin.Log.LogWarning((object)("Skipping container '" + ((Object)container).name + "': access check failed: " + ex.Message)); } return false; } } } return false; } private static void Prepare() { if (_preparing) { return; } int frameCount = Time.frameCount; if (_preparedFrame == frameCount && !_refreshRequested && Dirty.Count == 0) { return; } _preparing = true; try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)(object)_player) { _player = localPlayer; _refreshRequested = true; } if (_worldLevel != Game.m_worldLevel) { _worldLevel = Game.m_worldLevel; AllWorldCounts.Clear(); CurrentWorldCounts.Clear(); Candidates.Clear(); SourcesByName.Clear(); foreach (Entry entry in Entries) { entry.Nodes.Clear(); if (entry.Active) { AddContributions(entry); } } Revision++; } float value = Plugin.SearchRadius.Value; if (value != _lastRadius) { _lastRadius = value; _refreshRequested = true; } if (_refreshRequested || Time.unscaledTime >= _nextRefresh) { _refreshRequested = false; _nextRefresh = Time.unscaledTime + Mathf.Max(0.1f, Plugin.RefreshInterval.Value); RefreshNearby(localPlayer); } DrainDirty(); _preparedFrame = frameCount; } finally { _preparing = false; } } private static void RefreshNearby(Player player) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) _refreshes++; Vector3 position = (Object.op_Implicit((Object)(object)player) ? ((Component)player).transform.position : Vector3.zero); float radiusSquared = RadiusSquared(); for (int num = Entries.Count - 1; num >= 0; num--) { Entry entry = Entries[num]; if (!Object.op_Implicit((Object)(object)entry.Container) || !Object.op_Implicit((Object)(object)entry.View) || !entry.View.IsValid()) { RemoveEntry(entry); Entries.RemoveAt(num); continue; } if (!Object.op_Implicit((Object)(object)player) || !CanAccess(entry, player, position, radiusSquared, ignoreInUse: false)) { if (entry.Active) { RemoveContributions(entry); entry.Active = false; NearbyEntries.Remove(entry); _activeCount--; Revision++; } continue; } try { LoadInventory(entry.Container); } catch (Exception ex) { if (!entry.LoadErrorReported) { entry.LoadErrorReported = true; Plugin.Log.LogWarning((object)("Could not refresh '" + ((Object)entry.Container).name + "': " + ex.Message)); } if (entry.Active) { RemoveContributions(entry); entry.Active = false; NearbyEntries.Remove(entry); _activeCount--; Revision++; } continue; } if (!entry.Active && !IsLoading.Invoke(entry.Container)) { if (entry.NeedsRebuild) { Rebuild(entry); } entry.Active = true; NearbyEntries.Add(entry); _activeCount++; AddContributions(entry); Revision++; } } } private static void MarkDirty(Entry entry) { entry.NeedsRebuild = true; if (entry.Registered && entry.Active && !entry.Queued) { entry.Queued = true; Dirty.Add(entry); } } private static void DrainDirty() { for (int num = Dirty.Count - 1; num >= 0; num--) { Entry entry = Dirty[num]; if (!entry.Registered || !entry.Active || !Object.op_Implicit((Object)(object)entry.Container) || !IsLoading.Invoke(entry.Container)) { Dirty.RemoveAt(num); entry.Queued = false; if (entry.Registered && entry.Active && Object.op_Implicit((Object)(object)entry.Container) && entry.NeedsRebuild) { Rebuild(entry); } } } } private static void Rebuild(Entry entry) { if (entry.Active) { RemoveContributions(entry); } entry.Amounts.Clear(); entry.Items.Clear(); foreach (ItemData allItem in entry.Inventory.GetAllItems()) { if (allItem != null && allItem.m_shared != null) { ItemKey key = new ItemKey(allItem.m_shared.m_name, allItem.m_quality, allItem.m_worldLevel); entry.Amounts.TryGetValue(key, out var value); entry.Amounts[key] = value + allItem.m_stack; if (!entry.Items.ContainsKey(key)) { entry.Items.Add(key, allItem); } } } entry.NeedsRebuild = false; _rebuilds++; if (entry.Active) { AddContributions(entry); } Revision++; } private static void AddContributions(Entry entry) { foreach (KeyValuePair<ItemKey, int> amount in entry.Amounts) { AddAmount(AllWorldCounts, amount.Key, amount.Value); if (amount.Key.WorldLevel >= _worldLevel) { AddAmount(CurrentWorldCounts, amount.Key, amount.Value); } } foreach (KeyValuePair<ItemKey, ItemData> item in entry.Items) { if (item.Key.WorldLevel >= _worldLevel) { if (!SourcesByName.TryGetValue(item.Key.Name, out HashSet<Entry> value)) { value = new HashSet<Entry>(); SourcesByName.Add(item.Key.Name, value); } value.Add(entry); AddCandidate(entry, new QueryKey(item.Key.Name, item.Key.Quality), item.Value); AddCandidate(entry, new QueryKey(item.Key.Name, -1), item.Value); } } } private static void RemoveContributions(Entry entry) { foreach (KeyValuePair<ItemKey, int> amount in entry.Amounts) { AddAmount(AllWorldCounts, amount.Key, -amount.Value); if (amount.Key.WorldLevel >= _worldLevel) { AddAmount(CurrentWorldCounts, amount.Key, -amount.Value); } } foreach (KeyValuePair<QueryKey, LinkedListNode<ItemData>> node in entry.Nodes) { LinkedList<ItemData>? list = node.Value.List; list.Remove(node.Value); if (list.Count == 0) { Candidates.Remove(node.Key); } } entry.Nodes.Clear(); foreach (ItemKey key in entry.Items.Keys) { if (SourcesByName.TryGetValue(key.Name, out HashSet<Entry> value)) { value.Remove(entry); if (value.Count == 0) { SourcesByName.Remove(key.Name); } } } } private static void AddAmount(Dictionary<QueryKey, int> counts, ItemKey key, int delta) { AddAmount(counts, new QueryKey(key.Name, key.Quality), delta); AddAmount(counts, new QueryKey(key.Name, -1), delta); AddAmount(counts, new QueryKey(null, key.Quality), delta); AddAmount(counts, new QueryKey(null, -1), delta); } private static void AddAmount(Dictionary<QueryKey, int> counts, QueryKey key, int delta) { counts.TryGetValue(key, out var value); int num = value + delta; if (num == 0) { counts.Remove(key); } else { counts[key] = num; } } private static void AddCandidate(Entry entry, QueryKey key, ItemData item) { if (!entry.Nodes.ContainsKey(key)) { if (!Candidates.TryGetValue(key, out LinkedList<ItemData> value)) { value = new LinkedList<ItemData>(); Candidates.Add(key, value); } entry.Nodes.Add(key, value.AddLast(item)); } } private static void RemoveEntry(Entry entry) { Inventory inventory = entry.Inventory; inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, entry.OnChanged); entry.Registered = false; if (entry.Active) { RemoveContributions(entry); entry.Active = false; NearbyEntries.Remove(entry); _activeCount--; Revision++; } Lookup.Remove(entry.Container); } } [HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")] internal static class CookingStationFuelPatch { private static readonly Func<CookingStation, Switch, Humanoid, ItemData, bool> Add = AccessTools.MethodDelegate<Func<CookingStation, Switch, Humanoid, ItemData, bool>>(AccessTools.Method(typeof(CookingStation), "OnAddFuelSwitch", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(CookingStation __instance, Switch sw, Humanoid user, ItemData item, ZNetView ___m_nview, ref bool __result) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) if (item == null) { Player player = (Player)(object)((user is Player) ? user : null); if (player != null && ResourceActions.Local(player) && !InventoryBridge.Executing) { CookingStation station = __instance; ItemDrop fuel = station.m_fuelItem; ZNetView view = ___m_nview; if (!station.m_useFuel || !Object.op_Implicit((Object)(object)sw) || (Object)(object)station.m_addFuelSwitch != (Object)(object)sw || !Object.op_Implicit((Object)(object)fuel) || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || view.GetZDO() == null) { return true; } string name = fuel.m_itemData.m_shared.m_name; if (view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > (float)(station.m_maxFuel - 1) || ((Humanoid)player).GetInventory().HaveItem(name, true) || ContainerIndex.Count(name) <= 0) { return true; } Vector3 start = ((Component)station).transform.InverseTransformPoint(((Component)player).transform.position); bool num = ResourceActions.Begin(player, () => new List<ResourcePlanner.Demand> { new ResourcePlanner.Demand(name, 1) }, delegate { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)station) && Object.op_Implicit((Object)(object)sw) && station.m_useFuel && (Object)(object)station.m_addFuelSwitch == (Object)(object)sw && (Object)(object)station.m_fuelItem == (Object)(object)fuel && Object.op_Implicit((Object)(object)fuel) && fuel.m_itemData.m_shared.m_name == name && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.GetZDO() != null && view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) <= (float)(station.m_maxFuel - 1)) { Vector3 val = ((Component)station).transform.InverseTransformPoint(((Component)player).transform.position) - start; return ((Vector3)(ref val)).sqrMagnitude < 1f; } return false; }, delegate { Add(station, sw, (Humanoid)(object)player, null); }); if (!num) { __result = false; } return num; } } return true; } } [HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")] internal static class CraftingPanelRefresh { private static readonly Action<InventoryGui, bool> Rebuild = AccessTools.MethodDelegate<Action<InventoryGui, bool>>(AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null), (object)null, true); private static InventoryGui? _gui; private static long _builtRevision = -1L; private static void Prefix() { ContainerIndex.Tick(); } private static void Postfix(InventoryGui __instance) { _gui = __instance; _builtRevision = ContainerIndex.Revision; } internal static void Tick() { InventoryGui instance = InventoryGui.instance; if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && InventoryGui.IsVisible() && (instance != _gui || _builtRevision != ContainerIndex.Revision)) { Rebuild(instance, arg2: false); } } internal static void Reset() { _gui = null; _builtRevision = -1L; } } [HarmonyPatch(typeof(Fireplace), "Interact")] internal static class FireplaceActionPatch { private static bool Prefix(Fireplace __instance, Humanoid user, bool hold, bool alt, ZNetView ___m_nview, float ___m_lastUseTime, ref bool __result) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) Player player = (Player)(object)((user is Player) ? user : null); if (player == null || !ResourceActions.Local(player) || InventoryBridge.Executing) { return true; } Fireplace fireplace = __instance; if (hold && (fireplace.m_holdRepeatInterval <= 0f || Time.time - ___m_lastUseTime < fireplace.m_holdRepeatInterval)) { return true; } ZNetView view = ___m_nview; if (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || view.GetZDO() == null) { __result = false; return false; } if (WouldToggle(fireplace, view, hold, alt) || !fireplace.m_canRefill || fireplace.m_infiniteFuel) { return true; } ItemDrop fuel = fireplace.m_fuelItem; if (!Object.op_Implicit((Object)(object)fuel)) { __result = false; return false; } string name = fuel.m_itemData.m_shared.m_name; if (Full(fireplace, view) || ((Humanoid)player).GetInventory().HaveItem(name, true) || ContainerIndex.Count(name) <= 0) { return true; } Vector3 start = ((Component)fireplace).transform.InverseTransformPoint(((Component)player).transform.position); bool num = ResourceActions.Begin(player, () => new List<ResourcePlanner.Demand> { new ResourcePlanner.Demand(name, 1) }, delegate { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)fireplace) && Object.op_Implicit((Object)(object)view) && view.IsValid() && view.GetZDO() != null && fireplace.m_canRefill && !fireplace.m_infiniteFuel && (Object)(object)fireplace.m_fuelItem == (Object)(object)fuel && Object.op_Implicit((Object)(object)fuel) && fuel.m_itemData.m_shared.m_name == name) { Vector3 val = ((Component)fireplace).transform.InverseTransformPoint(((Component)player).transform.position) - start; if (((Vector3)(ref val)).sqrMagnitude < 1f && !Full(fireplace, view)) { return !WouldToggle(fireplace, view, hold, alt); } } return false; }, delegate { fireplace.Interact((Humanoid)(object)player, hold, alt); }); if (!num) { __result = false; } return num; } private static bool WouldToggle(Fireplace fireplace, ZNetView view, bool hold, bool alt) { if (fireplace.m_canTurnOff && !hold && !alt) { return view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > 0f; } return false; } private static bool Full(Fireplace fireplace, ZNetView view) { return (float)Mathf.CeilToInt(view.GetZDO().GetFloat(ZDOVars.s_fuel, 0f)) >= fireplace.m_maxFuel; } } internal static class InventoryBridge { private static int _depth; internal static ResourceTransaction? Transaction; internal static List<Container>? RestrictedSources; internal static bool Executing; internal static bool IsActive(Inventory inventory) { if (_depth > 0 && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return inventory == ((Humanoid)Player.m_localPlayer).GetInventory(); } return false; } internal static int Enter() { int depth = _depth; _depth++; return depth; } internal static void Leave(int previous) { _depth = previous; } internal static void Reset() { _depth = 0; Transaction = null; RestrictedSources = null; Executing = false; } internal static int ChestCount(string name, int quality, bool worldLevel) { if (RestrictedSources == null) { return ContainerIndex.Count(name, quality, worldLevel); } int num = 0; foreach (Container restrictedSource in RestrictedSources) { if (Object.op_Implicit((Object)(object)restrictedSource) && restrictedSource.GetInventory() != null) { num += restrictedSource.GetInventory().CountItems(name, quality, worldLevel); } } return num; } internal static ItemData? Find(string name, int quality, bool prefab = false) { if (RestrictedSources == null && !prefab) { return ContainerIndex.Find(name, quality); } foreach (Container item in RestrictedSources ?? ContainerIndex.GetNearby()) { if (Object.op_Implicit((Object)(object)item)) { Inventory inventory = item.GetInventory(); ItemData val = ((inventory != null) ? inventory.GetItem(name, quality, prefab) : null); if (val != null) { return val; } } } return null; } } [HarmonyPatch] internal static class ResourceReadScopePatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Player), "HaveRequirementItems", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2] { typeof(Piece), typeof(RequirementMode) }, (Type[])null); yield return AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null); } private static void Prefix(out int __state) { __state = InventoryBridge.Enter(); } private static void Finalizer(int __state) { InventoryBridge.Leave(__state); } } [HarmonyPatch(typeof(Inventory), "CountItems")] internal static class CountItemsPatch { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (InventoryBridge.IsActive(__instance)) { __result += InventoryBridge.ChestCount(name, quality, matchWorldLevel); } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] internal static class HaveItemPatch { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (!__result && InventoryBridge.IsActive(__instance)) { __result = InventoryBridge.ChestCount(name, -1, matchWorldLevel) > 0; } } } [HarmonyPatch(typeof(Inventory), "GetItem", new Type[] { typeof(string), typeof(int), typeof(bool) })] internal static class GetItemPatch { private static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result) { if (__result == null && InventoryBridge.IsActive(__instance)) { __result = InventoryBridge.Find(name, quality, isPrefabName); } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] internal static class RemoveByNamePatch { private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased) { if (InventoryBridge.Transaction == null || !InventoryBridge.IsActive(__instance)) { return true; } InventoryBridge.Transaction.Consume(name, amount, itemQuality, worldLevelBased); return false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData), typeof(int) })] internal static class RemoveByItemPatch { private static bool Prefix(Inventory __instance, ItemData item, int amount, ref bool __result) { if (InventoryBridge.Transaction == null || !InventoryBridge.IsActive(__instance) || __instance.ContainsItem(item)) { return true; } InventoryBridge.Transaction.Consume(item.m_shared.m_name, amount, item.m_quality, worldLevelBased: true); __result = true; return false; } } [HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[] { typeof(Requirement[]), typeof(int), typeof(bool) })] internal static class CheatedResourcePatch { private static void Postfix(Inventory __instance, ref bool __result) { if (InventoryBridge.IsActive(__instance) && InventoryBridge.Transaction != null) { __result |= InventoryBridge.Transaction.Cheated; } } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] internal static class RequirementTextPatch { private static readonly Dictionary<Transform, TMP_Text> Labels = new Dictionary<Transform, TMP_Text>(); private static void Postfix(Transform elementRoot, Requirement req, Player player, int quality, int craftMultiplier, bool __result) { if (!Plugin.ShowAvailable.Value || !__result || !Object.op_Implicit((Object)(object)req.m_resItem) || !Object.op_Implicit((Object)(object)player)) { return; } int num = req.GetAmount(quality) * craftMultiplier; if (num <= 0) { return; } if (!Labels.TryGetValue(elementRoot, out TMP_Text value) || !Object.op_Implicit((Object)(object)value)) { Transform obj = elementRoot.Find("res_amount"); value = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if (!Object.op_Implicit((Object)(object)value)) { return; } Labels[elementRoot] = value; } int num2 = ((Humanoid)player).GetInventory().CountItems(req.m_resItem.m_itemData.m_shared.m_name, -1, true); string text = num + "/" + num2; if (value.text != text) { value.text = text; } } internal static void Clear() { Labels.Clear(); } } internal sealed class InventorySnapshot { private sealed class InventoryState { internal readonly Inventory Inventory; internal readonly Container? Container; internal readonly List<ItemState> Items = new List<ItemState>(); internal InventoryState(Inventory inventory, Container? container) { Inventory = inventory; Container = container; foreach (ItemData allItem in inventory.GetAllItems()) { Items.Add(new ItemState(allItem)); } } internal void RestoreItems() { List<ItemData> allItems = Inventory.GetAllItems(); foreach (ItemState item in Items) { item.Restore(); } allItems.Clear(); foreach (ItemState item2 in Items) { allItems.Add(item2.Original); } } } private sealed class ItemState { internal readonly ItemData Original; internal readonly ItemData Copy; private readonly Dictionary<string, string> _customData; private readonly FieldInfo[] _fields; internal ItemState(ItemData item) { Original = item; Copy = item.Clone(); _customData = item.m_customData; _fields = FieldsFor(((object)item).GetType()); } internal void Restore() { FieldInfo[] fields = _fields; foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(Original, fieldInfo.GetValue(Copy)); } _customData.Clear(); foreach (KeyValuePair<string, string> customDatum in Copy.m_customData) { _customData.Add(customDatum.Key, customDatum.Value); } Original.m_customData = _customData; } } private static readonly Action<Inventory, bool, bool> NotifyChanged = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null), (object)null, true); private static readonly Action<Container> SaveContainer = AccessTools.MethodDelegate<Action<Container>>(AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Humanoid> SetupEquipment = AccessTools.MethodDelegate<Action<Humanoid>>(AccessTools.Method(typeof(Humanoid), "SetupEquipment", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldInfo[] EquipmentFields = GetEquipmentFields(); private static readonly FieldInfo HiddenLeft = AccessTools.Field(typeof(Humanoid), "m_hiddenLeftItem"); private static readonly FieldInfo HiddenRight = AccessTools.Field(typeof(Humanoid), "m_hiddenRightItem"); private static readonly Dictionary<Type, FieldInfo[]> ItemFields = new Dictionary<Type, FieldInfo[]>(); private readonly Player _player; private readonly List<InventoryState> _inventories = new List<InventoryState>(); private readonly ItemData?[] _equipment = (ItemData?[])(object)new ItemData[EquipmentFields.Length]; private readonly ItemData? _hiddenLeft; private readonly ItemData? _hiddenRight; private bool _restored; internal InventorySnapshot(Player player, IEnumerable<Container> sources) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)player)) { throw new ArgumentNullException("player"); } _player = player; HashSet<Inventory> hashSet = new HashSet<Inventory>(); Inventory inventory = ((Humanoid)player).GetInventory(); _inventories.Add(new InventoryState(inventory, null)); hashSet.Add(inventory); foreach (Container source in sources) { if (!Object.op_Implicit((Object)(object)source) || !NetworkLeases.Owns(source)) { throw new InvalidOperationException("Cannot snapshot a container without its current synchronized lease."); } Inventory inventory2 = source.GetInventory(); if (inventory2 == null) { throw new InvalidOperationException("A source inventory disappeared before snapshot."); } if (hashSet.Add(inventory2)) { _inventories.Add(new InventoryState(inventory2, source)); } } for (int i = 0; i < EquipmentFields.Length; i++) { _equipment[i] = (ItemData)EquipmentFields[i].GetValue(player); } _hiddenLeft = (ItemData)HiddenLeft.GetValue(player); _hiddenRight = (ItemData)HiddenRight.GetValue(player); } internal void Restore() { if (_restored) { return; } if (!Object.op_Implicit((Object)(object)_player) || ((Humanoid)_player).GetInventory() != _inventories[0].Inventory) { Fail("The player inventory changed before rollback."); } foreach (InventoryState inventory in _inventories) { if (inventory.Container != null && (!Object.op_Implicit((Object)(object)inventory.Container) || !NetworkLeases.Owns(inventory.Container))) { Fail("A container lease expired before rollback; refusing to overwrite another owner's inventory."); } } List<Exception> list = new List<Exception>(); Attempt(delegate { ((Humanoid)_player).UnequipAllItems(); }, list); Attempt(delegate { HiddenLeft.SetValue(_player, null); }, list); Attempt(delegate { HiddenRight.SetValue(_player, null); }, list); foreach (InventoryState inventory2 in _inventories) { Attempt(inventory2.RestoreItems, list); } ItemData[] equipment = _equipment; foreach (ItemData item in equipment) { if (item != null) { Attempt(delegate { ((Humanoid)_player).EquipItem(item, false); }, list); } } int index; for (index = 0; index < EquipmentFields.Length; index++) { Attempt(delegate { EquipmentFields[index].SetValue(_player, _equipment[index]); }, list); } Attempt(delegate { HiddenLeft.SetValue(_player, _hiddenLeft); }, list); Attempt(delegate { HiddenRight.SetValue(_player, _hiddenRight); }, list); foreach (ItemState item2 in _inventories[0].Items) { item2.Original.m_equipped = item2.Copy.m_equipped; } Attempt(delegate { SetupEquipment((Humanoid)(object)_player); }, list); foreach (InventoryState state in _inventories) { if (state.Container != null && (!Object.op_Implicit((Object)(object)state.Container) || !NetworkLeases.Owns(state.Container))) { list.Add(new InvalidOperationException("Container ownership changed during rollback; saving was refused.")); continue; } Attempt(delegate { NotifyChanged(state.Inventory, arg2: false, arg3: false); }, list); if (state.Container == null) { continue; } if (Object.op_Implicit((Object)(object)state.Container) && NetworkLeases.Owns(state.Container)) { Attempt(delegate { SaveContainer(state.Container); }, list); } else { list.Add(new InvalidOperationException("Container ownership changed before rollback save.")); } } if (list.Count != 0) { AggregateException ex = new AggregateException("Inventory rollback could not complete every restoration step.", list); Plugin.Log.LogError((object)ex); throw ex; } _restored = true; } private static void Attempt(Action operation, List<Exception> failures) { try { operation(); } catch (Exception item) { failures.Add(item); } } private static void Fail(string message) { InvalidOperationException ex = new InvalidOperationException(message); Plugin.Log.LogError((object)ex); throw ex; } private static FieldInfo[] GetEquipmentFields() { string[] array = new string[9] { "m_rightItem", "m_leftItem", "m_chestItem", "m_legItem", "m_helmetItem", "m_ammoItem", "m_shoulderItem", "m_utilityItem", "m_trinketItem" }; FieldInfo[] array2 = new FieldInfo[array.Length]; for (int i = 0; i < array.Length; i++) { array2[i] = AccessTools.Field(typeof(Humanoid), array[i]) ?? throw new MissingFieldException(typeof(Humanoid).FullName, array[i]); } return array2; } private static FieldInfo[] FieldsFor(Type type) { if (ItemFields.TryGetValue(type, out FieldInfo[] value)) { return value; } List<FieldInfo> list = new List<FieldInfo>(); Type type2 = type; while (type2 != null && type2 != typeof(object)) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsInitOnly && (!(fieldInfo.DeclaringType == typeof(ItemData)) || !(fieldInfo.Name == "m_customData"))) { list.Add(fieldInfo); } } type2 = type2.BaseType; } value = list.ToArray(); ItemFields.Add(type, value); return value; } } internal static class NetworkLeases { private sealed class Entry { internal Container Container; internal ZNetView View; internal long ExpectedOwner; internal byte[]? Snapshot; internal bool Synchronized; internal bool Requested; internal float RetryAfter; } private sealed class Session { internal readonly string Token = Guid.NewGuid().ToString("N"); internal readonly List<Entry> Entries = new List<Entry>(); internal Player Player; internal Action Ready; internal Action<string> Failure; internal float Started; internal bool Executing; internal bool AllowOwnOpen; } [HarmonyPatch] private static class NativeRequestPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Container), "RPC_RequestOpen", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_RequestStack", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Container), "RPC_RequestTakeAll", (Type[])null, (Type[])null); } [HarmonyPriority(800)] [HarmonyBefore(new string[] { "com.maxsch.valheim.MultiUserChest" })] private static void Prefix(Container __instance) { YieldToContainerUse(__instance); } } [HarmonyPatch(typeof(Container), "RPC_TakeAllResponse")] private static class TakeAllResponsePatch { private static readonly Action<Container, long, bool> Respond = AccessTools.MethodDelegate<Action<Container, long, bool>>(AccessTools.Method(typeof(Container), "RPC_TakeAllResponse", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(Container __instance, long uid, bool granted) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!granted || (Object)(object)replayingTakeAll == (Object)(object)__instance) { return true; } Player player = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)player)) { return false; } Vector3 startedAt = ((Component)player).transform.position; bool startedOpen = Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)OpenContainerField.Invoke(InventoryGui.instance) == (Object)(object)__instance; if (current != null && current.AllowOwnOpen && current.Entries.Count == 1 && (Object)(object)current.Entries[0].Container == (Object)(object)__instance) { return false; } if (current == null || !current.Executing) { YieldToContainerUse(__instance); } if (current != null && !current.Executing) { Fail(current, "TakeAll replaced the pending resource operation. No resources were consumed."); } Acquire(player, new List<Container> { __instance }, delegate { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)player)) { Vector3 val = ((Component)player).transform.position - startedAt; if (!(((Vector3)(ref val)).sqrMagnitude > 9f) && (!startedOpen || (Object.op_Implicit((Object)(object)InventoryGui.instance) && !((Object)(object)OpenContainerField.Invoke(InventoryGui.instance) != (Object)(object)__instance)))) { replayingTakeAll = __instance; try { Respond(__instance, uid, arg3: true); } finally { replayingTakeAll = null; } } } }, delegate { if (Object.op_Implicit((Object)(object)player)) { ((Character)player).Message((MessageType)2, "$msg_cantopen", 0, (Sprite)null, false); } }, allowOwnOpen: true); return false; } } private const string RequestRpc = "CFC_RequestLease_v1"; private const string TakeAllRequestRpc = "CFC_RequestTakeAllLease_v1"; private const string ResponseRpc = "CFC_LeaseResponse_v1"; private const string TokenKey = "cfc_lease_token_v1"; private const string PeerKey = "cfc_lease_peer_v1"; private const string UntilKey = "cfc_lease_until_v1"; private const string MultiUserChestGuid = "com.maxsch.valheim.MultiUserChest"; private const float AcquireTimeout = 8f; private const double LeaseSeconds = 15.0; private const long CommitMargin = 20000000L; private static readonly FieldRef<Container, ZNetView> ViewField = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview"); private static readonly FieldRef<Container, bool> LoadingField = AccessTools.FieldRefAccess<Container, bool>("m_loading"); private static readonly FieldRef<InventoryGui, Container> OpenContainerField = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer"); private static readonly Func<Container, bool> LoadContainer = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Container> SaveContainer = AccessTools.MethodDelegate<Action<Container>>(AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Container> UpdateRows = AccessTools.MethodDelegate<Action<Container>>(AccessTools.Method(typeof(Container), "UpdateRows", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Container, long, bool> CheckPrivateAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<PrivateArea, Piece> WardPiece = AccessTools.FieldRefAccess<PrivateArea, Piece>("m_piece"); private static readonly Func<PrivateArea, bool> WardEnabled = AccessTools.MethodDelegate<Func<PrivateArea, bool>>(AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<PrivateArea, long, bool> WardPermitted = AccessTools.MethodDelegate<Func<PrivateArea, long, bool>>(AccessTools.Method(typeof(PrivateArea), "IsPermitted", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldInfo AllWards = AccessTools.Field(typeof(PrivateArea), "m_allAreas"); private static readonly Dictionary<Container, ZNetView> Registered = new Dictionary<Container, ZNetView>(); private static readonly Dictionary<Inventory, Container> InventoryContainers = new Dictionary<Inventory, Container>(); private static readonly HashSet<ZNetView> RegisteredViews = new HashSet<ZNetView>(); private static readonly List<Container> RemovedContainers = new List<Container>(); private static readonly List<Session> Abandoned = new List<Session>(); private static Session? current; private static Container? replayingTakeAll; private static float nextPrune; internal static bool IsBusy => current != null; private static long Now { get { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return 0L; } return ZNet.instance.GetTime().Ticks; } } internal static void Register(Container container) { if (!Object.op_Implicit((Object)(object)container) || Registered.ContainsKey(container)) { return; } ZNetView val = ViewField.Invoke(container); if (Object.op_Implicit((Object)(object)val) && val.IsValid() && container.GetInventory() != null && !RegisteredViews.Contains(val)) { val.Register<long, string>("CFC_RequestLease_v1", (Action<long, long, string>)delegate(long sender, long playerId, string token) { Request(container, sender, playerId, token); }); val.Register<long, string>("CFC_RequestTakeAllLease_v1", (Action<long, long, string>)delegate(long sender, long playerId, string token) { Request(container, sender, playerId, token, allowOwnOpen: true); }); val.Register<string, bool, ZPackage>("CFC_LeaseResponse_v1", (Action<long, string, bool, ZPackage>)delegate(long sender, string token, bool granted, ZPackage snapshot) { Response(container, sender, token, granted, snapshot); }); Registered.Add(container, val); InventoryContainers[container.GetInventory()] = container; RegisteredViews.Add(val); } } internal static void Unregister(Container container) { if (Registered.TryGetValue(container, out ZNetView value)) { if (Object.op_Implicit((Object)(object)value)) { value.Unregister("CFC_RequestLease_v1"); value.Unregister("CFC_RequestTakeAllLease_v1"); value.Unregister("CFC_LeaseResponse_v1"); } Registered.Remove(container); InventoryContainers.Remove(container.GetInventory()); RegisteredViews.Remove(value); } } internal static bool IsLeased(Container container) { ZNetView view = GetView(container); if ((Object)(object)view != (Object)null) { return LeaseActive(view.GetZDO()); } return false; } internal static bool Owns(Container container) { Session session = current; if (session == null) { return false; } foreach (Entry entry in session.Entries) { if ((Object)(object)entry.Container == (Object)(object)container) { return entry.Synchronized && Matches(entry, session, 20000000L) && CanUse(container, session.Player, ZNet.GetUID(), session.AllowOwnOpen); } } return false; } internal static void Acquire(Player player, List<Container> containers, Action onReady, Action<string> onFailure) { Acquire(player, containers, onReady, onFailure, allowOwnOpen: false); } private static void Acquire(Player player, List<Container> containers, Action onReady, Action<string> onFailure, bool allowOwnOpen) { if (current != null) { onFailure("Another container operation is still pending."); return; } if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)ZNet.instance)) { onFailure("The local player is not ready."); return; } Session session = new Session { Player = player, Ready = onReady, Failure = onFailure, Started = Time.realtimeSinceStartup, AllowOwnOpen = allowOwnOpen }; foreach (Container container in containers) { if (!session.Entries.Exists((Entry entry) => (Object)(object)entry.Container == (Object)(object)container)) { Register(container); ZNetView view = GetView(container); if ((Object)(object)view == (Object)null || !Registered.ContainsKey(container) || !CanUse(container, player, ZNet.GetUID(), allowOwnOpen)) { onFailure("A required container is busy, unavailable, or outside your access range."); return; } session.Entries.Add(new Entry { Container = container, View = view, ExpectedOwner = view.GetZDO().GetOwner() }); } } session.Entries.Sort(delegate(Entry left, Entry right) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) ZDOID uid = left.View.GetZDO().m_uid; ZDOID uid2 = right.View.GetZDO().m_uid; int num = ((ZDOID)(ref uid)).UserID.CompareTo(((ZDOID)(ref uid2)).UserID); return (num != 0) ? num : ((ZDOID)(ref uid)).ID.CompareTo(((ZDOID)(ref uid2)).ID); }); current = session; } private static void Request(Container container, long sender, long playerId, string token, bool allowOwnOpen = false) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Expected O, but got Unknown ZNetView view = GetView(container); if ((Object)(object)view == (Object)null) { return; } if (!view.IsOwner()) { view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3] { token ?? "", false, (object)new ZPackage() }); return; } bool flag = false; ZPackage val = new ZPackage(); try { Player player = Player.GetPlayer(playerId); if (token == null || token.Length != 32 || LeaseActive(view.GetZDO()) || !CanUse(container, player, sender, allowOwnOpen)) { view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3] { token ?? "", false, val }); return; } LoadContainer(container); if (sender != ZNet.GetUID()) { SaveContainer(container); val = new ZPackage(view.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null)); } ZDO zDO = view.GetZDO(); zDO.Set("cfc_lease_token_v1", token); zDO.Set("cfc_lease_peer_v1", sender); zDO.Set("cfc_lease_until_v1", Now + 150000000); zDO.SetOwner(sender); ZDOMan.instance.ForceSendZDO(sender, zDO.m_uid); flag = true; view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3] { token, true, val }); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Container owner could not grant lease: " + ex.Message)); if (!flag && view.IsOwner()) { view.InvokeRPC(sender, "CFC_LeaseResponse_v1", new object[3] { token ?? "", false, (object)new ZPackage() }); } } } private static void Response(Container container, long sender, string token, bool granted, ZPackage snapshot) { Session session = ((current != null && current.Token == token) ? current : Abandoned.Find((Session item) => item.Token == token)); if (session == null) { return; } Entry entry = session.Entries.Find((Entry item) => (Object)(object)item.Container == (Object)(object)container); if (entry != null && entry.Requested && entry.ExpectedOwner == sender && entry.Snapshot == null) { if (!granted) { entry.Requested = false; entry.RetryAfter = Time.realtimeSinceStartup + Random.Range(0.12f, 0.3f); } else { entry.Snapshot = snapshot.GetArray(); } } } internal static void Tick() { PruneDestroyed(); Session session = current; if (session != null && !session.Executing) { if (Time.realtimeSinceStartup - session.Started >= 8f || !Object.op_Implicit((Object)(object)session.Player) || (Object)(object)session.Player != (Object)(object)Player.m_localPlayer || ((Character)session.Player).IsDead()) { Fail(session, "Container request timed out or the player is unavailable. No resources were consumed."); } else { try { bool flag = true; foreach (Entry entry3 in session.Entries) { if (!Object.op_Implicit((Object)(object)entry3.Container) || !Object.op_Implicit((Object)(object)entry3.View) || !entry3.View.IsValid()) { throw new InvalidOperationException("A required container disappeared."); } if (!CanUse(entry3.Container, session.Player, ZNet.GetUID(), session.AllowOwnOpen)) { throw new InvalidOperationException("A required container is no longer accessible."); } if (!entry3.Requested && Time.realtimeSinceStartup >= entry3.RetryAfter && !LeaseActive(entry3.View.GetZDO())) { long owner = entry3.View.GetZDO().GetOwner(); if (owner != 0L) { entry3.ExpectedOwner = owner; entry3.Requested = true; entry3.View.InvokeRPC(owner, session.AllowOwnOpen ? "CFC_RequestTakeAllLease_v1" : "CFC_RequestLease_v1", new object[2] { session.Player.GetPlayerID(), session.Token }); } } if (!entry3.Synchronized && entry3.Snapshot != null && Matches(entry3, session, 20000000L)) { Synchronize(entry3); } if (!entry3.Synchronized) { flag = false; break; } } if (flag) { foreach (Entry entry4 in session.Entries) { if (!Owns(entry4.Container)) { throw new InvalidOperationException("Container access or ownership changed."); } } session.Executing = true; try { session.Ready(); } finally { Release(session); if (current == session) { current = null; } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Container operation failed: " + ex)); if (session == current) { Fail(session, "Container operation failed: " + ex.Message); } else { session.Failure("Container operation failed: " + ex.Message); } } } } for (int num = Abandoned.Count - 1; num >= 0; num--) { Session session2 = Abandoned[num]; foreach (Entry entry5 in session2.Entries) { if (entry5.Snapshot != null && !entry5.Synchronized && Matches(entry5, session2, 0L)) { try { Synchronize(entry5); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Late container synchronization failed: " + ex2.Message)); } } } Release(session2); if ((double)(Time.realtimeSinceStartup - session2.Started) > 23.0) { Abandoned.RemoveAt(num); } } } private static void Synchronize(Entry entry) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if (entry.ExpectedOwner == ZNet.GetUID()) { entry.Synchronized = true; return; } LoadingField.Invoke(entry.Container) = true; try { entry.Container.GetInventory().Load(new ZPackage(entry.Snapshot)); } finally { LoadingField.Invoke(entry.Container) = false; } UpdateRows(entry.Container); SaveContainer(entry.Container); entry.Synchronized = true; } private static void Fail(Session session, string message) { if (current == session) { Release(session); current = null; Abandoned.Add(session); session.Failure(message); } } internal static void CancelPending() { Session session = current; if (session != null && !session.Executing) { Release(session); current = null; Abandoned.Add(session); } } private static void Release(Session session) { foreach (Entry entry in session.Entries) { if (entry.Synchronized && Matches(entry, session, 0L)) { ZDO zDO = entry.View.GetZDO(); zDO.Set("cfc_lease_until_v1", 0L); zDO.Set("cfc_lease_peer_v1", 0L); zDO.Set("cfc_lease_token_v1", ""); } } } internal static void Reset() { if (current != null) { Release(current); } current = null; foreach (Session item in Abandoned) { Release(item); } Abandoned.Clear(); replayingTakeAll = null; foreach (KeyValuePair<Container, ZNetView> item2 in Registered) { if (Object.op_Implicit((Object)(object)item2.Value)) { item2.Value.Unregister("CFC_RequestLease_v1"); item2.Value.Unregister("CFC_RequestTakeAllLease_v1"); item2.Value.Unregister("CFC_LeaseResponse_v1"); } } Registered.Clear(); InventoryContainers.Clear(); RegisteredViews.Clear(); RemovedContainers.Clear(); nextPrune = 0f; } private static void PruneDestroyed() { if (Time.realtimeSinceStartup < nextPrune) { return; } nextPrune = Time.realtimeSinceStartup + 2f; foreach (KeyValuePair<Container, ZNetView> item in Registered) { if (!Object.op_Implicit((Object)(object)item.Key) || !Object.op_Implicit((Object)(object)item.Value) || !item.Value.IsValid()) { RemovedContainers.Add(item.Key); } } foreach (Container removedContainer in RemovedContainers) { Unregister(removedContainer); } RemovedContainers.Clear(); } private static bool LeaseActive(ZDO zdo) { if (zdo.GetLong("cfc_lease_peer_v1", 0L) != 0L) { return zdo.GetLong("cfc_lease_until_v1", 0L) > Now; } return false; } private static bool Matches(Entry entry, Session session, long margin) { if (!Object.op_Implicit((Object)(object)entry.Container) || !Object.op_Implicit((Object)(object)entry.View) || !entry.View.IsValid() || !entry.View.IsOwner()) { return false; } ZDO zDO = entry.View.GetZDO(); if (zDO.GetLong("cfc_lease_peer_v1", 0L) == ZNet.GetUID() && zDO.GetString("cfc_lease_token_v1", "") == session.Token) { return zDO.GetLong("cfc_lease_until_v1", 0L) > Now + margin; } return false; } private static ZNetView? GetView(Container container) { if (!Object.op_Implicit((Object)(object)container)) { return null; } ZNetView val = ViewField.Invoke(container); if (!Object.op_Implicit((Object)(object)val) || !val.IsValid()) { return null; } return val; } private static bool CanUse(Container container, Player player, long peer, bool allowOwnOpen = false) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)container) || !Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead() || container.GetInventory() == null) { return false; } ZNetView component = ((Component)player).GetComponent<ZNetView>(); ZNetView view = GetView(container); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || component.GetZDO().GetOwner() != peer || (Object)(object)view == (Object)null) { return false; } float value = Plugin.SearchRadius.Value; Vector3 val = ((Component)container).transform.position - ((Component)player).transform.position; if (((Vector3)(ref val)).sqrMagnitude > value * value) { return false; } bool flag = allowOwnOpen && peer == ZNet.GetUID() && view.IsOwner() && Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)OpenContainerField.Invoke(InventoryGui.instance) == (Object)(object)container; if (((container.IsInUse() || view.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0) && !flag) || (Object.op_Implicit((Object)(object)container.m_wagon) && container.m_wagon.InUse())) { return false; } if (!CheckPrivateAccess(container, player.GetPlayerID())) { return false; } if (!container.m_checkGuardStone) { return true; } bool flag2 = false; foreach (PrivateArea item in (List<PrivateArea>)AllWards.GetValue(null)) { if (!Object.op_Implicit((Object)(object)item) || !WardEnabled(item)) { continue; } Vector3 val3 = ((Component)item).transform.position - ((Component)container).transform.position; if (!(val3.x * val3.x + val3.z * val3.z >= item.m_radius * item.m_radius)) { Piece val4 = WardPiece.Invoke(item); if ((Object.op_Implicit((Object)(object)val4) && val4.GetCreator() == player.GetPlayerID()) || WardPermitted(item, player.GetPlayerID())) { return true; } flag2 = true; } } return !flag2; } internal static void YieldToContainerUse(Container container) { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown ZNetView view = GetView(container); if ((Object)(object)view == (Object)null || !view.IsOwner()) { return; } ZDO zDO = view.GetZDO(); string text = zDO.GetString("cfc_lease_token_v1", ""); if (zDO.GetLong("cfc_lease_peer_v1", 0L) == 0L || string.IsNullOrEmpty(text)) { return; } Session session = current; if (session != null && session.Executing && session.Token == text) { throw new InvalidOperationException("A container mutation reentered a synchronous resource transaction."); } Entry entry = ((session != null && session.Token == text) ? session.Entries.Find((Entry item) => (Object)(object)item.Container == (Object)(object)container) : null); if (entry == null || (!entry.Synchronized && entry.ExpectedOwner != ZNet.GetUID())) { byte[] byteArray = zDO.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null) { throw new InvalidOperationException("The container handoff has no authoritative inventory snapshot."); } LoadingField.Invoke(container) = true; try { container.GetInventory().Load(new ZPackage(byteArray)); } finally { LoadingField.Invoke(container) = false; } UpdateRows(container); SaveContainer(container); } zDO.Set("cfc_lease_until_v1", 0L); zDO.Set("cfc_lease_peer_v1", 0L); zDO.Set("cfc_lease_token_v1", ""); if (session != null && session.Token == text) { Fail(session, "Container use interrupted the pending resource operation. No resources were consumed."); } } internal static void InitializeCompatibility(Harmony harmony) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown Type type = AccessTools.TypeByName("MultiUserChest.ContainerRPCHandler"); if (type == null) { return; } HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(NetworkLeases), "MultiUserMutationPrefix", (Type[])null, (Type[])null)) { priority = 800 }; string[] array = new string[5] { "RequestItemAdd", "RequestItemRemove", "RequestItemConsume", "RequestItemMove", "RequestDrop" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null); if (methodInfo == null || methodInfo.GetParameters().Length != 2 || methodInfo.GetParameters()[0].ParameterType != typeof(Inventory)) { throw new MissingMethodException(type.FullName, text); } harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Plugin.Log.LogInfo((object)"MultiUserChest compatibility enabled: chest interaction takes priority over pending crafting."); } private static void MultiUserMutationPrefix(Inventory __0) { if (InventoryContainers.TryGetValue(__0, out Container value)) { Session session = current; if (!((Object)(object)replayingTakeAll == (Object)(object)value) || session == null || !session.Executing || !session.AllowOwnOpen || !Owns(value)) { YieldToContainerUse(value); } } } } [BepInPlugin("local.craftfromcontainers", "Craft From Containers", "0.1.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("toxo.craftfromchests")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "local.craftfromcontainers"; public const string ModVersion = "0.1.4"; internal static ManualLogSource Log; internal static ConfigEntry<float> SearchRadius; internal static ConfigEntry<float> RefreshInterval; internal static ConfigEntry<bool> ShowAvailable; internal static ConfigEntry<string> KilnAllowedResources; private ConfigEntry<bool> _diagnostics; private float _nextDiagnostics; private Harmony? _harmony; private ZNetScene? _scene; private void Awake() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; SearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SearchRadius", 40f, new ConfigDescription("Resource search radius in metres. Use the same value on every client and server.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); RefreshInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "RefreshInterval", 0.5f, new ConfigDescription("Seconds between proximity/access/network revision checks. Inventory changes update the resource cache immediately.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); ShowAvailable = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowAvailable", true, "Display required/available resources."); KilnAllowedResources = ((BaseUnityPlugin)this).Config.Bind<string>("Refueling", "KilnAllowedResources", "*", "Allowed input prefab names for recipes producing Coal, separated by commas. * allows every existing recipe; empty blocks all coal inputs. Set Wood to protect FineWood and RoundLog. Applies to backpack, containers and explicit item use; does not add recipes."); _diagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Enabled", false, "Log cache counters every 30 seconds for performance checks."); _harmony = new Harmony("local.craftfromcontainers"); try { _harmony.PatchAll(); NetworkLeases.InitializeCompatibility(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Craft From Containers 0.1.4; Valheim " + Version.GetVersionString(false) + "; multiplayer protocol 1.")); } catch (Exception arg) { _harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)$"Unsupported game API: mod disabled. {arg}"); ((Behaviour)this).enabled = false; } } private void Update() { ZNetScene instance = ZNetScene.instance; if (instance != _scene) { NetworkLeases.Reset(); ContainerIndex.Reset(); InventoryBridge.Reset(); RequirementTextPatch.Clear(); CraftingPanelRefresh.Reset(); ResourceActions.Cancel(); _scene = instance; if (Object.op_Implicit((Object)(object)instance)) { Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { Register(array[i]); } } } NetworkLeases.Tick(); ContainerIndex.Tick(); CraftingPanelRefresh.Tick(); if (_diagnostics.Value && Time.unscaledTime >= _nextDiagnostics) { _nextDiagnostics = Time.unscaledTime + 30f; ((BaseUnityPlugin)this).Logger.LogInfo((object)ContainerIndex.Diagnostics); } } internal static void Register(Container container) { if (Object.op_Implicit((Object)(object)container) && container.GetInventory() != null) { ContainerIndex.Register(container); NetworkLeases.Register(container); } } private void OnDestroy() { NetworkLeases.Reset(); ContainerIndex.Reset(); InventoryBridge.Reset(); RequirementTextPatch.Clear(); CraftingPanelRefresh.Reset(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { Plugin.Register(__instance); } } [HarmonyPatch(typeof(Smelter), "OnAddOre")] internal static class OreActionPatch { private static readonly Func<Smelter, Switch, Humanoid, ItemData, bool> Add = AccessTools.MethodDelegate<Func<Smelter, Switch, Humanoid, ItemData, bool>>(AccessTools.Method(typeof(Smelter), "OnAddOre", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(Smelter __instance, Switch sw, Humanoid user, ref ItemData item, ref bool __result) { //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) Player player = (Player)(object)((user is Player) ? user : null); if (player == null || !ResourceActions.Local(player)) { return true; } Smelter smelter = __instance; if (item != null) { if (!RefuelingPolicy.AllowsOre(smelter, item)) { return RefuelingPolicy.Refuse(player, ref __result); } return true; } if (InventoryBridge.Executing) { return true; } foreach (ItemConversion item4 in smelter.m_conversion) { if (RefuelingPolicy.AllowsConversion(item4)) { ItemData item2 = ((Humanoid)player).GetInventory().GetItem(item4.m_from.m_itemData.m_shared.m_name, -1, false); if (item2 != null && RefuelingPolicy.AllowsOre(smelter, item2)) { item = item2; return true; } } } foreach (ItemConversion conversion in smelter.m_conversion) { if (!RefuelingPolicy.AllowsConversion(conversion)) { continue; } string name = conversion.m_from.m_itemData.m_shared.m_name; if (ContainerIndex.Count(name) <= 0) { continue; } Vector3 start = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position); if (ResourceActions.Begin(player, () => (!RefuelingPolicy.AllowsConversion(conversion)) ? null : new List<ResourcePlanner.Demand> { new ResourcePlanner.Demand(name, 1) }, delegate { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0059: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)smelter) && Object.op_Implicit((Object)(object)sw)) { Vector3 val = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position) - start; if (((Vector3)(ref val)).sqrMagnitude < 1f) { return RefuelingPolicy.AllowsConversion(conversion); } } return false; }, delegate { ItemData item3 = ((Humanoid)player).GetInventory().GetItem(name, -1, false); if (item3 != null && RefuelingPolicy.AllowsOre(smelter, item3)) { Add(smelter, sw, (Humanoid)(object)player, item3); } })) { item = ((Humanoid)player).GetInventory().GetItem(name, -1, false); if (item != null && RefuelingPolicy.AllowsOre(smelter, item)) { return true; } } __result = false; return false; } ((Character)player).Message((MessageType)2, "$msg_noprocessableitems", 0, (Sprite)null, false); __result = false; return false; } } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] internal static class FuelActionPatch { private static readonly Func<Smelter, Switch, Humanoid, ItemData, bool> Add = AccessTools.MethodDelegate<Func<Smelter, Switch, Humanoid, ItemData, bool>>(AccessTools.Method(typeof(Smelter), "OnAddFuel", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(Smelter __instance, Switch sw, Humanoid user, ItemData item, ref bool __result) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (item == null) { Player player = (Player)(object)((user is Player) ? user : null); if (player != null && ResourceActions.Local(player) && !InventoryBridge.Executing) { Smelter smelter = __instance; string name = smelter.m_fuelItem.m_itemData.m_shared.m_name; if (((Humanoid)player).GetInventory().HaveItem(name, true)) { return true; } Vector3 start = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position); bool num = ResourceActions.Begin(player, () => new List<ResourcePlanner.Demand> { new ResourcePlanner.Demand(name, 1) }, delegate { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)smelter) && Object.op_Implicit((Object)(object)sw)) { Vector3 val = ((Component)smelter).transform.InverseTransformPoint(((Component)player).transform.position) - start; return ((Vector3)(ref val)).sqrMagnitude < 1f; } return false; }, delegate { Add(smelter, sw, (Humanoid)(object)player, null); }); if (!num) { __result = false; } return num; } } return true; } } internal sealed class ResourceAllowList { private string? _configuration; private readonly HashSet<string> _names = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool _all; internal bool Allows(string configuration, string prefabName) { if (_configuration != configuration) { _names.Clear(); _all = false; string[] array = configuration.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text == "*") { _all = true; } else if (text.Length > 0) { _names.Add(text); } } _configuration = configuration; } if (!_all) { return _names.Contains(prefabName); } return true; } } internal static class RefuelingPolicy { private static readonly ResourceAllowList Kiln = new ResourceAllowList(); internal static bool AllowsConversion(ItemConversion conversion) { if (Object.op_Implicit((Object)(object)conversion.m_from)) { if (Object.op_Implicit((Object)(object)conversion.m_to) && !(((Object)((Component)conversion.m_to).gameObject).name != "Coal")) { return Kiln.Allows(Plugin.KilnAllowedResources.Value, ((Object)((Component)conversion.m_from).gameObject).name); } return true; } return false; } internal static bool AllowsOre(Smelter smelter, ItemData item) { foreach (ItemConversion item2 in smelter.m_conversion) { if (Object.op_Implicit((Object)(object)item2.m_from) && ((Object)((Component)item2.m_from).gameObject).name == ((Object)item.m_dropPrefab).name) { return AllowsConversion(item2); } } return true; } internal static bool Refuse(Player player, ref bool result) { ((Character)player).Message((MessageType)2, "$msg_wontwork", 0, (Sprite)null, false); result = false; return false; } } internal static class ResourceActions { private static int _generation; internal static bool Pending; internal static void Cancel() { _generation++; Pending = false; NetworkLeases.CancelPending(); } internal static bool Local(Player player) { if (Object.op_Implicit((Object)(object)player)) { return (Object)(object)player == (Object)(object)Player.m_localPlayer; } return false; } internal static List<ResourcePlanner.Demand> Costs(Player player, Requirement[] requirements, int quality, int multiplier) { List<ResourcePlanner.Demand> list = new List<ResourcePlanner.Demand>(); CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); foreach (Requirement val in requirements) { if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_upgraderResource == (Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader)) { int num = checked(val.GetAmount(quality) * multiplier); if (num > 0) { list.Add(new ResourcePlanner.Demand(val.m_resItem.m_itemData.m_shared.m_name, num)); } } } return list; } internal static List<ResourcePlanner.Demand>? RecipeCosts(Player player, Recipe recipe, int quality, int multiplier) { if (!recipe.m_requireOnlyOneIngredient) { return Costs(player, recipe.m_resources, quality, multiplier); } int previous = InventoryBridge.Enter(); try { int amount = default(int); int num = default(int); ItemData firstRequiredItem = player.GetFirstRequiredItem(((Humanoid)player).GetInventory(), recipe, quality, ref amount, ref num, multiplier); return (firstRequiredItem == null) ? null : new List<ResourcePlanner.Demand> { new ResourcePlanner.Demand(firstRequiredItem.m_shared.m_name, amount, firstRequiredItem.m_quality) }; } finally { InventoryBridge.Leave(previous); } } internal static bool Begin(Player player, Func<List<ResourcePlanner.Demand>?> costs, Func<bool> stillValid, Action action) { if (InventoryBridge.Executing) { return true; } if (Pending || NetworkLeases.IsBusy) { return false; } List<ResourcePlanner.Demand> list = costs(); List<string> list2 = new List<string>(); if (list != null) { foreach (ResourcePlanner.Demand item in list) { list2.Add(item.Name); } } if (list == null || !ResourceTransaction.TryCreate(player, ContainerIndex.GetSourcesFor(list2), list, requireOwnership: false, out ResourceTransaction preview)) { Fail(player, "$msg_missingrequirement"); return false; } if (preview.Sources.Count == 0) { return true; } int generation = _generation; Pending = true; NetworkLeases.Acquire(player, preview.Sources, delegate { Pending = false; if (generation != _generation || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || !stillValid()) { return; } foreach (Container source in preview.Sources) { if (!Object.op_Implicit((Object)(object)source) || !NetworkLeases.Owns(source) || !ContainerIndex.CanAccess(source, ignoreInUse: true)) { Fail(player, "$msg_cantopen"); return; } } InventoryBridge.RestrictedSources = preview.Sources; try { List<ResourcePlanner.Demand> list3 = costs(); if (list3 == null || !ResourceTransaction.TryCreate(player, preview.Sources, list3, requireOwnership: true, out ResourceTransaction transaction)) { Fail(player, "$msg_missingrequirement"); return; } transaction.Validate(); transaction.Capture(player); InventoryBridge.Transaction = transaction; InventoryBridge.Executing = true; int previous = InventoryBridge.Enter(); try { action(); } finally { try { transaction.Finish(); } finally { InventoryBridge.Leave(previous); } } } catch (Exception ex) { Plugin.Log.LogError((object)("Resource operation failed: " + ex)); Fail(player, "Craft From Containers: operation failed; see BepInEx log."); } finally { InventoryBridge.Transaction = null; InventoryBridge.RestrictedSources = null; InventoryBridge.Executing = false; } }, delegate(string reason) { Pending = false; Plugin.Log.LogDebug((object)("Resource request cancelled: " + reason)); if (Object.op_Implicit((Object)(object)player) && generation == _generation) { Fail(player, "Storage is busy or unavailable. Please try again."); } }); return false; } private static void Fail(Player player, string message) { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class CraftActionPatch { private static readonly FieldRef<InventoryGui, Recipe> RecipeField = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe"); private static readonly FieldRef<InventoryGui, ItemData> UpgradeField = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_craftUpgradeItem"); private static readonly FieldRef<InventoryGui, int> VariantField = AccessTools.FieldRefAccess<InventoryGui, int>("m_craftVariant"); private static readonly FieldRef<InventoryGui, bool> MultiField = AccessTools.FieldRefAccess<InventoryGui, bool>("m_multiCrafting"); private static readonly FieldRef<InventoryGui, int> AmountField = AccessTools.FieldRefAccess<InventoryGui, int>("m_multiCraftAmount"); private static readonly Action<InventoryGui, Player> Craft = AccessTools.MethodDelegate<Action<InventoryGui, Player>>(AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(InventoryGui __instance, Player player) { if (!ResourceActions.Local(player) || InventoryBridge.Executing || player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) { return true; } InventoryGui gui = __instance; Recipe recipe = RecipeField.Invoke(gui); if (!Object.op_Implicit((Object)(object)recipe)) { return true; } ItemData upgrade = UpgradeField.Invoke(gui); int quality = ((upgrade == null) ? 1 : (upgrade.m_quality + 1)); int variant = VariantField.Invoke(gui); bool multi = MultiField.Invoke(gui); int amount = ((!multi) ? 1 : AmountField.Invoke(gui)); CraftingStation station = player.GetCurrentCraftingStation(); return ResourceActions.Begin(player, () => ResourceActions.RecipeCosts(player, recipe, quality, amount), () => Object.op_Implicit((Object)(object)gui) && InventoryGui.IsVisible() && (Object)(object)RecipeField.Invoke(gui) == (Object)(object)recipe && UpgradeField.Invoke(gui) == upgrade && VariantField.Invoke(gui) == variant && MultiField.Invoke(gui) == multi && (!multi || AmountField.Invoke(gui) == amount) && (Object)(object)player.GetCurrentCraftingStation() == (Object)(object)station && (!Object.op_Implicit((Object)(object)station) || station.CheckUsable(player, false)), delegate { Craft(gui, player); }); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); FieldInfo objB = AccessTools.Field(typeof(InventoryGui), "m_craftVariant"); int num = -1; for (int i = 1; i < list.Count; i++) { if (list[i].opcode == OpCodes.Ldfld && object.Equals(list[i].operand, objB)) { num = i - 1; break; } } if (num < 0 || list[num].opcode != OpCodes.Ldarg_0) { throw new InvalidOperationException("Unsupported DoCrafting layout: cannot place debit before item creation."); } CodeInstruction val = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CraftActionPatch), "PayBeforeOutput", (Type[])null, (Type[])null)); val.labels.AddRange(list[num].labels); list[num].labels.Clear(); list.Insert(num, val); return list; } private static void PayBeforeOutput() { InventoryBridge.Transaction?.Pay(); } } [HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")] internal static class PendingCraftPatch { private static bool Prefix() { return !ResourceActions.Pending; } } [HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")] internal static class CancelCraftPatch { private static void Prefix() { ResourceActions.Cancel(); } } [HarmonyPatch(typeof(Recipe), "GetAmount")] internal static class MissingAlternativePatch { private static bool Prefix(Recipe __instance, int quality, int craftMultiplier, ref int need, ref ItemData singleReqItem, ref int __result) { if (!__instance.m_requireOnlyOneIngredient || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return true; } int num = default(int); int num2 = default(int); if (Player.m_localPlayer.GetFirstRequiredItem(((Humanoid)Player.m_localPlayer).GetInventory(), __instance, quality, ref num, ref num2, craftMultiplier) != null) { return true; } need = 0; singleReqItem = null; __result = __instance.m_amount * craftMultiplier; return false; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class BuildActionPatch { private static readonly FieldRef<Player, GameObject> Ghost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost"); private static readonly FieldRef<Player, float> LastUse = AccessTools.FieldRefAccess<Player, float>("m_lastToolUseTime"); private static readonly FieldRef<Player, int> Debt = AccessTools.FieldRefAccess<Player, int>("m_buildRemoveDebt"); private static readonly Func<Player, float> Stamina = AccessTools.MethodDelegate<Func<Player, float>>(AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Player, ItemData, float> Durability = AccessTools.MethodDelegate<Func<Player, ItemData, float>>(AccessTools.Method(typeof(Player), "GetPlaceDurability", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Humanoid, ItemData> RightItem = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetRightItem", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(Player __instance, Piece piece, ref bool __result) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (!ResourceActions.Local(__instance) || InventoryBridge.Executing || __instance.NoCostCheat() || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { return true; } GameObject val = Ghost.Invoke(__instance); if (!Object.op_Implicit((Object)(object)val)) { return true; } Vector3 position = val.transform.position; Quaternion rotation = val.transform.rotation; ItemData tool = RightItem((Humanoid)(object)__instance); bool num = ResourceActions.Begin(__instance, () => ResourceActions.Costs(__instance, piece.m_resources, 0, 1), delegate { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if (((Character)__instance).InPlaceMode() && (Object)(object)__instance.GetSelectedPiece() == (Object)(object)piece && RightItem((Humanoid)(object)__instance) == tool && Object.op_Implicit((Object)(object)Ghost.Invoke(__instance))) { Vector3 val2 = Ghost.Invoke(__instance).transform.position - position; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f && Quaternion.Angle(Ghost.Invoke(__instance).transform.rotation, rotation) < 1f) { return ((Character)__instance).HaveStamina(Stamina(__instance)); } } return false; }, delegate { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Invalid comparison between Unknown and I4 //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Invalid comparison between Unknown and I4 if (__instance.HaveRequirements(piece, (RequirementMode)0)) { InventoryBridge.Transaction.Pay(); if (__instance.TryPlacePiece(piece)) { InventoryBridge.Transaction.CompletePayment(); Hud.instance.m_buildUi.AddRecentPiece(piece); LastUse.Invoke(__instance) = Time.time; ((Character)__instance).UseStamina(Stamina(__instance)); PieceTable val2 = tool?.m_shared.m_buildPieces; if (Object.op_Implicit((Object)(object)val2) && (int)val2.m_skill != 0) { if ((int)val2.m_skill == 107) { Game.instance.IncrementPlayerStat((PlayerStatType)168, 1f, false); } if (Debt.Invoke(__instance) > 0) { Debt.Invoke(__instance)--; } else { ((Character)__instance).RaiseSkill(val2.m_skill, 1f); if ((int)val2.m_skill == 107) { Game.instance.IncrementPlayerStat((PlayerStatType)169, 1f, false); } } } if (tool != null) { if (tool.m_shared.m_useDurability) { ItemData obj = tool; obj.m_durability -= Durability(__instance, tool) * Game.m_durabilityRate; } tool.m_shared.m_buildEffect.Create(((Component)__instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)__instance).GetZDOID()); } } } }); if (!num) { __result = false; } return num; } } internal static class ResourcePlanner { internal readonly struct Stock { internal readonly string Name; internal readonly int Quality; internal readonly int WorldLevel; internal readonly int Amount; internal Stock(string name, int quality, int worldLevel, int amount) { Name = name; Quality = quality; WorldLevel = worldLevel; Amount = amount; } } internal readonly struct Demand { internal readonly string Name; internal readonly int Amount; internal readonly int Quality; internal Demand(string name, int amount, int quality = -1) { Name = name; Amount = amount; Quality = quality; } } internal readonly struct Take { internal readonly int StockIndex; internal readonly int Amount; internal Take(int stockIndex, int amount) { StockIndex = stockIndex; Amount = amount; } } internal static bool TryPlan(IReadOnlyList<Stock> stock, IReadOnlyList<Demand> demands, int worldLevel, out List<Take> plan) { plan = new List<Take>(); int[] array = new int[stock.Count]; for (int i = 0; i < stock.Count; i++) { array[i] = Math.Max(0, stock[i].Amount); } for (int j = 0; j < 2; j++) { foreach (Demand demand in demands) { if (demand.Amount < 0 || string.IsNullOrEmpty(demand.Name)) { plan.Clear(); return false; } if (demand.Quality >= 0 != (j == 0)) { continue; } int num = demand.Amount; for (int k = 0; k < stock.Count; k++) { if (num <= 0) { break; } Stock stock2 = stock[k]; if (!(stock2.Name != demand.Name) && stock2.WorldLevel >= worldLevel && (demand.Quality < 0 || stock2.Quality == demand.Quality)) { int num2 = Math.Min(num, array[k]); if (num2 != 0) { array[k] -= num2; num -= num2; plan.Add(new Take(k, num2)); } } } if (num != 0) { plan.Clear(); return false; } } } return true; } } internal sealed class ResourceTransaction { private sealed class Entry { internal Inventory Inventory; internal ItemData Item; internal Container? Container; internal int Reserved; } private readonly List<Entry> _entries = new List<Entry>(); private InventorySnapshot? _snapshot; private readonly List<Inventory> _inventories = new List<Inventory>(); private static readonly Action<Inventory, bool, bool> Changed = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), (object)null, true); internal readonly List<Container> Sources = new List<Container>(); internal bool Paid { get; private set; } internal bool Completed { get { if (!Paid) { return false; } foreach (Entry entry in _entries) { if (entry.Reserved != 0) { return false; } } return true; } } internal bool Cheated { get; private set; } internal static bool TryCreate(Player player, List<Container> candidates, List<ResourcePlanner.Demand> costs, bool requireOwnership, out ResourceTransaction transaction) { transaction = new ResourceTransaction(); List<ResourcePlanner.Stock> stock = new List<ResourcePlanner.Stock>(); List<Entry> list = new List<Entry>(); AddInventory(((Humanoid)player).GetInventory(), null, stock, list); foreach (Container candidate in candidates) { if (Object.op_Implicit((Object)(object)candidate) && candidate.GetIn