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 Quads Build From Chests v1.5.2
plugins/BuildFromChests.dll
Decompiled 15 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DHack.Shared; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI (Claude Code).")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BuildFromChests { internal static class ChestScanner { private class Claim { public float At; public float RetryAt = -1f; } private static readonly List<Container> AllContainers = new List<Container>(); private static readonly List<Container> Nearby = new List<Container>(); 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 FieldInfo NView = AccessTools.Field(typeof(Container), "m_nview"); private static readonly MethodInfo Load = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); internal static bool Suspend; internal static bool Consuming; private static int _contextFrame = -1; private static bool _contextActive; private static float _nearbyAt = -10f; private static Vector3 _nearbyOrigin; private const float Settle = 1f; private const float Retry = 5f; private const float Forget = 10f; private static readonly Dictionary<(long, uint), Claim> Claims = new Dictionary<(long, uint), Claim>(); private static float _prunedAt; private static int _countFrame = -1; private static readonly Dictionary<string, Dictionary<int, int>> Counts = new Dictionary<string, Dictionary<int, int>>(); internal static void Register(Container c) { if ((Object)(object)c != (Object)null && !((Object)c).name.StartsWith("piece_recycler") && !AllContainers.Contains(c)) { AllContainers.Add(c); } } private static bool BuildingNow() { object data = AppDomain.CurrentDomain.GetData("DHack.BuildFromChests.ForceContext"); bool flag = default(bool); int num; if (data is bool) { flag = (bool)data; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return true; } if (_contextFrame == Time.frameCount) { return _contextActive; } _contextFrame = Time.frameCount; Player localPlayer = Player.m_localPlayer; _contextActive = (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).InPlaceMode() && (Object)(object)localPlayer.GetCurrentCraftingStation() == (Object)null && !InventoryGui.IsVisible(); return _contextActive; } internal static bool Applies(Inventory inv) { if (Suspend || !Plugin.Enabled.Value) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || inv != ((Humanoid)localPlayer).GetInventory()) { return false; } if (BuildingNow()) { return OtherChestMods.Which == null; } return false; } internal static List<Container> GetNearby() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Nearby.Clear(); return Nearby; } Vector3 position = ((Component)localPlayer).transform.position; float num = Time.unscaledTime - _nearbyAt; Vector3 val; if (num < 1f) { if (!(num < 0.25f)) { val = position - _nearbyOrigin; if (!(((Vector3)(ref val)).sqrMagnitude < 9f)) { goto IL_0092; } } Nearby.RemoveAll((Container c) => (Object)(object)c == (Object)null); return Nearby; } goto IL_0092; IL_0092: _nearbyAt = Time.unscaledTime; _nearbyOrigin = position; InvalidateCounts(); Nearby.Clear(); float num2 = Plugin.Radius.Value * Plugin.Radius.Value; long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); AllContainers.RemoveAll((Container c) => (Object)(object)c == (Object)null); foreach (Container allContainer in AllContainers) { val = ((Component)allContainer).transform.position - position; if (!(((Vector3)(ref val)).sqrMagnitude > num2) && allContainer.GetInventory() != null && IsStorage(allContainer) && !InUse(allContainer) && (!allContainer.m_checkGuardStone || PrivateArea.CheckAccess(((Component)allContainer).transform.position, 0f, false, false)) && CheckAccess(allContainer, playerID)) { Nearby.Add(allContainer); } } return Nearby; } private static bool IsStorage(Container c) { if ((Object)(object)((Component)c).GetComponentInParent<Piece>() == (Object)null || (Object)(object)((Component)c).GetComponent<TombStone>() != (Object)null) { return false; } if ((Object)(object)((Component)c).GetComponentInParent<Character>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Vagon>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Ship>() != (Object)null) { return false; } if (SpecialContainers.NotStorage(c)) { return false; } object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null && val.IsValid()) { return val.GetZDO().GetLong("dhc_home", 0L) == 0; } return false; } private static bool InUse(Container c) { if (c.IsInUse()) { return true; } object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null && val.IsValid() && !val.IsOwner()) { return val.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1; } return false; } private static (long, uint) Id(ZDOID uid) { return (((ZDOID)(ref uid)).UserID, ((ZDOID)(ref uid)).ID); } private static Claim ClaimOn((long, uint) id, float now) { if (!Claims.TryGetValue(id, out var value)) { return null; } if ((value.RetryAt >= 0f) ? (now < value.RetryAt) : (now - value.At < 10f)) { return value; } Claims.Remove(id); return null; } private static bool Settled(Container c) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } float unscaledTime = Time.unscaledTime; Claim claim = ClaimOn(Id(val.GetZDO().m_uid), unscaledTime); if (!val.IsOwner()) { if (claim != null && claim.RetryAt < 0f) { claim.RetryAt = unscaledTime + 5f; } return false; } if (claim != null) { return unscaledTime - claim.At >= 1f; } return true; } private static bool Ready(Container c) { if (!Settled(c)) { return false; } Load?.Invoke(c, null); return true; } private static void Request(Container c) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid() || val.IsOwner() || InUse(c)) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime - _prunedAt > 30f) { _prunedAt = unscaledTime; foreach (var item in new List<(long, uint)>(Claims.Keys)) { ClaimOn(item, unscaledTime); } } (long, uint) tuple = Id(val.GetZDO().m_uid); Claim claim = ClaimOn(tuple, unscaledTime); if (claim != null) { if (claim.RetryAt < 0f) { claim.RetryAt = unscaledTime + 5f; } } else { val.ClaimOwnership(); Claims[tuple] = new Claim { At = unscaledTime }; } } private static void InvalidateCounts() { _countFrame = -1; } internal static int CountInChests(string name, int quality, bool matchWorldLevel) { if (_countFrame != Time.frameCount) { Counts.Clear(); _countFrame = Time.frameCount; } if (!Counts.TryGetValue(name, out var value)) { value = (Counts[name] = new Dictionary<int, int>()); } int key = quality * 2 + (matchWorldLevel ? 1 : 0); if (value.TryGetValue(key, out var value2)) { return value2; } int num = 0; foreach (Container item in GetNearby()) { int num2 = item.GetInventory().CountItems(name, quality, matchWorldLevel); if (num2 > 0 && !InUse(item)) { if (Settled(item)) { num += num2; } else { Request(item); } } } value[key] = num; return num; } internal static int OwnCount(Inventory inv, string name, int quality, bool matchWorldLevel) { int num = 0; foreach (ItemData allItem in inv.GetAllItems()) { if (allItem.m_shared.m_name == name && (quality < 0 || allItem.m_quality == quality) && (!matchWorldLevel || allItem.m_worldLevel >= Game.m_worldLevel)) { num += allItem.m_stack; } } return num; } internal static string FindShortfall(Inventory inv, Requirement[] resources) { foreach (Requirement val in resources) { if ((Object)(object)val.m_resItem == (Object)null || val.m_upgraderResource) { continue; } int amount = val.GetAmount(0); if (amount <= 0) { continue; } string name = val.m_resItem.m_itemData.m_shared.m_name; Suspend = true; int num; try { num = inv.CountItems(name, -1, true); } finally { Suspend = false; } int num2 = amount - num; if (num2 <= 0) { continue; } int num3 = 0; foreach (Container item in GetNearby()) { if (num3 >= num2) { break; } if ((Object)(object)item == (Object)null || InUse(item)) { continue; } Inventory inventory = item.GetInventory(); if (inventory.CountItems(name, -1, true) > 0) { if (!Ready(item)) { Request(item); } else { num3 += inventory.CountItems(name, -1, true); } } } InvalidateCounts(); if (num3 < num2) { return name; } } return null; } internal static int RemoveFromChests(string name, int amount, int quality, bool matchWorldLevel) { foreach (Container item in GetNearby()) { if (amount <= 0) { break; } if ((Object)(object)item == (Object)null || InUse(item)) { continue; } Inventory inventory = item.GetInventory(); if (inventory.CountItems(name, quality, matchWorldLevel) <= 0) { continue; } if (!Ready(item)) { Request(item); continue; } int num = Math.Min(inventory.CountItems(name, quality, matchWorldLevel), amount); if (num > 0) { inventory.RemoveItem(name, num, quality, matchWorldLevel); amount -= num; } } InvalidateCounts(); return amount; } } internal static class Api { internal const string TakeKey = "DHack.BuildFromChests.Take"; internal const string CountKey = "DHack.BuildFromChests.Count"; private static readonly Func<string, int, int> Take = delegate(string name, int amount) { if (!Plugin.Enabled.Value || amount <= 0) { return 0; } int num = ChestScanner.RemoveFromChests(name, amount, -1, matchWorldLevel: false); return amount - num; }; private static readonly Func<string, int> Count = (string name) => Plugin.Enabled.Value ? ChestScanner.CountInChests(name, -1, matchWorldLevel: false) : 0; internal static void Publish() { AppDomain.CurrentDomain.SetData("DHack.BuildFromChests.Take", Take); AppDomain.CurrentDomain.SetData("DHack.BuildFromChests.Count", Count); } internal static void Withdraw() { if (AppDomain.CurrentDomain.GetData("DHack.BuildFromChests.Take") == Take) { AppDomain.CurrentDomain.SetData("DHack.BuildFromChests.Take", null); } if (AppDomain.CurrentDomain.GetData("DHack.BuildFromChests.Count") == Count) { AppDomain.CurrentDomain.SetData("DHack.BuildFromChests.Count", null); } } } internal static class HaveLabel { private const string LabelName = "BFC_Have"; internal static void Show(Transform elementRoot, Requirement req, int quality, int multiplier) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) Transform val = elementRoot.Find("res_amount"); if ((Object)(object)val == (Object)null) { return; } TMP_Text orCreate = GetOrCreate(elementRoot, val); if (!((Object)(object)orCreate == (Object)null)) { string name = req.m_resItem.m_itemData.m_shared.m_name; Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); ChestScanner.Suspend = true; int num; try { num = inventory.CountItems(name, -1, true); } finally { ChestScanner.Suspend = false; } int num2 = ChestScanner.CountInChests(name, -1, matchWorldLevel: true); int num3 = num + num2; int num4 = req.GetAmount(quality) * multiplier; ((Component)orCreate).gameObject.SetActive(true); orCreate.text = num3.ToString(); ((Graphic)orCreate).color = ((num3 >= num4) ? new Color(0.55f, 1f, 0.6f) : new Color(1f, 0.62f, 0.45f)); UITooltip component = ((Component)elementRoot).GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.m_text)) { component.m_text = component.m_text + "\n" + Translations.T("buildfromchests_have", num3, num, num2); } } } internal static void Hide(Transform elementRoot) { Transform val = (((Object)(object)elementRoot != (Object)null) ? elementRoot.Find("BFC_Have") : null); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } private static TMP_Text GetOrCreate(Transform elementRoot, Transform amount) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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) Transform val = elementRoot.Find("BFC_Have"); if ((Object)(object)val != (Object)null) { return ((Component)val).GetComponent<TMP_Text>(); } GameObject val2 = Object.Instantiate<GameObject>(((Component)amount).gameObject, elementRoot); ((Object)val2).name = "BFC_Have"; RectTransform val3 = (RectTransform)amount; RectTransform val4 = (RectTransform)val2.transform; val4.anchorMin = new Vector2(1f - val3.anchorMax.x, val3.anchorMin.y); val4.anchorMax = new Vector2(1f - val3.anchorMin.x, val3.anchorMax.y); val4.pivot = new Vector2(1f - val3.pivot.x, val3.pivot.y); val4.anchoredPosition = new Vector2(0f - val3.anchoredPosition.x, val3.anchoredPosition.y); val4.sizeDelta = val3.sizeDelta; TMP_Text component = val2.GetComponent<TMP_Text>(); component.horizontalAlignment = (HorizontalAlignmentOptions)1; component.enableAutoSizing = false; component.fontSize = ((Component)amount).GetComponent<TMP_Text>().fontSize * 0.9f; return component; } internal static void DestroyAll() { if ((Object)(object)Hud.instance == (Object)null) { return; } TMP_Text[] componentsInChildren = ((Component)Hud.instance).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "BFC_Have") { Object.Destroy((Object)(object)((Component)val).gameObject); } } } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] internal static class InventoryGui_SetupRequirement { private static void Postfix(Transform elementRoot, Requirement req, Player player, int quality, int craftMultiplier, bool __result) { try { bool flag = (Object)(object)InventoryGui.instance != (Object)null && elementRoot.IsChildOf(((Component)InventoryGui.instance).transform); if (!flag && __result && Plugin.Enabled.Value && Plugin.ShowHaveCounts.Value && (Object)(object)req.m_resItem != (Object)null && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && ChestScanner.Applies(((Humanoid)player).GetInventory())) { HaveLabel.Show(elementRoot, req, quality, craftMultiplier); } else { HaveLabel.Hide(elementRoot); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not show the 'you have' count: " + ex.Message)); } } } internal static class OtherChestMods { private static readonly string[][] Always = new string[7][] { new string[2] { "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes" }, new string[2] { "aedenthorn.CraftFromContainers", "CraftFromContainers" }, new string[2] { "toxo.craftfromchests", "Craft From Chests (Toxo)" }, new string[2] { "poneis.valheim.CraftFromChestsPlus", "CraftFromChestsPlus" }, new string[2] { "com.teflonted.valheim.craftfromchests", "Teflon Ted's Craft From Chests" }, new string[2] { "com.morda.storeandcraft", "StoreAndCraft" }, new string[2] { "com.zellds.smartcraftstorage", "SmartCraft-Storage" } }; private const string ValheimPlus = "org.bepinex.plugins.valheim_plus"; private static bool _looked; private static string _always; private static PropertyInfo _vpCurrent; private static PropertyInfo _vpSection; private static PropertyInfo _vpEnabled; private static float _vpReadAt = -10f; private static bool _vpOn; private static string _said; internal static string Which { get { if (!_looked) { Look(); } string text = _always ?? (ValheimPlusOn() ? "ValheimPlus (its CraftFromChest section is on)" : null); if (text != _said) { _said = text; if (text != null) { Plugin.Log.LogWarning((object)(text + " also lets you build from chests; BuildFromChests stands down so chests aren't counted twice")); } else { Plugin.Log.LogInfo((object)"BuildFromChests builds from chests again (no other chest mod is doing it)"); } } return text; } } private static void Look() { _looked = true; string[][] always = Always; foreach (string[] array in always) { if (_always == null && Chainloader.PluginInfos.ContainsKey(array[0])) { _always = array[1]; } } if (!Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.valheim_plus", out var value) || !((Object)(object)value.Instance != (Object)null)) { return; } try { Type type = ((object)value.Instance).GetType().Assembly.GetType("ValheimPlus.Configurations.Configuration"); _vpCurrent = type?.GetProperty("Current", BindingFlags.Static | BindingFlags.Public); _vpSection = type?.GetProperty("CraftFromChest", BindingFlags.Instance | BindingFlags.Public); _vpEnabled = _vpSection?.PropertyType.GetProperty("IsEnabled", BindingFlags.Instance | BindingFlags.Public); if (_vpEnabled == null) { Plugin.Log.LogWarning((object)"ValheimPlus is installed but its CraftFromChest setting can't be read; if you turn it on, turn this mod off"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read ValheimPlus' settings: " + ex.Message)); } } private static bool ValheimPlusOn() { if (_vpEnabled == null) { return false; } if (Time.unscaledTime - _vpReadAt < 3f) { return _vpOn; } _vpReadAt = Time.unscaledTime; try { object value = _vpCurrent.GetValue(null, null); object obj = ((value != null) ? _vpSection.GetValue(value, null) : null); _vpOn = obj != null && (bool)_vpEnabled.GetValue(obj, null); } catch (Exception) { _vpOn = false; } return _vpOn; } } [HarmonyPatch(typeof(Container), "Awake")] internal static class Container_Awake { private static void Postfix(Container __instance) { ChestScanner.Register(__instance); } } [HarmonyPatch(typeof(Inventory), "CountItems", new Type[] { typeof(string), typeof(int), typeof(bool) })] internal static class Inventory_CountItems { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (name != null && ChestScanner.Applies(__instance)) { __result += ChestScanner.CountInChests(name, quality, matchWorldLevel); } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] internal static class Inventory_HaveItem { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (!__result && ChestScanner.Applies(__instance)) { __result = ChestScanner.CountInChests(name, -1, matchWorldLevel) > 0; } } } [HarmonyPatch(typeof(Player), "ConsumeResources")] internal static class Player_ConsumeResources { private static void Prefix(out bool __state) { __state = ChestScanner.Consuming; ChestScanner.Consuming = true; } private static void Finalizer(bool __state) { ChestScanner.Consuming = __state; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class Player_TryPlacePiece { [HarmonyPriority(0)] private static bool Prefix(Player __instance, Piece piece, ref bool __result, bool __runOriginal) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!__runOriginal) { return false; } if ((Object)(object)piece == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !ChestScanner.Applies(((Humanoid)__instance).GetInventory())) { return true; } if (__instance.NoCostCheat() || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) { return true; } string text = ChestScanner.FindShortfall(((Humanoid)__instance).GetInventory(), piece.m_resources); if (text == null) { return true; } ((Character)__instance).Message((MessageType)2, Translations.T("buildfromchests_short", Localization.instance.Localize(text)), 0, (Sprite)null, false); __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] internal static class Inventory_RemoveItem { [HarmonyPriority(0)] private static bool Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased, bool __runOriginal) { if (!__runOriginal) { return false; } if (!ChestScanner.Consuming || !ChestScanner.Applies(__instance)) { return true; } int num = ChestScanner.OwnCount(__instance, name, itemQuality, worldLevelBased); if (num >= amount) { return true; } int num2 = ChestScanner.RemoveFromChests(name, amount - num, itemQuality, worldLevelBased); if (num2 > 0) { Plugin.Log.LogWarning((object)$"Building: the chests were {num2} {name} short after all"); } amount = num; return num > 0; } } [BepInPlugin("com.quad.buildfromchests", "BuildFromChests", "1.5.2")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.quad.buildfromchests"; public const string OldGuid = "com.dhack.buildfromchests"; public const string Name = "BuildFromChests"; public const string Version = "1.5.2"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ShowHaveCounts; internal static ConfigEntry<float> Radius; internal static ManualLogSource Log; internal static ServerSettings Synced; private Harmony _harmony; private void Awake() { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown Migration.FromOldGuid((BaseUnityPlugin)(object)this, "com.dhack.buildfromchests"); Translations.Load((BaseUnityPlugin)(object)this); Log = ((BaseUnityPlugin)this).Logger; Synced = new ServerSettings("com.quad.buildfromchests", ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); ShowHaveCounts = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowHaveCounts", true, "In the build menu, show how many of each material you HAVE (inventory + chests) next to how many you need."); Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the mod on or off."); Radius = Synced.Add<float>(((BaseUnityPlugin)this).Config.Bind<float>("General", "Radius", 20f, new ConfigDescription("How far (in meters) from YOU a chest can be and still be used while building (also for BuildOrders: building its ghosts, hold E to build all, and its fetch key). Raise it to build far from your storehouse: 60 reaches across a big base. Chests only count while their area is loaded around you (about 100 m and more). Takes effect at once. In multiplayer the server's value applies.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 150f), Array.Empty<object>()))); _harmony = new Harmony("com.quad.buildfromchests"); _harmony.PatchAll(); Container[] array = Object.FindObjectsOfType<Container>(); foreach (Container c in array) { ChestScanner.Register(c); } Api.Publish(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"BuildFromChests 1.5.2 loaded"); if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString("[Mod]", Translations.T("buildfromchests_chat_reloaded", "BuildFromChests", "1.5.2", Localization.instance.Localize(Enabled.Value ? "$hud_on" : "$hud_off").ToLowerInvariant(), Radius.Value.ToString("0.#")), (Type)1, false); } } private void Update() { Synced?.Update(); } private void OnDestroy() { Api.Withdraw(); Translations.Unload(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Synced?.Dispose(); ChestScanner.Suspend = false; ChestScanner.Consuming = false; HaveLabel.DestroyAll(); } } } namespace DHack.Shared { internal static class Migration { private sealed class Notice : MonoBehaviour { public string Text; private float _next; private void Update() { if (!(Time.unscaledTime < _next) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)MessageHud.instance == (Object)null)) { ((Character)Player.m_localPlayer).Message((MessageType)2, Text, 0, (Sprite)null, false); _next = Time.unscaledTime + 60f; } } } public static void FromOldGuid(BaseUnityPlugin plugin, string oldGuid) { BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin); string text = ((metadata != null) ? metadata.GUID : null); if (string.IsNullOrEmpty(oldGuid) || string.IsNullOrEmpty(text) || oldGuid == text) { return; } ManualLogSource val = Logger.CreateLogSource(text); try { bool flag = false; if (Directory.Exists(Paths.ConfigPath)) { string[] files = Directory.GetFiles(Paths.ConfigPath, oldGuid + ".*"); foreach (string text2 in files) { string fileName = Path.GetFileName(text2); if (!fileName.EndsWith(".cfg") && !fileName.EndsWith(".json") && !fileName.EndsWith(".txt")) { continue; } string text3 = Path.Combine(Paths.ConfigPath, text + fileName.Substring(oldGuid.Length)); if (!File.Exists(text3)) { File.Copy(text2, text3); val.LogInfo((object)("Settings moved over from " + fileName + " (the mod's id is now " + text + "; the old file is kept as a backup)")); if (fileName == oldGuid + ".cfg") { flag = true; } } } } if (flag) { plugin.Config.Reload(); } } catch (Exception ex) { val.LogWarning((object)("Couldn't move the settings over from " + oldGuid + ".cfg: " + ex.Message)); } finally { Logger.Sources.Remove((ILogSource)(object)val); } ((MonoBehaviour)plugin).StartCoroutine(StandDownIfTwin(plugin, oldGuid)); } private static IEnumerator StandDownIfTwin(BaseUnityPlugin plugin, string oldGuid) { yield return null; yield return null; if (!((Object)(object)plugin == (Object)null) && Chainloader.PluginInfos.TryGetValue(oldGuid, out var value) && !((Object)(object)value.Instance == (Object)null) && !((Object)(object)value.Instance == (Object)(object)plugin)) { BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin); string text = ((metadata != null) ? metadata.Name : null) ?? oldGuid; string text2 = (string.IsNullOrEmpty(value.Location) ? "an older copy" : value.Location); string text3 = text + ": two copies are installed, an old one (" + text2 + ") and this one. Both would run and do everything twice, so this one stands down. Delete the old copy, then restart the game."; Debug.LogError((object)("[" + text + "] " + text3)); Object.Destroy((Object)(object)plugin); GameObject val = new GameObject("Migration_TwoCopies_" + text); Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<Notice>().Text = text3; } } } internal static class Translations { public static readonly string[] Shipped = new string[9] { "English", "Russian", "German", "Chinese", "Portuguese_Brazilian", "Spanish", "French", "Polish", "Turkish" }; private static readonly Dictionary<string, Dictionary<string, string>> Languages = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private static Dictionary<string, string> _english = new Dictionary<string, string>(); private static string _overrideDir; private static bool _hooked; private static readonly MethodInfo AddWord = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null); private static readonly FieldInfo Cache = AccessTools.Field(typeof(Localization), "m_cache"); public static void Load(BaseUnityPlugin plugin) { Languages.Clear(); Assembly assembly = ((object)plugin).GetType().Assembly; foreach (string item in from n in assembly.GetManifestResourceNames() where n.StartsWith("translations/") && n.EndsWith(".json") select n) { using StreamReader streamReader = new StreamReader(assembly.GetManifestResourceStream(item)); Languages[Path.GetFileNameWithoutExtension(item.Substring("translations/".Length))] = Parse(streamReader.ReadToEnd()); } _english = (Languages.TryGetValue("English", out var value) ? value : new Dictionary<string, string>()); _overrideDir = Path.Combine(Paths.ConfigPath, "translations", plugin.Info.Metadata.GUID); if (!_hooked) { Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(Apply)); _hooked = true; } Apply(); } public static void Unload() { if (_hooked) { Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(Apply)); _hooked = false; } } public static string T(string key, params object[] args) { string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + key) : null); if (string.IsNullOrEmpty(text) || text == "[" + key + "]") { text = (_english.TryGetValue(key, out var value) ? value : key); } if (args == null || args.Length == 0) { return text; } try { return string.Format(text, args); } catch (FormatException) { string value2; return _english.TryGetValue(key, out value2) ? SafeFormat(value2, args) : text; } } private static string SafeFormat(string text, object[] args) { try { return string.Format(text, args); } catch (FormatException) { return text; } } private static void Apply() { Localization instance = Localization.instance; if (instance == null || AddWord == null) { return; } string selectedLanguage = instance.GetSelectedLanguage(); Dictionary<string, string> dictionary = new Dictionary<string, string>(_english); if (Languages.TryGetValue(selectedLanguage, out var value)) { foreach (KeyValuePair<string, string> item in value) { dictionary[item.Key] = item.Value; } } try { string path = Path.Combine(_overrideDir, selectedLanguage + ".json"); if (File.Exists(path)) { foreach (KeyValuePair<string, string> item2 in Parse(File.ReadAllText(path))) { dictionary[item2.Key] = item2.Value; } } } catch (Exception ex) { Debug.LogWarning((object)("Translations: couldn't read the player's " + selectedLanguage + " file: " + ex.Message)); } foreach (KeyValuePair<string, string> item3 in dictionary) { AddWord.Invoke(instance, new object[2] { item3.Key, item3.Value }); } object obj = Cache?.GetValue(instance); obj?.GetType().GetMethod("EvictAll")?.Invoke(obj, null); } private static Dictionary<string, string> Parse(string json) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (Match item in Regex.Matches(json, "\"((?:[^\"\\\\]|\\\\.)*)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"")) { dictionary[Unescape(item.Groups[1].Value)] = Unescape(item.Groups[2].Value); } return dictionary; } private static string Unescape(string s) { return Regex.Replace(s, "\\\\(u[0-9a-fA-F]{4}|.)", delegate(Match m) { string value = m.Groups[1].Value; return value[0] switch { 'n' => "\n", 't' => "\t", 'u' => ((char)Convert.ToInt32(value.Substring(1), 16)).ToString(), _ => value, }; }); } } internal sealed class ServerSettings : IDisposable { private readonly string _send; private readonly string _ask; private readonly ConfigFile _config; private readonly ManualLogSource _log; private readonly List<ConfigEntryBase> _entries = new List<ConfigEntryBase>(); private readonly Dictionary<ConfigEntryBase, object> _own = new Dictionary<ConfigEntryBase, object>(); private readonly Dictionary<ConfigEntryBase, object> _server = new Dictionary<ConfigEntryBase, object>(); private ZRoutedRpc _registeredOn; private bool _applying; private bool _changePending; private float _nextChangeSend; private const float ChangeSendGap = 0.5f; public bool FromServer => _server.Count > 0; private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public ServerSettings(string guid, ConfigFile config, ManualLogSource log) { _send = guid + ".settings"; _ask = guid + ".settings.ask"; _config = config; _log = log; } public ConfigEntry<T> Add<T>(ConfigEntry<T> entry) { if (entry == null || _entries.Contains((ConfigEntryBase)(object)entry)) { return entry; } _entries.Add((ConfigEntryBase)(object)entry); entry.SettingChanged += OnChanged; return entry; } public void Update() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != _registeredOn) { Unregister(); Restore(); _changePending = false; if (instance != null) { Register(instance); } } if (_changePending && Time.unscaledTime >= _nextChangeSend) { SendChange(); } } public void Dispose() { Unregister(); Restore(); foreach (ConfigEntryBase entry in _entries) { Unsubscribe(entry); } _entries.Clear(); } private void Register(ZRoutedRpc rpc) { Forget(rpc, _send); Forget(rpc, _ask); rpc.Register<ZPackage>(_send, (Action<long, ZPackage>)OnValues); rpc.Register(_ask, (Action<long>)OnAsk); rpc.m_onNewPeer = (Action<long>)Delegate.Combine(rpc.m_onNewPeer, new Action<long>(OnNewPeer)); _registeredOn = rpc; } private void Unregister() { if (_registeredOn != null) { ZRoutedRpc registeredOn = _registeredOn; registeredOn.m_onNewPeer = (Action<long>)Delegate.Remove(registeredOn.m_onNewPeer, new Action<long>(OnNewPeer)); Forget(_registeredOn, _send); Forget(_registeredOn, _ask); _registeredOn = null; } } private static void Forget(ZRoutedRpc rpc, string name) { if (AccessTools.Field(typeof(ZRoutedRpc), "m_functions")?.GetValue(rpc) is IDictionary dictionary) { dictionary.Remove(StringExtensionMethods.GetStableHashCode(name)); } } private void OnNewPeer(long peer) { if (IsServer) { Send(peer); } else if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null && serverPeer.m_uid == peer) { Ask(); } } } private void OnAsk(long sender) { if (IsServer) { Send(sender); } } private void Ask() { try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZNet.instance.GetServerPeer().m_uid, _ask, Array.Empty<object>()); } } catch (Exception) { } } private void Send(long target) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (ZRoutedRpc.instance == null || _entries.Count == 0) { return; } ZPackage val = new ZPackage(); val.Write(_entries.Count); foreach (ConfigEntryBase entry in _entries) { val.Write(entry.Definition.Section); val.Write(entry.Definition.Key); val.Write(TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType)); } ZRoutedRpc.instance.InvokeRoutedRPC(target, _send, new object[1] { val }); } private void OnValues(long sender, ZPackage pkg) { if (IsServer) { return; } ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null); if (val == null || val.m_uid != sender) { return; } try { int num = pkg.ReadInt(); List<string> list = new List<string>(); for (int i = 0; i < num; i++) { string section = pkg.ReadString(); string key = pkg.ReadString(); string text = pkg.ReadString(); ConfigEntryBase val2 = ((IEnumerable<ConfigEntryBase>)_entries).FirstOrDefault((Func<ConfigEntryBase, bool>)((ConfigEntryBase x) => x.Definition.Section == section && x.Definition.Key == key)); if (val2 != null) { object obj; try { obj = TomlTypeConverter.ConvertToValue(text, val2.SettingType); } catch (Exception) { continue; } if (!_own.ContainsKey(val2)) { _own[val2] = val2.BoxedValue; } _server[val2] = obj; if (!object.Equals(val2.BoxedValue, obj)) { Apply(val2, obj); list.Add(section + "/" + key + " = " + text); } } } ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"Settings from the server apply while you play here ({_server.Count})" + ((list.Count > 0) ? (": " + string.Join(", ", list.ToArray())) : ""))); } } catch (Exception ex2) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("Could not read the server's settings: " + ex2.Message)); } } } private void Apply(ConfigEntryBase e, object value) { bool saveOnConfigSet = _config.SaveOnConfigSet; _applying = true; try { _config.SaveOnConfigSet = false; e.BoxedValue = value; } finally { _config.SaveOnConfigSet = saveOnConfigSet; _applying = false; } } private void Restore() { if (_server.Count == 0) { return; } foreach (KeyValuePair<ConfigEntryBase, object> item in _own) { Apply(item.Key, item.Value); } _own.Clear(); _server.Clear(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"Your own settings apply again"); } } private void OnChanged(object sender, EventArgs args) { if (_applying) { return; } ConfigEntryBase val = (ConfigEntryBase)((sender is ConfigEntryBase) ? sender : null); if (val == null) { return; } if (_server.TryGetValue(val, out var value)) { _own[val] = val.BoxedValue; Apply(val, value); } else if (IsServer) { _changePending = true; if (Time.unscaledTime >= _nextChangeSend) { SendChange(); } } } private void SendChange() { _changePending = false; _nextChangeSend = Time.unscaledTime + 0.5f; if (IsServer && ZNet.instance.GetPeers().Count > 0) { Send(0L); } } private void Unsubscribe(ConfigEntryBase e) { ((object)e).GetType().GetEvent("SettingChanged")?.RemoveEventHandler(e, new EventHandler(OnChanged)); } } internal static class SpecialContainers { private static readonly string[] Prefabs = new string[2] { "piece_ledgerchest", "piece_recycler" }; public static bool NotStorage(Container c) { if ((Object)(object)c == (Object)null) { return false; } Piece componentInParent = ((Component)c).GetComponentInParent<Piece>(); string text = (((Object)(object)componentInParent != (Object)null) ? ((Object)((Component)componentInParent).gameObject).name : ((Object)((Component)c).gameObject).name); string[] prefabs = Prefabs; foreach (string value in prefabs) { if (text.StartsWith(value, StringComparison.Ordinal) || ((Object)((Component)c).gameObject).name.StartsWith(value, StringComparison.Ordinal)) { return true; } } if ((Object)(object)(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponentInChildren<Incinerator>(true) : ((Component)c).GetComponentInParent<Incinerator>()) != (Object)null) { return true; } ZNetView component = ((Component)c).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val != null) { if (val.GetInt("dh_arena_chest", 0) == 0) { return val.GetBool("dh_notstorage", false); } return true; } return false; } } }