From 65bcd83330e9852110b33a424fb34e9d35e0d13b Mon Sep 17 00:00:00 2001 From: BruceChen Date: Thu, 8 Sep 2022 14:04:23 +0800 Subject: [PATCH] Shift click support --- MinecraftClient/Commands/Inventory.cs | 86 ++- MinecraftClient/McClient.cs | 681 +++++++++++++++--- .../Protocol/Handlers/DataTypes.cs | 8 +- .../Protocol/Handlers/Protocol16.cs | 2 +- .../Protocol/Handlers/Protocol18.cs | 4 +- MinecraftClient/Protocol/IMinecraftCom.cs | 2 +- .../Protocol/IMinecraftComHandler.cs | 2 +- MinecraftClient/Resources/lang/en.ini | 1 + 8 files changed, 631 insertions(+), 155 deletions(-) diff --git a/MinecraftClient/Commands/Inventory.cs b/MinecraftClient/Commands/Inventory.cs index b164662a..27562075 100644 --- a/MinecraftClient/Commands/Inventory.cs +++ b/MinecraftClient/Commands/Inventory.cs @@ -117,52 +117,72 @@ namespace MinecraftClient.Commands case "click": if (args.Length >= 3) { - int slot = int.Parse(args[2]); - WindowActionType actionType = WindowActionType.LeftClick; - string keyName = "cmd.inventory.left"; - if (args.Length >= 4) + try { - string b = args[3]; - if (b.ToLower()[0] == 'r') + int slot = int.Parse(args[2]); + WindowActionType actionType = WindowActionType.LeftClick; + string keyName = "cmd.inventory.left"; + if (args.Length >= 4) { - actionType = WindowActionType.RightClick; - keyName = "cmd.inventory.right"; - } - if (b.ToLower()[0] == 'm') - { - actionType = WindowActionType.MiddleClick; - keyName = "cmd.inventory.middle"; + string b = args[3]; + if (b.ToLower()[0] == 'r') + { + actionType = WindowActionType.RightClick; + keyName = "cmd.inventory.right"; + } + if (b.ToLower()[0] == 'm') + { + actionType = WindowActionType.MiddleClick; + keyName = "cmd.inventory.middle"; + } } + handler.DoWindowAction(inventoryId, slot, actionType); + return Translations.Get("cmd.inventory.clicking", Translations.Get(keyName), slot, inventoryId); } - handler.DoWindowAction(inventoryId, slot, actionType); - return Translations.Get("cmd.inventory.clicking", Translations.Get(keyName), slot, inventoryId); + catch (FormatException) { return GetCmdDescTranslated(); } + } + else return CmdUsage; + case "shiftclick": + if (args.Length >= 3) + { + try + { + int slot = int.Parse(args[2]); + handler.DoWindowAction(inventoryId, slot, WindowActionType.ShiftClick); + return Translations.Get("cmd.inventory.shiftclick", slot, inventoryId); + } + catch (FormatException) { return GetCmdDescTranslated(); } } else return CmdUsage; case "drop": if (args.Length >= 3) { - int slot = int.Parse(args[2]); - // check item exist - if (!handler.GetInventory(inventoryId).Items.ContainsKey(slot)) - return Translations.Get("cmd.inventory.no_item", slot); - WindowActionType actionType = WindowActionType.DropItem; - if (args.Length >= 4) + try { - if (args[3].ToLower() == "all") + int slot = int.Parse(args[2]); + // check item exist + if (!handler.GetInventory(inventoryId).Items.ContainsKey(slot)) + return Translations.Get("cmd.inventory.no_item", slot); + WindowActionType actionType = WindowActionType.DropItem; + if (args.Length >= 4) { - actionType = WindowActionType.DropItemStack; + if (args[3].ToLower() == "all") + { + actionType = WindowActionType.DropItemStack; + } + } + if (handler.DoWindowAction(inventoryId, slot, actionType)) + { + if (actionType == WindowActionType.DropItemStack) + return Translations.Get("cmd.inventory.drop_stack", slot); + else return Translations.Get("cmd.inventory.drop", slot); + } + else + { + return "Failed"; } } - if (handler.DoWindowAction(inventoryId, slot, actionType)) - { - if (actionType == WindowActionType.DropItemStack) - return Translations.Get("cmd.inventory.drop_stack", slot); - else return Translations.Get("cmd.inventory.drop", slot); - } - else - { - return "Failed"; - } + catch (FormatException) { return GetCmdDescTranslated(); } } else return GetCmdDescTranslated(); default: diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 53a92a89..65722866 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -1027,7 +1027,7 @@ namespace MinecraftClient /// /// Window ID of the requested inventory /// Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID) - public Container GetInventory(int inventoryID) + public Container? GetInventory(int inventoryID) { if (InvokeRequired) return InvokeOnMainThread(() => GetInventory(inventoryID)); @@ -1043,7 +1043,7 @@ namespace MinecraftClient /// Item Dictionary indexed by Slot ID (Check wiki.vg for slot ID) public Container GetPlayerInventory() { - return GetInventory(0); + return GetInventory(0)!; } /// @@ -1277,6 +1277,63 @@ namespace MinecraftClient return InvokeOnMainThread(() => handler.SendUseItem(0, this.sequenceId)); } + /// + /// Try to merge a slot + /// + /// The container where the item is located + /// Items to be processed + /// The ID of the slot of the item to be processed + /// The slot that was put down + /// The ID of the slot being put down + /// Record changes + /// Whether to fully merge + private static bool TryMergeSlot(Container inventory, Item item, int slotId, Item curItem, int curId, List> changedSlots) + { + int spaceLeft = curItem.Type.StackCount() - curItem.Count; + if (curItem.Type == item!.Type && spaceLeft > 0) + { + // Put item on that stack + if (item.Count <= spaceLeft) + { + // Can fit into the stack + item.Count = 0; + curItem.Count += item.Count; + + changedSlots.Add(new Tuple((short)curId, curItem)); + changedSlots.Add(new Tuple((short)slotId, null)); + + inventory.Items.Remove(slotId); + return true; + } + else + { + item.Count -= spaceLeft; + curItem.Count += spaceLeft; + + changedSlots.Add(new Tuple((short)curId, curItem)); + } + } + return false; + } + + /// + /// Store items in a new slot + /// + /// The container where the item is located + /// Items to be processed + /// The ID of the slot of the item to be processed + /// ID of the new slot + /// Record changes + private static void StoreInNewSlot(Container inventory, Item item, int slotId, int newSlotId, List> changedSlots) + { + Item newItem = new(item.Type, item.Count, item.NBT); + inventory.Items[newSlotId] = newItem; + inventory.Items.Remove(slotId); + + changedSlots.Add(new Tuple((short)newSlotId, newItem)); + changedSlots.Add(new Tuple((short)slotId, null)); + } + /// /// Click a slot in the specified window /// @@ -1286,15 +1343,15 @@ namespace MinecraftClient if (InvokeRequired) return InvokeOnMainThread(() => DoWindowAction(windowId, slotId, action)); - Item item = null; + Item? item = null; if (inventories.ContainsKey(windowId) && inventories[windowId].Items.ContainsKey(slotId)) item = inventories[windowId].Items[slotId]; - List> changedSlots = new List>(); // List + List> changedSlots = new(); // List // Update our inventory base on action type - var inventory = GetInventory(windowId); - var playerInventory = GetInventory(0); + Container inventory = GetInventory(windowId)!; + Container playerInventory = GetInventory(0)!; if (inventory != null) { switch (action) @@ -1343,9 +1400,9 @@ namespace MinecraftClient } if (inventory.Items.ContainsKey(slotId)) - changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); else - changedSlots.Add(new Tuple((short)slotId, null)); + changedSlots.Add(new Tuple((short)slotId, null)); } else { @@ -1359,7 +1416,7 @@ namespace MinecraftClient playerInventory.Items[-1] = inventory.Items[slotId]; inventory.Items.Remove(slotId); - changedSlots.Add(new Tuple((short)slotId, null)); + changedSlots.Add(new Tuple((short)slotId, null)); } } break; @@ -1439,30 +1496,454 @@ namespace MinecraftClient } } if (inventory.Items.ContainsKey(slotId)) - changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); else - changedSlots.Add(new Tuple((short)slotId, null)); + changedSlots.Add(new Tuple((short)slotId, null)); break; case WindowActionType.ShiftClick: if (slotId == 0) break; - if (inventory.Items.ContainsKey(slotId)) + if (item != null) { /* Target slot have item */ + bool lower2upper = false, upper2backpack = false, backpack2hotbar = false; // mutual exclusion + bool hotbarFirst = true; // Used when upper2backpack = true int upperStartSlot = 9; int upperEndSlot = 35; + int lowerStartSlot = 36; switch (inventory.Type) { case ContainerType.PlayerInventory: - upperStartSlot = 9; - upperEndSlot = 35; + if (slotId >= 0 && slotId <= 8 || slotId == 45) + { + if (slotId != 0) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 9; + } + else if (item != null && false /* Check if wearable */) + { + lower2upper = true; + // upperStartSlot = ?; + // upperEndSlot = ?; + // Todo: Distinguish the type of equipment + } + else + { + if (slotId >= 9 && slotId <= 35) + { + backpack2hotbar = true; + lowerStartSlot = 36; + } + else + { + lower2upper = true; + upperStartSlot = 9; + upperEndSlot = 35; + } + } + break; + case ContainerType.Generic_9x1: + if (slotId >= 0 && slotId <= 8) + { + upper2backpack = true; + lowerStartSlot = 9; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 8; + } + break; + case ContainerType.Generic_9x2: + if (slotId >= 0 && slotId <= 17) + { + upper2backpack = true; + lowerStartSlot = 18; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 17; + } + break; + case ContainerType.Generic_9x3: + case ContainerType.ShulkerBox: + if (slotId >= 0 && slotId <= 26) + { + upper2backpack = true; + lowerStartSlot = 27; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 26; + } + break; + case ContainerType.Generic_9x4: + if (slotId >= 0 && slotId <= 35) + { + upper2backpack = true; + lowerStartSlot = 36; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 35; + } + break; + case ContainerType.Generic_9x5: + if (slotId >= 0 && slotId <= 44) + { + upper2backpack = true; + lowerStartSlot = 45; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 44; + } + break; + case ContainerType.Generic_9x6: + if (slotId >= 0 && slotId <= 53) + { + upper2backpack = true; + lowerStartSlot = 54; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 53; + } + break; + case ContainerType.Generic_3x3: + if (slotId >= 0 && slotId <= 8) + { + upper2backpack = true; + lowerStartSlot = 9; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 8; + } + break; + case ContainerType.Anvil: + if (slotId >= 0 && slotId <= 2) + { + if (slotId >= 0 && slotId <= 1) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 3; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 1; + } + break; + case ContainerType.Beacon: + if (slotId == 0) + { + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 1; + } + else if (item != null && item.Count == 1 && (item.Type == ItemType.NetheriteIngot || + item.Type == ItemType.Emerald || item.Type == ItemType.Diamond || item.Type == ItemType.GoldIngot || + item.Type == ItemType.IronIngot) && !inventory.Items.ContainsKey(0)) + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 0; + } + else + { + if (slotId >= 1 && slotId <= 27) + { + backpack2hotbar = true; + lowerStartSlot = 28; + } + else + { + lower2upper = true; + upperStartSlot = 1; + upperEndSlot = 27; + } + } + break; + case ContainerType.BlastFurnace: + case ContainerType.Furnace: + case ContainerType.Smoker: + if (slotId >= 0 && slotId <= 2) + { + if (slotId >= 0 && slotId <= 1) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 3; + } + else if (item != null && false /* Check if it can be burned */) + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 0; + } + else + { + if (slotId >= 3 && slotId <= 29) + { + backpack2hotbar = true; + lowerStartSlot = 30; + } + else + { + lower2upper = true; + upperStartSlot = 3; + upperEndSlot = 29; + } + } + break; + case ContainerType.BrewingStand: + if (slotId >= 0 && slotId <= 3) + { + upper2backpack = true; + lowerStartSlot = 5; + } + else if (item != null && item.Type == ItemType.BlazePowder) + { + lower2upper = true; + if (!inventory.Items.ContainsKey(4) || inventory.Items[4].Count < 64) + upperStartSlot = upperEndSlot = 4; + else + upperStartSlot = upperEndSlot = 3; + } + else if (item != null && false /* Check if it can be used for alchemy */) + { + lower2upper = true; + upperStartSlot = upperEndSlot = 3; + } + else if (item != null && (item.Type == ItemType.Potion || item.Type == ItemType.GlassBottle)) + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 2; + } + else + { + if (slotId >= 5 && slotId <= 31) + { + backpack2hotbar = true; + lowerStartSlot = 32; + } + else + { + lower2upper = true; + upperStartSlot = 5; + upperEndSlot = 31; + } + } break; case ContainerType.Crafting: - upperStartSlot = 1; - upperEndSlot = 9; + if (slotId >= 0 && slotId <= 9) + { + if (slotId >= 1 && slotId <= 9) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 10; + } + else + { + lower2upper = true; + upperStartSlot = 1; + upperEndSlot = 9; + } break; + case ContainerType.Enchantment: + if (slotId >= 0 && slotId <= 1) + { + upper2backpack = true; + lowerStartSlot = 5; + } + else if (item != null && item.Type == ItemType.LapisLazuli) + { + lower2upper = true; + upperStartSlot = upperEndSlot = 1; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 0; + } + break; + case ContainerType.Grindstone: + if (slotId >= 0 && slotId <= 2) + { + if (slotId >= 0 && slotId <= 1) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 3; + } + else if (item != null && false /* Check */) + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 1; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 1; + } + break; + case ContainerType.Hopper: + if (slotId >= 0 && slotId <= 4) + { + upper2backpack = true; + lowerStartSlot = 5; + } + else + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 4; + } + break; + case ContainerType.Lectern: + break; + case ContainerType.Loom: + if (slotId >= 0 && slotId <= 3) + { + if (slotId >= 0 && slotId <= 5) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 4; + } + else if (item != null && false /* Check for availability for staining */) + { + lower2upper = true; + // upperStartSlot = ?; + // upperEndSlot = ?; + } + else + { + if (slotId >= 4 && slotId <= 30) + { + backpack2hotbar = true; + lowerStartSlot = 31; + } + else + { + lower2upper = true; + upperStartSlot = 4; + upperEndSlot = 30; + } + } + break; + case ContainerType.Merchant: + if (slotId >= 0 && slotId <= 2) + { + if (slotId >= 0 && slotId <= 1) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 3; + } + else if (item != null && false /* Check if it is available for trading */) + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 1; + } + else + { + if (slotId >= 3 && slotId <= 29) + { + backpack2hotbar = true; + lowerStartSlot = 30; + } + else + { + lower2upper = true; + upperStartSlot = 3; + upperEndSlot = 29; + } + } + break; + case ContainerType.Cartography: + if (slotId >= 0 && slotId <= 2) + { + if (slotId >= 0 && slotId <= 1) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 3; + } + else if (item != null && item.Type == ItemType.FilledMap) + { + lower2upper = true; + upperStartSlot = upperEndSlot = 0; + } + else if (item != null && item.Type == ItemType.Map) + { + lower2upper = true; + upperStartSlot = upperEndSlot = 1; + } + else + { + if (slotId >= 3 && slotId <= 29) + { + backpack2hotbar = true; + lowerStartSlot = 30; + } + else + { + lower2upper = true; + upperStartSlot = 3; + upperEndSlot = 29; + } + } + break; + case ContainerType.Stonecutter: + if (slotId >= 0 && slotId <= 1) + { + if (slotId == 0) + hotbarFirst = false; + upper2backpack = true; + lowerStartSlot = 2; + } + else if (item != null && false /* Check if it is available for stone cutteing */) + { + lower2upper = true; + upperStartSlot = 0; + upperEndSlot = 0; + } + else + { + if (slotId >= 2 && slotId <= 28) + { + backpack2hotbar = true; + lowerStartSlot = 29; + } + else + { + lower2upper = true; + upperStartSlot = 2; + upperEndSlot = 28; + } + } + break; + default: // TODO: Define more container type here + goto case ContainerType.Generic_9x3; } // Cursor have item or not doesn't matter @@ -1470,120 +1951,94 @@ namespace MinecraftClient // 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) + if (lower2upper) { - // Clicked slot is on upper side inventory, put it to hotbar - // Now try to find same item and put on them - var itemsClone = playerInventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value); - foreach (KeyValuePair _item in itemsClone) + int firstEmptySlot = -1; + for (int i = upperStartSlot; i <= upperEndSlot; ++i) { - if (_item.Key <= upperEndSlot) continue; - - int maxCount = _item.Value.Type.StackCount(); - if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount) + if (inventory.Items.TryGetValue(i, out Item? curItem)) { - // Put item on that stack - int spaceLeft = maxCount - _item.Value.Count; - if (inventory.Items[slotId].Count <= spaceLeft) + if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots)) + break; + } + else if (firstEmptySlot == -1) + firstEmptySlot = i; + } + if (item!.Count > 0) + { + if (firstEmptySlot != -1) + StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots); + else if (item.Count != itemCount) + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); + } + } + else if (upper2backpack) + { + int hotbarEnd = lowerStartSlot + 4 * 9 - 1; + if (hotbarFirst) + { + int lastEmptySlot = -1; + for (int i = hotbarEnd; i >= lowerStartSlot; --i) + { + if (inventory.Items.TryGetValue(i, out Item? curItem)) { - // 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, null)); - } - 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])); + if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots)) + break; } + else if (lastEmptySlot == -1) + lastEmptySlot = i; + } + if (item!.Count > 0) + { + if (lastEmptySlot != -1) + StoreInNewSlot(inventory, item, slotId, lastEmptySlot, changedSlots); + else if (item.Count != itemCount) + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } } - if (inventory.Items[slotId].Count > 0) + else { - int[] emptySlots = inventory.GetEmpytSlots(); - int emptySlot = -2; - foreach (int slot in emptySlots) + int firstEmptySlot = -1; + for (int i = lowerStartSlot; i <= hotbarEnd; ++i) { - if (slot <= upperEndSlot) continue; - emptySlot = slot; - break; + if (inventory.Items.TryGetValue(i, out Item? curItem)) + { + if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots)) + break; + } + else if (firstEmptySlot == -1) + firstEmptySlot = i; } - if (emptySlot != -2) + if (item!.Count > 0) { - 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])); + if (firstEmptySlot != -1) + StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots); + else if (item.Count != itemCount) + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } } } - else + else if (backpack2hotbar) { - // Clicked slot is on hotbar, put it to upper inventory - // Now try to find same item and put on them - var itemsClone = playerInventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value); - foreach (KeyValuePair _item in itemsClone) + int hotbarEnd = lowerStartSlot + 1 * 9 - 1; + + int firstEmptySlot = -1; + for (int i = lowerStartSlot; i <= hotbarEnd; ++i) { - if (_item.Key < upperStartSlot) continue; - if (_item.Key >= upperEndSlot) break; - - int maxCount = _item.Value.Type.StackCount(); - if (_item.Value.Type == inventory.Items[slotId].Type && _item.Value.Count < maxCount) + if (inventory.Items.TryGetValue(i, out Item? curItem)) { - // Put item on that stack - int spaceLeft = maxCount - _item.Value.Count; - if (inventory.Items[slotId].Count <= spaceLeft) - { - // 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, null)); - } - 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])); - } + if (TryMergeSlot(inventory, item!, slotId, curItem, i, changedSlots)) + break; } + else if (firstEmptySlot == -1) + firstEmptySlot = i; } - if (inventory.Items[slotId].Count > 0) + if (item!.Count > 0) { - int[] emptySlots = inventory.GetEmpytSlots(); - int emptySlot = -2; - foreach (int slot in emptySlots) - { - if (slot < upperStartSlot) continue; - if (slot >= upperEndSlot) break; - emptySlot = slot; - break; - } - if (emptySlot != -2) - { - 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])); - } + if (firstEmptySlot != -1) + StoreInNewSlot(inventory, item, slotId, firstEmptySlot, changedSlots); + else if (item.Count != itemCount) + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } } } @@ -1592,19 +2047,19 @@ namespace MinecraftClient if (inventory.Items.ContainsKey(slotId)) { inventory.Items[slotId].Count--; - changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); + changedSlots.Add(new Tuple((short)slotId, inventory.Items[slotId])); } if (inventory.Items[slotId].Count <= 0) { inventory.Items.Remove(slotId); - changedSlots.Add(new Tuple((short)slotId, null)); + changedSlots.Add(new Tuple((short)slotId, null)); } break; case WindowActionType.DropItemStack: inventory.Items.Remove(slotId); - changedSlots.Add(new Tuple((short)slotId, null)); + changedSlots.Add(new Tuple((short)slotId, null)); break; } } diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index f9a75213..d026c919 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -741,7 +741,7 @@ namespace MinecraftClient.Protocol.Handlers /// /// Dictionary to encode as Nbt /// Byte array for this NBT tag - public byte[] GetNbt(Dictionary nbt) + public byte[] GetNbt(Dictionary? nbt) { return GetNbt(nbt, true); } @@ -752,7 +752,7 @@ namespace MinecraftClient.Protocol.Handlers /// Dictionary to encode as Nbt /// TRUE if starting a new NBT tag, FALSE if processing a nested NBT tag /// Byte array for this NBT tag - private byte[] GetNbt(Dictionary nbt, bool root) + private byte[] GetNbt(Dictionary? nbt, bool root) { if (nbt == null || nbt.Count == 0) return new byte[] { 0 }; // TAG_End @@ -1065,9 +1065,9 @@ namespace MinecraftClient.Protocol.Handlers /// Item /// Item Palette /// Item slot representation - public byte[] GetItemSlot(Item item, ItemPalette itemPalette) + public byte[] GetItemSlot(Item? item, ItemPalette itemPalette) { - List slotData = new List(); + List slotData = new(); if (protocolversion > Protocol18Handler.MC_1_13_Version) { // MC 1.13 and greater diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index b0ff6546..4c8542e7 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -752,7 +752,7 @@ namespace MinecraftClient.Protocol.Handlers return false; //Currently not implemented } - public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, 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 a00011ce..fe60c690 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -2634,7 +2634,7 @@ namespace MinecraftClient.Protocol.Handlers catch (ObjectDisposedException) { return false; } } - public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item item, List> changedSlots, int stateId) + public bool SendWindowAction(int windowId, int slotId, WindowActionType action, Item? item, List> changedSlots, int stateId) { try { @@ -2669,7 +2669,7 @@ namespace MinecraftClient.Protocol.Handlers case WindowActionType.AddDragMiddle: button = 9; mode = 5; item = new Item(ItemType.Null, 0, null); break; } - List packet = new List(); + List packet = new(); packet.Add((byte)windowId); // Window ID // 1.18+ diff --git a/MinecraftClient/Protocol/IMinecraftCom.cs b/MinecraftClient/Protocol/IMinecraftCom.cs index 52bfcac7..d584d13b 100644 --- a/MinecraftClient/Protocol/IMinecraftCom.cs +++ b/MinecraftClient/Protocol/IMinecraftCom.cs @@ -169,7 +169,7 @@ namespace MinecraftClient.Protocol /// 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, List> changedSlots, 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 4e0de511..5ef147dd 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -41,7 +41,7 @@ namespace MinecraftClient.Protocol bool GetNetworkPacketCaptureEnabled(); void SetNetworkPacketCaptureEnabled(bool enabled); int GetProtocolVersion(); - Container GetInventory(int inventoryID); + Container? GetInventory(int inventoryID); ILogger GetLogger(); /// diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini index ba86f955..fc5b5c81 100644 --- a/MinecraftClient/Resources/lang/en.ini +++ b/MinecraftClient/Resources/lang/en.ini @@ -296,6 +296,7 @@ cmd.inventory.left=Left cmd.inventory.right=Right cmd.inventory.middle=Middle cmd.inventory.clicking={0} clicking slot {1} in window #{2} +cmd.inventory.shiftclick=Shift clicking slot {0} in window #{1} cmd.inventory.no_item=No item in slot #{0} cmd.inventory.drop=Dropped 1 item from slot #{0} cmd.inventory.drop_stack=Dropped whole item stack from slot #{0}