net.minecraft.core.dispenser

This commit is contained in:
Jake Potrebic 2024-12-13 13:30:06 -08:00
parent 732cf86b99
commit f252b67a97
No known key found for this signature in database
GPG key ID: ECE0B3C133C016C5
16 changed files with 1009 additions and 1168 deletions

View file

@ -0,0 +1,45 @@
--- a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
@@ -40,13 +_,39 @@
d4 = 0.0;
}
+ // CraftBukkit start
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink at end and single item in event
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
AbstractBoat abstractBoat = this.type.create(serverLevel, EntitySpawnReason.DISPENSER);
if (abstractBoat != null) {
- abstractBoat.setInitialPos(d1, d2 + d4, d3);
+ abstractBoat.setInitialPos(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); // CraftBukkit
EntityType.<AbstractBoat>createDefaultStackConfig(serverLevel, item, null).accept(abstractBoat);
abstractBoat.setYRot(direction.toYRot());
- serverLevel.addFreshEntity(abstractBoat);
- item.shrink(1);
+ if (serverLevel.addFreshEntity(abstractBoat) && shrink) item.shrink(1); // Paper - if entity add was successful and supposed to shrink
}
return item;

View file

@ -0,0 +1,100 @@
--- a/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
@@ -8,25 +_,48 @@
import net.minecraft.world.level.block.DispenserBlock;
public class DefaultDispenseItemBehavior implements DispenseItemBehavior {
+ private Direction direction; // Paper - cache facing direction
private static final int DEFAULT_ACCURACY = 6;
+ // CraftBukkit start
+ private boolean dropper;
+
+ public DefaultDispenseItemBehavior(boolean dropper) {
+ this.dropper = dropper;
+ }
+
+ public DefaultDispenseItemBehavior() {}
+ // CraftBukkit end
+
@Override
public final ItemStack dispense(BlockSource blockSource, ItemStack item) {
+ this.direction = blockSource.state().getValue(DispenserBlock.FACING); // Paper - cache facing direction
ItemStack itemStack = this.execute(blockSource, item);
this.playSound(blockSource);
- this.playAnimation(blockSource, blockSource.state().getValue(DispenserBlock.FACING));
+ this.playAnimation(blockSource, this.direction); // Paper - cache facing direction
return itemStack;
}
protected ItemStack execute(BlockSource blockSource, ItemStack item) {
- Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
+ // Paper - cached enum direction
Position dispensePosition = DispenserBlock.getDispensePosition(blockSource);
ItemStack itemStack = item.split(1);
- spawnItem(blockSource.level(), itemStack, 6, direction, dispensePosition);
+ // CraftBukkit start
+ if (!DefaultDispenseItemBehavior.spawnItem(blockSource.level(), itemStack, 6, this.direction, dispensePosition, blockSource, this.dropper)) {
+ item.grow(1);
+ }
+ // CraftBukkit end
return item;
}
public static void spawnItem(Level level, ItemStack stack, int speed, Direction facing, Position position) {
+ // CraftBukkit start
+ ItemEntity itemEntity = prepareItem(level, stack, speed, facing, position);
+ level.addFreshEntity(itemEntity);
+ }
+
+ private static ItemEntity prepareItem(Level level, ItemStack stack, int speed, Direction facing, Position position) {
+ // CraftBukkit end
double d = position.x();
double d1 = position.y();
double d2 = position.z();
@@ -44,6 +_,45 @@
level.random.triangle(facing.getStepZ() * d3, 0.0172275 * speed)
);
level.addFreshEntity(itemEntity);
+ return itemEntity; // CraftBukkit
+ }
+
+ // CraftBukkit - void -> boolean return, IPosition -> ISourceBlock last argument, dropper
+ public static boolean spawnItem(Level level, ItemStack stack, int speed, Direction facing, Position dispensePosition, BlockSource blockSource, boolean dropper) {
+ if (stack.isEmpty()) return true;
+ ItemEntity itemEntity = DefaultDispenseItemBehavior.prepareItem(level, stack, speed, facing, dispensePosition);
+
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(itemEntity.getDeltaMovement()));
+ if (!DispenserBlock.eventFired) {
+ level.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ itemEntity.setItem(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
+ itemEntity.setDeltaMovement(org.bukkit.craftbukkit.util.CraftVector.toNMS(event.getVelocity()));
+
+ if (!dropper && !event.getItem().getType().equals(craftItem.getType())) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior.getClass() != DefaultDispenseItemBehavior.class) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ } else {
+ level.addFreshEntity(itemEntity);
+ }
+ return false;
+ }
+
+ level.addFreshEntity(itemEntity);
+
+ return true;
+ // CraftBukkit end
}
protected void playSound(BlockSource blockSource) {

View file

@ -0,0 +1,606 @@
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -43,7 +_,9 @@
import net.minecraft.world.level.block.CandleCakeBlock;
import net.minecraft.world.level.block.CarvedPumpkinBlock;
import net.minecraft.world.level.block.DispenserBlock;
+import net.minecraft.world.level.block.LiquidBlockContainer;
import net.minecraft.world.level.block.RespawnAnchorBlock;
+import net.minecraft.world.level.block.SaplingBlock;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import net.minecraft.world.level.block.SkullBlock;
import net.minecraft.world.level.block.TntBlock;
@@ -82,16 +_,48 @@
Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
EntityType<?> type = ((SpawnEggItem)item.getItem()).getType(blockSource.level().registryAccess(), item);
+ // CraftBukkit start
+ ServerLevel serverLevel = blockSource.level();
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // item.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ // Paper start - track changed items in the dispense event
+ itemstack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified
+ type = ((SpawnEggItem) itemstack1.getItem()).getType(serverLevel.registryAccess(), itemstack1);
+ // Paper end - track changed item from dispense event
+ }
try {
type.spawn(
- blockSource.level(), item, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false
+ blockSource.level(), itemstack1, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false // Paper - track changed item in dispense event
);
} catch (Exception var6) {
LOGGER.error("Error while dispensing spawn egg from dispenser at {}", blockSource.pos(), var6);
return ItemStack.EMPTY;
}
- item.shrink(1);
+ if (shrink) item.shrink(1); // Paper - actually handle here
+ // CraftBukkit end
blockSource.level().gameEvent(null, GameEvent.ENTITY_PLACE, blockSource.pos());
return item;
}
@@ -109,12 +_,40 @@
Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
BlockPos blockPos = blockSource.pos().relative(direction);
ServerLevel serverLevel = blockSource.level();
+ // CraftBukkit start
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // item.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
+ final ItemStack newStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // Paper - use event itemstack (unwrap is fine here because the stack won't be modified)
Consumer<ArmorStand> consumer = EntityType.appendDefaultStackConfig(
- armorStand1 -> armorStand1.setYRot(direction.toYRot()), serverLevel, item, null
+ armorStand1 -> armorStand1.setYRot(direction.toYRot()), serverLevel, newStack, null // Paper - track changed items in the dispense event
);
ArmorStand armorStand = EntityType.ARMOR_STAND.spawn(serverLevel, consumer, blockPos, EntitySpawnReason.DISPENSER, false, false);
if (armorStand != null) {
- item.shrink(1);
+ if (shrink) item.shrink(1); // Paper - actually handle here
}
return item;
@@ -134,7 +_,36 @@
livingEntity -> livingEntity instanceof Saddleable saddleable && !saddleable.isSaddled() && saddleable.isSaddleable()
);
if (!entitiesOfClass.isEmpty()) {
- ((Saddleable)entitiesOfClass.get(0)).equipSaddle(item.split(1), SoundSource.BLOCKS);
+ // CraftBukkit start
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event
+ ServerLevel world = blockSource.level();
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // item.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+ idispensebehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ ((Saddleable) entitiesOfClass.get(0)).equipSaddle(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), SoundSource.BLOCKS); // Paper - track changed items in dispense event
+ // CraftBukkit end
+ if (shrink) item.shrink(1); // Paper - actually handle here
this.setSuccess(true);
return item;
} else {
@@ -156,8 +_,36 @@
new AABB(blockPos),
abstractChestedHorse1 -> abstractChestedHorse1.isAlive() && !abstractChestedHorse1.hasChest()
)) {
- if (abstractChestedHorse.isTamed() && abstractChestedHorse.getSlot(499).set(item)) {
- item.shrink(1);
+ if (abstractChestedHorse.isTamed()/* && abstractChestedHorse.getSlot(499).set(item)*/) {
+ ItemStack singleCopy = item.copyWithCount(1); // Paper - shrink below
+ ServerLevel world = blockSource.level();
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleCopy);
+ org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), abstractChestedHorse.getBukkitLivingEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below (this was actually missing and should be here, added it commented out to be consistent)
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { // Paper - fix possible StackOverflowError
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ abstractChestedHorse.getSlot(499).set(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
+ // CraftBukkit end
+
+ if (shrink) item.shrink(1); // Paper - actually handle here
this.setSuccess(true);
return item;
}
@@ -195,8 +_,50 @@
DispensibleContainerItem dispensibleContainerItem = (DispensibleContainerItem)item.getItem();
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
Level level = blockSource.level();
+ // CraftBukkit start
+ int x = blockPos.getX();
+ int y = blockPos.getY();
+ int z = blockPos.getZ();
+ BlockState iblockdata = level.getBlockState(blockPos);
+ ItemStack dispensedItem = item; // Paper - track changed item from the dispense event
+ // Paper start - correctly check if the bucket place will succeed
+ /* Taken from SolidBucketItem#emptyContents */
+ boolean willEmptyContentsSolidBucketItem = dispensibleContainerItem instanceof net.minecraft.world.item.SolidBucketItem && level.isInWorldBounds(blockPos) && iblockdata.isAir();
+ /* Taken from BucketItem#emptyContents */
+ boolean willEmptyBucketItem = dispensibleContainerItem instanceof final net.minecraft.world.item.BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (iblockdata.isAir() || iblockdata.canBeReplaced(bucketItem.content) || (iblockdata.getBlock() instanceof LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, level, blockPos, iblockdata, bucketItem.content)));
+ if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
+ // Paper end - correctly check if the bucket place will succeed
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
+ if (!DispenserBlock.eventFired) {
+ level.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+
+ // Paper start - track changed item from dispense event
+ dispensedItem = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe here as the stack isn't mutated
+ dispensibleContainerItem = (DispensibleContainerItem) dispensedItem.getItem();
+ // Paper end - track changed item from dispense event
+ }
+ // CraftBukkit end
+
if (dispensibleContainerItem.emptyContents(null, level, blockPos, null)) {
- dispensibleContainerItem.checkExtraContent(null, level, item, blockPos);
+ dispensibleContainerItem.checkExtraContent(null, level, dispensedItem, blockPos); // Paper - track changed item from dispense event
return this.consumeWithRemainder(blockSource, item, new ItemStack(Items.BUCKET));
} else {
return this.defaultDispenseItemBehavior.dispense(blockSource, item);
@@ -219,12 +_,37 @@
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
BlockState blockState = levelAccessor.getBlockState(blockPos);
if (blockState.getBlock() instanceof BucketPickup bucketPickup) {
- ItemStack itemStack = bucketPickup.pickupBlock(null, levelAccessor, blockPos, blockState);
+ ItemStack itemStack = bucketPickup.pickupBlock(null, org.bukkit.craftbukkit.util.DummyGeneratorAccess.INSTANCE, blockPos, blockState); // CraftBukkit
if (itemStack.isEmpty()) {
return super.execute(blockSource, item);
} else {
levelAccessor.gameEvent(null, GameEvent.FLUID_PICKUP, blockPos);
Item item1 = itemStack.getItem();
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
+ if (!DispenserBlock.eventFired) {
+ levelAccessor.getMinecraftWorld().getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+
+ itemStack = bucketPickup.pickupBlock(null, levelAccessor, blockPos, blockState); // From above
+ // CraftBukkit end
return this.consumeWithRemainder(blockSource, item, new ItemStack(item1));
}
} else {
@@ -236,17 +_,44 @@
@Override
protected ItemStack execute(BlockSource blockSource, ItemStack item) {
ServerLevel serverLevel = blockSource.level();
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
this.setSuccess(true);
Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
BlockPos blockPos = blockSource.pos().relative(direction);
BlockState blockState = serverLevel.getBlockState(blockPos);
if (BaseFireBlock.canBePlacedAt(serverLevel, blockPos, direction)) {
- serverLevel.setBlockAndUpdate(blockPos, BaseFireBlock.getState(serverLevel, blockPos));
- serverLevel.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos);
+ // CraftBukkit start - Ignition by dispensing flint and steel
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(serverLevel, blockPos, blockSource.pos()).isCancelled()) {
+ serverLevel.setBlockAndUpdate(blockPos, BaseFireBlock.getState(serverLevel, blockPos));
+ serverLevel.gameEvent(null, GameEvent.BLOCK_PLACE, blockPos);
+ }
+ // CraftBukkit end
} else if (CampfireBlock.canLight(blockState) || CandleBlock.canLight(blockState) || CandleCakeBlock.canLight(blockState)) {
serverLevel.setBlockAndUpdate(blockPos, blockState.setValue(BlockStateProperties.LIT, Boolean.valueOf(true)));
serverLevel.gameEvent(null, GameEvent.BLOCK_CHANGE, blockPos);
- } else if (blockState.getBlock() instanceof TntBlock) {
+ } else if (blockState.getBlock() instanceof TntBlock && org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(serverLevel, blockPos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, blockSource.pos())) { // CraftBukkit - TNTPrimeEvent
TntBlock.explode(serverLevel, blockPos);
serverLevel.removeBlock(blockPos, false);
} else {
@@ -266,11 +_,62 @@
this.setSuccess(true);
Level level = blockSource.level();
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
+ // CraftBukkit start
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ level.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+
+ level.captureTreeGeneration = true;
+ // CraftBukkit end
if (!BoneMealItem.growCrop(item, level, blockPos) && !BoneMealItem.growWaterPlant(item, level, blockPos, null)) {
this.setSuccess(false);
} else if (!level.isClientSide) {
level.levelEvent(1505, blockPos, 15);
}
+ // CraftBukkit start
+ level.captureTreeGeneration = false;
+ if (level.capturedBlockStates.size() > 0) {
+ org.bukkit.TreeType treeType = SaplingBlock.treeType;
+ SaplingBlock.treeType = null;
+ org.bukkit.Location location = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(blockPos, level.getWorld());
+ List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(level.capturedBlockStates.values());
+ level.capturedBlockStates.clear();
+ org.bukkit.event.world.StructureGrowEvent structureEvent = null;
+ if (treeType != null) {
+ structureEvent = new org.bukkit.event.world.StructureGrowEvent(location, treeType, false, null, blocks);
+ org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
+ }
+
+ org.bukkit.event.block.BlockFertilizeEvent fertilizeEvent = new org.bukkit.event.block.BlockFertilizeEvent(location.getBlock(), null, blocks);
+ fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled());
+ org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent);
+
+ if (!fertilizeEvent.isCancelled()) {
+ for (org.bukkit.block.BlockState blockstate : blocks) {
+ blockstate.update(true);
+ blockSource.level().checkCapturedTreeStateForObserverNotify(blockPos, (org.bukkit.craftbukkit.block.CraftBlockState) blockstate); // Paper - notify observers even if grow failed
+ }
+ }
+ }
+ // CraftBukkit end
return item;
}
@@ -280,11 +_,39 @@
protected ItemStack execute(BlockSource blockSource, ItemStack item) {
Level level = blockSource.level();
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
- PrimedTnt primedTnt = new PrimedTnt(level, blockPos.getX() + 0.5, blockPos.getY(), blockPos.getZ() + 0.5, null);
+ // CraftBukkit start
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink at end and single item in event
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockPos.getX() + 0.5D, (double) blockPos.getY(), (double) blockPos.getZ() + 0.5D));
+ if (!DispenserBlock.eventFired) {
+ level.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // item.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+
+ PrimedTnt primedTnt = new PrimedTnt(level, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), null);
+ // CraftBukkit end
level.addFreshEntity(primedTnt);
level.playSound(null, primedTnt.getX(), primedTnt.getY(), primedTnt.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F);
level.gameEvent(null, GameEvent.ENTITY_PLACE, blockPos);
- item.shrink(1);
+ if (shrink) item.shrink(1); // Paper - actually handle here
return item;
}
});
@@ -296,6 +_,29 @@
Level level = blockSource.level();
Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
BlockPos blockPos = blockSource.pos().relative(direction);
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
+ if (!DispenserBlock.eventFired) {
+ level.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
if (level.isEmptyBlock(blockPos) && WitherSkullBlock.canSpawnMob(level, blockPos, item)) {
level.setBlock(
blockPos,
@@ -313,7 +_,7 @@
item.shrink(1);
this.setSuccess(true);
} else {
- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item));
+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item, this)); // Paper - fix possible StackOverflowError
}
return item;
@@ -326,6 +_,29 @@
Level level = blockSource.level();
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
CarvedPumpkinBlock carvedPumpkinBlock = (CarvedPumpkinBlock)Blocks.CARVED_PUMPKIN;
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
+ if (!DispenserBlock.eventFired) {
+ level.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
if (level.isEmptyBlock(blockPos) && carvedPumpkinBlock.canSpawnGolem(level, blockPos)) {
if (!level.isClientSide) {
level.setBlock(blockPos, carvedPumpkinBlock.defaultBlockState(), 3);
@@ -335,7 +_,7 @@
item.shrink(1);
this.setSuccess(true);
} else {
- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item));
+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(blockSource, item, this)); // Paper - fix possible StackOverflowError
}
return item;
@@ -361,6 +_,29 @@
ServerLevel serverLevel = blockSource.level();
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
BlockState blockState = serverLevel.getBlockState(blockPos);
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - only single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(blockPos));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
if (blockState.is(
BlockTags.BEEHIVES,
blockStateBase -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockStateBase.getBlock() instanceof BeehiveBlock
@@ -389,6 +_,13 @@
this.setSuccess(true);
if (blockState.is(Blocks.RESPAWN_ANCHOR)) {
if (blockState.getValue(RespawnAnchorBlock.CHARGE) != 4) {
+ // Paper start - Call missing BlockDispenseEvent
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+ if (result != null) {
+ this.setSuccess(false);
+ return result;
+ }
+ // Paper end - Call missing BlockDispenseEvent
RespawnAnchorBlock.charge(null, level, blockPos, blockState);
item.shrink(1);
} else {
@@ -412,6 +_,29 @@
this.setSuccess(false);
return item;
} else {
+ // CraftBukkit start
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity());
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+ idispensebehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
for (Armadillo armadillo : entitiesOfClass) {
if (armadillo.brushOffScute()) {
item.hurtAndBreak(16, serverLevel, null, item1 -> {});
@@ -432,6 +_,13 @@
BlockState blockState = level.getBlockState(blockPos);
Optional<BlockState> waxed = HoneycombItem.getWaxed(blockState);
if (waxed.isPresent()) {
+ // Paper start - Call missing BlockDispenseEvent
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos, item, this);
+ if (result != null) {
+ this.setSuccess(false);
+ return result;
+ }
+ // Paper end - Call missing BlockDispenseEvent
level.setBlockAndUpdate(blockPos, waxed.get());
level.levelEvent(3003, blockPos, 0);
item.shrink(1);
@@ -459,6 +_,12 @@
if (!serverLevel.getBlockState(blockPos1).is(BlockTags.CONVERTABLE_TO_MUD)) {
return this.defaultDispenseItemBehavior.dispense(blockSource, item);
} else {
+ // Paper start - Call missing BlockDispenseEvent
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(blockSource, blockPos1, item, this);
+ if (result != null) {
+ return result;
+ }
+ // Paper end - Call missing BlockDispenseEvent
if (!serverLevel.isClientSide) {
for (int i = 0; i < 5; i++) {
serverLevel.sendParticles(

View file

@ -0,0 +1,61 @@
--- a/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
@@ -17,7 +_,13 @@
return dispenseEquipment(blockSource, item) ? item : super.execute(blockSource, item);
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
public static boolean dispenseEquipment(BlockSource blockSource, ItemStack item) {
+ // Paper start
+ return dispenseEquipment(blockSource, item, null);
+ }
+ public static boolean dispenseEquipment(BlockSource blockSource, ItemStack item, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) {
+ // Paper end
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
List<LivingEntity> entitiesOfClass = blockSource.level()
.getEntitiesOfClass(LivingEntity.class, new AABB(blockPos), entity -> entity.canEquipWithDispenser(item));
@@ -26,13 +_,42 @@
} else {
LivingEntity livingEntity = entitiesOfClass.getFirst();
EquipmentSlot equipmentSlotForItem = livingEntity.getEquipmentSlotForItem(item);
- ItemStack itemStack = item.split(1);
- livingEntity.setItemSlot(equipmentSlotForItem, itemStack);
+ ItemStack itemStack = item.copyWithCount(1); // Paper - shrink below and single item in event
+ // CraftBukkit start
+ net.minecraft.world.level.Level world = blockSource.level();
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack);
+
+ org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) livingEntity.getBukkitEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return false;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseItemBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseItemBehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || dispenseItemBehavior != currentBehavior)) { // Paper - fix possible StackOverflowError
+ dispenseItemBehavior.dispense(blockSource, eventStack);
+ return true;
+ }
+ }
+
+ livingEntity.setItemSlot(equipmentSlotForItem, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()));
+ // CraftBukkit end
if (livingEntity instanceof Mob mob) {
mob.setDropChance(equipmentSlotForItem, 2.0F);
mob.setPersistenceRequired();
}
+ if (shrink) item.shrink(1); // Paper - shrink here
return true;
}
}

View file

@ -0,0 +1,46 @@
--- a/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
@@ -57,12 +_,38 @@
}
Vec3 vec31 = new Vec3(d, d1 + d3, d2);
- AbstractMinecart abstractMinecart = AbstractMinecart.createMinecart(
- serverLevel, vec31.x, vec31.y, vec31.z, this.entityType, EntitySpawnReason.DISPENSER, item, null
- );
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block2 = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(vec31.x, vec31.y, vec31.z));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseItemBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseItemBehavior != DispenseItemBehavior.NOOP && dispenseItemBehavior != this) {
+ dispenseItemBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+
+ itemstack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ AbstractMinecart abstractMinecart = AbstractMinecart.createMinecart(serverLevel, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.entityType, EntitySpawnReason.DISPENSER, itemstack1, null);
+
if (abstractMinecart != null) {
- serverLevel.addFreshEntity(abstractMinecart);
- item.shrink(1);
+ if (serverLevel.addFreshEntity(abstractMinecart) && shrink) item.shrink(1); // Paper - if entity add was successful and supposed to shrink
+ // CraftBukkit end
}
return item;

View file

@ -0,0 +1,52 @@
--- a/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
+++ b/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
@@ -27,16 +_,39 @@
ServerLevel serverLevel = blockSource.level();
Direction direction = blockSource.state().getValue(DispenserBlock.FACING);
Position dispensePosition = this.dispenseConfig.positionFunction().getDispensePosition(blockSource, direction);
- Projectile.spawnProjectileUsingShoot(
- this.projectileItem.asProjectile(serverLevel, dispensePosition, item, direction),
- serverLevel,
- item,
- direction.getStepX(),
- direction.getStepY(),
- direction.getStepZ(),
- this.dispenseConfig.power(),
- this.dispenseConfig.uncertainty()
- );
+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1);
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) direction.getStepX(), (double) direction.getStepY(), (double) direction.getStepZ()));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // item.grow(1); // Paper - shrink below
+ return item;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+
+ // SPIGOT-7923: Avoid create projectiles with empty item
+ if (!itemstack1.isEmpty()) {
+ Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(serverLevel, dispensePosition, org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()), direction), serverLevel, itemstack1, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies
+ iprojectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(blockSource.blockEntity());
+ }
+ if (shrink) item.shrink(1); // Paper - actually handle here
+ // CraftBukkit end
item.shrink(1);
return item;
}

View file

@ -0,0 +1,57 @@
--- a/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
@@ -20,9 +_,32 @@
@Override
protected ItemStack execute(BlockSource blockSource, ItemStack item) {
ServerLevel serverLevel = blockSource.level();
+
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item); // Paper - ignore stack size on damageable items
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ serverLevel.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
if (!serverLevel.isClientSide()) {
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
- this.setSuccess(tryShearBeehive(serverLevel, blockPos) || tryShearLivingEntity(serverLevel, blockPos, item));
+ this.setSuccess(tryShearBeehive(serverLevel, blockPos) || tryShearLivingEntity(serverLevel, blockPos, item, bukkitBlock, craftItem)); // CraftBukkit
if (this.isSuccess()) {
item.hurtAndBreak(1, serverLevel, null, item1 -> {});
}
@@ -50,10 +_,18 @@
return false;
}
- private static boolean tryShearLivingEntity(ServerLevel level, BlockPos pos, ItemStack stack) {
+ private static boolean tryShearLivingEntity(ServerLevel level, BlockPos pos, ItemStack stack, org.bukkit.block.Block bukkitBlock, org.bukkit.craftbukkit.inventory.CraftItemStack craftItem) { // CraftBukkit - add args
for (LivingEntity livingEntity : level.getEntitiesOfClass(LivingEntity.class, new AABB(pos), EntitySelector.NO_SPECTATORS)) {
if (livingEntity instanceof Shearable shearable && shearable.readyForShearing()) {
- shearable.shear(level, SoundSource.BLOCKS, stack);
+ // CraftBukkit start
+ // Paper start - Add drops to shear events
+ org.bukkit.event.block.BlockShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockShearEntityEvent(livingEntity, bukkitBlock, craftItem, shearable.generateDefaultDrops(level, stack));
+ if (event.isCancelled()) {
+ // Paper end - Add drops to shear events
+ continue;
+ }
+ // CraftBukkit end
+ shearable.shear(level, SoundSource.BLOCKS, stack, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events
level.gameEvent(null, GameEvent.SHEAR, pos);
return true;
}

View file

@ -0,0 +1,42 @@
--- a/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
+++ b/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
@@ -22,10 +_,38 @@
BlockPos blockPos = blockSource.pos().relative(direction);
Direction direction1 = blockSource.level().isEmptyBlock(blockPos.below()) ? direction : Direction.UP;
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(blockSource.level(), blockSource.pos());
+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(item.copyWithCount(1)); // Paper - single item in event
+
+ org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
+ if (!DispenserBlock.eventFired) {
+ blockSource.level().getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return item;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) {
+ dispenseBehavior.dispense(blockSource, eventStack);
+ return item;
+ }
+ }
+ // CraftBukkit end
try {
+ // Paper start - track changed items in the dispense event
this.setSuccess(
- ((BlockItem)item1).place(new DirectionalPlaceContext(blockSource.level(), blockPos, direction, item, direction1)).consumesAction()
+ ((BlockItem) item1).place(new DirectionalPlaceContext(blockSource.level(), blockPos, direction, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), direction1)).consumesAction()
);
+ if (this.isSuccess()) {
+ item.shrink(1); // vanilla shrink is in the place function above, manually handle it here
+ }
+ // Paper end - track changed items in the dispense event
} catch (Exception var8) {
LOGGER.error("Error trying to place shulker box at {}", blockPos, var8);
}

View file

@ -1,58 +0,0 @@
--- a/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
@@ -11,6 +11,11 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
@@ -43,14 +48,40 @@
d4 = 0.0D;
}
+ // CraftBukkit start
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
AbstractBoat abstractboat = (AbstractBoat) this.type.create(worldserver, EntitySpawnReason.DISPENSER);
if (abstractboat != null) {
- abstractboat.setInitialPos(d1, d2 + d4, d3);
+ abstractboat.setInitialPos(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); // CraftBukkit
EntityType.createDefaultStackConfig(worldserver, stack, (Player) null).accept(abstractboat);
abstractboat.setYRot(enumdirection.toYRot());
- worldserver.addFreshEntity(abstractboat);
- stack.shrink(1);
+ if (worldserver.addFreshEntity(abstractboat) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
}
return stack;

View file

@ -1,126 +0,0 @@
--- a/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java
@@ -6,47 +6,114 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.DispenserBlock;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.util.CraftVector;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
public class DefaultDispenseItemBehavior implements DispenseItemBehavior {
+ private Direction enumdirection; // Paper - cache facing direction
private static final int DEFAULT_ACCURACY = 6;
+ // CraftBukkit start
+ private boolean dropper;
+
+ public DefaultDispenseItemBehavior(boolean dropper) {
+ this.dropper = dropper;
+ }
+ // CraftBukkit end
+
public DefaultDispenseItemBehavior() {}
@Override
public final ItemStack dispense(BlockSource pointer, ItemStack stack) {
+ enumdirection = pointer.state().getValue(DispenserBlock.FACING); // Paper - cache facing direction
ItemStack itemstack1 = this.execute(pointer, stack);
this.playSound(pointer);
- this.playAnimation(pointer, (Direction) pointer.state().getValue(DispenserBlock.FACING));
+ this.playAnimation(pointer, enumdirection); // Paper - cache facing direction
return itemstack1;
}
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
- Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
+ // Paper - cached enum direction
Position iposition = DispenserBlock.getDispensePosition(pointer);
ItemStack itemstack1 = stack.split(1);
- DefaultDispenseItemBehavior.spawnItem(pointer.level(), itemstack1, 6, enumdirection, iposition);
+ // CraftBukkit start
+ if (!DefaultDispenseItemBehavior.spawnItem(pointer.level(), itemstack1, 6, enumdirection, pointer, this.dropper)) {
+ stack.grow(1);
+ }
+ // CraftBukkit end
return stack;
}
public static void spawnItem(Level world, ItemStack stack, int speed, Direction side, Position pos) {
- double d0 = pos.x();
- double d1 = pos.y();
- double d2 = pos.z();
+ // CraftBukkit start
+ ItemEntity entityitem = DefaultDispenseItemBehavior.prepareItem(world, stack, speed, side, pos);
+ world.addFreshEntity(entityitem);
+ }
- if (side.getAxis() == Direction.Axis.Y) {
+ private static ItemEntity prepareItem(Level world, ItemStack itemstack, int i, Direction enumdirection, Position iposition) {
+ // CraftBukkit end
+ double d0 = iposition.x();
+ double d1 = iposition.y();
+ double d2 = iposition.z();
+
+ if (enumdirection.getAxis() == Direction.Axis.Y) {
d1 -= 0.125D;
} else {
d1 -= 0.15625D;
}
- ItemEntity entityitem = new ItemEntity(world, d0, d1, d2, stack);
+ ItemEntity entityitem = new ItemEntity(world, d0, d1, d2, itemstack);
double d3 = world.random.nextDouble() * 0.1D + 0.2D;
- entityitem.setDeltaMovement(world.random.triangle((double) side.getStepX() * d3, 0.0172275D * (double) speed), world.random.triangle(0.2D, 0.0172275D * (double) speed), world.random.triangle((double) side.getStepZ() * d3, 0.0172275D * (double) speed));
+ entityitem.setDeltaMovement(world.random.triangle((double) enumdirection.getStepX() * d3, 0.0172275D * (double) i), world.random.triangle(0.2D, 0.0172275D * (double) i), world.random.triangle((double) enumdirection.getStepZ() * d3, 0.0172275D * (double) i));
+ // CraftBukkit start
+ return entityitem;
+ }
+
+ // CraftBukkit - void -> boolean return, IPosition -> ISourceBlock last argument, dropper
+ public static boolean spawnItem(Level world, ItemStack itemstack, int i, Direction enumdirection, BlockSource sourceblock, boolean dropper) {
+ if (itemstack.isEmpty()) return true;
+ Position iposition = DispenserBlock.getDispensePosition(sourceblock);
+ ItemEntity entityitem = DefaultDispenseItemBehavior.prepareItem(world, itemstack, i, enumdirection, iposition);
+
+ org.bukkit.block.Block block = CraftBlock.at(world, sourceblock.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), CraftVector.toBukkit(entityitem.getDeltaMovement()));
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ entityitem.setItem(CraftItemStack.asNMSCopy(event.getItem()));
+ entityitem.setDeltaMovement(CraftVector.toNMS(event.getVelocity()));
+
+ if (!dropper && !event.getItem().getType().equals(craftItem.getType())) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(sourceblock, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior.getClass() != DefaultDispenseItemBehavior.class) {
+ idispensebehavior.dispense(sourceblock, eventStack);
+ } else {
+ world.addFreshEntity(entityitem);
+ }
+ return false;
+ }
+
world.addFreshEntity(entityitem);
+
+ return true;
+ // CraftBukkit end
}
protected void playSound(BlockSource pointer) {

View file

@ -1,651 +0,0 @@
--- a/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -28,6 +28,7 @@
import net.minecraft.world.entity.item.PrimedTnt;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BoneMealItem;
+import net.minecraft.world.item.BucketItem;
import net.minecraft.world.item.DispensibleContainerItem;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.HoneycombItem;
@@ -47,7 +48,9 @@
import net.minecraft.world.level.block.CandleCakeBlock;
import net.minecraft.world.level.block.CarvedPumpkinBlock;
import net.minecraft.world.level.block.DispenserBlock;
+import net.minecraft.world.level.block.LiquidBlockContainer;
import net.minecraft.world.level.block.RespawnAnchorBlock;
+import net.minecraft.world.level.block.SaplingBlock;
import net.minecraft.world.level.block.ShulkerBoxBlock;
import net.minecraft.world.level.block.SkullBlock;
import net.minecraft.world.level.block.TntBlock;
@@ -62,6 +65,17 @@
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import org.slf4j.Logger;
+import org.bukkit.Location;
+import org.bukkit.TreeType;
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.util.CraftLocation;
+import org.bukkit.craftbukkit.util.DummyGeneratorAccess;
+import org.bukkit.event.block.BlockDispenseArmorEvent;
+import org.bukkit.event.block.BlockDispenseEvent;
+import org.bukkit.event.block.BlockFertilizeEvent;
+import org.bukkit.event.world.StructureGrowEvent;
+// CraftBukkit end
public interface DispenseItemBehavior {
@@ -90,14 +104,47 @@
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
EntityType<?> entitytypes = ((SpawnEggItem) stack.getItem()).getType(pointer.level().registryAccess(), stack);
+ // CraftBukkit start
+ ServerLevel worldserver = pointer.level();
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ // Paper start - track changed items in the dispense event
+ itemstack1 = CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified
+ entitytypes = ((SpawnEggItem) itemstack1.getItem()).getType(worldserver.registryAccess(), itemstack1);
+ // Paper end - track changed item from dispense event
+ }
+
try {
- entitytypes.spawn(pointer.level(), stack, (Player) null, pointer.pos().relative(enumdirection), EntitySpawnReason.DISPENSER, enumdirection != Direction.UP, false);
+ entitytypes.spawn(pointer.level(), itemstack1, (Player) null, pointer.pos().relative(enumdirection), EntitySpawnReason.DISPENSER, enumdirection != Direction.UP, false); // Paper - track changed item in dispense event
} catch (Exception exception) {
- null.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", pointer.pos(), exception);
+ DispenseItemBehavior.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", pointer.pos(), exception); // CraftBukkit - decompile error
return ItemStack.EMPTY;
}
- stack.shrink(1);
+ if (shrink) stack.shrink(1); // Paper - actually handle here
+ // CraftBukkit end
pointer.level().gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, pointer.pos());
return stack;
}
@@ -116,13 +163,43 @@
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
BlockPos blockposition = pointer.pos().relative(enumdirection);
ServerLevel worldserver = pointer.level();
+
+ // CraftBukkit start
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
+ final ItemStack newStack = CraftItemStack.unwrap(event.getItem()); // Paper - use event itemstack (unwrap is fine here because the stack won't be modified)
Consumer<ArmorStand> consumer = EntityType.appendDefaultStackConfig((entityarmorstand) -> {
entityarmorstand.setYRot(enumdirection.toYRot());
- }, worldserver, stack, (Player) null);
+ }, worldserver, newStack, (Player) null); // Paper - track changed items in the dispense event
ArmorStand entityarmorstand = (ArmorStand) EntityType.ARMOR_STAND.spawn(worldserver, consumer, blockposition, EntitySpawnReason.DISPENSER, false, false);
if (entityarmorstand != null) {
- stack.shrink(1);
+ if (shrink) stack.shrink(1); // Paper - actually handle here
}
return stack;
@@ -141,7 +218,36 @@
});
if (!list.isEmpty()) {
- ((Saddleable) list.get(0)).equipSaddle(stack.split(1), SoundSource.BLOCKS);
+ // CraftBukkit start
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
+ ServerLevel world = pointer.level();
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) list.get(0).getBukkitEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ ((Saddleable) list.get(0)).equipSaddle(CraftItemStack.asNMSCopy(event.getItem()), SoundSource.BLOCKS); // Paper - track changed items in dispense event
+ // CraftBukkit end
+ if (shrink) stack.shrink(1); // Paper - actually handle here
this.setSuccess(true);
return stack;
} else {
@@ -166,9 +272,38 @@
}
entityhorsechestedabstract = (AbstractChestedHorse) iterator1.next();
- } while (!entityhorsechestedabstract.isTamed() || !entityhorsechestedabstract.getSlot(499).set(stack));
+ // CraftBukkit start
+ } while (!entityhorsechestedabstract.isTamed());
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below
+ ServerLevel world = pointer.level();
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
- stack.shrink(1);
+ BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityhorsechestedabstract.getBukkitEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below (this was actually missing and should be here, added it commented out to be consistent)
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ entityhorsechestedabstract.getSlot(499).set(CraftItemStack.asNMSCopy(event.getItem()));
+ // CraftBukkit end
+
+ if (shrink) stack.shrink(1); // Paper - actually handle here
this.setSuccess(true);
return stack;
}
@@ -202,8 +337,50 @@
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
ServerLevel worldserver = pointer.level();
+ // CraftBukkit start
+ int x = blockposition.getX();
+ int y = blockposition.getY();
+ int z = blockposition.getZ();
+ BlockState iblockdata = worldserver.getBlockState(blockposition);
+ ItemStack dispensedItem = stack; // Paper - track changed item from the dispense event
+ // Paper start - correctly check if the bucket place will succeed
+ /* Taken from SolidBucketItem#emptyContents */
+ boolean willEmptyContentsSolidBucketItem = dispensiblecontaineritem instanceof net.minecraft.world.item.SolidBucketItem && worldserver.isInWorldBounds(blockposition) && iblockdata.isAir();
+ /* Taken from BucketItem#emptyContents */
+ boolean willEmptyBucketItem = dispensiblecontaineritem instanceof final BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (iblockdata.isAir() || iblockdata.canBeReplaced(bucketItem.content) || (iblockdata.getBlock() instanceof LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, worldserver, blockposition, iblockdata, bucketItem.content)));
+ if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) {
+ // Paper end - correctly check if the bucket place will succeed
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+
+ // Paper start - track changed item from dispense event
+ dispensedItem = CraftItemStack.unwrap(event.getItem()); // unwrap is safe here as the stack isn't mutated
+ dispensiblecontaineritem = (DispensibleContainerItem) dispensedItem.getItem();
+ // Paper end - track changed item from dispense event
+ }
+ // CraftBukkit end
+
if (dispensiblecontaineritem.emptyContents((Player) null, worldserver, blockposition, (BlockHitResult) null)) {
- dispensiblecontaineritem.checkExtraContent((Player) null, worldserver, stack, blockposition);
+ dispensiblecontaineritem.checkExtraContent((Player) null, worldserver, dispensedItem, blockposition); // Paper - track changed item from dispense event
return this.consumeWithRemainder(pointer, stack, new ItemStack(Items.BUCKET));
} else {
return this.defaultDispenseItemBehavior.dispense(pointer, stack);
@@ -229,7 +406,7 @@
Block block = iblockdata.getBlock();
if (block instanceof BucketPickup ifluidsource) {
- ItemStack itemstack1 = ifluidsource.pickupBlock((Player) null, worldserver, blockposition, iblockdata);
+ ItemStack itemstack1 = ifluidsource.pickupBlock((Player) null, DummyGeneratorAccess.INSTANCE, blockposition, iblockdata); // CraftBukkit
if (itemstack1.isEmpty()) {
return super.execute(pointer, stack);
@@ -237,6 +414,32 @@
worldserver.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PICKUP, blockposition);
Item item = itemstack1.getItem();
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
+
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+
+ itemstack1 = ifluidsource.pickupBlock((Player) null, worldserver, blockposition, iblockdata); // From above
+ // CraftBukkit end
+
return this.consumeWithRemainder(pointer, stack, new ItemStack(item));
}
} else {
@@ -249,16 +452,44 @@
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
ServerLevel worldserver = pointer.level();
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
+
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
this.setSuccess(true);
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
BlockPos blockposition = pointer.pos().relative(enumdirection);
BlockState iblockdata = worldserver.getBlockState(blockposition);
if (BaseFireBlock.canBePlacedAt(worldserver, blockposition, enumdirection)) {
- worldserver.setBlockAndUpdate(blockposition, BaseFireBlock.getState(worldserver, blockposition));
- worldserver.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_PLACE, blockposition);
+ // CraftBukkit start - Ignition by dispensing flint and steel
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(worldserver, blockposition, pointer.pos()).isCancelled()) {
+ worldserver.setBlockAndUpdate(blockposition, BaseFireBlock.getState(worldserver, blockposition));
+ worldserver.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_PLACE, blockposition);
+ }
+ // CraftBukkit end
} else if (!CampfireBlock.canLight(iblockdata) && !CandleBlock.canLight(iblockdata) && !CandleCakeBlock.canLight(iblockdata)) {
- if (iblockdata.getBlock() instanceof TntBlock) {
+ if (iblockdata.getBlock() instanceof TntBlock && org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(worldserver, blockposition, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.DISPENSER, null, pointer.pos())) { // CraftBukkit - TNTPrimeEvent
TntBlock.explode(worldserver, blockposition);
worldserver.removeBlock(blockposition, false);
} else {
@@ -283,13 +514,64 @@
this.setSuccess(true);
ServerLevel worldserver = pointer.level();
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
+ // CraftBukkit start
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+
+ worldserver.captureTreeGeneration = true;
+ // CraftBukkit end
+
if (!BoneMealItem.growCrop(stack, worldserver, blockposition) && !BoneMealItem.growWaterPlant(stack, worldserver, blockposition, (Direction) null)) {
this.setSuccess(false);
} else if (!worldserver.isClientSide) {
worldserver.levelEvent(1505, blockposition, 15);
}
+ // CraftBukkit start
+ worldserver.captureTreeGeneration = false;
+ if (worldserver.capturedBlockStates.size() > 0) {
+ TreeType treeType = SaplingBlock.treeType;
+ SaplingBlock.treeType = null;
+ Location location = CraftLocation.toBukkit(blockposition, worldserver.getWorld());
+ List<org.bukkit.block.BlockState> blocks = new java.util.ArrayList<>(worldserver.capturedBlockStates.values());
+ worldserver.capturedBlockStates.clear();
+ StructureGrowEvent structureEvent = null;
+ if (treeType != null) {
+ structureEvent = new StructureGrowEvent(location, treeType, false, null, blocks);
+ org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
+ }
+ BlockFertilizeEvent fertilizeEvent = new BlockFertilizeEvent(location.getBlock(), null, blocks);
+ fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled());
+ org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent);
+
+ if (!fertilizeEvent.isCancelled()) {
+ for (org.bukkit.block.BlockState blockstate : blocks) {
+ blockstate.update(true);
+ worldserver.checkCapturedTreeStateForObserverNotify(blockposition, (org.bukkit.craftbukkit.block.CraftBlockState) blockstate); // Paper - notify observers even if grow failed
+ }
+ }
+ }
+ // CraftBukkit end
+
return stack;
}
});
@@ -298,12 +580,42 @@
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
ServerLevel worldserver = pointer.level();
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
- PrimedTnt entitytntprimed = new PrimedTnt(worldserver, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (LivingEntity) null);
+ // CraftBukkit start
+ // EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(worldserver, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null);
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink at end and single item in event
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+
+ PrimedTnt entitytntprimed = new PrimedTnt(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (LivingEntity) null);
+ // CraftBukkit end
+
worldserver.addFreshEntity(entitytntprimed);
worldserver.playSound((Player) null, entitytntprimed.getX(), entitytntprimed.getY(), entitytntprimed.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F);
worldserver.gameEvent((Entity) null, (Holder) GameEvent.ENTITY_PLACE, blockposition);
- stack.shrink(1);
+ if (shrink) stack.shrink(1); // Paper - actually handle here
return stack;
}
});
@@ -313,7 +625,31 @@
ServerLevel worldserver = pointer.level();
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
BlockPos blockposition = pointer.pos().relative(enumdirection);
+
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
+
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
if (worldserver.isEmptyBlock(blockposition) && WitherSkullBlock.canSpawnMob(worldserver, blockposition, stack)) {
worldserver.setBlock(blockposition, (BlockState) Blocks.WITHER_SKELETON_SKULL.defaultBlockState().setValue(SkullBlock.ROTATION, RotationSegment.convertToSegment(enumdirection)), 3);
worldserver.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_PLACE, blockposition);
@@ -326,7 +662,7 @@
stack.shrink(1);
this.setSuccess(true);
} else {
- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
}
return stack;
@@ -339,6 +675,30 @@
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
CarvedPumpkinBlock blockpumpkincarved = (CarvedPumpkinBlock) Blocks.CARVED_PUMPKIN;
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
+
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
if (worldserver.isEmptyBlock(blockposition) && blockpumpkincarved.canSpawnGolem(worldserver, blockposition)) {
if (!worldserver.isClientSide) {
worldserver.setBlock(blockposition, blockpumpkincarved.defaultBlockState(), 3);
@@ -348,7 +708,7 @@
stack.shrink(1);
this.setSuccess(true);
} else {
- this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack));
+ this.setSuccess(EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this)); // Paper - fix possible StackOverflowError
}
return stack;
@@ -377,6 +737,30 @@
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
BlockState iblockdata = worldserver.getBlockState(blockposition);
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - only single item in event
+
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
if (iblockdata.is(BlockTags.BEEHIVES, (blockbase_blockdata) -> {
return blockbase_blockdata.hasProperty(BeehiveBlock.HONEY_LEVEL) && blockbase_blockdata.getBlock() instanceof BeehiveBlock;
}) && (Integer) iblockdata.getValue(BeehiveBlock.HONEY_LEVEL) >= 5) {
@@ -402,6 +786,13 @@
this.setSuccess(true);
if (iblockdata.is(Blocks.RESPAWN_ANCHOR)) {
if ((Integer) iblockdata.getValue(RespawnAnchorBlock.CHARGE) != 4) {
+ // Paper start - Call missing BlockDispenseEvent
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(pointer, blockposition, stack, this);
+ if (result != null) {
+ this.setSuccess(false);
+ return result;
+ }
+ // Paper end - Call missing BlockDispenseEvent
RespawnAnchorBlock.charge((Entity) null, worldserver, blockposition, iblockdata);
stack.shrink(1);
} else {
@@ -426,6 +817,31 @@
this.setSuccess(false);
return stack;
} else {
+ // CraftBukkit start
+ ItemStack itemstack1 = stack;
+ ServerLevel world = pointer.level();
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); // Paper - ignore stack size on damageable items
+
+ BlockDispenseEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) list.get(0).getBukkitEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
Iterator iterator1 = list.iterator();
Armadillo armadillo;
@@ -454,6 +870,13 @@
Optional<BlockState> optional = HoneycombItem.getWaxed(iblockdata);
if (optional.isPresent()) {
+ // Paper start - Call missing BlockDispenseEvent
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(pointer, blockposition, stack, this);
+ if (result != null) {
+ this.setSuccess(false);
+ return result;
+ }
+ // Paper end - Call missing BlockDispenseEvent
worldserver.setBlockAndUpdate(blockposition, (BlockState) optional.get());
worldserver.levelEvent(3003, blockposition, 0);
stack.shrink(1);
@@ -481,6 +904,12 @@
if (!worldserver.getBlockState(blockposition1).is(BlockTags.CONVERTABLE_TO_MUD)) {
return this.defaultDispenseItemBehavior.dispense(pointer, stack);
} else {
+ // Paper start - Call missing BlockDispenseEvent
+ ItemStack result = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDispenseEvent(pointer, blockposition1, stack, this);
+ if (result != null) {
+ return result;
+ }
+ // Paper end - Call missing BlockDispenseEvent
if (!worldserver.isClientSide) {
for (int k = 0; k < 5; ++k) {
worldserver.sendParticles(ParticleTypes.SPLASH, (double) blockposition.getX() + worldserver.random.nextDouble(), (double) (blockposition.getY() + 1), (double) blockposition.getZ() + worldserver.random.nextDouble(), 1, 0.0D, 0.0D, 0.0D, 1.0D);

View file

@ -1,82 +0,0 @@
--- a/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java
@@ -7,8 +7,13 @@
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.phys.AABB;
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseArmorEvent;
+// CraftBukkit end
public class EquipmentDispenseItemBehavior extends DefaultDispenseItemBehavior {
@@ -18,10 +23,15 @@
@Override
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
- return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack) ? stack : super.execute(pointer, stack);
+ return EquipmentDispenseItemBehavior.dispenseEquipment(pointer, stack, this) ? stack : super.execute(pointer, stack); // Paper - fix possible StackOverflowError
}
- public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack) {
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
+ public static boolean dispenseEquipment(BlockSource pointer, ItemStack armor) {
+ // Paper start
+ return dispenseEquipment(pointer, armor, null);
+ }
+ public static boolean dispenseEquipment(BlockSource pointer, ItemStack stack, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) {
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
List<LivingEntity> list = pointer.level().getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), (entityliving) -> {
return entityliving.canEquipWithDispenser(stack);
@@ -32,9 +42,37 @@
} else {
LivingEntity entityliving = (LivingEntity) list.getFirst();
EquipmentSlot enumitemslot = entityliving.getEquipmentSlotForItem(stack);
- ItemStack itemstack1 = stack.split(1);
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
- entityliving.setItemSlot(enumitemslot, itemstack1);
+ // CraftBukkit start
+ Level world = pointer.level();
+ org.bukkit.block.Block block = CraftBlock.at(world, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityliving.getBukkitEntity());
+ if (!DispenserBlock.eventFired) {
+ world.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return false;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || idispensebehavior != currentBehavior)) { // Paper - fix possible StackOverflowError
+ idispensebehavior.dispense(pointer, eventStack);
+ return true;
+ }
+ }
+
+ entityliving.setItemSlot(enumitemslot, CraftItemStack.asNMSCopy(event.getItem()));
+ // CraftBukkit end
if (entityliving instanceof Mob) {
Mob entityinsentient = (Mob) entityliving;
@@ -42,6 +80,7 @@
entityinsentient.setPersistenceRequired();
}
+ if (shrink) stack.shrink(1); // Paper - shrink here
return true;
}
}

View file

@ -1,58 +0,0 @@
--- a/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java
@@ -15,6 +15,11 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.RailShape;
import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
public class MinecartDispenseItemBehavior extends DefaultDispenseItemBehavior {
@@ -62,11 +67,40 @@
}
Vec3 vec3d1 = new Vec3(d0, d1 + d3, d2);
- AbstractMinecart entityminecartabstract = AbstractMinecart.createMinecart(worldserver, vec3d1.x, vec3d1.y, vec3d1.z, this.entityType, EntitySpawnReason.DISPENSER, stack, (Player) null);
+ // CraftBukkit start
+ // EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.createMinecart(worldserver, vec3d1.x, vec3d1.y, vec3d1.z, this.entityType, EntitySpawnReason.DISPENSER, itemstack, (EntityHuman) null);
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block2 = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+ BlockDispenseEvent event = new BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(vec3d1.x, vec3d1.y, vec3d1.z));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+
+ itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
+ AbstractMinecart entityminecartabstract = AbstractMinecart.createMinecart(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.entityType, EntitySpawnReason.DISPENSER, itemstack1, (Player) null);
+
if (entityminecartabstract != null) {
- worldserver.addFreshEntity(entityminecartabstract);
- stack.shrink(1);
+ if (worldserver.addFreshEntity(entityminecartabstract) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
+ // CraftBukkit end
}
return stack;

View file

@ -1,58 +0,0 @@
--- a/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
+++ b/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java
@@ -8,6 +8,11 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ProjectileItem;
import net.minecraft.world.level.block.DispenserBlock;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
public class ProjectileDispenseBehavior extends DefaultDispenseItemBehavior {
@@ -31,8 +36,41 @@
Direction enumdirection = (Direction) pointer.state().getValue(DispenserBlock.FACING);
Position iposition = this.dispenseConfig.positionFunction().getDispensePosition(pointer, enumdirection);
- Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, stack, enumdirection), worldserver, stack, (double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty());
- stack.shrink(1);
+ // CraftBukkit start
+ // IProjectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, itemstack, enumdirection), worldserver, itemstack, (double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // CraftBukkit - call when finish the BlockDispenseEvent
+ ItemStack itemstack1 = stack.copyWithCount(1); // Paper - shrink below and single item in event
+ org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
+
+ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumdirection.getStepX(), (double) enumdirection.getStepY(), (double) enumdirection.getStepZ()));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ // stack.grow(1); // Paper - shrink below
+ return stack;
+ }
+
+ boolean shrink = true; // Paper
+ if (!event.getItem().equals(craftItem)) {
+ shrink = false; // Paper - shrink below
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+
+ // SPIGOT-7923: Avoid create projectiles with empty item
+ if (!itemstack1.isEmpty()) {
+ Projectile iprojectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(worldserver, iposition, CraftItemStack.unwrap(event.getItem()), enumdirection), worldserver, itemstack1, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies
+ iprojectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(pointer.blockEntity());
+ }
+ if (shrink) stack.shrink(1); // Paper - actually handle here
+ // CraftBukkit end
return stack;
}

View file

@ -1,81 +0,0 @@
--- a/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
+++ b/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
@@ -22,6 +22,12 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.AABB;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
@@ -30,11 +36,34 @@
@Override
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
ServerLevel worldserver = pointer.level();
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); // Paper - ignore stack size on damageable items
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+ if (!DispenserBlock.eventFired) {
+ worldserver.getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
if (!worldserver.isClientSide()) {
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
- this.setSuccess(ShearsDispenseItemBehavior.tryShearBeehive(worldserver, blockposition) || ShearsDispenseItemBehavior.tryShearLivingEntity(worldserver, blockposition, stack));
+ this.setSuccess(ShearsDispenseItemBehavior.tryShearBeehive(worldserver, blockposition) || ShearsDispenseItemBehavior.tryShearLivingEntity(worldserver, blockposition, stack, bukkitBlock, craftItem)); // CraftBukkit
if (this.isSuccess()) {
stack.hurtAndBreak(1, worldserver, (ServerPlayer) null, (item) -> {
});
@@ -64,8 +93,8 @@
return false;
}
- private static boolean tryShearLivingEntity(ServerLevel world, BlockPos pos, ItemStack shears) {
- List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, new AABB(pos), EntitySelector.NO_SPECTATORS);
+ private static boolean tryShearLivingEntity(ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, org.bukkit.block.Block bukkitBlock, CraftItemStack craftItem) { // CraftBukkit - add args
+ List<LivingEntity> list = worldserver.getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), EntitySelector.NO_SPECTATORS);
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
@@ -73,8 +102,16 @@
if (entityliving instanceof Shearable ishearable) {
if (ishearable.readyForShearing()) {
- ishearable.shear(world, SoundSource.BLOCKS, shears);
- world.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, pos);
+ // CraftBukkit start
+ // Paper start - Add drops to shear events
+ org.bukkit.event.block.BlockShearEntityEvent event = CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem, ishearable.generateDefaultDrops(worldserver, itemstack));
+ if (event.isCancelled()) {
+ // Paper end - Add drops to shear events
+ continue;
+ }
+ // CraftBukkit end
+ ishearable.shear(worldserver, SoundSource.BLOCKS, itemstack, CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events
+ worldserver.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, blockposition);
return true;
}
}

View file

@ -1,54 +0,0 @@
--- a/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
+++ b/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java
@@ -10,6 +10,12 @@
import net.minecraft.world.level.block.DispenserBlock;
import org.slf4j.Logger;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
+
public class ShulkerBoxDispenseBehavior extends OptionalDispenseItemBehavior {
private static final Logger LOGGER = LogUtils.getLogger();
@@ -26,8 +32,37 @@
BlockPos blockposition = pointer.pos().relative(enumdirection);
Direction enumdirection1 = pointer.level().isEmptyBlock(blockposition.below()) ? enumdirection : Direction.UP;
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = CraftBlock.at(pointer.level(), pointer.pos());
+ CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack.copyWithCount(1)); // Paper - single item in event
+
+ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
+ if (!DispenserBlock.eventFired) {
+ pointer.level().getCraftServer().getPluginManager().callEvent(event);
+ }
+
+ if (event.isCancelled()) {
+ return stack;
+ }
+
+ if (!event.getItem().equals(craftItem)) {
+ // Chain to handler for new item
+ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(pointer, eventStack); // Paper - Fix NPE with equippable and items without behavior
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) {
+ idispensebehavior.dispense(pointer, eventStack);
+ return stack;
+ }
+ }
+ // CraftBukkit end
+
try {
- this.setSuccess(((BlockItem) item).place(new DirectionalPlaceContext(pointer.level(), blockposition, enumdirection, stack, enumdirection1)).consumesAction());
+ // Paper start - track changed items in the dispense event
+ this.setSuccess(((BlockItem) item).place(new DirectionalPlaceContext(pointer.level(), blockposition, enumdirection, CraftItemStack.asNMSCopy(event.getItem()), enumdirection1)).consumesAction());
+ if (this.isSuccess()) {
+ stack.shrink(1); // vanilla shrink is in the place function above, manually handle it here
+ }
+ // Paper end - track changed items in the dispense event
} catch (Exception exception) {
ShulkerBoxDispenseBehavior.LOGGER.error("Error trying to place shulker box at {}", blockposition, exception);
}