diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index d608cc5d..2f517021 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1243,8 +1243,7 @@ namespace MinecraftClient if (inventories.ContainsKey(windowId) && inventories[windowId].Items.ContainsKey(slotId)) item = inventories[windowId].Items[slotId]; - // Inventory update must be after sending packet - bool result = handler.SendWindowAction(windowId, slotId, action, item, inventories[windowId].Items, inventories[windowId].StateID); + List> changedSlots = new List>(); // List // Update our inventory base on action type var inventory = GetInventory(windowId); @@ -1295,6 +1294,8 @@ namespace MinecraftClient inventory.Items[slotId] = playerInventory.Items[-1]; playerInventory.Items.Remove(-1); } + + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } else { @@ -1307,6 +1308,8 @@ namespace MinecraftClient // Put target slot item to cursor playerInventory.Items[-1] = inventory.Items[slotId]; inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)slotId, null)); } } break; @@ -1385,6 +1388,7 @@ namespace MinecraftClient } } } + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); break; case WindowActionType.ShiftClick: if (slotId == 0) break; @@ -1412,6 +1416,7 @@ namespace MinecraftClient // If hotbar already have same item, will put on it first until every stack are full // If no more same item , will put on the first empty slot (smaller slot id) // If inventory full, item will not move + int itemCount = inventory.Items[slotId].Count; if (slotId <= upperEndSlot) { // Clicked slot is on upper side inventory, put it to hotbar @@ -1431,11 +1436,16 @@ namespace MinecraftClient // Can fit into the stack inventory.Items[_item.Key].Count += inventory.Items[slotId].Count; inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key])); + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } else { inventory.Items[slotId].Count -= spaceLeft; inventory.Items[_item.Key].Count = inventory.Items[_item.Key].Type.StackCount(); + + changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key])); } } } @@ -1454,6 +1464,13 @@ namespace MinecraftClient var itemTmp = inventory.Items[slotId]; inventory.Items[emptySlot] = new Item(itemTmp.Type, itemTmp.Count, itemTmp.NBT); inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)emptySlot, inventory.Items[emptySlot])); + changedSlots.Add(new Tuple((short)slotId, null)); + } + else if (inventory.Items[slotId].Count != itemCount) + { + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } } } @@ -1477,11 +1494,16 @@ namespace MinecraftClient // Can fit into the stack inventory.Items[_item.Key].Count += inventory.Items[slotId].Count; inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key])); + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } else { inventory.Items[slotId].Count -= spaceLeft; inventory.Items[_item.Key].Count = inventory.Items[_item.Key].Type.StackCount(); + + changedSlots.Add(new Tuple((short)_item.Key, inventory.Items[_item.Key])); } } } @@ -1501,6 +1523,13 @@ namespace MinecraftClient var itemTmp = inventory.Items[slotId]; inventory.Items[emptySlot] = new Item(itemTmp.Type, itemTmp.Count, itemTmp.NBT); inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)emptySlot, inventory.Items[emptySlot])); + changedSlots.Add(new Tuple((short)slotId, null)); + } + else if (inventory.Items[slotId].Count != itemCount) + { + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } } } @@ -1512,14 +1541,18 @@ namespace MinecraftClient if (inventory.Items[slotId].Count <= 0) inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); break; case WindowActionType.DropItemStack: inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)slotId, null)); break; } } - return result; + return handler.SendWindowAction(windowId, slotId, action, item, changedSlots, inventories[windowId].StateID); } /// @@ -2046,8 +2079,11 @@ namespace MinecraftClient /// Window ID /// Slot ID /// Item (may be null for empty slot) - public void OnSetSlot(byte inventoryID, short slotID, Item item) + public void OnSetSlot(byte inventoryID, short slotID, Item item, int stateId) { + if (inventories.ContainsKey(inventoryID)) + inventories[inventoryID].StateID = stateId; + // Handle inventoryID -2 - Add item to player inventory without animation if (inventoryID == 254) inventoryID = 0; diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index c7e0b9ef..0f30bfce 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -720,7 +720,7 @@ namespace MinecraftClient.Protocol.Handlers return false; //Currently not implemented } - public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, Dictionary Items, int stateId) + public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId) { return false; //Currently not implemented } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index abc4f2bb..cd8f831e 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -917,7 +917,7 @@ namespace MinecraftClient.Protocol.Handlers short slotID = dataTypes.ReadNextShort(packetData); Item item = dataTypes.ReadNextItemSlot(packetData, itemPalette); - handler.OnSetSlot(windowID, slotID, item); + handler.OnSetSlot(windowID, slotID, item, stateId); } break; case PacketTypesIn.WindowConfirmation: @@ -1967,7 +1967,7 @@ namespace MinecraftClient.Protocol.Handlers catch (ObjectDisposedException) { return false; } } - public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, Dictionary items, int stateId) + public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId) { try { @@ -2004,51 +2004,48 @@ namespace MinecraftClient.Protocol.Handlers List packet = new List(); - log.Info("Window id: " + windowId + " - State id: " + stateId + " - Slot id: " + slotId + " - Mode: " + mode); - log.Info("Bytes > " + (byte)windowId + " - State id: " + dataTypes.ByteArrayToString(dataTypes.GetVarInt(stateId)) + " - Slot id: " + dataTypes.ByteArrayToString(dataTypes.GetVarInt(slotId)) + " - Mode: " + dataTypes.ByteArrayToString(dataTypes.GetVarInt(mode))); - - packet.Add((byte)windowId); + packet.Add((byte)windowId); // Window ID // 1.18+ if (protocolversion >= MC1181Version) { - packet.AddRange(dataTypes.GetVarInt(stateId)); - packet.AddRange(dataTypes.GetShort((short)slotId)); + packet.AddRange(dataTypes.GetVarInt(stateId)); // State ID + packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID } // 1.17.1 else if (protocolversion == MC1171Version) { - packet.AddRange(dataTypes.GetShort((short)slotId)); - packet.AddRange(dataTypes.GetVarInt(stateId)); + packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID + packet.AddRange(dataTypes.GetVarInt(stateId)); // State ID } // Older else { - packet.AddRange(dataTypes.GetShort((short)slotId)); + packet.AddRange(dataTypes.GetShort((short)slotId)); // Slot ID } - packet.Add(button); - if (protocolversion < MC117Version) packet.AddRange(dataTypes.GetShort(actionNumber)); + packet.Add(button); // Button + + if (protocolversion < MC117Version) + packet.AddRange(dataTypes.GetShort(actionNumber)); + if (protocolversion >= MC19Version) - packet.AddRange(dataTypes.GetVarInt(mode)); + packet.AddRange(dataTypes.GetVarInt(mode)); // Mode else packet.Add(mode); // 1.17+ if (protocolversion >= MC117Version) { - byte[] arrayOfSlots = dataTypes.GetSlotsArray(items, itemPalette); - - log.Info("Length: " + dataTypes.ByteArrayToString(dataTypes.GetVarInt(arrayOfSlots.Length)) + " (" + arrayOfSlots.Length + ")"); - log.Info("Array: " + dataTypes.ByteArrayToString(arrayOfSlots)); - - packet.AddRange(dataTypes.GetVarInt(arrayOfSlots.Length)); - packet.AddRange(arrayOfSlots); + packet.AddRange(dataTypes.GetVarInt(changedSlots.Count)); // Length of the array + foreach (var slot in changedSlots) + { + packet.AddRange(dataTypes.GetShort(slot.Item1)); // slot ID + packet.AddRange(dataTypes.GetItemSlot(slot.Item2, itemPalette)); // slot Data + } } packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); // Carried item (Clicked item) - log.Info("Packet data: " + dataTypes.ByteArrayToString(packet.ToArray())); - SendPacket(PacketTypesOut.ClickWindow, packet); return true; } diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index 97aaca35..487e8190 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -162,10 +162,12 @@ namespace MinecraftClient.Protocol /// /// Id of the window being clicked /// Id of the clicked slot - /// Action to perform + /// Action to perform /// Item in the clicked slot + /// Slots that have been changed in this event: List + /// Inventory's stateId /// True if packet was successfully sent - bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, Dictionary Items, int stateId); + bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId); /// /// Request Creative Mode item creation into regular/survival Player Inventory diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 4ca8faf9..3da38c94 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -272,7 +272,8 @@ namespace MinecraftClient.Protocol /// Window ID /// Slot ID /// Item (may be null for empty slot) - void OnSetSlot(byte inventoryID, short slotID, Item item); + /// State ID + void OnSetSlot(byte inventoryID, short slotID, Item item, int stateId); /// /// Called when player health or hunger changed.