From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Cryptite Date: Mon, 10 Apr 2023 07:30:29 -0500 Subject: [PATCH] Add BlockDestroyedByNeighborEvent diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java index a7b217ddbcbf92513bd38101fdfca2075505e267..92c8cb4fe9c82e32876fa75134a6009c874f6594 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java @@ -419,6 +419,7 @@ public class ServerPlayerGameMode { this.level.captureDrops = new ArrayList<>(); // CraftBukkit end BlockState iblockdata1 = block.playerWillDestroy(this.level, pos, iblockdata, this.player); + level.pendingPlayerBlockEvents.put(pos, new Level.PendingBlockEvent(pos, this.player)); // Paper boolean flag = this.level.removeBlock(pos, false); if (flag) { @@ -446,6 +447,7 @@ public class ServerPlayerGameMode { // CraftBukkit start java.util.List itemsToDrop = this.level.captureDrops; // Paper - capture all item additions to the world this.level.captureDrops = null; // Paper - capture all item additions to the world; Remove this earlier so that we can actually drop stuff + level.pendingPlayerBlockEvents.remove(pos); // Paper if (event.isDropItems()) { org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, itemsToDrop); // Paper - capture all item additions to the world } diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java index ed27a963223bfe18310ad5adabf461b5e307ef9c..94a94186dc27d69ef09b12afbce7ecc69ec2bfc5 100644 --- a/src/main/java/net/minecraft/world/item/ItemStack.java +++ b/src/main/java/net/minecraft/world/item/ItemStack.java @@ -378,6 +378,7 @@ public final class ItemStack { CompoundTag oldData = this.getTagClone(); int oldCount = this.getCount(); ServerLevel world = (ServerLevel) context.getLevel(); + if (entityhuman != null) world.pendingPlayerBlockEvents.put(blockposition, new Level.PendingBlockEvent(blockposition, entityhuman)); // Paper if (!(item instanceof BucketItem/* || item instanceof SolidBucketItem*/)) { // if not bucket // Paper - Fix cancelled powdered snow bucket placement world.captureBlockStates = true; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index d2ca67b1a9468b4b5a9f2e388310715e86695501..a343c16def4d03e82cde7e7ad4d40cd94ab99715 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -181,6 +181,27 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here + // Paper start - Holder class used to track what Player is responsible the last block event + public static class PendingBlockEvent { + + public final BlockPos block; + public final Player player; + public @Nullable BlockPos sourceBlock; + + public PendingBlockEvent(BlockPos block, Player player) { + this(block, player, null); + } + + public PendingBlockEvent(BlockPos block, Player player, @Nullable BlockPos sourceBlock) { + this.block = block; + this.player = player; + this.sourceBlock = sourceBlock; + } + + } + public final Map pendingPlayerBlockEvents = new HashMap<>(); + // Paper end + // Paper start - fix and optimise world upgrading // copied from below public static ResourceKey getDimensionKey(DimensionType manager) { @@ -1046,6 +1067,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { if (!this.preventPoiUpdated) { this.onBlockStateChange(blockposition, iblockdata1, iblockdata2); } + pendingPlayerBlockEvents.remove(blockposition); // Paper // CraftBukkit end } } @@ -1067,6 +1089,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable { if (iblockdata.isAir()) { return false; } else { + // Paper start + PendingBlockEvent blockEvent = pendingPlayerBlockEvents.get(pos); + if (blockEvent != null && blockEvent.sourceBlock != null) { + io.papermc.paper.event.block.BlockDestroyedByNeighborEvent event = + new io.papermc.paper.event.block.BlockDestroyedByNeighborEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), + (org.bukkit.entity.Player) blockEvent.player.getBukkitEntity(), + org.bukkit.craftbukkit.block.CraftBlock.at(this, blockEvent.sourceBlock)); + event.callEvent(); + } + // Paper end + FluidState fluid = this.getFluidState(pos); // Paper start - BlockDestroyEvent; while the above setAir method is named same and looks very similar // they are NOT used with same intent and the above should not fire this event. The above method is more of a BlockSetToAirEvent, diff --git a/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java b/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java index 03b5ab8251497c0c94467f90e6663a0dc766babb..0be28c971bbd88268af4dfdd4489d4fb309ad03a 100644 --- a/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java +++ b/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java @@ -112,6 +112,15 @@ public class DoublePlantBlock extends BushBlock { BlockPos blockposition1 = pos.below(); BlockState iblockdata1 = world.getBlockState(blockposition1); + Level.PendingBlockEvent blockEvent = world.pendingPlayerBlockEvents.remove(pos); + if (blockEvent != null) { + io.papermc.paper.event.block.BlockDestroyedByNeighborEvent event = + new io.papermc.paper.event.block.BlockDestroyedByNeighborEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition1), + (org.bukkit.entity.Player) blockEvent.player.getBukkitEntity(), + org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)); + if (!event.callEvent()) return; + } + if (iblockdata1.is(state.getBlock()) && iblockdata1.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.LOWER) { BlockState iblockdata2 = iblockdata1.getFluidState().is((Fluid) Fluids.WATER) ? Blocks.WATER.defaultBlockState() : Blocks.AIR.defaultBlockState(); diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java index 2892e586146cbc560f0bcf4b9af6d0575cb0a82e..b665a8f932ddae519cdefade555f9ea31fe8f4ac 100644 --- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java @@ -1242,11 +1242,22 @@ public abstract class BlockBehaviour implements FeatureElement { BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); Direction[] aenumdirection = BlockBehaviour.UPDATE_SHAPE_ORDER; int k = aenumdirection.length; + BlockState blockState = world.getBlockState(pos); // Slice for (int l = 0; l < k; ++l) { Direction enumdirection = aenumdirection[l]; blockposition_mutableblockposition.setWithOffset(pos, enumdirection); + // Paper start - Propagate the PendingBlockEvent from the current blockPos to the next block + // if it will break (!canSurvive) + if (blockState.getBukkitMaterial() == org.bukkit.Material.AIR && !world.getBlockState(blockposition_mutableblockposition).canSurvive(world, blockposition_mutableblockposition)) { + Level.PendingBlockEvent blockEvent = ((Level) world).pendingPlayerBlockEvents.get(pos); + if (blockEvent != null) { + BlockPos blockPosCopy = blockposition_mutableblockposition.immutable(); + ((Level) world).pendingPlayerBlockEvents.put(blockPosCopy, new Level.PendingBlockEvent(blockPosCopy, blockEvent.player, pos)); + } + } + // Paper end world.neighborShapeChanged(enumdirection.getOpposite(), this.asState(), blockposition_mutableblockposition, pos, flags, maxUpdateDepth); }