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 StoreAndCraft v1.0.0
plugins/StoreAndCraft/StoreAndCraft.dll
Decompiled 6 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("StoreAndCraft")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+90e867a18187e478d916d898790b505a6bfe1b78")] [assembly: AssemblyProduct("StoreAndCraft")] [assembly: AssemblyTitle("StoreAndCraft")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace StoreAndCraft; public class ModConfig { public const int ProtocolVersion = 1; public ConfigEntry<bool> LockConfig { get; } public ConfigEntry<bool> ModEnabled { get; } public ConfigEntry<bool> StoreEnabled { get; } public ConfigEntry<bool> CraftEnabled { get; } public ConfigEntry<bool> MustHaveExisting { get; } public ConfigEntry<bool> LeaveOneItem { get; } public ConfigEntry<bool> IgnoreHotbar { get; } public ConfigEntry<bool> HighlightOnStore { get; } public ConfigEntry<bool> PingOnStore { get; } public ConfigEntry<float> PlayerDumpRange { get; } public ConfigEntry<float> StoreRange { get; } public ConfigEntry<float> CraftRange { get; } public ConfigEntry<float> IntakeInterval { get; } public ConfigEntry<float> PauseSeconds { get; } public ConfigEntry<int> MaxTransfersPerTick { get; } public ConfigEntry<KeyboardShortcut> DumpKey { get; } public ConfigEntry<KeyboardShortcut> HoverStoreKey { get; } public ConfigEntry<KeyboardShortcut> PauseKey { get; } public ConfigEntry<KeyboardShortcut> SearchKey { get; } public ConfigEntry<KeyboardShortcut> PreventPullKey { get; } public ConfigEntry<KeyboardShortcut> RenameKey { get; } public ModConfig(ConfigFile file) { //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) LockConfig = file.Bind<bool>("1 - General", "LockConfig", true, "If enabled, gameplay settings come from the server and clients cannot override them."); ModEnabled = file.Bind<bool>("1 - General", "ModEnabled", true, "Turns the whole mod on or off without uninstalling."); StoreEnabled = file.Bind<bool>("2 - Store", "StoreEnabled", true, "If enabled, ground items can be auto-stored and dump / middle-click store works."); CraftEnabled = file.Bind<bool>("3 - Craft", "CraftEnabled", true, "If enabled, crafting and building can use items stored in nearby chests."); MustHaveExisting = file.Bind<bool>("2 - Store", "MustHaveExisting", true, "If enabled, a chest only accepts an item if that item is already inside it. Empty chests will not vacuum new item types."); LeaveOneItem = file.Bind<bool>("3 - Craft", "LeaveOneItem", true, "If enabled, crafting/building leaves 1 item in each chest so auto-store can keep filling that stack."); IgnoreHotbar = file.Bind<bool>("2 - Store", "IgnoreHotbar", true, "If enabled, dump will not move items from the hotbar (first inventory row)."); HighlightOnStore = file.Bind<bool>("2 - Store", "HighlightOnStore", true, "If enabled, a chest flashes when something is stored into it."); PingOnStore = file.Bind<bool>("2 - Store", "PingOnStore", false, "If enabled, a map ping is placed on the chest after a store."); PlayerDumpRange = file.Bind<float>("2 - Store", "PlayerDumpRange", 8f, "Fallback dump / middle-click range in meters (player to chest). YAML dumpRange overrides this."); StoreRange = file.Bind<float>("2 - Store", "StoreRange", 10f, "Fallback auto-store range in meters (item on ground to chest). YAML storeRange overrides this."); CraftRange = file.Bind<float>("3 - Craft", "CraftRange", 20f, "Fallback craft/build range in meters (player to chest). YAML craftRange overrides this."); IntakeInterval = file.Bind<float>("2 - Store", "IntakeInterval", 5f, "Seconds between automatic scans for ground items. Lower = snappier, higher = less CPU."); PauseSeconds = file.Bind<float>("2 - Store", "PauseSeconds", 10f, "How many seconds auto-store stays paused after the pause hotkey."); MaxTransfersPerTick = file.Bind<int>("1 - General", "MaxTransfersPerTick", 8, "Maximum item moves per frame. Raise only if storing feels too slow."); DumpKey = file.Bind<KeyboardShortcut>("4 - Keys", "DumpKey", new KeyboardShortcut((KeyCode)46, Array.Empty<KeyCode>()), "Hotkey: move allowed inventory stacks into nearby chests that already hold those items."); HoverStoreKey = file.Bind<KeyboardShortcut>("4 - Keys", "HoverStoreKey", new KeyboardShortcut((KeyCode)325, Array.Empty<KeyCode>()), "Hotkey: store the inventory item under the cursor. If no item is hovered, dumps all allowed stacks."); PauseKey = file.Bind<KeyboardShortcut>("4 - Keys", "PauseKey", new KeyboardShortcut((KeyCode)112, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Hotkey: pause auto-store for PauseSeconds."); SearchKey = file.Bind<KeyboardShortcut>("4 - Keys", "SearchKey", new KeyboardShortcut((KeyCode)121, Array.Empty<KeyCode>()), "Hold this key and click an inventory item to ping the nearest chest that contains it."); PreventPullKey = file.Bind<KeyboardShortcut>("4 - Keys", "PreventPullKey", new KeyboardShortcut((KeyCode)111, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Hotkey: locally disable pulling from chests for crafting/building."); RenameKey = file.Bind<KeyboardShortcut>("4 - Keys", "RenameKey", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Look at a chest and hold this combo instead of opening it. Shift+E (Valheim alt-use) also renames. The custom name is shown on hover."); } public void WriteToPackage(ZPackage pkg) { pkg.Write(LockConfig.Value); pkg.Write(ModEnabled.Value); pkg.Write(StoreEnabled.Value); pkg.Write(CraftEnabled.Value); pkg.Write(MustHaveExisting.Value); pkg.Write(LeaveOneItem.Value); pkg.Write(PlayerDumpRange.Value); pkg.Write(StoreRange.Value); pkg.Write(CraftRange.Value); pkg.Write(IntakeInterval.Value); pkg.Write(PauseSeconds.Value); pkg.Write(MaxTransfersPerTick.Value); } public void ReadFromPackage(ZPackage pkg) { LockConfig.Value = pkg.ReadBool(); ModEnabled.Value = pkg.ReadBool(); StoreEnabled.Value = pkg.ReadBool(); CraftEnabled.Value = pkg.ReadBool(); MustHaveExisting.Value = pkg.ReadBool(); LeaveOneItem.Value = pkg.ReadBool(); PlayerDumpRange.Value = pkg.ReadSingle(); StoreRange.Value = pkg.ReadSingle(); CraftRange.Value = pkg.ReadSingle(); IntakeInterval.Value = pkg.ReadSingle(); PauseSeconds.Value = pkg.ReadSingle(); MaxTransfersPerTick.Value = pkg.ReadInt(); } } internal sealed class PieceRule { public bool? Store; public bool? Craft; public float? StoreRange; public float? CraftRange; public float? DumpRange; public readonly List<string> Allow = new List<string>(); public readonly List<string> Deny = new List<string>(); } internal static class RulesFile { private static readonly Dictionary<string, PieceRule> Pieces = new Dictionary<string, PieceRule>(StringComparer.OrdinalIgnoreCase); public static float? DefaultStoreRange { get; private set; } public static float? DefaultCraftRange { get; private set; } public static float? DefaultDumpRange { get; private set; } public static string Path => System.IO.Path.Combine(Paths.ConfigPath, "StoreAndCraft.rules.yml"); public static void LoadOrCreate() { try { if (!File.Exists(Path)) { File.WriteAllText(Path, DefaultText(), Encoding.UTF8); } Parse(File.ReadAllLines(Path)); Plugin.Log.LogInfo((object)("StoreAndCraft rules loaded (" + Pieces.Count + " piece entries).")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("StoreAndCraft rules failed: " + ex.Message)); } } public static PieceRule Get(string piecePrefab) { if (string.IsNullOrEmpty(piecePrefab)) { return null; } if (!Pieces.TryGetValue(piecePrefab, out var value)) { return null; } return value; } public static bool AllowsStore(string piecePrefab, ItemData item) { return Allows(piecePrefab, item, store: true); } public static bool AllowsCraft(string piecePrefab, string sharedName) { PieceRule pieceRule = Get(piecePrefab); if (pieceRule != null && pieceRule.Craft.HasValue && !pieceRule.Craft.Value) { return false; } if (pieceRule == null) { return true; } return MatchesLists(pieceRule, sharedName, null); } public static bool AllowsCraft(string piecePrefab, ItemData item) { return Allows(piecePrefab, item, store: false); } private static bool Allows(string piecePrefab, ItemData item, bool store) { PieceRule pieceRule = Get(piecePrefab); if (pieceRule == null) { return true; } if (store && pieceRule.Store.HasValue && !pieceRule.Store.Value) { return false; } if (!store && pieceRule.Craft.HasValue && !pieceRule.Craft.Value) { return false; } return MatchesLists(pieceRule, ItemIds.SharedName(item), item); } private static bool MatchesLists(PieceRule rule, string sharedName, ItemData item) { if (rule.Deny.Count > 0) { foreach (string item2 in rule.Deny) { if (item != null && ItemIds.Matches(item, item2)) { return false; } if (item == null && NamesEqual(sharedName, item2)) { return false; } } } if (rule.Allow.Count == 0) { return true; } foreach (string item3 in rule.Allow) { if (item != null && ItemIds.Matches(item, item3)) { return true; } if (item == null && NamesEqual(sharedName, item3)) { return true; } } return false; } private static bool NamesEqual(string sharedName, string token) { if (string.IsNullOrEmpty(sharedName) || string.IsNullOrEmpty(token)) { return false; } if (string.Equals(sharedName, token, StringComparison.OrdinalIgnoreCase)) { return true; } string b = ItemIds.SharedFromToken(token); return string.Equals(sharedName, b, StringComparison.OrdinalIgnoreCase); } public static float StoreRange(string piecePrefab, float fallback) { PieceRule pieceRule = Get(piecePrefab); if (pieceRule != null && pieceRule.StoreRange.HasValue) { return pieceRule.StoreRange.Value; } if (DefaultStoreRange.HasValue) { return DefaultStoreRange.Value; } return fallback; } public static float CraftRange(string piecePrefab, float fallback) { PieceRule pieceRule = Get(piecePrefab); if (pieceRule != null && pieceRule.CraftRange.HasValue) { return pieceRule.CraftRange.Value; } if (DefaultCraftRange.HasValue) { return DefaultCraftRange.Value; } return fallback; } public static float DumpRange(string piecePrefab, float fallback) { PieceRule pieceRule = Get(piecePrefab); if (pieceRule != null && pieceRule.DumpRange.HasValue) { return pieceRule.DumpRange.Value; } if (DefaultDumpRange.HasValue) { return DefaultDumpRange.Value; } return fallback; } public static float MaxScanRange(float cfgStore, float cfgCraft, float cfgDump) { float num = Mathf.Max(new float[3] { cfgStore, cfgCraft, cfgDump }); if (DefaultStoreRange.HasValue) { num = Mathf.Max(num, DefaultStoreRange.Value); } if (DefaultCraftRange.HasValue) { num = Mathf.Max(num, DefaultCraftRange.Value); } if (DefaultDumpRange.HasValue) { num = Mathf.Max(num, DefaultDumpRange.Value); } foreach (PieceRule value in Pieces.Values) { if (value.StoreRange.HasValue) { num = Mathf.Max(num, value.StoreRange.Value); } if (value.CraftRange.HasValue) { num = Mathf.Max(num, value.CraftRange.Value); } if (value.DumpRange.HasValue) { num = Mathf.Max(num, value.DumpRange.Value); } } return num; } private static void Parse(string[] lines) { Pieces.Clear(); DefaultStoreRange = null; DefaultCraftRange = null; DefaultDumpRange = null; string text = null; PieceRule pieceRule = null; string text2 = null; bool flag = false; for (int i = 0; i < lines.Length; i++) { string text3 = lines[i]; int num = text3.IndexOf('#'); if (num >= 0) { text3 = text3.Substring(0, num); } if (string.IsNullOrWhiteSpace(text3)) { continue; } int j; for (j = 0; j < text3.Length && text3[j] == ' '; j++) { } text3 = text3.Trim(); if (j == 0 && text3.EndsWith(":") && !text3.Equals("defaults:", StringComparison.OrdinalIgnoreCase) && !text3.Equals("pieces:", StringComparison.OrdinalIgnoreCase)) { continue; } if (text3.Equals("defaults:", StringComparison.OrdinalIgnoreCase)) { flag = true; text = null; pieceRule = null; text2 = null; } else if (text3.Equals("pieces:", StringComparison.OrdinalIgnoreCase)) { flag = false; text = null; pieceRule = null; text2 = null; } else if (flag) { if (SplitKv(text3, out var key, out var value)) { if (key.Equals("storeRange", StringComparison.OrdinalIgnoreCase)) { DefaultStoreRange = ParseFloat(value); } else if (key.Equals("craftRange", StringComparison.OrdinalIgnoreCase)) { DefaultCraftRange = ParseFloat(value); } else if (key.Equals("dumpRange", StringComparison.OrdinalIgnoreCase)) { DefaultDumpRange = ParseFloat(value); } } } else if (j <= 2 && text3.EndsWith(":") && j < 4 && !IsKeyValue(text3)) { text = text3.TrimEnd(new char[1] { ':' }).Trim(); pieceRule = new PieceRule(); Pieces[text] = pieceRule; text2 = null; } else { if (pieceRule == null) { continue; } if (text3.Equals("allow:", StringComparison.OrdinalIgnoreCase)) { text2 = "allow"; continue; } if (text3.Equals("deny:", StringComparison.OrdinalIgnoreCase)) { text2 = "deny"; continue; } if (text3.StartsWith("- ")) { string item = text3.Substring(2).Trim().Trim(new char[1] { '"' }); if (text2 == "allow") { pieceRule.Allow.Add(item); } else if (text2 == "deny") { pieceRule.Deny.Add(item); } continue; } text2 = null; if (SplitKv(text3, out var key2, out var value2)) { if (key2.Equals("store", StringComparison.OrdinalIgnoreCase)) { pieceRule.Store = ParseBool(value2); } else if (key2.Equals("craft", StringComparison.OrdinalIgnoreCase)) { pieceRule.Craft = ParseBool(value2); } else if (key2.Equals("storeRange", StringComparison.OrdinalIgnoreCase)) { pieceRule.StoreRange = ParseFloat(value2); } else if (key2.Equals("craftRange", StringComparison.OrdinalIgnoreCase)) { pieceRule.CraftRange = ParseFloat(value2); } else if (key2.Equals("dumpRange", StringComparison.OrdinalIgnoreCase)) { pieceRule.DumpRange = ParseFloat(value2); } } } } } private static bool IsKeyValue(string line) { int num = line.IndexOf(':'); if (num > 0) { return num < line.Length - 1; } return false; } private static bool SplitKv(string line, out string key, out string value) { key = null; value = null; int num = line.IndexOf(':'); if (num <= 0) { return false; } key = line.Substring(0, num).Trim(); value = line.Substring(num + 1).Trim().Trim(new char[1] { '"' }); return key.Length > 0; } private static bool ParseBool(string value) { if (!value.Equals("true", StringComparison.OrdinalIgnoreCase) && !value.Equals("yes", StringComparison.OrdinalIgnoreCase)) { return value.Equals("on", StringComparison.OrdinalIgnoreCase); } return true; } private static float ParseFloat(string value) { if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return 0f; } private static string DefaultText() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# StoreAndCraft rules"); stringBuilder.AppendLine("# File: BepInEx/config/StoreAndCraft.rules.yml"); stringBuilder.AppendLine("# Distances are in meters. Saving this file on the server reloads and syncs."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# storeRange = how far a chest pulls items from the ground"); stringBuilder.AppendLine("# dumpRange = how far dump / middle-click can send items from the player to a chest"); stringBuilder.AppendLine("# craftRange = how far crafting and building can pull materials from a chest"); stringBuilder.AppendLine("# store/craft = true/false to allow storing or crafting from that piece"); stringBuilder.AppendLine("# allow = only these items (prefab name like Wood, or $item_wood). Empty = all items."); stringBuilder.AppendLine("# deny = never store/craft these items"); stringBuilder.AppendLine(); stringBuilder.AppendLine("defaults:"); stringBuilder.AppendLine(" storeRange: 10"); stringBuilder.AppendLine(" dumpRange: 12"); stringBuilder.AppendLine(" craftRange: 20"); stringBuilder.AppendLine(); stringBuilder.AppendLine("pieces:"); stringBuilder.AppendLine(" piece_chest:"); stringBuilder.AppendLine(" store: true"); stringBuilder.AppendLine(" craft: true"); stringBuilder.AppendLine(" piece_chest_wood:"); stringBuilder.AppendLine(" store: true"); stringBuilder.AppendLine(" craft: true"); stringBuilder.AppendLine(" # storeRange: 15"); stringBuilder.AppendLine(" # dumpRange: 15"); stringBuilder.AppendLine(" # craftRange: 25"); return stringBuilder.ToString(); } } internal static class RequirementBridge { public static int CountNearby(Player player, string sharedName) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || Plugin.Settings == null || string.IsNullOrEmpty(sharedName)) { return 0; } return NearbyIndex.CountItem(((Component)player).transform.position, 0f, sharedName, Plugin.Settings.LeaveOneItem.Value); } public static bool SenderInRange(long sender, Vector3 target, float range) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null) { return true; } ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null) { return true; } float num = 6f; return Vector3.Distance(peer.m_refPos, target) <= range + num; } } internal static class StagingPull { public static bool PullingEnabled = true; private static float _retryCraftAt; private static bool _retryCraft; public static bool Active { get { if (Plugin.Settings != null && Plugin.Settings.ModEnabled.Value && Plugin.Settings.CraftEnabled.Value) { return PullingEnabled; } return false; } } public static void Tick() { if (_retryCraft && !(Time.time < _retryCraftAt)) { _retryCraft = false; InventoryGui instance = InventoryGui.instance; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)instance == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)Refs.CraftRecipe(instance) == (Object)null)) { AccessToolsDoCraft(instance, localPlayer); } } } public static bool EnsureRecipe(Player player, Recipe recipe, int amount, out bool waiting) { waiting = false; if (!Active || (Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || recipe.m_resources == null) { return true; } return PullRequirements(player, recipe.m_resources, (!((Object)(object)recipe.m_item != (Object)null)) ? 1 : recipe.m_item.m_itemData.m_quality, -1, amount, out waiting); } public static bool EnsurePiece(Player player, Piece piece, out bool waiting) { waiting = false; if (!Active || (Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || piece.m_resources == null) { return true; } return PullRequirements(player, piece.m_resources, 1, -1, 1, out waiting); } public static bool PullRequirements(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, out bool waiting) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) waiting = false; if ((Object)(object)player == (Object)null || requirements == null) { return true; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return true; } NearbyIndex.Tick(); bool value = Plugin.Settings.LeaveOneItem.Value; float value2 = Plugin.Settings.CraftRange.Value; Vector3 position = ((Component)player).transform.position; bool result = true; foreach (Requirement val in requirements) { if (val == null || (Object)(object)val.m_resItem == (Object)null) { continue; } int num = val.GetAmount(qualityLevel) * Mathf.Max(1, multiplier); if (num <= 0) { continue; } string text = ((val.m_resItem.m_itemData != null) ? val.m_resItem.m_itemData.m_shared.m_name : null); if (string.IsNullOrEmpty(text)) { continue; } int num2 = inventory.CountItems(text, itemQuality, true); int num3 = num - num2; if (num3 <= 0) { continue; } foreach (Container item in NearbyIndex.Current) { if (num3 <= 0) { break; } if ((Object)(object)item == (Object)null) { continue; } string piecePrefab = ContainerFilter.PiecePrefab(item); if (!RulesFile.AllowsCraft(piecePrefab, text)) { continue; } float num4 = RulesFile.CraftRange(piecePrefab, value2); if (!(ContainerFilter.Distance(position, ((Component)item).transform.position) > num4)) { ZNetView val2 = Refs.View(item); if ((Object)(object)val2 == (Object)null || !val2.IsOwner()) { TransferService.Withdraw(item, text, num3, inventory, value); result = false; waiting = true; } else { int num5 = TransferService.Withdraw(item, text, num3, inventory, value); num3 -= num5; } } } } return result; } public static void ScheduleCraftRetry() { _retryCraft = true; _retryCraftAt = Time.time + 0.2f; } private static void AccessToolsDoCraft(InventoryGui gui, Player player) { MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(gui, new object[1] { player }); } } } internal static class Hotkeys { public static void Tick() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value || Console.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || TextInput.IsVisible()) { return; } if (KeyUtil.Down(Plugin.Settings.DumpKey.Value)) { InventoryDump.DumpNearby(); } if (KeyUtil.Down(Plugin.Settings.HoverStoreKey.Value)) { HoverStore.TryStoreHovered(); } if (KeyUtil.Down(Plugin.Settings.PauseKey.Value)) { AutoIntake.TogglePause(); } if (KeyUtil.Down(Plugin.Settings.RenameKey.Value)) { ChestRename.TryOpen(); } if (KeyUtil.Down(Plugin.Settings.PreventPullKey.Value)) { StagingPull.PullingEnabled = !StagingPull.PullingEnabled; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { string text = (StagingPull.PullingEnabled ? Loc.T("on", "an") : Loc.T("off", "aus")); ((Character)localPlayer).Message((MessageType)2, Loc.T("Chest pulling " + text + ".", "Truhen-Ziehen " + text + "."), 0, (Sprite)null, false); } } } public static bool SearchHeld() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings != null) { return KeyUtil.Held(Plugin.Settings.SearchKey.Value); } return false; } } internal static class HoverStore { public static ItemData GetHoveredPlayerItem() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible() || (Object)(object)instance.m_playerGrid == (Object)null) { return null; } ItemData val = FromGrid(instance.m_playerGrid); if (val != null) { return val; } return FromGridByMouse(instance.m_playerGrid); } public static void TryStoreHovered() { if (Plugin.Settings == null || !Plugin.Settings.StoreEnabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (!InventoryGui.IsVisible()) { ((Character)localPlayer).Message((MessageType)2, Loc.T("Open your inventory and middle-click an item.", "Inventar öffnen und mit mittlerer Maustaste auf ein Item klicken."), 0, (Sprite)null, false); return; } ItemData hoveredPlayerItem = GetHoveredPlayerItem(); if (hoveredPlayerItem != null) { if (InventoryDump.StoreOne(hoveredPlayerItem)) { ((Character)localPlayer).Message((MessageType)1, Loc.T("Stored hovered item.", "Gehovertes Item eingelagert."), 0, (Sprite)null, false); } } else { InventoryDump.DumpNearby(); } } private static ItemData FromGrid(InventoryGrid grid) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)grid == (Object)null) { return null; } MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGrid), "GetHoveredElement", (Type[])null, (Type[])null); if (methodInfo == null) { return null; } object? obj = methodInfo.Invoke(grid, null); InventoryElement val = (InventoryElement)((obj is InventoryElement) ? obj : null); if ((Object)(object)val == (Object)null) { return null; } return grid.GetItem(val.Position); } private static ItemData FromGridByMouse(InventoryGrid grid) { //IL_003d: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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) if ((Object)(object)grid == (Object)null) { return null; } FieldInfo fieldInfo = AccessTools.Field(typeof(InventoryGrid), "m_elements"); if (fieldInfo == null) { return null; } if (!(fieldInfo.GetValue(grid) is IList list)) { return null; } Vector3 mousePosition = Input.mousePosition; foreach (object item2 in list) { InventoryElement val = (InventoryElement)((item2 is InventoryElement) ? item2 : null); if ((Object)(object)val == (Object)null) { continue; } RectTransform elementRectTransform = val.GetElementRectTransform(); if ((Object)(object)elementRectTransform == (Object)null) { continue; } Vector2 val2 = Vector2.op_Implicit(((Transform)elementRectTransform).InverseTransformPoint(mousePosition)); Rect rect = elementRectTransform.rect; if (((Rect)(ref rect)).Contains(val2)) { ItemData item = grid.GetItem(val.Position); if (item != null) { return item; } } } return null; } } internal static class KeyUtil { public static bool Down(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return false; } if (!MainDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (!Held(modifier)) { return false; } } return true; } public static bool Held(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return false; } if (!MainHeld(((KeyboardShortcut)(ref shortcut)).MainKey)) { return false; } return ModifiersHeld(shortcut); } public static bool ModifiersHeld(KeyboardShortcut shortcut) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) bool result = false; foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { result = true; if (!Held(modifier)) { return false; } } return result; } public static string Format(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return string.Empty; } List<string> list = new List<string>(); foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { list.Add(Nice(modifier)); } list.Add(Nice(((KeyboardShortcut)(ref shortcut)).MainKey)); return string.Join("+", list.ToArray()); } private unsafe static string Nice(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown switch (key - 303) { case 4: case 5: return "Alt"; case 0: case 1: return "Shift"; case 2: case 3: return "Ctrl"; default: return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(); } } private static bool MainDown(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) int num = MouseIndex(key); if (num >= 0) { return Input.GetMouseButtonDown(num); } return Input.GetKeyDown(key); } private static bool MainHeld(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) int num = MouseIndex(key); if (num >= 0) { return Input.GetMouseButton(num); } return Input.GetKey(key); } private static bool Held(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) int num = MouseIndex(key); if (num >= 0) { return Input.GetMouseButton(num); } return Input.GetKey(key); } private static int MouseIndex(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected I4, but got Unknown return (key - 323) switch { 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, _ => -1, }; } } internal static class Loc { public static string T(string en, string de) { return en; } } internal static class AdminUtil { public static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } public static bool CanEditSettings() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return true; } if (instance.IsDedicated()) { return false; } if (instance.IsServer()) { return true; } if (ConfigSync.ServerGrantedEdit) { return true; } return instance.LocalPlayerIsAdminOrHost(); } } internal static class ConfigSync { public const string RpcSyncName = "KAC_SyncConfig"; public const string RpcRequestName = "KAC_RequestConfig"; public const string HandshakeName = "KAC_Hello"; public static bool IsApplyingRemoteConfig { get; private set; } public static bool Registered { get; private set; } public static bool HasReceivedConfig { get; private set; } public static bool ServerGrantedEdit { get; private set; } public static void Register() { if (ZRoutedRpc.instance != null) { if (!Registered) { ZRoutedRpc.instance.Register<ZPackage>("KAC_SyncConfig", (Action<long, ZPackage>)RPC_ReceiveConfig); ZRoutedRpc.instance.Register("KAC_RequestConfig", (Action<long>)RPC_RequestConfig); ZRoutedRpc.instance.Register<int, int>("KAC_Hello", (Action<long, int, int>)RPC_Hello); Registered = true; Plugin.Log.LogInfo((object)"StoreAndCraft RPCs registered."); } if (AdminUtil.IsServer()) { HasReceivedConfig = true; ServerGrantedEdit = true; BroadcastConfig(); } else if (!HasReceivedConfig) { RequestConfigFromServer(); } } } public static void BroadcastConfig() { if (!AdminUtil.IsServer() || (Object)(object)ZNet.instance == (Object)null || Plugin.Settings == null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.IsReady()) { SendToPeer(peer.m_uid); } } } public static void SendToPeer(long peerId) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown if (AdminUtil.IsServer() && ZRoutedRpc.instance != null && Plugin.Settings != null) { bool flag = PeerIsAdmin(((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeer(peerId) : null); ZPackage val = new ZPackage(); val.Write(ModConfigProtocol()); val.Write(flag); Plugin.Settings.WriteToPackage(val); ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "KAC_SyncConfig", new object[1] { val }); } } public static void RequestConfigFromServer() { if (!AdminUtil.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "KAC_RequestConfig", Array.Empty<object>()); } } private static int ModConfigProtocol() { return 1; } public static bool PeerIsAdmin(ZNetPeer peer) { if (peer?.m_rpc == null || (Object)(object)ZNet.instance == (Object)null) { return false; } try { ISocket socket = peer.m_rpc.GetSocket(); if (socket == null) { return false; } string hostName = socket.GetHostName(); return !string.IsNullOrEmpty(hostName) && ZNet.instance.IsAdmin(hostName); } catch { return false; } } private static void RPC_ReceiveConfig(long sender, ZPackage pkg) { if (AdminUtil.IsServer() || pkg == null || Plugin.Settings == null) { return; } int num = pkg.ReadInt(); if (num != 1) { Plugin.Log.LogWarning((object)("StoreAndCraft config protocol mismatch: " + num)); return; } ServerGrantedEdit = pkg.ReadBool(); IsApplyingRemoteConfig = true; try { Plugin.Settings.ReadFromPackage(pkg); HasReceivedConfig = true; } finally { IsApplyingRemoteConfig = false; } } private static void RPC_RequestConfig(long sender) { if (AdminUtil.IsServer()) { SendToPeer(sender); } } private static void RPC_Hello(long sender, int protocol, int major) { if (AdminUtil.IsServer()) { VersionGate.Accept(sender, protocol, major); SendToPeer(sender); } } } internal class ConfigSyncRetry : MonoBehaviour { private float _nextRequest; private int _attempts; private void Update() { if (AdminUtil.IsServer() || ConfigSync.HasReceivedConfig) { ((Behaviour)this).enabled = false; } else if (ConfigSync.Registered && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && !(Time.time < _nextRequest)) { _attempts++; _nextRequest = Time.time + 2f; ConfigSync.RequestConfigFromServer(); VersionGate.SendHello(); if (_attempts == 1 || _attempts % 5 == 0) { Plugin.Log.LogInfo((object)("StoreAndCraft: waiting for server config (attempt " + _attempts + ")...")); } } } } internal static class ConfigWatch { private static FileSystemWatcher _watcher; private static float _reloadAt; private static bool _pending; public static void Start() { if (_watcher != null) { return; } try { _watcher = new FileSystemWatcher(Paths.ConfigPath); _watcher.Filter = "StoreAndCraft.*"; _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; _watcher.Changed += OnChanged; _watcher.Created += OnChanged; _watcher.EnableRaisingEvents = true; Plugin.Log.LogInfo((object)("StoreAndCraft config watcher on " + Paths.ConfigPath)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("StoreAndCraft config watcher failed: " + ex.Message)); } } public static void Tick() { if (_pending && !(Time.unscaledTime < _reloadAt)) { _pending = false; Reload(); } } private static void OnChanged(object sender, FileSystemEventArgs e) { _pending = true; _reloadAt = Time.unscaledTime + 0.4f; } private static void Reload() { try { if ((Object)(object)Plugin.Instance != (Object)null) { ((BaseUnityPlugin)Plugin.Instance).Config.Reload(); } RulesFile.LoadOrCreate(); if (AdminUtil.IsServer()) { ConfigSync.BroadcastConfig(); } Plugin.Log.LogInfo((object)"StoreAndCraft config/rules reloaded from disk."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("StoreAndCraft reload failed: " + ex.Message)); } } } internal static class TransferService { private struct PendingMove { public Container Chest; public ItemDrop Drop; public ItemData Item; public Inventory From; public int Amount; public float Deadline; public bool Withdraw; public string SharedName; public bool LeaveOne; } public const string RpcRemove = "KAC_Remove"; public const string RpcDeposit = "KAC_Deposit"; public const string RpcGrant = "KAC_Grant"; public const string RpcStoreDrop = "KAC_StoreDrop"; private static readonly Queue<PendingMove> Queue = new Queue<PendingMove>(); private static readonly Dictionary<int, float> ClaimThrottle = new Dictionary<int, float>(); private static bool _grantRegistered; public static void RegisterGrant() { if (!_grantRegistered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<ZPackage>("KAC_Grant", (Action<long, ZPackage>)RPC_Grant); _grantRegistered = true; } } public static void Tick() { int num = ((Plugin.Settings != null) ? Plugin.Settings.MaxTransfersPerTick.Value : 8); int num2 = 0; int count = Queue.Count; while (Queue.Count > 0 && num2 < num && count-- > 0) { PendingMove pendingMove = Queue.Dequeue(); if (!(Time.time > pendingMove.Deadline) && !((Object)(object)pendingMove.Chest == (Object)null) && ContainerFilter.IsUsable(pendingMove.Chest)) { if (!TryOwner(pendingMove.Chest, claim: true)) { Queue.Enqueue(pendingMove); break; } Execute(pendingMove); num2++; } } } public static bool StoreItem(Container chest, Inventory from, ItemData item, int amount) { if ((Object)(object)chest == (Object)null || from == null || item == null || amount <= 0) { return false; } if (TryOwner(chest, claim: true)) { return DepositLocal(chest, from, item, amount); } InvokeDeposit(chest, item, amount); Enqueue(new PendingMove { Chest = chest, From = from, Item = item, Amount = amount, Deadline = Time.time + 2f }); return true; } public static bool StoreDrop(Container chest, ItemDrop drop) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || (Object)(object)drop == (Object)null || drop.m_itemData == null) { return false; } ZNetView val = Refs.View(drop); if (TryOwner(chest, claim: true) && (Object)(object)val != (Object)null && val.IsOwner()) { return StoreDropLocal(chest, drop); } if ((Object)(object)val != (Object)null && val.IsValid()) { drop.RequestOwn(); } ZNetView val2 = Refs.View(chest); if ((Object)(object)val2 != (Object)null && val2.IsValid() && (Object)(object)val != (Object)null && val.GetZDO() != null) { val2.InvokeRPC("KAC_StoreDrop", new object[1] { val.GetZDO().m_uid }); } Enqueue(new PendingMove { Chest = chest, Drop = drop, Amount = drop.m_itemData.m_stack, Deadline = Time.time + 2.5f }); return true; } public static int Withdraw(Container chest, string sharedName, int amount, Inventory playerInv, bool leaveOne) { if ((Object)(object)chest == (Object)null || playerInv == null || amount <= 0 || string.IsNullOrEmpty(sharedName)) { return 0; } if (TryOwner(chest, claim: true)) { return WithdrawLocal(chest, sharedName, amount, playerInv, leaveOne); } ZNetView val = Refs.View(chest); if ((Object)(object)val != (Object)null && val.IsValid()) { val.InvokeRPC("KAC_Remove", new object[3] { sharedName, amount, leaveOne ? 1 : 0 }); } Enqueue(new PendingMove { Chest = chest, From = playerInv, SharedName = sharedName, Amount = amount, LeaveOne = leaveOne, Withdraw = true, Deadline = Time.time + 2f }); return 0; } public static void RegisterOn(Container container) { ZNetView nv = Refs.View(container); if ((Object)(object)nv == (Object)null || !nv.IsValid()) { return; } TryRegister(nv, "KAC_Remove", delegate { nv.Register<string, int, int>("KAC_Remove", (Action<long, string, int, int>)delegate(long sender, string name, int amount, int leaveOne) { OnRemove(container, sender, name, amount, leaveOne != 0); }); }); TryRegister(nv, "KAC_Deposit", delegate { nv.Register<ZPackage>("KAC_Deposit", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnDeposit(container, sender, pkg); }); }); TryRegister(nv, "KAC_StoreDrop", delegate { nv.Register<ZDOID>("KAC_StoreDrop", (Action<long, ZDOID>)delegate(long sender, ZDOID dropId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) OnStoreDrop(container, sender, dropId); }); }); } private static void TryRegister(ZNetView nv, string name, Action register) { try { register(); } catch (Exception ex) { Plugin.Log.LogDebug((object)("RPC register " + name + ": " + ex.Message)); } } internal static void OnRemove(Container container, long sender, string sharedName, int amount, bool leaveOne) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown ZNetView val = Refs.View(container); if ((Object)(object)container == (Object)null || (Object)(object)val == (Object)null || !val.IsOwner() || amount <= 0 || !ValidateRpc(container, sender, (Plugin.Settings != null) ? Plugin.Settings.CraftRange.Value : 20f) || !RulesFile.AllowsCraft(ContainerFilter.PiecePrefab(container), sharedName)) { return; } Inventory inventory = container.GetInventory(); if (inventory != null) { int num = inventory.CountItems(sharedName, -1, true); if (leaveOne && num > 0) { num--; } int num2 = Mathf.Min(amount, num); if (num2 > 0) { inventory.RemoveItem(sharedName, num2, -1, true); ZPackage val2 = new ZPackage(); val2.Write(sharedName); val2.Write(num2); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "KAC_Grant", new object[1] { val2 }); } } } internal static void OnDeposit(Container container, long sender, ZPackage pkg) { ZNetView val = Refs.View(container); if ((Object)(object)container == (Object)null || (Object)(object)val == (Object)null || !val.IsOwner() || pkg == null || !ValidateRpc(container, sender, MaxStoreRange())) { return; } string text = pkg.ReadString(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); int num3 = pkg.ReadInt(); long num4 = pkg.ReadLong(); string text2 = pkg.ReadString(); if (num <= 0) { return; } Inventory inventory = container.GetInventory(); if (inventory == null) { return; } ItemData val2 = inventory.AddItem(text, num, num2, num3, num4, text2, false, false); if (val2 != null) { if (!RulesFile.AllowsStore(ContainerFilter.PiecePrefab(container), val2)) { inventory.RemoveItem(val2, val2.m_stack); } else { Highlight(container); } } } internal static void OnStoreDrop(Container container, long sender, ZDOID dropId) { //IL_0043: 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) //IL_0092: Unknown result type (might be due to invalid IL or missing references) ZNetView val = Refs.View(container); if ((Object)(object)container == (Object)null || (Object)(object)val == (Object)null || !val.IsOwner() || (Object)(object)ZNetScene.instance == (Object)null || !ValidateRpc(container, sender, MaxStoreRange())) { return; } GameObject val2 = ZNetScene.instance.FindInstance(dropId); if ((Object)(object)val2 == (Object)null) { return; } ItemDrop component = val2.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && RulesFile.AllowsStore(ContainerFilter.PiecePrefab(container), component.m_itemData) && !(Vector3.Distance(((Component)component).transform.position, ((Component)container).transform.position) > MaxStoreRange() + 6f)) { ZNetView val3 = Refs.View(component); if ((Object)(object)val3 != (Object)null && !val3.IsOwner()) { component.RequestOwn(); } else { StoreDropLocal(container, component); } } } private static void RPC_Grant(long sender, ZPackage pkg) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && pkg != null) { string text = pkg.ReadString(); int num = pkg.ReadInt(); GameObject val = ItemIds.PrefabFromToken(text); if ((Object)(object)val == (Object)null) { val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null); } if (!((Object)(object)val == (Object)null)) { ((Humanoid)localPlayer).GetInventory().AddItem(val, num); } } } private static bool Execute(PendingMove op) { if ((Object)(object)op.Drop != (Object)null) { ZNetView val = Refs.View(op.Drop); if ((Object)(object)val != (Object)null && !val.IsOwner()) { op.Drop.RequestOwn(); Queue.Enqueue(op); return false; } return StoreDropLocal(op.Chest, op.Drop); } if (op.Withdraw) { return WithdrawLocal(op.Chest, op.SharedName, op.Amount, op.From, op.LeaveOne) > 0; } return DepositLocal(op.Chest, op.From, op.Item, op.Amount); } private static bool DepositLocal(Container chest, Inventory from, ItemData item, int amount) { Inventory inventory = chest.GetInventory(); if (inventory == null || item == null) { return false; } int num = Mathf.Min(amount, item.m_stack); if (num <= 0 || !inventory.CanAddItem(item, num)) { return false; } ItemData val = item.Clone(); val.m_stack = num; if (!inventory.AddItem(val)) { return false; } from.RemoveItem(item, num); Highlight(chest); return true; } private static bool StoreDropLocal(Container chest, ItemDrop drop) { if ((Object)(object)drop == (Object)null || drop.m_itemData == null) { return false; } Inventory inventory = chest.GetInventory(); if (inventory == null || !inventory.CanAddItem(drop.m_itemData, drop.m_itemData.m_stack)) { return false; } ItemData val = drop.m_itemData.Clone(); if (!inventory.AddItem(val)) { return false; } ZNetView val2 = Refs.View(drop); if ((Object)(object)val2 != (Object)null && val2.IsValid() && (Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(((Component)drop).gameObject); } else { Object.Destroy((Object)(object)((Component)drop).gameObject); } Highlight(chest); return true; } private static int WithdrawLocal(Container chest, string sharedName, int amount, Inventory playerInv, bool leaveOne) { Inventory inventory = chest.GetInventory(); if (inventory == null || playerInv == null) { return 0; } int num = inventory.CountItems(sharedName, -1, true); if (leaveOne && num > 0) { num--; } int num2 = Mathf.Min(amount, num); if (num2 <= 0) { return 0; } GameObject val = ItemIds.PrefabFromToken(sharedName); if ((Object)(object)val == (Object)null && (Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(sharedName); } if ((Object)(object)val == (Object)null) { return 0; } if (!playerInv.CanAddItem(val, num2)) { while (num2 > 0 && !playerInv.CanAddItem(val, num2)) { num2--; } if (num2 <= 0) { return 0; } } if (!playerInv.AddItem(val, num2)) { return 0; } inventory.RemoveItem(sharedName, num2, -1, true); return num2; } private static float MaxStoreRange() { if (Plugin.Settings == null) { return 12f; } return RulesFile.MaxScanRange(Plugin.Settings.StoreRange.Value, Plugin.Settings.CraftRange.Value, Plugin.Settings.PlayerDumpRange.Value); } private static bool ValidateRpc(Container container, long sender, float range) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!ContainerFilter.IsUsable(container)) { return false; } if (!PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true)) { return false; } if (!RequirementBridge.SenderInRange(sender, ((Component)container).transform.position, range)) { return false; } return true; } private static bool TryOwner(Container chest, bool claim) { ZNetView val = Refs.View(chest); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } if (val.IsOwner()) { return true; } if (!claim) { return false; } int instanceID = ((Object)chest).GetInstanceID(); if (ClaimThrottle.TryGetValue(instanceID, out var value) && Time.time < value) { return false; } val.ClaimOwnership(); ClaimThrottle[instanceID] = Time.time + 0.35f; return val.IsOwner(); } private static void InvokeDeposit(Container chest, ItemData item, int amount) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ZNetView val = Refs.View(chest); if (!((Object)(object)val == (Object)null) && val.IsValid() && item != null) { ZPackage val2 = new ZPackage(); val2.Write(ItemIds.PrefabName(item) ?? ItemIds.SharedName(item) ?? ""); val2.Write(amount); val2.Write(item.m_quality); val2.Write(item.m_variant); val2.Write(item.m_crafterID); val2.Write(item.m_crafterName ?? ""); val.InvokeRPC("KAC_Deposit", new object[1] { val2 }); } } private static void Enqueue(PendingMove op) { if (Queue.Count <= 80) { Queue.Enqueue(op); } } public static void Highlight(Container chest) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null || Plugin.Settings == null) { return; } if (Plugin.Settings.HighlightOnStore.Value) { WearNTear component = ((Component)chest).GetComponent<WearNTear>(); if ((Object)(object)component != (Object)null) { component.Highlight(); } } if (Plugin.Settings.PingOnStore.Value && (Object)(object)Chat.instance != (Object)null) { Chat.instance.SendPing(((Component)chest).transform.position); } } } internal class VersionGate : MonoBehaviour { public const int Major = 1; public const float GraceSeconds = 12f; private static readonly Dictionary<long, float> Deadline = new Dictionary<long, float>(); private static readonly HashSet<long> Verified = new HashSet<long>(); public static void OnPeerReady(long peerId) { if (AdminUtil.IsServer() && peerId != 0L && !Verified.Contains(peerId) && !Deadline.ContainsKey(peerId)) { Deadline[peerId] = Time.time + 12f; } } public static void Accept(long peerId, int protocol, int major) { if (AdminUtil.IsServer()) { if (protocol != 1 || major != 1) { Plugin.Log.LogWarning((object)("StoreAndCraft: rejecting peer " + peerId + " (protocol " + protocol + "/" + 1 + ", major " + major + "/" + 1 + ").")); Kick(peerId); } else { Verified.Add(peerId); Deadline.Remove(peerId); } } } public static void SendHello() { if (!AdminUtil.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "KAC_Hello", new object[2] { 1, 1 }); } } private void Update() { if (!AdminUtil.IsServer() || (Object)(object)ZNet.instance == (Object)null || Deadline.Count == 0) { return; } List<long> list = new List<long>(); foreach (KeyValuePair<long, float> item in Deadline) { if (!Verified.Contains(item.Key) && Time.time >= item.Value) { list.Add(item.Key); } } foreach (long item2 in list) { Deadline.Remove(item2); Plugin.Log.LogWarning((object)("StoreAndCraft: kicking peer " + item2 + " (missing StoreAndCraft or handshake timeout).")); Kick(item2); } } private static void Kick(long peerId) { if (!((Object)(object)ZNet.instance == (Object)null)) { ZNetPeer peer = ZNet.instance.GetPeer(peerId); if (peer != null) { ZNet.instance.Disconnect(peer); } } } } [HarmonyPatch(typeof(Container), "Interact")] internal static class ContainerInteractRenamePatch { private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result) { if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer) { return true; } if (!ChestRename.WantsRename(alt)) { return true; } if (!ChestRename.TryOpen(__instance, warnIfMissing: false)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Container), "GetHoverName")] internal static class ContainerHoverNamePatch { private static void Postfix(Container __instance, ref string __result) { string text = ChestNames.Get(__instance); if (!string.IsNullOrEmpty(text)) { __result = text; } } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class ContainerHoverTextPatch { private static void Postfix(Container __instance, ref string __result) { if (string.IsNullOrEmpty(__result)) { return; } string text = ChestNames.Get(__instance); if (!string.IsNullOrEmpty(text)) { string text2 = Refs.VanillaHoverName(__instance); if (!string.IsNullOrEmpty(text2) && Localization.instance != null) { text2 = Localization.instance.Localize(text2); } if (!string.IsNullOrEmpty(text2) && __result.StartsWith(text2)) { __result = text + __result.Substring(text2.Length); } else if (__result.IndexOf(text, StringComparison.Ordinal) < 0) { __result = text + "\n" + __result; } } __result = __result + "\n[<color=yellow><b>" + ChestRename.PromptLabel() + "</b></color>] Rename"; } } [HarmonyPatch(typeof(Inventory))] internal static class InventoryCountPatches { internal static int Skip; [HarmonyPostfix] [HarmonyPatch("CountItems")] private static void CountItemsPostfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (Skip > 0 || !StagingPull.Active) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Humanoid)localPlayer).GetInventory() != __instance || string.IsNullOrEmpty(name)) { return; } Skip++; try { __result += RequirementBridge.CountNearby(localPlayer, name); } finally { Skip--; } } [HarmonyPostfix] [HarmonyPatch("HaveItem", new Type[] { typeof(string), typeof(bool) })] private static void HaveItemPostfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (__result || Skip > 0 || !StagingPull.Active) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Humanoid)localPlayer).GetInventory() != __instance) { return; } Skip++; try { __result = RequirementBridge.CountNearby(localPlayer, name) > 0; } finally { Skip--; } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { TransferService.RegisterOn(__instance); if ((Object)(object)((Component)__instance).GetComponent<ChestRenameReceiver>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ChestRenameReceiver>(); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class GameStartPatch { private static void Postfix(Game __instance) { ConfigSync.Register(); TransferService.RegisterGrant(); VersionGate.SendHello(); if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<ConfigSyncRetry>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ConfigSyncRetry>(); } if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<VersionGate>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<VersionGate>(); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class PeerInfoPatch { private static void Postfix(ZRpc rpc) { if (!AdminUtil.IsServer() || rpc == null || (Object)(object)ZNet.instance == (Object)null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_rpc == rpc) { VersionGate.OnPeerReady(peer.m_uid); ConfigSync.SendToPeer(peer.m_uid); break; } } } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class PlayerSpawnedPatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsOwner() && !AdminUtil.IsServer()) { VersionGate.SendHello(); if (!ConfigSync.HasReceivedConfig) { ConfigSync.RequestConfigFromServer(); } } } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class TerminalInitPatch { private static bool _added; private static void Postfix() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!_added) { _added = true; new ConsoleCommand("storesearch", "Find an item in nearby containers", new ConsoleEvent(SearchPing.OnCommand), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } } [HarmonyPatch] internal static class PlayerCraftPatches { [HarmonyPrefix] [HarmonyPatch(typeof(Player), "ConsumeResources")] private static void ConsumeResourcesPrefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier) { if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsOwner() && StagingPull.Active) { StagingPull.PullRequirements(__instance, requirements, qualityLevel, itemQuality, multiplier, out var _); } } [HarmonyPrefix] [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] private static bool DoCraftingPrefix(InventoryGui __instance, Player player) { Recipe val = Refs.CraftRecipe(__instance); if (!StagingPull.Active || (Object)(object)player == (Object)null || (Object)(object)__instance == (Object)null || (Object)(object)val == (Object)null) { return true; } int amount = ((!Refs.MultiCrafting(__instance)) ? 1 : Mathf.Max(1, __instance.m_multiCraftAmount)); StagingPull.EnsureRecipe(player, val, amount, out var waiting); if (waiting) { StagingPull.ScheduleCraftRetry(); return false; } return true; } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static bool TryPlacePiecePrefix(Player __instance, Piece piece, ref bool __result) { if (!StagingPull.Active || (Object)(object)__instance == (Object)null || (Object)(object)piece == (Object)null) { return true; } StagingPull.EnsurePiece(__instance, piece, out var waiting); if (waiting) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] internal static class CraftUiHintPatch { private static readonly Color Flash = new Color(1f, 0.92f, 0.2f, 1f); private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (!__result || !StagingPull.Active || (Object)(object)player == (Object)null || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)elementRoot == (Object)null) { return; } string text = ((req.m_resItem.m_itemData != null) ? req.m_resItem.m_itemData.m_shared.m_name : null); if (string.IsNullOrEmpty(text)) { return; } InventoryCountPatches.Skip++; try { ((Humanoid)player).GetInventory().CountItems(text, -1, true); } finally { InventoryCountPatches.Skip--; } if (RequirementBridge.CountNearby(player, text) <= 0) { return; } TMP_Text[] componentsInChildren = ((Component)elementRoot).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { ((Graphic)val).color = Color.Lerp(((Graphic)val).color, Flash, 0.65f); } } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] internal static class SearchClickPatch { private static bool Prefix(ItemData item) { if (item == null || !Hotkeys.SearchHeld()) { return true; } SearchPing.PingItem(item); return false; } } [BepInPlugin("com.morda.storeandcraft", "StoreAndCraft", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.morda.storeandcraft"; public const string ModName = "StoreAndCraft"; public const string ModVersion = "1.0.0"; public const string ModAuthor = "Morda"; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ModConfig Settings { get; private set; } private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); RulesFile.LoadOrCreate(); ConfigWatch.Start(); _harmony = new Harmony("com.morda.storeandcraft"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"StoreAndCraft v1.0.0 by Morda loaded."); } private void Update() { ConfigWatch.Tick(); TransferService.Tick(); if (!((Object)(object)Player.m_localPlayer == (Object)null)) { NearbyIndex.Tick(); AutoIntake.Tick(); StagingPull.Tick(); } } private void LateUpdate() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { Hotkeys.Tick(); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } internal static class Refs { private static readonly FieldInfo ItemDropNview = AccessTools.Field(typeof(ItemDrop), "m_nview"); private static readonly FieldInfo ItemDropInstances = AccessTools.Field(typeof(ItemDrop), "s_instances"); private static readonly FieldInfo ContainerNview = AccessTools.Field(typeof(Container), "m_nview"); private static readonly FieldInfo ContainerName = AccessTools.Field(typeof(Container), "m_name"); private static readonly FieldInfo GuiRecipe = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe"); private static readonly FieldInfo GuiMulti = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting"); public static ZNetView View(Container container) { if ((Object)(object)container == (Object)null) { return null; } if (ContainerNview != null) { object? value = ContainerNview.GetValue(container); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } return ((Component)container).GetComponent<ZNetView>(); } public static string VanillaHoverName(Container container) { if ((Object)(object)container == (Object)null || ContainerName == null) { return null; } return ContainerName.GetValue(container) as string; } public static ZNetView View(ItemDrop drop) { if ((Object)(object)drop == (Object)null) { return null; } if (ItemDropNview != null) { object? value = ItemDropNview.GetValue(drop); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } return ((Component)drop).GetComponent<ZNetView>(); } public static List<ItemDrop> Drops() { if (ItemDropInstances == null) { return null; } return ItemDropInstances.GetValue(null) as List<ItemDrop>; } public static Recipe CraftRecipe(InventoryGui gui) { if (!((Object)(object)gui != (Object)null) || !(GuiRecipe != null)) { return null; } object? value = GuiRecipe.GetValue(gui); return (Recipe)((value is Recipe) ? value : null); } public static bool MultiCrafting(InventoryGui gui) { if ((Object)(object)gui == (Object)null || GuiMulti == null) { return false; } object value = GuiMulti.GetValue(gui); if (value is bool) { return (bool)value; } return false; } } internal static class AutoIntake { private static float _next; public static float PauseUntil { get; set; } public static bool Paused => Time.time < PauseUntil; public static void TogglePause() { if (Plugin.Settings != null) { if (Paused) { PauseUntil = 0f; Tell(Loc.T("Auto-store resumed.", "Auto-Einlagern wieder an.")); } else { PauseUntil = Time.time + Plugin.Settings.PauseSeconds.Value; Tell(Loc.T("Auto-store paused for " + Plugin.Settings.PauseSeconds.Value + "s.", "Auto-Einlagern pausiert für " + Plugin.Settings.PauseSeconds.Value + "s.")); } } } public static void Tick() { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value || !Plugin.Settings.StoreEnabled.Value || Paused) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || Time.time < _next) { return; } _next = Time.time + Mathf.Max(1f, Plugin.Settings.IntakeInterval.Value); List<ItemDrop> list = Refs.Drops(); if (list == null) { return; } int num = 0; int value = Plugin.Settings.MaxTransfersPerTick.Value; Vector3 position = ((Component)localPlayer).transform.position; foreach (ItemDrop item in list) { if (num >= value) { break; } if ((Object)(object)item == (Object)null || item.m_itemData == null) { continue; } ZNetView val = Refs.View(item); if ((Object)(object)val == (Object)null || !val.IsValid() || item.IsPiece() || !item.CanPickup(true)) { continue; } float value2 = Plugin.Settings.StoreRange.Value; Container val2 = null; float num2 = float.MaxValue; foreach (Container item2 in NearbyIndex.Current) { if (!((Object)(object)item2 == (Object)null)) { float num3 = RulesFile.StoreRange(ContainerFilter.PiecePrefab(item2), value2); float num4 = ContainerFilter.Distance(((Component)item).transform.position, ((Component)item2).transform.position); if (!(num4 > num3) && !(ContainerFilter.Distance(position, ((Component)item2).transform.position) > NearbyIndex.ScanRange()) && ChestPicker.CanAccept(item2, item.m_itemData, ((Component)item).transform.position, Plugin.Settings.MustHaveExisting.Value) && num4 < num2) { num2 = num4; val2 = item2; } } } if (!((Object)(object)val2 == (Object)null) && TransferService.StoreDrop(val2, item)) { num++; } } } private static void Tell(string msg) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, msg, 0, (Sprite)null, false); } } } internal static class ChestNames { public const string ZdoKey = "kac_label"; public const int MaxLength = 32; public static bool CanRename(Container container) { ZNetView val = Refs.View(container); if ((Object)(object)val != (Object)null && val.IsValid()) { return val.GetZDO() != null; } return false; } public static string Get(Container container) { ZNetView val = Refs.View(container); if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null) { return null; } string text = val.GetZDO().GetString("kac_label", string.Empty); if (!string.IsNullOrEmpty(text)) { return text; } return null; } public static bool Set(Container container, string name) { ZNetView val = Refs.View(container); if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null) { return false; } if (!val.IsOwner()) { val.ClaimOwnership(); } val.GetZDO().Set("kac_label", Sanitize(name)); return true; } public static string Sanitize(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } name = name.Trim(); if (name.Length > 32) { name = name.Substring(0, 32); } return name; } public static Container Hovered() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return null; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); Container val = (((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent<Container>() : null); if ((Object)(object)val != (Object)null) { return val; } Piece hoveringPiece = localPlayer.GetHoveringPiece(); if (!((Object)(object)hoveringPiece != (Object)null)) { return null; } return ((Component)hoveringPiece).GetComponent<Container>(); } } internal class ChestRenameReceiver : MonoBehaviour, TextReceiver { public string GetText() { return ChestNames.Get(((Component)this).GetComponent<Container>()) ?? string.Empty; } public void SetText(string text) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) Container component = ((Component)this).GetComponent<Container>(); if (!((Object)(object)component == (Object)null) && PrivateArea.CheckAccess(((Component)component).transform.position, 0f, false, true)) { ChestNames.Set(component, text); } } } internal static class ChestPicker { public static Container FindStoreTarget(Vector3 origin, ItemData item, bool mustExist) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) if (item == null) { return null; } float fallback = ((Plugin.Settings != null) ? Plugin.Settings.PlayerDumpRange.Value : 8f); Container result = null; float num = float.MaxValue; foreach (Container item2 in NearbyIndex.Current) { if (!((Object)(object)item2 == (Object)null)) { float num2 = RulesFile.DumpRange(ContainerFilter.PiecePrefab(item2), fallback); float num3 = ContainerFilter.Distance(origin, ((Component)item2).transform.position); if (!(num3 > num2) && CanAccept(item2, item, origin, mustExist) && num3 < num) { num = num3; result = item2; } } } return result; } public static bool CanAccept(Container chest, ItemData item, Vector3 from, bool mustExist) { if ((Object)(object)chest == (Object)null || item == null) { return false; } Inventory inventory = chest.GetInventory(); if (inventory == null || !inventory.CanAddItem(item, item.m_stack)) { return false; } if (!RulesFile.AllowsStore(ContainerFilter.PiecePrefab(chest), item)) { return false; } if (mustExist) { string text = ItemIds.SharedName(item); if (string.IsNullOrEmpty(text) || inventory.CountItems(text, -1, true) <= 0) { return false; } } return true; } public static List<Container> FindHolding(Vector3 origin, float range, string sharedName) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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) List<Container> list = new List<Container>(); if (string.IsNullOrEmpty(sharedName)) { return list; } foreach (Container item in NearbyIndex.Within(origin, range)) { Inventory inventory = item.GetInventory(); if (inventory != null && inventory.CountItems(sharedName, -1, true) > 0) { list.Add(item); } } list.Sort((Container a, Container b) => ContainerFilter.Distance(origin, ((Component)a).transform.position).CompareTo(ContainerFilter.Distance(origin, ((Component)b).transform.position))); return list; } } internal static class ChestRename { public static bool WantsRename(bool alt) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value) { return false; } if (alt) { return true; } KeyboardShortcut value = Plugin.Settings.RenameKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 0) { return false; } if (!KeyUtil.ModifiersHeld(value)) { return false; } if ((int)((KeyboardShortcut)(ref value)).MainKey != 101) { return KeyUtil.Held(value); } return true; } public static bool TryOpen(Container container = null, bool warnIfMissing = true) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } if ((Object)(object)container == (Object)null) { container = ChestNames.Hovered(); } if ((Object)(object)container == (Object)null || !ChestNames.CanRename(container)) { if (warnIfMissing) { ((Character)localPlayer).Message((MessageType)2, "Look at a chest, then press the rename key.", 0, (Sprite)null, false); } return false; } if (!PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true)) { ((Character)localPlayer).Message((MessageType)2, "No access to this chest.", 0, (Sprite)null, false); return false; } ChestRenameReceiver chestRenameReceiver = ((Component)container).GetComponent<ChestRenameReceiver>(); if ((Object)(object)chestRenameReceiver == (Object)null) { chestRenameReceiver = ((Component)container).gameObject.AddComponent<ChestRenameReceiver>(); } if ((Object)(object)TextInput.instance == (Object)null) { return false; } TextInput.instance.RequestText((TextReceiver)(object)chestRenameReceiver, "Rename chest", 32); return true; } public static string PromptLabel() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings == null) { return "Alt+E"; } string text = KeyUtil.Format(Plugin.Settings.RenameKey.Value); if (!string.IsNullOrEmpty(text)) { return text; } return "Shift+E"; } } internal static class InventoryDump { public static void DumpNearby() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value || !Plugin.Settings.StoreEnabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return; } NearbyIndex.Rescan(((Component)localPlayer).transform.position, NearbyIndex.ScanRange()); List<ItemData> list = new List<ItemData>(inventory.GetAllItems()); int num = 0; foreach (ItemData item in list) { if (ShouldDump(item, inventory)) { Container val = ChestPicker.FindStoreTarget(((Component)localPlayer).transform.position, item, Plugin.Settings.MustHaveExisting.Value); if (!((Object)(object)val == (Object)null) && TransferService.StoreItem(val, inventory, item, item.m_stack)) { num++; } } } ((Character)localPlayer).Message((MessageType)1, (num > 0) ? Loc.T("Stored " + num + " stacks.", num + " Stapel eingelagert.") : Loc.T("Nothing to store.", "Nichts einzulagern."), 0, (Sprite)null, false); } public static bool StoreOne(ItemData item) { //IL_002d: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || item == null || Plugin.Settings == null) { return false; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return false; } NearbyIndex.Rescan(((Component)localPlayer).transform.position, NearbyIndex.ScanRange()); Container val = ChestPicker.FindStoreTarget(((Component)localPlayer).transform.position, item, Plugin.Settings.MustHaveExisting.Value); if ((Object)(object)val == (Object)null) { ((Character)localPlayer).Message((MessageType)2, Loc.T("No matching chest.", "Keine passende Truhe."), 0, (Sprite)null, false); return false; } return TransferService.StoreItem(val, inventory, item, item.m_stack); } public static bool ShouldDump(ItemData item, Inventory inv) { if (item == null || item.m_stack <= 0) { return false; } if (item.m_equipped) { return false; } if (Plugin.Settings.IgnoreHotbar.Value && item.m_gridPos.y == 0) { return false; } return true; } } internal static class SearchPing { public static void PingItem(ItemData item) { if (item != null) { Search(ItemIds.SharedName(item), ItemIds.PrefabName(item)); } } public static void OnCommand(ConsoleEventArgs args) { string text = ((args != null && args.Length > 1) ? args[1] : null); if (string.IsNullOrEmpty(text)) { ItemData hoveredPlayerItem = HoverStore.GetHoveredPlayerItem(); if (hoveredPlayerItem != null) { PingItem(hoveredPlayerItem); } else { Tell(Loc.T("Usage: storesearch <item>", "Nutzung: storesearch <item>")); } } else { Search(text, text); } } public static void Search(string sharedOrToken, string prefabHint) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } string text = ItemIds.SharedFromToken(sharedOrToken); NearbyIndex.Rescan(((Component)localPlayer).transform.position, NearbyIndex.ScanRange()); List<Container> list = ChestPicker.FindHolding(((Component)localPlayer).transform.position, NearbyIndex.ScanRange(), text); if (list.Count == 0 && !string.IsNullOrEmpty(prefabHint)) { string text2 = ItemIds.SharedFromToken(prefabHint); if (text2 != text) { list = ChestPicker.FindHolding(((Component)localPlayer).transform.position, NearbyIndex.ScanRange(), text2); } } if (list.Count == 0) { Tell(Loc.T("No nearby chest has that item.", "Keine nahe Truhe hat dieses Item.")); return; } Container val = list[0]; int num = 0; foreach (Container item in list) { Inventory inventory = item.GetInventory(); if (inventory != null) { num += inventory.CountItems(text, -1, true); } } TransferService.Highlight(val); if ((Object)(object)Chat.instance != (Object)null) { Chat.instance.SendPing(((Component)val).transform.position); } string text3 = prefabHint; if (Localization.instance != null && !string.IsNullOrEmpty(text)) { text3 = Localization.instance.Localize(text); } Tell(Loc.T(text3 + ": " + num + " in " + list.Count + " chest(s).", text3 + ": " + num + " in " + list.Count + " Truhe(n).")); } private static void Tell(string msg) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, msg, 0, (Sprite)null, false); } } } internal static class ContainerFilter { public static bool IsUsable(Container container) { if ((Object)(object)container == (Object)null) { return false; } ZNetView val = Refs.View(container); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } if (container.GetInventory() == null) { return false; } if ((Object)(object)((Component)container).GetComponent<Piece>() == (Object)null) { return false; } return true; } public static string PiecePrefab(Container container) { Piece val = (((Object)(object)container != (Object)null) ? ((Component)container).GetComponent<Piece>() : null); if ((Object)(object)val == (Object)null) { return null; } return ItemIds.StripClone(((Object)((Component)val).gameObject).name); } public static bool PlayerMayUse(Container container, Vector3 from) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!IsUsable(container)) { return false; } if (!PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true)) { return false; } return true; } public static float Distance(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Vector3.Distance(a, b); } } internal static class ItemIds { public static string PrefabName(ItemData item) { if (item == null) { return null; } if ((Object)(object)item.m_dropPrefab != (Object)null) { return StripClone(((Object)item.m_dropPrefab).name); } if (item.m_shared == null || string.IsNullOrEmpty(item.m_shared.m_name) || (Object)(object)ObjectDB.instance == (Object)null) { return SharedName(item); } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.m_shared); if ((Object)(object)itemPrefab != (Object)null) { item.m_dropPrefab = itemPrefab; return StripClone(((Object)itemPrefab).name); } return SharedName(item); } public static string SharedName(ItemData item) { if (item?.m_shared == null) { return null; } return item.m_shared.m_name; } public static string StripClone(string name) { if (string.IsNullOrEmpty(name)) { return name; } return name.Replace("(Clone)", string.Empty).Trim(); } public static bool Matches(ItemData item, string token) { if (item == null || string.IsNullOrEmpty(token)) { return false; } if (string.Equals(PrefabName(item), token, StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(SharedName(item), token, StringComparison.OrdinalIgnoreCase)) { return true; } if (Localization.instance != null && item.m_shared != null && string.Equals(Localization.instance.Localize(item.m_shared.m_name), token, StringComparison.OrdinalIgnoreCase)) { return true; } return false; } public static GameObject PrefabFromToken(string token) { if (string.IsNullOrEmpty(token) || (Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(token); if ((Object)(object)itemPrefab != (Object)null) { return itemPrefab; } foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if (component?.m_itemData?.m_shared != null) { if (string.Equals(component.m_itemData.m_shared.m_name, token, StringComparison.OrdinalIgnoreCase)) { return item; } if (Localization.instance != null && string.Equals(Localization.instance.Localize(component.m_itemData.m_shared.m_name), token, StringComparison.OrdinalIgnoreCase)) { return item; } } } return null; } public static string SharedFromToken(string token) { GameObject val = PrefabFromToken(token); if ((Object)(object)val == (Object)null) { return token; } ItemDrop component = val.GetComponent<ItemDrop>(); if (component?.m_itemData?.m_shared == null) { return token; } return component.m_itemData.m_shared.m_name; } } internal static class NearbyIndex { private static readonly List<Container> Cached = new List<Container>(); private static float _nextScan; private static Vector3 _lastOrigin; private const float RescanMove = 1.5f; public static IReadOnlyList<Container> Current => Cached; public static void Tick() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value) { Cached.Clear(); return; } float range = ScanRange(); Vector3 position = ((Component)localPlayer).transform.position; bool flag = Vector3.Distance(position, _lastOrigin) > 1.5f; if (!(Time.time < _nextScan) || flag || Cached.Count <= 0) { Rescan(position, range); _lastOrigin = position; _nextScan = Time.time + 0.6f; } } public static float ScanRange() { if (Plugin.Settings == null) { return 20f; } return RulesFile.MaxScanRange(Plugin.Settings.StoreRange.Value, Plugin.Settings.CraftRange.Value, Plugin.Settings.PlayerDumpRange.Value); } public static void Rescan(Vector3 origin, float range) { //IL_0013: 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) Cached.Clear(); if (range <= 0f) { return; } Collider[] array = Physics.OverlapSphere(origin, range); HashSet<int> hashSet = new HashSet<int>(); Collider[] array2 = array; foreach (Collider val in array2) { 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 (hashSet.Add(instanceID) && ContainerFilter.PlayerMayUse(componentInParent, origin)) { Cached.Add(componentInParent); } } } } public static List<Container> Within(Vector3 origin, float range) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) List<Container> list = new List<Container>(); foreach (Container item in Cached) { if (!((Object)(object)item == (Object)null) && ContainerFilter.Distance(origin, ((Component)item).transform.position) <= range) { list.Add(item); } } return list; } public static int CountItem(Vector3 origin, float range, string sharedName, bool leaveOne) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(sharedName)) { return 0; } int num = 0; float fallback = ((Plugin.Settings != null) ? Plugin.Settings.CraftRange.Value : range); foreach (Container item in Cached) { if ((Object)(object)item == (Object)null) { continue; } string piecePrefab = ContainerFilter.PiecePrefab(item); float num2 = RulesFile.CraftRange(piecePrefab, fallback); if (ContainerFilter.Distance(origin, ((Component)item).transform.position) > num2) { continue; } Inventory inventory = item.GetInventory(); if (inventory != null && RulesFile.AllowsCraft(piecePrefab, sharedName)) { int num3 = inventory.CountItems(sharedName, -1, true); if (leaveOne && num3 > 0) { num3--; } if (num3 > 0) { num += num3; } } } return num; } }