Lot of inventory improvements/fixes

This commit is contained in:
Felix Cravic 2020-05-22 23:19:04 +02:00
parent d05a2feff3
commit 45d901a0bc
8 changed files with 227 additions and 93 deletions

View File

@ -11,6 +11,7 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.play.SetSlotPacket;
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
import net.minestom.server.network.packet.server.play.WindowPropertyPacket;
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
import java.util.Arrays;
import java.util.Collections;
@ -76,7 +77,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
@Override
public void setItemStack(int slot, ItemStack itemStack) {
if (slot < 0 || slot > inventoryType.getAdditionalSlot())
if (slot < 0 || slot > getSize())
throw new IllegalArgumentException(inventoryType.toString() + " does not have slot " + slot);
safeItemInsert(slot, itemStack);
@ -97,6 +98,11 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
return Arrays.copyOf(itemStacks, itemStacks.length);
}
@Override
public int getSize() {
return size;
}
@Override
public List<InventoryCondition> getInventoryConditions() {
return inventoryConditions;
@ -107,11 +113,28 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
this.inventoryConditions.add(inventoryCondition);
}
/**
* Refresh the inventory for all viewers
*/
public void update() {
WindowItemsPacket windowItemsPacket = getWindowItemsPacket();
sendPacketToViewers(windowItemsPacket);
}
/**
* Refresh the inventory for a specific viewer
* the player needs to be a viewer, otherwise nothing is sent
*
* @param player the player to update the inventory
*/
public void update(Player player) {
if (!getViewers().contains(player))
return;
WindowItemsPacket windowItemsPacket = getWindowItemsPacket();
player.getPlayerConnection().sendPacket(windowItemsPacket);
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
@ -168,11 +191,11 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
}
private boolean isClickInWindow(int slot) {
return slot < inventoryType.getAdditionalSlot();
return slot < getSize();
}
@Override
public void leftClick(Player player, int slot) {
public boolean leftClick(Player player, int slot) {
PlayerInventory playerInventory = player.getInventory();
ItemStack cursor = getCursorItem(player);
boolean isInWindow = isClickInWindow(slot);
@ -181,8 +204,9 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
InventoryClickResult clickResult = clickProcessor.leftClick(this, player, slot, clicked, cursor);
if (clickResult.doRefresh())
player.getPlayerConnection().sendPacket(getWindowItemsPacket());
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
}
if (isInWindow) {
setItemStack(slot, clickResult.getClicked());
@ -194,10 +218,12 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
if (!clickResult.isCancel())
callClickEvent(player, this, slot, ClickType.LEFT_CLICK, clicked, cursor);
return !clickResult.isCancel();
}
@Override
public void rightClick(Player player, int slot) {
public boolean rightClick(Player player, int slot) {
PlayerInventory playerInventory = player.getInventory();
ItemStack cursor = getCursorItem(player);
boolean isInWindow = isClickInWindow(slot);
@ -205,8 +231,9 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
InventoryClickResult clickResult = clickProcessor.rightClick(this, player, slot, clicked, cursor);
if (clickResult.doRefresh())
player.getPlayerConnection().sendPacket(getWindowItemsPacket());
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
}
if (isInWindow) {
setItemStack(slot, clickResult.getClicked());
@ -218,10 +245,12 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
if (!clickResult.isCancel())
callClickEvent(player, this, slot, ClickType.RIGHT_CLICK, clicked, cursor);
return !clickResult.isCancel();
}
@Override
public void shiftClick(Player player, int slot) {
public boolean shiftClick(Player player, int slot) {
PlayerInventory playerInventory = player.getInventory();
boolean isInWindow = isClickInWindow(slot);
ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(slot, offset);
@ -234,7 +263,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
clickResult = clickProcessor.shiftClick(this, player, slot, clicked, cursor,
// Player inventory loop
new InventoryClickLoopHandler(0, PlayerInventory.INVENTORY_SIZE, 1,
i -> playerInventory.convertToPacketSlot(i),
i -> PlayerInventoryUtils.convertToPacketSlot(i),
index -> isClickInWindow(index) ? getItemStack(index) : playerInventory.getItemStack(index, offset),
(index, itemStack) -> {
if (isClickInWindow(index)) {
@ -259,18 +288,21 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
}
if (clickResult == null)
return;
return false;
if (clickResult.doRefresh())
update();
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
}
setCursorPlayerItem(player, clickResult.getCursor());
playerInventory.update();
update();
return !clickResult.isCancel();
}
@Override
public void changeHeld(Player player, int slot, int key) {
public boolean changeHeld(Player player, int slot, int key) {
PlayerInventory playerInventory = player.getInventory();
boolean isInWindow = isClickInWindow(slot);
ItemStack clicked = isInWindow ? getItemStack(slot) : playerInventory.getItemStack(slot, offset);
@ -278,8 +310,9 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
InventoryClickResult clickResult = clickProcessor.changeHeld(this, player, slot, key, clicked, heldItem);
if (clickResult.doRefresh())
player.getPlayerConnection().sendPacket(getWindowItemsPacket());
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
}
if (isInWindow) {
setItemStack(slot, clickResult.getClicked());
@ -290,15 +323,18 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
if (!clickResult.isCancel())
callClickEvent(player, this, slot, ClickType.CHANGE_HELD, clicked, getCursorItem(player));
return !clickResult.isCancel();
}
@Override
public void middleClick(Player player, int slot) {
public boolean middleClick(Player player, int slot) {
// TODO
return false;
}
@Override
public void drop(Player player, int mode, int slot, int button) {
public boolean drop(Player player, int mode, int slot, int button) {
PlayerInventory playerInventory = player.getInventory();
boolean isInWindow = isClickInWindow(slot);
ItemStack clicked = slot == -999 ?
@ -308,8 +344,9 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
InventoryClickResult clickResult = clickProcessor.drop(this, player,
mode, slot, button, clicked, cursor);
if (clickResult.doRefresh())
player.getPlayerConnection().sendPacket(getWindowItemsPacket());
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
}
ItemStack resultClicked = clickResult.getClicked();
if (isInWindow) {
@ -322,10 +359,11 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
setCursorPlayerItem(player, clickResult.getCursor());
}
return !clickResult.isCancel();
}
@Override
public void dragging(Player player, int slot, int button) {
public boolean dragging(Player player, int slot, int button) {
PlayerInventory playerInventory = player.getInventory();
boolean isInWindow = isClickInWindow(slot);
ItemStack clicked = null;
@ -347,18 +385,21 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
}
});
if (clickResult == null)
return;
if (isInWindow) {
setCursorPlayerItem(player, clickResult.getCursor());
} else {
setCursorPlayerItem(player, clickResult.getCursor());
if (clickResult == null) {
return false;
}
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);
}
setCursorPlayerItem(player, clickResult.getCursor());
return !clickResult.isCancel();
}
@Override
public void doubleClick(Player player, int slot) {
public boolean doubleClick(Player player, int slot) {
PlayerInventory playerInventory = player.getInventory();
ItemStack cursor = getCursorItem(player);
@ -371,24 +412,39 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
(index, itemStack) -> setItemStack(index, itemStack)),
// Looping through player inventory
new InventoryClickLoopHandler(0, PlayerInventory.INVENTORY_SIZE - 9, 1,
i -> playerInventory.convertToPacketSlot(i),
i -> PlayerInventoryUtils.convertToPacketSlot(i),
index -> playerInventory.getItemStack(index, offset),
(index, itemStack) -> playerInventory.setItemStack(index, offset, itemStack)),
// Player hotbar
new InventoryClickLoopHandler(0, 9, 1,
i -> playerInventory.convertToPacketSlot(i),
i -> PlayerInventoryUtils.convertToPacketSlot(i),
index -> playerInventory.getItemStack(index, offset),
(index, itemStack) -> {
playerInventory.setItemStack(index, offset, itemStack);
}));
if (clickResult == null)
return;
return false;
if (clickResult.doRefresh())
update();
updateFromClick(clickResult, player);
setCursorPlayerItem(player, clickResult.getCursor());
playerInventory.update();
return !clickResult.isCancel();
}
/**
* Used to update the inventory for a specific player in order to fix his cancelled actions
*
* @param clickResult the action result
* @param player the player who did the action
*/
private void updateFromClick(InventoryClickResult clickResult, Player player) {
if (clickResult.isPlayerInventory()) {
player.getInventory().update();
} else {
update(player);
}
}
}

View File

@ -5,23 +5,28 @@ import net.minestom.server.event.inventory.InventoryClickEvent;
import net.minestom.server.inventory.click.ClickType;
import net.minestom.server.item.ItemStack;
/**
* Represent an inventory which can receive click input
* <p>
* all methods returning boolean returns true if the action is successful, false otherwise
*/
public interface InventoryClickHandler {
void leftClick(Player player, int slot);
boolean leftClick(Player player, int slot);
void rightClick(Player player, int slot);
boolean rightClick(Player player, int slot);
void shiftClick(Player player, int slot); // shift + left/right click have the same behavior
boolean shiftClick(Player player, int slot); // shift + left/right click have the same behavior
void changeHeld(Player player, int slot, int key);
boolean changeHeld(Player player, int slot, int key);
void middleClick(Player player, int slot);
boolean middleClick(Player player, int slot);
void drop(Player player, int mode, int slot, int button);
boolean drop(Player player, int mode, int slot, int button);
void dragging(Player player, int slot, int button);
boolean dragging(Player player, int slot, int button);
void doubleClick(Player player, int slot);
boolean doubleClick(Player player, int slot);
default void callClickEvent(Player player, Inventory inventory, int slot,
ClickType clickType, ItemStack clicked, ItemStack cursor) {

View File

@ -15,6 +15,8 @@ public interface InventoryModifier {
ItemStack[] getItemStacks();
int getSize();
List<InventoryCondition> getInventoryConditions();
void addInventoryCondition(InventoryCondition inventoryCondition);

View File

@ -13,6 +13,7 @@ import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
import net.minestom.server.network.packet.server.play.SetSlotPacket;
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.MathUtils;
import java.util.Arrays;
import java.util.List;
@ -101,6 +102,11 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
return false;
}
@Override
public int getSize() {
return INVENTORY_SIZE;
}
@Override
public ItemStack getItemInMainHand() {
return getItemStack(player.getHeldSlot());
@ -235,24 +241,9 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
return this.items[slot];
}
protected int convertToPacketSlot(int slot) {
if (slot > -1 && slot < 9) { // Held bar 0-9
slot = slot + 36;
} else if (slot > 8 && slot < 36) { // Inventory 9-35
slot = slot;
} else if (slot >= CRAFT_RESULT && slot <= CRAFT_SLOT_4) { // Crafting 36-40
slot = slot - 36;
} else if (slot >= HELMET_SLOT && slot <= BOOTS_SLOT) { // Armor 41-44
slot = slot - 36;
} else if (slot == OFFHAND_SLOT) { // Off hand
slot = 45;
}
return slot;
}
private void sendSlotRefresh(short slot, ItemStack itemStack) {
SetSlotPacket setSlotPacket = new SetSlotPacket();
setSlotPacket.windowId = (byte) (slot > 35 && slot < INVENTORY_SIZE ? 0 : -2);
setSlotPacket.windowId = (byte) (MathUtils.isBetween(slot, 35, INVENTORY_SIZE) ? 0 : -2);
setSlotPacket.slot = slot;
setSlotPacket.itemStack = itemStack;
player.getPlayerConnection().sendPacket(setSlotPacket);
@ -274,7 +265,7 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
}
@Override
public void leftClick(Player player, int slot) {
public boolean leftClick(Player player, int slot) {
ItemStack cursor = getCursorItem();
ItemStack clicked = getItemStack(convertSlot(slot, OFFSET));
@ -288,10 +279,12 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
if (!clickResult.isCancel())
callClickEvent(player, null, slot, ClickType.LEFT_CLICK, clicked, cursor);
return !clickResult.isCancel();
}
@Override
public void rightClick(Player player, int slot) {
public boolean rightClick(Player player, int slot) {
ItemStack cursor = getCursorItem();
ItemStack clicked = getItemStack(slot, OFFSET);
@ -305,15 +298,18 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
if (!clickResult.isCancel())
callClickEvent(player, null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
return !clickResult.isCancel();
}
@Override
public void middleClick(Player player, int slot) {
public boolean middleClick(Player player, int slot) {
// TODO
return false;
}
@Override
public void drop(Player player, int mode, int slot, int button) {
public boolean drop(Player player, int mode, int slot, int button) {
ItemStack cursor = getCursorItem();
ItemStack clicked = slot == -999 ? null : getItemStack(slot, OFFSET);
@ -327,10 +323,12 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
if (resultClicked != null)
setItemStack(slot, OFFSET, resultClicked);
setCursorItem(clickResult.getCursor());
return !clickResult.isCancel();
}
@Override
public void shiftClick(Player player, int slot) {
public boolean shiftClick(Player player, int slot) {
ItemStack cursor = getCursorItem();
ItemStack clicked = getItemStack(slot, OFFSET);
@ -348,18 +346,20 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
(index, itemStack) -> setItemStack(index, OFFSET, itemStack)));
if (clickResult == null)
return;
return false;
if (clickResult.doRefresh())
update();
setCursorItem(clickResult.getCursor());
return !clickResult.isCancel();
}
@Override
public void changeHeld(Player player, int slot, int key) {
public boolean changeHeld(Player player, int slot, int key) {
if (!getCursorItem().isAir())
return;
return false;
ItemStack heldItem = getItemStack(key);
ItemStack clicked = getItemStack(slot, OFFSET);
@ -374,10 +374,12 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
if (!clickResult.isCancel())
callClickEvent(player, null, slot, ClickType.CHANGE_HELD, clicked, getCursorItem());
return !clickResult.isCancel();
}
@Override
public void dragging(Player player, int slot, int button) {
public boolean dragging(Player player, int slot, int button) {
ItemStack cursor = getCursorItem();
ItemStack clicked = null;
if (slot != -999)
@ -388,17 +390,20 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
clicked, cursor, s -> getItemStack(s, OFFSET),
(s, item) -> setItemStack(s, OFFSET, item));
if (clickResult == null)
return;
if (clickResult == null) {
return false;
}
if (clickResult.doRefresh())
update();
setCursorItem(clickResult.getCursor());
return !clickResult.isCancel();
}
@Override
public void doubleClick(Player player, int slot) {
public boolean doubleClick(Player player, int slot) {
ItemStack cursor = getCursorItem();
InventoryClickResult clickResult = clickProcessor.doubleClick(null, player, slot, cursor,
@ -408,11 +413,13 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
(index, itemStack) -> setItemStack(index, itemStack)));
if (clickResult == null)
return;
return false;
if (clickResult.doRefresh())
update();
setCursorItem(clickResult.getCursor());
return !clickResult.isCancel();
}
}

View File

@ -127,7 +127,10 @@ public class InventoryClickProcessor {
return clickResult;
}
clickResult = startCondition(clickResult, inventory, player, key, ClickType.CHANGE_HELD, clicked, cursor);
// Converted again during the inventory condition calling to internal slot
int keySlot = PlayerInventoryUtils.convertToPacketSlot(key);
clickResult = startCondition(clickResult, null, player, keySlot, ClickType.CHANGE_HELD, clicked, cursor);
if (clickResult.isCancel()) {
return clickResult;
}
@ -478,13 +481,18 @@ public class InventoryClickProcessor {
return clickResult;
}
private InventoryClickResult startCondition(InventoryClickResult clickResult, Inventory inventory, Player player, int slot, ClickType clickType, ItemStack clicked, ItemStack cursor) {
private InventoryClickResult startCondition(InventoryClickResult clickResult, Inventory inventory,
Player player, int slot, ClickType clickType,
ItemStack clicked, ItemStack cursor) {
boolean isPlayerInventory = inventory == null;
int inventorySlot = isPlayerInventory ? 0 : inventory.getInventoryType().getAdditionalSlot();
int inventorySlot = isPlayerInventory ? 0 : inventory.getSize();
isPlayerInventory = isPlayerInventory ? isPlayerInventory : slot >= inventorySlot;
if (isPlayerInventory && inventory != null)
clickResult.setPlayerInventory(isPlayerInventory);
if (isPlayerInventory && inventory != null) {
slot = slot - inventorySlot + PlayerInventoryUtils.OFFSET;
}
List<InventoryCondition> inventoryConditions = isPlayerInventory ?
player.getInventory().getInventoryConditions() : inventory.getInventoryConditions();
@ -507,8 +515,8 @@ public class InventoryClickProcessor {
cursor = result.getCursorItem();
clicked = result.getClickedItem();
clickResult.setClicked(clicked);
clickResult.setCursor(cursor);
clickResult.setClicked(clicked);
clickResult.setCancel(result.isCancel());
if (result.isCancel()) {

View File

@ -7,6 +7,8 @@ public class InventoryClickResult {
private ItemStack clicked;
private ItemStack cursor;
private boolean playerInventory;
private boolean cancel;
private boolean refresh;
@ -31,6 +33,14 @@ public class InventoryClickResult {
this.cursor = cursor;
}
public boolean isPlayerInventory() {
return playerInventory;
}
protected void setPlayerInventory(boolean playerInventory) {
this.playerInventory = playerInventory;
}
public boolean isCancel() {
return cancel;
}

View File

@ -27,10 +27,7 @@ public class WindowListener {
// System.out.println("Window id: " + windowId + " | slot: " + slot + " | button: " + button + " | mode: " + mode);
WindowConfirmationPacket windowConfirmationPacket = new WindowConfirmationPacket();
windowConfirmationPacket.windowId = windowId;
windowConfirmationPacket.actionNumber = actionNumber;
windowConfirmationPacket.accepted = true; // Change depending on output
boolean successful = false;
switch (mode) {
case 0:
@ -38,53 +35,74 @@ public class WindowListener {
case 0:
if (slot != -999) {
// Left click
clickHandler.leftClick(player, slot);
successful = clickHandler.leftClick(player, slot);
} else {
// DROP
clickHandler.drop(player, mode, slot, button);
successful = clickHandler.drop(player, mode, slot, button);
}
break;
case 1:
if (slot != -999) {
// Right click
clickHandler.rightClick(player, slot);
successful = clickHandler.rightClick(player, slot);
} else {
// DROP
clickHandler.drop(player, mode, slot, button);
successful = clickHandler.drop(player, mode, slot, button);
}
break;
}
break;
case 1:
clickHandler.shiftClick(player, slot); // Shift + left/right have identical behavior
successful = clickHandler.shiftClick(player, slot); // Shift + left/right have identical behavior
break;
case 2:
clickHandler.changeHeld(player, slot, button);
successful = clickHandler.changeHeld(player, slot, button);
break;
case 3:
// Middle click (only creative players in non-player inventories)
break;
case 4:
// Dropping functions
clickHandler.drop(player, mode, slot, button);
successful = clickHandler.drop(player, mode, slot, button);
break;
case 5:
// Dragging
clickHandler.dragging(player, slot, button);
successful = clickHandler.dragging(player, slot, button);
break;
case 6:
clickHandler.doubleClick(player, slot);
successful = clickHandler.doubleClick(player, slot);
break;
}
ItemStack cursorItem = clickHandler instanceof Inventory ? ((Inventory) clickHandler).getCursorItem(player) : ((PlayerInventory) clickHandler).getCursorItem();
refreshCursorItem(player, player.getOpenInventory());
WindowConfirmationPacket windowConfirmationPacket = new WindowConfirmationPacket();
windowConfirmationPacket.windowId = windowId;
windowConfirmationPacket.actionNumber = actionNumber;
windowConfirmationPacket.accepted = successful;
player.getPlayerConnection().sendPacket(windowConfirmationPacket);
}
private static void refreshCursorItem(Player player, Inventory inventory) {
PlayerInventory playerInventory = player.getInventory();
ItemStack cursorItem;
if (inventory != null) {
cursorItem = inventory.getCursorItem(player);
} else {
cursorItem = playerInventory.getCursorItem();
}
// Setting the window id properly seems to broke +64 stack support
//byte windowId = inventory == null ? 0 : inventory.getWindowId();
SetSlotPacket setSlotPacket = new SetSlotPacket();
setSlotPacket.windowId = -1;
setSlotPacket.slot = -1;
setSlotPacket.itemStack = cursorItem;
player.getPlayerConnection().sendPacket(setSlotPacket);
player.getPlayerConnection().sendPacket(windowConfirmationPacket);
}
public static void closeWindowListener(ClientCloseWindow packet, Player player) {

View File

@ -16,6 +16,13 @@ public class PlayerInventoryUtils {
public static final int BOOTS_SLOT = 44;
public static final int OFFHAND_SLOT = 45;
/**
* Convert a packet slot to an internal one
*
* @param slot the packet slot
* @param offset the slot count separating the up part of the inventory to the bottom part (armor/craft in PlayerInventory, inventory slots in others)
* @return a packet which can be use internally with Minestom
*/
public static int convertSlot(int slot, int offset) {
switch (slot) {
case 0:
@ -48,4 +55,25 @@ public class PlayerInventoryUtils {
//System.out.println("CONVERT: " + slot);
return slot;
}
/**
* Used to convert internal slot to one used in packets
*
* @param slot the internal slot
* @return a slot id which can be used for packets
*/
public static int convertToPacketSlot(int slot) {
if (slot > -1 && slot < 9) { // Held bar 0-9
slot = slot + 36;
} else if (slot > 8 && slot < 36) { // Inventory 9-35
slot = slot;
} else if (slot >= CRAFT_RESULT && slot <= CRAFT_SLOT_4) { // Crafting 36-40
slot = slot - 36;
} else if (slot >= HELMET_SLOT && slot <= BOOTS_SLOT) { // Armor 41-44
slot = slot - 36;
} else if (slot == OFFHAND_SLOT) { // Off hand
slot = 45;
}
return slot;
}
}