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;
}
}
}
}