diff --git a/src/main/java/com/loohp/limbo/Limbo.java b/src/main/java/com/loohp/limbo/Limbo.java index 30e8213..af422ef 100644 --- a/src/main/java/com/loohp/limbo/Limbo.java +++ b/src/main/java/com/loohp/limbo/Limbo.java @@ -27,6 +27,7 @@ import com.loohp.limbo.consolegui.GUI; import com.loohp.limbo.events.EventsManager; import com.loohp.limbo.file.ServerProperties; +import com.loohp.limbo.inventory.AnvilInventory; import com.loohp.limbo.inventory.CustomInventory; import com.loohp.limbo.inventory.Inventory; import com.loohp.limbo.inventory.InventoryHolder; @@ -626,7 +627,15 @@ public Inventory createInventory(InventoryType type, InventoryHolder holder) { } public Inventory createInventory(Component title, InventoryType type, InventoryHolder holder) { - throw new UnsupportedOperationException("This function has not been implemented yet."); + if (!type.isCreatable()) { + throw new UnsupportedOperationException("This InventoryType cannot be created."); + } + switch (type) { + case ANVIL: + return new AnvilInventory(title, holder); + default: + throw new UnsupportedOperationException("This InventoryType has not been implemented yet."); + } } } diff --git a/src/main/java/com/loohp/limbo/events/inventory/AnvilRenameInputEvent.java b/src/main/java/com/loohp/limbo/events/inventory/AnvilRenameInputEvent.java new file mode 100644 index 0000000..a8937b8 --- /dev/null +++ b/src/main/java/com/loohp/limbo/events/inventory/AnvilRenameInputEvent.java @@ -0,0 +1,53 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.events.inventory; + +import com.loohp.limbo.events.Cancellable; +import com.loohp.limbo.inventory.InventoryView; + +public class AnvilRenameInputEvent extends InventoryEvent implements Cancellable { + + private boolean cancelled; + private String input; + + public AnvilRenameInputEvent(InventoryView inventoryView, String input) { + super(inventoryView, inventoryView.getTopInventory()); + this.input = input; + this.cancelled = false; + } + + public void setInput(String input) { + this.input = input; + } + + public String getInput() { + return input; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @Override + public boolean isCancelled() { + return cancelled; + } +} diff --git a/src/main/java/com/loohp/limbo/events/inventory/InventoryClickEvent.java b/src/main/java/com/loohp/limbo/events/inventory/InventoryClickEvent.java index 57f4159..44e52c7 100644 --- a/src/main/java/com/loohp/limbo/events/inventory/InventoryClickEvent.java +++ b/src/main/java/com/loohp/limbo/events/inventory/InventoryClickEvent.java @@ -20,16 +20,86 @@ package com.loohp.limbo.events.inventory; import com.loohp.limbo.events.Cancellable; -import com.loohp.limbo.inventory.Inventory; +import com.loohp.limbo.inventory.ClickType; +import com.loohp.limbo.inventory.InventoryAction; +import com.loohp.limbo.inventory.InventoryType; import com.loohp.limbo.inventory.InventoryView; -import com.loohp.limbo.player.Player; +import com.loohp.limbo.inventory.ItemStack; public class InventoryClickEvent extends InventoryEvent implements Cancellable { private boolean cancelled; + private final ClickType click; + private final InventoryAction action; + private InventoryType.SlotType type; + private int whichSlot; + private int rawSlot; + private ItemStack current; + private int hotbarKey; - public InventoryClickEvent(Player player, InventoryView inventoryView, Inventory clickedInventory) { - super(player, inventoryView, clickedInventory); + public InventoryClickEvent(InventoryView view, InventoryType.SlotType type, int rawSlot, ClickType click, InventoryAction action) { + super(view, view.getInventory(rawSlot)); + this.type = type; + this.rawSlot = rawSlot; + this.whichSlot = view.convertSlot(rawSlot); + this.click = click; + this.action = action; + this.current = null; + this.hotbarKey = -1; + this.cancelled = false; + } + + public InventoryClickEvent(InventoryView view, InventoryType.SlotType type, int rawSlot, ClickType click, InventoryAction action, int hotbarKey) { + this(view, type, rawSlot, click, action); + this.hotbarKey = hotbarKey; + } + + public ClickType getClick() { + return click; + } + + public InventoryAction getAction() { + return action; + } + + public InventoryType.SlotType getType() { + return type; + } + + public int getWhichSlot() { + return whichSlot; + } + + public int getRawSlot() { + return rawSlot; + } + + public int getHotbarKey() { + return hotbarKey; + } + + public ItemStack getCarriedItem() { + return getView().getCarriedItem(); + } + + @Deprecated + public void setCarriedItem(ItemStack stack) { + getView().setCarriedItem(stack); + } + + public ItemStack getCurrentItem() { + if (type == InventoryType.SlotType.OUTSIDE) { + return current; + } + return getView().getItem(rawSlot); + } + + public void setCurrentItem(ItemStack stack) { + if (type == InventoryType.SlotType.OUTSIDE) { + current = stack; + } else { + getView().setItem(rawSlot, stack); + } } @Override diff --git a/src/main/java/com/loohp/limbo/events/inventory/InventoryCloseEvent.java b/src/main/java/com/loohp/limbo/events/inventory/InventoryCloseEvent.java index 1eb560d..1dc2485 100644 --- a/src/main/java/com/loohp/limbo/events/inventory/InventoryCloseEvent.java +++ b/src/main/java/com/loohp/limbo/events/inventory/InventoryCloseEvent.java @@ -20,12 +20,11 @@ package com.loohp.limbo.events.inventory; import com.loohp.limbo.inventory.InventoryView; -import com.loohp.limbo.player.Player; public class InventoryCloseEvent extends InventoryEvent { - public InventoryCloseEvent(Player player, InventoryView inventoryView) { - super(player, inventoryView, inventoryView.getTopInventory()); + public InventoryCloseEvent(InventoryView inventoryView) { + super(inventoryView, inventoryView.getTopInventory()); } } diff --git a/src/main/java/com/loohp/limbo/events/inventory/InventoryCreativeEvent.java b/src/main/java/com/loohp/limbo/events/inventory/InventoryCreativeEvent.java index 5a890c2..7be5b60 100644 --- a/src/main/java/com/loohp/limbo/events/inventory/InventoryCreativeEvent.java +++ b/src/main/java/com/loohp/limbo/events/inventory/InventoryCreativeEvent.java @@ -22,8 +22,6 @@ import com.loohp.limbo.events.Cancellable; import com.loohp.limbo.inventory.InventoryView; import com.loohp.limbo.inventory.ItemStack; -import com.loohp.limbo.player.Player; -import com.loohp.limbo.player.PlayerInventory; public class InventoryCreativeEvent extends InventoryEvent implements Cancellable { @@ -31,8 +29,8 @@ public class InventoryCreativeEvent extends InventoryEvent implements Cancellabl private final int slot; private ItemStack newItem; - public InventoryCreativeEvent(Player player, InventoryView inventoryView, PlayerInventory playerInventory, int slot, ItemStack newItem) { - super(player, inventoryView, playerInventory); + public InventoryCreativeEvent(InventoryView inventoryView, int slot, ItemStack newItem) { + super(inventoryView, inventoryView.getBottomInventory()); this.slot = slot; this.newItem = newItem; this.cancelled = false; diff --git a/src/main/java/com/loohp/limbo/events/inventory/InventoryDragEvent.java b/src/main/java/com/loohp/limbo/events/inventory/InventoryDragEvent.java new file mode 100644 index 0000000..e6e7679 --- /dev/null +++ b/src/main/java/com/loohp/limbo/events/inventory/InventoryDragEvent.java @@ -0,0 +1,97 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.events.inventory; + +import com.loohp.limbo.events.Cancellable; +import com.loohp.limbo.inventory.DragType; +import com.loohp.limbo.inventory.InventoryView; +import com.loohp.limbo.inventory.ItemStack; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class InventoryDragEvent extends InventoryEvent implements Cancellable { + + private boolean cancelled; + private final DragType type; + private final Map addedItems; + private final Set containerSlots; + private final ItemStack oldCarried; + private ItemStack newCarried; + + public InventoryDragEvent(InventoryView view, ItemStack newCarried, ItemStack oldCarried, boolean right, Map slots) { + super(view, view.getInventory(view.convertSlot(slots.keySet().iterator().next()))); + this.type = right ? DragType.SINGLE : DragType.EVEN; + this.newCarried = newCarried; + this.oldCarried = oldCarried; + this.addedItems = Collections.unmodifiableMap(slots); + Set containerSlots = new HashSet<>(); + for (Integer slot : slots.keySet()) { + containerSlots.add(view.convertSlot(slot)); + } + this.containerSlots = Collections.unmodifiableSet(containerSlots); + this.cancelled = false; + } + + public Map getNewItems() { + return addedItems; + } + + public Set getRawSlots() { + return addedItems.keySet(); + } + + public Set getInventorySlots() { + return containerSlots; + } + + public ItemStack get() { + return newCarried; + } + + public ItemStack getCarriedItem() { + return newCarried; + } + + public void setCarriedItem(ItemStack newCursor) { + this.newCarried = newCursor; + } + + public ItemStack getOldCarriedItem() { + return oldCarried.clone(); + } + + public DragType getType() { + return type; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + +} diff --git a/src/main/java/com/loohp/limbo/events/inventory/InventoryEvent.java b/src/main/java/com/loohp/limbo/events/inventory/InventoryEvent.java index 92620fd..553bbc5 100644 --- a/src/main/java/com/loohp/limbo/events/inventory/InventoryEvent.java +++ b/src/main/java/com/loohp/limbo/events/inventory/InventoryEvent.java @@ -25,22 +25,20 @@ import com.loohp.limbo.player.Player; public class InventoryEvent extends Event { - - private final Player player; + private final InventoryView inventoryView; private final Inventory clickedInventory; - public InventoryEvent(Player player, InventoryView inventoryView, Inventory clickedInventory) { - this.player = player; + public InventoryEvent(InventoryView inventoryView, Inventory clickedInventory) { this.inventoryView = inventoryView; this.clickedInventory = clickedInventory; } public Player getPlayer() { - return player; + return inventoryView.getPlayer(); } - public InventoryView getInventoryView() { + public InventoryView getView() { return inventoryView; } diff --git a/src/main/java/com/loohp/limbo/events/inventory/InventoryOpenEvent.java b/src/main/java/com/loohp/limbo/events/inventory/InventoryOpenEvent.java index ee79170..df352bd 100644 --- a/src/main/java/com/loohp/limbo/events/inventory/InventoryOpenEvent.java +++ b/src/main/java/com/loohp/limbo/events/inventory/InventoryOpenEvent.java @@ -21,14 +21,13 @@ import com.loohp.limbo.events.Cancellable; import com.loohp.limbo.inventory.InventoryView; -import com.loohp.limbo.player.Player; public class InventoryOpenEvent extends InventoryEvent implements Cancellable { private boolean cancelled; - public InventoryOpenEvent(Player player, InventoryView inventoryView) { - super(player, inventoryView, inventoryView.getTopInventory()); + public InventoryOpenEvent(InventoryView inventoryView) { + super(inventoryView, inventoryView.getTopInventory()); this.cancelled = false; } diff --git a/src/main/java/com/loohp/limbo/events/player/PlayerSelectedSlotChangeEvent.java b/src/main/java/com/loohp/limbo/events/player/PlayerSelectedSlotChangeEvent.java index 794e66d..ef1dd39 100644 --- a/src/main/java/com/loohp/limbo/events/player/PlayerSelectedSlotChangeEvent.java +++ b/src/main/java/com/loohp/limbo/events/player/PlayerSelectedSlotChangeEvent.java @@ -24,22 +24,23 @@ public class PlayerSelectedSlotChangeEvent extends PlayerEvent implements Cancellable { - private boolean cancel = false; + private boolean cancelled; private byte slot; public PlayerSelectedSlotChangeEvent(Player player, byte slot) { super(player); this.slot = slot; + this.cancelled = false; } @Override public void setCancelled(boolean cancelled) { - this.cancel = cancelled; + this.cancelled = cancelled; } @Override public boolean isCancelled() { - return cancel; + return cancelled; } public byte getSlot() { diff --git a/src/main/java/com/loohp/limbo/events/player/PlayerSwapHandItemsEvent.java b/src/main/java/com/loohp/limbo/events/player/PlayerSwapHandItemsEvent.java new file mode 100644 index 0000000..480d93c --- /dev/null +++ b/src/main/java/com/loohp/limbo/events/player/PlayerSwapHandItemsEvent.java @@ -0,0 +1,65 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.events.player; + +import com.loohp.limbo.events.Cancellable; +import com.loohp.limbo.inventory.ItemStack; +import com.loohp.limbo.player.Player; + +public class PlayerSwapHandItemsEvent extends PlayerEvent implements Cancellable { + + private boolean cancelled; + private ItemStack mainHandItem; + private ItemStack offHandItem; + + public PlayerSwapHandItemsEvent(Player player, ItemStack mainHandItem, ItemStack offHandItem) { + super(player); + this.mainHandItem = mainHandItem; + this.offHandItem = offHandItem; + this.cancelled = false; + } + + public ItemStack getMainHandItem() { + return mainHandItem; + } + + public void setMainHandItem(ItemStack mainHandItem) { + this.mainHandItem = mainHandItem; + } + + public ItemStack getOffHandItem() { + return offHandItem; + } + + public void setOffHandItem(ItemStack offHandItem) { + this.offHandItem = offHandItem; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + +} diff --git a/src/main/java/com/loohp/limbo/inventory/AbstractInventory.java b/src/main/java/com/loohp/limbo/inventory/AbstractInventory.java index 0fab894..1d66234 100644 --- a/src/main/java/com/loohp/limbo/inventory/AbstractInventory.java +++ b/src/main/java/com/loohp/limbo/inventory/AbstractInventory.java @@ -19,6 +19,7 @@ package com.loohp.limbo.inventory; +import com.loohp.limbo.location.Location; import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot; import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowItems; import com.loohp.limbo.player.Player; @@ -109,6 +110,11 @@ public void updateInventory() { } } + @Override + public Location getLocation() { + return inventoryHolder == null ? null : inventoryHolder.getLocation(); + } + @Override public int getSize() { return inventory.length(); @@ -311,7 +317,7 @@ public boolean contains(Key material) throws IllegalArgumentException { public boolean contains(ItemStack item) { for (int i = 0; i < inventory.length(); i++) { ItemStack itemStack = getItem(i); - if (itemStack.equals(item)) { + if (Objects.equals(itemStack, item)) { return true; } } @@ -525,6 +531,9 @@ public Unsafe(AbstractInventory inventory) { @Deprecated public void a(int index, ItemStack itemStack) { + if (itemStack != null && itemStack.type().equals(ItemStack.AIR.type())) { + itemStack = null; + } inventory.inventory.set(index, itemStack); } diff --git a/src/main/java/com/loohp/limbo/inventory/AnvilInventory.java b/src/main/java/com/loohp/limbo/inventory/AnvilInventory.java new file mode 100644 index 0000000..bfda39d --- /dev/null +++ b/src/main/java/com/loohp/limbo/inventory/AnvilInventory.java @@ -0,0 +1,43 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.inventory; + +import net.kyori.adventure.text.Component; + +public class AnvilInventory extends AbstractInventory implements TitledInventory { + + public static final Component DEFAULT_TITLE = Component.translatable("container.repair"); + + private Component title; + + public AnvilInventory(Component title, InventoryHolder inventoryHolder) { + super(InventoryType.ANVIL.getDefaultSize(), inventoryHolder, InventoryType.ANVIL, null, null); + this.title = title == null ? DEFAULT_TITLE : title; + } + + public void setTitle(Component title) { + this.title = title; + } + + @Override + public Component getTitle() { + return title; + } +} diff --git a/src/main/java/com/loohp/limbo/inventory/ClickType.java b/src/main/java/com/loohp/limbo/inventory/ClickType.java new file mode 100644 index 0000000..de3dd3f --- /dev/null +++ b/src/main/java/com/loohp/limbo/inventory/ClickType.java @@ -0,0 +1,119 @@ +package com.loohp.limbo.inventory; + +/** + * What the client did to trigger this action (not the result). + */ +public enum ClickType { + + /** + * The left (or primary) mouse button. + */ + LEFT, + /** + * Holding shift while pressing the left mouse button. + */ + SHIFT_LEFT, + /** + * The right mouse button. + */ + RIGHT, + /** + * Holding shift while pressing the right mouse button. + */ + SHIFT_RIGHT, + /** + * Clicking the left mouse button on the grey area around the inventory. + */ + WINDOW_BORDER_LEFT, + /** + * Clicking the right mouse button on the grey area around the inventory. + */ + WINDOW_BORDER_RIGHT, + /** + * The middle mouse button, or a "scrollwheel click". + */ + MIDDLE, + /** + * One of the number keys 1-9, correspond to slots on the hotbar. + */ + NUMBER_KEY, + /** + * Pressing the left mouse button twice in quick succession. + */ + DOUBLE_CLICK, + /** + * The "Drop" key (defaults to Q). + */ + DROP, + /** + * Holding Ctrl while pressing the "Drop" key (defaults to Q). + */ + CONTROL_DROP, + /** + * Any action done with the Creative inventory open. + */ + CREATIVE, + /** + * The "swap item with offhand" key (defaults to F). + */ + SWAP_OFFHAND, + /** + * A type of inventory manipulation not yet recognized by Bukkit. + *

+ * This is only for transitional purposes on a new Minecraft update, and + * should never be relied upon. + *

+ * Any ClickType.UNKNOWN is called on a best-effort basis. + */ + UNKNOWN, + ; + + /** + * Gets whether this ClickType represents the pressing of a key on a + * keyboard. + * + * @return true if this ClickType represents the pressing of a key + */ + public boolean isKeyboardClick() { + return (this == ClickType.NUMBER_KEY) || (this == ClickType.DROP) || (this == ClickType.CONTROL_DROP); + } + + /** + * Gets whether this ClickType represents an action that can only be + * performed by a Player in creative mode. + * + * @return true if this action requires Creative mode + */ + public boolean isCreativeAction() { + // Why use middle click? + return (this == ClickType.MIDDLE) || (this == ClickType.CREATIVE); + } + + /** + * Gets whether this ClickType represents a right click. + * + * @return true if this ClickType represents a right click + */ + public boolean isRightClick() { + return (this == ClickType.RIGHT) || (this == ClickType.SHIFT_RIGHT); + } + + /** + * Gets whether this ClickType represents a left click. + * + * @return true if this ClickType represents a left click + */ + public boolean isLeftClick() { + return (this == ClickType.LEFT) || (this == ClickType.SHIFT_LEFT) || (this == ClickType.DOUBLE_CLICK) || (this == ClickType.CREATIVE); + } + + /** + * Gets whether this ClickType indicates that the shift key was pressed + * down when the click was made. + * + * @return true if the action uses Shift. + */ + public boolean isShiftClick() { + return (this == ClickType.SHIFT_LEFT) || (this == ClickType.SHIFT_RIGHT) || (this == ClickType.CONTROL_DROP); + } +} diff --git a/src/main/java/com/loohp/limbo/inventory/CustomInventory.java b/src/main/java/com/loohp/limbo/inventory/CustomInventory.java index 6b38e35..13ecab5 100644 --- a/src/main/java/com/loohp/limbo/inventory/CustomInventory.java +++ b/src/main/java/com/loohp/limbo/inventory/CustomInventory.java @@ -49,9 +49,4 @@ public void setTitle(Component title) { this.title = title; } - @Override - public Location getLocation() { - return null; - } - } diff --git a/src/main/java/com/loohp/limbo/inventory/DragType.java b/src/main/java/com/loohp/limbo/inventory/DragType.java new file mode 100644 index 0000000..9425b47 --- /dev/null +++ b/src/main/java/com/loohp/limbo/inventory/DragType.java @@ -0,0 +1,17 @@ +package com.loohp.limbo.inventory; + +/** + * Represents the effect of a drag that will be applied to an Inventory in an + * InventoryDragEvent. + */ +public enum DragType { + /** + * One item from the cursor is placed in each selected slot. + */ + SINGLE, + /** + * The cursor is split evenly across all selected slots, not to exceed the + * Material's max stack size, with the remainder going to the cursor. + */ + EVEN, +} diff --git a/src/main/java/com/loohp/limbo/inventory/InventoryAction.java b/src/main/java/com/loohp/limbo/inventory/InventoryAction.java new file mode 100644 index 0000000..df325b2 --- /dev/null +++ b/src/main/java/com/loohp/limbo/inventory/InventoryAction.java @@ -0,0 +1,95 @@ +package com.loohp.limbo.inventory; + +/** + * An estimation of what the result will be. + */ +public enum InventoryAction { + + /** + * Nothing will happen from the click. + *

+ * There may be cases where nothing will happen and this is value is not + * provided, but it is guaranteed that this value is accurate when given. + */ + NOTHING, + /** + * All of the items on the clicked slot are moved to the cursor. + */ + PICKUP_ALL, + /** + * Some of the items on the clicked slot are moved to the cursor. + */ + PICKUP_SOME, + /** + * Half of the items on the clicked slot are moved to the cursor. + */ + PICKUP_HALF, + /** + * One of the items on the clicked slot are moved to the cursor. + */ + PICKUP_ONE, + /** + * All of the items on the cursor are moved to the clicked slot. + */ + PLACE_ALL, + /** + * Some of the items from the cursor are moved to the clicked slot + * (usually up to the max stack size). + */ + PLACE_SOME, + /** + * A single item from the cursor is moved to the clicked slot. + */ + PLACE_ONE, + /** + * The clicked item and the cursor are exchanged. + */ + SWAP_WITH_CURSOR, + /** + * The entire cursor item is dropped. + */ + DROP_ALL_CURSOR, + /** + * One item is dropped from the cursor. + */ + DROP_ONE_CURSOR, + /** + * The entire clicked slot is dropped. + */ + DROP_ALL_SLOT, + /** + * One item is dropped from the clicked slot. + */ + DROP_ONE_SLOT, + /** + * The item is moved to the opposite inventory if a space is found. + */ + MOVE_TO_OTHER_INVENTORY, + /** + * The clicked item is moved to the hotbar, and the item currently there + * is re-added to the player's inventory. + * + * The hotbar includes the player's off hand. + */ + HOTBAR_MOVE_AND_READD, + /** + * The clicked slot and the picked hotbar slot are swapped. + * + * The hotbar includes the player's off hand. + */ + HOTBAR_SWAP, + /** + * A max-size stack of the clicked item is put on the cursor. + */ + CLONE_STACK, + /** + * The inventory is searched for the same material, and they are put on + * the cursor up to {@link ItemStack#getMaxStackSize()}. + */ + COLLECT_TO_CURSOR, + /** + * An unrecognized ClickType. + */ + UNKNOWN, + ; +} diff --git a/src/main/java/com/loohp/limbo/inventory/InventoryHolder.java b/src/main/java/com/loohp/limbo/inventory/InventoryHolder.java index 3566303..76af6af 100644 --- a/src/main/java/com/loohp/limbo/inventory/InventoryHolder.java +++ b/src/main/java/com/loohp/limbo/inventory/InventoryHolder.java @@ -19,10 +19,14 @@ package com.loohp.limbo.inventory; +import com.loohp.limbo.location.Location; + public interface InventoryHolder { Inventory getInventory(); InventoryHolder getHolder(); + Location getLocation(); + } diff --git a/src/main/java/com/loohp/limbo/inventory/InventoryView.java b/src/main/java/com/loohp/limbo/inventory/InventoryView.java index 191fada..4ff8fc1 100644 --- a/src/main/java/com/loohp/limbo/inventory/InventoryView.java +++ b/src/main/java/com/loohp/limbo/inventory/InventoryView.java @@ -19,8 +19,11 @@ package com.loohp.limbo.inventory; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot; import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowData; import com.loohp.limbo.player.Player; +import com.loohp.limbo.player.PlayerInventory; +import net.kyori.adventure.text.Component; import java.io.IOException; import java.util.Collections; @@ -29,8 +32,10 @@ public class InventoryView { + public static final int OUTSIDE = -999; + private final Player player; - private final String title; + private Component title; private Inventory topInventory; private final Inventory bottomInventory; private final Map properties; @@ -38,7 +43,7 @@ public class InventoryView { private final Unsafe unsafe; - public InventoryView(Player player, String title, Inventory topInventory, Inventory bottomInventory) { + public InventoryView(Player player, Component title, Inventory topInventory, Inventory bottomInventory) { this.player = player; this.title = title; this.topInventory = topInventory; @@ -58,14 +63,14 @@ public void setCarriedItem(ItemStack carriedItem) { } public InventoryType getType() { - return topInventory.getType(); + return topInventory == null ? bottomInventory.getType() : topInventory.getType(); } public Player getPlayer() { return player; } - public String getTitle() { + public Component getTitle() { return title; } @@ -81,29 +86,237 @@ public Map getProperties() { return Collections.unmodifiableMap(properties); } - public int countSlots() { - return topInventory.getSize() + bottomInventory.getSize(); + /** + * Gets the inventory corresponding to the given raw slot ID. + * + * If the slot ID is {@link #OUTSIDE} null will be returned, otherwise + * behaviour for illegal and negative slot IDs is undefined. + * + * May be used with {@link #convertSlot(int)} to directly index an + * underlying inventory. + * + * @param rawSlot The raw slot ID. + * @return corresponding inventory, or null + */ + public Inventory getInventory(int rawSlot) { + // Slot may be -1 if not properly detected due to client bug + // e.g. dropping an item into part of the enchantment list section of an enchanting table + if (rawSlot == OUTSIDE || rawSlot == -1) { + return null; + } + if (rawSlot < 0) { + throw new IllegalArgumentException("Negative, non outside slot " + rawSlot); + } + if (rawSlot >= countSlots()) { + throw new IllegalArgumentException("Slot " + rawSlot + " greater than inventory slot count"); + } + + if (rawSlot < topInventory.getSize()) { + return getTopInventory(); + } else { + return getBottomInventory(); + } } - public ItemStack getItem(int index) { - if (topInventory != null) { - if (index < topInventory.getSize()) { - return topInventory.getItem(topInventory.getUnsafe().b().applyAsInt(index)); + /** + * Converts a raw slot ID into its local slot ID into whichever of the two + * inventories the slot points to. + *

+ * If the raw slot refers to the upper inventory, it will be returned + * unchanged and thus be suitable for getTopInventory().getItem(); if it + * refers to the lower inventory, the output will differ from the input + * and be suitable for getBottomInventory().getItem(). + * + * @param rawSlot The raw slot ID. + * @return The converted slot ID. + */ + public int convertSlot(int rawSlot) { + int numInTop = topInventory == null ? 0 : topInventory.getSize(); + // Index from the top inventory as having slots from [0,size] + if (rawSlot < numInTop) { + return rawSlot; + } + + // Move down the slot index by the top size + int slot = rawSlot - numInTop; + + // Player crafting slots are indexed differently. The matrix is caught by the first return. + // Creative mode is the same, except that you can't see the crafting slots (but the IDs are still used) + if (getType() == InventoryType.CRAFTING || getType() == InventoryType.CREATIVE) { + /* + * Raw Slots: + * + * 5 1 2 0 + * 6 3 4 + * 7 + * 8 45 + * 9 10 11 12 13 14 15 16 17 + * 18 19 20 21 22 23 24 25 26 + * 27 28 29 30 31 32 33 34 35 + * 36 37 38 39 40 41 42 43 44 + */ + + /* + * Converted Slots: + * + * 39 1 2 0 + * 38 3 4 + * 37 + * 36 40 + * 9 10 11 12 13 14 15 16 17 + * 18 19 20 21 22 23 24 25 26 + * 27 28 29 30 31 32 33 34 35 + * 0 1 2 3 4 5 6 7 8 + */ + + if (slot < 4) { + // Send [5,8] to [39,36] + return 39 - slot; + } else if (slot > 39) { + // Slot lives in the extra slot section + return slot; + } else { + // Reset index so 9 -> 0 + slot -= 4; } - index -= topInventory.getSize(); } - return bottomInventory.getItem(bottomInventory.getUnsafe().b().applyAsInt(index)); + + // 27 = 36 - 9 + if (slot >= 27) { + // Put into hotbar section + slot -= 27; + } else { + // Take out of hotbar section + // 9 = 36 - 27 + slot += 9; + } + + return slot; } - public void setItem(int index, ItemStack itemStack) { + /** + * Determine the type of the slot by its raw slot ID. + *

+ * If the type of the slot is unknown, then + * {@link InventoryType.SlotType#CONTAINER} will be returned. + * + * @param slot The raw slot ID + * @return the slot type + */ + public InventoryType.SlotType getSlotType(int slot) { + InventoryType.SlotType type = InventoryType.SlotType.CONTAINER; + if (topInventory != null && slot >= 0 && slot < topInventory.getSize()) { + switch (this.getType()) { + case BLAST_FURNACE: + case FURNACE: + case SMOKER: + if (slot == 2) { + type = InventoryType.SlotType.RESULT; + } else if (slot == 1) { + type = InventoryType.SlotType.FUEL; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case BREWING: + if (slot == 3) { + type = InventoryType.SlotType.FUEL; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case ENCHANTING: + case BEACON: + type = InventoryType.SlotType.CRAFTING; + break; + case WORKBENCH: + case CRAFTING: + if (slot == 0) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case ANVIL: + case SMITHING: + case CARTOGRAPHY: + case GRINDSTONE: + case MERCHANT: + if (slot == 2) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case STONECUTTER: + if (slot == 1) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + case LOOM: + if (slot == 3) { + type = InventoryType.SlotType.RESULT; + } else { + type = InventoryType.SlotType.CRAFTING; + } + break; + default: + // Nothing to do, it's a CONTAINER slot + } + } else { + if (slot < 0) { + type = InventoryType.SlotType.OUTSIDE; + } else if (this.getType() == InventoryType.CRAFTING) { // Also includes creative inventory + if (slot < 9) { + type = InventoryType.SlotType.ARMOR; + } else if (slot > 35) { + type = InventoryType.SlotType.QUICKBAR; + } + } else if (slot >= (this.countSlots() - (9 + 4 + 1))) { // Quickbar, Armor, Offhand + type = InventoryType.SlotType.QUICKBAR; + } + } + return type; + } + + /** + * Check the total number of slots in this view, combining the upper and + * lower inventories. + *

+ * Note though that it's possible for this to be greater than the sum of + * the two inventories if for example some slots are not being used. + * + * @return The total size + */ + public int countSlots() { + return (topInventory == null ? 0 : topInventory.getSize()) + bottomInventory.getSize(); + } + + public void close() { + player.closeInventory(); + } + + public boolean isSlot(int index) { if (topInventory != null) { if (index < topInventory.getSize()) { - topInventory.setItem(topInventory.getUnsafe().b().applyAsInt(index), itemStack); - return; + return true; } index -= topInventory.getSize(); } - bottomInventory.setItem(bottomInventory.getUnsafe().b().applyAsInt(index), itemStack); + if (bottomInventory instanceof PlayerInventory) { + return index < 36; + } + return index < bottomInventory.getSize(); + } + + public ItemStack getItem(int index) { + return getInventory(index).getItem(convertSlot(index)); + } + + public void setItem(int index, ItemStack itemStack) { + getInventory(index).setItem(convertSlot(index), itemStack); } public void setProperty(InventoryView.Property prop, int value) { @@ -121,6 +334,18 @@ public void setProperty(InventoryView.Property prop, int value) { } } + public void updateView() { + if (topInventory != null) { + topInventory.updateInventory(player); + } + bottomInventory.updateInventory(player); + try { + player.clientConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, 0, carriedItem)); + } catch (IOException e) { + e.printStackTrace(); + } + } + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public Unsafe getUnsafe() { @@ -139,10 +364,20 @@ public Unsafe(InventoryView inventoryView) { } @Deprecated - public void a(Inventory topInventory) { + public void a(Inventory topInventory, Component title) { inventoryView.topInventory = topInventory; + inventoryView.title = title; inventoryView.properties.clear(); } + + @Deprecated + public int a() { + if (inventoryView.topInventory != null) { + return inventoryView.topInventory.getUnsafe().c().getOrDefault(inventoryView.player, -1); + } + return inventoryView.bottomInventory.getUnsafe().c().getOrDefault(inventoryView.player, -1); + } + } /** diff --git a/src/main/java/com/loohp/limbo/inventory/ItemStack.java b/src/main/java/com/loohp/limbo/inventory/ItemStack.java index 31ae90e..7a631ea 100644 --- a/src/main/java/com/loohp/limbo/inventory/ItemStack.java +++ b/src/main/java/com/loohp/limbo/inventory/ItemStack.java @@ -20,8 +20,12 @@ package com.loohp.limbo.inventory; import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.querz.nbt.io.SNBTUtil; import net.querz.nbt.tag.CompoundTag; +import java.io.IOException; import java.util.Objects; public class ItemStack implements Cloneable { @@ -86,6 +90,43 @@ public ItemStack nbt(CompoundTag nbt) { return new ItemStack(material, amount, nbt == null ? null : nbt.clone()); } + public Component displayName() { + if (type().equals(AIR.type()) || nbt == null) { + return null; + } + CompoundTag displayTag = nbt.getCompoundTag("display"); + if (displayTag == null) { + return null; + } + String json = displayTag.getString("Name"); + if (json == null) { + return null; + } + try { + return GsonComponentSerializer.gson().deserialize(json); + } catch (Exception e) { + return null; + } + } + + public ItemStack displayName(Component component) { + if (type().equals(AIR.type())) { + return this; + } + try { + String json = GsonComponentSerializer.gson().serialize(component); + CompoundTag nbt = this.nbt.clone(); + CompoundTag displayTag = nbt.getCompoundTag("display"); + if (displayTag == null) { + nbt.put("display", displayTag = new CompoundTag()); + } + displayTag.putString("Name", json); + return nbt(nbt); + } catch (Exception ignore) { + } + return this; + } + public int getMaxStackSize() { return 64; } @@ -112,11 +153,21 @@ public boolean equals(Object o) { } public boolean isSimilar(ItemStack stack) { - return material.equals(stack.material) && Objects.equals(nbt, stack.nbt); + return stack != null && material.equals(stack.material) && Objects.equals(nbt, stack.nbt); } @Override public int hashCode() { return Objects.hash(material, amount, nbt); } + + @Override + public String toString() { + try { + return SNBTUtil.toSNBT(getFullTag()); + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } } diff --git a/src/main/java/com/loohp/limbo/network/ClientConnection.java b/src/main/java/com/loohp/limbo/network/ClientConnection.java index 2c4cdbd..0104b5d 100644 --- a/src/main/java/com/loohp/limbo/network/ClientConnection.java +++ b/src/main/java/com/loohp/limbo/network/ClientConnection.java @@ -20,6 +20,8 @@ package com.loohp.limbo.network; import com.loohp.limbo.Limbo; +import com.loohp.limbo.entity.EntityEquipment; +import com.loohp.limbo.events.inventory.AnvilRenameInputEvent; import com.loohp.limbo.events.inventory.InventoryCloseEvent; import com.loohp.limbo.events.inventory.InventoryCreativeEvent; import com.loohp.limbo.events.player.PlayerInteractEvent; @@ -30,10 +32,13 @@ import com.loohp.limbo.events.player.PlayerResourcePackStatusEvent; import com.loohp.limbo.events.player.PlayerSelectedSlotChangeEvent; import com.loohp.limbo.events.player.PlayerSpawnEvent; +import com.loohp.limbo.events.player.PlayerSwapHandItemsEvent; import com.loohp.limbo.events.player.PluginMessageEvent; import com.loohp.limbo.events.status.StatusPingEvent; import com.loohp.limbo.file.ServerProperties; +import com.loohp.limbo.inventory.AnvilInventory; import com.loohp.limbo.inventory.Inventory; +import com.loohp.limbo.inventory.ItemStack; import com.loohp.limbo.location.Location; import com.loohp.limbo.network.protocol.packets.Packet; import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn; @@ -44,11 +49,14 @@ import com.loohp.limbo.network.protocol.packets.PacketLoginOutLoginSuccess; import com.loohp.limbo.network.protocol.packets.PacketLoginOutPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketOut; +import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockDig; import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockPlace; import com.loohp.limbo.network.protocol.packets.PacketPlayInChat; import com.loohp.limbo.network.protocol.packets.PacketPlayInCloseWindow; import com.loohp.limbo.network.protocol.packets.PacketPlayInHeldItemChange; +import com.loohp.limbo.network.protocol.packets.PacketPlayInItemName; import com.loohp.limbo.network.protocol.packets.PacketPlayInKeepAlive; +import com.loohp.limbo.network.protocol.packets.PacketPlayInPickItem; import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketPlayInPosition; import com.loohp.limbo.network.protocol.packets.PacketPlayInPositionAndLook; @@ -85,6 +93,7 @@ import com.loohp.limbo.network.protocol.packets.ServerboundChatCommandPacket; import com.loohp.limbo.player.Player; import com.loohp.limbo.player.PlayerInteractManager; +import com.loohp.limbo.player.PlayerInventory; import com.loohp.limbo.utils.BungeecordAdventureConversionUtils; import com.loohp.limbo.utils.CheckedBiConsumer; import com.loohp.limbo.utils.CustomStringUtils; @@ -92,6 +101,7 @@ import com.loohp.limbo.utils.DeclareCommands; import com.loohp.limbo.utils.ForwardingUtils; import com.loohp.limbo.utils.GameMode; +import com.loohp.limbo.utils.InventoryClickUtils; import com.loohp.limbo.utils.MojangAPIUtils; import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; import com.loohp.limbo.world.BlockPosition; @@ -99,6 +109,7 @@ import com.loohp.limbo.world.World; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; @@ -124,7 +135,6 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Random; import java.util.Set; @@ -668,9 +678,9 @@ public void run() { sendPacket(response); } }; - System.out.println("Waiting"); + PacketIn packetIn = channel.readPacket(); - System.out.println("Received " + packetIn.getClass()); + if (packetIn instanceof PacketPlayInPositionAndLook) { PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn; Location from = player.getLocation(); @@ -757,33 +767,80 @@ public void run() { Limbo.getInstance().getEventsManager().callEvent(new PlayerInteractEvent(player, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, player.getEquipment().getItem(packet.getHand()), block, packet.getBlockHit().getDirection(), packet.getHand())); } else if (packetIn instanceof PacketPlayInSetCreativeSlot) { PacketPlayInSetCreativeSlot packet = (PacketPlayInSetCreativeSlot) packetIn; - InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player, player.getInventoryView(), player.getInventory(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack())); + InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player.getInventoryView(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack())); if (event.isCancelled()) { player.updateInventory(); } else { - player.getInventory().getUnsafe().b(packet.getSlotNumber(), event.getNewItem()); + player.getInventory().setItem(event.getSlot(), event.getNewItem()); } } else if (packetIn instanceof PacketPlayInWindowClick) { PacketPlayInWindowClick packet = (PacketPlayInWindowClick) packetIn; - /* - InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player, player.getInventoryView(), player.getInventory(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack())); - if (event.isCancelled()) { - player.updateInventory(); - } else { - player.getInventory().getUnsafe().b(packet.getSlotNumber(), event.getNewItem()); + try { + InventoryClickUtils.handle(player, packet); + } catch (Throwable e) { + e.printStackTrace(); } - */ } else if (packetIn instanceof PacketPlayInCloseWindow) { PacketPlayInCloseWindow packet = (PacketPlayInCloseWindow) packetIn; Inventory inventory = player.getInventoryView().getTopInventory(); if (inventory != null) { Integer id = inventory.getUnsafe().c().get(player); if (id != null) { - Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(player, player.getInventoryView())); - player.getInventoryView().getUnsafe().a(null); + Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(player.getInventoryView())); + player.getInventoryView().getUnsafe().a(null, null); inventory.getUnsafe().c().remove(player); } } + } else if (packetIn instanceof PacketPlayInBlockDig) { + PacketPlayInBlockDig packet = (PacketPlayInBlockDig) packetIn; + //noinspection SwitchStatementWithTooFewBranches + switch (packet.getAction()) { + case SWAP_ITEM_WITH_OFFHAND: { + EntityEquipment equipment = player.getEquipment(); + PlayerSwapHandItemsEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerSwapHandItemsEvent(player, equipment.getItemInOffHand(), equipment.getItemInMainHand())); + if (!event.isCancelled()) { + equipment.setItemInMainHand(event.getMainHandItem()); + equipment.setItemInOffHand(event.getOffHandItem()); + } + break; + } + } + } else if (packetIn instanceof PacketPlayInPickItem) { + PacketPlayInPickItem packet = (PacketPlayInPickItem) packetIn; + PlayerInventory inventory = player.getInventory(); + int slot = inventory.getUnsafe().b().applyAsInt(packet.getSlot()); + int i = player.getSelectedSlot(); + byte selectedSlot = -1; + boolean firstRun = true; + while (selectedSlot < 0 || (!firstRun && i == player.getSelectedSlot())) { + ItemStack itemStack = inventory.getItem(i); + if (itemStack == null) { + selectedSlot = (byte) i; + break; + } + if (++i >= 9) { + i = 0; + } + } + if (selectedSlot < 0) { + selectedSlot = player.getSelectedSlot(); + } + ItemStack leavingHotbar = inventory.getItem(selectedSlot); + inventory.setItem(selectedSlot, inventory.getItem(slot)); + inventory.setItem(slot, leavingHotbar); + player.setSelectedSlot(selectedSlot); + } else if (packetIn instanceof PacketPlayInItemName) { + PacketPlayInItemName packet = (PacketPlayInItemName) packetIn; + if (player.getInventoryView().getTopInventory() instanceof AnvilInventory) { + AnvilRenameInputEvent event = Limbo.getInstance().getEventsManager().callEvent(new AnvilRenameInputEvent(player.getInventoryView(), packet.getName())); + if (!event.isCancelled()) { + AnvilInventory anvilInventory = (AnvilInventory) player.getInventoryView().getTopInventory(); + ItemStack result = anvilInventory.getItem(2); + if (result != null) { + result.displayName(LegacyComponentSerializer.legacySection().deserialize(event.getInput())); + } + } + } } } catch (Exception e) { break; diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInBlockDig.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInBlockDig.java new file mode 100644 index 0000000..44db574 --- /dev/null +++ b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInBlockDig.java @@ -0,0 +1,74 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.network.protocol.packets; + +import com.loohp.limbo.location.BlockFace; +import com.loohp.limbo.utils.DataTypeIO; +import com.loohp.limbo.world.BlockPosition; + +import java.io.DataInputStream; +import java.io.IOException; + +public class PacketPlayInBlockDig extends PacketIn { + + public enum PlayerDigType { + + START_DESTROY_BLOCK, + ABORT_DESTROY_BLOCK, + STOP_DESTROY_BLOCK, + DROP_ALL_ITEMS, + DROP_ITEM, + RELEASE_USE_ITEM, + SWAP_ITEM_WITH_OFFHAND; + + } + + private PlayerDigType action; + private BlockPosition pos; + private BlockFace direction; + private int sequence; + + public PacketPlayInBlockDig(PlayerDigType action, BlockPosition pos, BlockFace direction, int sequence) { + this.action = action; + this.pos = pos; + this.direction = direction; + this.sequence = sequence; + } + + public PacketPlayInBlockDig(DataInputStream in) throws IOException { + this(PlayerDigType.values()[DataTypeIO.readVarInt(in)], DataTypeIO.readBlockPosition(in), BlockFace.values()[in.readByte()], DataTypeIO.readVarInt(in)); + } + + public BlockPosition getPos() { + return pos; + } + + public BlockFace getDirection() { + return direction; + } + + public PlayerDigType getAction() { + return action; + } + + public int getSequence() { + return sequence; + } +} diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInItemName.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInItemName.java new file mode 100644 index 0000000..71eec3b --- /dev/null +++ b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInItemName.java @@ -0,0 +1,43 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.network.protocol.packets; + +import com.loohp.limbo.utils.DataTypeIO; + +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class PacketPlayInItemName extends PacketIn { + + private String name; + + public PacketPlayInItemName(String name) { + this.name = name; + } + + public PacketPlayInItemName(DataInputStream in) throws IOException { + this(DataTypeIO.readString(in, StandardCharsets.UTF_8)); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInPickItem.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInPickItem.java new file mode 100644 index 0000000..efc8e04 --- /dev/null +++ b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayInPickItem.java @@ -0,0 +1,42 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.network.protocol.packets; + +import com.loohp.limbo.utils.DataTypeIO; + +import java.io.DataInputStream; +import java.io.IOException; + +public class PacketPlayInPickItem extends PacketIn { + + private int slot; + + public PacketPlayInPickItem(int slot) { + this.slot = slot; + } + + public PacketPlayInPickItem(DataInputStream in) throws IOException { + this(DataTypeIO.readVarInt(in)); + } + + public int getSlot() { + return slot; + } +} diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutPlayerAbilities.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutPlayerAbilities.java index 4dd5445..b78f4ae 100644 --- a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutPlayerAbilities.java +++ b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutPlayerAbilities.java @@ -31,7 +31,7 @@ public enum PlayerAbilityFlags { ALLOW_FLYING(0x04), CREATIVE(0x08); - int bitvalue; + private final int bitvalue; PlayerAbilityFlags(int bitvalue) { this.bitvalue = bitvalue; diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutSetSlot.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutSetSlot.java index 4cbf813..992347f 100644 --- a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutSetSlot.java +++ b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutSetSlot.java @@ -38,7 +38,7 @@ public PacketPlayOutSetSlot(int containerId, int stateId, int slot, ItemStack it this.containerId = containerId; this.stateId = stateId; this.slot = slot; - this.itemStack = itemStack; + this.itemStack = itemStack == null ? ItemStack.AIR : itemStack; } public int getContainerId() { diff --git a/src/main/java/com/loohp/limbo/player/Player.java b/src/main/java/com/loohp/limbo/player/Player.java index a4fefd1..091ee8e 100644 --- a/src/main/java/com/loohp/limbo/player/Player.java +++ b/src/main/java/com/loohp/limbo/player/Player.java @@ -132,8 +132,9 @@ public byte getSelectedSlot() { } public void setSelectedSlot(byte slot) { - if(slot == selectedSlot) + if (slot == selectedSlot) { return; + } try { PacketPlayOutHeldItemChange state = new PacketPlayOutHeldItemChange(slot); clientConnection.sendPacket(state); @@ -603,22 +604,22 @@ public void updateInventory() { } public void openInventory(Inventory inventory) { - inventoryView.getUnsafe().a(inventory); + Component title = inventory instanceof TitledInventory ? ((TitledInventory) inventory).getTitle() : Component.translatable("container.chest"); + inventoryView.getUnsafe().a(inventory, title); int id = nextContainerId(); inventory.getUnsafe().c().put(this, id); - InventoryOpenEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryOpenEvent(this, inventoryView)); + InventoryOpenEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryOpenEvent(inventoryView)); if (event.isCancelled()) { - inventoryView.getUnsafe().a(null); + inventoryView.getUnsafe().a(null, null); inventory.getUnsafe().c().remove(this); } else { - Component title = inventory instanceof TitledInventory ? ((TitledInventory) inventory).getTitle() : Component.translatable("container.chest"); PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, inventory.getType().getRawType(inventory.getSize()), title); try { clientConnection.sendPacket(packet); } catch (IOException e) { e.printStackTrace(); } - inventory.updateInventory(this); + inventoryView.updateView(); } } @@ -627,8 +628,8 @@ public void closeInventory() { if (inventory != null) { Integer id = inventory.getUnsafe().c().get(this); if (id != null) { - Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(this, inventoryView)); - inventoryView.getUnsafe().a(null); + Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(inventoryView)); + inventoryView.getUnsafe().a(null, null); inventory.getUnsafe().c().remove(this); PacketPlayOutCloseWindow packet = new PacketPlayOutCloseWindow(id); try { diff --git a/src/main/java/com/loohp/limbo/player/PlayerInventory.java b/src/main/java/com/loohp/limbo/player/PlayerInventory.java index 04e76c1..aa58a59 100644 --- a/src/main/java/com/loohp/limbo/player/PlayerInventory.java +++ b/src/main/java/com/loohp/limbo/player/PlayerInventory.java @@ -27,7 +27,6 @@ import com.loohp.limbo.inventory.EquipmentSlot; import com.loohp.limbo.inventory.InventoryType; import com.loohp.limbo.inventory.ItemStack; -import com.loohp.limbo.location.Location; import java.util.Arrays; import java.util.Collections; @@ -165,9 +164,4 @@ public void setArmorContents(ItemStack[] items) { } } - @Override - public Location getLocation() { - return player.getLocation(); - } - } diff --git a/src/main/java/com/loohp/limbo/utils/InventoryClickUtils.java b/src/main/java/com/loohp/limbo/utils/InventoryClickUtils.java new file mode 100644 index 0000000..891dc5d --- /dev/null +++ b/src/main/java/com/loohp/limbo/utils/InventoryClickUtils.java @@ -0,0 +1,531 @@ +/* + * This file is part of Limbo. + * + * Copyright (C) 2022. LoohpJames + * Copyright (C) 2022. Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.loohp.limbo.utils; + +import com.loohp.limbo.Limbo; +import com.loohp.limbo.events.inventory.InventoryClickEvent; +import com.loohp.limbo.events.inventory.InventoryDragEvent; +import com.loohp.limbo.inventory.ClickType; +import com.loohp.limbo.inventory.Inventory; +import com.loohp.limbo.inventory.InventoryAction; +import com.loohp.limbo.inventory.InventoryClickType; +import com.loohp.limbo.inventory.InventoryType; +import com.loohp.limbo.inventory.InventoryView; +import com.loohp.limbo.inventory.ItemStack; +import com.loohp.limbo.network.protocol.packets.PacketPlayInWindowClick; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot; +import com.loohp.limbo.player.Player; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; + +public class InventoryClickUtils { + + private static final Map QUICK_CRAFT_INFO = Collections.synchronizedMap(new WeakHashMap<>()); + + public static synchronized void handle(Player player, PacketPlayInWindowClick packetplayinwindowclick) { + InventoryClickEvent event; + + InventoryView inventory = player.getInventoryView(); + InventoryType.SlotType type = inventory.getSlotType(packetplayinwindowclick.getSlotNum()); + int rawSlot = packetplayinwindowclick.getSlotNum(); + + boolean cancelled = player.getGamemode().equals(GameMode.SPECTATOR); + ClickType click = ClickType.UNKNOWN; + InventoryAction action = InventoryAction.UNKNOWN; + + ItemStack itemstack = null; + + switch (packetplayinwindowclick.getClickType()) { + case PICKUP: + if (packetplayinwindowclick.getButtonNum() == 0) { + click = ClickType.LEFT; + } else if (packetplayinwindowclick.getButtonNum() == 1) { + click = ClickType.RIGHT; + } + if (packetplayinwindowclick.getButtonNum() == 0 || packetplayinwindowclick.getButtonNum() == 1) { + action = InventoryAction.NOTHING; // Don't want to repeat ourselves + if (packetplayinwindowclick.getSlotNum() == -999) { + if (inventory.getCarriedItem() != null) { + action = packetplayinwindowclick.getButtonNum() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; + } + } else if (packetplayinwindowclick.getSlotNum() < 0) { + action = InventoryAction.NOTHING; + } else { + ItemStack clickedItem = inventory.getItem(rawSlot); + if (inventory.isSlot(rawSlot)) { + ItemStack cursor = inventory.getCarriedItem(); + if (clickedItem == null) { + if (cursor != null) { + action = packetplayinwindowclick.getButtonNum() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; + } + } else { + if (cursor == null) { + action = packetplayinwindowclick.getButtonNum() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; + } else { + if (clickedItem.isSimilar(cursor)) { + int toPlace = packetplayinwindowclick.getButtonNum() == 0 ? cursor.amount() : 1; + toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.amount()); + toPlace = Math.min(toPlace, cursor.getMaxStackSize() - clickedItem.amount()); + if (toPlace == 1) { + action = InventoryAction.PLACE_ONE; + } else if (toPlace == cursor.amount()) { + action = InventoryAction.PLACE_ALL; + } else if (toPlace < 0) { + action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks + } else if (toPlace != 0) { + action = InventoryAction.PLACE_SOME; + } + } else if (cursor.amount() <= cursor.getMaxStackSize()) { + action = InventoryAction.SWAP_WITH_CURSOR; + } + } + } + } + } + } + break; + case QUICK_MOVE: + if (packetplayinwindowclick.getButtonNum() == 0) { + click = ClickType.SHIFT_LEFT; + } else if (packetplayinwindowclick.getButtonNum() == 1) { + click = ClickType.SHIFT_RIGHT; + } + if (packetplayinwindowclick.getButtonNum() == 0 || packetplayinwindowclick.getButtonNum() == 1) { + if (packetplayinwindowclick.getSlotNum() < 0) { + action = InventoryAction.NOTHING; + } else { + ItemStack slot = inventory.getItem(rawSlot); + if (inventory.isSlot(rawSlot) && slot != null) { + action = InventoryAction.MOVE_TO_OTHER_INVENTORY; + } else { + action = InventoryAction.NOTHING; + } + } + } + break; + case SWAP: + if ((packetplayinwindowclick.getButtonNum() >= 0 && packetplayinwindowclick.getButtonNum() < 9) || packetplayinwindowclick.getButtonNum() == 40) { + click = (packetplayinwindowclick.getButtonNum() == 40) ? ClickType.SWAP_OFFHAND : ClickType.NUMBER_KEY; + ItemStack clickedSlot = inventory.getItem(rawSlot); + ItemStack hotbar = inventory.getPlayer().getInventory().getItem(packetplayinwindowclick.getButtonNum()); + boolean canCleanSwap = hotbar == null || inventory.getInventory(rawSlot).equals(inventory.getPlayer().getInventory()); // the slot will accept the hotbar item + if (clickedSlot != null) { + if (canCleanSwap) { + action = InventoryAction.HOTBAR_SWAP; + } else { + action = InventoryAction.HOTBAR_MOVE_AND_READD; + } + } else if (clickedSlot == null && hotbar != null) { + action = InventoryAction.HOTBAR_SWAP; + } else { + action = InventoryAction.NOTHING; + } + } + break; + case CLONE: + if (packetplayinwindowclick.getButtonNum() == 2) { + click = ClickType.MIDDLE; + if (packetplayinwindowclick.getSlotNum() < 0) { + action = InventoryAction.NOTHING; + } else { + ItemStack slot = inventory.getItem(rawSlot); + if (inventory.isSlot(rawSlot) && slot != null && player.getGamemode().equals(GameMode.CREATIVE) && inventory.getCarriedItem() == null) { + action = InventoryAction.CLONE_STACK; + } else { + action = InventoryAction.NOTHING; + } + } + } else { + click = ClickType.UNKNOWN; + action = InventoryAction.UNKNOWN; + } + break; + case THROW: + if (packetplayinwindowclick.getSlotNum() >= 0) { + if (packetplayinwindowclick.getButtonNum() == 0) { + click = ClickType.DROP; + ItemStack slot = inventory.getItem(rawSlot); + if (inventory.isSlot(rawSlot) && slot != null && !slot.type().equals(ItemStack.AIR.type())) { + action = InventoryAction.DROP_ONE_SLOT; + } else { + action = InventoryAction.NOTHING; + } + } else if (packetplayinwindowclick.getButtonNum() == 1) { + click = ClickType.CONTROL_DROP; + ItemStack slot = inventory.getItem(rawSlot); + if (inventory.isSlot(rawSlot) && slot != null && !slot.type().equals(ItemStack.AIR.type())) { + action = InventoryAction.DROP_ALL_SLOT; + } else { + action = InventoryAction.NOTHING; + } + } + } else { + // Sane default (because this happens when they are holding nothing. Don't ask why.) + click = ClickType.LEFT; + if (packetplayinwindowclick.getButtonNum() == 1) { + click = ClickType.RIGHT; + } + action = InventoryAction.NOTHING; + } + break; + case PICKUP_ALL: + click = ClickType.DOUBLE_CLICK; + action = InventoryAction.NOTHING; + if (packetplayinwindowclick.getSlotNum() >= 0 && inventory.getCarriedItem() != null) { + ItemStack cursor = inventory.getCarriedItem(); + int amount = cursor == null ? 0 : cursor.amount(); + action = InventoryAction.NOTHING; + // Quick check for if we have any of the item + if ((inventory.getTopInventory() != null && inventory.getTopInventory().containsAtLeast(cursor, 1)) || inventory.getBottomInventory().containsAtLeast(cursor, 1)) { + action = InventoryAction.COLLECT_TO_CURSOR; + } + } + break; + case QUICK_CRAFT: { + QuickCraftInfo quickCraft; + synchronized (QUICK_CRAFT_INFO) { + quickCraft = QUICK_CRAFT_INFO.get(player); + if (quickCraft == null) { + QUICK_CRAFT_INFO.put(player, quickCraft = new QuickCraftInfo()); + } + } + int slotNum = packetplayinwindowclick.getSlotNum(); + int buttonNum = packetplayinwindowclick.getButtonNum(); + int quickcraftStatus = quickCraft.quickcraftStatus; + ItemStack itemstack1; + int l; + + quickCraft.quickcraftStatus = getQuickcraftHeader(buttonNum); + if ((quickcraftStatus != 1 || quickCraft.quickcraftStatus != 2) && quickcraftStatus != quickCraft.quickcraftStatus) { + quickCraft.resetQuickCraft(); + } else if (inventory.getCarriedItem() == null) { + quickCraft.resetQuickCraft(); + } else if (quickCraft.quickcraftStatus == 0) { + quickCraft.quickcraftType = getQuickcraftType(buttonNum); + if (isValidQuickcraftType(quickCraft.quickcraftType, player)) { + quickCraft.quickcraftStatus = 1; + quickCraft.quickcraftSlots.clear(); + } else { + quickCraft.resetQuickCraft(); + } + } else if (quickCraft.quickcraftStatus == 1) { + itemstack = inventory.getCarriedItem(); + if (canItemQuickReplace(inventory, slotNum, itemstack, true) && (quickCraft.quickcraftType == 2 || itemstack.amount() > quickCraft.quickcraftSlots.size())) { + quickCraft.quickcraftSlots.add(slotNum); + } + } else if (quickCraft.quickcraftStatus == 2) { + if (!quickCraft.quickcraftSlots.isEmpty()) { + itemstack1 = inventory.getCarriedItem(); + l = inventory.getCarriedItem().amount(); + Iterator iterator = quickCraft.quickcraftSlots.iterator(); + + Map draggedSlots = new HashMap<>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) + while (iterator.hasNext()) { + int slot1 = iterator.next(); + ItemStack slotItem = inventory.getItem(slot1); + ItemStack itemstack2 = inventory.getCarriedItem(); + + if (inventory.isSlot(slot1) && canItemQuickReplace(inventory, slot1, slotItem, true) && (quickCraft.quickcraftType == 2 || itemstack2.amount() >= quickCraft.quickcraftSlots.size())) { + ItemStack itemstack3 = itemstack1; + int j1 = slotItem != null ? slotItem.amount() : 0; + + itemstack3 = getQuickCraftSlotCount(quickCraft.quickcraftSlots, quickCraft.quickcraftType, itemstack3, j1); + int k1 = Math.min(itemstack3.getMaxStackSize(), slotItem == null ? 64 : slotItem.getMaxStackSize()); + + if (itemstack3.amount() > k1) { + itemstack3 = itemstack3.amount(k1); + } + + l -= itemstack3.amount() - j1; + // slot1.set(itemstack3); + draggedSlots.put(slot1, itemstack3); // CraftBukkit - Put in map instead of setting + } + } + + // CraftBukkit start - InventoryDragEvent + ItemStack newcursor = itemstack1.amount(l); + + // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. + ItemStack oldCursor = inventory.getCarriedItem(); + inventory.setCarriedItem(newcursor); + + InventoryDragEvent dragEvent = new InventoryDragEvent(inventory, (newcursor.type() != ItemStack.AIR.type() ? newcursor : null), oldCursor, quickCraft.quickcraftType == 1, draggedSlots); + Limbo.getInstance().getEventsManager().callEvent(dragEvent); + + if (!dragEvent.isCancelled()) { + for (Map.Entry dslot : draggedSlots.entrySet()) { + inventory.setItem(dslot.getKey(), dslot.getValue()); + } + // The only time the carried item will be set to null is if the inventory is closed by the server. + // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. + if (inventory.getCarriedItem() != null) { + inventory.setCarriedItem(dragEvent.getCarriedItem()); + } + } else { + inventory.setCarriedItem(oldCursor); + } + inventory.updateView(); + } + + quickCraft.resetQuickCraft(); + } else { + quickCraft.resetQuickCraft(); + } + break; + } + default: + break; + } + if (packetplayinwindowclick.getClickType() != InventoryClickType.QUICK_CRAFT) { + if (click == ClickType.NUMBER_KEY) { + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.getSlotNum(), click, action, packetplayinwindowclick.getButtonNum()); + } else { + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.getSlotNum(), click, action); + } + + event.setCancelled(cancelled); + Inventory oldTopInventory = player.getInventoryView().getTopInventory(); + Limbo.getInstance().getEventsManager().callEvent(event); + if (player.getInventoryView().getTopInventory() != oldTopInventory) { + return; + } + + if (event.isCancelled()) { + try { + switch (action) { + // Modified other slots + case PICKUP_ALL: + case MOVE_TO_OTHER_INVENTORY: + case HOTBAR_MOVE_AND_READD: + case HOTBAR_SWAP: + case COLLECT_TO_CURSOR: + case UNKNOWN: + player.getInventoryView().updateView(); + break; + // Modified cursor and clicked + case PICKUP_SOME: + case PICKUP_HALF: + case PICKUP_ONE: + case PLACE_ALL: + case PLACE_SOME: + case PLACE_ONE: + case SWAP_WITH_CURSOR: + player.clientConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, 0, inventory.getCarriedItem())); + player.clientConnection.sendPacket(new PacketPlayOutSetSlot(inventory.getUnsafe().a(), 0, packetplayinwindowclick.getSlotNum(), inventory.getItem(packetplayinwindowclick.getSlotNum()))); + break; + // Modified clicked only + case DROP_ALL_SLOT: + case DROP_ONE_SLOT: + player.clientConnection.sendPacket(new PacketPlayOutSetSlot(inventory.getUnsafe().a(), 0, packetplayinwindowclick.getSlotNum(), inventory.getItem(packetplayinwindowclick.getSlotNum()))); + break; + // Modified cursor only + case DROP_ALL_CURSOR: + case DROP_ONE_CURSOR: + case CLONE_STACK: + player.clientConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, 0, inventory.getCarriedItem())); + break; + // Nothing + case NOTHING: + break; + } + } catch (IOException e) { + e.printStackTrace(); + } + } else { + switch (event.getAction()) { + case PICKUP_ALL: { + inventory.setCarriedItem(event.getCurrentItem()); + inventory.setItem(event.getRawSlot(), null); + break; + } + case PICKUP_SOME: { + int amountTaken = Math.min(event.getCurrentItem().getMaxStackSize(), event.getCurrentItem().amount()); + inventory.setCarriedItem(event.getCurrentItem().amount(amountTaken)); + ItemStack oversize = event.getCurrentItem(); + inventory.setItem(event.getRawSlot(), oversize.amount(oversize.amount() - amountTaken)); + break; + } + case PICKUP_HALF: { + int amountTaken = (int) Math.ceil((double) event.getCurrentItem().amount() / 2.0); + inventory.setCarriedItem(event.getCurrentItem().amount(amountTaken)); + ItemStack left = event.getCurrentItem(); + inventory.setItem(event.getRawSlot(), left.amount(left.amount() - amountTaken)); + break; + } + case PICKUP_ONE: { + inventory.setCarriedItem(event.getCurrentItem().amount(1)); + ItemStack left = event.getCurrentItem(); + inventory.setItem(event.getRawSlot(), left.amount(left.amount() - 1)); + break; + } + case PLACE_ALL: { + ItemStack stack = event.getCarriedItem(); + inventory.setCarriedItem(null); + ItemStack item = event.getCurrentItem(); + inventory.setItem(event.getRawSlot(), stack.amount((item == null ? 0 : item.amount()) + stack.amount())); + break; + } + case PLACE_SOME: { + ItemStack stack = event.getCarriedItem(); + ItemStack item = event.getCurrentItem(); + int amountPlaced = item.getMaxStackSize() - item.amount(); + inventory.setItem(event.getRawSlot(), item.amount(item.getMaxStackSize())); + inventory.setCarriedItem(event.getCarriedItem().amount(event.getCarriedItem().amount() - amountPlaced)); + break; + } + case PLACE_ONE: { + ItemStack stack = event.getCarriedItem(); + ItemStack item = event.getCurrentItem(); + inventory.setItem(event.getRawSlot(), item == null ? stack.amount(1) : item.amount(item.amount() + 1)); + inventory.setCarriedItem(event.getCarriedItem().amount(event.getCarriedItem().amount() - 1)); + break; + } + case SWAP_WITH_CURSOR: { + ItemStack stack = event.getCarriedItem(); + inventory.setCarriedItem(event.getCurrentItem()); + inventory.setItem(event.getRawSlot(), stack); + break; + } + case DROP_ALL_CURSOR: { + inventory.setCarriedItem(null); + break; + } + case DROP_ONE_CURSOR: { + inventory.setCarriedItem(event.getCarriedItem().amount(event.getCarriedItem().amount() - 1)); + break; + } + case DROP_ALL_SLOT: { + inventory.setItem(event.getRawSlot(), null); + break; + } + case DROP_ONE_SLOT: { + ItemStack item = event.getCurrentItem(); + inventory.setItem(event.getRawSlot(), item.amount(item.amount() - 1)); + break; + } + case MOVE_TO_OTHER_INVENTORY: { + ItemStack item = event.getCurrentItem(); + Inventory inv; + if (event.getClickedInventory() == inventory.getTopInventory()) { + inv = inventory.getBottomInventory(); + } else { + inv = inventory.getTopInventory(); + } + HashMap leftOver = inv.addItem(item); + if (leftOver.isEmpty()) { + inventory.setItem(event.getRawSlot(), null); + } else { + inventory.setItem(event.getRawSlot(), leftOver.values().iterator().next()); + } + break; + } + case HOTBAR_MOVE_AND_READD: { + ItemStack item = inventory.getPlayer().getInventory().getItem(event.getHotbarKey()); + inventory.getPlayer().getInventory().setItem(event.getHotbarKey(), event.getCurrentItem()); + inventory.setItem(event.getRawSlot(), null); + inventory.getPlayer().getInventory().addItem(item); + break; + } + case HOTBAR_SWAP: { + int hotbarNum = event.getClick().equals(ClickType.SWAP_OFFHAND) ? 40 : event.getHotbarKey(); + ItemStack item = inventory.getPlayer().getInventory().getItem(hotbarNum); + inventory.getPlayer().getInventory().setItem(hotbarNum, event.getCurrentItem()); + inventory.setItem(event.getRawSlot(), item); + break; + } + case CLONE_STACK: { + ItemStack item = event.getCurrentItem(); + inventory.setCarriedItem(item.amount(item.getMaxStackSize())); + break; + } + case COLLECT_TO_CURSOR: { + ItemStack item = event.getCarriedItem(); + ItemStack toSearch = item.amount(item.getMaxStackSize() - item.amount()); + HashMap grabbed = event.getClickedInventory().removeItem(toSearch); + int newAmount = item.amount() + toSearch.amount(); + if (!grabbed.isEmpty()) { + newAmount -= grabbed.values().iterator().next().amount(); + } + inventory.setCarriedItem(item.amount(newAmount)); + break; + } + } + } + inventory.updateView(); + } + } + + public static int getQuickcraftType(int i) { + return i >> 2 & 3; + } + + public static int getQuickcraftHeader(int i) { + return i & 3; + } + + public static int getQuickcraftMask(int i, int j) { + return i & 3 | (j & 3) << 2; + } + + public static boolean isValidQuickcraftType(int i, Player player) { + return i == 0 || (i == 1 || i == 2 && player.getGamemode().equals(GameMode.CREATIVE)); + } + + public static boolean canItemQuickReplace(InventoryView view, int slot, ItemStack itemstack, boolean flag) { + boolean flag1 = !view.isSlot(slot) || view.getItem(slot) == null; + ItemStack slotItem = view.getItem(slot); + return !flag1 && slotItem.isSimilar(itemstack) ? slotItem.amount() + (flag ? 0 : itemstack.amount()) <= itemstack.getMaxStackSize() : flag1; + } + + public static ItemStack getQuickCraftSlotCount(Set set, int i, ItemStack itemstack, int j) { + switch (i) { + case 0: + itemstack = itemstack.amount((int) Math.floor((float) itemstack.amount() / (float) set.size())); + break; + case 1: + itemstack = itemstack.amount(1); + break; + case 2: + itemstack = itemstack.amount(itemstack.getMaxStackSize()); + } + return itemstack.amount(itemstack.amount() + j); + } + + public static class QuickCraftInfo { + + public int quickcraftType; + public int quickcraftStatus; + public final Set quickcraftSlots = ConcurrentHashMap.newKeySet(); + + public void resetQuickCraft() { + quickcraftStatus = 0; + quickcraftSlots.clear(); + } + + } + +} diff --git a/src/main/resources/mapping.json b/src/main/resources/mapping.json index 019a472..bab6de6 100644 --- a/src/main/resources/mapping.json +++ b/src/main/resources/mapping.json @@ -26,7 +26,10 @@ "0x31": "PacketPlayInUseItem", "0x2B": "PacketPlayInSetCreativeSlot", "0x0A": "PacketPlayInWindowClick", - "0x0B": "PacketPlayInCloseWindow" + "0x0B": "PacketPlayInCloseWindow", + "0x19": "PacketPlayInPickItem", + "0x1C": "PacketPlayInBlockDig", + "0x23": "PacketPlayInItemName" }, "PlayOut": { "PacketPlayOutLogin": "0x24",