-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
Expected behavior
Placing container blocks in places such as the end portal and the end podium, and cancelling the destruction of those containers doesn't duplicate the items within the container
Additionally, placing blocks in places such as the end podium and end platform, and cancelling the destruction of those blocks actually prevents their destruction.
Observed/Actual behavior
Cancelling BlockDestroyEvent and the subsequent BlockMultiPlaceEvent when a container is placed within the area the end portal generates when placing the last eye in an end portal frame, or around/inside the end podium when the Ender Dragon is respawned, causes the items inside the container to drop, but also remain within the container itself, resulting in duplicate items.
This is because Paper stores the blocks modified during an item use (in this case, eye of ender or end crystal), and fires a BlockMultiPlaceEvent with those modified blocks. Cancelling the event causes Paper to restore the blocks as they were before the item use. However, the blocks are still temporarily modified. In particular, LevelChunk.setBlockState is called, which checks if a block entity is being replaced, and if so, calls BlockEntity.preRemoveSideEffects, which for containers runs Containers.dropContents.
This means that before BlockMultiPlaceEvent is fired, all the items from containers are already dropped and in the world. Reverting the modified blocks restores the contents of those containers in addition to the already dropped items, since Paper saved the block data from before they were replaced.
Additionally, cancelling BlockDestroyEvent when a block is placed around/inside the end podium when the Ender Dragon dies, or in the area of the end platform when a player enters the end portal, doesn't prevent the blocks from actually being destroyed. No item duplication can happen here, but the items are still spilled and not prevented from being destroyed. BlockMultiPlaceEvent isn't fired here as there is no player to attribute the changes to.
There may be more ways to trigger this, but what all these scenarios have in common is that they call Level.destroyBlock followed by Level.setBlock. Thanks to Paper events, the destroyBlock may be cancelled, but vanilla code still assumes that the blocks are empty. setBlock causes the item drops, since instead of overwriting air, an actual block may still exist there. This action doesn't call any events, unless an item is currently being used.
There is no API method to prevent these blocks from being overridden without duplication.
Steps/models to reproduce
@EventHandler
public void onBlockDestroy(BlockDestroyEvent e) {
if (e.getBlock().getType() == Material.BARREL) {
e.setCancelled(true);
}
}
@EventHandler
public void onBlockPlace(BlockMultiPlaceEvent e) {
if (e.getReplacedBlockStates().stream().anyMatch(i -> i.getType() == Material.BARREL)) {
e.setCancelled(true);
}
}Place a barrel with items within the end fountain, podium, or platform, and observe either item duplication, or the barrel being destroyed regardless of destruction being cancelled.
Plugin and Datapack List
Paper-Test-Plugin
Paper version
This server is running Paper version 1.21.11-DEV-main@4873e3f (1970-01-01T00:00:00Z) (Implementing API version 1.21.11-R0.1-SNAPSHOT)
(4873e3f)
Other
Fixing #12576 would cause end fountains to move from "duplication" to "ignoring destruction"
#13536 is a similar issue (duplication due to event system)