net.minecraft.core.dispenser

This commit is contained in:
Jake Potrebic
2024-12-13 13:30:06 -08:00
parent 732cf86b99
commit f252b67a97
16 changed files with 1009 additions and 1168 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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