mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-04 23:47:59 +01:00
PlayerDiggingListener refactoring
This commit is contained in:
parent
8410ac51c3
commit
6f2cf8e6a5
@ -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,143 +16,144 @@ 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) {
|
||||||
final Block block = instance.getBlock(blockPosition);
|
diggingResult = startDigging(player, instance, 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING) {
|
} else if (status == ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING) {
|
||||||
|
diggingResult = cancelDigging(instance, blockPosition);
|
||||||
final Block block = instance.getBlock(blockPosition);
|
|
||||||
sendAcknowledgePacket(player, blockPosition, block,
|
|
||||||
ClientPlayerDiggingPacket.Status.CANCELLED_DIGGING, true);
|
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.FINISHED_DIGGING) {
|
} else if (status == ClientPlayerDiggingPacket.Status.FINISHED_DIGGING) {
|
||||||
|
diggingResult = finishDigging(player, instance, blockPosition);
|
||||||
final Block block = instance.getBlock(blockPosition);
|
|
||||||
// Vanilla block
|
|
||||||
breakBlock(instance, player, blockPosition, block, status);
|
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
|
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM_STACK) {
|
||||||
|
dropStack(player);
|
||||||
final ItemStack droppedItemStack = player.getInventory().getItemInMainHand();
|
|
||||||
dropItem(player, droppedItemStack, ItemStack.AIR);
|
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) {
|
} else if (status == ClientPlayerDiggingPacket.Status.DROP_ITEM) {
|
||||||
|
dropSingle(player);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.UPDATE_ITEM_STATE) {
|
} else if (status == ClientPlayerDiggingPacket.Status.UPDATE_ITEM_STATE) {
|
||||||
PlayerMeta meta = player.getEntityMeta();
|
updateItemState(player);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (status == ClientPlayerDiggingPacket.Status.SWAP_ITEM_HAND) {
|
} else if (status == ClientPlayerDiggingPacket.Status.SWAP_ITEM_HAND) {
|
||||||
|
swapItemHand(player);
|
||||||
|
}
|
||||||
|
|
||||||
final PlayerInventory playerInventory = player.getInventory();
|
// Acknowledge start/cancel/finish digging status
|
||||||
final ItemStack mainHand = playerInventory.getItemInMainHand();
|
if (diggingResult != null) {
|
||||||
final ItemStack offHand = playerInventory.getItemInOffHand();
|
player.getPlayerConnection().sendPacket(new AcknowledgePlayerDiggingPacket(blockPosition, diggingResult.block,
|
||||||
|
status, diggingResult.success));
|
||||||
PlayerSwapItemEvent swapItemEvent = new PlayerSwapItemEvent(player, offHand, mainHand);
|
|
||||||
EventDispatcher.callCancellable(swapItemEvent, () -> {
|
|
||||||
playerInventory.setItemInMainHand(swapItemEvent.getMainHandItem());
|
|
||||||
playerInventory.setItemInOffHand(swapItemEvent.getOffHandItem());
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void breakBlock(Instance instance,
|
private static DiggingResult startDigging(Player player, Instance instance, Point blockPosition) {
|
||||||
Player player,
|
final Block block = instance.getBlock(blockPosition);
|
||||||
Point blockPosition, Block block,
|
final GameMode gameMode = player.getGameMode();
|
||||||
ClientPlayerDiggingPacket.Status status) {
|
|
||||||
|
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
|
// 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,
|
||||||
@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user