Add ItemSpawnEntityEvent and expand EntityPlaceEvent

This commit is contained in:
Jake Potrebic 2022-01-20 10:55:19 -08:00
parent 11c39637de
commit 5ef0ea1ea7
No known key found for this signature in database
GPG key ID: ECE0B3C133C016C5
2 changed files with 678 additions and 0 deletions

View file

@ -0,0 +1,304 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 20 Jan 2022 10:54:54 -0800
Subject: [PATCH] Add ItemSpawnEntityEvent and expand EntityPlaceEvent
diff --git a/src/main/java/io/papermc/paper/event/entity/BlockPlaceEntityEvent.java b/src/main/java/io/papermc/paper/event/entity/BlockPlaceEntityEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..7287ebcf3338ca30cc23efa22084cdaefbcad2da
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/entity/BlockPlaceEntityEvent.java
@@ -0,0 +1,48 @@
+package io.papermc.paper.event.entity;
+
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.Dispenser;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Called when a block, like a dispenser, places an
+ * entity. {@link #getPlayer()} will always be null.
+ * @see org.bukkit.event.hanging.HangingPlaceEvent for paintings, item frames, and leashes.
+ * @see org.bukkit.event.entity.EntityPlaceEvent for a player-only version with more context
+ * @see PlaceEntityEvent to listen to both blocks and players placing entities
+ */
+public class BlockPlaceEntityEvent extends PlaceEntityEvent {
+
+ private final Dispenser dispenser;
+
+ @ApiStatus.Internal
+ public BlockPlaceEntityEvent(final @NotNull Entity entity, final @NotNull Block block, final @NotNull BlockFace blockFace, final @NotNull ItemStack spawningStack, final @NotNull Dispenser dispenser) {
+ super(entity, null, block, blockFace, spawningStack);
+ this.dispenser = dispenser;
+ }
+
+ /**
+ * Get the dispenser responsible for placing the entity.
+ *
+ * @return a non-snapshot Dispenser
+ */
+ public @NotNull Dispenser getDispenser() {
+ return this.dispenser;
+ }
+
+ /**
+ * Player will always be null on this event.
+ */
+ @Override
+ @Contract("-> null")
+ public @Nullable Player getPlayer() {
+ return null;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/entity/ItemSpawnEntityEvent.java b/src/main/java/io/papermc/paper/event/entity/ItemSpawnEntityEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..6668171113257f5383904ab145da089600630544
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/entity/ItemSpawnEntityEvent.java
@@ -0,0 +1,96 @@
+package io.papermc.paper.event.entity;
+
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.entity.EntityEvent;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * When an itemstack causes the spawning of an entity. Most event fires are going to
+ * be the through the sub-event {@link org.bukkit.event.entity.EntityPlaceEvent} but this
+ * event will also be fired for mob spawn eggs from players and dispensers.
+ */
+public class ItemSpawnEntityEvent extends EntityEvent implements Cancellable {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ private final Player player;
+ private final Block block;
+ private final BlockFace blockFace;
+ private final ItemStack spawningStack;
+ private boolean cancelled;
+
+ @ApiStatus.Internal
+ public ItemSpawnEntityEvent(final @NotNull Entity entity, final @Nullable Player player, final @NotNull Block block, final @NotNull BlockFace blockFace, final @NotNull ItemStack spawningStack) {
+ super(entity);
+ this.player = player;
+ this.block = block;
+ this.blockFace = blockFace;
+ this.spawningStack = spawningStack;
+ }
+
+ /**
+ * Returns the player placing the entity (if one is available).
+ *
+ * @return the player placing the entity
+ */
+ public @Nullable Player getPlayer() {
+ return this.player;
+ }
+
+ /**
+ * Returns the block that the entity was placed on
+ *
+ * @return the block that the entity was placed on
+ */
+ public @NotNull Block getBlock() {
+ return this.block;
+ }
+
+ /**
+ * Returns the face of the block that the entity was placed on
+ *
+ * @return the face of the block that the entity was placed on
+ */
+ public @NotNull BlockFace getBlockFace() {
+ return this.blockFace;
+ }
+
+ /**
+ * Gets the itemstack responsible for spawning the entity. Mutating
+ * this itemstack has no effect.
+ * <p>
+ * May return an empty itemstack if the actual stack isn't available.
+ *
+ * @return the spawning itemstack
+ */
+ public @NotNull ItemStack getSpawningStack() {
+ return this.spawningStack;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public void setCancelled(final boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static @NotNull HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/entity/PlaceEntityEvent.java b/src/main/java/io/papermc/paper/event/entity/PlaceEntityEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfd7204c71a1ca46df3e5174aef3fe33e59b4ee6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/entity/PlaceEntityEvent.java
@@ -0,0 +1,28 @@
+package io.papermc.paper.event.entity;
+
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Triggered when an entity is created in the world by "placing" an item
+ * on a block from a player or dispenser.
+ * <br>
+ * Note that this event is currently only fired for these specific placements:
+ * armor stands, boats, minecarts, end crystals, mob buckets, and tnt (dispenser only).
+ * @see org.bukkit.event.hanging.HangingPlaceEvent for paintings, item frames, and leashes.
+ * @see org.bukkit.event.entity.EntityPlaceEvent for a player-only version with more context
+ * @see BlockPlaceEntityEvent for a dispener-only version with more context
+ */
+public abstract class PlaceEntityEvent extends ItemSpawnEntityEvent {
+
+ @ApiStatus.Internal
+ protected PlaceEntityEvent(final @NotNull Entity entity, final @Nullable Player player, final @NotNull Block block, final @NotNull BlockFace blockFace, final @NotNull ItemStack spawningStack) {
+ super(entity, player, block, blockFace, spawningStack);
+ }
+}
diff --git a/src/main/java/org/bukkit/event/entity/EntityPlaceEvent.java b/src/main/java/org/bukkit/event/entity/EntityPlaceEvent.java
index 71d664dd89995f088c47d17b38547d530319470c..645ef8dde4acce134531cb94344d9d84a8ffdc6a 100644
--- a/src/main/java/org/bukkit/event/entity/EntityPlaceEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityPlaceEvent.java
@@ -14,61 +14,34 @@ import org.jetbrains.annotations.Nullable;
* Triggered when an entity is created in the world by a player "placing" an item
* on a block.
* <br>
- * Note that this event is currently only fired for four specific placements:
- * armor stands, boats, minecarts, and end crystals.
+ * Note that this event is currently only fired for these specific placements:
+ * armor stands, boats, minecarts, end crystals, and mob buckets.
+ * @see org.bukkit.event.hanging.HangingPlaceEvent for paintings, item frames, and leashes.
+ * @see io.papermc.paper.event.entity.BlockPlaceEntityEvent for a dispenser-only version
+ * @see io.papermc.paper.event.entity.PlaceEntityEvent to listen to both blocks and players placing entities
*/
-public class EntityPlaceEvent extends EntityEvent implements Cancellable {
+public class EntityPlaceEvent extends io.papermc.paper.event.entity.PlaceEntityEvent implements Cancellable { // Paper - move to superclass
- private static final HandlerList handlers = new HandlerList();
- private boolean cancelled;
- private final Player player;
- private final Block block;
- private final BlockFace blockFace;
+ // Paper - move to superclass
private final EquipmentSlot hand;
- public EntityPlaceEvent(@NotNull final Entity entity, @Nullable final Player player, @NotNull final Block block, @NotNull final BlockFace blockFace, @NotNull final EquipmentSlot hand) {
- super(entity);
- this.player = player;
- this.block = block;
- this.blockFace = blockFace;
- this.hand = hand;
- }
-
+ // Paper start - move event to superclass
@Deprecated
public EntityPlaceEvent(@NotNull final Entity entity, @Nullable final Player player, @NotNull final Block block, @NotNull final BlockFace blockFace) {
this(entity, player, block, blockFace, EquipmentSlot.HAND);
}
- /**
- * Returns the player placing the entity
- *
- * @return the player placing the entity
- */
- @Nullable
- public Player getPlayer() {
- return player;
- }
-
- /**
- * Returns the block that the entity was placed on
- *
- * @return the block that the entity was placed on
- */
- @NotNull
- public Block getBlock() {
- return block;
+ @Deprecated
+ public EntityPlaceEvent(@NotNull final Entity entity, @Nullable final Player player, @NotNull final Block block, @NotNull final BlockFace blockFace, @NotNull final EquipmentSlot hand) {
+ this(entity, player, block, blockFace, hand, org.bukkit.inventory.ItemStack.empty());
}
- /**
- * Returns the face of the block that the entity was placed on
- *
- * @return the face of the block that the entity was placed on
- */
- @NotNull
- public BlockFace getBlockFace() {
- return blockFace;
+ @org.jetbrains.annotations.ApiStatus.Internal
+ public EntityPlaceEvent(final @NotNull Entity entity, final @Nullable Player player, final @NotNull Block block, final @NotNull BlockFace blockFace, final @NotNull EquipmentSlot hand, final @NotNull org.bukkit.inventory.ItemStack spawningStack) {
+ super(entity, player, block, blockFace, spawningStack);
+ this.hand = hand;
}
-
+ // Paper end
/**
* Get the hand used to place the entity.
*
@@ -79,24 +52,5 @@ public class EntityPlaceEvent extends EntityEvent implements Cancellable {
return hand;
}
- @Override
- public boolean isCancelled() {
- return cancelled;
- }
-
- @Override
- public void setCancelled(boolean cancel) {
- this.cancelled = cancel;
- }
-
- @NotNull
- @Override
- public HandlerList getHandlers() {
- return handlers;
- }
-
- @NotNull
- public static HandlerList getHandlerList() {
- return handlers;
- }
+ // Paper - move to superclass
}

View file

@ -0,0 +1,374 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 20 Jan 2022 10:54:45 -0800
Subject: [PATCH] Add ItemSpawnEntityEvent and expand EntityPlaceEvent
diff --git a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
index 6df0db8b4cdab23494ea34236949ece4989110a3..9ebc4e88efcf1ab3130f150a84c3f2c72d18c92c 100644
--- a/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java
@@ -90,6 +90,11 @@ public class BoatDispenseItemBehavior extends DefaultDispenseItemBehavior {
EntityType.createDefaultStackConfig(worldserver, stack, (Player) null).accept(object);
((Boat) object).setVariant(this.type);
((Boat) object).setYRot(enumdirection.toYRot());
+ // Paper start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEntityEvent(pointer, object, stack).isCancelled()) {
+ return stack;
+ }
+ // Paper end
if (worldserver.addFreshEntity((Entity) object) && shrink) stack.shrink(1); // Paper - if entity add was successful and supposed to shrink
return stack;
}
diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
index 5dab1e10303177e5a4d97a91ee46ede66f30ae35..f1a1168d1cff8095eb8fdd6ac445ae8a26161692 100644
--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -140,7 +140,13 @@ public interface DispenseItemBehavior {
}
try {
- entitytypes.spawn(pointer.level(), itemstack1, (Player) null, pointer.pos().relative(enumdirection), MobSpawnType.DISPENSER, enumdirection != Direction.UP, false); // Paper - track changed item in dispense event
+ // Paper start
+ entitytypes.spawn(pointer.level(), itemstack1, (Player) null, pointer.pos().relative(enumdirection), MobSpawnType.DISPENSER, enumdirection != Direction.UP, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG, entity -> { // Paper - track changed item in dispense event
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemSpawnEntityEvent(pointer.level(), pointer.pos().relative(enumdirection), enumdirection.getOpposite(), null, entity, stack).isCancelled()) {
+ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ }
+ });
+ // Paper end
} catch (Exception exception) {
DispenseItemBehavior.LOGGER.error("Error while dispensing spawn egg from dispenser at {}", pointer.pos(), exception); // CraftBukkit - decompile error
return ItemStack.EMPTY;
@@ -199,7 +205,16 @@ public interface DispenseItemBehavior {
Consumer<ArmorStand> consumer = EntityType.appendDefaultStackConfig((entityarmorstand) -> {
entityarmorstand.setYRot(enumdirection.toYRot());
}, worldserver, newStack, (Player) null); // Paper - track changed items in the dispense event
+ // Paper start
+ final java.util.concurrent.atomic.AtomicBoolean cancelled = new java.util.concurrent.atomic.AtomicBoolean(false);
+ consumer = consumer.andThen(stand -> {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEntityEvent(pointer, stand, stack).isCancelled()) {
+ cancelled.set(true);
+ }
+ });
+ // Paper end
ArmorStand entityarmorstand = (ArmorStand) EntityType.ARMOR_STAND.spawn(worldserver, consumer, blockposition, MobSpawnType.DISPENSER, false, false);
+ if (cancelled.get()) shrink = false; // Paper
if (entityarmorstand != null) {
if (shrink) stack.shrink(1); // Paper - actually handle here
@@ -456,7 +471,7 @@ public interface DispenseItemBehavior {
// CraftBukkit end
if (dispensiblecontaineritem.emptyContents((Player) null, worldserver, blockposition, (BlockHitResult) null)) {
- dispensiblecontaineritem.checkExtraContent((Player) null, worldserver, dispensedItem, blockposition); // Paper - track changed item from dispense event
+ dispensiblecontaineritem.checkExtraContent((Player) null, worldserver, dispensedItem, blockposition, null, pointer.state().getValue(DispenserBlock.FACING).getOpposite()); // Paper - track changed item from dispense event
// CraftBukkit start - Handle stacked buckets
Item item = Items.BUCKET;
stack.shrink(1);
@@ -708,6 +723,11 @@ public interface DispenseItemBehavior {
PrimedTnt entitytntprimed = new PrimedTnt(worldserver, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (LivingEntity) null);
// CraftBukkit end
+ // Paper start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEntityEvent(pointer, entitytntprimed, stack).isCancelled()) {
+ return stack;
+ }
+ // Paper end
worldserver.addFreshEntity(entitytntprimed);
worldserver.playSound((Player) null, entitytntprimed.getX(), entitytntprimed.getY(), entitytntprimed.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F);
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index a46bf73c608641bf1f00fd55242de71a0f2ee06e..2589b77aee2ffcd2d743d28c3c222f2fcd5798c5 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -371,6 +371,13 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
@Nullable
public T spawn(ServerLevel worldserver, @Nullable ItemStack itemstack, @Nullable Player entityhuman, BlockPos blockposition, MobSpawnType enummobspawn, boolean flag, boolean flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
+ // Paper start
+ return this.spawn(worldserver, itemstack, entityhuman, blockposition, enummobspawn, flag, flag1, spawnReason, null);
+ }
+
+ @Nullable
+ public T spawn(ServerLevel worldserver, @Nullable ItemStack itemstack, @Nullable Player entityhuman, BlockPos blockposition, MobSpawnType enummobspawn, boolean flag, boolean flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason, @Nullable final Consumer<T> op) {
+ // Paper end
// CraftBukkit end
Consumer<T> consumer; // CraftBukkit - decompile error
@@ -380,6 +387,7 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
consumer = (entity) -> {
};
}
+ if (op != null) consumer = consumer.andThen(op); // Paper
return this.spawn(worldserver, consumer, blockposition, enummobspawn, flag, flag1, spawnReason); // CraftBukkit
}
@@ -443,6 +451,7 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
T t0 = this.create(worldserver, consumer, blockposition, enummobspawn, flag, flag1);
if (t0 != null) {
+ if (t0.isRemoved()) return null; // Paper - if consumer removed entity, return null
worldserver.addFreshEntityWithPassengers(t0, spawnReason);
return !t0.isRemoved() ? t0 : null; // Don't return an entity when CreatureSpawnEvent is canceled
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/item/BoatItem.java b/src/main/java/net/minecraft/world/item/BoatItem.java
index eb74d45ad458b80cf8455297c3bc550186adaea3..2bc6df3255b34bfc6d3bb8c9bd9e2bd70af1b049 100644
--- a/src/main/java/net/minecraft/world/item/BoatItem.java
+++ b/src/main/java/net/minecraft/world/item/BoatItem.java
@@ -77,7 +77,7 @@ public class BoatItem extends Item {
} else {
if (!world.isClientSide) {
// CraftBukkit start
- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(world, movingobjectpositionblock.getBlockPos(), movingobjectpositionblock.getDirection(), user, entityboat, hand).isCancelled()) {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(world, movingobjectpositionblock.getBlockPos(), movingobjectpositionblock.getDirection(), user, entityboat, hand, itemstack).isCancelled()) { // Paper
return InteractionResultHolder.fail(itemstack);
}
diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java
index 49557d6f22c5725c663a231deab019d4f6fe95fa..d0d6a4525825d30fffce2efa86e77e8492109fea 100644
--- a/src/main/java/net/minecraft/world/item/BucketItem.java
+++ b/src/main/java/net/minecraft/world/item/BucketItem.java
@@ -107,7 +107,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
BlockPos blockposition2 = iblockdata.getBlock() instanceof LiquidBlockContainer && this.content == Fluids.WATER ? blockposition : blockposition1;
if (this.emptyContents(user, world, blockposition2, movingobjectpositionblock, movingobjectpositionblock.getDirection(), blockposition, itemstack, hand)) { // CraftBukkit
- this.checkExtraContent(user, world, itemstack, blockposition2);
+ this.checkExtraContent(user, world, itemstack, blockposition2, hand, movingobjectpositionblock.getDirection()); // Paper
if (user instanceof ServerPlayer) {
CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer) user, blockposition2, itemstack);
}
@@ -135,8 +135,8 @@ public class BucketItem extends Item implements DispensibleContainerItem {
return !player.hasInfiniteMaterials() ? new ItemStack(Items.BUCKET) : stack;
}
- @Override
- public void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos) {}
+ // @Override // Paper - comment out
+ // public void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos) {}
@Override
public boolean emptyContents(@Nullable Player player, Level world, BlockPos pos, @Nullable BlockHitResult hitResult) {
diff --git a/src/main/java/net/minecraft/world/item/DispensibleContainerItem.java b/src/main/java/net/minecraft/world/item/DispensibleContainerItem.java
index 0ca0e2a0a5f63939bd30de22a55806152a5a7698..67f3bc5a88e23bb60e6b300cfa0a7c85fa3ba2d2 100644
--- a/src/main/java/net/minecraft/world/item/DispensibleContainerItem.java
+++ b/src/main/java/net/minecraft/world/item/DispensibleContainerItem.java
@@ -7,8 +7,13 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
public interface DispensibleContainerItem {
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
default void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos) {
}
+ // Paper start
+ default void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos, @Nullable net.minecraft.world.InteractionHand hand, @Nullable net.minecraft.core.Direction direction) {
+ }
+ // Paper end
boolean emptyContents(@Nullable Player player, Level world, BlockPos pos, @Nullable BlockHitResult hitResult);
}
diff --git a/src/main/java/net/minecraft/world/item/MobBucketItem.java b/src/main/java/net/minecraft/world/item/MobBucketItem.java
index dbbc7dd46484b3434ed3ef9bf5ef7ca7774f0d56..ad62f4fdf9aea0fed5c4953215f05fc048440bf8 100644
--- a/src/main/java/net/minecraft/world/item/MobBucketItem.java
+++ b/src/main/java/net/minecraft/world/item/MobBucketItem.java
@@ -35,9 +35,9 @@ public class MobBucketItem extends BucketItem {
}
@Override
- public void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos) {
+ public void checkExtraContent(@Nullable Player player, Level world, ItemStack stack, BlockPos pos, @Nullable net.minecraft.world.InteractionHand hand, @Nullable net.minecraft.core.Direction direction) { // Paper - add parameters
if (world instanceof ServerLevel) {
- this.spawn((ServerLevel)world, stack, pos);
+ this.spawn((ServerLevel)world, stack, pos, player, hand, direction); // Paper - add parameters
world.gameEvent(player, GameEvent.ENTITY_PLACE, pos);
}
}
@@ -47,8 +47,26 @@ public class MobBucketItem extends BucketItem {
world.playSound(player, pos, this.emptySound, SoundSource.NEUTRAL, 1.0F, 1.0F);
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
private void spawn(ServerLevel world, ItemStack stack, BlockPos pos) {
- if (this.type.spawn(world, stack, null, pos, MobSpawnType.BUCKET, true, false) instanceof Bucketable bucketable) {
+ // Paper start
+ this.spawn(world, stack, pos, null, null, null);
+ }
+ private void spawn(ServerLevel world, ItemStack stack, BlockPos pos, @Nullable Player player, @Nullable net.minecraft.world.InteractionHand hand, @Nullable net.minecraft.core.Direction direction) {
+ if (this.type.spawn(world, stack, (Player)null, pos, MobSpawnType.BUCKET, true, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG, entity1 -> {
+ if (direction != null) {
+ if (hand == null) {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEntityEvent(world, pos, direction, entity1, stack).isCancelled()) {
+ entity1.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ }
+ } else {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(world, pos, direction, player, entity1, hand, stack).isCancelled()) {
+ entity1.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ }
+ }
+ }
+ }) instanceof Bucketable bucketable) {
+ // Paper end
CustomData customData = stack.getOrDefault(DataComponents.BUCKET_ENTITY_DATA, CustomData.EMPTY);
bucketable.loadFromBucketTag(customData.copyTag());
bucketable.setFromBucket(true);
diff --git a/src/main/java/net/minecraft/world/item/SpawnEggItem.java b/src/main/java/net/minecraft/world/item/SpawnEggItem.java
index 9cea8da84f39bb3f687139ef213ccea358724dee..a6704f39599164dd38c2a92999b7bb72b6e936b5 100644
--- a/src/main/java/net/minecraft/world/item/SpawnEggItem.java
+++ b/src/main/java/net/minecraft/world/item/SpawnEggItem.java
@@ -66,7 +66,7 @@ public class SpawnEggItem extends Item {
Direction enumdirection = context.getClickedFace();
BlockState iblockdata = world.getBlockState(blockposition);
BlockEntity tileentity = world.getBlockEntity(blockposition);
- EntityType entitytypes;
+ EntityType<?> entitytypes; // Paper - fixme move to mc dev fixes
if (tileentity instanceof Spawner) {
if (world.paperConfig().entities.spawning.disableMobSpawnerSpawnEggTransformation) return InteractionResult.FAIL; // Paper - Allow disabling mob spawner spawn egg transformation
@@ -89,7 +89,13 @@ public class SpawnEggItem extends Item {
}
entitytypes = this.getType(itemstack);
- if (entitytypes.spawn((ServerLevel) world, itemstack, context.getPlayer(), blockposition1, MobSpawnType.SPAWN_EGG, true, !Objects.equals(blockposition, blockposition1) && enumdirection == Direction.UP) != null) {
+ // Paper start
+ if (entitytypes.spawn((ServerLevel) world, itemstack, context.getPlayer(), blockposition1, MobSpawnType.SPAWN_EGG, true, !Objects.equals(blockposition, blockposition1) && enumdirection == Direction.UP, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG, entity -> {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemSpawnEntityEvent(context, entity).isCancelled()) {
+ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ }
+ }) != null) {
+ // Paper end
itemstack.shrink(1);
world.gameEvent((Entity) context.getPlayer(), (Holder) GameEvent.ENTITY_PLACE, blockposition);
}
@@ -115,7 +121,13 @@ public class SpawnEggItem extends Item {
return InteractionResultHolder.pass(itemstack);
} else if (world.mayInteract(user, blockposition) && user.mayUseItemAt(blockposition, movingobjectpositionblock.getDirection(), itemstack)) {
EntityType<?> entitytypes = this.getType(itemstack);
- Entity entity = entitytypes.spawn((ServerLevel) world, itemstack, user, blockposition, MobSpawnType.SPAWN_EGG, false, false);
+ // Paper start
+ Entity entity = entitytypes.spawn((ServerLevel) world, itemstack, user, blockposition, MobSpawnType.SPAWN_EGG, false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG, e -> {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemSpawnEntityEvent(world, movingobjectpositionblock.getBlockPos(), movingobjectpositionblock.getDirection(), user, e, itemstack).isCancelled()) {
+ e.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ }
+ });
+ // Paper end
if (entity == null) {
return InteractionResultHolder.pass(itemstack);
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 34b91eff3190848bae38b20e1d956ece497b1473..61c05182fa73f0b2f2c9d64d6fcc138929277309 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -479,19 +479,56 @@ public class CraftEventFactory {
}
public static EntityPlaceEvent callEntityPlaceEvent(UseOnContext itemactioncontext, Entity entity) {
- return CraftEventFactory.callEntityPlaceEvent(itemactioncontext.getLevel(), itemactioncontext.getClickedPos(), itemactioncontext.getClickedFace(), itemactioncontext.getPlayer(), entity, itemactioncontext.getHand());
+ return CraftEventFactory.callEntityPlaceEvent(itemactioncontext.getLevel(), itemactioncontext.getClickedPos(), itemactioncontext.getClickedFace(), itemactioncontext.getPlayer(), entity, itemactioncontext.getHand(), itemactioncontext.getItemInHand()); // Paper
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - use #callEntityPlaceEvent with spawningStack parameter
public static EntityPlaceEvent callEntityPlaceEvent(Level world, BlockPos clickPosition, Direction clickedFace, net.minecraft.world.entity.player.Player human, Entity entity, InteractionHand enumhand) {
+ // Paper start
+ return CraftEventFactory.callEntityPlaceEvent(world, clickPosition, clickedFace, human, entity, enumhand, ItemStack.EMPTY);
+ }
+
+ public static EntityPlaceEvent callEntityPlaceEvent(final Level world, final BlockPos clickPosition, final Direction clickedFace, @Nullable final net.minecraft.world.entity.player.Player human, final Entity entity, final InteractionHand hand, final ItemStack spawningStack) {
+ // Paper end
Player who = (human == null) ? null : (Player) human.getBukkitEntity();
org.bukkit.block.Block blockClicked = CraftBlock.at(world, clickPosition);
org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(clickedFace);
- EntityPlaceEvent event = new EntityPlaceEvent(entity.getBukkitEntity(), who, blockClicked, blockFace, CraftEquipmentSlot.getHand(enumhand));
+ final EntityPlaceEvent event = new EntityPlaceEvent(entity.getBukkitEntity(), who, blockClicked, blockFace, CraftEquipmentSlot.getHand(hand), spawningStack.asBukkitCopy()); // Paper
entity.level().getCraftServer().getPluginManager().callEvent(event);
return event;
}
+ // Paper start - various spawn entity events
+ public static io.papermc.paper.event.entity.ItemSpawnEntityEvent callItemSpawnEntityEvent(final UseOnContext useOnContext, final Entity entity) {
+ return CraftEventFactory.callItemSpawnEntityEvent(useOnContext.getLevel(), useOnContext.getClickedPos(), useOnContext.getClickedFace(), useOnContext.getPlayer(), entity, useOnContext.getItemInHand());
+ }
+
+ public static io.papermc.paper.event.entity.ItemSpawnEntityEvent callItemSpawnEntityEvent(final Level world, final BlockPos clickPosition, final Direction clickedFace, @Nullable final net.minecraft.world.entity.player.Player human, final Entity entity, final ItemStack spawningStack) {
+ final Player who = (human == null) ? null : (Player) human.getBukkitEntity();
+ final Block blockClicked = CraftBlock.at(world, clickPosition);
+ final BlockFace blockFace = CraftBlock.notchToBlockFace(clickedFace);
+
+ final io.papermc.paper.event.entity.ItemSpawnEntityEvent event = new io.papermc.paper.event.entity.ItemSpawnEntityEvent(entity.getBukkitEntity(), who, blockClicked, blockFace, CraftItemStack.asBukkitCopy(spawningStack));
+ event.callEvent();
+ return event;
+ }
+
+ public static io.papermc.paper.event.entity.BlockPlaceEntityEvent callBlockPlaceEntityEvent(final net.minecraft.core.dispenser.BlockSource pointer, final Entity entity, final ItemStack spawningStack) {
+ final Direction direction = pointer.state().getValue(net.minecraft.world.level.block.DispenserBlock.FACING);
+ return callBlockPlaceEntityEvent(pointer.level(), pointer.pos().relative(direction), direction.getOpposite(), entity, spawningStack);
+ }
+
+ public static io.papermc.paper.event.entity.BlockPlaceEntityEvent callBlockPlaceEntityEvent(final Level world, final BlockPos clickedPosition, final Direction clickedFace, final Entity entity, final ItemStack spawningStack) {
+ final Block blockClicked = CraftBlock.at(world, clickedPosition);
+ final BlockFace blockFace = CraftBlock.notchToBlockFace(clickedFace);
+ final org.bukkit.block.Dispenser dispenser = (org.bukkit.block.Dispenser) CraftBlockStates.getBlockState(CraftBlock.at(world, clickedPosition.relative(clickedFace)));
+
+ final io.papermc.paper.event.entity.BlockPlaceEntityEvent event = new io.papermc.paper.event.entity.BlockPlaceEntityEvent(entity.getBukkitEntity(), blockClicked, blockFace, CraftItemStack.asBukkitCopy(spawningStack), dispenser);
+ event.callEvent();
+ return event;
+ }
+ // Paper end - various spawn entity events
/**
* Bucket methods
diff --git a/src/test/java/io/papermc/paper/block/DispensibleContainerItemExtraContentsOverrideTest.java b/src/test/java/io/papermc/paper/block/DispensibleContainerItemExtraContentsOverrideTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1e599a6802e823f94c7a8506a1cea2486acc54c
--- /dev/null
+++ b/src/test/java/io/papermc/paper/block/DispensibleContainerItemExtraContentsOverrideTest.java
@@ -0,0 +1,50 @@
+package io.papermc.paper.block;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.ClassInfoList;
+import io.github.classgraph.MethodInfo;
+import io.github.classgraph.MethodInfoList;
+import io.github.classgraph.MethodParameterInfo;
+import io.github.classgraph.ScanResult;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import net.minecraft.world.item.DispensibleContainerItem;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class DispensibleContainerItemExtraContentsOverrideTest {
+
+ public static Stream<ClassInfo> parameters() {
+ final List<ClassInfo> classInfo = new ArrayList<>();
+ try (final ScanResult scanResult = new ClassGraph()
+ .enableClassInfo()
+ .enableMethodInfo()
+ .whitelistPackages("net.minecraft")
+ .scan()
+ ) {
+ final ClassInfoList classesImplementing = scanResult.getClassesImplementing(DispensibleContainerItem.class.getName());
+ for (final ClassInfo info : classesImplementing) {
+ if (info.hasDeclaredMethod("checkExtraContent")) {
+ classInfo.add(info);
+ }
+ }
+ }
+ return classInfo.stream();
+ }
+
+ @ParameterizedTest
+ @MethodSource("parameters")
+ public void checkCheckExtraContentOverride(final ClassInfo implementsDispensibleContainerItem) {
+ final MethodInfoList checkExtraContent = implementsDispensibleContainerItem.getDeclaredMethodInfo("checkExtraContent");
+ assertEquals(1, checkExtraContent.size(), implementsDispensibleContainerItem.getName() + " has multiple checkExtraContent methods");
+ final MethodInfo next = checkExtraContent.iterator().next();
+ final MethodParameterInfo[] parameterInfo = next.getParameterInfo();
+ assertEquals(6, parameterInfo.length, implementsDispensibleContainerItem.getName() + " doesn't have 6 params for checkExtraContent");
+ assertEquals("InteractionHand", parameterInfo[parameterInfo.length - 2].getTypeDescriptor().toStringWithSimpleNames(), implementsDispensibleContainerItem.getName() + " needs to change its override of checkExtraContent");
+ assertEquals("Direction", parameterInfo[parameterInfo.length - 1].getTypeDescriptor().toStringWithSimpleNames(), implementsDispensibleContainerItem.getName() + " needs to change its override of checkExtraContent");
+ }
+}