Move patches to unapplied

This commit is contained in:
Nassim Jahnke
2024-12-12 12:22:12 +01:00
parent ff75689b08
commit 45ddf764d9
821 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
--- a/net/minecraft/core/BlockPos.java
+++ b/net/minecraft/core/BlockPos.java
@@ -158,67 +158,84 @@
@Override
public BlockPos above() {
- return this.relative(Direction.UP);
+ return new BlockPos(this.getX(), this.getY() + 1, this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos above(int distance) {
- return this.relative(Direction.UP, distance);
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() + distance, this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos below() {
- return this.relative(Direction.DOWN);
+ return new BlockPos(this.getX(), this.getY() - 1, this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos below(int i) {
- return this.relative(Direction.DOWN, i);
+ return i == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY() - i, this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos north() {
- return this.relative(Direction.NORTH);
+ return new BlockPos(this.getX(), this.getY(), this.getZ() - 1); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos north(int distance) {
- return this.relative(Direction.NORTH, distance);
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() - distance); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos south() {
- return this.relative(Direction.SOUTH);
+ return new BlockPos(this.getX(), this.getY(), this.getZ() + 1); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos south(int distance) {
- return this.relative(Direction.SOUTH, distance);
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX(), this.getY(), this.getZ() + distance); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos west() {
- return this.relative(Direction.WEST);
+ return new BlockPos(this.getX() - 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos west(int distance) {
- return this.relative(Direction.WEST, distance);
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX() - distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos east() {
- return this.relative(Direction.EAST);
+ return new BlockPos(this.getX() + 1, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos east(int distance) {
- return this.relative(Direction.EAST, distance);
+ return distance == 0 ? this.immutable() : new BlockPos(this.getX() + distance, this.getY(), this.getZ()); // Paper - Perf: Optimize BlockPosition
}
@Override
public BlockPos relative(Direction direction) {
+ // Paper start - Perf: Optimize BlockPosition
+ switch(direction) {
+ case UP:
+ return new BlockPos(this.getX(), this.getY() + 1, this.getZ());
+ case DOWN:
+ return new BlockPos(this.getX(), this.getY() - 1, this.getZ());
+ case NORTH:
+ return new BlockPos(this.getX(), this.getY(), this.getZ() - 1);
+ case SOUTH:
+ return new BlockPos(this.getX(), this.getY(), this.getZ() + 1);
+ case WEST:
+ return new BlockPos(this.getX() - 1, this.getY(), this.getZ());
+ case EAST:
+ return new BlockPos(this.getX() + 1, this.getY(), this.getZ());
+ default:
return new BlockPos(this.getX() + direction.getStepX(), this.getY() + direction.getStepY(), this.getZ() + direction.getStepZ());
+ }
+ // Paper end - Perf: Optimize BlockPosition
}
@Override
@@ -324,9 +341,11 @@
public static Iterable<BlockPos> withinManhattan(BlockPos center, int rangeX, int rangeY, int rangeZ) {
int i = rangeX + rangeY + rangeZ;
- int j = center.getX();
- int k = center.getY();
- int l = center.getZ();
+ // Paper start - rename variables to fix conflict with anonymous class (remap fix)
+ int centerX = center.getX();
+ int centerY = center.getY();
+ int centerZ = center.getZ();
+ // Paper end
return () -> new AbstractIterator<BlockPos>() {
private final BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
private int currentDepth;
@@ -340,7 +359,7 @@
protected BlockPos computeNext() {
if (this.zMirror) {
this.zMirror = false;
- this.cursor.setZ(l - (this.cursor.getZ() - l));
+ this.cursor.setZ(centerZ - (this.cursor.getZ() - centerZ)); // Paper - remap fix
return this.cursor;
} else {
BlockPos blockPos;
@@ -366,7 +385,7 @@
int k = this.currentDepth - Math.abs(i) - Math.abs(j);
if (k <= rangeZ) {
this.zMirror = k != 0;
- blockPos = this.cursor.set(j + i, k + j, l + k);
+ blockPos = this.cursor.set(centerX + i, centerY + j, centerZ + k); // Paper - remap fix
}
}
@@ -444,12 +463,12 @@
if (this.index == l) {
return this.endOfData();
} else {
- int i = this.index % i;
- int j = this.index / i;
- int k = j % j;
- int l = j / j;
+ int offsetX = this.index % i; // Paper - decomp fix
+ int u = this.index / i; // Paper - decomp fix
+ int offsetY = u % j; // Paper - decomp fix
+ int offsetZ = u / j; // Paper - decomp fix
this.index++;
- return this.cursor.set(startX + i, startY + k, startZ + l);
+ return this.cursor.set(startX + offsetX, startY + offsetY, startZ + offsetZ); // Paper - decomp fix
}
}
};
@@ -569,9 +588,9 @@
}
public BlockPos.MutableBlockPos set(int x, int y, int z) {
- this.setX(x);
- this.setY(y);
- this.setZ(z);
+ this.x = x; // Paper - Perf: Manually inline methods in BlockPosition
+ this.y = y; // Paper - Perf: Manually inline methods in BlockPosition
+ this.z = z; // Paper - Perf: Manually inline methods in BlockPosition
return this;
}
@@ -636,19 +655,19 @@
@Override
public BlockPos.MutableBlockPos setX(int i) {
- super.setX(i);
+ this.x = i; // Paper - Perf: Manually inline methods in BlockPosition
return this;
}
@Override
public BlockPos.MutableBlockPos setY(int i) {
- super.setY(i);
+ this.y = i; // Paper - Perf: Manually inline methods in BlockPosition
return this;
}
@Override
public BlockPos.MutableBlockPos setZ(int i) {
- super.setZ(i);
+ this.z = i; // Paper - Perf: Manually inline methods in BlockPosition
return this;
}

View File

@@ -0,0 +1,46 @@
--- a/net/minecraft/core/Direction.java
+++ b/net/minecraft/core/Direction.java
@@ -57,6 +57,12 @@
.sorted(Comparator.comparingInt(direction -> direction.data2d))
.toArray(Direction[]::new);
+ // Paper start - Perf: Inline shift direction fields
+ private final int adjX;
+ private final int adjY;
+ private final int adjZ;
+ // Paper end - Perf: Inline shift direction fields
+
private Direction(
final int id,
final int idOpposite,
@@ -74,6 +80,11 @@
this.axisDirection = direction;
this.normal = vector;
this.normalVec3 = Vec3.atLowerCornerOf(vector);
+ // Paper start - Perf: Inline shift direction fields
+ this.adjX = vector.getX();
+ this.adjY = vector.getY();
+ this.adjZ = vector.getZ();
+ // Paper end - Perf: Inline shift direction fields
}
public static Direction[] orderedByNearest(Entity entity) {
@@ -247,15 +258,15 @@
}
public int getStepX() {
- return this.normal.getX();
+ return this.adjX; // Paper - Perf: Inline shift direction fields
}
public int getStepY() {
- return this.normal.getY();
+ return this.adjY; // Paper - Perf: Inline shift direction fields
}
public int getStepZ() {
- return this.normal.getZ();
+ return this.adjZ; // Paper - Perf: Inline shift direction fields
}
public Vector3f step() {

View File

@@ -0,0 +1,11 @@
--- a/net/minecraft/core/Holder.java
+++ b/net/minecraft/core/Holder.java
@@ -230,7 +230,7 @@
}
void bindTags(Collection<TagKey<T>> tags) {
- this.tags = Set.copyOf(tags);
+ this.tags = java.util.Collections.unmodifiableSet(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(tags)); // Paper
}
@Override

View File

@@ -0,0 +1,33 @@
--- a/net/minecraft/core/MappedRegistry.java
+++ b/net/minecraft/core/MappedRegistry.java
@@ -33,11 +33,11 @@
public class MappedRegistry<T> implements WritableRegistry<T> {
private final ResourceKey<? extends Registry<T>> key;
private final ObjectList<Holder.Reference<T>> byId = new ObjectArrayList<>(256);
- private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1));
- private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>();
- private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>();
- private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>();
- private final Map<ResourceKey<T>, RegistrationInfo> registrationInfos = new IdentityHashMap<>();
+ private final Reference2IntMap<T> toId = Util.make(new Reference2IntOpenHashMap<>(2048), map -> map.defaultReturnValue(-1)); // Paper - Perf: Use bigger expected size to reduce collisions
+ private final Map<ResourceLocation, Holder.Reference<T>> byLocation = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
+ private final Map<ResourceKey<T>, Holder.Reference<T>> byKey = new HashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
+ private final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
+ private final Map<ResourceKey<T>, RegistrationInfo> registrationInfos = new IdentityHashMap<>(2048); // Paper - Perf: Use bigger expected size to reduce collisions
private Lifecycle registryLifecycle;
private final Map<TagKey<T>, HolderSet.Named<T>> frozenTags = new IdentityHashMap<>();
MappedRegistry.TagSet<T> allTags = MappedRegistry.TagSet.unbound();
@@ -508,5 +508,13 @@
void forEach(BiConsumer<? super TagKey<T>, ? super HolderSet.Named<T>> consumer);
Stream<HolderSet.Named<T>> getTags();
+ }
+ // Paper start
+ // used to clear intrusive holders from GameEvent, Item, Block, EntityType, and Fluid from unused instances of those types
+ public void clearIntrusiveHolder(final T instance) {
+ if (this.unregisteredIntrusiveHolders != null) {
+ this.unregisteredIntrusiveHolders.remove(instance);
+ }
}
+ // Paper end
}

View File

@@ -0,0 +1,21 @@
--- a/net/minecraft/core/Rotations.java
+++ b/net/minecraft/core/Rotations.java
@@ -34,6 +34,18 @@
this(serialized.getFloat(0), serialized.getFloat(1), serialized.getFloat(2));
}
+ // Paper start - faster alternative constructor
+ private Rotations(float x, float y, float z, Void dummy_var) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public static Rotations createWithoutValidityChecks(float x, float y, float z) {
+ return new Rotations(x, y, z, null);
+ }
+ // Paper end - faster alternative constructor
+
public ListTag save() {
ListTag listTag = new ListTag();
listTag.add(FloatTag.valueOf(this.x));

View File

@@ -0,0 +1,49 @@
--- a/net/minecraft/core/Vec3i.java
+++ b/net/minecraft/core/Vec3i.java
@@ -16,9 +16,9 @@
vec -> IntStream.of(vec.getX(), vec.getY(), vec.getZ())
);
public static final Vec3i ZERO = new Vec3i(0, 0, 0);
- private int x;
- private int y;
- private int z;
+ protected int x; // Paper - Perf: Manually inline methods in BlockPosition; protected
+ protected int y; // Paper - Perf: Manually inline methods in BlockPosition; protected
+ protected int z; // Paper - Perf: Manually inline methods in BlockPosition; protected
public static Codec<Vec3i> offsetCodec(int maxAbsValue) {
return CODEC.validate(
@@ -35,12 +35,12 @@
}
@Override
- public boolean equals(Object object) {
+ public final boolean equals(Object object) { // Paper - Perf: Final for inline
return this == object || object instanceof Vec3i vec3i && this.getX() == vec3i.getX() && this.getY() == vec3i.getY() && this.getZ() == vec3i.getZ();
}
@Override
- public int hashCode() {
+ public final int hashCode() { // Paper - Perf: Final for inline
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
}
@@ -53,15 +53,15 @@
}
}
- public int getX() {
+ public final int getX() { // Paper - Perf: Final for inline
return this.x;
}
- public int getY() {
+ public final int getY() { // Paper - Perf: Final for inline
return this.y;
}
- public int getZ() {
+ public final int getZ() { // Paper - Perf: Final for inline
return this.z;
}

View File

@@ -0,0 +1,330 @@
--- a/net/minecraft/core/cauldron/CauldronInteraction.java
+++ b/net/minecraft/core/cauldron/CauldronInteraction.java
@@ -35,20 +35,25 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.FluidState;
+// CraftBukkit start
+import org.bukkit.event.block.CauldronLevelChangeEvent;
+// CraftBukkit end
public interface CauldronInteraction {
Map<String, CauldronInteraction.InteractionMap> INTERACTIONS = new Object2ObjectArrayMap();
- Codec<CauldronInteraction.InteractionMap> CODEC;
- CauldronInteraction.InteractionMap EMPTY;
- CauldronInteraction.InteractionMap WATER;
- CauldronInteraction.InteractionMap LAVA;
- CauldronInteraction.InteractionMap POWDER_SNOW;
+ // CraftBukkit start - decompile errors
+ Codec<CauldronInteraction.InteractionMap> CODEC = Codec.stringResolver(CauldronInteraction.InteractionMap::name, CauldronInteraction.INTERACTIONS::get);
+ CauldronInteraction.InteractionMap EMPTY = CauldronInteraction.newInteractionMap("empty");
+ CauldronInteraction.InteractionMap WATER = CauldronInteraction.newInteractionMap("water");
+ CauldronInteraction.InteractionMap LAVA = CauldronInteraction.newInteractionMap("lava");
+ CauldronInteraction.InteractionMap POWDER_SNOW = CauldronInteraction.newInteractionMap("powder_snow");
+ // CraftBukkit end
static CauldronInteraction.InteractionMap newInteractionMap(String name) {
Object2ObjectOpenHashMap<Item, CauldronInteraction> object2objectopenhashmap = new Object2ObjectOpenHashMap();
- object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return InteractionResult.TRY_WITH_EMPTY_HAND;
});
CauldronInteraction.InteractionMap cauldroninteraction_a = new CauldronInteraction.InteractionMap(name, object2objectopenhashmap);
@@ -57,23 +62,28 @@
return cauldroninteraction_a;
}
- InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack);
+ InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection); // Paper - add hitDirection
static void bootStrap() {
Map<Item, CauldronInteraction> map = CauldronInteraction.EMPTY.map();
CauldronInteraction.addDefaultInteractions(map);
- map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
PotionContents potioncontents = (PotionContents) itemstack.get(DataComponents.POTION_CONTENTS);
if (potioncontents != null && potioncontents.is(Potions.WATER)) {
if (!world.isClientSide) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, Blocks.WATER_CAULDRON.defaultBlockState(), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
Item item = itemstack.getItem();
entityhuman.setItemInHand(enumhand, ItemUtils.createFilledResult(itemstack, entityhuman, new ItemStack(Items.GLASS_BOTTLE)));
entityhuman.awardStat(Stats.USE_CAULDRON);
entityhuman.awardStat(Stats.ITEM_USED.get(item));
- world.setBlockAndUpdate(blockposition, Blocks.WATER_CAULDRON.defaultBlockState());
+ // world.setBlockAndUpdate(blockposition, Blocks.WATER_CAULDRON.defaultBlockState()); // CraftBukkit
world.playSound((Player) null, blockposition, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PLACE, blockposition);
}
@@ -86,26 +96,31 @@
Map<Item, CauldronInteraction> map1 = CauldronInteraction.WATER.map();
CauldronInteraction.addDefaultInteractions(map1);
- map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.WATER_BUCKET), (iblockdata1) -> {
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
- }, SoundEvents.BUCKET_FILL);
+ }, SoundEvents.BUCKET_FILL, hitDirection); // Paper - add hitDirection
});
- map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
if (!world.isClientSide) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
Item item = itemstack.getItem();
entityhuman.setItemInHand(enumhand, ItemUtils.createFilledResult(itemstack, entityhuman, PotionContents.createItemStack(Items.POTION, Potions.WATER)));
entityhuman.awardStat(Stats.USE_CAULDRON);
entityhuman.awardStat(Stats.ITEM_USED.get(item));
- LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition);
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
world.playSound((Player) null, blockposition, SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS, 1.0F, 1.0F);
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PICKUP, blockposition);
}
return InteractionResult.SUCCESS;
});
- map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
if ((Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL) == 3) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
@@ -113,10 +128,15 @@
if (potioncontents != null && potioncontents.is(Potions.WATER)) {
if (!world.isClientSide) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata.cycle(LayeredCauldronBlock.LEVEL), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
entityhuman.setItemInHand(enumhand, ItemUtils.createFilledResult(itemstack, entityhuman, new ItemStack(Items.GLASS_BOTTLE)));
entityhuman.awardStat(Stats.USE_CAULDRON);
entityhuman.awardStat(Stats.ITEM_USED.get(itemstack.getItem()));
- world.setBlockAndUpdate(blockposition, (BlockState) iblockdata.cycle(LayeredCauldronBlock.LEVEL));
+ // world.setBlockAndUpdate(blockposition, (IBlockData) iblockdata.cycle(LayeredCauldronBlock.LEVEL)); // CraftBukkit
world.playSound((Player) null, blockposition, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PLACE, blockposition);
}
@@ -167,18 +187,18 @@
map1.put(Items.YELLOW_SHULKER_BOX, CauldronInteraction::shulkerBoxInteraction);
Map<Item, CauldronInteraction> map2 = CauldronInteraction.LAVA.map();
- map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.LAVA_BUCKET), (iblockdata1) -> {
return true;
- }, SoundEvents.BUCKET_FILL_LAVA);
+ }, SoundEvents.BUCKET_FILL_LAVA, hitDirection); // Paper - add hitDirection
});
CauldronInteraction.addDefaultInteractions(map2);
Map<Item, CauldronInteraction> map3 = CauldronInteraction.POWDER_SNOW.map();
- map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.POWDER_SNOW_BUCKET), (iblockdata1) -> {
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
- }, SoundEvents.BUCKET_FILL_POWDER_SNOW);
+ }, SoundEvents.BUCKET_FILL_POWDER_SNOW, hitDirection); // Paper - add hitDirection
});
CauldronInteraction.addDefaultInteractions(map3);
}
@@ -190,16 +210,35 @@
}
static InteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent) {
+ // Paper start - add hitDirection
+ return fillBucket(state, world, pos, player, hand, stack, output, fullPredicate, soundEvent, null); // Paper - add hitDirection
+ }
+ static InteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
+ // Paper end - add hitDirection
if (!fullPredicate.test(state)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!world.isClientSide) {
+ // Paper start - fire PlayerBucketFillEvent
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, output.getItem(), hand);
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketFillEvent
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.CAULDRON.defaultBlockState(), player, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL, false)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
Item item = stack.getItem();
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, output));
player.awardStat(Stats.USE_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(item));
- world.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState());
+ // world.setBlockAndUpdate(blockposition, Blocks.CAULDRON.defaultBlockState()); // CraftBukkit
world.playSound((Player) null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PICKUP, pos);
}
@@ -209,13 +248,33 @@
}
static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent) {
+ // Paper start - add hitDirection
+ return emptyBucket(world, pos, player, hand, stack, state, soundEvent, null);
+ }
+ static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
+ // Paper end - add hitDirection
if (!world.isClientSide) {
+ // Paper start - fire PlayerBucketEmptyEvent
+ ItemStack output = new ItemStack(Items.BUCKET);
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, hand);
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketEmptyEvent
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, state, player, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
Item item = stack.getItem();
- player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.BUCKET)));
+ player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, output)); // Paper
player.awardStat(Stats.FILL_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(item));
- world.setBlockAndUpdate(pos, state);
+ // world.setBlockAndUpdate(blockposition, iblockdata); // CraftBukkit
world.playSound((Player) null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
world.gameEvent((Entity) null, (Holder) GameEvent.FLUID_PLACE, pos);
}
@@ -223,65 +282,79 @@
return InteractionResult.SUCCESS;
}
- private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
- return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY);
+ private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
+ return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection); // Paper - add hitDirection
}
- private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA));
+ private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection
}
- private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW));
+ private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection)); // Paper - add hitDirection
}
- private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
+ private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
Block block = Block.byItem(stack.getItem());
if (!(block instanceof ShulkerBoxBlock)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!world.isClientSide) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, world, pos, player, CauldronLevelChangeEvent.ChangeReason.SHULKER_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
ItemStack itemstack1 = stack.transmuteCopy(Blocks.SHULKER_BOX, 1);
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, itemstack1, false));
player.awardStat(Stats.CLEAN_SHULKER_BOX);
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
}
-
return InteractionResult.SUCCESS;
}
}
- private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
+ private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
BannerPatternLayers bannerpatternlayers = (BannerPatternLayers) stack.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY);
if (bannerpatternlayers.layers().isEmpty()) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!world.isClientSide) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, world, pos, player, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
ItemStack itemstack1 = stack.copyWithCount(1);
itemstack1.set(DataComponents.BANNER_PATTERNS, bannerpatternlayers.removeLast());
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, itemstack1, false));
player.awardStat(Stats.CLEAN_BANNER);
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
}
return InteractionResult.SUCCESS;
}
}
- private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
+ private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
if (!stack.is(ItemTags.DYEABLE)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else if (!stack.has(DataComponents.DYED_COLOR)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!world.isClientSide) {
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.lowerFillLevel(state, world, pos, player, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
stack.remove(DataComponents.DYED_COLOR);
player.awardStat(Stats.CLEAN_ARMOR);
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
+ // LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
}
return InteractionResult.SUCCESS;
@@ -294,8 +367,10 @@
return fluid.is(FluidTags.WATER);
}
+ // CraftBukkit start - decompile errors
+ /*
static {
- Function function = CauldronInteraction.InteractionMap::name;
+ Function function = CauldronInteraction.a::name;
Map map = CauldronInteraction.INTERACTIONS;
Objects.requireNonNull(map);
@@ -305,6 +380,8 @@
LAVA = newInteractionMap("lava");
POWDER_SNOW = newInteractionMap("powder_snow");
}
+ */
+ // CraftBukkit end
public static record InteractionMap(String name, Map<Item, CauldronInteraction> map) {

View File

@@ -0,0 +1,70 @@
--- a/net/minecraft/core/component/DataComponentPatch.java
+++ b/net/minecraft/core/component/DataComponentPatch.java
@@ -61,7 +61,7 @@
}
}
- return reference2objectmap;
+ return (Reference2ObjectMap) reference2objectmap; // CraftBukkit - decompile error
});
public static final StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch> STREAM_CODEC = new StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch>() {
public DataComponentPatch decode(RegistryFriendlyByteBuf registryfriendlybytebuf) {
@@ -144,7 +144,13 @@
}
private static <T> void encodeComponent(RegistryFriendlyByteBuf buf, DataComponentType<T> type, Object value) {
- type.streamCodec().encode(buf, value);
+ // Paper start - codec errors of random anonymous classes are useless
+ try {
+ type.streamCodec().encode(buf, (T) value); // CraftBukkit - decompile error
+ } catch (final Exception e) {
+ throw new RuntimeException("Error encoding component " + type, e);
+ }
+ // Paper end - codec errors of random anonymous classes are useless
}
};
private static final String REMOVED_PREFIX = "!";
@@ -270,7 +276,43 @@
private final Reference2ObjectMap<DataComponentType<?>, Optional<?>> map = new Reference2ObjectArrayMap();
Builder() {}
+
+ // CraftBukkit start
+ public void copy(DataComponentPatch orig) {
+ this.map.putAll(orig.map);
+ }
+
+ public void clear(DataComponentType<?> type) {
+ this.map.remove(type);
+ }
+
+ public boolean isSet(DataComponentType<?> type) {
+ return this.map.containsKey(type);
+ }
+
+ public boolean isEmpty() {
+ return this.map.isEmpty();
+ }
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if (object instanceof DataComponentPatch.Builder patch) {
+ return this.map.equals(patch.map);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.map.hashCode();
+ }
+ // CraftBukkit end
+
public <T> DataComponentPatch.Builder set(DataComponentType<T> type, T value) {
this.map.put(type, Optional.of(value));
return this;

View File

@@ -0,0 +1,24 @@
--- a/net/minecraft/core/component/DataComponents.java
+++ b/net/minecraft/core/component/DataComponents.java
@@ -180,10 +180,10 @@
"map_post_processing", builder -> builder.networkSynchronized(MapPostProcessing.STREAM_CODEC)
);
public static final DataComponentType<ChargedProjectiles> CHARGED_PROJECTILES = register(
- "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(ChargedProjectiles.STREAM_CODEC).cacheEncoding()
+ "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles
);
public static final DataComponentType<BundleContents> BUNDLE_CONTENTS = register(
- "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(BundleContents.STREAM_CODEC).cacheEncoding()
+ "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents
);
public static final DataComponentType<PotionContents> POTION_CONTENTS = register(
"potion_contents", builder -> builder.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding()
@@ -250,7 +250,7 @@
"pot_decorations", builder -> builder.persistent(PotDecorations.CODEC).networkSynchronized(PotDecorations.STREAM_CODEC).cacheEncoding()
);
public static final DataComponentType<ItemContainerContents> CONTAINER = register(
- "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(ItemContainerContents.STREAM_CODEC).cacheEncoding()
+ "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CONTAINER).cacheEncoding() // Paper - sanitize container contents
);
public static final DataComponentType<BlockItemStateProperties> BLOCK_STATE = register(
"block_state", builder -> builder.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,54 @@
--- 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);
}

View File

@@ -0,0 +1,52 @@
--- a/net/minecraft/core/registries/BuiltInRegistries.java
+++ b/net/minecraft/core/registries/BuiltInRegistries.java
@@ -296,6 +296,17 @@
public static final Registry<SlotDisplay.Type<?>> SLOT_DISPLAY = registerSimple(Registries.SLOT_DISPLAY, SlotDisplays::bootstrap);
public static final Registry<RecipeBookCategory> RECIPE_BOOK_CATEGORY = registerSimple(Registries.RECIPE_BOOK_CATEGORY, RecipeBookCategories::bootstrap);
public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
+ // Paper start - add built-in registry conversions
+ public static final io.papermc.paper.registry.data.util.Conversions BUILT_IN_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(new net.minecraft.resources.RegistryOps.RegistryInfoLookup() {
+ @Override
+ public <T> java.util.Optional<net.minecraft.resources.RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryRef) {
+ final Registry<T> registry = net.minecraft.server.RegistryLayer.STATIC_ACCESS.lookupOrThrow(registryRef);
+ return java.util.Optional.of(
+ new net.minecraft.resources.RegistryOps.RegistryInfo<>(registry, registry, Lifecycle.experimental())
+ );
+ }
+ });
+ // Paper end - add built-in registry conversions
private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> key, BuiltInRegistries.RegistryBootstrap<T> initializer) {
return internalRegister(key, new MappedRegistry<>(key, Lifecycle.stable(), false), initializer);
@@ -323,14 +334,22 @@
ResourceKey<? extends Registry<T>> key, R registry, BuiltInRegistries.RegistryBootstrap<T> initializer
) {
Bootstrap.checkBootstrapCalled(() -> "registry " + key.location());
+ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry.key(), registry); // Paper - initialize API registry
ResourceLocation resourceLocation = key.location();
LOADERS.put(resourceLocation, () -> initializer.run(registry));
- WRITABLE_REGISTRY.register((ResourceKey<WritableRegistry<?>>)key, registry, RegistrationInfo.BUILT_IN);
+ WRITABLE_REGISTRY.register((ResourceKey)key, registry, RegistrationInfo.BUILT_IN); // Paper - decompile fix
return registry;
}
public static void bootStrap() {
+ // Paper start
+ bootStrap(() -> {});
+ }
+ public static void bootStrap(Runnable runnable) {
+ // Paper end
+ REGISTRY.freeze(); // Paper - freeze main registry early
createContents();
+ runnable.run(); // Paper
freeze();
validate(REGISTRY);
}
@@ -348,6 +367,7 @@
for (Registry<?> registry : REGISTRY) {
bindBootstrappedTagsToEmpty(registry);
+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BUILT_IN_CONVERSIONS); // Paper
registry.freeze();
}
}