From 6f2cf8e6a578ea4dce8406a78eb01de0d2895199 Mon Sep 17 00:00:00 2001 From: TheMode Date: Sun, 15 Aug 2021 20:25:23 +0200 Subject: [PATCH] PlayerDiggingListener refactoring --- .../listener/PlayerDiggingListener.java | 247 +++++++++--------- .../play/AcknowledgePlayerDiggingPacket.java | 10 +- 2 files changed, 131 insertions(+), 126 deletions(-) diff --git a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java index 9437f43e8..04bb92a08 100644 --- a/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java +++ b/src/main/java/net/minestom/server/listener/PlayerDiggingListener.java @@ -1,5 +1,7 @@ package net.minestom.server.listener; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.entity.metadata.PlayerMeta; @@ -14,143 +16,144 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.StackingRule; import net.minestom.server.network.packet.client.play.ClientPlayerDiggingPacket; import net.minestom.server.network.packet.server.play.AcknowledgePlayerDiggingPacket; -import net.minestom.server.coordinate.Point; import org.jetbrains.annotations.NotNull; -public class PlayerDiggingListener { +public final class PlayerDiggingListener { public static void playerDiggingListener(ClientPlayerDiggingPacket packet, Player player) { final ClientPlayerDiggingPacket.Status status = packet.status; final Point blockPosition = packet.blockPosition; - final Instance instance = player.getInstance(); + if (instance == null) return; - if (instance == null) - return; - + DiggingResult diggingResult = null; if (status == ClientPlayerDiggingPacket.Status.STARTED_DIGGING) { - final Block block = instance.getBlock(blockPosition); - - //Check if the player is allowed to break blocks based on their game mode - if (player.getGameMode() == GameMode.SPECTATOR) { - sendAcknowledgePacket(player, blockPosition, block, - ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false); - return; //Spectators can't break blocks - } else if (player.getGameMode() == GameMode.ADVENTURE) { - //Check if the item can break the block with the current item - ItemStack itemInMainHand = player.getItemInMainHand(); - Block destroyedBlock = instance.getBlock(blockPosition); - if (!itemInMainHand.getMeta().getCanDestroy().contains(destroyedBlock)) { - sendAcknowledgePacket(player, blockPosition, block, - ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false); - return; - } - } - - final boolean instantBreak = player.isCreative() || - player.isInstantBreak() || - block.registry().hardness() == 0; - - if (instantBreak) { - // No need to check custom block - breakBlock(instance, player, blockPosition, block, status); - } else { - PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(player, block, blockPosition); - EventDispatcher.call(playerStartDiggingEvent); - - sendAcknowledgePacket(player, blockPosition, block, - ClientPlayerDiggingPacket.Status.STARTED_DIGGING, !playerStartDiggingEvent.isCancelled()); - } - + diggingResult = startDigging(player, instance, blockPosition); } else if (status == ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING) { - - final Block block = instance.getBlock(blockPosition); - sendAcknowledgePacket(player, blockPosition, block, - ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING, true); - + diggingResult = cancelDigging(instance, blockPosition); } else if (status == ClientPlayerDiggingPacket.Status.FINISHED_DIGGING) { - - final Block block = instance.getBlock(blockPosition); - // Vanilla block - breakBlock(instance, player, blockPosition, block, status); - + diggingResult = finishDigging(player, instance, blockPosition); } else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) { - - final ItemStack droppedItemStack = player.getInventory().getItemInMainHand(); - dropItem(player, droppedItemStack, ItemStack.AIR); - + dropStack(player); } else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) { - - final int dropAmount = 1; - - ItemStack handItem = player.getInventory().getItemInMainHand(); - final StackingRule stackingRule = handItem.getStackingRule(); - final int handAmount = stackingRule.getAmount(handItem); - - if (handAmount <= dropAmount) { - // Drop the whole item without copy - dropItem(player, handItem, ItemStack.AIR); - } else { - // Drop a single item, need a copy - ItemStack droppedItemStack2 = stackingRule.apply(handItem, dropAmount); - - handItem = stackingRule.apply(handItem, handAmount - dropAmount); - - dropItem(player, droppedItemStack2, handItem); - } - + dropSingle(player); } else if (status == ClientPlayerDiggingPacket.Status.UPDATE_ITEM_STATE) { - PlayerMeta meta = player.getEntityMeta(); - if (!meta.isHandActive()) return; - Player.Hand hand = meta.getActiveHand(); - - player.refreshEating(null); - player.triggerStatus((byte) 9); - - ItemUpdateStateEvent itemUpdateStateEvent = player.callItemUpdateStateEvent(hand); - - if (itemUpdateStateEvent == null) { - player.refreshActiveHand(true, false, false); - } else { - final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF; - player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), isOffHand, false); - } - + updateItemState(player); } else if (status == ClientPlayerDiggingPacket.Status.SWAP_ITEM_HAND) { + swapItemHand(player); + } - final PlayerInventory playerInventory = player.getInventory(); - final ItemStack mainHand = playerInventory.getItemInMainHand(); - final ItemStack offHand = playerInventory.getItemInOffHand(); - - PlayerSwapItemEvent swapItemEvent = new PlayerSwapItemEvent(player, offHand, mainHand); - EventDispatcher.callCancellable(swapItemEvent, () -> { - playerInventory.setItemInMainHand(swapItemEvent.getMainHandItem()); - playerInventory.setItemInOffHand(swapItemEvent.getOffHandItem()); - }); - + // Acknowledge start/cancel/finish digging status + if (diggingResult != null) { + player.getPlayerConnection().sendPacket(new AcknowledgePlayerDiggingPacket(blockPosition, diggingResult.block, + status, diggingResult.success)); } } - private static void breakBlock(Instance instance, - Player player, - Point blockPosition, Block block, - ClientPlayerDiggingPacket.Status status) { + private static DiggingResult startDigging(Player player, Instance instance, Point blockPosition) { + final Block block = instance.getBlock(blockPosition); + final GameMode gameMode = player.getGameMode(); + + if (gameMode == GameMode.SPECTATOR) { + // Spectators can't break blocks + return new DiggingResult(block, false); + } else if (gameMode == GameMode.ADVENTURE) { + // Check if the item can break the block with the current item + final ItemStack itemInMainHand = player.getItemInMainHand(); + if (!itemInMainHand.getMeta().getCanDestroy().contains(block)) { + return new DiggingResult(block, false); + } + } else if (gameMode == GameMode.CREATIVE) { + return breakBlock(instance, player, blockPosition, block); + } + + // Survival digging + // FIXME: verify mineable tag and enchantment + final boolean instantBreak = player.isInstantBreak() || block.registry().hardness() == 0; + if (!instantBreak) { + PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(player, block, blockPosition); + EventDispatcher.call(playerStartDiggingEvent); + return new DiggingResult(block, !playerStartDiggingEvent.isCancelled()); + } + // Client only send a single STARTED_DIGGING when insta-break is enabled + return breakBlock(instance, player, blockPosition, block); + } + + private static DiggingResult cancelDigging(Instance instance, Point blockPosition) { + final Block block = instance.getBlock(blockPosition); + return new DiggingResult(block, true); + } + + private static DiggingResult finishDigging(Player player, Instance instance, Point blockPosition) { + final Block block = instance.getBlock(blockPosition); + // TODO sanity check + return breakBlock(instance, player, blockPosition, block); + } + + private static void dropStack(Player player) { + final ItemStack droppedItemStack = player.getInventory().getItemInMainHand(); + dropItem(player, droppedItemStack, ItemStack.AIR); + } + + private static void dropSingle(Player player) { + final ItemStack handItem = player.getInventory().getItemInMainHand(); + final StackingRule stackingRule = handItem.getStackingRule(); + final int handAmount = stackingRule.getAmount(handItem); + if (handAmount <= 1) { + // Drop the whole item without copy + dropItem(player, handItem, ItemStack.AIR); + } else { + // Drop a single item + dropItem(player, + stackingRule.apply(handItem, 1), // Single dropped item + stackingRule.apply(handItem, handAmount - 1)); // Updated hand + } + } + + private static void updateItemState(Player player) { + PlayerMeta meta = player.getEntityMeta(); + if (!meta.isHandActive()) return; + Player.Hand hand = meta.getActiveHand(); + + player.refreshEating(null); + player.triggerStatus((byte) 9); + + ItemUpdateStateEvent itemUpdateStateEvent = player.callItemUpdateStateEvent(hand); + if (itemUpdateStateEvent == null) { + player.refreshActiveHand(true, false, false); + } else { + final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF; + player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), isOffHand, false); + } + } + + private static void swapItemHand(Player player) { + final PlayerInventory inventory = player.getInventory(); + final ItemStack mainHand = inventory.getItemInMainHand(); + final ItemStack offHand = inventory.getItemInOffHand(); + PlayerSwapItemEvent swapItemEvent = new PlayerSwapItemEvent(player, offHand, mainHand); + EventDispatcher.callCancellable(swapItemEvent, () -> { + inventory.setItemInMainHand(swapItemEvent.getMainHandItem()); + inventory.setItemInOffHand(swapItemEvent.getOffHandItem()); + }); + } + + private static DiggingResult breakBlock(Instance instance, + Player player, + Point blockPosition, Block previousBlock) { // Unverified block break, client is fully responsible - final boolean result = instance.breakBlock(player, blockPosition); - + final boolean success = instance.breakBlock(player, blockPosition); final Block updatedBlock = instance.getBlock(blockPosition); - - // Send acknowledge packet to allow or cancel the digging process - sendAcknowledgePacket(player, blockPosition, updatedBlock, status, result); - - if (!result) { - if (block.isSolid()) { - final var playerPosition = player.getPosition(); + if (!success) { + if (previousBlock.isSolid()) { + final Pos playerPosition = player.getPosition(); // Teleport the player back if he broke a solid block just below him - if (playerPosition.sub(0, 1, 0).samePoint(blockPosition)) + if (playerPosition.sub(0, 1, 0).samePoint(blockPosition)) { player.teleport(playerPosition); + } } } + return new DiggingResult(updatedBlock, success); } private static void dropItem(@NotNull Player player, @@ -163,17 +166,13 @@ public class PlayerDiggingListener { } } - /** - * Sends an {@link AcknowledgePlayerDiggingPacket} to a connection. - * - * @param player the player - * @param blockPosition the block position - * @param block the block - * @param status the status of the digging - * @param success true to notify of a success, false otherwise - */ - private static void sendAcknowledgePacket(@NotNull Player player, @NotNull Point blockPosition, Block block, - @NotNull ClientPlayerDiggingPacket.Status status, boolean success) { - player.getPlayerConnection().sendPacket(new AcknowledgePlayerDiggingPacket(blockPosition, block.stateId(), status, success)); + private static final class DiggingResult { + public final Block block; + public final boolean success; + + public DiggingResult(Block block, boolean success) { + this.block = block; + this.success = success; + } } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/AcknowledgePlayerDiggingPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/AcknowledgePlayerDiggingPacket.java index 52e9e3f7a..5bb042046 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/AcknowledgePlayerDiggingPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/AcknowledgePlayerDiggingPacket.java @@ -1,12 +1,13 @@ package net.minestom.server.network.packet.server.play; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.instance.block.Block; import net.minestom.server.network.packet.client.play.ClientPlayerDiggingPacket; 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 net.minestom.server.coordinate.Point; -import net.minestom.server.coordinate.Vec; import org.jetbrains.annotations.NotNull; public class AcknowledgePlayerDiggingPacket implements ServerPacket { @@ -24,6 +25,11 @@ public class AcknowledgePlayerDiggingPacket implements ServerPacket { this.successful = success; } + public AcknowledgePlayerDiggingPacket(@NotNull Point blockPosition, Block block, + @NotNull ClientPlayerDiggingPacket.Status status, boolean success) { + this(blockPosition, block.stateId(), status, success); + } + public AcknowledgePlayerDiggingPacket() { this(Vec.ZERO, 0, ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false); }