diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch new file mode 100644 index 0000000000..13ffca4845 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch @@ -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.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; diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch new file mode 100644 index 0000000000..fcdecd3dc5 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch @@ -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) { diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch new file mode 100644 index 0000000000..687221251f --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch @@ -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 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 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 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( diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch new file mode 100644 index 0000000000..b3e53ec5b6 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch @@ -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 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; + } + } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch new file mode 100644 index 0000000000..1f891175a1 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch @@ -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; diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch new file mode 100644 index 0000000000..8f98c3092d --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch @@ -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; + } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch new file mode 100644 index 0000000000..fb3b9ff276 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch @@ -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; + } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch new file mode 100644 index 0000000000..56088e9418 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch @@ -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); + } diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch deleted file mode 100644 index a97acf2799..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch +++ /dev/null @@ -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; diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch deleted file mode 100644 index 9a3359fc85..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/DefaultDispenseItemBehavior.java.patch +++ /dev/null @@ -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) { diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch deleted file mode 100644 index 824b11e0d1..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch +++ /dev/null @@ -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 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 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 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); diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch deleted file mode 100644 index 18eb499fba..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/EquipmentDispenseItemBehavior.java.patch +++ /dev/null @@ -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 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; - } - } diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch deleted file mode 100644 index ea22a6dd42..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/MinecartDispenseItemBehavior.java.patch +++ /dev/null @@ -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; diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch deleted file mode 100644 index 2061a38275..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch +++ /dev/null @@ -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; - } - diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch deleted file mode 100644 index bc579690dd..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java.patch +++ /dev/null @@ -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 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 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; - } - } diff --git a/paper-server/patches/unapplied/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch b/paper-server/patches/unapplied/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch deleted file mode 100644 index bfa73fef42..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/core/dispenser/ShulkerBoxDispenseBehavior.java.patch +++ /dev/null @@ -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); - }