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 Build From Chest v0.1.0
FrizzQOL.BuildFromChest.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("FrizzQOL.BuildFromChest")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+41e0e2d57405e14d22a27a1d3d5c99c152fb52f0")] [assembly: AssemblyProduct("FrizzQOL.BuildFromChest")] [assembly: AssemblyTitle("FrizzQOL.BuildFromChest")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 BuildFromChest { public sealed class ChestCandidate { public int Id; public float Distance; public bool CanOpen; public int WoodCount; public int StoneCount; } public static class ChestPay { public const string Wood = "$item_wood"; public const string Stone = "$item_stone"; public const string WoodPrefab = "Wood"; public const string StonePrefab = "Stone"; public static int Missing(int cost, int inInventory) { int num = cost - inInventory; if (num <= 0) { return 0; } return num; } public static bool NeedsPull(int missingWood, int missingStone) { if (missingWood <= 0) { return missingStone > 0; } return true; } public static bool CanCover(int chestWood, int chestStone, int missingWood, int missingStone) { if (chestWood >= missingWood) { return chestStone >= missingStone; } return false; } public static bool IsInRange(float distance, float radius) { if (radius > 0f) { return distance <= radius; } return false; } public static bool IsChestPrefab(string name) { switch (name) { default: return name == "piece_chest_blackmetal"; case "piece_chest_wood": case "piece_chest": case "piece_chest_private": return true; } } public static bool IsHammer(string prefabName) { return prefabName == "Hammer"; } public static string PrefabKey(string name) { if (string.IsNullOrEmpty(name)) { return ""; } if (name.EndsWith("(Clone)")) { return name.Substring(0, name.Length - "(Clone)".Length); } return name; } public static void AddCost(string itemName, int amount, ref int wood, ref int stone) { if (!string.IsNullOrEmpty(itemName) && amount > 0) { if (itemName == "$item_wood") { wood += amount; } else if (itemName == "$item_stone") { stone += amount; } } } public static int? PickNearest(IList<ChestCandidate> candidates, float radius) { int? result = null; float num = 0f; if (candidates == null) { return null; } for (int i = 0; i < candidates.Count; i++) { ChestCandidate chestCandidate = candidates[i]; if (chestCandidate != null && chestCandidate.CanOpen && IsInRange(chestCandidate.Distance, radius) && (!result.HasValue || chestCandidate.Distance < num || (chestCandidate.Distance == num && chestCandidate.Id < result.Value))) { result = chestCandidate.Id; num = chestCandidate.Distance; } } return result; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class PlacePatch { private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null); private static readonly MethodInfo SaveMethod = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null); private static readonly FieldInfo NViewField = AccessTools.Field(typeof(Container), "m_nview"); private static bool _busy; private static bool _loggedAccess; private static bool _loggedSave; private static Container _chest; private static int _wood; private static int _stone; private static Collider[] _hits = (Collider[])(object)new Collider[256]; private static readonly List<ChestCandidate> Candidates = new List<ChestCandidate>(); private static readonly Dictionary<int, Container> ById = new Dictionary<int, Container>(); private static void Prefix(Player __instance, Piece piece) { if (!_busy && !((Object)(object)_chest != (Object)null)) { Prepare(__instance, piece); } } internal static void Prepare(Player player, Piece piece) { if (_busy || (Object)(object)_chest != (Object)null || (Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !CanSaveChests()) { return; } ItemData rightItem = ((Humanoid)player).RightItem; if (!ChestPay.IsHammer((rightItem != null && (Object)(object)rightItem.m_dropPrefab != (Object)null) ? ChestPay.PrefabKey(((Object)rightItem.m_dropPrefab).name) : "") || IsFreeBuild(piece)) { return; } int wood = 0; int stone = 0; SumCosts(piece.m_resources, ref wood, ref stone); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } int num = ChestPay.Missing(wood, inventory.CountItems("$item_wood", -1, true)); int num2 = ChestPay.Missing(stone, inventory.CountItems("$item_stone", -1, true)); if (!ChestPay.NeedsPull(num, num2)) { return; } float radius = Plugin.CurrentRadius(); int wood2; int stone2; Container val = FindChest(player, radius, out wood2, out stone2); if ((Object)(object)val == (Object)null || !ChestPay.CanCover(wood2, stone2, num, num2)) { return; } _busy = true; try { if (MoveToPlayer(val, inventory, num, num2)) { _chest = val; _wood = num; _stone = num2; } } catch (Exception ex) { _chest = null; _wood = 0; _stone = 0; Plugin.LogWarning("Chest pull failed: " + ex.Message); } finally { _busy = false; } } internal static void Finish(Player player) { if ((Object)(object)_chest == (Object)null) { _busy = false; return; } Container chest = _chest; int wood = _wood; int stone = _stone; _chest = null; _wood = 0; _stone = 0; _busy = false; ReturnToChest(player, chest, wood, stone); } private static bool IsFreeBuild(Piece piece) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)piece == (Object)null || (Object)(object)instance == (Object)null) { return false; } return instance.GetGlobalKey(piece.FreeBuildKey()); } private static void Postfix(Player __instance, bool __result) { try { if (!((Object)(object)_chest == (Object)null)) { Container chest = _chest; int wood = _wood; int stone = _stone; _chest = null; _wood = 0; _stone = 0; if (!__result) { ReturnToChest(__instance, chest, wood, stone); } } } finally { _busy = false; } } private static void SumCosts(Requirement[] requirements, ref int wood, ref int stone) { if (requirements == null) { return; } foreach (Requirement val in requirements) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_resItem.m_itemData != null && val.m_resItem.m_itemData.m_shared != null) { ChestPay.AddCost(val.m_resItem.m_itemData.m_shared.m_name, val.m_amount, ref wood, ref stone); } } } private static Container FindChest(Player player, float radius, out int wood, out int stone) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) wood = 0; stone = 0; if (radius <= 0f) { return null; } Vector3 position = ((Component)player).transform.position; int num = Overlap(position, radius + 2f); Candidates.Clear(); ById.Clear(); for (int i = 0; i < num; i++) { Collider val = _hits[i]; _hits[i] = null; if ((Object)(object)val == (Object)null) { continue; } Container componentInParent = ((Component)val).GetComponentInParent<Container>(); if (!((Object)(object)componentInParent == (Object)null)) { int instanceID = ((Object)componentInParent).GetInstanceID(); if (!ById.ContainsKey(instanceID) && ChestPay.IsChestPrefab(ChestPay.PrefabKey(((Object)((Component)componentInParent).gameObject).name))) { Inventory inventory = componentInParent.GetInventory(); bool flag = inventory != null && CanOpen(componentInParent, player); ChestCandidate item = new ChestCandidate { Id = instanceID, Distance = Vector3.Distance(position, ((Component)componentInParent).transform.position), CanOpen = flag, WoodCount = (flag ? inventory.CountItems("$item_wood", -1, true) : 0), StoneCount = (flag ? inventory.CountItems("$item_stone", -1, true) : 0) }; Candidates.Add(item); ById[instanceID] = componentInParent; } } } int? num2 = ChestPay.PickNearest(Candidates, radius); if (!num2.HasValue || !ById.TryGetValue(num2.Value, out var value)) { return null; } for (int j = 0; j < Candidates.Count; j++) { if (Candidates[j].Id == num2.Value) { wood = Candidates[j].WoodCount; stone = Candidates[j].StoneCount; break; } } return value; } private static int Overlap(Vector3 origin, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) int num = Physics.OverlapSphereNonAlloc(origin, radius, _hits, -1, (QueryTriggerInteraction)2); while (num == _hits.Length && _hits.Length < 4096) { _hits = (Collider[])(object)new Collider[_hits.Length * 2]; num = Physics.OverlapSphereNonAlloc(origin, radius, _hits, -1, (QueryTriggerInteraction)2); } return num; } private static bool MoveToPlayer(Container chest, Inventory playerInventory, int missingWood, int missingStone) { Inventory inventory = chest.GetInventory(); if (inventory == null) { return false; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; try { num = Take(inventory, "$item_wood", missingWood); if (num != missingWood) { UndoPull(chest, inventory, playerInventory, num, num2, num3, num4); return false; } num2 = Take(inventory, "$item_stone", missingStone); if (num2 != missingStone) { UndoPull(chest, inventory, playerInventory, num, num2, num3, num4); return false; } num3 = Give(playerInventory, "Wood", "$item_wood", missingWood); num4 = Give(playerInventory, "Stone", "$item_stone", missingStone); if (num3 != missingWood || num4 != missingStone) { UndoPull(chest, inventory, playerInventory, num, num2, num3, num4); return false; } SaveChest(chest); return true; } catch (Exception ex) { Plugin.LogWarning("Chest pull failed, restoring items: " + ex.Message); UndoPull(chest, inventory, playerInventory, num, num2, num3, num4); return false; } } private static void UndoPull(Container chest, Inventory chestInventory, Inventory playerInventory, int gotWood, int gotStone, int putWood, int putStone) { int num = Take(playerInventory, "$item_wood", putWood); int num2 = Take(playerInventory, "$item_stone", putStone); Give(chestInventory, "Wood", "$item_wood", gotWood - putWood + num); Give(chestInventory, "Stone", "$item_stone", gotStone - putStone + num2); SaveChest(chest); } private static void ReturnToChest(Player player, Container chest, int wood, int stone) { if ((Object)(object)player == (Object)null || (Object)(object)chest == (Object)null || chest.GetInventory() == null || ((Humanoid)player).GetInventory() == null) { Plugin.LogWarning("Could not return pulled wood and stone."); return; } Inventory inventory = ((Humanoid)player).GetInventory(); Inventory inventory2 = chest.GetInventory(); int num = Take(inventory, "$item_wood", wood); int num2 = Take(inventory, "$item_stone", stone); int num3 = Give(inventory2, "Wood", "$item_wood", num); int num4 = Give(inventory2, "Stone", "$item_stone", num2); int num5 = num - num3; int num6 = num2 - num4; if (num5 > 0) { Give(inventory, "Wood", "$item_wood", num5); } if (num6 > 0) { Give(inventory, "Stone", "$item_stone", num6); } SaveChest(chest); if (num != wood || num2 != stone || num5 > 0 || num6 > 0) { Plugin.LogWarning("Pulled wood or stone could not all return to the chest."); } } private static bool CanOpen(Container container, Player player) { if (CheckAccessMethod == null) { if (!_loggedAccess) { _loggedAccess = true; Plugin.LogWarning("Container.CheckAccess is missing. Chest pull is off."); } return false; } object obj = CheckAccessMethod.Invoke(container, new object[1] { player.GetPlayerID() }); if (!(obj is bool) || !(bool)obj || container.IsInUse()) { return false; } if (NViewField == null) { return false; } object? value = NViewField.GetValue(container); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if (val == null || (Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner()) { return false; } return true; } private static bool CanSaveChests() { if (SaveMethod != null) { return true; } if (!_loggedSave) { _loggedSave = true; Plugin.LogWarning("Container.Save is missing. Chest pull is off."); } return false; } private static void SaveChest(Container container) { if ((Object)(object)container != (Object)null && SaveMethod != null) { SaveMethod.Invoke(container, null); } } private static int Take(Inventory inventory, string sharedName, int amount) { if (inventory == null || amount <= 0) { return 0; } int num = inventory.CountItems(sharedName, -1, true); inventory.RemoveItem(sharedName, amount, -1, true); return num - inventory.CountItems(sharedName, -1, true); } private static int Give(Inventory inventory, string prefabName, string sharedName, int amount) { if (inventory == null || amount <= 0) { return 0; } int num = inventory.CountItems(sharedName, -1, true); inventory.AddItem(prefabName, amount, 1, 0, 0L, "", false, false); return inventory.CountItems(sharedName, -1, true) - num; } } public static class PlacementClick { [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static class UpdatePlacementPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(PlacementClick), "PullBeforeRequirements", (Type[])null, (Type[])null); for (int i = 3; i < list.Count; i++) { if (IsPieceRequirementCheck(list[i])) { CodeInstruction val = list[i - 2]; if (!(list[i - 3].opcode != OpCodes.Ldarg_0) && IsLoadLocal(val.opcode) && !(list[i - 1].opcode != OpCodes.Ldc_I4_0)) { int num = i - 3; list.Insert(num, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Insert(num + 1, new CodeInstruction(val.opcode, val.operand)); list.Insert(num + 2, new CodeInstruction(OpCodes.Call, (object)methodInfo)); return list; } } } Plugin.LogWarning("UpdatePlacement does not check requirements before place. A short hammer click will not pull."); return list; } private static void Postfix(Player __instance) { PlacePatch.Finish(__instance); } private static Exception Finalizer(Player __instance, Exception __exception) { PlacePatch.Finish(__instance); return __exception; } private static bool IsPieceRequirementCheck(CodeInstruction instruction) { if (instruction.opcode != OpCodes.Call && instruction.opcode != OpCodes.Callvirt) { return false; } MethodInfo methodInfo = instruction.operand as MethodInfo; if (methodInfo == null || methodInfo.Name != "HaveRequirements") { return false; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 2) { return parameters[0].ParameterType == typeof(Piece); } return false; } private static bool IsLoadLocal(OpCode opcode) { if (!(opcode == OpCodes.Ldloc) && !(opcode == OpCodes.Ldloc_S) && !(opcode == OpCodes.Ldloc_0) && !(opcode == OpCodes.Ldloc_1) && !(opcode == OpCodes.Ldloc_2)) { return opcode == OpCodes.Ldloc_3; } return true; } } public static void PullBeforeRequirements(Player player, Piece piece) { PlacePatch.Prepare(player, piece); } } [BepInPlugin("com.frizzqol.buildfromchest", "FrizzQOL Build From Chest", "0.1.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.frizzqol.buildfromchest"; public const string PluginName = "FrizzQOL Build From Chest"; public const string PluginVersion = "0.1.0"; internal static ConfigEntry<float> ChestRadius; private Harmony _harmony; internal static Plugin Instance { get; private set; } private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown Instance = this; ChestRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ChestRadius", 10f, "Meters from the player to the nearest chest. 0 or less disables the pull."); _harmony = new Harmony("com.frizzqol.buildfromchest"); try { _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"FrizzQOL Build From Chest 0.1.0 loaded"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to patch placement: " + ex.Message)); } } internal static float CurrentRadius() { if (ChestRadius == null) { return 10f; } return ChestRadius.Value; } internal static void LogWarning(string message) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)message); } } } }