2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.listener;
|
2019-08-20 17:41:07 +02:00
|
|
|
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.MinecraftServer;
|
2021-07-13 18:27:45 +02:00
|
|
|
import net.minestom.server.coordinate.Point;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.entity.Entity;
|
2021-03-23 17:01:12 +01:00
|
|
|
import net.minestom.server.entity.EntityType;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.entity.GameMode;
|
|
|
|
import net.minestom.server.entity.Player;
|
2021-06-26 13:47:29 +02:00
|
|
|
import net.minestom.server.entity.metadata.other.ArmorStandMeta;
|
2021-06-04 03:48:51 +02:00
|
|
|
import net.minestom.server.event.EventDispatcher;
|
2020-05-07 15:46:21 +02:00
|
|
|
import net.minestom.server.event.player.PlayerBlockInteractEvent;
|
|
|
|
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
|
|
|
|
import net.minestom.server.event.player.PlayerUseItemOnBlockEvent;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.instance.Chunk;
|
|
|
|
import net.minestom.server.instance.Instance;
|
|
|
|
import net.minestom.server.instance.block.Block;
|
2020-06-28 23:11:40 +02:00
|
|
|
import net.minestom.server.instance.block.BlockFace;
|
2021-06-23 22:39:17 +02:00
|
|
|
import net.minestom.server.instance.block.BlockHandler;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.instance.block.BlockManager;
|
|
|
|
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
|
|
|
import net.minestom.server.inventory.PlayerInventory;
|
|
|
|
import net.minestom.server.item.ItemStack;
|
|
|
|
import net.minestom.server.item.Material;
|
|
|
|
import net.minestom.server.network.packet.client.play.ClientPlayerBlockPlacementPacket;
|
2021-02-28 19:37:31 +01:00
|
|
|
import net.minestom.server.network.packet.server.play.BlockChangePacket;
|
2020-08-13 19:00:19 +02:00
|
|
|
import net.minestom.server.utils.Direction;
|
2020-05-25 13:46:48 +02:00
|
|
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
2020-12-20 03:38:36 +01:00
|
|
|
import net.minestom.server.utils.validate.Check;
|
2019-08-20 17:41:07 +02:00
|
|
|
|
2020-04-21 15:31:41 +02:00
|
|
|
import java.util.Set;
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
public class BlockPlacementListener {
|
2020-12-20 03:38:36 +01:00
|
|
|
private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
public static void listener(ClientPlayerBlockPlacementPacket packet, Player player) {
|
2020-07-23 05:36:15 +02:00
|
|
|
final PlayerInventory playerInventory = player.getInventory();
|
|
|
|
final Player.Hand hand = packet.hand;
|
|
|
|
final BlockFace blockFace = packet.blockFace;
|
2021-07-05 09:10:03 +02:00
|
|
|
final Point blockPosition = packet.blockPosition;
|
2020-08-13 19:00:19 +02:00
|
|
|
final Direction direction = blockFace.toDirection();
|
2019-08-20 17:41:07 +02:00
|
|
|
|
2020-07-23 05:36:15 +02:00
|
|
|
final Instance instance = player.getInstance();
|
2019-08-20 17:41:07 +02:00
|
|
|
if (instance == null)
|
|
|
|
return;
|
|
|
|
|
2020-12-20 03:38:36 +01:00
|
|
|
// Prevent outdated/modified client data
|
2021-07-23 06:12:57 +02:00
|
|
|
final Chunk interactedChunk = instance.getChunkAt(blockPosition);
|
|
|
|
if (!ChunkUtils.isLoaded(interactedChunk)) {
|
2020-12-20 03:38:36 +01:00
|
|
|
// Client tried to place a block in an unloaded chunk, ignore the request
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-13 19:00:19 +02:00
|
|
|
final ItemStack usedItem = player.getItemInHand(hand);
|
2021-05-24 23:39:22 +02:00
|
|
|
final Block interactedBlock = instance.getBlock(blockPosition);
|
2020-08-13 19:00:19 +02:00
|
|
|
|
2020-04-21 15:31:41 +02:00
|
|
|
// Interact at block
|
2021-04-02 18:13:02 +02:00
|
|
|
// FIXME: onUseOnBlock
|
2021-05-24 23:39:22 +02:00
|
|
|
PlayerBlockInteractEvent playerBlockInteractEvent = new PlayerBlockInteractEvent(player, hand, interactedBlock, blockPosition, blockFace);
|
2021-06-14 15:26:16 +02:00
|
|
|
EventDispatcher.call(playerBlockInteractEvent);
|
2021-07-17 21:28:00 +02:00
|
|
|
boolean blockUse = playerBlockInteractEvent.isBlockingItemUse();
|
2021-06-23 22:39:17 +02:00
|
|
|
if (!playerBlockInteractEvent.isCancelled()) {
|
|
|
|
final var handler = interactedBlock.handler();
|
|
|
|
if (handler != null) {
|
2021-07-17 21:28:00 +02:00
|
|
|
blockUse |= !handler.onInteract(new BlockHandler.Interaction(interactedBlock, instance, blockPosition, player, hand));
|
2021-06-23 22:39:17 +02:00
|
|
|
}
|
|
|
|
}
|
2021-07-17 21:28:00 +02:00
|
|
|
if (blockUse) {
|
2021-07-23 06:12:57 +02:00
|
|
|
refresh(player, interactedChunk);
|
2020-04-28 18:12:06 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-18 00:28:06 +01:00
|
|
|
final Material useMaterial = usedItem.getMaterial();
|
2021-07-17 21:28:00 +02:00
|
|
|
if (!useMaterial.isBlock()) {
|
|
|
|
// Player didn't try to place a block but interacted with one
|
|
|
|
PlayerUseItemOnBlockEvent event = new PlayerUseItemOnBlockEvent(player, hand, usedItem, blockPosition, direction);
|
|
|
|
EventDispatcher.call(event);
|
|
|
|
return;
|
|
|
|
}
|
2020-12-18 00:28:06 +01:00
|
|
|
|
|
|
|
// Verify if the player can place the block
|
2021-02-28 19:37:31 +01:00
|
|
|
boolean canPlaceBlock = true;
|
2021-07-17 21:28:00 +02:00
|
|
|
// Check if the player is allowed to place blocks based on their game mode
|
|
|
|
if (player.getGameMode() == GameMode.SPECTATOR) {
|
|
|
|
canPlaceBlock = false; // Spectators can't place blocks
|
|
|
|
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
|
|
|
//Check if the block can be placed on the block
|
|
|
|
canPlaceBlock = usedItem.getMeta().getCanPlaceOn().contains(interactedBlock);
|
2019-09-01 06:18:41 +02:00
|
|
|
}
|
2019-08-29 02:15:52 +02:00
|
|
|
|
2020-04-21 15:31:41 +02:00
|
|
|
// Get the newly placed block position
|
2020-07-21 18:48:15 +02:00
|
|
|
final int offsetX = blockFace == BlockFace.WEST ? -1 : blockFace == BlockFace.EAST ? 1 : 0;
|
|
|
|
final int offsetY = blockFace == BlockFace.BOTTOM ? -1 : blockFace == BlockFace.TOP ? 1 : 0;
|
|
|
|
final int offsetZ = blockFace == BlockFace.NORTH ? -1 : blockFace == BlockFace.SOUTH ? 1 : 0;
|
2021-07-17 21:28:00 +02:00
|
|
|
final Point placementPosition = blockPosition.add(offsetX, offsetY, offsetZ);
|
2020-04-21 15:31:41 +02:00
|
|
|
|
2021-03-02 23:37:46 +01:00
|
|
|
if (!canPlaceBlock) {
|
2021-07-17 21:28:00 +02:00
|
|
|
// Send a block change with AIR as block to keep the client in sync,
|
|
|
|
// using refreshChunk results in the client not being in sync
|
|
|
|
// after rapid invalid block placements
|
|
|
|
player.getPlayerConnection().sendPacket(new BlockChangePacket(placementPosition, Block.AIR));
|
2021-02-28 19:37:31 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-04-21 15:31:41 +02:00
|
|
|
|
2021-07-05 09:10:03 +02:00
|
|
|
final Chunk chunk = instance.getChunkAt(placementPosition);
|
2020-12-20 03:38:36 +01:00
|
|
|
Check.stateCondition(!ChunkUtils.isLoaded(chunk),
|
2021-07-05 09:10:03 +02:00
|
|
|
"A player tried to place a block in the border of a loaded chunk {0}", placementPosition);
|
2021-07-17 21:28:00 +02:00
|
|
|
if (chunk.isReadOnly()) {
|
2021-07-23 06:12:57 +02:00
|
|
|
refresh(player, chunk);
|
2021-07-17 21:28:00 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-12-20 03:24:29 +01:00
|
|
|
|
2021-07-27 09:40:57 +02:00
|
|
|
final Block placedBlock = useMaterial.block();
|
2021-07-17 21:28:00 +02:00
|
|
|
final Set<Entity> entities = instance.getChunkEntities(chunk);
|
|
|
|
// Check if the player is trying to place a block in an entity
|
|
|
|
boolean intersect = player.getBoundingBox().intersectWithBlock(placementPosition);
|
|
|
|
if (!intersect && placedBlock.isSolid()) {
|
|
|
|
// TODO push entities too close to the position
|
|
|
|
for (Entity entity : entities) {
|
|
|
|
// 'player' has already been checked
|
|
|
|
if (entity == player ||
|
|
|
|
entity.getEntityType() == EntityType.ITEM)
|
|
|
|
continue;
|
|
|
|
// Marker Armor Stands should not prevent block placement
|
|
|
|
if (entity.getEntityMeta() instanceof ArmorStandMeta) {
|
|
|
|
ArmorStandMeta armorStandMeta = (ArmorStandMeta) entity.getEntityMeta();
|
|
|
|
if (armorStandMeta.isMarker()) {
|
|
|
|
continue;
|
2019-08-31 07:54:53 +02:00
|
|
|
}
|
2019-08-29 02:15:52 +02:00
|
|
|
}
|
2021-07-17 21:28:00 +02:00
|
|
|
intersect = entity.getBoundingBox().intersectWithBlock(placementPosition);
|
|
|
|
if (intersect)
|
|
|
|
break;
|
2019-08-29 02:15:52 +02:00
|
|
|
}
|
2021-07-17 21:28:00 +02:00
|
|
|
}
|
|
|
|
if (intersect) {
|
2021-07-23 06:12:57 +02:00
|
|
|
refresh(player, chunk);
|
2021-07-17 21:28:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// BlockPlaceEvent check
|
|
|
|
PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, placedBlock, blockFace, placementPosition, packet.hand);
|
|
|
|
playerBlockPlaceEvent.consumeBlock(player.getGameMode() != GameMode.CREATIVE);
|
|
|
|
EventDispatcher.call(playerBlockPlaceEvent);
|
|
|
|
if (playerBlockPlaceEvent.isCancelled()) {
|
2021-07-23 06:12:57 +02:00
|
|
|
refresh(player, chunk);
|
2021-07-17 21:28:00 +02:00
|
|
|
return;
|
2020-04-21 15:31:41 +02:00
|
|
|
}
|
|
|
|
|
2021-07-17 21:28:00 +02:00
|
|
|
// BlockPlacementRule check
|
|
|
|
Block resultBlock = playerBlockPlaceEvent.getBlock();
|
|
|
|
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(resultBlock);
|
|
|
|
if (blockPlacementRule != null) {
|
|
|
|
// Get id from block placement rule instead of the event
|
|
|
|
resultBlock = blockPlacementRule.blockPlace(instance, resultBlock, blockFace, blockPosition, player);
|
|
|
|
}
|
|
|
|
if (resultBlock == null) {
|
2021-07-23 06:12:57 +02:00
|
|
|
refresh(player, chunk);
|
2021-07-17 21:28:00 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Place the block
|
|
|
|
instance.placeBlock(player, resultBlock, placementPosition,
|
|
|
|
blockFace, packet.cursorPositionX, packet.cursorPositionY, packet.cursorPositionZ);
|
|
|
|
// Block consuming
|
|
|
|
if (playerBlockPlaceEvent.doesConsumeBlock()) {
|
|
|
|
// Consume the block in the player's hand
|
|
|
|
final ItemStack newUsedItem = usedItem.getStackingRule().apply(usedItem, usedItem.getAmount() - 1);
|
|
|
|
playerInventory.setItemInHand(hand, newUsedItem);
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
}
|
2021-07-23 06:12:57 +02:00
|
|
|
|
|
|
|
private static void refresh(Player player, Chunk chunk) {
|
|
|
|
player.getInventory().update();
|
|
|
|
chunk.sendChunk(player);
|
|
|
|
}
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|