From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 4 Mar 2023 10:52:52 -0800 Subject: [PATCH] Properly handle BlockBreakEvent#isDropItems Setting whether a block break dropped items controlled far more than just whether blocks dropped, like stat increases food consumption, turtle egg count decreases, ice to water conversions and beehive releases diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java index 3b93db1e23266c6ee3d2e22825c7668ef5b81f95..7fa8041b12b8398a1d4838142c61cdc0f5bd51ad 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java @@ -431,8 +431,8 @@ public class ServerPlayerGameMode { isCorrectTool = flag1; // Paper - Trigger bee_nest_destroyed trigger in the correct place itemstack.mineBlock(this.level, iblockdata1, pos, this.player); - if (flag && flag1 && event.isDropItems()) { // CraftBukkit - Check if block should drop items - block.playerDestroy(this.level, this.player, pos, iblockdata1, tileentity, itemstack1); + if (flag && flag1/* && event.isDropItems() */) { // CraftBukkit - Check if block should drop items // Paper - fix drops not preventing stats/food exhaustion + block.playerDestroy(this.level, this.player, pos, iblockdata1, tileentity, itemstack1, event.isDropItems(), false); // Paper - fix drops not preventing stats/food exhaustion } // return true; // CraftBukkit diff --git a/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java b/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java index a0ab721a01faccf216259c46e6d6f638426732c2..2d4a770ec7c0737eee6f06b32958716d50d230a1 100644 --- a/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java +++ b/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java @@ -84,8 +84,8 @@ public class BeehiveBlock extends BaseEntityBlock { } @Override - public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { - super.playerDestroy(world, player, pos, state, blockEntity, tool); + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion + super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion if (!world.isClientSide && blockEntity instanceof BeehiveBlockEntity) { BeehiveBlockEntity tileentitybeehive = (BeehiveBlockEntity) blockEntity; diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java index 9120b4a70dbf84f19480b952658414e16d2c23c1..660ede322b4f4ccad241820d8ffd4540ebb18fbc 100644 --- a/src/main/java/net/minecraft/world/level/block/Block.java +++ b/src/main/java/net/minecraft/world/level/block/Block.java @@ -410,10 +410,18 @@ public class Block extends BlockBehaviour implements ItemLike { return this.defaultBlockState(); } + @io.papermc.paper.annotation.DoNotUse // Paper - fix drops not preventing stats/food exhaustion public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { + // Paper start - fix drops not preventing stats/food exhaustion + this.playerDestroy(world, player, pos, state, blockEntity, tool, true, true); + } + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { + // Paper end - fix drops not preventing stats/food exhaustion player.awardStat(Stats.BLOCK_MINED.get(this)); player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); // CraftBukkit - EntityExhaustionEvent + if (includeDrops) { // Paper - fix drops not preventing stats/food exhaustion Block.dropResources(state, world, pos, blockEntity, player, tool); + } // Paper - fix drops not preventing stats/food exhaustion } public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {} 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 0f0750f8c790d0db72a0e6b277449a1461674890..03b5ab8251497c0c94467f90e6663a0dc766babb 100644 --- a/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java +++ b/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java @@ -96,8 +96,8 @@ public class DoublePlantBlock extends BushBlock { } @Override - public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { - super.playerDestroy(world, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, tool); + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion + super.playerDestroy(world, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion } protected static void preventDropFromBottomPart(Level world, BlockPos pos, BlockState state, Player player) { diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java index 52c6c26a300cfd19c478afba411a17c8d5ea0c3c..9c8c1df5187daefb1c8098b4d4a0976c71a7bbfd 100644 --- a/src/main/java/net/minecraft/world/level/block/IceBlock.java +++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java @@ -33,8 +33,8 @@ public class IceBlock extends HalfTransparentBlock { } @Override - public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { - super.playerDestroy(world, player, pos, state, blockEntity, tool); + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion + super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion // Paper start - Improve Block#breakNaturally API this.afterDestroy(world, pos, tool); } diff --git a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java index c79f3a8885a5ffc9ebac51992e63df14929d9f24..b4646e26965e0f1f26c5019e7c6a13fdf22bdb47 100644 --- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java +++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java @@ -173,8 +173,8 @@ public class TurtleEggBlock extends Block { } @Override - public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { - super.playerDestroy(world, player, pos, state, blockEntity, tool); + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion + super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion this.decreaseEggs(world, pos, state); } diff --git a/src/test/java/io/papermc/paper/world/block/BlockPlayerDestroyOverrideTest.java b/src/test/java/io/papermc/paper/world/block/BlockPlayerDestroyOverrideTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7c435f7079b429873f33d7bade82eca0c6b45842 --- /dev/null +++ b/src/test/java/io/papermc/paper/world/block/BlockPlayerDestroyOverrideTest.java @@ -0,0 +1,47 @@ +package io.papermc.paper.world.block; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.MethodInfoList; +import io.github.classgraph.MethodParameterInfo; +import io.github.classgraph.ScanResult; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.bukkit.support.AbstractTestingBase; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BlockPlayerDestroyOverrideTest extends AbstractTestingBase { + + public static Stream parameters() { + final List classInfo = new ArrayList<>(); + try (ScanResult scanResult = new ClassGraph() + .enableClassInfo() + .enableMethodInfo() + .whitelistPackages("net.minecraft") + .scan() + ) { + for (final ClassInfo subclass : scanResult.getSubclasses("net.minecraft.world.level.block.Block")) { + final MethodInfoList playerDestroy = subclass.getDeclaredMethodInfo("playerDestroy"); + if (!playerDestroy.isEmpty()) { + classInfo.add(subclass); + } + } + } + return classInfo.stream(); + } + + @ParameterizedTest + @MethodSource("parameters") + public void checkPlayerDestroyOverrides(ClassInfo overridesPlayerDestroy) { + final MethodInfoList playerDestroy = overridesPlayerDestroy.getDeclaredMethodInfo("playerDestroy"); + assertEquals(1, playerDestroy.size(), overridesPlayerDestroy.getName() + " has multiple playerDestroy methods"); + final MethodInfo next = playerDestroy.iterator().next(); + final MethodParameterInfo[] parameterInfo = next.getParameterInfo(); + assertEquals("boolean", parameterInfo[parameterInfo.length - 1].getTypeDescriptor().toStringWithSimpleNames(), overridesPlayerDestroy.getName() + " needs to change its override of playerDestroy"); + } +}