Fix inventory click

This commit is contained in:
TheMode 2021-06-06 01:50:28 +02:00
parent a63c16892c
commit 56eead948a
11 changed files with 146 additions and 94 deletions

View File

@ -3,8 +3,8 @@ package net.minestom.server.entity.fakeplayer;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.AbstractInventory;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.client.ClientPlayPacket;
@ -43,10 +43,9 @@ public class FakePlayerController {
* @param playerInventory {@code true} if the window a {@link PlayerInventory}, otherwise {@code false}.
* @param slot The slot where the fake player should click on.
* @param button The mouse button that the fake player should used.
* @param action The action that the fake player should perform.
* @param mode The inventory operation mode that the fake player should used.
* @param clickType The click type
*/
public void clickWindow(boolean playerInventory, short slot, byte button, short action, int mode) {
public void clickWindow(boolean playerInventory, short slot, byte button, ClientClickWindowPacket.ClickType clickType) {
Inventory inventory = playerInventory ? null : fakePlayer.getOpenInventory();
AbstractInventory abstractInventory = inventory == null ? fakePlayer.getInventory() : inventory;
playerInventory = abstractInventory instanceof PlayerInventory;
@ -59,8 +58,7 @@ public class FakePlayerController {
clickWindowPacket.windowId = playerInventory ? 0 : inventory.getWindowId();
clickWindowPacket.slot = slot;
clickWindowPacket.button = button;
clickWindowPacket.actionNumber = action;
clickWindowPacket.mode = mode;
clickWindowPacket.clickType = clickType;
clickWindowPacket.item = itemStack;
addToQueue(clickWindowPacket);
}

View File

@ -398,7 +398,7 @@ public class Inventory extends AbstractInventory implements Viewable {
}
@Override
public boolean drop(@NotNull Player player, int mode, int slot, int button) {
public boolean drop(@NotNull Player player, boolean all, int slot, int button) {
final PlayerInventory playerInventory = player.getInventory();
final boolean isInWindow = isClickInWindow(slot);
final boolean outsideDrop = slot == -999;
@ -408,7 +408,7 @@ public class Inventory extends AbstractInventory implements Viewable {
final ItemStack cursor = getCursorItem(player);
final InventoryClickResult clickResult = clickProcessor.drop(isInWindow ? this : null, player,
mode, slot, button, clicked, cursor);
all, slot, button, clicked, cursor);
if (clickResult.doRefresh()) {
updateFromClick(clickResult, player);

View File

@ -57,12 +57,12 @@ public interface InventoryClickHandler {
* Called when a {@link Player} press the drop button
*
* @param player the player who clicked
* @param mode
* @param all
* @param slot the slot number
* @param button -999 if clicking outside, normal if he is not
* @return true if the drop hasn't been cancelled, false otherwise
*/
boolean drop(@NotNull Player player, int mode, int slot, int button);
boolean drop(@NotNull Player player, boolean all, int slot, int button);
boolean dragging(@NotNull Player player, int slot, int button);

View File

@ -311,13 +311,13 @@ public class PlayerInventory extends AbstractInventory implements EquipmentHandl
}
@Override
public boolean drop(@NotNull Player player, int mode, int slot, int button) {
public boolean drop(@NotNull Player player, boolean all, int slot, int button) {
final ItemStack cursor = getCursorItem();
final boolean outsideDrop = slot == -999;
final ItemStack clicked = outsideDrop ? ItemStack.AIR : getItemStack(slot, OFFSET);
final InventoryClickResult clickResult = clickProcessor.drop(null, player,
mode, slot, button, clicked, cursor);
all, slot, button, clicked, cursor);
if (clickResult.doRefresh())
sendSlotRefresh((short) slot, clicked);

View File

@ -416,7 +416,7 @@ public class InventoryClickProcessor {
@NotNull
public InventoryClickResult drop(@Nullable Inventory inventory, @NotNull Player player,
int mode, int slot, int button,
boolean all, int slot, int button,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
final InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.DROP, clicked, cursor);
@ -454,7 +454,7 @@ public class InventoryClickProcessor {
}
}
} else if (mode == 4) {
} else if (!all) {
if (button == 0) {
// Drop key Q (drop 1)
final ItemStack dropItem = cursorRule.apply(resultClicked, 1);

View File

@ -2,12 +2,14 @@ package net.minestom.server.listener;
import net.minestom.server.entity.Player;
import net.minestom.server.event.inventory.InventoryCloseEvent;
import net.minestom.server.inventory.AbstractInventory;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryClickHandler;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.client.play.ClientClickWindowPacket;
import net.minestom.server.network.packet.client.play.ClientCloseWindowPacket;
import net.minestom.server.network.packet.client.play.ClientPongPacket;
import net.minestom.server.network.packet.server.play.PingPacket;
import net.minestom.server.network.packet.server.play.SetSlotPacket;
import java.util.Objects;
@ -15,23 +17,18 @@ import java.util.Objects;
public class WindowListener {
public static void clickWindowListener(ClientClickWindowPacket packet, Player player) {
final Inventory inventory;
final byte windowId = packet.windowId;
if (windowId == 0) {
inventory = null;
} else {
inventory = player.getOpenInventory();
final AbstractInventory inventory = windowId == 0 ? player.getInventory() : player.getOpenInventory();
if (inventory == null) {
// Invalid packet
return;
}
InventoryClickHandler clickHandler = inventory == null ?
player.getInventory() : player.getOpenInventory();
final short slot = packet.slot;
final byte button = packet.button;
final short actionNumber = packet.actionNumber;
final int mode = packet.mode;
final ClientClickWindowPacket.ClickType clickType = packet.clickType;
// System.out.println("Window id: " + windowId + " | slot: " + slot + " | button: " + button + " | mode: " + mode);
//System.out.println("Window id: " + windowId + " | slot: " + slot + " | button: " + button + " | clickType: " + clickType);
boolean successful = false;
@ -40,63 +37,50 @@ public class WindowListener {
return;
}
switch (mode) {
case 0:
switch (button) {
case 0:
if (slot != -999) {
// Left click
successful = clickHandler.leftClick(player, slot);
} else {
// DROP
successful = clickHandler.drop(player, mode, slot, button);
}
break;
case 1:
if (slot != -999) {
// Right click
successful = clickHandler.rightClick(player, slot);
} else {
// DROP
successful = clickHandler.drop(player, mode, slot, button);
}
break;
}
break;
case 1:
successful = clickHandler.shiftClick(player, slot); // Shift + left/right have identical behavior
break;
case 2:
successful = clickHandler.changeHeld(player, slot, button);
break;
case 3:
// Middle click (only creative players in non-player inventories)
break;
case 4:
// Dropping functions
successful = clickHandler.drop(player, mode, slot, button);
break;
case 5:
// Dragging
successful = clickHandler.dragging(player, slot, button);
break;
case 6:
successful = clickHandler.doubleClick(player, slot);
break;
if (clickType == ClientClickWindowPacket.ClickType.PICKUP) {
if (button == 0) {
successful = inventory.leftClick(player, slot);
} else if (button == 1) {
successful = inventory.rightClick(player, slot);
}
} else if (clickType == ClientClickWindowPacket.ClickType.QUICK_MOVE) {
successful = inventory.shiftClick(player, slot);
} else if (clickType == ClientClickWindowPacket.ClickType.SWAP) {
successful = inventory.changeHeld(player, slot, button);
} else if (clickType == ClientClickWindowPacket.ClickType.CLONE) {
successful = player.isCreative();
if (successful) {
setCursor(player, inventory, packet.item);
}
} else if (clickType == ClientClickWindowPacket.ClickType.THROW) {
successful = inventory.drop(player, false, slot, button);
} else if (clickType == ClientClickWindowPacket.ClickType.QUICK_CRAFT) {
successful = inventory.dragging(player, slot, button);
} else if (clickType == ClientClickWindowPacket.ClickType.PICKUP_ALL) {
successful = inventory.doubleClick(player, slot);
}
// Prevent the player from picking a ghost item in cursor
if (Objects.equals(player.getOpenInventory(), inventory)) {
refreshCursorItem(player, inventory);
assert inventory instanceof Inventory;
refreshCursorItem(player, (Inventory) inventory);
}
// Prevent ghost item when the click is cancelled
if (!successful) {
player.getInventory().update();
if (inventory != null) {
inventory.update(player);
if (inventory instanceof Inventory) {
((Inventory) inventory).update(player);
}
}
PingPacket pingPacket = new PingPacket();
pingPacket.id = (1 << 30) | (windowId << 16);
player.getPlayerConnection().sendPacket(pingPacket);
}
public static void pong(ClientPongPacket packet, Player player) {
// Empty
}
public static void closeWindowListener(ClientCloseWindowPacket packet, Player player) {
@ -116,23 +100,20 @@ public class WindowListener {
* @param inventory the player open inventory, null if not any (could be player inventory)
*/
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();
}
// Error occurred while retrieving the cursor item, stop here
if (cursorItem == null) {
return;
}
ItemStack cursorItem = inventory != null ?
inventory.getCursorItem(player) : player.getInventory().getCursorItem();
final SetSlotPacket setSlotPacket = SetSlotPacket.createCursorPacket(cursorItem);
player.getPlayerConnection().sendPacket(setSlotPacket);
}
private static void setCursor(Player player, AbstractInventory inventory, ItemStack itemStack) {
if (inventory instanceof PlayerInventory) {
((PlayerInventory) inventory).setCursorItem(itemStack);
} else if (inventory instanceof Inventory) {
((Inventory) inventory).setCursorItem(player, itemStack);
} else {
System.err.println("Invalid inventory: " + inventory.getClass());
}
}
}

View File

@ -28,6 +28,7 @@ public final class PacketListenerManager {
setListener(ClientChatMessagePacket.class, ChatMessageListener::listener);
setListener(ClientClickWindowPacket.class, WindowListener::clickWindowListener);
setListener(ClientCloseWindowPacket.class, WindowListener::closeWindowListener);
setListener(ClientPongPacket.class, WindowListener::pong);
setListener(ClientEntityActionPacket.class, EntityActionListener::listener);
setListener(ClientHeldItemChangePacket.class, PlayerHeldListener::heldListener);
setListener(ClientPlayerBlockPlacementPacket.class, BlockPlacementListener::listener);

View File

@ -34,7 +34,7 @@ public class ClientPlayPacketsHandler extends ClientPacketsHandler {
register(0x1A, ClientPlayerDiggingPacket::new);
register(0x1B, ClientEntityActionPacket::new);
register(0x1C, ClientSteerVehiclePacket::new);
//register(0x1D, PONG); // TODO pong packet
register(0x1D, ClientPongPacket::new);
register(0x1E, ClientSetRecipeBookStatePacket::new);
register(0x1F, ClientSetDisplayedRecipePacket::new);

View File

@ -1,5 +1,7 @@
package net.minestom.server.network.packet.client.play;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.client.ClientPlayPacket;
import net.minestom.server.utils.binary.BinaryReader;
@ -11,8 +13,8 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
public byte windowId;
public short slot;
public byte button;
public short actionNumber;
public int mode;
public ClickType clickType;
public Short2ObjectMap<ItemStack> changedSlots = new Short2ObjectOpenHashMap<>();
public ItemStack item = ItemStack.AIR;
@Override
@ -20,8 +22,15 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
this.windowId = reader.readByte();
this.slot = reader.readShort();
this.button = reader.readByte();
this.actionNumber = reader.readShort();
this.mode = reader.readVarInt();
this.clickType = ClickType.values()[reader.readVarInt()];
final int length = reader.readVarInt();
this.changedSlots = new Short2ObjectOpenHashMap<>(length);
for (int i = 0; i < length; i++) {
short slot = reader.readShort();
ItemStack item = reader.readItemStack();
changedSlots.put(slot, item);
}
this.item = reader.readItemStack();
}
@ -30,8 +39,23 @@ public class ClientClickWindowPacket extends ClientPlayPacket {
writer.writeByte(windowId);
writer.writeShort(slot);
writer.writeByte(button);
writer.writeShort(actionNumber);
writer.writeVarInt(mode);
writer.writeVarInt(clickType.ordinal());
writer.writeVarInt(changedSlots.size());
changedSlots.forEach((slot, itemStack) -> {
writer.writeShort(slot);
writer.writeItemStack(itemStack);
});
writer.writeItemStack(item);
}
public enum ClickType {
PICKUP,
QUICK_MOVE,
SWAP,
CLONE,
THROW,
QUICK_CRAFT,
PICKUP_ALL
}
}

View File

@ -0,0 +1,21 @@
package net.minestom.server.network.packet.client.play;
import net.minestom.server.network.packet.client.ClientPlayPacket;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class ClientPongPacket extends ClientPlayPacket {
public int id;
@Override
public void read(@NotNull BinaryReader reader) {
this.id = reader.readInt();
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeInt(id);
}
}

View File

@ -0,0 +1,27 @@
package net.minestom.server.network.packet.server.play;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class PingPacket implements ServerPacket {
public int id;
@Override
public void read(@NotNull BinaryReader reader) {
this.id = reader.readInt();
}
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeInt(id);
}
@Override
public int getId() {
return ServerPacketIdentifier.PING;
}
}