Minestom/src/main/java/net/minestom/server/listener/BlockPlacementListener.java

196 lines
9.0 KiB
Java
Raw Normal View History

2020-04-24 03:25:58 +02:00
package net.minestom.server.listener;
2020-04-24 03:25:58 +02:00
import net.minestom.server.MinecraftServer;
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;
import net.minestom.server.entity.metadata.other.ArmorStandMeta;
2021-06-04 03:48:51 +02:00
import net.minestom.server.event.EventDispatcher;
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;
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;
import net.minestom.server.network.packet.server.play.BlockChangePacket;
2020-04-24 03:25:58 +02:00
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Direction;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.validate.Check;
2020-04-21 15:31:41 +02:00
import java.util.Set;
public class BlockPlacementListener {
private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
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;
final BlockPosition blockPosition = packet.blockPosition;
final Direction direction = blockFace.toDirection();
2020-07-23 05:36:15 +02:00
final Instance instance = player.getInstance();
if (instance == null)
return;
// Prevent outdated/modified client data
if (!ChunkUtils.isLoaded(instance.getChunkAt(blockPosition))) {
// Client tried to place a block in an unloaded chunk, ignore the request
return;
}
final ItemStack usedItem = player.getItemInHand(hand);
2021-05-24 23:39:22 +02:00
final Block interactedBlock = instance.getBlock(blockPosition);
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-06-23 22:39:17 +02:00
if (!playerBlockInteractEvent.isCancelled()) {
final var handler = interactedBlock.handler();
if (handler != null) {
handler.onInteract(new BlockHandler.Interaction(interactedBlock, instance, blockPosition, player, hand));
2021-06-23 22:39:17 +02:00
}
}
2020-07-21 18:48:15 +02:00
if (playerBlockInteractEvent.isBlockingItemUse()) {
return;
}
final Material useMaterial = usedItem.getMaterial();
// Verify if the player can place the block
boolean canPlaceBlock = true;
{
if (useMaterial == Material.AIR) { // Can't place air
return;
}
//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 placed on the block
2021-05-24 23:39:22 +02:00
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;
2019-08-22 14:52:32 +02:00
blockPosition.add(offsetX, offsetY, offsetZ);
2020-04-21 15:31:41 +02:00
if (!canPlaceBlock) {
2021-05-07 00:16:09 +02:00
if (useMaterial.isBlock()) {
//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(blockPosition, Block.AIR.stateId()));
}
return;
}
2020-04-21 15:31:41 +02:00
2020-07-21 18:48:15 +02:00
final Chunk chunk = instance.getChunkAt(blockPosition);
2020-12-20 03:24:29 +01:00
Check.stateCondition(!ChunkUtils.isLoaded(chunk),
"A player tried to place a block in the border of a loaded chunk " + blockPosition);
2020-12-20 03:24:29 +01:00
2020-07-24 18:23:15 +02:00
// The concerned chunk will be send to the player if an error occur
// This will ensure that the player has the correct version of the chunk
2020-04-21 15:31:41 +02:00
boolean refreshChunk = false;
if (useMaterial.isBlock()) {
if (!chunk.isReadOnly()) {
2021-05-24 23:39:22 +02:00
final Block placedBlock = useMaterial.getBlock();
final Set<Entity> entities = instance.getChunkEntities(chunk);
// Check if the player is trying to place a block in an entity
boolean intersect = player.getBoundingBox().intersect(blockPosition);
2021-05-24 23:39:22 +02:00
if (!intersect && placedBlock.isSolid()) {
2021-03-23 17:01:12 +01:00
// TODO push entities too close to the position
for (Entity entity : entities) {
2021-01-13 06:34:09 +01:00
// 'player' has already been checked
2021-03-23 17:01:12 +01:00
if (entity == player ||
entity.getEntityType() == EntityType.ITEM)
2021-01-13 06:34:09 +01:00
continue;
// Marker Armor Stands should not prevent block placement
if(entity.getEntityMeta() instanceof ArmorStandMeta) {
ArmorStandMeta armorStandMeta = (ArmorStandMeta) entity.getEntityMeta();
if(armorStandMeta.isMarker()) {
continue;
}
}
intersect = entity.getBoundingBox().intersect(blockPosition);
if (intersect)
break;
}
}
if (!intersect) {
2020-12-20 03:24:29 +01:00
// BlockPlaceEvent check
2021-05-24 23:39:22 +02:00
PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, placedBlock, blockPosition, packet.hand);
playerBlockPlaceEvent.consumeBlock(player.getGameMode() != GameMode.CREATIVE);
2021-06-04 03:48:51 +02:00
EventDispatcher.call(playerBlockPlaceEvent);
2020-12-20 03:24:29 +01:00
if (!playerBlockPlaceEvent.isCancelled()) {
// BlockPlacementRule check
2021-05-24 22:00:18 +02:00
Block resultBlock = playerBlockPlaceEvent.getBlock();
final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(resultBlock);
if (blockPlacementRule != null) {
// Get id from block placement rule instead of the event
2021-05-24 22:00:18 +02:00
resultBlock = blockPlacementRule.blockPlace(instance, resultBlock, blockFace, blockPosition, player);
}
2021-05-24 22:00:18 +02:00
final boolean placementRuleCheck = resultBlock != null;
2020-12-20 03:24:29 +01:00
if (placementRuleCheck) {
// Place the block
instance.placeBlock(player, resultBlock, blockPosition,
blockFace, packet.cursorPositionX, packet.cursorPositionY, packet.cursorPositionZ);
2020-12-20 03:24:29 +01:00
// Block consuming
if (playerBlockPlaceEvent.doesConsumeBlock()) {
// Consume the block in the player's hand
2021-04-02 18:13:02 +02:00
final ItemStack newUsedItem = usedItem.getStackingRule().apply(usedItem, usedItem.getAmount() - 1);
playerInventory.setItemInHand(hand, newUsedItem);
}
2020-12-20 03:24:29 +01:00
} else {
refreshChunk = true;
}
} else {
refreshChunk = true;
2019-08-31 07:54:53 +02:00
}
} else {
refreshChunk = true;
2019-08-29 02:15:52 +02:00
}
2019-08-31 07:54:53 +02:00
} else {
2020-04-21 15:31:41 +02:00
refreshChunk = true;
2019-08-29 02:15:52 +02:00
}
2020-04-20 18:46:39 +02:00
} else {
2020-07-24 18:23:15 +02:00
// Player didn't try to place a block but interacted with one
final BlockPosition usePosition = blockPosition.clone().subtract(offsetX, offsetY, offsetZ);
PlayerUseItemOnBlockEvent event = new PlayerUseItemOnBlockEvent(player, hand, usedItem, usePosition, direction);
2021-06-04 03:48:51 +02:00
EventDispatcher.call(event);
2020-04-21 15:31:41 +02:00
refreshChunk = true;
}
// Refresh chunk section if needed
if (refreshChunk) {
2021-06-23 15:25:05 +02:00
chunk.sendChunk(player);
}
}
}