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
This commit is contained in:
Jake Potrebic 2023-03-04 10:52:52 -08:00
parent 0f4ee39a8e
commit 597cb633e8
7 changed files with 107 additions and 16 deletions

View file

@ -337,8 +337,9 @@
itemstack.mineBlock(this.level, iblockdata1, pos, this.player);
- if (flag && flag1) {
+ if (flag && flag1 && event.isDropItems()) { // CraftBukkit - Check if block should drop items
block.playerDestroy(this.level, this.player, pos, iblockdata1, tileentity, itemstack1);
- 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;

View file

@ -1,5 +1,16 @@
--- a/net/minecraft/world/level/block/BeehiveBlock.java
+++ b/net/minecraft/world/level/block/BeehiveBlock.java
@@ -94,8 +94,8 @@
}
@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 tileentitybeehive) {
if (!EnchantmentHelper.hasTag(tool, EnchantmentTags.PREVENTS_BEE_SPAWNS_WHEN_MINING)) {
tileentitybeehive.emptyAllLivingFromHive(player, state, BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY);
@@ -103,7 +103,7 @@
this.angerNearbyBees(world, pos);
}

View file

@ -1,11 +1,8 @@
--- a/net/minecraft/world/level/block/Block.java
+++ b/net/minecraft/world/level/block/Block.java
@@ -292,8 +292,26 @@
});
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
}
+
+ }
@@ -295,6 +295,24 @@
}
+ // Paper start - Add BlockBreakBlockEvent
+ public static boolean dropResources(BlockState state, LevelAccessor levelAccessor, BlockPos pos, @Nullable BlockEntity blockEntity, BlockPos source) {
@ -22,11 +19,12 @@
+ state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, true);
+ }
+ return true;
}
+ }
+ // Paper end - Add BlockBreakBlockEvent
+
public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) {
if (world instanceof ServerLevel) {
Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> {
@@ -340,7 +358,13 @@
ItemEntity entityitem = (ItemEntity) itemEntitySupplier.get();
@ -57,16 +55,27 @@
}
}
@@ -369,7 +398,7 @@
@@ -367,10 +396,18 @@
return this.defaultBlockState();
}
+ @io.papermc.paper.annotation.DoNotUse @Deprecated // 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);
+ 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
}
@@ -490,15 +519,35 @@
public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {}
@@ -490,15 +527,35 @@
return this.builtInRegistryHolder;
}

View file

@ -1,6 +1,13 @@
--- a/net/minecraft/world/level/block/DoublePlantBlock.java
+++ b/net/minecraft/world/level/block/DoublePlantBlock.java
@@ -103,6 +103,11 @@
@@ -98,11 +98,16 @@
}
@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) {

View file

@ -1,9 +1,13 @@
--- a/net/minecraft/world/level/block/IceBlock.java
+++ b/net/minecraft/world/level/block/IceBlock.java
@@ -36,6 +36,11 @@
@@ -34,8 +34,13 @@
}
@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) {
- 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);
+ }

View file

@ -63,3 +63,14 @@
}
}
}
@@ -147,8 +175,8 @@
}
@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);
}

View file

@ -0,0 +1,48 @@
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.environment.Normal;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Normal
public class BlockPlayerDestroyOverrideTest {
public static Stream<ClassInfo> parameters() {
final List<ClassInfo> 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");
}
}