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 baf3e79489e310f443788bc917c553ae7ea86c89..9a17193c9fe26c9c0390906b4c1f9d619ccba249 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java @@ -438,8 +438,8 @@ public class ServerPlayerGameMode { isCorrectTool = flag1; // Paper itemstack.mineBlock(this.level, iblockdata, pos, this.player); - if (flag && flag1 && event.isDropItems()) { // CraftBukkit - Check if block should drop items - block.playerDestroy(this.level, this.player, pos, iblockdata, 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, iblockdata, tileentity, itemstack1, event.isDropItems()); // Paper } // 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 8b715a750a0406d1d7b25c2f023dc2fb4d8b70fa..2abb05c5d4de9ebd0764c4aab444adc914173cdc 100644 --- a/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java +++ b/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java @@ -77,8 +77,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) { // Paper + super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops); // Paper 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 9522e646529f3d849471931b4b3c0d133e7fcfc5..fdd9c61b7248e92dbcbec91cd6fe4c6310bba237 100644 --- a/src/main/java/net/minecraft/world/level/block/Block.java +++ b/src/main/java/net/minecraft/world/level/block/Block.java @@ -416,10 +416,18 @@ public class Block extends BlockBehaviour implements ItemLike { return this.defaultBlockState(); } + @io.papermc.paper.annotation.DoNotUse // Paper - method below allows better control of item drops public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) { + // Paper start + this.playerDestroy(world, player, pos, state, blockEntity, tool, true); + } + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops) { + // Paper end player.awardStat(Stats.BLOCK_MINED.get(this)); player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); // CraftBukkit - EntityExhaustionEvent + if (includeDrops) { // Paper Block.dropResources(state, world, pos, blockEntity, player, tool); + } // Paper } 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 9db66b393e057d93a8025b803ae0ad2a1bca61f6..030b38d5d5d2578d6ef482a239ef58787efa3b08 100644 --- a/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java +++ b/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java @@ -89,8 +89,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) { // Paper + super.playerDestroy(world, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, tool, includeDrops); // Paper } protected static void preventCreativeDropFromBottomPart(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 5fbdc96f29e29dfc092b9e84a988032db0fa36ab..04089e6f7d6e3d532b00585870283922b6be5246 100644 --- a/src/main/java/net/minecraft/world/level/block/IceBlock.java +++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java @@ -25,8 +25,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) { // Paper + super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops); // Paper // Paper start 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 6c1a0e6f961e46a1a89850746a71e97b32514adf..181e99e3f29c70568b6f28f8a5d3bb692f7abad8 100644 --- a/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java +++ b/src/main/java/net/minecraft/world/level/block/TurtleEggBlock.java @@ -164,8 +164,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) { // Paper + super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops); // Paper 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..934b4b5b0d36e84fed6c2754b513395a22b86eb5 --- /dev/null +++ b/src/test/java/io/papermc/paper/world/block/BlockPlayerDestroyOverrideTest.java @@ -0,0 +1,51 @@ +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 org.bukkit.support.AbstractTestingBase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.assertEquals; + +@RunWith(Parameterized.class) +public class BlockPlayerDestroyOverrideTest extends AbstractTestingBase { + + @Parameterized.Parameter + public ClassInfo overridesPlayerDestroy; + + @Parameterized.Parameters + public static Iterable 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; + } + + @Test + public void checkPlayerDestroyOverrides() { + final MethodInfoList playerDestroy = this.overridesPlayerDestroy.getDeclaredMethodInfo("playerDestroy"); + assertEquals(this.overridesPlayerDestroy.getName() + " has multiple playerDestroy methods", 1, playerDestroy.size()); + final MethodInfo next = playerDestroy.iterator().next(); + final MethodParameterInfo[] parameterInfo = next.getParameterInfo(); + assertEquals(this.overridesPlayerDestroy.getName() + " needs to change it's override of playerDestroy", "boolean", parameterInfo[parameterInfo.length - 1].getTypeDescriptor().toStringWithSimpleNames()); + } +}