PlayerDiggingListener refactoring

This commit is contained in:
TheMode 2021-08-15 20:25:23 +02:00
parent 8410ac51c3
commit 6f2cf8e6a5
2 changed files with 131 additions and 126 deletions

View File

@ -1,5 +1,7 @@
package net.minestom.server.listener; 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.GameMode;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.entity.metadata.PlayerMeta; import net.minestom.server.entity.metadata.PlayerMeta;
@ -14,92 +16,101 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.item.StackingRule; import net.minestom.server.item.StackingRule;
import net.minestom.server.network.packet.client.play.ClientPlayerDiggingPacket; import net.minestom.server.network.packet.client.play.ClientPlayerDiggingPacket;
import net.minestom.server.network.packet.server.play.AcknowledgePlayerDiggingPacket; import net.minestom.server.network.packet.server.play.AcknowledgePlayerDiggingPacket;
import net.minestom.server.coordinate.Point;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class PlayerDiggingListener { public final class PlayerDiggingListener {
public static void playerDiggingListener(ClientPlayerDiggingPacket packet, Player player) { public static void playerDiggingListener(ClientPlayerDiggingPacket packet, Player player) {
final ClientPlayerDiggingPacket.Status status = packet.status; final ClientPlayerDiggingPacket.Status status = packet.status;
final Point blockPosition = packet.blockPosition; final Point blockPosition = packet.blockPosition;
final Instance instance = player.getInstance(); final Instance instance = player.getInstance();
if (instance == null) return;
if (instance == null) DiggingResult diggingResult = null;
return;
if (status == ClientPlayerDiggingPacket.Status.STARTED_DIGGING) { if (status == ClientPlayerDiggingPacket.Status.STARTED_DIGGING) {
diggingResult = startDigging(player, instance, blockPosition);
} else if (status == ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING) {
diggingResult = cancelDigging(instance, blockPosition);
} else if (status == ClientPlayerDiggingPacket.Status.FINISHED_DIGGING) {
diggingResult = finishDigging(player, instance, blockPosition);
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
dropStack(player);
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) {
dropSingle(player);
} else if (status == ClientPlayerDiggingPacket.Status.UPDATE_ITEM_STATE) {
updateItemState(player);
} else if (status == ClientPlayerDiggingPacket.Status.SWAP_ITEM_HAND) {
swapItemHand(player);
}
// Acknowledge start/cancel/finish digging status
if (diggingResult != null) {
player.getPlayerConnection().sendPacket(new AcknowledgePlayerDiggingPacket(blockPosition, diggingResult.block,
status, diggingResult.success));
}
}
private static DiggingResult startDigging(Player player, Instance instance, Point blockPosition) {
final Block block = instance.getBlock(blockPosition); final Block block = instance.getBlock(blockPosition);
final GameMode gameMode = player.getGameMode();
//Check if the player is allowed to break blocks based on their game mode if (gameMode == GameMode.SPECTATOR) {
if (player.getGameMode() == GameMode.SPECTATOR) { // Spectators can't break blocks
sendAcknowledgePacket(player, blockPosition, block, return new DiggingResult(block, false);
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false); } else if (gameMode == GameMode.ADVENTURE) {
return; //Spectators can't break blocks
} else if (player.getGameMode() == GameMode.ADVENTURE) {
// Check if the item can break the block with the current item // Check if the item can break the block with the current item
ItemStack itemInMainHand = player.getItemInMainHand(); final ItemStack itemInMainHand = player.getItemInMainHand();
Block destroyedBlock = instance.getBlock(blockPosition); if (!itemInMainHand.getMeta().getCanDestroy().contains(block)) {
if (!itemInMainHand.getMeta().getCanDestroy().contains(destroyedBlock)) { return new DiggingResult(block, false);
sendAcknowledgePacket(player, blockPosition, block,
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
return;
} }
} else if (gameMode == GameMode.CREATIVE) {
return breakBlock(instance, player, blockPosition, block);
} }
final boolean instantBreak = player.isCreative() || // Survival digging
player.isInstantBreak() || // FIXME: verify mineable tag and enchantment
block.registry().hardness() == 0; final boolean instantBreak = player.isInstantBreak() || block.registry().hardness() == 0;
if (!instantBreak) {
if (instantBreak) {
// No need to check custom block
breakBlock(instance, player, blockPosition, block, status);
} else {
PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(player, block, blockPosition); PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(player, block, blockPosition);
EventDispatcher.call(playerStartDiggingEvent); EventDispatcher.call(playerStartDiggingEvent);
return new DiggingResult(block, !playerStartDiggingEvent.isCancelled());
sendAcknowledgePacket(player, blockPosition, block, }
ClientPlayerDiggingPacket.Status.STARTED_DIGGING, !playerStartDiggingEvent.isCancelled()); // Client only send a single STARTED_DIGGING when insta-break is enabled
return breakBlock(instance, player, blockPosition, block);
} }
} else if (status == ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING) { private static DiggingResult cancelDigging(Instance instance, Point blockPosition) {
final Block block = instance.getBlock(blockPosition); final Block block = instance.getBlock(blockPosition);
sendAcknowledgePacket(player, blockPosition, block, return new DiggingResult(block, true);
ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING, true); }
} else if (status == ClientPlayerDiggingPacket.Status.FINISHED_DIGGING) {
private static DiggingResult finishDigging(Player player, Instance instance, Point blockPosition) {
final Block block = instance.getBlock(blockPosition); final Block block = instance.getBlock(blockPosition);
// Vanilla block // TODO sanity check
breakBlock(instance, player, blockPosition, block, status); return breakBlock(instance, player, blockPosition, block);
}
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
private static void dropStack(Player player) {
final ItemStack droppedItemStack = player.getInventory().getItemInMainHand(); final ItemStack droppedItemStack = player.getInventory().getItemInMainHand();
dropItem(player, droppedItemStack, ItemStack.AIR); dropItem(player, droppedItemStack, ItemStack.AIR);
}
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) { private static void dropSingle(Player player) {
final ItemStack handItem = player.getInventory().getItemInMainHand();
final int dropAmount = 1;
ItemStack handItem = player.getInventory().getItemInMainHand();
final StackingRule stackingRule = handItem.getStackingRule(); final StackingRule stackingRule = handItem.getStackingRule();
final int handAmount = stackingRule.getAmount(handItem); final int handAmount = stackingRule.getAmount(handItem);
if (handAmount <= 1) {
if (handAmount <= dropAmount) {
// Drop the whole item without copy // Drop the whole item without copy
dropItem(player, handItem, ItemStack.AIR); dropItem(player, handItem, ItemStack.AIR);
} else { } else {
// Drop a single item, need a copy // Drop a single item
ItemStack droppedItemStack2 = stackingRule.apply(handItem, dropAmount); dropItem(player,
stackingRule.apply(handItem, 1), // Single dropped item
handItem = stackingRule.apply(handItem, handAmount - dropAmount); stackingRule.apply(handItem, handAmount - 1)); // Updated hand
}
dropItem(player, droppedItemStack2, handItem);
} }
} else if (status == ClientPlayerDiggingPacket.Status.UPDATE_ITEM_STATE) { private static void updateItemState(Player player) {
PlayerMeta meta = player.getEntityMeta(); PlayerMeta meta = player.getEntityMeta();
if (!meta.isHandActive()) return; if (!meta.isHandActive()) return;
Player.Hand hand = meta.getActiveHand(); Player.Hand hand = meta.getActiveHand();
@ -108,50 +119,42 @@ public class PlayerDiggingListener {
player.triggerStatus((byte) 9); player.triggerStatus((byte) 9);
ItemUpdateStateEvent itemUpdateStateEvent = player.callItemUpdateStateEvent(hand); ItemUpdateStateEvent itemUpdateStateEvent = player.callItemUpdateStateEvent(hand);
if (itemUpdateStateEvent == null) { if (itemUpdateStateEvent == null) {
player.refreshActiveHand(true, false, false); player.refreshActiveHand(true, false, false);
} else { } else {
final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF; final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF;
player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), isOffHand, false); player.refreshActiveHand(itemUpdateStateEvent.hasHandAnimation(), isOffHand, false);
} }
}
} else if (status == ClientPlayerDiggingPacket.Status.SWAP_ITEM_HAND) { private static void swapItemHand(Player player) {
final PlayerInventory inventory = player.getInventory();
final PlayerInventory playerInventory = player.getInventory(); final ItemStack mainHand = inventory.getItemInMainHand();
final ItemStack mainHand = playerInventory.getItemInMainHand(); final ItemStack offHand = inventory.getItemInOffHand();
final ItemStack offHand = playerInventory.getItemInOffHand();
PlayerSwapItemEvent swapItemEvent = new PlayerSwapItemEvent(player, offHand, mainHand); PlayerSwapItemEvent swapItemEvent = new PlayerSwapItemEvent(player, offHand, mainHand);
EventDispatcher.callCancellable(swapItemEvent, () -> { EventDispatcher.callCancellable(swapItemEvent, () -> {
playerInventory.setItemInMainHand(swapItemEvent.getMainHandItem()); inventory.setItemInMainHand(swapItemEvent.getMainHandItem());
playerInventory.setItemInOffHand(swapItemEvent.getOffHandItem()); inventory.setItemInOffHand(swapItemEvent.getOffHandItem());
}); });
}
} }
private static void breakBlock(Instance instance, private static DiggingResult breakBlock(Instance instance,
Player player, Player player,
Point blockPosition, Block block, Point blockPosition, Block previousBlock) {
ClientPlayerDiggingPacket.Status status) {
// Unverified block break, client is fully responsible // 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); final Block updatedBlock = instance.getBlock(blockPosition);
if (!success) {
// Send acknowledge packet to allow or cancel the digging process if (previousBlock.isSolid()) {
sendAcknowledgePacket(player, blockPosition, updatedBlock, status, result); final Pos playerPosition = player.getPosition();
if (!result) {
if (block.isSolid()) {
final var playerPosition = player.getPosition();
// Teleport the player back if he broke a solid block just below him // 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); player.teleport(playerPosition);
} }
} }
} }
return new DiggingResult(updatedBlock, success);
}
private static void dropItem(@NotNull Player player, private static void dropItem(@NotNull Player player,
@NotNull ItemStack droppedItem, @NotNull ItemStack handItem) { @NotNull ItemStack droppedItem, @NotNull ItemStack handItem) {
@ -163,17 +166,13 @@ public class PlayerDiggingListener {
} }
} }
/** private static final class DiggingResult {
* Sends an {@link AcknowledgePlayerDiggingPacket} to a connection. public final Block block;
* public final boolean success;
* @param player the player
* @param blockPosition the block position public DiggingResult(Block block, boolean success) {
* @param block the block this.block = block;
* @param status the status of the digging this.success = success;
* @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));
} }
} }

View File

@ -1,12 +1,13 @@
package net.minestom.server.network.packet.server.play; 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.client.play.ClientPlayerDiggingPacket;
import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class AcknowledgePlayerDiggingPacket implements ServerPacket { public class AcknowledgePlayerDiggingPacket implements ServerPacket {
@ -24,6 +25,11 @@ public class AcknowledgePlayerDiggingPacket implements ServerPacket {
this.successful = success; this.successful = success;
} }
public AcknowledgePlayerDiggingPacket(@NotNull Point blockPosition, Block block,
@NotNull ClientPlayerDiggingPacket.Status status, boolean success) {
this(blockPosition, block.stateId(), status, success);
}
public AcknowledgePlayerDiggingPacket() { public AcknowledgePlayerDiggingPacket() {
this(Vec.ZERO, 0, ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false); this(Vec.ZERO, 0, ClientPlayerDiggingPacket.Status.STARTED_DIGGING, false);
} }