using System.Collections.Generic; namespace MinecraftClient.Inventory { /// /// Represents a Minecraft inventory (player inventory, chest, etc.) /// public class Container { /// /// ID of the container on the server /// public int ID; /// /// Type of container /// public ContainerType Type; /// /// title of container /// public string? Title; /// /// state of container /// public int StateID; /// /// Container Items /// public Dictionary Items; /// /// Container Properties /// Used for Frunaces, Enchanting Table, Beacon, Brewing stand, Stone cutter, Loom and Lectern /// More info about: https://wiki.vg/Protocol#Set_Container_Property /// public Dictionary Properties; /// /// Create an empty container with ID, Type and Title /// /// Container ID /// Container Type /// Container Title public Container(int id, ContainerType type, string title) { ID = id; Type = type; Title = title; Items = new(); Properties = new(); } /// /// Create a container with ID, Type, Title and Items /// /// Container ID /// Container Type /// Container Title /// Container Items (key: slot ID, value: item info) public Container(int id, ContainerType type, string? title, Dictionary items) { ID = id; Type = type; Title = title; Items = items; Properties = new(); } /// /// Create an empty container with ID, Type and Title /// /// Container ID /// Container Type /// Container title public Container(int id, ContainerTypeOld type, string title) { ID = id; Title = title; Type = ConvertType.ToNew(type); Items = new(); Properties = new(); } /// /// Create an empty container with ID, Type and Title /// /// Container ID /// Container Type /// Container Title /// Protocol version for version-specific mapping public Container(int id, int typeID, string title, int protocolVersion = 0) { ID = id; Type = GetContainerType(typeID, protocolVersion); Title = title; Items = new(); Properties = new(); } /// /// Create an empty container with Type /// /// Container Type public Container(ContainerType type) { ID = -1; Type = type; Title = null; Items = new(); Properties = new(); } /// /// Create an empty container with T^ype and Items /// /// Container Type /// Container Items (key: slot ID, value: item info) public Container(ContainerType type, Dictionary items) { ID = -1; Type = type; Title = null; Items = items; Properties = new(); } /// /// Get container type from Type ID /// /// Container Type ID /// Protocol version (menu registry changed across versions) /// Container Type public static ContainerType GetContainerType(int typeID, int protocolVersion = 0) { // MC 1.20.4 (protocol 765) added crafter_3x3 at index 7, shifting all subsequent IDs by +1. // Registry order from decompiled MenuType.java: // 1.14-1.20.2: generic_9x1..generic_3x3(6), anvil(7), beacon(8), ... stonecutter(22) // 1.20.4+: generic_9x1..generic_3x3(6), crafter_3x3(7), anvil(8), beacon(9), ... stonecutter(24) if (protocolVersion >= 765) { return typeID switch { #pragma warning disable format // @formatter:off 0 => ContainerType.Generic_9x1, 1 => ContainerType.Generic_9x2, 2 => ContainerType.Generic_9x3, 3 => ContainerType.Generic_9x4, 4 => ContainerType.Generic_9x5, 5 => ContainerType.Generic_9x6, 6 => ContainerType.Generic_3x3, 7 => ContainerType.Crafter, 8 => ContainerType.Anvil, 9 => ContainerType.Beacon, 10 => ContainerType.BlastFurnace, 11 => ContainerType.BrewingStand, 12 => ContainerType.Crafting, 13 => ContainerType.Enchantment, 14 => ContainerType.Furnace, 15 => ContainerType.Grindstone, 16 => ContainerType.Hopper, 17 => ContainerType.Lectern, 18 => ContainerType.Loom, 19 => ContainerType.Merchant, 20 => ContainerType.ShulkerBox, 21 => ContainerType.SmightingTable, 22 => ContainerType.Smoker, 23 => ContainerType.Cartography, 24 => ContainerType.Stonecutter, _ => ContainerType.Unknown, #pragma warning restore format // @formatter:on }; } return typeID switch { #pragma warning disable format // @formatter:off 0 => ContainerType.Generic_9x1, 1 => ContainerType.Generic_9x2, 2 => ContainerType.Generic_9x3, 3 => ContainerType.Generic_9x4, 4 => ContainerType.Generic_9x5, 5 => ContainerType.Generic_9x6, 6 => ContainerType.Generic_3x3, 7 => ContainerType.Anvil, 8 => ContainerType.Beacon, 9 => ContainerType.BlastFurnace, 10 => ContainerType.BrewingStand, 11 => ContainerType.Crafting, 12 => ContainerType.Enchantment, 13 => ContainerType.Furnace, 14 => ContainerType.Grindstone, 15 => ContainerType.Hopper, 16 => ContainerType.Lectern, 17 => ContainerType.Loom, 18 => ContainerType.Merchant, 19 => ContainerType.ShulkerBox, 20 => ContainerType.Smoker, 21 => ContainerType.Cartography, 22 => ContainerType.Stonecutter, _ => ContainerType.Unknown, #pragma warning restore format // @formatter:on }; } /// /// Search an item in the container /// /// The item to search /// An array of slot ID public int[] SearchItem(ItemType itemType) { List result = new(); if (Items is not null) { foreach (var item in Items) { if (item.Value.Type == itemType) result.Add(item.Key); } } return result.ToArray(); } /// /// List empty slots in the container /// /// An array of slot ID /// Also depending on the container type, some empty slots cannot be used e.g. armor slots. This might cause issues. public int[] GetEmpytSlots() { List result = new(); for (int i = 0; i < Type.SlotCount(); i++) { result.Add(i); } foreach (var item in Items) { result.Remove(item.Key); } return result.ToArray(); } /// /// Check the given slot ID is a hotbar slot and give the hotbar number /// /// The slot ID to check /// Zero-based, 0-8. -1 if not a hotbar /// True if given slot ID is a hotbar slot public bool IsHotbar(int slotId, out int hotbar) { int hotbarStart = Type.SlotCount() - 9; // Remove offhand slot if (Type == ContainerType.PlayerInventory) hotbarStart--; if ((slotId >= hotbarStart) && (slotId <= hotbarStart + 9)) { hotbar = slotId - hotbarStart; return true; } else { hotbar = -1; return false; } } } }