mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
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:
parent
bc3d6aba00
commit
e04f06cece
13 changed files with 574 additions and 217 deletions
BIN
DebugTools/MinecraftClientProxy.suo
Normal file
BIN
DebugTools/MinecraftClientProxy.suo
Normal file
Binary file not shown.
|
|
@ -149,6 +149,22 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
g = readNextBool(ref packetData);
|
g = readNextBool(ref packetData);
|
||||||
Console.WriteLine("[C -> S] Location: " + x + ", " + y + ", " + z + ", (look)" + ", " + g);
|
Console.WriteLine("[C -> S] Location: " + x + ", " + y + ", " + z + ", (look)" + ", " + g);
|
||||||
break;
|
break;
|
||||||
|
case 0x09:
|
||||||
|
byte window = readNextByte(ref packetData);
|
||||||
|
short slot = readNextShort(ref packetData);
|
||||||
|
byte button = readNextByte(ref packetData);
|
||||||
|
short action = readNextShort(ref packetData);
|
||||||
|
int mode = readNextVarInt(ref packetData);
|
||||||
|
bool slotPresent = readNextBool(ref packetData);
|
||||||
|
int itemId = -1;
|
||||||
|
byte itemCount = 0;
|
||||||
|
if (slotPresent)
|
||||||
|
{
|
||||||
|
itemId = readNextVarInt(ref packetData);
|
||||||
|
itemCount = readNextByte(ref packetData);
|
||||||
|
}
|
||||||
|
Console.WriteLine("[C -> S] Window #" + window + " click: #" + slot + " button " + button + " action " + action + " mode " + mode + " item " + itemId + " x" + itemCount);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -209,6 +225,19 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return rawValue[0] != 0;
|
return rawValue[0] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte readNextByte(ref byte[] cache)
|
||||||
|
{
|
||||||
|
byte[] rawValue = readData(1, ref cache);
|
||||||
|
return rawValue[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private short readNextShort(ref byte[] cache)
|
||||||
|
{
|
||||||
|
byte[] rawValue = readData(2, ref cache);
|
||||||
|
Array.Reverse(rawValue); //Endianness
|
||||||
|
return BitConverter.ToInt16(rawValue, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private double readNextDouble(ref byte[] cache)
|
private double readNextDouble(ref byte[] cache)
|
||||||
{
|
{
|
||||||
byte[] rawValue = readData(8, ref cache);
|
byte[] rawValue = readData(8, ref cache);
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,63 @@ using MinecraftClient.Inventory;
|
||||||
|
|
||||||
namespace MinecraftClient.Commands
|
namespace MinecraftClient.Commands
|
||||||
{
|
{
|
||||||
class GetInventory : Command
|
class Inventory : Command
|
||||||
{
|
{
|
||||||
public override string CMDName { get { return "inventory"; } }
|
public override string CMDName { get { return "inventory"; } }
|
||||||
public override string CMDDesc { get { return "inventory: Show your inventory."; } }
|
public override string CMDDesc { get { return "inventory <id> <list|close|click <slot>>: Interact with inventories"; } }
|
||||||
|
|
||||||
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
|
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
|
||||||
{
|
{
|
||||||
List<string> response = new List<string>();
|
if (handler.GetInventoryEnabled())
|
||||||
|
|
||||||
response.Add("Inventory slots:");
|
|
||||||
|
|
||||||
foreach (KeyValuePair<int, Item> item in handler.GetPlayerInventory().Items)
|
|
||||||
{
|
{
|
||||||
response.Add(String.Format(" #{0}: {1} x{2}", item.Key, item.Value.Type, item.Value.Count));
|
string[] args = getArgs(command);
|
||||||
|
if (args.Length >= 1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int inventoryId = int.Parse(args[0]);
|
||||||
|
string action = args.Length > 1
|
||||||
|
? args[1].ToLower()
|
||||||
|
: "list";
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case "close":
|
||||||
|
if (handler.CloseInventory(inventoryId))
|
||||||
|
return "Closing Inventoy #" + inventoryId;
|
||||||
|
else return "Failed to close Inventory #" + inventoryId;
|
||||||
|
case "list":
|
||||||
|
Container inventory = handler.GetInventory(inventoryId);
|
||||||
|
List<string> response = new List<string>();
|
||||||
|
response.Add("Inventory #" + inventoryId + " - " + inventory.Title + "§8");
|
||||||
|
foreach (KeyValuePair<int, Item> item in inventory.Items)
|
||||||
|
response.Add(String.Format(" #{0}: {1} x{2}", item.Key, item.Value.Type, item.Value.Count));
|
||||||
|
return String.Join("\n", response.ToArray());
|
||||||
|
case "click":
|
||||||
|
if (args.Length == 3)
|
||||||
|
{
|
||||||
|
int slot = int.Parse(args[2]);
|
||||||
|
handler.ClickWindowSlot(inventoryId, slot);
|
||||||
|
return "Clicking slot " + slot + " in window #" + inventoryId;
|
||||||
|
}
|
||||||
|
else return CMDDesc;
|
||||||
|
default:
|
||||||
|
return CMDDesc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FormatException) { return CMDDesc; }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dictionary<int, Container> inventories = handler.GetInventories();
|
||||||
|
List<string> response = new List<string>();
|
||||||
|
response.Add("Inventories:");
|
||||||
|
foreach (var inventory in inventories)
|
||||||
|
response.Add(String.Format(" #{0}: {1}", inventory.Key, inventory.Value.Title + "§8"));
|
||||||
|
response.Add(CMDDesc);
|
||||||
|
return String.Join("\n", response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else return "Please enable inventoryhandling in config to use this command.";
|
||||||
return String.Join("\n", response.ToArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,12 @@ namespace MinecraftClient.Commands
|
||||||
|
|
||||||
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
|
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
|
||||||
{
|
{
|
||||||
handler.UseItemOnHand();
|
if (handler.GetInventoryEnabled())
|
||||||
return "Use an item";
|
{
|
||||||
|
handler.UseItemOnHand();
|
||||||
|
return "Used an item";
|
||||||
|
}
|
||||||
|
else return "Please enable inventoryhandling in config to use this command.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,44 +20,34 @@ namespace MinecraftClient.Inventory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Count;
|
public int Count;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Slot ID in the parent inventory
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>-1 means currently being dragged by mouse</remarks>
|
|
||||||
public int SlotID;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Item Metadata
|
/// Item Metadata
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, object> NBT;
|
public Dictionary<string, object> NBT;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an item with Type ID, Count and Slot ID
|
/// Create an item with Type ID, Count and Metadata
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ID">Item Type ID</param>
|
/// <param name="ID">Item Type ID</param>
|
||||||
/// <param name="Count">Item Count</param>
|
/// <param name="Count">Item Count</param>
|
||||||
/// <param name="SlotID">Item Slot ID in parent inventory</param>
|
/// <param name="NBT">Item Metadata</param>
|
||||||
public Item(int id, int count, int slotID)
|
public Item(int id, int count, Dictionary<string, object> nbt)
|
||||||
{
|
{
|
||||||
this.Type = (ItemType)id;
|
this.Type = (ItemType)id;
|
||||||
this.Count = count;
|
this.Count = count;
|
||||||
this.SlotID = slotID;
|
this.NBT = nbt;
|
||||||
this.NBT = new Dictionary<string, object>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an item with Type ID, Count, Slot ID and Metadata
|
/// Check if the item slot is empty
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ID">Item Type ID</param>
|
/// <returns>TRUE if the item is empty</returns>
|
||||||
/// <param name="Count">Item Count</param>
|
public bool IsEmpty
|
||||||
/// <param name="SlotID">Item Slot ID in parent inventory</param>
|
|
||||||
/// <param name="NBT">Item Metadata</param>
|
|
||||||
public Item(int id, int count, int slotID, Dictionary<string, object> nbt)
|
|
||||||
{
|
{
|
||||||
this.Type = (ItemType)id;
|
get
|
||||||
this.Count = count;
|
{
|
||||||
this.SlotID = slotID;
|
return Type == ItemType.Air || Count == 0;
|
||||||
this.NBT = nbt;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace MinecraftClient
|
||||||
|
|
||||||
private readonly List<ChatBot> bots = new List<ChatBot>();
|
private readonly List<ChatBot> bots = new List<ChatBot>();
|
||||||
private static readonly List<ChatBot> botsOnHold = new List<ChatBot>();
|
private static readonly List<ChatBot> botsOnHold = new List<ChatBot>();
|
||||||
private static List<Container> inventories = new List<Container>();
|
private static Dictionary<int, Container> inventories = new Dictionary<int, Container>();
|
||||||
|
|
||||||
private readonly Dictionary<string, List<ChatBot>> registeredBotPluginChannels = new Dictionary<string, List<ChatBot>>();
|
private readonly Dictionary<string, List<ChatBot>> registeredBotPluginChannels = new Dictionary<string, List<ChatBot>>();
|
||||||
private readonly List<string> registeredServerPluginChannels = new List<String>();
|
private readonly List<string> registeredServerPluginChannels = new List<String>();
|
||||||
|
|
@ -53,7 +53,6 @@ namespace MinecraftClient
|
||||||
private string username;
|
private string username;
|
||||||
private string uuid;
|
private string uuid;
|
||||||
private string sessionid;
|
private string sessionid;
|
||||||
private Container playerInventory = new Container(ContainerType.PlayerInventory);
|
|
||||||
private DateTime lastKeepAlive;
|
private DateTime lastKeepAlive;
|
||||||
private object lastKeepAliveLock = new object();
|
private object lastKeepAliveLock = new object();
|
||||||
|
|
||||||
|
|
@ -136,6 +135,12 @@ namespace MinecraftClient
|
||||||
inventoryHandlingEnabled = Settings.InventoryHandling;
|
inventoryHandlingEnabled = Settings.InventoryHandling;
|
||||||
entityHandlingEnabled = Settings.EntityHandling;
|
entityHandlingEnabled = Settings.EntityHandling;
|
||||||
|
|
||||||
|
if (inventoryHandlingEnabled)
|
||||||
|
{
|
||||||
|
inventories.Clear();
|
||||||
|
inventories[0] = new Container(0, ContainerType.PlayerInventory, "Player Inventory");
|
||||||
|
}
|
||||||
|
|
||||||
bool retry = false;
|
bool retry = false;
|
||||||
this.sessionid = sessionID;
|
this.sessionid = sessionID;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
|
@ -550,7 +555,6 @@ namespace MinecraftClient
|
||||||
inventoryHandlingEnabled = false;
|
inventoryHandlingEnabled = false;
|
||||||
inventoryHandlingRequested = false;
|
inventoryHandlingRequested = false;
|
||||||
inventories.Clear();
|
inventories.Clear();
|
||||||
playerInventory = null;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -592,13 +596,34 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all inventories. ID 0 is the player inventory.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>All inventories</returns>
|
||||||
|
public Dictionary<int, Container> GetInventories()
|
||||||
|
{
|
||||||
|
return inventories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get client player's inventory items
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inventoryID">Window ID of the requested inventory</param>
|
||||||
|
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
||||||
|
public Container GetInventory(int inventoryID)
|
||||||
|
{
|
||||||
|
if (inventories.ContainsKey(inventoryID))
|
||||||
|
return inventories[inventoryID];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get client player's inventory items
|
/// Get client player's inventory items
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
/// <returns> Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID)</returns>
|
||||||
public Container GetPlayerInventory()
|
public Container GetPlayerInventory()
|
||||||
{
|
{
|
||||||
return playerInventory;
|
return GetInventory(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -765,19 +790,14 @@ namespace MinecraftClient
|
||||||
/// When an inventory is opened
|
/// When an inventory is opened
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inventory">Location to reach</param>
|
/// <param name="inventory">Location to reach</param>
|
||||||
public void OnInventoryOpen(Container inventory)
|
public void OnInventoryOpen(int inventoryID, Container inventory)
|
||||||
{
|
{
|
||||||
//TODO: Handle Inventory
|
inventories[inventoryID] = inventory;
|
||||||
if (!inventories.Contains(inventory))
|
|
||||||
{
|
|
||||||
inventories.Add(inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.DebugMessages)
|
if (inventoryID != 0)
|
||||||
{
|
{
|
||||||
ConsoleIO.WriteLineFormatted("§8An Inventory opened: " + inventory.Type + " - " + inventory.Title);
|
ConsoleIO.WriteLogLine("Inventory # " + inventoryID + " opened: " + inventory.Title);
|
||||||
foreach (var item in inventory.Items)
|
ConsoleIO.WriteLogLine("Use /inventory to interact with it.");
|
||||||
ConsoleIO.WriteLineFormatted("§8 - Slot " + item.Key + ": " + item.Value.Type + " x" + item.Value.Count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -785,64 +805,42 @@ namespace MinecraftClient
|
||||||
/// When an inventory is close
|
/// When an inventory is close
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inventoryID">Location to reach</param>
|
/// <param name="inventoryID">Location to reach</param>
|
||||||
public void OnInventoryClose(byte inventoryID)
|
public void OnInventoryClose(int inventoryID)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < inventories.Count; i++)
|
if (inventories.ContainsKey(inventoryID))
|
||||||
{
|
inventories.Remove(inventoryID);
|
||||||
Container inventory = inventories[i];
|
|
||||||
if (inventory == null) continue;
|
if (inventoryID != 0)
|
||||||
if (inventory.Type == Container.GetContainerType(inventoryID))
|
ConsoleIO.WriteLogLine("Inventory # " + inventoryID + " closed.");
|
||||||
{
|
|
||||||
inventories.Remove(inventory);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When received window items from server.
|
/// When received window items from server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Inventory type</param>
|
/// <param name="inventoryID">Inventory ID</param>
|
||||||
/// <param name="itemList">Item list, key = slot ID, value = Item information</param>
|
/// <param name="itemList">Item list, key = slot ID, value = Item information</param>
|
||||||
public void OnWindowItems(int type, Dictionary<int, Inventory.Item> itemList)
|
public void OnWindowItems(byte inventoryID, Dictionary<int, Inventory.Item> itemList)
|
||||||
{
|
{
|
||||||
// 0 is player inventory
|
if (inventories.ContainsKey(inventoryID))
|
||||||
if (type == 0)
|
inventories[inventoryID].Items = itemList;
|
||||||
playerInventory.Items = itemList;
|
|
||||||
if (Settings.DebugMessages)
|
|
||||||
{
|
|
||||||
ConsoleIO.WriteLineFormatted("§8Received Window of type " + type);
|
|
||||||
foreach (var item in itemList)
|
|
||||||
ConsoleIO.WriteLineFormatted("§8 - Slot " + item.Key + ": " + item.Value.Type + " x" + item.Value.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When a slot is cleared inside window items
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="WindowID">Window ID</param>
|
|
||||||
/// <param name="SlotID">Slot ID</param>
|
|
||||||
public void OnSlotClear(byte WindowID, short SlotID)
|
|
||||||
{
|
|
||||||
if (WindowID == 0 && playerInventory.Items.ContainsKey(SlotID))
|
|
||||||
{
|
|
||||||
playerInventory.Items.Remove(SlotID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When a slot is set inside window items
|
/// When a slot is set inside window items
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="WindowID">Window ID</param>
|
/// <param name="inventoryID">Window ID</param>
|
||||||
/// <param name="SlotID">Slot ID</param>
|
/// <param name="slotID">Slot ID</param>
|
||||||
/// <param name="ItemID">Item ID</param>
|
/// <param name="item">Item (may be null for empty slot)</param>
|
||||||
/// <param name="Count">Item Count</param>
|
public void OnSetSlot(byte inventoryID, short slotID, Item item)
|
||||||
/// <param name="NBT">Item Metadata</param>
|
|
||||||
public void OnSetSlot(byte WindowID, short SlotID, int ItemID, byte Count, Dictionary<string, object> NBT)
|
|
||||||
{
|
{
|
||||||
if (WindowID == 0)
|
if (inventories.ContainsKey(inventoryID))
|
||||||
{
|
{
|
||||||
playerInventory.Items[SlotID] = new Inventory.Item(ItemID, Count, SlotID, NBT);
|
if (item == null || item.IsEmpty)
|
||||||
|
{
|
||||||
|
if (inventories[inventoryID].Items.ContainsKey(slotID))
|
||||||
|
inventories[inventoryID].Items.Remove(slotID);
|
||||||
|
}
|
||||||
|
else inventories[inventoryID].Items[slotID] = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1312,7 +1310,35 @@ namespace MinecraftClient
|
||||||
/// <returns>TRUE if the item was successfully used</returns>
|
/// <returns>TRUE if the item was successfully used</returns>
|
||||||
public bool UseItemOnHand()
|
public bool UseItemOnHand()
|
||||||
{
|
{
|
||||||
return handler.SendUseItemPacket(0);
|
return handler.SendUseItem(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Click a slot in the specified window
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>TRUE if the slot was successfully clicked</returns>
|
||||||
|
public bool ClickWindowSlot(int windowId, int slotId)
|
||||||
|
{
|
||||||
|
Item item = null;
|
||||||
|
if (inventories.ContainsKey(windowId) && inventories[windowId].Items.ContainsKey(slotId))
|
||||||
|
item = inventories[windowId].Items[slotId];
|
||||||
|
|
||||||
|
return handler.SendClickWindow(windowId, slotId, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close the specified inventory window
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="windowId">Window ID</param>
|
||||||
|
/// <returns>TRUE if the window was successfully closed</returns>
|
||||||
|
public bool CloseInventory(int windowId)
|
||||||
|
{
|
||||||
|
if (windowId != 0 && inventories.ContainsKey(windowId))
|
||||||
|
{
|
||||||
|
inventories.Remove(windowId);
|
||||||
|
return handler.SendCloseWindow(windowId);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1320,10 +1346,10 @@ namespace MinecraftClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="EntityID"></param>
|
/// <param name="EntityID"></param>
|
||||||
/// <param name="type">0: interact, 1: attack, 2: interact at</param>
|
/// <param name="type">0: interact, 1: attack, 2: interact at</param>
|
||||||
/// <returns></returns>
|
/// <returns>TRUE if interaction succeeded</returns>
|
||||||
public bool InteractEntity(int EntityID, int type)
|
public bool InteractEntity(int EntityID, int type)
|
||||||
{
|
{
|
||||||
return handler.SendInteractEntityPacket(EntityID, type);
|
return handler.SendInteractEntity(EntityID, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1342,7 +1368,7 @@ namespace MinecraftClient
|
||||||
/// Change active slot in the player inventory
|
/// Change active slot in the player inventory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slot">Slot to activate (0 to 8)</param>
|
/// <param name="slot">Slot to activate (0 to 8)</param>
|
||||||
/// <returns></returns>
|
/// <returns>TRUE if the slot was changed</returns>
|
||||||
public bool ChangeSlot(short slot)
|
public bool ChangeSlot(short slot)
|
||||||
{
|
{
|
||||||
if (slot >= 0 && slot <= 8)
|
if (slot >= 0 && slot <= 8)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Text;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
using MinecraftClient.Crypto;
|
using MinecraftClient.Crypto;
|
||||||
|
using MinecraftClient.Inventory;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
|
|
@ -319,6 +320,40 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return ReadNextNbt(cache, true);
|
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>
|
/// <summary>
|
||||||
/// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
|
/// Read an uncompressed Named Binary Tag blob and remove it from the cache (internal)
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Build an integer for sending over the network
|
/// Build an integer for sending over the network
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -412,6 +604,30 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return bytes.ToArray();
|
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>
|
/// <summary>
|
||||||
/// Get byte array representing a short
|
/// Get byte array representing a short
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -424,6 +640,18 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return theShort;
|
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>
|
/// <summary>
|
||||||
/// Get byte array representing a double
|
/// Get byte array representing a double
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -448,7 +676,6 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return theFloat;
|
return theFloat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get byte array with length information prepended to it
|
/// Get byte array with length information prepended to it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -473,7 +700,6 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
public byte[] GetString(string text)
|
public byte[] GetString(string text)
|
||||||
{
|
{
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(text);
|
byte[] bytes = Encoding.UTF8.GetBytes(text);
|
||||||
|
|
||||||
return ConcatBytes(GetVarInt(bytes.Length), bytes);
|
return ConcatBytes(GetVarInt(bytes.Length), bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,6 +725,42 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return locationBytes;
|
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>
|
/// <summary>
|
||||||
/// Easily append several byte arrays
|
/// Easily append several byte arrays
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
HeldItemChange,
|
HeldItemChange,
|
||||||
InteractEntity,
|
InteractEntity,
|
||||||
UseItem,
|
UseItem,
|
||||||
|
ClickWindow,
|
||||||
|
CloseWindow,
|
||||||
PlayerBlockPlacement
|
PlayerBlockPlacement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using MinecraftClient.Crypto;
|
||||||
using MinecraftClient.Proxy;
|
using MinecraftClient.Proxy;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
|
using MinecraftClient.Inventory;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol.Handlers
|
namespace MinecraftClient.Protocol.Handlers
|
||||||
{
|
{
|
||||||
|
|
@ -641,22 +642,32 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
return false; //Currently not implemented
|
return false; //Currently not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SendInteractEntityPacket(int EntityID, int type)
|
public bool SendInteractEntity(int EntityID, int type)
|
||||||
{
|
{
|
||||||
return false; //Currently not implemented
|
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
|
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
|
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
|
return false; //Currently not implemented
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
private bool autocomplete_received = false;
|
private bool autocomplete_received = false;
|
||||||
private int autocomplete_transaction_id = 0;
|
private int autocomplete_transaction_id = 0;
|
||||||
private readonly List<string> autocomplete_result = new List<string>();
|
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 bool login_phase = true;
|
||||||
private int protocolversion;
|
private int protocolversion;
|
||||||
private int currentDimension;
|
private int currentDimension;
|
||||||
|
|
@ -497,23 +498,27 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case PacketIncomingType.OpenWindow:
|
case PacketIncomingType.OpenWindow:
|
||||||
if (handler.GetInventoryEnabled())
|
if (handler.GetInventoryEnabled())
|
||||||
{
|
{
|
||||||
if (protocolversion < MC114Version) // packet changed at 1.14
|
if (protocolversion < MC114Version)
|
||||||
{
|
{
|
||||||
|
// MC 1.13 or lower
|
||||||
byte windowID = dataTypes.ReadNextByte(packetData);
|
byte windowID = dataTypes.ReadNextByte(packetData);
|
||||||
string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper();
|
string type = dataTypes.ReadNextString(packetData).Replace("minecraft:", "").ToUpper();
|
||||||
ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
|
ContainerTypeOld inventoryType = (ContainerTypeOld)Enum.Parse(typeof(ContainerTypeOld), type);
|
||||||
string title = dataTypes.ReadNextString(packetData);
|
string title = dataTypes.ReadNextString(packetData);
|
||||||
byte slots = dataTypes.ReadNextByte(packetData);
|
byte slots = dataTypes.ReadNextByte(packetData);
|
||||||
Container inventory = new Container(windowID, inventoryType, title);
|
Container inventory = new Container(windowID, inventoryType, ChatParser.ParseText(title));
|
||||||
handler.OnInventoryOpen(inventory);
|
window_actions[windowID] = 0;
|
||||||
|
handler.OnInventoryOpen(windowID, inventory);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int WindowID = dataTypes.ReadNextVarInt(packetData);
|
// MC 1.14 or greater
|
||||||
int WindowType = dataTypes.ReadNextVarInt(packetData);
|
int windowID = dataTypes.ReadNextVarInt(packetData);
|
||||||
|
int windowType = dataTypes.ReadNextVarInt(packetData);
|
||||||
string title = dataTypes.ReadNextString(packetData);
|
string title = dataTypes.ReadNextString(packetData);
|
||||||
Container inventory = new Container(WindowID, WindowType, title);
|
Container inventory = new Container(windowID, windowType, ChatParser.ParseText(title));
|
||||||
handler.OnInventoryOpen(inventory);
|
window_actions[windowID] = 0;
|
||||||
|
handler.OnInventoryOpen(windowID, inventory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -521,103 +526,32 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
if (handler.GetInventoryEnabled())
|
if (handler.GetInventoryEnabled())
|
||||||
{
|
{
|
||||||
byte windowID = dataTypes.ReadNextByte(packetData);
|
byte windowID = dataTypes.ReadNextByte(packetData);
|
||||||
|
window_actions[windowID] = 0;
|
||||||
handler.OnInventoryClose(windowID);
|
handler.OnInventoryClose(windowID);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PacketIncomingType.WindowItems:
|
case PacketIncomingType.WindowItems:
|
||||||
if (handler.GetInventoryEnabled())
|
if (handler.GetInventoryEnabled())
|
||||||
{
|
{
|
||||||
// MC 1.12.2 or lower
|
byte windowId = dataTypes.ReadNextByte(packetData);
|
||||||
if (protocolversion < MC113Version)
|
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);
|
Item item = dataTypes.ReadNextItemSlot(packetData);
|
||||||
short elements = dataTypes.ReadNextShort(packetData);
|
if (item != null)
|
||||||
Dictionary<int, Item> itemsList = new Dictionary<int, Item>(); // index is SlotID
|
inventorySlots[slotId] = item;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
handler.OnWindowItems(windowId, inventorySlots);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PacketIncomingType.SetSlot:
|
case PacketIncomingType.SetSlot:
|
||||||
if(handler.GetInventoryEnabled())
|
if (handler.GetInventoryEnabled())
|
||||||
{
|
{
|
||||||
// MC 1.12.2 or lower
|
byte windowID = dataTypes.ReadNextByte(packetData);
|
||||||
if (protocolversion < MC113Version)
|
short slotID = dataTypes.ReadNextShort(packetData);
|
||||||
{
|
Item item = dataTypes.ReadNextItemSlot(packetData);
|
||||||
byte WindowID = dataTypes.ReadNextByte(packetData);
|
handler.OnSetSlot(windowID, slotID, item);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PacketIncomingType.ResourcePackSend:
|
case PacketIncomingType.ResourcePackSend:
|
||||||
|
|
@ -1285,7 +1219,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
/// <param name="EntityID"></param>
|
/// <param name="EntityID"></param>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public bool SendInteractEntityPacket(int EntityID, int type)
|
public bool SendInteractEntity(int EntityID, int type)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -1300,7 +1234,7 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
catch (ObjectDisposedException) { return false; }
|
catch (ObjectDisposedException) { return false; }
|
||||||
}
|
}
|
||||||
// TODO: Interact at block location (e.g. chest minecart)
|
// 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
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -1318,12 +1252,12 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
catch (System.IO.IOException) { return false; }
|
catch (System.IO.IOException) { return false; }
|
||||||
catch (ObjectDisposedException) { 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SendUseItemPacket(int hand)
|
public bool SendUseItem(int hand)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -1370,5 +1304,45 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
catch (System.IO.IOException) { return false; }
|
catch (System.IO.IOException) { return false; }
|
||||||
catch (ObjectDisposedException) { 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case PacketOutgoingType.HeldItemChange: return 0x17;
|
case PacketOutgoingType.HeldItemChange: return 0x17;
|
||||||
case PacketOutgoingType.InteractEntity: return 0x02;
|
case PacketOutgoingType.InteractEntity: return 0x02;
|
||||||
case PacketOutgoingType.TeleportConfirm: throw new InvalidOperationException("Teleport confirm is not supported in protocol " + protocol);
|
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
|
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.TeleportConfirm: return 0x00;
|
||||||
case PacketOutgoingType.HeldItemChange: return 0x17;
|
case PacketOutgoingType.HeldItemChange: return 0x17;
|
||||||
case PacketOutgoingType.InteractEntity: return 0x0A;
|
case PacketOutgoingType.InteractEntity: return 0x0A;
|
||||||
|
case PacketOutgoingType.ClickWindow: return 0x07;
|
||||||
|
case PacketOutgoingType.CloseWindow: return 0x08;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12
|
else if (protocol <= Protocol18Handler.MC112Version) // MC 1.12
|
||||||
|
|
@ -342,6 +346,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||||
case PacketOutgoingType.HeldItemChange: return 0x1A;
|
case PacketOutgoingType.HeldItemChange: return 0x1A;
|
||||||
case PacketOutgoingType.InteractEntity: return 0x0B;
|
case PacketOutgoingType.InteractEntity: return 0x0B;
|
||||||
|
case PacketOutgoingType.ClickWindow: return 0x07;
|
||||||
|
case PacketOutgoingType.CloseWindow: return 0x08;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (protocol <= Protocol18Handler.MC1122Version) // 1.12.2
|
else if (protocol <= Protocol18Handler.MC1122Version) // 1.12.2
|
||||||
|
|
@ -360,6 +366,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
case PacketOutgoingType.TeleportConfirm: return 0x00;
|
||||||
case PacketOutgoingType.HeldItemChange: return 0x1F;
|
case PacketOutgoingType.HeldItemChange: return 0x1F;
|
||||||
case PacketOutgoingType.InteractEntity: return 0x0A;
|
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
|
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.TeleportConfirm: return 0x00;
|
||||||
case PacketOutgoingType.HeldItemChange: return 0x21;
|
case PacketOutgoingType.HeldItemChange: return 0x21;
|
||||||
case PacketOutgoingType.InteractEntity: return 0x0D;
|
case PacketOutgoingType.InteractEntity: return 0x0D;
|
||||||
|
case PacketOutgoingType.ClickWindow: return 0x08;
|
||||||
|
case PacketOutgoingType.CloseWindow: return 0x09;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // MC 1.14 to 1.15
|
else // MC 1.14 to 1.15
|
||||||
|
|
@ -398,6 +408,8 @@ namespace MinecraftClient.Protocol.Handlers
|
||||||
case PacketOutgoingType.InteractEntity: return 0x0E;
|
case PacketOutgoingType.InteractEntity: return 0x0E;
|
||||||
case PacketOutgoingType.UseItem: return 0x2D;
|
case PacketOutgoingType.UseItem: return 0x2D;
|
||||||
case PacketOutgoingType.PlayerBlockPlacement: return 0x2C;
|
case PacketOutgoingType.PlayerBlockPlacement: return 0x2C;
|
||||||
|
case PacketOutgoingType.ClickWindow: return 0x09;
|
||||||
|
case PacketOutgoingType.CloseWindow: return 0x0A;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MinecraftClient.Crypto;
|
using MinecraftClient.Crypto;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
|
using MinecraftClient.Inventory;
|
||||||
|
|
||||||
namespace MinecraftClient.Protocol
|
namespace MinecraftClient.Protocol
|
||||||
{
|
{
|
||||||
|
|
@ -98,7 +99,7 @@ namespace MinecraftClient.Protocol
|
||||||
/// <param name="EntityID">Entity ID to interact with</param>
|
/// <param name="EntityID">Entity ID to interact with</param>
|
||||||
/// <param name="type">Type of interaction (0: interact, 1: attack, 2: interact at)</param>
|
/// <param name="type">Type of interaction (0: interact, 1: attack, 2: interact at)</param>
|
||||||
/// <returns>True if packet was successfully sent</returns>
|
/// <returns>True if packet was successfully sent</returns>
|
||||||
bool SendInteractEntityPacket(int EntityID, int type);
|
bool SendInteractEntity(int EntityID, int type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Send an entity interaction packet to the server.
|
/// 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="Z">Z coordinate for "interact at"</param>
|
||||||
/// <param name="hand">Player hand (0: main hand, 1: off hand)</param>
|
/// <param name="hand">Player hand (0: main hand, 1: off hand)</param>
|
||||||
/// <returns>True if packet was successfully sent</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Send an entity interaction packet to the server.
|
/// 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="Y">Y coordinate for "interact at"</param>
|
||||||
/// <param name="Z">Z coordinate for "interact at"</param>
|
/// <param name="Z">Z coordinate for "interact at"</param>
|
||||||
/// <returns>True if packet was successfully sent</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Send a use item packet to the server
|
/// Send a use item packet to the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hand">0: main hand, 1: off hand</param>
|
/// <param name="hand">0: main hand, 1: off hand</param>
|
||||||
/// <returns>True if packet was successfully sent</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Send player block placement packet to the server
|
/// Send player block placement packet to the server
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,12 @@ namespace MinecraftClient.Protocol
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when an inventory is opened
|
/// Called when an inventory is opened
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnInventoryOpen(Container inventory);
|
void OnInventoryOpen(int inventoryID, Container inventory);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when an inventory is closed
|
/// Called when an inventory is closed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OnInventoryClose(byte inventoryID);
|
void OnInventoryClose(int inventoryID);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the player respawns, which happens on login, respawn and world change.
|
/// Called when the player respawns, which happens on login, respawn and world change.
|
||||||
|
|
@ -199,26 +199,17 @@ namespace MinecraftClient.Protocol
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when inventory items have been received
|
/// Called when inventory items have been received
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Inventory type</param>
|
/// <param name="inventoryID">Inventory ID</param>
|
||||||
/// <param name="itemList">Item list</param>
|
/// <param name="itemList">Item list</param>
|
||||||
void OnWindowItems(int type, Dictionary<int, MinecraftClient.Inventory.Item> itemList);
|
void OnWindowItems(byte inventoryID, 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);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a single slot has been updated inside an inventory
|
/// Called when a single slot has been updated inside an inventory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="WindowID">Inventory ID</param>
|
/// <param name="inventoryID">Window ID</param>
|
||||||
/// <param name="SlotID">Slot ID</param>
|
/// <param name="slotID">Slot ID</param>
|
||||||
/// <param name="ItemID">Item ID</param>
|
/// <param name="item">Item (may be null for empty slot)</param>
|
||||||
/// <param name="Count">Item Count</param>
|
void OnSetSlot(byte inventoryID, short slotID, Item item);
|
||||||
/// <param name="NBT">Item Metadata</param>
|
|
||||||
void OnSetSlot(byte WindowID, short SlotID, int ItemID, byte Count, Dictionary<string, object> NBT);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the Player entity ID has been received from the server
|
/// Called when the Player entity ID has been received from the server
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue