Interact with inventories

The /inventory command allow listing inventory and clicking on items
Should be enough for operating GUI menus such as Server chooser/teleporter
This commit is contained in:
ORelio 2020-03-29 18:41:26 +02:00
parent bc3d6aba00
commit e04f06cece
13 changed files with 574 additions and 217 deletions

View file

@ -5,6 +5,7 @@ using System.Text;
using System.Net.Sockets;
using MinecraftClient.Mapping;
using MinecraftClient.Crypto;
using MinecraftClient.Inventory;
namespace MinecraftClient.Protocol.Handlers
{
@ -319,6 +320,40 @@ namespace MinecraftClient.Protocol.Handlers
return ReadNextNbt(cache, true);
}
/// <summary>
/// Read a single item slot from a cache of byte and remove it from the cache
/// </summary>
/// <param name="item">Item</param>
/// <returns>The item that was read or NULL for an empty slot</returns>
public Item ReadNextItemSlot(List<byte> cache)
{
List<byte> slotData = new List<byte>();
if (protocolversion > Protocol18Handler.MC113Version)
{
// MC 1.13 and greater
bool itemPresent = ReadNextBool(cache);
if (itemPresent)
{
int itemID = ReadNextVarInt(cache);
byte itemCount = ReadNextByte(cache);
Dictionary<string, object> NBT = ReadNextNbt(cache);
return new Item(itemID, itemCount, NBT);
}
else return null;
}
else
{
// MC 1.12.2 and lower
short itemID = ReadNextShort(cache);
if (itemID == -1)
return null;
byte itemCount = ReadNextByte(cache);
short itemDamage = ReadNextShort(cache);
Dictionary<string, object> NBT = ReadNextNbt(cache);
return new Item(itemID, itemCount, NBT);
}
}
/// <summary>
/// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
/// </summary>
@ -395,6 +430,163 @@ namespace MinecraftClient.Protocol.Handlers
}
}
/// <summary>
/// Build an uncompressed Named Binary Tag blob for sending over the network
/// </summary>
/// <param name="nbt">Dictionary to encode as Nbt</param>
/// <returns>Byte array for this NBT tag</returns>
public byte[] GetNbt(Dictionary<string, object> nbt)
{
return GetNbt(nbt, true);
}
/// <summary>
/// Build an uncompressed Named Binary Tag blob for sending over the network (internal)
/// </summary>
/// <param name="nbt">Dictionary to encode as Nbt</param>
/// <param name="root">TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag</param>
/// <returns>Byte array for this NBT tag</returns>
private byte[] GetNbt(Dictionary<string, object> nbt, bool root)
{
if (nbt == null || nbt.Count == 0)
return new byte[] { 0 }; // TAG_End
List<byte> bytes = new List<byte>();
if (root)
{
bytes.Add(10); // TAG_Compound
}
foreach (var item in nbt)
{
byte fieldType;
byte[] fieldNameLength = GetUShort((ushort)item.Key.Length);
byte[] fieldName = Encoding.ASCII.GetBytes(item.Key);
byte[] fieldData = GetNbtField(item.Value, out fieldType);
bytes.AddRange(fieldNameLength);
bytes.AddRange(fieldName);
bytes.Add(fieldType);
bytes.AddRange(fieldData);
}
bytes.Add(0); // TAG_End
return bytes.ToArray();
}
/// <summary>
/// Convert a single object into its NBT representation (internal)
/// </summary>
/// <param name="obj">Object to convert</param>
/// <param name="fieldType">Field type for the passed object</param>
/// <returns>Binary data for the passed object</returns>
private byte[] GetNbtField(object obj, out byte fieldType)
{
if (obj is byte)
{
fieldType = 1; // TAG_Byte
return new[] { (byte)obj };
}
else if (obj is short)
{
fieldType = 2; // TAG_Short
return GetShort((short)obj);
}
else if (obj is int)
{
fieldType = 3; // TAG_Int
return GetInt((int)obj);
}
else if (obj is long)
{
fieldType = 4; // TAG_Long
return GetLong((long)obj);
}
else if (obj is float)
{
fieldType = 5; // TAG_Float
return GetFloat((float)obj);
}
else if (obj is double)
{
fieldType = 6; // TAG_Double
return GetDouble((double)obj);
}
else if (obj is byte[])
{
fieldType = 7; // TAG_Byte_Array
return (byte[])obj;
}
else if (obj is string)
{
fieldType = 8; // TAG_String
byte[] stringBytes = Encoding.UTF8.GetBytes((string)obj);
return ConcatBytes(GetUShort((ushort)stringBytes.Length), stringBytes);
}
else if (obj is object[])
{
fieldType = 9; // TAG_List
List<object> list = new List<object>((object[])obj);
int arrayLengthTotal = list.Count;
// Treat empty list as TAG_Byte, length 0
if (arrayLengthTotal == 0)
return ConcatBytes(new[] { (byte)1 }, GetInt(0));
// Encode first list item, retain its type
byte firstItemType;
string firstItemTypeString = list[0].GetType().Name;
byte[] firstItemBytes = GetNbtField(list[0], out firstItemType);
list.RemoveAt(0);
// Encode further list items, check they have the same type
byte subsequentItemType;
List<byte> subsequentItemsBytes = new List<byte>();
foreach (object item in list)
{
subsequentItemsBytes.AddRange(GetNbtField(item, out subsequentItemType));
if (subsequentItemType != firstItemType)
throw new System.IO.InvalidDataException(
"GetNbt: Cannot encode object[] list with mixed types: " + firstItemTypeString + ", " + item.GetType().Name + " into NBT!");
}
// Build NBT list: type, length, item array
return ConcatBytes(new[] { firstItemType }, GetInt(arrayLengthTotal), firstItemBytes, subsequentItemsBytes.ToArray());
}
else if (obj is Dictionary<string, object>)
{
fieldType = 10; // TAG_Compound
return GetNbt((Dictionary<string, object>)obj, false);
}
else if (obj is int[])
{
fieldType = 11; // TAG_Int_Array
int[] srcIntList = (int[])obj;
List<byte> encIntList = new List<byte>();
encIntList.AddRange(GetInt(srcIntList.Length));
foreach (int item in srcIntList)
encIntList.AddRange(GetInt(item));
return encIntList.ToArray();
}
else if (obj is long[])
{
fieldType = 12; // TAG_Long_Array
long[] srcLongList = (long[])obj;
List<byte> encLongList = new List<byte>();
encLongList.AddRange(GetInt(srcLongList.Length));
foreach (long item in srcLongList)
encLongList.AddRange(GetLong(item));
return encLongList.ToArray();
}
else
{
throw new System.IO.InvalidDataException("GetNbt: Cannot encode data type " + obj.GetType().Name + " into NBT!");
}
}
/// <summary>
/// Build an integer for sending over the network
/// </summary>
@ -412,6 +604,30 @@ namespace MinecraftClient.Protocol.Handlers
return bytes.ToArray();
}
/// <summary>
/// Get byte array representing a long integer
/// </summary>
/// <param name="number">Long to process</param>
/// <returns>Array ready to send</returns>
public byte[] GetLong(long number)
{
byte[] theLong = BitConverter.GetBytes(number);
Array.Reverse(theLong);
return theLong;
}
/// <summary>
/// Get byte array representing an integer
/// </summary>
/// <param name="number">Integer to process</param>
/// <returns>Array ready to send</returns>
public byte[] GetInt(int number)
{
byte[] theInt = BitConverter.GetBytes(number);
Array.Reverse(theInt);
return theInt;
}
/// <summary>
/// Get byte array representing a short
/// </summary>
@ -424,6 +640,18 @@ namespace MinecraftClient.Protocol.Handlers
return theShort;
}
/// <summary>
/// Get byte array representing an unsigned short
/// </summary>
/// <param name="number">Short to process</param>
/// <returns>Array ready to send</returns>
public byte[] GetUShort(ushort number)
{
byte[] theShort = BitConverter.GetBytes(number);
Array.Reverse(theShort);
return theShort;
}
/// <summary>
/// Get byte array representing a double
/// </summary>
@ -448,7 +676,6 @@ namespace MinecraftClient.Protocol.Handlers
return theFloat;
}
/// <summary>
/// Get byte array with length information prepended to it
/// </summary>
@ -473,7 +700,6 @@ namespace MinecraftClient.Protocol.Handlers
public byte[] GetString(string text)
{
byte[] bytes = Encoding.UTF8.GetBytes(text);
return ConcatBytes(GetVarInt(bytes.Length), bytes);
}
@ -499,6 +725,42 @@ namespace MinecraftClient.Protocol.Handlers
return locationBytes;
}
/// <summary>
/// Get a byte array representing the given item as an item slot
/// </summary>
/// <param name="item">Item</param>
/// <returns>Item slot representation</returns>
public byte[] GetItemSlot(Item item)
{
List<byte> slotData = new List<byte>();
if (protocolversion > Protocol18Handler.MC113Version)
{
// MC 1.13 and greater
if (item == null || item.IsEmpty)
slotData.Add(0); // No item
else
{
slotData.Add(1); // Item is present
slotData.AddRange(GetVarInt((int)item.Type));
slotData.Add((byte)item.Count);
slotData.AddRange(GetNbt(item.NBT));
}
}
else
{
// MC 1.12.2 and lower
if (item == null || item.IsEmpty)
slotData.AddRange(GetShort(-1));
else
{
slotData.AddRange(GetShort((short)item.Type));
slotData.Add((byte)item.Count);
slotData.AddRange(GetNbt(item.NBT));
}
}
return slotData.ToArray();
}
/// <summary>
/// Easily append several byte arrays
/// </summary>

View file

@ -23,6 +23,8 @@ namespace MinecraftClient.Protocol.Handlers
HeldItemChange,
InteractEntity,
UseItem,
ClickWindow,
CloseWindow,
PlayerBlockPlacement
}
}

View file

@ -8,6 +8,7 @@ using MinecraftClient.Crypto;
using MinecraftClient.Proxy;
using System.Security.Cryptography;
using MinecraftClient.Mapping;
using MinecraftClient.Inventory;
namespace MinecraftClient.Protocol.Handlers
{
@ -641,22 +642,32 @@ namespace MinecraftClient.Protocol.Handlers
return false; //Currently not implemented
}
public bool SendInteractEntityPacket(int EntityID, int type)
public bool SendInteractEntity(int EntityID, int type)
{
return false; //Currently not implemented
}
public bool SendInteractEntityPacket(int EntityID, int type, float X, float Y, float Z, int hand)
public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z, int hand)
{
return false; //Currently not implemented
}
public bool SendInteractEntityPacket(int EntityID, int type, float X, float Y, float Z)
public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z)
{
return false; //Currently not implemented
}
public bool SendUseItemPacket(int hand)
public bool SendUseItem(int hand)
{
return false; //Currently not implemented
}
public bool SendClickWindow(int windowId, int slotId, Item item)
{
return false; //Currently not implemented
}
public bool SendCloseWindow(int windowId)
{
return false; //Currently not implemented
}

View file

@ -41,6 +41,7 @@ namespace MinecraftClient.Protocol.Handlers
private bool autocomplete_received = false;
private int autocomplete_transaction_id = 0;
private readonly List<string> autocomplete_result = new List<string>();
private readonly Dictionary<int, short> window_actions = new Dictionary<int, short>();
private bool login_phase = true;
private int protocolversion;
private int currentDimension;
@ -497,23 +498,27 @@ namespace MinecraftClient.Protocol.Handlers
case PacketIncomingType.OpenWindow:
if (handler.GetInventoryEnabled())
{
if (protocolversion < MC114Version) // packet changed at 1.14
if (protocolversion < MC114Version)
{
// MC 1.13 or lower
byte windowID = dataTypes.ReadNextByte(packetData);
string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper();
ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
string title = dataTypes.ReadNextString(packetData);
byte slots = dataTypes.ReadNextByte(packetData);
Container inventory = new Container(windowID, inventoryType, title);
handler.OnInventoryOpen(inventory);
Container inventory = new Container(windowID, inventoryType, ChatParser.ParseText(title));
window_actions[windowID] = 0;
handler.OnInventoryOpen(windowID, inventory);
}
else
{
int WindowID = dataTypes.ReadNextVarInt(packetData);
int WindowType = dataTypes.ReadNextVarInt(packetData);
// MC 1.14 or greater
int windowID = dataTypes.ReadNextVarInt(packetData);
int windowType = dataTypes.ReadNextVarInt(packetData);
string title = dataTypes.ReadNextString(packetData);
Container inventory = new Container(WindowID, WindowType, title);
handler.OnInventoryOpen(inventory);
Container inventory = new Container(windowID, windowType, ChatParser.ParseText(title));
window_actions[windowID] = 0;
handler.OnInventoryOpen(windowID, inventory);
}
}
break;
@ -521,103 +526,32 @@ namespace MinecraftClient.Protocol.Handlers
if (handler.GetInventoryEnabled())
{
byte windowID = dataTypes.ReadNextByte(packetData);
window_actions[windowID] = 0;
handler.OnInventoryClose(windowID);
}
break;
case PacketIncomingType.WindowItems:
if (handler.GetInventoryEnabled())
{
// MC 1.12.2 or lower
if (protocolversion < MC113Version)
byte windowId = dataTypes.ReadNextByte(packetData);
short elements = dataTypes.ReadNextShort(packetData);
Dictionary<int, Item> inventorySlots = new Dictionary<int, Item>();
for (short slotId = 0; slotId < elements; slotId++)
{
byte id = dataTypes.ReadNextByte(packetData);
short elements = dataTypes.ReadNextShort(packetData);
Dictionary<int, Item> itemsList = new Dictionary<int, Item>(); // index is SlotID
for (int i = 0; i < elements; i++)
{
short itemID = dataTypes.ReadNextShort(packetData);
if (itemID == -1) continue;
byte itemCount = dataTypes.ReadNextByte(packetData);
short itemDamage = dataTypes.ReadNextShort(packetData);
Dictionary<string, object> NBT = new Dictionary<string, object>();
//TODO: Add to the dictionary for the inventory its in using the id
if (packetData.ToArray().Count() > 0)
{
NBT = dataTypes.ReadNextNbt(packetData);
}
Item item = new Item(itemID, itemCount, itemDamage, NBT);
itemsList.Add(i, item);
}
handler.OnWindowItems(id, itemsList);
}
else
{
// MC 1.13 after
byte id = dataTypes.ReadNextByte(packetData);
short elements = dataTypes.ReadNextShort(packetData);
Dictionary<int, Item> itemsList = new Dictionary<int, Item>(); // index is SlotID
for (int i = 0; i < elements; i++)
{
bool haveItem = dataTypes.ReadNextBool(packetData);
if (haveItem)
{
int itemID = dataTypes.ReadNextVarInt(packetData);
byte itemCount = dataTypes.ReadNextByte(packetData);
dataTypes.ReadNextNbt(packetData);
Item item = new Item(itemID, itemCount, i);
itemsList.Add(i, item);
}
}
handler.OnWindowItems(id, itemsList);
Item item = dataTypes.ReadNextItemSlot(packetData);
if (item != null)
inventorySlots[slotId] = item;
}
handler.OnWindowItems(windowId, inventorySlots);
}
break;
case PacketIncomingType.SetSlot:
if(handler.GetInventoryEnabled())
if (handler.GetInventoryEnabled())
{
// MC 1.12.2 or lower
if (protocolversion < MC113Version)
{
byte WindowID = dataTypes.ReadNextByte(packetData);
short SlotID = dataTypes.ReadNextShort(packetData);
short ItemID = dataTypes.ReadNextShort(packetData);
if (ItemID == -1)
{
handler.OnSlotClear(WindowID, SlotID);
}
else
{
byte Count = dataTypes.ReadNextByte(packetData);
short itemDamage = dataTypes.ReadNextShort(packetData); // useless so ignored
Dictionary<string, object> NBT = new Dictionary<string, object>();
//TODO: Add to the dictionary for the inventory its in using the id
if (packetData.ToArray().Count() > 0)
{
NBT = dataTypes.ReadNextNbt(packetData);
}
handler.OnSetSlot(WindowID, SlotID, ItemID, Count, NBT);
}
}
else
{
// MC 1.13 after
byte WindowID = dataTypes.ReadNextByte(packetData);
short SlotID = dataTypes.ReadNextShort(packetData);
bool Present = dataTypes.ReadNextBool(packetData);
if (Present)
{
int ItemID = dataTypes.ReadNextVarInt(packetData);
byte Count = dataTypes.ReadNextByte(packetData);
Dictionary<string, object> NBT = dataTypes.ReadNextNbt(packetData);
handler.OnSetSlot(WindowID, SlotID, ItemID, Count, NBT);
}
else
{
handler.OnSlotClear(WindowID, SlotID);
}
}
byte windowID = dataTypes.ReadNextByte(packetData);
short slotID = dataTypes.ReadNextShort(packetData);
Item item = dataTypes.ReadNextItemSlot(packetData);
handler.OnSetSlot(windowID, slotID, item);
}
break;
case PacketIncomingType.ResourcePackSend:
@ -1285,7 +1219,7 @@ namespace MinecraftClient.Protocol.Handlers
/// <param name="EntityID"></param>
/// <param name="type"></param>
/// <returns></returns>
public bool SendInteractEntityPacket(int EntityID, int type)
public bool SendInteractEntity(int EntityID, int type)
{
try
{
@ -1300,7 +1234,7 @@ namespace MinecraftClient.Protocol.Handlers
catch (ObjectDisposedException) { return false; }
}
// TODO: Interact at block location (e.g. chest minecart)
public bool SendInteractEntityPacket(int EntityID, int type, float X, float Y, float Z, int hand)
public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z, int hand)
{
try
{
@ -1318,12 +1252,12 @@ namespace MinecraftClient.Protocol.Handlers
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendInteractEntityPacket(int EntityID, int type, float X, float Y, float Z)
public bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z)
{
return false;
}
public bool SendUseItemPacket(int hand)
public bool SendUseItem(int hand)
{
try
{
@ -1370,5 +1304,45 @@ namespace MinecraftClient.Protocol.Handlers
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendClickWindow(int windowId, int slotId, Item item)
{
try
{
short actionNumber = (short)(window_actions[windowId] + 1);
window_actions[windowId] = actionNumber;
List<byte> packet = new List<byte>();
packet.Add((byte)windowId);
packet.AddRange(dataTypes.GetShort((short)slotId));
packet.Add(0); // Left mouse click
packet.AddRange(dataTypes.GetShort(actionNumber));
// Operation mode = 0 (default)
if (protocolversion >= MC19Version)
packet.AddRange(dataTypes.GetVarInt(0));
else packet.Add(0);
packet.AddRange(dataTypes.GetItemSlot(item));
SendPacket(PacketOutgoingType.ClickWindow, packet);
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
public bool SendCloseWindow(int windowId)
{
try
{
SendPacket(PacketOutgoingType.CloseWindow, new[] { (byte)windowId });
return true;
}
catch (SocketException) { return false; }
catch (System.IO.IOException) { return false; }
catch (ObjectDisposedException) { return false; }
}
}
}

View file

@ -306,6 +306,8 @@ namespace MinecraftClient.Protocol.Handlers
case PacketOutgoingType.HeldItemChange: return 0x17;
case PacketOutgoingType.InteractEntity: return 0x02;
case PacketOutgoingType.TeleportConfirm: throw new InvalidOperationException("Teleport confirm is not supported in protocol " + protocol);
case PacketOutgoingType.ClickWindow: return 0x0E;
case PacketOutgoingType.CloseWindow: return 0x0D;
}
}
else if (protocol <= Protocol18Handler.MC1112Version) // MC 1.9, 1,10 and 1.11
@ -324,6 +326,8 @@ namespace MinecraftClient.Protocol.Handlers
case PacketOutgoingType.TeleportConfirm: return 0x00;
case PacketOutgoingType.HeldItemChange: return 0x17;
case PacketOutgoingType.InteractEntity: return 0x0A;
case PacketOutgoingType.ClickWindow: return 0x07;
case PacketOutgoingType.CloseWindow: return 0x08;
}
}
else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12
@ -342,6 +346,8 @@ namespace MinecraftClient.Protocol.Handlers
case PacketOutgoingType.TeleportConfirm: return 0x00;
case PacketOutgoingType.HeldItemChange: return 0x1A;
case PacketOutgoingType.InteractEntity: return 0x0B;
case PacketOutgoingType.ClickWindow: return 0x07;
case PacketOutgoingType.CloseWindow: return 0x08;
}
}
else if (protocol <= Protocol18Handler.MC1122Version) // 1.12.2
@ -360,6 +366,8 @@ namespace MinecraftClient.Protocol.Handlers
case PacketOutgoingType.TeleportConfirm: return 0x00;
case PacketOutgoingType.HeldItemChange: return 0x1F;
case PacketOutgoingType.InteractEntity: return 0x0A;
case PacketOutgoingType.ClickWindow: return 0x07;
case PacketOutgoingType.CloseWindow: return 0x08;
}
}
else if (protocol < Protocol18Handler.MC114Version) // MC 1.13 to 1.13.2
@ -378,6 +386,8 @@ namespace MinecraftClient.Protocol.Handlers
case PacketOutgoingType.TeleportConfirm: return 0x00;
case PacketOutgoingType.HeldItemChange: return 0x21;
case PacketOutgoingType.InteractEntity: return 0x0D;
case PacketOutgoingType.ClickWindow: return 0x08;
case PacketOutgoingType.CloseWindow: return 0x09;
}
}
else // MC 1.14 to 1.15
@ -398,6 +408,8 @@ namespace MinecraftClient.Protocol.Handlers
case PacketOutgoingType.InteractEntity: return 0x0E;
case PacketOutgoingType.UseItem: return 0x2D;
case PacketOutgoingType.PlayerBlockPlacement: return 0x2C;
case PacketOutgoingType.ClickWindow: return 0x09;
case PacketOutgoingType.CloseWindow: return 0x0A;
}
}

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using MinecraftClient.Crypto;
using MinecraftClient.Mapping;
using MinecraftClient.Inventory;
namespace MinecraftClient.Protocol
{
@ -98,7 +99,7 @@ namespace MinecraftClient.Protocol
/// <param name="EntityID">Entity ID to interact with</param>
/// <param name="type">Type of interaction (0: interact, 1: attack, 2: interact at)</param>
/// <returns>True if packet was successfully sent</returns>
bool SendInteractEntityPacket(int EntityID, int type);
bool SendInteractEntity(int EntityID, int type);
/// <summary>
/// Send an entity interaction packet to the server.
@ -110,7 +111,7 @@ namespace MinecraftClient.Protocol
/// <param name="Z">Z coordinate for "interact at"</param>
/// <param name="hand">Player hand (0: main hand, 1: off hand)</param>
/// <returns>True if packet was successfully sent</returns>
bool SendInteractEntityPacket(int EntityID, int type, float X, float Y, float Z, int hand);
bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z, int hand);
/// <summary>
/// Send an entity interaction packet to the server.
@ -121,14 +122,29 @@ namespace MinecraftClient.Protocol
/// <param name="Y">Y coordinate for "interact at"</param>
/// <param name="Z">Z coordinate for "interact at"</param>
/// <returns>True if packet was successfully sent</returns>
bool SendInteractEntityPacket(int EntityID, int type, float X, float Y, float Z);
bool SendInteractEntity(int EntityID, int type, float X, float Y, float Z);
/// <summary>
/// Send a use item packet to the server
/// </summary>
/// <param name="hand">0: main hand, 1: off hand</param>
/// <returns>True if packet was successfully sent</returns>
bool SendUseItemPacket(int hand);
bool SendUseItem(int hand);
/// <summary>
/// Send a click window slot packet to the server
/// </summary>
/// <param name="windowId">Id of the window being clicked</param>
/// <param name="slotId">Id of the clicked slot</param>
/// <param name="item">Item in the clicked slot</param>
/// <returns>True if packet was successfully sent</returns>
bool SendClickWindow(int windowId, int slotId, Item item);
/// <summary>
/// Send a close window packet to the server
/// </summary>
/// <param name="windowId">Id of the window being closed</param>
bool SendCloseWindow(int windowId);
/// <summary>
/// Send player block placement packet to the server

View file

@ -53,12 +53,12 @@ namespace MinecraftClient.Protocol
/// <summary>
/// Called when an inventory is opened
/// </summary>
void OnInventoryOpen(Container inventory);
void OnInventoryOpen(int inventoryID, Container inventory);
/// <summary>
/// Called when an inventory is closed
/// </summary>
void OnInventoryClose(byte inventoryID);
void OnInventoryClose(int inventoryID);
/// <summary>
/// Called when the player respawns, which happens on login, respawn and world change.
@ -199,26 +199,17 @@ namespace MinecraftClient.Protocol
/// <summary>
/// Called when inventory items have been received
/// </summary>
/// <param name="type">Inventory type</param>
/// <param name="inventoryID">Inventory ID</param>
/// <param name="itemList">Item list</param>
void OnWindowItems(int type, Dictionary<int, MinecraftClient.Inventory.Item> itemList);
/// <summary>
/// Called when a single slot has been cleared inside an inventory
/// </summary>
/// <param name="WindowID">Inventory ID</param>
/// <param name="SlotID">Slot ID</param>
void OnSlotClear(byte WindowID, short SlotID);
void OnWindowItems(byte inventoryID, Dictionary<int, MinecraftClient.Inventory.Item> itemList);
/// <summary>
/// Called when a single slot has been updated inside an inventory
/// </summary>
/// <param name="WindowID">Inventory ID</param>
/// <param name="SlotID">Slot ID</param>
/// <param name="ItemID">Item ID</param>
/// <param name="Count">Item Count</param>
/// <param name="NBT">Item Metadata</param>
void OnSetSlot(byte WindowID, short SlotID, int ItemID, byte Count, Dictionary<string, object> NBT);
/// <param name="inventoryID">Window ID</param>
/// <param name="slotID">Slot ID</param>
/// <param name="item">Item (may be null for empty slot)</param>
void OnSetSlot(byte inventoryID, short slotID, Item item);
/// <summary>
/// Called when the Player entity ID has been received from the server