Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package io.papermc.paper.event.block;

import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;

/**
Called when a block is about to drop items

<p>
This event will be called once for every block broken, even if multiple blocks are broken simultaneously.
For example, this event will be called twice when breaking a block with a torch on top.
</p>
<p>
This event will also be called when a block is dropping items as a result of being consumed, for example a cake dropping its candle after being eaten.
</p>
<p>
If you do not need the drops of each individual block, use {@link org.bukkit.event.block.BlockBreakEvent}.
</p>
*/
public class BlockDropResourcesEvent extends BlockEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled;
private final List<ItemStack> drops;
private final Entity breaker;
private final ItemStack tool;
private final BlockState state;

@ApiStatus.Internal
public BlockDropResourcesEvent(final @NotNull Block block, final @NotNull BlockState state, final @NotNull List<ItemStack> drops, final @Nullable Entity breaker, final @Nullable ItemStack tool) {
super(block);
this.cancelled = false;
this.drops = drops;
this.breaker = breaker;
this.tool = tool;
this.state = state;
}

/**
* Get the entity that caused the block to drop resources
* @return The responsible entity, or null if no entity was involved
*/
public @Nullable Entity getEntity() {
return breaker;
}

/**
* Get the tool used to break the block
* @return The tool used, or null
*/
public @Nullable ItemStack getTool() {
return tool;
}

/**
* Get the list of items to be dropped
*
* @return A mutable list of items to be dropped
*/
public @NotNull List<ItemStack> getItems() {
return drops;
}

/**
* Get the block state of the block that is about to drop resources
* @return The block state
*/
public @NotNull BlockState getBlockState() {
return state;
}

@Override
public boolean isCancelled() {
return cancelled;
}

@Override
public void setCancelled(final boolean cancel) {
cancelled = cancel;
}

@Override
public @NotNull HandlerList getHandlers() {
return HANDLER_LIST;
}
public @NotNull static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
private @Nullable Item item;
private static final int CACHE_SIZE = 256;
private static final ThreadLocal<Object2ByteLinkedOpenHashMap<Block.ShapePairKey>> OCCLUSION_CACHE = ThreadLocal.withInitial(() -> {
@@ -382,6 +_,27 @@
@@ -382,19 +_,46 @@
return state.getDrops(params);
}

Expand All @@ -85,29 +85,38 @@
+
public static void dropResources(final BlockState state, final Level level, final BlockPos pos) {
if (level instanceof ServerLevel serverLevel) {
getDrops(state, serverLevel, pos, null).forEach(stack -> popResource(level, pos, stack));
@@ -396,6 +_,12 @@
- getDrops(state, serverLevel, pos, null).forEach(stack -> popResource(level, pos, stack));
+ org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDropResourcesEvent(getDrops(state, serverLevel, pos, null), level, pos, state, null, null).forEach(stack -> popResource(level, pos, stack)); // Paper - implement BlockDropResourcesEvent
state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, true);
}
}

public static void dropResources(final BlockState state, final LevelAccessor level, final BlockPos pos, final @Nullable BlockEntity blockEntity) {
if (level instanceof ServerLevel serverLevel) {
- getDrops(state, serverLevel, pos, blockEntity).forEach(stack -> popResource(serverLevel, pos, stack));
+ org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDropResourcesEvent(getDrops(state, serverLevel, pos, blockEntity), level, pos, state, null, null).forEach(stack -> popResource(serverLevel, pos, stack)); // Paper - implement BlockDropResourcesEvent
state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, true);
}
}
+
+ // Paper start - Properly handle xp dropping
+ public static void dropResources(final BlockState state, final Level level, final BlockPos pos, final @Nullable BlockEntity blockEntity, final @Nullable Entity breaker, final ItemStack tool) {
+ dropResources(state, level, pos, blockEntity, breaker, tool, true);
+ }
+ // Paper end - Properly handle xp dropping
+
public static void dropResources(
final BlockState state,
final Level level,
@@ -403,10 +_,11 @@
final @Nullable BlockEntity blockEntity,
final @Nullable Entity breaker,
final ItemStack tool
+ , final boolean dropExperience // Paper - Properly handle xp dropping
) {
if (level instanceof ServerLevel serverLevel) {
getDrops(state, serverLevel, pos, blockEntity, breaker, tool).forEach(stack -> popResource(level, pos, stack));
- getDrops(state, serverLevel, pos, blockEntity, breaker, tool).forEach(stack -> popResource(level, pos, stack));
- state.spawnAfterBreak(serverLevel, pos, tool, true);
+ org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDropResourcesEvent(getDrops(state, serverLevel, pos, blockEntity, breaker, tool), level, pos, state, breaker, tool).forEach(stack -> popResource(level, pos, stack)); // Paper - implement BlockDropResourcesEvent
+ state.spawnAfterBreak(serverLevel, pos, tool, dropExperience); // Paper - Properly handle xp dropping
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.papermc.paper.block.bed.BedEnterProblem;
import io.papermc.paper.connection.HorriblePlayerLoginEventHack;
import io.papermc.paper.connection.PlayerConnection;
import io.papermc.paper.event.block.BlockDropResourcesEvent;
import io.papermc.paper.event.block.BlockLockCheckEvent;
import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent;
import io.papermc.paper.event.entity.EntityIgniteEvent;
Expand Down Expand Up @@ -2425,4 +2426,31 @@ public static int callEntityIgniteEvent(Entity entity, int fuseTime) {
}
return event.getFuseTime();
}

public static List<ItemStack> callBlockDropResourcesEvent(List<ItemStack> drops, LevelAccessor level, BlockPos pos, net.minecraft.world.level.block.state.BlockState state, @Nullable Entity breaker, @Nullable ItemStack tool) {
if (BlockDropResourcesEvent.getHandlerList().getRegisteredListeners().length == 0) {
return drops; // No listeners, skip event creation
}

var converted = new ArrayList<org.bukkit.inventory.ItemStack>();
for (var drop : drops) {
converted.add(CraftItemStack.asCraftMirror(drop));
}

var stateSnapshot = CraftBlockStates.getBlockState(level, pos);
stateSnapshot.setBlock(state);

var event = new BlockDropResourcesEvent(
CraftBlock.at(level, pos),
stateSnapshot,
converted, breaker == null ? null : breaker.getBukkitEntity(),
tool == null ? null : CraftItemStack.asCraftMirror(tool)
);
if (event.callEvent()) {
// convert items back
return event.getItems().stream().map(CraftItemStack::asNMSCopy).toList();
}

return List.of(); // return nothing if event was cancelled
}
}
Loading