Files
Paper/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch
Bjarne Koll b9d3147d3b Use correct placed block position for sound (#12410)
Previously the server attempted to compute the block placed by using the
BlockPlaceContext. This approach however fails on replacable blocks, as
the BlockPlaceContext computes this during its construction, which
happened after the actual world modification.

The commit reworks this approach and now stores metadata in the
InteractionResult which can later be read.
The diff is structured to allow for easy future expansion of the tracked
metadata.
2025-04-27 14:19:42 +02:00

99 lines
6.5 KiB
Diff

--- a/net/minecraft/world/item/BlockItem.java
+++ b/net/minecraft/world/item/BlockItem.java
@@ -57,6 +_,14 @@
return InteractionResult.FAIL;
} else {
BlockState placementState = this.getPlacementState(blockPlaceContext);
+ // CraftBukkit start - special case for handling block placement with water lilies and snow buckets
+ org.bukkit.block.BlockState bukkitState = null;
+ if (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem) {
+ bukkitState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos());
+ }
+ final org.bukkit.block.BlockState oldBukkitState = bukkitState != null ? bukkitState : org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockPlaceContext.getLevel(), blockPlaceContext.getClickedPos()); // Paper - Reset placed block on exception
+ // CraftBukkit end
+
if (placementState == null) {
return InteractionResult.FAIL;
} else if (!this.placeBlock(blockPlaceContext, placementState)) {
@@ -69,15 +_,40 @@
BlockState blockState = level.getBlockState(clickedPos);
if (blockState.is(placementState.getBlock())) {
blockState = this.updateBlockStateFromTag(clickedPos, level, itemInHand, blockState);
+ // Paper start - Reset placed block on exception
+ try {
this.updateCustomBlockEntityTag(clickedPos, level, player, itemInHand, blockState);
updateBlockEntityComponents(level, clickedPos, itemInHand);
+ } catch (Exception ex) {
+ ((org.bukkit.craftbukkit.block.CraftBlockState) oldBukkitState).revertPlace();
+ if (player instanceof ServerPlayer serverPlayer) {
+ org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), ex);
+ serverPlayer.getBukkitEntity().kickPlayer("Packet processing error");
+ return InteractionResult.FAIL;
+ }
+ throw ex; // Rethrow exception if not placed by a player
+ }
+ // Paper end - Reset placed block on exception
blockState.getBlock().setPlacedBy(level, clickedPos, blockState, player, itemInHand);
+ // CraftBukkit start
+ if (bukkitState != null) {
+ org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent((net.minecraft.server.level.ServerLevel) level, player, blockPlaceContext.getHand(), bukkitState, clickedPos);
+ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) {
+ ((org.bukkit.craftbukkit.block.CraftBlockState) bukkitState).revertPlace();
+
+ // Paper - if the event is called here, the inventory should be updated
+ player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541
+ return InteractionResult.FAIL;
+ }
+ }
+ // CraftBukkit end
if (player instanceof ServerPlayer) {
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, clickedPos, itemInHand);
}
}
SoundType soundType = blockState.getSoundType();
+ if (player == null) // Paper - Fix block place logic; reintroduce this for the dispenser (i.e the shulker)
level.playSound(
player,
clickedPos,
@@ -88,7 +_,7 @@
);
level.gameEvent(GameEvent.BLOCK_PLACE, clickedPos, GameEvent.Context.of(player, blockState));
itemInHand.consume(1, player);
- return InteractionResult.SUCCESS;
+ return InteractionResult.SUCCESS.configurePaper(e -> e.placedBlockAt(clickedPos.immutable())); // Paper - track placed block position from block item
}
}
}
@@ -137,8 +_,19 @@
protected boolean canPlace(BlockPlaceContext context, BlockState state) {
Player player = context.getPlayer();
- return (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos()))
- && context.getLevel().isUnobstructed(state, context.getClickedPos(), CollisionContext.placementContext(player));
+ // CraftBukkit start
+ Level world = context.getLevel(); // Paper - Cancel hit for vanished players
+ boolean canBuild = (!this.mustSurvive() || state.canSurvive(world, context.getClickedPos())) && world.checkEntityCollision(state, player, CollisionContext.placementContext(player), context.getClickedPos(), true); // Paper - Cancel hit for vanished players
+ org.bukkit.entity.Player bukkitPlayer = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null;
+
+ org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent(
+ org.bukkit.craftbukkit.block.CraftBlock.at(world, context.getClickedPos()), bukkitPlayer,
+ org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(state), canBuild, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand())
+ );
+ world.getCraftServer().getPluginManager().callEvent(event);
+
+ return event.isBuildable();
+ // CraftBukkit end
}
protected boolean mustSurvive() {
@@ -167,7 +_,7 @@
return false;
}
- if (!type.onlyOpCanSetNbt() || player != null && player.canUseGameMasterBlocks()) {
+ if (!type.onlyOpCanSetNbt() || player != null && (player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place")))) { // Spigot - add permission
return customData.loadInto(blockEntity, level.registryAccess());
}