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,39 @@
--- a/net/minecraft/world/level/BaseCommandBlock.java
+++ b/net/minecraft/world/level/BaseCommandBlock.java
@@ -33,6 +33,10 @@
private String command = "";
@Nullable
private Component customName;
+ // CraftBukkit start
+ @Override
+ public abstract org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper);
+ // CraftBukkit end
public BaseCommandBlock() {}
@@ -132,7 +136,7 @@
});
- minecraftserver.getCommands().performPrefixedCommand(commandlistenerwrapper, this.command);
+ minecraftserver.getCommands().dispatchServerCommand(commandlistenerwrapper, this.command); // CraftBukkit
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Executing command block");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Command to be executed");
@@ -174,6 +178,7 @@
@Override
public void sendSystemMessage(Component message) {
if (this.trackOutput) {
+ org.spigotmc.AsyncCatcher.catchOp("sendSystemMessage to a command block"); // Paper - Don't broadcast messages to command blocks
SimpleDateFormat simpledateformat = BaseCommandBlock.TIME_FORMAT;
Date date = new Date();
@@ -200,7 +205,7 @@
}
public InteractionResult usedBy(Player player) {
- if (!player.canUseGameMasterBlocks()) {
+ if (!player.canUseGameMasterBlocks() && (!player.isCreative() || !player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission
return InteractionResult.PASS;
} else {
if (player.getCommandSenderWorld().isClientSide) {

View File

@@ -0,0 +1,167 @@
--- a/net/minecraft/world/level/BaseSpawner.java
+++ b/net/minecraft/world/level/BaseSpawner.java
@@ -49,15 +49,17 @@
public int maxNearbyEntities = 6;
public int requiredPlayerRange = 16;
public int spawnRange = 4;
+ private int tickDelay = 0; // Paper - Configurable mob spawner tick rate
public BaseSpawner() {}
public void setEntityId(EntityType<?> type, @Nullable Level world, RandomSource random, BlockPos pos) {
this.getOrCreateNextSpawnData(world, random, pos).getEntityToSpawn().putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(type).toString());
+ this.spawnPotentials = SimpleWeightedRandomList.empty(); // CraftBukkit - SPIGOT-3496, MC-92282
}
public boolean isNearPlayer(Level world, BlockPos pos) {
- return world.hasNearbyAlivePlayer((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange);
+ return world.hasNearbyAlivePlayerThatAffectsSpawning((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, (double) this.requiredPlayerRange); // Paper - Affects Spawning API
}
public void clientTick(Level world, BlockPos pos) {
@@ -82,13 +84,19 @@
}
public void serverTick(ServerLevel world, BlockPos pos) {
+ if (spawnCount <= 0 || maxNearbyEntities <= 0) return; // Paper - Ignore impossible spawn tick
+ // Paper start - Configurable mob spawner tick rate
+ if (spawnDelay > 0 && --tickDelay > 0) return;
+ tickDelay = world.paperConfig().tickRates.mobSpawner;
+ if (tickDelay == -1) { return; } // If disabled
+ // Paper end - Configurable mob spawner tick rate
if (this.isNearPlayer(world, pos)) {
- if (this.spawnDelay == -1) {
+ if (this.spawnDelay < -tickDelay) { // Paper - Configurable mob spawner tick rate
this.delay(world, pos);
}
if (this.spawnDelay > 0) {
- --this.spawnDelay;
+ this.spawnDelay -= tickDelay; // Paper - Configurable mob spawner tick rate
} else {
boolean flag = false;
RandomSource randomsource = world.getRandom();
@@ -125,6 +133,20 @@
} else if (!SpawnPlacements.checkSpawnRules((EntityType) optional.get(), world, EntitySpawnReason.SPAWNER, blockposition1, world.getRandom())) {
continue;
}
+ // Paper start - PreCreatureSpawnEvent
+ com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent(
+ io.papermc.paper.util.MCUtil.toLocation(world, d0, d1, d2),
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(optional.get()),
+ io.papermc.paper.util.MCUtil.toLocation(world, pos)
+ );
+ if (!event.callEvent()) {
+ flag = true;
+ if (event.shouldAbortSpawn()) {
+ break;
+ }
+ continue;
+ }
+ // Paper end - PreCreatureSpawnEvent
Entity entity = EntityType.loadEntityRecursive(nbttagcompound, world, EntitySpawnReason.SPAWNER, (entity1) -> {
entity1.moveTo(d0, d1, d2, entity1.getYRot(), entity1.getXRot());
@@ -143,6 +165,7 @@
return;
}
+ entity.preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; preserve entity motion from tag
entity.moveTo(entity.getX(), entity.getY(), entity.getZ(), randomsource.nextFloat() * 360.0F, 0.0F);
if (entity instanceof Mob) {
Mob entityinsentient = (Mob) entity;
@@ -157,13 +180,27 @@
((Mob) entity).finalizeSpawn(world, world.getCurrentDifficultyAt(entity.blockPosition()), EntitySpawnReason.SPAWNER, (SpawnGroupData) null);
}
- Optional optional1 = mobspawnerdata.getEquipment();
+ Optional<net.minecraft.world.entity.EquipmentTable> optional1 = mobspawnerdata.getEquipment(); // CraftBukkit - decompile error
Objects.requireNonNull(entityinsentient);
optional1.ifPresent(entityinsentient::equip);
+ // Spigot Start
+ if ( entityinsentient.level().spigotConfig.nerfSpawnerMobs )
+ {
+ entityinsentient.aware = false;
+ }
+ // Spigot End
}
- if (!world.tryAddFreshEntityWithPassengers(entity)) {
+ entity.spawnedViaMobSpawner = true; // Paper
+ entity.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER; // Paper - Entity#getEntitySpawnReason
+ flag = true; // Paper
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, pos).isCancelled()) {
+ continue;
+ }
+ if (!world.tryAddFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER)) {
+ // CraftBukkit end
this.delay(world, pos);
return;
}
@@ -174,7 +211,7 @@
((Mob) entity).spawnAnim();
}
- flag = true;
+ //flag = true; // Paper - moved up above cancellable event
}
}
@@ -202,7 +239,13 @@
}
public void load(@Nullable Level world, BlockPos pos, CompoundTag nbt) {
+ // Paper start - use larger int if set
+ if (nbt.contains("Paper.Delay")) {
+ this.spawnDelay = nbt.getInt("Paper.Delay");
+ } else {
this.spawnDelay = nbt.getShort("Delay");
+ }
+ // Paper end
boolean flag = nbt.contains("SpawnData", 10);
if (flag) {
@@ -225,9 +268,15 @@
this.spawnPotentials = SimpleWeightedRandomList.single(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData());
}
+ // Paper start - use ints if set
+ if (nbt.contains("Paper.MinSpawnDelay", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
+ this.minSpawnDelay = nbt.getInt("Paper.MinSpawnDelay");
+ this.maxSpawnDelay = nbt.getInt("Paper.MaxSpawnDelay");
+ this.spawnCount = nbt.getShort("SpawnCount");
+ } else // Paper end
if (nbt.contains("MinSpawnDelay", 99)) {
- this.minSpawnDelay = nbt.getShort("MinSpawnDelay");
- this.maxSpawnDelay = nbt.getShort("MaxSpawnDelay");
+ this.minSpawnDelay = nbt.getInt("MinSpawnDelay"); // Paper - short -> int
+ this.maxSpawnDelay = nbt.getInt("MaxSpawnDelay"); // Paper - short -> int
this.spawnCount = nbt.getShort("SpawnCount");
}
@@ -244,9 +293,20 @@
}
public CompoundTag save(CompoundTag nbt) {
- nbt.putShort("Delay", (short) this.spawnDelay);
- nbt.putShort("MinSpawnDelay", (short) this.minSpawnDelay);
- nbt.putShort("MaxSpawnDelay", (short) this.maxSpawnDelay);
+ // Paper start
+ if (spawnDelay > Short.MAX_VALUE) {
+ nbt.putInt("Paper.Delay", this.spawnDelay);
+ }
+ nbt.putShort("Delay", (short) Math.min(Short.MAX_VALUE, this.spawnDelay));
+
+ if (minSpawnDelay > Short.MAX_VALUE || maxSpawnDelay > Short.MAX_VALUE) {
+ nbt.putInt("Paper.MinSpawnDelay", this.minSpawnDelay);
+ nbt.putInt("Paper.MaxSpawnDelay", this.maxSpawnDelay);
+ }
+
+ nbt.putShort("MinSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.minSpawnDelay));
+ nbt.putShort("MaxSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.maxSpawnDelay));
+ // Paper end
nbt.putShort("SpawnCount", (short) this.spawnCount);
nbt.putShort("MaxNearbyEntities", (short) this.maxNearbyEntities);
nbt.putShort("RequiredPlayerRange", (short) this.requiredPlayerRange);

View File

@@ -0,0 +1,90 @@
--- a/net/minecraft/world/level/BlockGetter.java
+++ b/net/minecraft/world/level/BlockGetter.java
@@ -12,6 +12,7 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
+import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
@@ -31,11 +32,20 @@
default <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos pos, BlockEntityType<T> type) {
BlockEntity tileentity = this.getBlockEntity(pos);
- return tileentity != null && tileentity.getType() == type ? Optional.of(tileentity) : Optional.empty();
+ return tileentity != null && tileentity.getType() == type ? (Optional<T>) Optional.of(tileentity) : Optional.empty(); // CraftBukkit - decompile error
}
BlockState getBlockState(BlockPos pos);
+ // Paper start - if loaded util
+ @Nullable BlockState getBlockStateIfLoaded(BlockPos blockposition);
+ default @Nullable Block getBlockIfLoaded(BlockPos blockposition) {
+ BlockState type = this.getBlockStateIfLoaded(blockposition);
+ return type == null ? null : type.getBlock();
+ }
+ @Nullable FluidState getFluidIfLoaded(BlockPos blockposition);
+ // Paper end
+
FluidState getFluidState(BlockPos pos);
default int getLightEmission(BlockPos pos) {
@@ -59,10 +69,25 @@
});
}
- default BlockHitResult clip(ClipContext context) {
- return (BlockHitResult) BlockGetter.traverseBlocks(context.getFrom(), context.getTo(), context, (raytrace1, blockposition) -> {
- BlockState iblockdata = this.getBlockState(blockposition);
- FluidState fluid = this.getFluidState(blockposition);
+ // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace
+ default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) {
+ // Paper start - Add predicate for blocks when raytracing
+ return clip(raytrace1, blockposition, null);
+ }
+
+ default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition, java.util.function.Predicate<? super org.bukkit.block.Block> canCollide) {
+ // Paper end - Add predicate for blocks when raytracing
+ // Paper start - Prevent raytrace from loading chunks
+ BlockState iblockdata = this.getBlockStateIfLoaded(blockposition);
+ if (iblockdata == null) {
+ // copied the last function parameter (listed below)
+ Vec3 vec3d = raytrace1.getFrom().subtract(raytrace1.getTo());
+
+ return BlockHitResult.miss(raytrace1.getTo(), Direction.getApproximateNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(raytrace1.getTo()));
+ }
+ // Paper end - Prevent raytrace from loading chunks
+ if (iblockdata.isAir() || (canCollide != null && this instanceof LevelAccessor levelAccessor && !canCollide.test(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, blockposition)))) return null; // Paper - Perf: optimise air cases & check canCollide predicate
+ FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: don't need to go to world state again
Vec3 vec3d = raytrace1.getFrom();
Vec3 vec3d1 = raytrace1.getTo();
VoxelShape voxelshape = raytrace1.getBlockShape(iblockdata, this, blockposition);
@@ -73,6 +98,18 @@
double d1 = movingobjectpositionblock1 == null ? Double.MAX_VALUE : raytrace1.getFrom().distanceToSqr(movingobjectpositionblock1.getLocation());
return d0 <= d1 ? movingobjectpositionblock : movingobjectpositionblock1;
+ }
+ // CraftBukkit end
+
+ default BlockHitResult clip(ClipContext context) {
+ // Paper start - Add predicate for blocks when raytracing
+ return clip(context, (java.util.function.Predicate<org.bukkit.block.Block>) null);
+ }
+
+ default BlockHitResult clip(ClipContext context, java.util.function.Predicate<? super org.bukkit.block.Block> canCollide) {
+ // Paper end - Add predicate for blocks when raytracing
+ return (BlockHitResult) BlockGetter.traverseBlocks(context.getFrom(), context.getTo(), context, (raytrace1, blockposition) -> {
+ return this.clip(raytrace1, blockposition, canCollide); // CraftBukkit - moved into separate method // Paper - Add predicate for blocks when raytracing
}, (raytrace1) -> {
Vec3 vec3d = raytrace1.getFrom().subtract(raytrace1.getTo());
@@ -145,7 +182,7 @@
double d13 = d10 * (i1 > 0 ? 1.0D - Mth.frac(d4) : Mth.frac(d4));
double d14 = d11 * (j1 > 0 ? 1.0D - Mth.frac(d5) : Mth.frac(d5));
- Object object;
+ T object; // CraftBukkit - decompile error
do {
if (d12 > 1.0D && d13 > 1.0D && d14 > 1.0D) {

View File

@@ -0,0 +1,39 @@
--- a/net/minecraft/world/level/ChunkPos.java
+++ b/net/minecraft/world/level/ChunkPos.java
@@ -46,6 +46,7 @@
public static final int REGION_MAX_INDEX = 31;
public final int x;
public final int z;
+ public final long longKey; // Paper
private static final int HASH_A = 1664525;
private static final int HASH_C = 1013904223;
private static final int HASH_Z_XOR = -559038737;
@@ -53,16 +54,19 @@
public ChunkPos(int x, int z) {
this.x = x;
this.z = z;
+ this.longKey = asLong(this.x, this.z); // Paper
}
public ChunkPos(BlockPos pos) {
this.x = SectionPos.blockToSectionCoord(pos.getX());
this.z = SectionPos.blockToSectionCoord(pos.getZ());
+ this.longKey = asLong(this.x, this.z); // Paper
}
public ChunkPos(long pos) {
this.x = (int)pos;
this.z = (int)(pos >> 32);
+ this.longKey = asLong(this.x, this.z); // Paper
}
public static ChunkPos minFromRegion(int x, int z) {
@@ -74,7 +78,7 @@
}
public long toLong() {
- return asLong(this.x, this.z);
+ return longKey; // Paper
}
public static long asLong(int chunkX, int chunkZ) {

View File

@@ -0,0 +1,20 @@
--- a/net/minecraft/world/level/ClipContext.java
+++ b/net/minecraft/world/level/ClipContext.java
@@ -22,7 +22,7 @@
private final CollisionContext collisionContext;
public ClipContext(Vec3 start, Vec3 end, ClipContext.Block shapeType, ClipContext.Fluid fluidHandling, Entity entity) {
- this(start, end, shapeType, fluidHandling, CollisionContext.of(entity));
+ this(start, end, shapeType, fluidHandling, (entity == null) ? CollisionContext.empty() : CollisionContext.of(entity)); // CraftBukkit
}
public ClipContext(Vec3 start, Vec3 end, ClipContext.Block shapeType, ClipContext.Fluid fluidHandling, CollisionContext shapeContext) {
@@ -79,7 +79,7 @@
private final Predicate<FluidState> canPick;
- private Fluid(final Predicate predicate) {
+ private Fluid(final Predicate<FluidState> predicate) { // CraftBukkit - decompile error
this.canPick = predicate;
}

View File

@@ -0,0 +1,22 @@
--- a/net/minecraft/world/level/EmptyBlockGetter.java
+++ b/net/minecraft/world/level/EmptyBlockGetter.java
@@ -17,7 +17,19 @@
return null;
}
+ // Paper start - If loaded util
@Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ return Fluids.EMPTY.defaultFluidState();
+ }
+
+ @Override
+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) {
+ return Blocks.AIR.defaultBlockState();
+ }
+ // Paper end
+
+ @Override
public BlockState getBlockState(BlockPos pos) {
return Blocks.AIR.defaultBlockState();
}

View File

@@ -0,0 +1,76 @@
--- a/net/minecraft/world/level/EntityGetter.java
+++ b/net/minecraft/world/level/EntityGetter.java
@@ -71,6 +71,11 @@
}
}
+ // Paper start - Affects Spawning API
+ default @Nullable Player findNearbyPlayer(Entity entity, double maxDistance, @Nullable Predicate<Entity> predicate) {
+ return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, predicate);
+ }
+ // Paper end - Affects Spawning API
@Nullable
default Player getNearestPlayer(double x, double y, double z, double maxDistance, @Nullable Predicate<Entity> targetPredicate) {
double d = -1.0;
@@ -89,6 +94,28 @@
return player;
}
+ // Paper start
+ default List<org.bukkit.entity.HumanEntity> findNearbyBukkitPlayers(double x, double y, double z, double radius, boolean notSpectator) {
+ return findNearbyBukkitPlayers(x, y, z, radius, notSpectator ? EntitySelector.NO_SPECTATORS : net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR);
+ }
+
+ default List<org.bukkit.entity.HumanEntity> findNearbyBukkitPlayers(double x, double y, double z, double radius, @Nullable Predicate<Entity> predicate) {
+ com.google.common.collect.ImmutableList.Builder<org.bukkit.entity.HumanEntity> builder = com.google.common.collect.ImmutableList.builder();
+
+ for (Player human : this.players()) {
+ if (predicate == null || predicate.test(human)) {
+ double distanceSquared = human.distanceToSqr(x, y, z);
+
+ if (radius < 0.0D || distanceSquared < radius * radius) {
+ builder.add(human.getBukkitEntity());
+ }
+ }
+ }
+
+ return builder.build();
+ }
+ // Paper end
+
@Nullable
default Player getNearestPlayer(Entity entity, double maxDistance) {
return this.getNearestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, false);
@@ -100,6 +127,20 @@
return this.getNearestPlayer(x, y, z, maxDistance, predicate);
}
+ // Paper start - Affects Spawning API
+ default boolean hasNearbyAlivePlayerThatAffectsSpawning(double x, double y, double z, double range) {
+ for (Player player : this.players()) {
+ if (EntitySelector.PLAYER_AFFECTS_SPAWNING.test(player)) { // combines NO_SPECTATORS and LIVING_ENTITY_STILL_ALIVE with an "affects spawning" check
+ double distanceSqr = player.distanceToSqr(x, y, z);
+ if (range < 0.0D || distanceSqr < range * range) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ // Paper end - Affects Spawning API
+
default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) {
for (Player player : this.players()) {
if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
@@ -124,4 +165,11 @@
return null;
}
+
+ // Paper start - check global player list where appropriate
+ @Nullable
+ default Player getGlobalPlayerByUUID(UUID uuid) {
+ return this.getPlayerByUUID(uuid);
+ }
+ // Paper end - check global player list where appropriate
}

View File

@@ -0,0 +1,321 @@
--- a/net/minecraft/world/level/GameRules.java
+++ b/net/minecraft/world/level/GameRules.java
@@ -36,6 +36,14 @@
public class GameRules {
+ // Paper start - allow disabling gamerule limits
+ private static final boolean DISABLE_LIMITS = Boolean.getBoolean("paper.disableGameRuleLimits");
+
+ private static int limit(final int limit, final int unlimited) {
+ return DISABLE_LIMITS ? unlimited : limit;
+ }
+ // Paper end - allow disabling gamerule limits
+
public static final int DEFAULT_RANDOM_TICK_SPEED = 3;
static final Logger LOGGER = LogUtils.getLogger();
private static final Map<GameRules.Key<?>, GameRules.Type<?>> GAME_RULE_TYPES = Maps.newTreeMap(Comparator.comparing((gamerules_gamerulekey) -> {
@@ -58,7 +66,7 @@
public static final GameRules.Key<GameRules.BooleanValue> RULE_SENDCOMMANDFEEDBACK = GameRules.register("sendCommandFeedback", GameRules.Category.CHAT, GameRules.BooleanValue.create(true));
public static final GameRules.Key<GameRules.BooleanValue> RULE_REDUCEDDEBUGINFO = GameRules.register("reducedDebugInfo", GameRules.Category.MISC, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
int i = gamerules_gameruleboolean.get() ? 22 : 23;
- Iterator iterator = minecraftserver.getPlayerList().getPlayers().iterator();
+ Iterator iterator = minecraftserver.players().iterator(); // CraftBukkit - per-world
while (iterator.hasNext()) {
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
@@ -74,7 +82,7 @@
public static final GameRules.Key<GameRules.IntegerValue> RULE_MAX_ENTITY_CRAMMING = GameRules.register("maxEntityCramming", GameRules.Category.MOBS, GameRules.IntegerValue.create(24));
public static final GameRules.Key<GameRules.BooleanValue> RULE_WEATHER_CYCLE = GameRules.register("doWeatherCycle", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
public static final GameRules.Key<GameRules.BooleanValue> RULE_LIMITED_CRAFTING = GameRules.register("doLimitedCrafting", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
- Iterator iterator = minecraftserver.getPlayerList().getPlayers().iterator();
+ Iterator iterator = minecraftserver.players().iterator(); // CraftBukkit - per-world
while (iterator.hasNext()) {
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
@@ -90,7 +98,7 @@
public static final GameRules.Key<GameRules.BooleanValue> RULE_DISABLE_RAIDS = GameRules.register("disableRaids", GameRules.Category.MOBS, GameRules.BooleanValue.create(false));
public static final GameRules.Key<GameRules.BooleanValue> RULE_DOINSOMNIA = GameRules.register("doInsomnia", GameRules.Category.SPAWNING, GameRules.BooleanValue.create(true));
public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_IMMEDIATE_RESPAWN = GameRules.register("doImmediateRespawn", GameRules.Category.PLAYER, GameRules.BooleanValue.create(false, (minecraftserver, gamerules_gameruleboolean) -> {
- Iterator iterator = minecraftserver.getPlayerList().getPlayers().iterator();
+ Iterator iterator = minecraftserver.players().iterator(); // CraftBukkit - per-world
while (iterator.hasNext()) {
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
@@ -120,15 +128,16 @@
public static final GameRules.Key<GameRules.BooleanValue> RULE_GLOBAL_SOUND_EVENTS = GameRules.register("globalSoundEvents", GameRules.Category.MISC, GameRules.BooleanValue.create(true));
public static final GameRules.Key<GameRules.BooleanValue> RULE_DO_VINES_SPREAD = GameRules.register("doVinesSpread", GameRules.Category.UPDATES, GameRules.BooleanValue.create(true));
public static final GameRules.Key<GameRules.BooleanValue> RULE_ENDER_PEARLS_VANISH_ON_DEATH = GameRules.register("enderPearlsVanishOnDeath", GameRules.Category.PLAYER, GameRules.BooleanValue.create(true));
- public static final GameRules.Key<GameRules.IntegerValue> RULE_MINECART_MAX_SPEED = GameRules.register("minecartMaxSpeed", GameRules.Category.MISC, GameRules.IntegerValue.create(8, 1, 1000, FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS), (minecraftserver, gamerules_gameruleint) -> {
+ public static final GameRules.Key<GameRules.IntegerValue> RULE_MINECART_MAX_SPEED = GameRules.register("minecartMaxSpeed", GameRules.Category.MISC, GameRules.IntegerValue.create(8, 1, limit(1000, Integer.MAX_VALUE), FeatureFlagSet.of(FeatureFlags.MINECART_IMPROVEMENTS), (minecraftserver, gamerules_gameruleint) -> { // Paper - allow disabling gamerule limits
}));
- public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_CHUNK_RADIUS = GameRules.register("spawnChunkRadius", GameRules.Category.MISC, GameRules.IntegerValue.create(2, 0, 32, FeatureFlagSet.of(), (minecraftserver, gamerules_gameruleint) -> {
- ServerLevel worldserver = minecraftserver.overworld();
+ public static final GameRules.Key<GameRules.IntegerValue> RULE_SPAWN_CHUNK_RADIUS = GameRules.register("spawnChunkRadius", GameRules.Category.MISC, GameRules.IntegerValue.create(2, 0, limit(32, Integer.MAX_VALUE), FeatureFlagSet.of(), (minecraftserver, gamerules_gameruleint) -> { // Paper - allow disabling gamerule limits
+ ServerLevel worldserver = minecraftserver; // CraftBukkit - per-world
worldserver.setDefaultSpawnPos(worldserver.getSharedSpawnPos(), worldserver.getSharedSpawnAngle());
}));
private final Map<GameRules.Key<?>, GameRules.Value<?>> rules;
private final FeatureFlagSet enabledFeatures;
+ private final GameRules.Value<?>[] gameruleArray; // Paper - Perf: Use array for gamerule storage
private static <T extends GameRules.Value<T>> GameRules.Key<T> register(String name, GameRules.Category category, GameRules.Type<T> type) {
GameRules.Key<T> gamerules_gamerulekey = new GameRules.Key<>(name, category);
@@ -161,10 +170,21 @@
private GameRules(Map<GameRules.Key<?>, GameRules.Value<?>> rules, FeatureFlagSet enabledFeatures) {
this.rules = rules;
this.enabledFeatures = enabledFeatures;
+
+ // Paper start - Perf: Use array for gamerule storage
+ int arraySize = GameRules.Key.lastGameRuleIndex + 1;
+ GameRules.Value<?>[] values = new GameRules.Value[arraySize];
+
+ for (Entry<GameRules.Key<?>, GameRules.Value<?>> entry : rules.entrySet()) {
+ values[entry.getKey().gameRuleIndex] = entry.getValue();
+ }
+
+ this.gameruleArray = values;
+ // Paper end - Perf: Use array for gamerule storage
}
public <T extends GameRules.Value<T>> T getRule(GameRules.Key<T> key) {
- T t0 = (GameRules.Value) this.rules.get(key);
+ T t0 = key == null ? null : (T) this.gameruleArray[key.gameRuleIndex]; // Paper - Perf: Use array for gamerule storage
if (t0 == null) {
throw new IllegalArgumentException("Tried to access invalid game rule");
@@ -184,7 +204,7 @@
private void loadFromTag(DynamicLike<?> values) {
this.rules.forEach((gamerules_gamerulekey, gamerules_gamerulevalue) -> {
- DataResult dataresult = values.get(gamerules_gamerulekey.id).asString();
+ DataResult<String> dataresult = values.get(gamerules_gamerulekey.id).asString(); // CraftBukkit - decompile error
Objects.requireNonNull(gamerules_gamerulevalue);
dataresult.ifSuccess(gamerules_gamerulevalue::deserialize);
@@ -205,22 +225,22 @@
private <T extends GameRules.Value<T>> void callVisitorCap(GameRules.GameRuleTypeVisitor visitor, GameRules.Key<?> key, GameRules.Type<?> type) {
if (type.requiredFeatures.isSubsetOf(this.enabledFeatures)) {
- visitor.visit(key, type);
- type.callVisitor(visitor, key);
+ visitor.visit((GameRules.Key<T>) key, (GameRules.Type<T>) type); // CraftBukkit - decompile error
+ ((GameRules.Type<T>) type).callVisitor(visitor, (GameRules.Key<T>) key); // CraftBukkit - decompile error
}
}
- public void assignFrom(GameRules rules, @Nullable MinecraftServer server) {
- rules.rules.keySet().forEach((gamerules_gamerulekey) -> {
- this.assignCap(gamerules_gamerulekey, rules, server);
+ public void assignFrom(GameRules gamerules, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ gamerules.rules.keySet().forEach((gamerules_gamerulekey) -> {
+ this.assignCap(gamerules_gamerulekey, gamerules, minecraftserver);
});
}
- private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> key, GameRules rules, @Nullable MinecraftServer server) {
- T t0 = rules.getRule(key);
+ private <T extends GameRules.Value<T>> void assignCap(GameRules.Key<T> gamerules_gamerulekey, GameRules gamerules, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ T t0 = gamerules.getRule(gamerules_gamerulekey);
- this.getRule(key).setFrom(t0, server);
+ this.getRule(gamerules_gamerulekey).setFrom(t0, minecraftserver);
}
public boolean getBoolean(GameRules.Key<GameRules.BooleanValue> rule) {
@@ -232,6 +252,10 @@
}
public static final class Key<T extends GameRules.Value<T>> {
+ // Paper start - Perf: Use array for gamerule storage
+ public static int lastGameRuleIndex = 0;
+ public final int gameRuleIndex = lastGameRuleIndex++;
+ // Paper end - Perf: Use array for gamerule storage
final String id;
private final GameRules.Category category;
@@ -285,11 +309,11 @@
final Supplier<ArgumentType<?>> argument;
private final Function<GameRules.Type<T>, T> constructor;
- final BiConsumer<MinecraftServer, T> callback;
+ final BiConsumer<ServerLevel, T> callback; // CraftBukkit - per-world
private final GameRules.VisitorCaller<T> visitorCaller;
final FeatureFlagSet requiredFeatures;
- Type(Supplier<ArgumentType<?>> argumentType, Function<GameRules.Type<T>, T> ruleFactory, BiConsumer<MinecraftServer, T> changeCallback, GameRules.VisitorCaller<T> ruleAcceptor, FeatureFlagSet requiredFeatures) {
+ Type(Supplier<ArgumentType<?>> argumentType, Function<GameRules.Type<T>, T> ruleFactory, BiConsumer<ServerLevel, T> changeCallback, GameRules.VisitorCaller<T> ruleAcceptor, FeatureFlagSet requiredFeatures) { // CraftBukkit - per-world
this.argument = argumentType;
this.constructor = ruleFactory;
this.callback = changeCallback;
@@ -302,7 +326,7 @@
}
public T createRule() {
- return (GameRules.Value) this.constructor.apply(this);
+ return this.constructor.apply(this); // CraftBukkit - decompile error
}
public void callVisitor(GameRules.GameRuleTypeVisitor consumer, GameRules.Key<T> key) {
@@ -322,21 +346,21 @@
this.type = type;
}
- protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name);
+ protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey); // Paper - Add WorldGameRuleChangeEvent
- public void setFromArgument(CommandContext<CommandSourceStack> context, String name) {
- this.updateFromArgument(context, name);
- this.onChanged(((CommandSourceStack) context.getSource()).getServer());
+ public void setFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey) { // Paper - Add WorldGameRuleChangeEvent
+ this.updateFromArgument(context, name, gameRuleKey); // Paper - Add WorldGameRuleChangeEvent
+ this.onChanged(((CommandSourceStack) context.getSource()).getLevel()); // CraftBukkit - per-world
}
- public void onChanged(@Nullable MinecraftServer server) {
- if (server != null) {
- this.type.callback.accept(server, this.getSelf());
+ public void onChanged(@Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ if (minecraftserver != null) {
+ this.type.callback.accept(minecraftserver, this.getSelf());
}
}
- protected abstract void deserialize(String value);
+ public abstract void deserialize(String value); // PAIL - private->public
public abstract String serialize();
@@ -350,7 +374,7 @@
protected abstract T copy();
- public abstract void setFrom(T rule, @Nullable MinecraftServer server);
+ public abstract void setFrom(T t0, @Nullable ServerLevel minecraftserver); // CraftBukkit - per-world
}
public interface GameRuleTypeVisitor {
@@ -366,7 +390,7 @@
private boolean value;
- static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue, BiConsumer<MinecraftServer, GameRules.BooleanValue> changeCallback) {
+ static GameRules.Type<GameRules.BooleanValue> create(boolean initialValue, BiConsumer<ServerLevel, GameRules.BooleanValue> changeCallback) { // CraftBukkit - per-world
return new GameRules.Type<>(BoolArgumentType::bool, (gamerules_gameruledefinition) -> {
return new GameRules.BooleanValue(gamerules_gameruledefinition, initialValue);
}, changeCallback, GameRules.GameRuleTypeVisitor::visitBoolean, FeatureFlagSet.of());
@@ -383,17 +407,20 @@
}
@Override
- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) {
- this.value = BoolArgumentType.getBool(context, name);
+ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<BooleanValue> gameRuleKey) { // Paper start - Add WorldGameRuleChangeEvent
+ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Boolean>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(BoolArgumentType.getBool(context, name)));
+ if (!event.callEvent()) return;
+ this.value = Boolean.parseBoolean(event.getValue());
+ // Paper end - Add WorldGameRuleChangeEvent
}
public boolean get() {
return this.value;
}
- public void set(boolean value, @Nullable MinecraftServer server) {
- this.value = value;
- this.onChanged(server);
+ public void set(boolean flag, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ this.value = flag;
+ this.onChanged(minecraftserver);
}
@Override
@@ -402,7 +429,7 @@
}
@Override
- protected void deserialize(String value) {
+ public void deserialize(String value) { // PAIL - protected->public
this.value = Boolean.parseBoolean(value);
}
@@ -421,9 +448,9 @@
return new GameRules.BooleanValue(this.type, this.value);
}
- public void setFrom(GameRules.BooleanValue rule, @Nullable MinecraftServer server) {
- this.value = rule.value;
- this.onChanged(server);
+ public void setFrom(GameRules.BooleanValue gamerules_gameruleboolean, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ this.value = gamerules_gameruleboolean.value;
+ this.onChanged(minecraftserver);
}
}
@@ -431,13 +458,13 @@
private int value;
- private static GameRules.Type<GameRules.IntegerValue> create(int initialValue, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeCallback) {
+ private static GameRules.Type<GameRules.IntegerValue> create(int initialValue, BiConsumer<ServerLevel, GameRules.IntegerValue> changeCallback) { // CraftBukkit - per-world
return new GameRules.Type<>(IntegerArgumentType::integer, (gamerules_gameruledefinition) -> {
return new GameRules.IntegerValue(gamerules_gameruledefinition, initialValue);
}, changeCallback, GameRules.GameRuleTypeVisitor::visitInteger, FeatureFlagSet.of());
}
- static GameRules.Type<GameRules.IntegerValue> create(int initialValue, int min, int max, FeatureFlagSet requiredFeatures, BiConsumer<MinecraftServer, GameRules.IntegerValue> changeCallback) {
+ static GameRules.Type<GameRules.IntegerValue> create(int initialValue, int min, int max, FeatureFlagSet requiredFeatures, BiConsumer<ServerLevel, GameRules.IntegerValue> changeCallback) { // CraftBukkit - per-world
return new GameRules.Type<>(() -> {
return IntegerArgumentType.integer(min, max);
}, (gamerules_gameruledefinition) -> {
@@ -456,17 +483,20 @@
}
@Override
- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) {
- this.value = IntegerArgumentType.getInteger(context, name);
+ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<IntegerValue> gameRuleKey) { // Paper start - Add WorldGameRuleChangeEvent
+ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Integer>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(IntegerArgumentType.getInteger(context, name)));
+ if (!event.callEvent()) return;
+ this.value = Integer.parseInt(event.getValue());
+ // Paper end - Add WorldGameRuleChangeEvent
}
public int get() {
return this.value;
}
- public void set(int value, @Nullable MinecraftServer server) {
- this.value = value;
- this.onChanged(server);
+ public void set(int i, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ this.value = i;
+ this.onChanged(minecraftserver);
}
@Override
@@ -475,7 +505,7 @@
}
@Override
- protected void deserialize(String value) {
+ public void deserialize(String value) { // PAIL - protected->public
this.value = IntegerValue.safeParse(value);
}
@@ -517,9 +547,9 @@
return new GameRules.IntegerValue(this.type, this.value);
}
- public void setFrom(GameRules.IntegerValue rule, @Nullable MinecraftServer server) {
- this.value = rule.value;
- this.onChanged(server);
+ public void setFrom(GameRules.IntegerValue gamerules_gameruleint, @Nullable ServerLevel minecraftserver) { // CraftBukkit - per-world
+ this.value = gamerules_gameruleint.value;
+ this.onChanged(minecraftserver);
}
}

View File

@@ -0,0 +1,710 @@
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -25,8 +25,10 @@
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
+import io.papermc.paper.util.MCUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.FullChunkStatus;
+import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
@@ -43,6 +45,7 @@
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
+import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.alchemy.PotionBrewing;
@@ -57,12 +60,14 @@
import net.minecraft.world.level.block.entity.FuelValues;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.border.BorderChangeListener;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.dimension.DimensionType;
+import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.level.gameevent.GameEvent;
@@ -81,6 +86,25 @@
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.scores.Scoreboard;
+// CraftBukkit start
+import java.util.HashMap;
+import java.util.Map;
+import net.minecraft.network.protocol.game.ClientboundSetBorderCenterPacket;
+import net.minecraft.network.protocol.game.ClientboundSetBorderLerpSizePacket;
+import net.minecraft.network.protocol.game.ClientboundSetBorderSizePacket;
+import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDelayPacket;
+import net.minecraft.network.protocol.game.ClientboundSetBorderWarningDistancePacket;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.CraftServer;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.block.CapturedBlockState;
+import org.bukkit.craftbukkit.block.CraftBlockState;
+import org.bukkit.craftbukkit.block.data.CraftBlockData;
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
+import org.bukkit.entity.SpawnCategory;
+import org.bukkit.event.block.BlockPhysicsEvent;
+// CraftBukkit end
+
public abstract class Level implements LevelAccessor, AutoCloseable {
public static final Codec<ResourceKey<Level>> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION);
@@ -94,7 +118,7 @@
public static final int TICKS_PER_DAY = 24000;
public static final int MAX_ENTITY_SPAWN_Y = 20000000;
public static final int MIN_ENTITY_SPAWN_Y = -20000000;
- protected final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList();
+ public final List<TickingBlockEntity> blockEntityTickers = Lists.newArrayList(); // Paper - public
protected final NeighborUpdater neighborUpdater;
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
private boolean tickingBlockEntities;
@@ -121,23 +145,91 @@
private final DamageSources damageSources;
private long subTickCount;
- protected Level(WritableLevelData properties, ResourceKey<Level> registryRef, RegistryAccess registryManager, Holder<DimensionType> dimensionEntry, boolean isClient, boolean debugWorld, long seed, int maxChainedNeighborUpdates) {
- this.levelData = properties;
- this.dimensionTypeRegistration = dimensionEntry;
- final DimensionType dimensionmanager = (DimensionType) dimensionEntry.value();
+ // CraftBukkit start Added the following
+ private final CraftWorld world;
+ public boolean pvpMode;
+ public org.bukkit.generator.ChunkGenerator generator;
- this.dimension = registryRef;
- this.isClientSide = isClient;
+ public boolean preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
+ public boolean captureBlockStates = false;
+ public boolean captureTreeGeneration = false;
+ public boolean isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
+ public Map<BlockPos, org.bukkit.craftbukkit.block.CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
+ public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
+ public List<ItemEntity> captureDrops;
+ public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<SpawnCategory> ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>();
+ public boolean populating;
+ public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
+ // Paper start - add paper world config
+ private final io.papermc.paper.configuration.WorldConfiguration paperConfig;
+ public io.papermc.paper.configuration.WorldConfiguration paperConfig() {
+ return this.paperConfig;
+ }
+ // Paper end - add paper world config
+
+ public static BlockPos lastPhysicsProblem; // Spigot
+ private org.spigotmc.TickLimiter entityLimiter;
+ private org.spigotmc.TickLimiter tileLimiter;
+ private int tileTickPosition;
+ public final Map<ServerExplosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
+ public java.util.ArrayDeque<net.minecraft.world.level.block.RedstoneTorchBlock.Toggle> redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here
+
+ public CraftWorld getWorld() {
+ return this.world;
+ }
+
+ public CraftServer getCraftServer() {
+ return (CraftServer) Bukkit.getServer();
+ }
+ // Paper start - Use getChunkIfLoadedImmediately
+ @Override
+ public boolean hasChunk(int chunkX, int chunkZ) {
+ return this.getChunkIfLoaded(chunkX, chunkZ) != null;
+ }
+ // Paper end - Use getChunkIfLoadedImmediately
+ // Paper start - per world ticks per spawn
+ private int getTicksPerSpawn(SpawnCategory spawnCategory) {
+ final int perWorld = this.paperConfig().entities.spawning.ticksPerSpawn.getInt(CraftSpawnCategory.toNMS(spawnCategory));
+ if (perWorld >= 0) {
+ return perWorld;
+ }
+ return this.getCraftServer().getTicksPerSpawns(spawnCategory);
+ }
+ // Paper end
+
+
+ public abstract ResourceKey<LevelStem> getTypeKey();
+
+ protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator) { // Paper - create paper world config
+ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
+ this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
+ this.generator = gen;
+ this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env);
+
+ // CraftBukkit Ticks things
+ for (SpawnCategory spawnCategory : SpawnCategory.values()) {
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ this.ticksPerSpawnCategory.put(spawnCategory, this.getTicksPerSpawn(spawnCategory)); // Paper
+ }
+ }
+
+ // CraftBukkit end
+ this.levelData = worlddatamutable;
+ this.dimensionTypeRegistration = holder;
+ final DimensionType dimensionmanager = (DimensionType) holder.value();
+
+ this.dimension = resourcekey;
+ this.isClientSide = flag;
if (dimensionmanager.coordinateScale() != 1.0D) {
- this.worldBorder = new WorldBorder(this) {
+ this.worldBorder = new WorldBorder() { // CraftBukkit - decompile error
@Override
public double getCenterX() {
- return super.getCenterX() / dimensionmanager.coordinateScale();
+ return super.getCenterX(); // CraftBukkit
}
@Override
public double getCenterZ() {
- return super.getCenterZ() / dimensionmanager.coordinateScale();
+ return super.getCenterZ(); // CraftBukkit
}
};
} else {
@@ -145,13 +237,90 @@
}
this.thread = Thread.currentThread();
- this.biomeManager = new BiomeManager(this, seed);
- this.isDebug = debugWorld;
- this.neighborUpdater = new CollectingNeighborUpdater(this, maxChainedNeighborUpdates);
- this.registryAccess = registryManager;
- this.damageSources = new DamageSources(registryManager);
+ this.biomeManager = new BiomeManager(this, i);
+ this.isDebug = flag1;
+ this.neighborUpdater = new CollectingNeighborUpdater(this, j);
+ this.registryAccess = iregistrycustom;
+ this.damageSources = new DamageSources(iregistrycustom);
+ // CraftBukkit start
+ this.getWorldBorder().world = (ServerLevel) this;
+ // From PlayerList.setPlayerFileData
+ this.getWorldBorder().addListener(new BorderChangeListener() {
+ @Override
+ public void onBorderSizeSet(WorldBorder border, double size) {
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderSizePacket(border), border.world);
+ }
+
+ @Override
+ public void onBorderSizeLerping(WorldBorder border, double fromSize, double toSize, long time) {
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderLerpSizePacket(border), border.world);
+ }
+
+ @Override
+ public void onBorderCenterSet(WorldBorder border, double centerX, double centerZ) {
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderCenterPacket(border), border.world);
+ }
+
+ @Override
+ public void onBorderSetWarningTime(WorldBorder border, int warningTime) {
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderWarningDelayPacket(border), border.world);
+ }
+
+ @Override
+ public void onBorderSetWarningBlocks(WorldBorder border, int warningBlockDistance) {
+ Level.this.getCraftServer().getHandle().broadcastAll(new ClientboundSetBorderWarningDistancePacket(border), border.world);
+ }
+
+ @Override
+ public void onBorderSetDamagePerBlock(WorldBorder border, double damagePerBlock) {}
+
+ @Override
+ public void onBorderSetDamageSafeZOne(WorldBorder border, double safeZoneRadius) {}
+ });
+ // CraftBukkit end
+ this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime);
+ this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime);
}
+ // Paper start - Cancel hit for vanished players
+ // ret true if no collision
+ public final boolean checkEntityCollision(BlockState data, Entity source, net.minecraft.world.phys.shapes.CollisionContext voxelshapedcollision,
+ BlockPos position, boolean checkCanSee) {
+ // Copied from IWorldReader#a(IBlockData, BlockPosition, VoxelShapeCollision) & EntityAccess#a(Entity, VoxelShape)
+ net.minecraft.world.phys.shapes.VoxelShape voxelshape = data.getCollisionShape(this, position, voxelshapedcollision);
+ if (voxelshape.isEmpty()) {
+ return true;
+ }
+
+ voxelshape = voxelshape.move((double) position.getX(), (double) position.getY(), (double) position.getZ());
+ if (voxelshape.isEmpty()) {
+ return true;
+ }
+
+ List<Entity> entities = this.getEntities(null, voxelshape.bounds());
+ for (int i = 0, len = entities.size(); i < len; ++i) {
+ Entity entity = entities.get(i);
+
+ if (checkCanSee && source instanceof net.minecraft.server.level.ServerPlayer && entity instanceof net.minecraft.server.level.ServerPlayer
+ && !((net.minecraft.server.level.ServerPlayer) source).getBukkitEntity().canSee(((net.minecraft.server.level.ServerPlayer) entity).getBukkitEntity())) {
+ continue;
+ }
+
+ // !entity1.dead && entity1.i && (entity == null || !entity1.x(entity));
+ // elide the last check since vanilla calls with entity = null
+ // only we care about the source for the canSee check
+ if (entity.isRemoved() || !entity.blocksBuilding) {
+ continue;
+ }
+
+ if (net.minecraft.world.phys.shapes.Shapes.joinIsNotEmpty(voxelshape, net.minecraft.world.phys.shapes.Shapes.create(entity.getBoundingBox()), net.minecraft.world.phys.shapes.BooleanOp.AND)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ // Paper end - Cancel hit for vanished players
@Override
public boolean isClientSide() {
return this.isClientSide;
@@ -163,6 +332,13 @@
return null;
}
+ // Paper start
+ public net.minecraft.world.phys.BlockHitResult.Type clipDirect(Vec3 start, Vec3 end, net.minecraft.world.phys.shapes.CollisionContext context) {
+ // To be patched over
+ return this.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, context)).getType();
+ }
+ // Paper end
+
public boolean isInWorldBounds(BlockPos pos) {
return !this.isOutsideBuildHeight(pos) && Level.isInWorldBoundsHorizontal(pos);
}
@@ -172,25 +348,87 @@
}
private static boolean isInWorldBoundsHorizontal(BlockPos pos) {
- return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000;
+ return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000; // Diff on change warnUnsafeChunk()
}
private static boolean isOutsideSpawnableHeight(int y) {
return y < -20000000 || y >= 20000000;
}
- public LevelChunk getChunkAt(BlockPos pos) {
+ public final LevelChunk getChunkAt(BlockPos pos) { // Paper - help inline
return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
}
@Override
- public LevelChunk getChunk(int chunkX, int chunkZ) {
- return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL);
+ public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
+ // Paper start - Perf: make sure loaded chunks get the inlined variant of this function
+ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource();
+ LevelChunk ifLoaded = cps.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
+ if (ifLoaded != null) {
+ return ifLoaded;
+ }
+ return (LevelChunk) cps.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
+ // Paper end - Perf: make sure loaded chunks get the inlined variant of this function
}
+ // Paper start - if loaded
@Nullable
@Override
+ public final ChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+ return ((ServerLevel)this).chunkSource.getChunkAtIfLoadedImmediately(x, z);
+ }
+
+ @Override
+ @Nullable
+ public final BlockState getBlockStateIfLoaded(BlockPos pos) {
+ // CraftBukkit start - tree generation
+ if (this.captureTreeGeneration) {
+ CraftBlockState previous = this.capturedBlockStates.get(pos);
+ if (previous != null) {
+ return previous.getHandle();
+ }
+ }
+ // CraftBukkit end
+ if (this.isOutsideBuildHeight(pos)) {
+ return Blocks.VOID_AIR.defaultBlockState();
+ } else {
+ ChunkAccess chunk = this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);
+
+ return chunk == null ? null : chunk.getBlockState(pos);
+ }
+ }
+
+ @Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ ChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+
+ return chunk == null ? null : chunk.getFluidState(blockposition);
+ }
+
+ @Override
+ public final boolean hasChunkAt(BlockPos pos) {
+ return getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4) != null; // Paper - Perf: Optimize Level.hasChunkAt(BlockPosition)Z
+ }
+
+ public final boolean isLoadedAndInBounds(BlockPos blockposition) { // Paper - final for inline
+ return getWorldBorder().isWithinBounds(blockposition) && getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null;
+ }
+
+ public @Nullable LevelChunk getChunkIfLoaded(int x, int z) { // Overridden in WorldServer for ABI compat which has final
+ return ((ServerLevel) this).getChunkSource().getChunkAtIfLoadedImmediately(x, z);
+ }
+ public final @Nullable LevelChunk getChunkIfLoaded(BlockPos blockposition) {
+ return ((ServerLevel) this).getChunkSource().getChunkAtIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+ }
+
+ // reduces need to do isLoaded before getType
+ public final @Nullable BlockState getBlockStateIfLoadedAndInBounds(BlockPos blockposition) {
+ return getWorldBorder().isWithinBounds(blockposition) ? getBlockStateIfLoaded(blockposition) : null;
+ }
+
+ @Override
public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
+ // Paper end
ChunkAccess ichunkaccess = this.getChunkSource().getChunk(chunkX, chunkZ, leastStatus, create);
if (ichunkaccess == null && create) {
@@ -207,6 +445,22 @@
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
+ // CraftBukkit start - tree generation
+ if (this.captureTreeGeneration) {
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
+ BlockState type = getBlockState(pos);
+ if (!type.isDestroyable()) return false;
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
+ CraftBlockState blockstate = this.capturedBlockStates.get(pos);
+ if (blockstate == null) {
+ blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
+ this.capturedBlockStates.put(pos.immutable(), blockstate);
+ }
+ blockstate.setData(state);
+ blockstate.setFlag(flags);
+ return true;
+ }
+ // CraftBukkit end
if (this.isOutsideBuildHeight(pos)) {
return false;
} else if (!this.isClientSide && this.isDebug()) {
@@ -214,44 +468,125 @@
} else {
LevelChunk chunk = this.getChunkAt(pos);
Block block = state.getBlock();
- BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0);
+ // CraftBukkit start - capture blockstates
+ boolean captured = false;
+ if (this.captureBlockStates && !this.capturedBlockStates.containsKey(pos)) {
+ CraftBlockState blockstate = (CraftBlockState) world.getBlockAt(pos.getX(), pos.getY(), pos.getZ()).getState(); // Paper - use CB getState to get a suitable snapshot
+ blockstate.setFlag(flags); // Paper - set flag
+ this.capturedBlockStates.put(pos.immutable(), blockstate);
+ captured = true;
+ }
+ // CraftBukkit end
+
+ BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
+
if (iblockdata1 == null) {
+ // CraftBukkit start - remove blockstate if failed (or the same)
+ if (this.captureBlockStates && captured) {
+ this.capturedBlockStates.remove(pos);
+ }
+ // CraftBukkit end
return false;
} else {
BlockState iblockdata2 = this.getBlockState(pos);
- if (iblockdata2 == state) {
+ /*
+ if (iblockdata2 == iblockdata) {
if (iblockdata1 != iblockdata2) {
- this.setBlocksDirty(pos, iblockdata1, iblockdata2);
+ this.setBlocksDirty(blockposition, iblockdata1, iblockdata2);
}
- if ((flags & 2) != 0 && (!this.isClientSide || (flags & 4) == 0) && (this.isClientSide || chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) {
- this.sendBlockUpdated(pos, iblockdata1, state, flags);
+ if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) {
+ this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i);
}
- if ((flags & 1) != 0) {
- this.blockUpdated(pos, iblockdata1.getBlock());
- if (!this.isClientSide && state.hasAnalogOutputSignal()) {
- this.updateNeighbourForOutputSignal(pos, block);
+ if ((i & 1) != 0) {
+ this.blockUpdated(blockposition, iblockdata1.getBlock());
+ if (!this.isClientSide && iblockdata.hasAnalogOutputSignal()) {
+ this.updateNeighbourForOutputSignal(blockposition, block);
}
}
- if ((flags & 16) == 0 && maxUpdateDepth > 0) {
- int k = flags & -34;
+ if ((i & 16) == 0 && j > 0) {
+ int k = i & -34;
- iblockdata1.updateIndirectNeighbourShapes(this, pos, k, maxUpdateDepth - 1);
- state.updateNeighbourShapes(this, pos, k, maxUpdateDepth - 1);
- state.updateIndirectNeighbourShapes(this, pos, k, maxUpdateDepth - 1);
+ iblockdata1.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
+ iblockdata.updateNeighbourShapes(this, blockposition, k, j - 1);
+ iblockdata.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
}
- this.onBlockStateChange(pos, iblockdata1, iblockdata2);
+ this.onBlockStateChange(blockposition, iblockdata1, iblockdata2);
}
+ */
+ // CraftBukkit start
+ if (!this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates
+ // Modularize client and physic updates
+ // Spigot start
+ try {
+ this.notifyAndUpdatePhysics(pos, chunk, iblockdata1, state, iblockdata2, flags, maxUpdateDepth);
+ } catch (StackOverflowError ex) {
+ Level.lastPhysicsProblem = new BlockPos(pos);
+ }
+ // Spigot end
+ }
+ // CraftBukkit end
+
return true;
+ }
+ }
+ }
+
+ // CraftBukkit start - Split off from above in order to directly send client and physic updates
+ public void notifyAndUpdatePhysics(BlockPos blockposition, LevelChunk chunk, BlockState oldBlock, BlockState newBlock, BlockState actualBlock, int i, int j) {
+ BlockState iblockdata = newBlock;
+ BlockState iblockdata1 = oldBlock;
+ BlockState iblockdata2 = actualBlock;
+ if (iblockdata2 == iblockdata) {
+ if (iblockdata1 != iblockdata2) {
+ this.setBlocksDirty(blockposition, iblockdata1, iblockdata2);
+ }
+
+ if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || (chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING)))) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement
+ this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i);
+ }
+
+ if ((i & 1) != 0) {
+ this.blockUpdated(blockposition, iblockdata1.getBlock());
+ if (!this.isClientSide && iblockdata.hasAnalogOutputSignal()) {
+ this.updateNeighbourForOutputSignal(blockposition, newBlock.getBlock());
+ }
+ }
+
+ if ((i & 16) == 0 && j > 0) {
+ int k = i & -34;
+
+ // CraftBukkit start
+ iblockdata1.updateIndirectNeighbourShapes(this, blockposition, k, j - 1); // Don't call an event for the old block to limit event spam
+ CraftWorld world = ((ServerLevel) this).getWorld();
+ boolean cancelledUpdates = false; // Paper - Fix block place logic
+ if (world != null && ((ServerLevel)this).hasPhysicsEvent) { // Paper - BlockPhysicsEvent
+ BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftBlockData.fromData(iblockdata));
+ this.getCraftServer().getPluginManager().callEvent(event);
+
+ cancelledUpdates = event.isCancelled(); // Paper - Fix block place logic
+ }
+ // CraftBukkit end
+ if (!cancelledUpdates) { // Paper - Fix block place logic
+ iblockdata.updateNeighbourShapes(this, blockposition, k, j - 1);
+ iblockdata.updateIndirectNeighbourShapes(this, blockposition, k, j - 1);
+ } // Paper - Fix block place logic
}
+
+ // CraftBukkit start - SPIGOT-5710
+ if (!this.preventPoiUpdated) {
+ this.onBlockStateChange(blockposition, iblockdata1, iblockdata2);
+ }
+ // CraftBukkit end
}
}
+ // CraftBukkit end
public void onBlockStateChange(BlockPos pos, BlockState oldBlock, BlockState newBlock) {}
@@ -270,15 +605,33 @@
return false;
} else {
FluidState fluid = this.getFluidState(pos);
+ // Paper start - BlockDestroyEvent; while the above setAir method is named same and looks very similar
+ // they are NOT used with same intent and the above should not fire this event. The above method is more of a BlockSetToAirEvent,
+ // it doesn't imply destruction of a block that plays a sound effect / drops an item.
+ boolean playEffect = true;
+ BlockState effectType = iblockdata;
+ int xp = iblockdata.getBlock().getExpDrop(iblockdata, (ServerLevel) this, pos, ItemStack.EMPTY, true);
+ if (com.destroystokyo.paper.event.block.BlockDestroyEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), fluid.createLegacyBlock().createCraftBlockData(), effectType.createCraftBlockData(), xp, drop);
+ if (!event.callEvent()) {
+ return false;
+ }
+ effectType = ((CraftBlockData) event.getEffectBlock()).getState();
+ playEffect = event.playEffect();
+ drop = event.willDrop();
+ xp = event.getExpToDrop();
+ }
+ // Paper end - BlockDestroyEvent
- if (!(iblockdata.getBlock() instanceof BaseFireBlock)) {
- this.levelEvent(2001, pos, Block.getId(iblockdata));
+ if (playEffect && !(effectType.getBlock() instanceof BaseFireBlock)) { // Paper - BlockDestroyEvent
+ this.levelEvent(2001, pos, Block.getId(effectType)); // Paper - BlockDestroyEvent
}
if (drop) {
BlockEntity tileentity = iblockdata.hasBlockEntity() ? this.getBlockEntity(pos) : null;
- Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY);
+ Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
+ iblockdata.getBlock().popExperience((ServerLevel) this, pos, xp, breakingEntity); // Paper - Properly handle xp dropping; custom amount
}
boolean flag1 = this.setBlock(pos, fluid.createLegacyBlock(), 3, maxUpdateDepth);
@@ -340,10 +693,18 @@
@Override
public BlockState getBlockState(BlockPos pos) {
+ // CraftBukkit start - tree generation
+ if (this.captureTreeGeneration) {
+ CraftBlockState previous = this.capturedBlockStates.get(pos); // Paper
+ if (previous != null) {
+ return previous.getHandle();
+ }
+ }
+ // CraftBukkit end
if (this.isOutsideBuildHeight(pos)) {
return Blocks.VOID_AIR.defaultBlockState();
} else {
- LevelChunk chunk = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
+ ChunkAccess chunk = this.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.FULL, true); // Paper - manually inline to reduce hops and avoid unnecessary null check to reduce total byte code size, this should never return null and if it does we will see it the next line but the real stack trace will matter in the chunk engine
return chunk.getBlockState(pos);
}
@@ -446,34 +807,53 @@
this.pendingBlockEntityTickers.clear();
}
- Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
+ // Spigot start
+ // Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
boolean flag = this.tickRateManager().runsNormally();
- while (iterator.hasNext()) {
- TickingBlockEntity tickingblockentity = (TickingBlockEntity) iterator.next();
+ int tilesThisCycle = 0;
+ var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<TickingBlockEntity>(); // Paper - Fix MC-117075; use removeAll
+ toRemove.add(null); // Paper - Fix MC-117075
+ for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters
+ this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0;
+ TickingBlockEntity tickingblockentity = (TickingBlockEntity) this.blockEntityTickers.get(this.tileTickPosition);
+ // Spigot end
if (tickingblockentity.isRemoved()) {
- iterator.remove();
+ // Spigot start
+ tilesThisCycle--;
+ toRemove.add(tickingblockentity); // Paper - Fix MC-117075; use removeAll
+ // Spigot end
} else if (flag && this.shouldTickBlocksAt(tickingblockentity.getPos())) {
tickingblockentity.tick();
}
}
+ this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075
this.tickingBlockEntities = false;
gameprofilerfiller.pop();
+ this.spigotConfig.currentPrimedTnt = 0; // Spigot
}
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
try {
tickConsumer.accept(entity);
} catch (Throwable throwable) {
- CrashReport crashreport = CrashReport.forThrowable(throwable, "Ticking entity");
- CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being ticked");
-
- entity.fillCrashReportCategory(crashreportsystemdetails);
- throw new ReportedException(crashreport);
+ // Paper start - Prevent block entity and entity crashes
+ final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
+ MinecraftServer.LOGGER.error(msg, throwable);
+ getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); // Paper - ServerExceptionEvent
+ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DISCARD);
+ // Paper end - Prevent block entity and entity crashes
}
+ }
+ // Paper start - Option to prevent armor stands from doing entity lookups
+ @Override
+ public boolean noCollision(@Nullable Entity entity, AABB box) {
+ if (entity instanceof net.minecraft.world.entity.decoration.ArmorStand && !entity.level().paperConfig().entities.armorStands.doCollisionEntityLookups) return false;
+ return LevelAccessor.super.noCollision(entity, box);
}
+ // Paper end - Option to prevent armor stands from doing entity lookups
public boolean shouldTickDeath(Entity entity) {
return true;
@@ -510,13 +890,32 @@
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
- return this.isOutsideBuildHeight(pos) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE));
+ // CraftBukkit start
+ return this.getBlockEntity(pos, true);
}
+ @Nullable
+ public BlockEntity getBlockEntity(BlockPos blockposition, boolean validate) {
+ // Paper start - Perf: Optimize capturedTileEntities lookup
+ net.minecraft.world.level.block.entity.BlockEntity blockEntity;
+ if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(blockposition)) != null) {
+ return blockEntity;
+ }
+ // Paper end - Perf: Optimize capturedTileEntities lookup
+ // CraftBukkit end
+ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE));
+ }
+
public void setBlockEntity(BlockEntity blockEntity) {
BlockPos blockposition = blockEntity.getBlockPos();
if (!this.isOutsideBuildHeight(blockposition)) {
+ // CraftBukkit start
+ if (this.captureBlockStates) {
+ this.capturedTileEntities.put(blockposition.immutable(), blockEntity);
+ return;
+ }
+ // CraftBukkit end
this.getChunkAt(blockposition).addAndRegisterBlockEntity(blockEntity);
}
}
@@ -643,7 +1042,7 @@
for (int k = 0; k < j; ++k) {
EnderDragonPart entitycomplexpart = aentitycomplexpart[k];
- T t0 = (Entity) filter.tryCast(entitycomplexpart);
+ T t0 = filter.tryCast(entitycomplexpart); // CraftBukkit - decompile error
if (t0 != null && predicate.test(t0)) {
result.add(t0);
@@ -912,7 +1311,7 @@
public static enum ExplosionInteraction implements StringRepresentable {
- NONE("none"), BLOCK("block"), MOB("mob"), TNT("tnt"), TRIGGER("trigger");
+ NONE("none"), BLOCK("block"), MOB("mob"), TNT("tnt"), TRIGGER("trigger"), STANDARD("standard"); // CraftBukkit - Add STANDARD which will always use Explosion.Effect.DESTROY
public static final Codec<Level.ExplosionInteraction> CODEC = StringRepresentable.fromEnum(Level.ExplosionInteraction::values);
private final String id;

View File

@@ -0,0 +1,9 @@
--- a/net/minecraft/world/level/LevelAccessor.java
+++ b/net/minecraft/world/level/LevelAccessor.java
@@ -101,4 +101,6 @@
default void gameEvent(ResourceKey<GameEvent> event, BlockPos pos, GameEvent.Context emitter) {
this.gameEvent((Holder) this.registryAccess().lookupOrThrow(Registries.GAME_EVENT).getOrThrow(event), pos, emitter);
}
+
+ net.minecraft.server.level.ServerLevel getMinecraftWorld(); // CraftBukkit
}

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/world/level/LevelReader.java
+++ b/net/minecraft/world/level/LevelReader.java
@@ -26,6 +26,9 @@
@Nullable
ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create);
+ @Nullable ChunkAccess getChunkIfLoadedImmediately(int x, int z); // Paper - ifLoaded api (we need this since current impl blocks if the chunk is loading)
+ @Nullable default ChunkAccess getChunkIfLoadedImmediately(BlockPos pos) { return this.getChunkIfLoadedImmediately(pos.getX() >> 4, pos.getZ() >> 4);}
+
@Deprecated
boolean hasChunk(int chunkX, int chunkZ);

View File

@@ -0,0 +1,13 @@
--- a/net/minecraft/world/level/LevelWriter.java
+++ b/net/minecraft/world/level/LevelWriter.java
@@ -28,4 +28,10 @@
default boolean addFreshEntity(Entity entity) {
return false;
}
+
+ // CraftBukkit start
+ default boolean addFreshEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
+ return false;
+ }
+ // CraftBukkit end
}

View File

@@ -0,0 +1,212 @@
--- a/net/minecraft/world/level/NaturalSpawner.java
+++ b/net/minecraft/world/level/NaturalSpawner.java
@@ -47,8 +47,13 @@
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure;
import net.minecraft.world.level.material.FluidState;
+import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
+import org.bukkit.craftbukkit.util.CraftSpawnCategory;
+import org.bukkit.entity.SpawnCategory;
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
+// CraftBukkit end
public final class NaturalSpawner {
@@ -82,6 +87,13 @@
MobCategory enumcreaturetype = entity.getType().getCategory();
if (enumcreaturetype != MobCategory.MISC) {
+ // Paper start - Only count natural spawns
+ if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
+ continue;
+ }
+ // Paper end - Only count natural spawns
BlockPos blockposition = entity.blockPosition();
chunkSource.query(ChunkPos.asLong(blockposition), (chunk) -> {
@@ -107,15 +119,31 @@
return (Biome) chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
}
- public static List<MobCategory> getFilteredSpawningCategories(NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rare) {
+ // CraftBukkit start - add server
+ public static List<MobCategory> getFilteredSpawningCategories(NaturalSpawner.SpawnState spawnercreature_d, boolean flag, boolean flag1, boolean flag2, ServerLevel worldserver) {
+ LevelData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate
+ // CraftBukkit end
List<MobCategory> list = new ArrayList(NaturalSpawner.SPAWNING_CATEGORIES.length);
MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
int i = aenumcreaturetype.length;
for (int j = 0; j < i; ++j) {
MobCategory enumcreaturetype = aenumcreaturetype[j];
+ // CraftBukkit start - Use per-world spawn limits
+ boolean spawnThisTick = true;
+ int limit = enumcreaturetype.getMaxInstancesPerChunk();
+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype);
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ spawnThisTick = worldserver.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % worldserver.ticksPerSpawnCategory.getLong(spawnCategory) == 0;
+ limit = worldserver.getWorld().getSpawnLimit(spawnCategory);
+ }
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rare || !enumcreaturetype.isPersistent()) && info.canSpawnForCategoryGlobal(enumcreaturetype)) {
+ if (!spawnThisTick || limit == 0) {
+ continue;
+ }
+
+ if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) {
+ // CraftBukkit end
list.add(enumcreaturetype);
}
}
@@ -144,6 +172,16 @@
gameprofilerfiller.pop();
}
+ // Paper start - Add mobcaps commands
+ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) {
+ final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category));
+ if (categoryLimit < 1) {
+ return categoryLimit;
+ }
+ return categoryLimit * spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ }
+ // Paper end - Add mobcaps commands
+
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
@@ -164,9 +202,9 @@
StructureManager structuremanager = world.structureManager();
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
int i = pos.getY();
- BlockState iblockdata = chunk.getBlockState(pos);
+ BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
- if (!iblockdata.isRedstoneConductor(chunk, pos)) {
+ if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
int j = 0;
int k = 0;
@@ -195,7 +233,7 @@
if (entityhuman != null) {
double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
- if (NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) {
+ if (world.isLoadedAndInBounds(blockposition_mutableblockposition) && NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) { // Paper - don't load chunks for mob spawn
if (biomesettingsmobs_c == null) {
Optional<MobSpawnSettings.SpawnerData> optional = NaturalSpawner.getRandomSpawnMobAt(world, structuremanager, chunkgenerator, group, world.random, blockposition_mutableblockposition);
@@ -207,7 +245,13 @@
j1 = biomesettingsmobs_c.minCount + world.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount);
}
- if (NaturalSpawner.isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
+ // Paper start - PreCreatureSpawnEvent
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
+ if (doSpawning == PreSpawnStatus.ABORT) {
+ return;
+ }
+ if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
+ // Paper end - PreCreatureSpawnEvent
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
if (entityinsentient == null) {
@@ -217,10 +261,15 @@
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
if (NaturalSpawner.isValidPositionForMob(world, entityinsentient, d2)) {
groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.NATURAL, groupdataentity);
- ++j;
- ++k1;
- world.addFreshEntityWithPassengers(entityinsentient);
- runner.run(entityinsentient, chunk);
+ // CraftBukkit start
+ // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs.
+ world.addFreshEntityWithPassengers(entityinsentient, (entityinsentient instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) entityinsentient.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL);
+ if (!entityinsentient.isRemoved()) {
+ ++j;
+ ++k1;
+ runner.run(entityinsentient, chunk);
+ }
+ // CraftBukkit end
if (j >= entityinsentient.getMaxSpawnClusterSize()) {
return;
}
@@ -250,10 +299,31 @@
return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos));
}
- private static boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
+ // Paper start - PreCreatureSpawnEvent
+ private enum PreSpawnStatus {
+ FAIL,
+ SUCCESS,
+ CANCELLED,
+ ABORT
+ }
+ private static PreSpawnStatus isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
+ // Paper end - PreCreatureSpawnEvent
EntityType<?> entitytypes = spawnEntry.type;
- return entitytypes.getCategory() == MobCategory.MISC ? false : (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance()) ? false : (entitytypes.canSummon() && NaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos) ? (!SpawnPlacements.isSpawnPositionOk(entitytypes, world, pos) ? false : (!SpawnPlacements.checkSpawnRules(entitytypes, world, EntitySpawnReason.NATURAL, pos, world.random) ? false : world.noCollision(entitytypes.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)))) : false));
+ // Paper start - PreCreatureSpawnEvent
+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+ io.papermc.paper.util.MCUtil.toLocation(world, pos),
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entitytypes), SpawnReason.NATURAL
+ );
+ if (!event.callEvent()) {
+ if (event.shouldAbortSpawn()) {
+ return PreSpawnStatus.ABORT;
+ }
+ return PreSpawnStatus.CANCELLED;
+ }
+ // Paper end - PreCreatureSpawnEvent
+
+ return entitytypes.getCategory() == MobCategory.MISC ? PreSpawnStatus.FAIL : (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance()) ? PreSpawnStatus.FAIL : (entitytypes.canSummon() && NaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos) ? (!SpawnPlacements.isSpawnPositionOk(entitytypes, world, pos) ? PreSpawnStatus.FAIL : (!SpawnPlacements.checkSpawnRules(entitytypes, world, EntitySpawnReason.NATURAL, pos, world.random) ? PreSpawnStatus.FAIL : world.noCollision(entitytypes.getSpawnAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)) ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL)) : PreSpawnStatus.FAIL)); // Paper - PreCreatureSpawnEvent
}
@Nullable
@@ -268,6 +338,7 @@
NaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(type));
} catch (Exception exception) {
NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
}
return null;
@@ -356,6 +427,7 @@
entity = biomesettingsmobs_c.type.create(world.getLevel(), EntitySpawnReason.NATURAL);
} catch (Exception exception) {
NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
continue;
}
@@ -369,7 +441,7 @@
if (entityinsentient.checkSpawnRules(world, EntitySpawnReason.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(world)) {
groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, groupdataentity);
- world.addFreshEntityWithPassengers(entityinsentient);
+ world.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit
flag = true;
}
}
@@ -482,10 +554,12 @@
return this.unmodifiableMobCategoryCounts;
}
- boolean canSpawnForCategoryGlobal(MobCategory group) {
- int i = group.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit start
+ boolean canSpawnForCategoryGlobal(MobCategory enumcreaturetype, int limit) {
+ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ // CraftBukkit end
- return this.mobCategoryCounts.getInt(group) < i;
+ return this.mobCategoryCounts.getInt(enumcreaturetype) < i;
}
boolean canSpawnForCategoryLocal(MobCategory group, ChunkPos chunkPos) {

View File

@@ -0,0 +1,51 @@
--- a/net/minecraft/world/level/PathNavigationRegion.java
+++ b/net/minecraft/world/level/PathNavigationRegion.java
@@ -8,6 +8,7 @@
import net.minecraft.core.Holder;
import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.Registries;
+import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
@@ -66,7 +67,7 @@
private ChunkAccess getChunk(int chunkX, int chunkZ) {
int i = chunkX - this.centerX;
int j = chunkZ - this.centerZ;
- if (i >= 0 && i < this.chunks.length && j >= 0 && j < this.chunks[i].length) {
+ if (i >= 0 && i < this.chunks.length && j >= 0 && j < this.chunks[i].length) { // Paper - if this changes, update getChunkIfLoaded below
ChunkAccess chunkAccess = this.chunks[i][j];
return (ChunkAccess)(chunkAccess != null ? chunkAccess : new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), this.plains.get()));
} else {
@@ -74,7 +75,31 @@
}
}
+ // Paper start - if loaded util
+ private @Nullable ChunkAccess getChunkIfLoaded(int x, int z) {
+ // Based on getChunk(int, int)
+ int xx = x - this.centerX;
+ int zz = z - this.centerZ;
+
+ if (xx >= 0 && xx < this.chunks.length && zz >= 0 && zz < this.chunks[xx].length) {
+ return this.chunks[xx][zz];
+ }
+ return null;
+ }
@Override
+ public final FluidState getFluidIfLoaded(BlockPos blockposition) {
+ ChunkAccess chunk = getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+ return chunk == null ? null : chunk.getFluidState(blockposition);
+ }
+
+ @Override
+ public final BlockState getBlockStateIfLoaded(BlockPos blockposition) {
+ ChunkAccess chunk = getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+ return chunk == null ? null : chunk.getBlockState(blockposition);
+ }
+ // Paper end
+
+ @Override
public WorldBorder getWorldBorder() {
return this.level.getWorldBorder();
}

View File

@@ -0,0 +1,342 @@
--- a/net/minecraft/world/level/ServerExplosion.java
+++ b/net/minecraft/world/level/ServerExplosion.java
@@ -22,18 +22,27 @@
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.boss.EnderDragonPart;
+import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.item.PrimedTnt;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.BaseFireBlock;
import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.util.CraftLocation;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.Location;
+import org.bukkit.event.block.BlockExplodeEvent;
+// CraftBukkit end
public class ServerExplosion implements Explosion {
@@ -50,16 +59,22 @@
private final DamageSource damageSource;
private final ExplosionDamageCalculator damageCalculator;
private final Map<Player, Vec3> hitPlayers = new HashMap();
+ // CraftBukkit - add field
+ public boolean wasCanceled = false;
+ public float yield;
+ // CraftBukkit end
+ public boolean excludeSourceFromDamage = true; // Paper - Allow explosions to damage source
public ServerExplosion(ServerLevel world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, Vec3 pos, float power, boolean createFire, Explosion.BlockInteraction destructionType) {
this.level = world;
this.source = entity;
- this.radius = power;
+ this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values
this.center = pos;
this.fire = createFire;
this.blockInteraction = destructionType;
this.damageSource = damageSource == null ? world.damageSources().explosion(this) : damageSource;
this.damageCalculator = behavior == null ? this.makeDamageCalculator(entity) : behavior;
+ this.yield = this.blockInteraction == Explosion.BlockInteraction.DESTROY_WITH_DECAY ? 1.0F / this.radius : 1.0F; // CraftBukkit
}
private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity entity) {
@@ -135,7 +150,8 @@
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
BlockPos blockposition = BlockPos.containing(d4, d5, d6);
BlockState iblockdata = this.level.getBlockState(blockposition);
- FluidState fluid = this.level.getFluidState(blockposition);
+ if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
+ FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions
if (!this.level.isInWorldBounds(blockposition)) {
break;
@@ -149,6 +165,15 @@
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockposition, iblockdata, f)) {
set.add(blockposition);
+ // Paper start - prevent headless pistons from forming
+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowHeadlessPistons && iblockdata.getBlock() == Blocks.MOVING_PISTON) {
+ net.minecraft.world.level.block.entity.BlockEntity extension = this.level.getBlockEntity(blockposition);
+ if (extension instanceof net.minecraft.world.level.block.piston.PistonMovingBlockEntity blockEntity && blockEntity.isSourcePiston()) {
+ net.minecraft.core.Direction direction = iblockdata.getValue(net.minecraft.world.level.block.piston.PistonHeadBlock.FACING);
+ set.add(blockposition.relative(direction.getOpposite()));
+ }
+ }
+ // Paper end - prevent headless pistons from forming
}
d4 += d0 * 0.30000001192092896D;
@@ -171,7 +196,7 @@
int l = Mth.floor(this.center.y + (double) f + 1.0D);
int i1 = Mth.floor(this.center.z - (double) f - 1.0D);
int j1 = Mth.floor(this.center.z + (double) f + 1.0D);
- List<Entity> list = this.level.getEntities(this.source, new AABB((double) i, (double) k, (double) i1, (double) j, (double) l, (double) j1));
+ List<Entity> list = this.level.getEntities(excludeSourceFromDamage ? this.source : null, new AABB((double) i, (double) k, (double) i1, (double) j, (double) l, (double) j1), (com.google.common.base.Predicate<Entity>) entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
@@ -192,10 +217,38 @@
d3 /= d4;
boolean flag = this.damageCalculator.shouldDamageEntity(this, entity);
float f1 = this.damageCalculator.getKnockbackMultiplier(entity);
- float f2 = !flag && f1 == 0.0F ? 0.0F : ServerExplosion.getSeenPercent(this.center, entity);
+ float f2 = !flag && f1 == 0.0F ? 0.0F : this.getBlockDensity(this.center, entity); // Paper - Optimize explosions
if (flag) {
- entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
+ // CraftBukkit start
+
+ // Special case ender dragon only give knockback if no damage is cancelled
+ // Thinks to note:
+ // - Setting a velocity to a ComplexEntityPart is ignored (and therefore not needed)
+ // - Damaging ComplexEntityPart while forward the damage to EntityEnderDragon
+ // - Damaging EntityEnderDragon does nothing
+ // - EntityEnderDragon hitbock always covers the other parts and is therefore always present
+ if (entity instanceof EnderDragonPart) {
+ continue;
+ }
+
+ entity.lastDamageCancelled = false;
+
+ if (entity instanceof EnderDragon) {
+ for (EnderDragonPart entityComplexPart : ((EnderDragon) entity).subEntities) {
+ // Calculate damage separately for each EntityComplexPart
+ if (list.contains(entityComplexPart)) {
+ entityComplexPart.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
+ }
+ }
+ } else {
+ entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
+ }
+
+ if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled
+ continue;
+ }
+ // CraftBukkit end
}
double d5 = (1.0D - d0) * (double) f2 * (double) f1;
@@ -204,7 +257,7 @@
if (entity instanceof LivingEntity) {
LivingEntity entityliving = (LivingEntity) entity;
- d6 = d5 * (1.0D - entityliving.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
+ d6 = entity instanceof Player && this.level.paperConfig().environment.disableExplosionKnockback ? 0 : d5 * (1.0D - entityliving.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE)); // Paper
} else {
d6 = d5;
}
@@ -214,11 +267,19 @@
d3 *= d6;
Vec3 vec3d = new Vec3(d1, d2, d3);
+ // CraftBukkit start - Call EntityKnockbackEvent
+ if (entity instanceof LivingEntity) {
+ // Paper start - knockback events
+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) entity.getBukkitEntity(), this.source, this.damageSource.getEntity() != null ? this.damageSource.getEntity() : this.source, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.EXPLOSION, d6, vec3d);
+ vec3d = event.isCancelled() ? Vec3.ZERO : org.bukkit.craftbukkit.util.CraftVector.toNMS(event.getKnockback());
+ // Paper end - knockback events
+ }
+ // CraftBukkit end
entity.push(vec3d);
if (entity instanceof Player) {
Player entityhuman = (Player) entity;
- if (!entityhuman.isSpectator() && (!entityhuman.isCreative() || !entityhuman.getAbilities().flying)) {
+ if (!entityhuman.isSpectator() && (!entityhuman.isCreative() || !entityhuman.getAbilities().flying) && !level.paperConfig().environment.disableExplosionKnockback) { // Paper - Option to disable explosion knockback
this.hitPlayers.put(entityhuman, vec3d);
}
}
@@ -235,10 +296,62 @@
List<ServerExplosion.StackCollector> list1 = new ArrayList();
Util.shuffle(positions, this.level.random);
+ // CraftBukkit start
+ org.bukkit.World bworld = this.level.getWorld();
+ Location location = CraftLocation.toBukkit(this.center, bworld);
+
+ List<org.bukkit.block.Block> blockList = new ObjectArrayList<>();
+ for (int i1 = positions.size() - 1; i1 >= 0; i1--) {
+ BlockPos cpos = positions.get(i1);
+ org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
+ if (!bblock.getType().isAir()) {
+ blockList.add(bblock);
+ }
+ }
+
+ List<org.bukkit.block.Block> bukkitBlocks;
+
+ if (this.source != null) {
+ EntityExplodeEvent event = CraftEventFactory.callEntityExplodeEvent(this.source, blockList, this.yield, this.getBlockInteraction());
+ this.wasCanceled = event.isCancelled();
+ bukkitBlocks = event.blockList();
+ this.yield = event.getYield();
+ } else {
+ org.bukkit.block.Block block = location.getBlock();
+ org.bukkit.block.BlockState blockState = (this.damageSource.getDirectBlockState() != null) ? this.damageSource.getDirectBlockState() : block.getState();
+ BlockExplodeEvent event = CraftEventFactory.callBlockExplodeEvent(block, blockState, blockList, this.yield, this.getBlockInteraction());
+ this.wasCanceled = event.isCancelled();
+ bukkitBlocks = event.blockList();
+ this.yield = event.getYield();
+ }
+
+ positions.clear();
+
+ for (org.bukkit.block.Block bblock : bukkitBlocks) {
+ BlockPos coords = new BlockPos(bblock.getX(), bblock.getY(), bblock.getZ());
+ positions.add(coords);
+ }
+
+ if (this.wasCanceled) {
+ return;
+ }
+ // CraftBukkit end
Iterator iterator = positions.iterator();
while (iterator.hasNext()) {
BlockPos blockposition = (BlockPos) iterator.next();
+ // CraftBukkit start - TNTPrimeEvent
+ BlockState iblockdata = this.level.getBlockState(blockposition);
+ Block block = iblockdata.getBlock();
+ if (block instanceof net.minecraft.world.level.block.TntBlock) {
+ Entity sourceEntity = this.source == null ? null : this.source;
+ BlockPos sourceBlock = sourceEntity == null ? BlockPos.containing(this.center) : null;
+ if (!CraftEventFactory.callTNTPrimeEvent(this.level, blockposition, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.EXPLOSION, sourceEntity, sourceBlock)) {
+ this.level.sendBlockUpdated(blockposition, Blocks.AIR.defaultBlockState(), iblockdata, 3); // Update the block on the client
+ continue;
+ }
+ }
+ // CraftBukkit end
this.level.getBlockState(blockposition).onExplosionHit(this.level, blockposition, this, (itemstack, blockposition1) -> {
ServerExplosion.addOrAppendStack(list1, itemstack, blockposition1);
@@ -262,13 +375,22 @@
BlockPos blockposition = (BlockPos) iterator.next();
if (this.level.random.nextInt(3) == 0 && this.level.getBlockState(blockposition).isAir() && this.level.getBlockState(blockposition.below()).isSolidRender()) {
- this.level.setBlockAndUpdate(blockposition, BaseFireBlock.getState(this.level, blockposition));
+ // CraftBukkit start - Ignition by explosion
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockposition, this).isCancelled()) {
+ this.level.setBlockAndUpdate(blockposition, BaseFireBlock.getState(this.level, blockposition));
+ }
+ // CraftBukkit end
}
}
}
public void explode() {
+ // CraftBukkit start
+ if (this.radius < 0.1F) {
+ return;
+ }
+ // CraftBukkit end
this.level.gameEvent(this.source, (Holder) GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
@@ -288,6 +410,7 @@
}
private static void addOrAppendStack(List<ServerExplosion.StackCollector> droppedItemsOut, ItemStack item, BlockPos pos) {
+ if (item.isEmpty()) return; // CraftBukkit - SPIGOT-5425
Iterator iterator = droppedItemsOut.iterator();
do {
@@ -372,4 +495,85 @@
}
}
+
+ // Paper start - Optimize explosions
+ private float getBlockDensity(Vec3 vec3d, Entity entity) {
+ if (!this.level.paperConfig().environment.optimizeExplosions) {
+ return getSeenPercent(vec3d, entity);
+ }
+ CacheKey key = new CacheKey(this, entity.getBoundingBox());
+ Float blockDensity = this.level.explosionDensityCache.get(key);
+ if (blockDensity == null) {
+ blockDensity = getSeenPercent(vec3d, entity);
+ this.level.explosionDensityCache.put(key, blockDensity);
+ }
+
+ return blockDensity;
+ }
+
+ static class CacheKey {
+ private final Level world;
+ private final double posX, posY, posZ;
+ private final double minX, minY, minZ;
+ private final double maxX, maxY, maxZ;
+
+ public CacheKey(Explosion explosion, AABB aabb) {
+ this.world = explosion.level();
+ this.posX = explosion.center().x;
+ this.posY = explosion.center().y;
+ this.posZ = explosion.center().z;
+ this.minX = aabb.minX;
+ this.minY = aabb.minY;
+ this.minZ = aabb.minZ;
+ this.maxX = aabb.maxX;
+ this.maxY = aabb.maxY;
+ this.maxZ = aabb.maxZ;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CacheKey cacheKey = (CacheKey) o;
+
+ if (Double.compare(cacheKey.posX, posX) != 0) return false;
+ if (Double.compare(cacheKey.posY, posY) != 0) return false;
+ if (Double.compare(cacheKey.posZ, posZ) != 0) return false;
+ if (Double.compare(cacheKey.minX, minX) != 0) return false;
+ if (Double.compare(cacheKey.minY, minY) != 0) return false;
+ if (Double.compare(cacheKey.minZ, minZ) != 0) return false;
+ if (Double.compare(cacheKey.maxX, maxX) != 0) return false;
+ if (Double.compare(cacheKey.maxY, maxY) != 0) return false;
+ if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false;
+ return world.equals(cacheKey.world);
+ }
+
+ @Override
+ public int hashCode() {
+ int result;
+ long temp;
+ result = world.hashCode();
+ temp = Double.doubleToLongBits(posX);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(posY);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(posZ);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(minX);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(minY);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(minZ);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(maxX);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(maxY);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(maxZ);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+ }
+ // Paper end
}

View File

@@ -0,0 +1,21 @@
--- a/net/minecraft/world/level/ServerLevelAccessor.java
+++ b/net/minecraft/world/level/ServerLevelAccessor.java
@@ -8,6 +8,17 @@
ServerLevel getLevel();
default void addFreshEntityWithPassengers(Entity entity) {
- entity.getSelfAndPassengers().forEach(this::addFreshEntity);
+ // CraftBukkit start
+ this.addFreshEntityWithPassengers(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
}
+
+ default void addFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
+ entity.getSelfAndPassengers().forEach((e) -> this.addFreshEntity(e, reason));
+ }
+
+ @Override
+ default ServerLevel getMinecraftWorld() {
+ return this.getLevel();
+ }
+ // CraftBukkit end
}

View File

@@ -0,0 +1,38 @@
--- a/net/minecraft/world/level/StructureManager.java
+++ b/net/minecraft/world/level/StructureManager.java
@@ -48,7 +48,12 @@
}
public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate) {
- Map<Structure, LongSet> map = this.level.getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences();
+ // Paper start - Fix swamp hut cat generation deadlock
+ return this.startsForStructure(pos, predicate, null);
+ }
+ public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate, @Nullable ServerLevelAccessor levelAccessor) {
+ Map<Structure, LongSet> map = (levelAccessor == null ? this.level : levelAccessor).getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences();
+ // Paper end - Fix swamp hut cat generation deadlock
Builder<StructureStart> builder = ImmutableList.builder();
for (Entry<Structure, LongSet> entry : map.entrySet()) {
@@ -116,10 +121,20 @@
}
public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate) {
+ // Paper start - Fix swamp hut cat generation deadlock
+ return this.getStructureWithPieceAt(pos, predicate, null);
+ }
+
+ public StructureStart getStructureWithPieceAt(BlockPos pos, TagKey<Structure> tag, @Nullable ServerLevelAccessor levelAccessor) {
+ return this.getStructureWithPieceAt(pos, structure -> structure.is(tag), levelAccessor);
+ }
+
+ public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate, @Nullable ServerLevelAccessor levelAccessor) {
+ // Paper end - Fix swamp hut cat generation deadlock
Registry<Structure> registry = this.registryAccess().lookupOrThrow(Registries.STRUCTURE);
for (StructureStart structureStart : this.startsForStructure(
- new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false)
+ new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false), levelAccessor // Paper - Fix swamp hut cat generation deadlock
)) {
if (this.structureHasPieceAt(pos, structureStart)) {
return structureStart;

View File

@@ -0,0 +1,44 @@
--- a/net/minecraft/world/level/biome/MobSpawnSettings.java
+++ b/net/minecraft/world/level/biome/MobSpawnSettings.java
@@ -75,8 +75,40 @@
}
public static class Builder {
+ // Paper start - Perf: keep track of data in a pair set to give O(1) contains calls - we have to hook removals incase plugins mess with it
+ public static class MobList extends java.util.ArrayList<MobSpawnSettings.SpawnerData> {
+ java.util.Set<MobSpawnSettings.SpawnerData> biomes = new java.util.HashSet<>();
+
+ @Override
+ public boolean contains(Object o) {
+ return biomes.contains(o);
+ }
+
+ @Override
+ public boolean add(MobSpawnSettings.SpawnerData BiomeSettingsMobs) {
+ biomes.add(BiomeSettingsMobs);
+ return super.add(BiomeSettingsMobs);
+ }
+
+ @Override
+ public MobSpawnSettings.SpawnerData remove(int index) {
+ MobSpawnSettings.SpawnerData removed = super.remove(index);
+ if (removed != null) {
+ biomes.remove(removed);
+ }
+ return removed;
+ }
+
+ @Override
+ public void clear() {
+ biomes.clear();
+ super.clear();
+ }
+ }
+ // use toImmutableEnumMap collector
private final Map<MobCategory, List<MobSpawnSettings.SpawnerData>> spawners = Stream.of(MobCategory.values())
- .collect(ImmutableMap.toImmutableMap(mobCategory -> (MobCategory)mobCategory, mobCategory -> Lists.newArrayList()));
+ .collect(Maps.toImmutableEnumMap(mobCategory -> (MobCategory)mobCategory, mobCategory -> new MobList())); // Use MobList instead of ArrayList
+ // Paper end - Perf: keep track of data in a pair set to give O(1) contains calls
private final Map<EntityType<?>, MobSpawnSettings.MobSpawnCost> mobSpawnCosts = Maps.newLinkedHashMap();
private float creatureGenerationProbability = 0.1F;

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/AbstractCandleBlock.java
+++ b/net/minecraft/world/level/block/AbstractCandleBlock.java
@@ -47,6 +47,11 @@
@Override
protected void onProjectileHit(Level world, BlockState state, BlockHitResult hit, Projectile projectile) {
if (!world.isClientSide && projectile.isOnFire() && this.canBeLit(state)) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, hit.getBlockPos(), projectile).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
AbstractCandleBlock.setLit(world, state, hit.getBlockPos(), true);
}

View File

@@ -0,0 +1,11 @@
--- a/net/minecraft/world/level/block/AbstractCauldronBlock.java
+++ b/net/minecraft/world/level/block/AbstractCauldronBlock.java
@@ -56,7 +56,7 @@
@Override
protected InteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
CauldronInteraction cauldronInteraction = this.interactions.map().get(stack.getItem());
- return cauldronInteraction.interact(state, world, pos, player, hand, stack);
+ return cauldronInteraction.interact(state, world, pos, player, hand, stack, hit.getDirection()); // Paper - pass hit direction
}
@Override

View File

@@ -0,0 +1,13 @@
--- a/net/minecraft/world/level/block/AnvilBlock.java
+++ b/net/minecraft/world/level/block/AnvilBlock.java
@@ -62,8 +62,9 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_ANVIL);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;

View File

@@ -0,0 +1,19 @@
--- a/net/minecraft/world/level/block/BambooSaplingBlock.java
+++ b/net/minecraft/world/level/block/BambooSaplingBlock.java
@@ -45,7 +45,7 @@
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
- if (random.nextInt(3) == 0 && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) {
+ if (random.nextFloat() < (world.spigotConfig.bambooModifier / (100.0f * 3)) && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution
this.growBamboo(world, pos);
}
@@ -87,6 +87,6 @@
}
protected void growBamboo(Level world, BlockPos pos) {
- world.setBlock(pos.above(), (BlockState) Blocks.BAMBOO.defaultBlockState().setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), 3);
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, pos.above(), (BlockState) Blocks.BAMBOO.defaultBlockState().setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), 3); // CraftBukkit - BlockSpreadEvent
}
}

View File

@@ -0,0 +1,89 @@
--- a/net/minecraft/world/level/block/BambooStalkBlock.java
+++ b/net/minecraft/world/level/block/BambooStalkBlock.java
@@ -134,10 +134,10 @@
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if ((Integer) state.getValue(BambooStalkBlock.STAGE) == 0) {
- if (random.nextInt(3) == 0 && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) {
+ if (random.nextFloat() < (world.spigotConfig.bambooModifier / (100.0f * 3)) && world.isEmptyBlock(pos.above()) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution
int i = this.getHeightBelowUpToMax(world, pos) + 1;
- if (i < 16) {
+ if (i < world.paperConfig().maxGrowthHeight.bamboo.max) { // Paper - Configurable cactus/bamboo/reed growth height
this.growBamboo(state, world, pos, random, i);
}
}
@@ -164,7 +164,7 @@
int i = this.getHeightAboveUpToMax(world, pos);
int j = this.getHeightBelowUpToMax(world, pos);
- return i + j + 1 < 16 && (Integer) world.getBlockState(pos.above(i)).getValue(BambooStalkBlock.STAGE) != 1;
+ return i + j + 1 < ((Level) world).paperConfig().maxGrowthHeight.bamboo.max && (Integer) world.getBlockState(pos.above(i)).getValue(BambooStalkBlock.STAGE) != 1; // Paper - Configurable cactus/bamboo/reed growth height
}
@Override
@@ -183,7 +183,7 @@
BlockPos blockposition1 = pos.above(i);
BlockState iblockdata1 = world.getBlockState(blockposition1);
- if (k >= 16 || (Integer) iblockdata1.getValue(BambooStalkBlock.STAGE) == 1 || !world.isEmptyBlock(blockposition1.above())) {
+ if (k >= world.paperConfig().maxGrowthHeight.bamboo.max || !iblockdata1.is(Blocks.BAMBOO) || (Integer) iblockdata1.getValue(BambooStalkBlock.STAGE) == 1 || !world.isEmptyBlock(blockposition1.above())) { // CraftBukkit - If the BlockSpreadEvent was cancelled, we have no bamboo here // Paper - Configurable cactus/bamboo/reed growth height
return;
}
@@ -204,14 +204,18 @@
BlockPos blockposition1 = pos.below(2);
BlockState iblockdata2 = world.getBlockState(blockposition1);
BambooLeaves blockpropertybamboosize = BambooLeaves.NONE;
+ boolean shouldUpdateOthers = false; // CraftBukkit
if (height >= 1) {
if (iblockdata1.is(Blocks.BAMBOO) && iblockdata1.getValue(BambooStalkBlock.LEAVES) != BambooLeaves.NONE) {
if (iblockdata1.is(Blocks.BAMBOO) && iblockdata1.getValue(BambooStalkBlock.LEAVES) != BambooLeaves.NONE) {
blockpropertybamboosize = BambooLeaves.LARGE;
if (iblockdata2.is(Blocks.BAMBOO)) {
- world.setBlock(pos.below(), (BlockState) iblockdata1.setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), 3);
- world.setBlock(blockposition1, (BlockState) iblockdata2.setValue(BambooStalkBlock.LEAVES, BambooLeaves.NONE), 3);
+ // CraftBukkit start - moved down
+ // world.setBlock(blockposition.below(), (IBlockData) iblockdata1.setValue(BlockBamboo.LEAVES, BlockPropertyBambooSize.SMALL), 3);
+ // world.setBlock(blockposition1, (IBlockData) iblockdata2.setValue(BlockBamboo.LEAVES, BlockPropertyBambooSize.NONE), 3);
+ shouldUpdateOthers = true;
+ // CraftBukkit end
}
}
} else {
@@ -220,15 +224,22 @@
}
int j = (Integer) state.getValue(BambooStalkBlock.AGE) != 1 && !iblockdata2.is(Blocks.BAMBOO) ? 0 : 1;
- int k = (height < 11 || random.nextFloat() >= 0.25F) && height != 15 ? 0 : 1;
+ int k = (height < world.paperConfig().maxGrowthHeight.bamboo.min || random.nextFloat() >= 0.25F) && height != (world.paperConfig().maxGrowthHeight.bamboo.max - 1) ? 0 : 1; // Paper - Configurable cactus/bamboo/reed growth height
- world.setBlock(pos.above(), (BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(BambooStalkBlock.AGE, j)).setValue(BambooStalkBlock.LEAVES, blockpropertybamboosize)).setValue(BambooStalkBlock.STAGE, k), 3);
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, pos.above(), (BlockState) ((BlockState) ((BlockState) this.defaultBlockState().setValue(BambooStalkBlock.AGE, j)).setValue(BambooStalkBlock.LEAVES, blockpropertybamboosize)).setValue(BambooStalkBlock.STAGE, k), 3)) {
+ if (shouldUpdateOthers) {
+ world.setBlock(pos.below(), (BlockState) iblockdata1.setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), 3);
+ world.setBlock(blockposition1, (BlockState) iblockdata2.setValue(BambooStalkBlock.LEAVES, BambooLeaves.NONE), 3);
+ }
+ }
+ // CraftBukkit end
}
protected int getHeightAboveUpToMax(BlockGetter world, BlockPos pos) {
int i;
- for (i = 0; i < 16 && world.getBlockState(pos.above(i + 1)).is(Blocks.BAMBOO); ++i) {
+ for (i = 0; i < ((Level) world).paperConfig().maxGrowthHeight.bamboo.max && world.getBlockState(pos.above(i + 1)).is(Blocks.BAMBOO); ++i) { // Paper - Configurable cactus/bamboo/reed growth height
;
}
@@ -238,7 +249,7 @@
protected int getHeightBelowUpToMax(BlockGetter world, BlockPos pos) {
int i;
- for (i = 0; i < 16 && world.getBlockState(pos.below(i + 1)).is(Blocks.BAMBOO); ++i) {
+ for (i = 0; i < ((Level) world).paperConfig().maxGrowthHeight.bamboo.max && world.getBlockState(pos.below(i + 1)).is(Blocks.BAMBOO); ++i) { // Paper - Configurable cactus/bamboo/reed growth height
;
}

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/world/level/block/BarrelBlock.java
+++ b/net/minecraft/world/level/block/BarrelBlock.java
@@ -41,8 +41,7 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (world instanceof ServerLevel serverLevel && world.getBlockEntity(pos) instanceof BarrelBlockEntity barrelBlockEntity) {
- player.openMenu(barrelBlockEntity);
+ if (world instanceof ServerLevel serverLevel && world.getBlockEntity(pos) instanceof BarrelBlockEntity barrelBlockEntity && player.openMenu(barrelBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.OPEN_BARREL);
PiglinAi.angerNearbyPiglins(serverLevel, player, true);
}

View File

@@ -0,0 +1,85 @@
--- a/net/minecraft/world/level/block/BaseFireBlock.java
+++ b/net/minecraft/world/level/block/BaseFireBlock.java
@@ -12,6 +12,7 @@
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
+import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockBehaviour;
@@ -127,6 +128,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (!entity.fireImmune()) {
if (entity.getRemainingFireTicks() < 0) {
entity.setRemainingFireTicks(entity.getRemainingFireTicks() + 1);
@@ -137,7 +139,18 @@
}
if (entity.getRemainingFireTicks() >= 0) {
- entity.igniteForSeconds(8.0F);
+ // CraftBukkit start
+ org.bukkit.event.entity.EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustByBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, pos), entity.getBukkitEntity(), 8.0F);
+ world.getCraftServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ entity.igniteForSeconds(event.getDuration(), false);
+ // Paper start - fix EntityCombustEvent cancellation
+ } else {
+ entity.setRemainingFireTicks(entity.getRemainingFireTicks() - 1);
+ // Paper end - fix EntityCombustEvent cancellation
+ }
+ // CraftBukkit end
}
}
@@ -146,26 +159,26 @@
}
@Override
- protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
- if (!oldState.is(state.getBlock())) {
+ protected void onPlace(BlockState iblockdata, Level world, BlockPos blockposition, BlockState iblockdata1, boolean flag, UseOnContext context) { // CraftBukkit - context
+ if (!iblockdata1.is(iblockdata.getBlock())) {
if (BaseFireBlock.inPortalDimension(world)) {
- Optional<PortalShape> optional = PortalShape.findEmptyPortalShape(world, pos, Direction.Axis.X);
+ Optional<PortalShape> optional = PortalShape.findEmptyPortalShape(world, blockposition, Direction.Axis.X);
if (optional.isPresent()) {
- ((PortalShape) optional.get()).createPortalBlocks(world);
+ ((PortalShape) optional.get()).createPortalBlocks(world, (context == null) ? null : context.getPlayer()); // CraftBukkit - player
return;
}
}
- if (!state.canSurvive(world, pos)) {
- world.removeBlock(pos, false);
+ if (!iblockdata.canSurvive(world, blockposition)) {
+ this.fireExtinguished(world, blockposition); // CraftBukkit - fuel block broke
}
}
}
private static boolean inPortalDimension(Level world) {
- return world.dimension() == Level.OVERWORLD || world.dimension() == Level.NETHER;
+ return world.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.OVERWORLD || world.getTypeKey() == net.minecraft.world.level.dimension.LevelStem.NETHER; // CraftBukkit - getTypeKey()
}
@Override
@@ -213,4 +226,12 @@
}
}
}
+
+ // CraftBukkit start
+ protected void fireExtinguished(net.minecraft.world.level.LevelAccessor world, BlockPos position) {
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, position, Blocks.AIR.defaultBlockState()).isCancelled()) {
+ world.removeBlock(position, false);
+ }
+ }
+ // CraftBukkit end
}

View File

@@ -0,0 +1,56 @@
--- a/net/minecraft/world/level/block/BasePressurePlateBlock.java
+++ b/net/minecraft/world/level/block/BasePressurePlateBlock.java
@@ -22,6 +22,7 @@
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
public abstract class BasePressurePlateBlock extends Block {
@@ -76,6 +77,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (!world.isClientSide) {
int i = this.getSignalForState(state);
@@ -91,6 +93,19 @@
boolean flag = output > 0;
boolean flag1 = j > 0;
+ // CraftBukkit start - Interact Pressure Plate
+ org.bukkit.World bworld = world.getWorld();
+ org.bukkit.plugin.PluginManager manager = world.getCraftServer().getPluginManager();
+
+ if (flag != flag1) {
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), output, j);
+ manager.callEvent(eventRedstone);
+
+ flag1 = eventRedstone.getNewCurrent() > 0;
+ j = eventRedstone.getNewCurrent();
+ }
+ // CraftBukkit end
+
if (output != j) {
BlockState iblockdata1 = this.setSignalForState(state, j);
@@ -145,9 +160,15 @@
}
protected static int getEntityCount(Level world, AABB box, Class<? extends Entity> entityClass) {
- return world.getEntitiesOfClass(entityClass, box, EntitySelector.NO_SPECTATORS.and((entity) -> {
+ // CraftBukkit start
+ return BasePressurePlateBlock.getEntities(world, box, entityClass).size();
+ }
+
+ protected static <T extends Entity> java.util.List<T> getEntities(Level world, AABB axisalignedbb, Class<T> oclass) {
+ // CraftBukkit end
+ return world.getEntitiesOfClass(oclass, axisalignedbb, EntitySelector.NO_SPECTATORS.and((entity) -> {
return !entity.isIgnoringBlockTriggers();
- })).size();
+ })); // CraftBukkit
}
protected abstract int getSignalStrength(Level world, BlockPos pos);

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/BaseRailBlock.java
+++ b/net/minecraft/world/level/block/BaseRailBlock.java
@@ -71,6 +71,7 @@
state = this.updateDir(world, pos, state, true);
if (this.isStraight) {
world.neighborChanged(state, pos, this, null, notify);
+ state = world.getBlockState(pos); // Paper - Fix some rails connecting improperly
}
return state;

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/world/level/block/BeaconBlock.java
+++ b/net/minecraft/world/level/block/BeaconBlock.java
@@ -46,8 +46,7 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (!world.isClientSide && world.getBlockEntity(pos) instanceof BeaconBlockEntity beaconBlockEntity) {
- player.openMenu(beaconBlockEntity);
+ if (!world.isClientSide && world.getBlockEntity(pos) instanceof BeaconBlockEntity beaconBlockEntity && player.openMenu(beaconBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_BEACON);
}

View File

@@ -0,0 +1,92 @@
--- a/net/minecraft/world/level/block/BedBlock.java
+++ b/net/minecraft/world/level/block/BedBlock.java
@@ -95,7 +95,8 @@
}
}
- if (!BedBlock.canSetSpawn(world)) {
+ // CraftBukkit - moved world and biome check into EntityHuman
+ if (false && !BedBlock.canSetSpawn(world)) {
world.removeBlock(pos, false);
BlockPos blockposition1 = pos.relative(((Direction) state.getValue(BedBlock.FACING)).getOpposite());
@@ -108,25 +109,65 @@
world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK);
return InteractionResult.SUCCESS_SERVER;
} else if ((Boolean) state.getValue(BedBlock.OCCUPIED)) {
+ if (!BedBlock.canSetSpawn(world)) return this.explodeBed(state, world, pos); // Paper - check explode first
if (!this.kickVillagerOutOfBed(world, pos)) {
player.displayClientMessage(Component.translatable("block.minecraft.bed.occupied"), true);
}
return InteractionResult.SUCCESS_SERVER;
} else {
+ // CraftBukkit start
+ BlockState finaliblockdata = state;
+ BlockPos finalblockposition = pos;
+ // CraftBukkit end
player.startSleepInBed(pos).ifLeft((entityhuman_enumbedresult) -> {
+ // Paper start - PlayerBedFailEnterEvent
+ if (entityhuman_enumbedresult != null) {
+ io.papermc.paper.event.player.PlayerBedFailEnterEvent event = new io.papermc.paper.event.player.PlayerBedFailEnterEvent((org.bukkit.entity.Player) player.getBukkitEntity(), io.papermc.paper.event.player.PlayerBedFailEnterEvent.FailReason.values()[entityhuman_enumbedresult.ordinal()], org.bukkit.craftbukkit.block.CraftBlock.at(world, finalblockposition), !world.dimensionType().bedWorks(), io.papermc.paper.adventure.PaperAdventure.asAdventure(entityhuman_enumbedresult.getMessage()));
+ if (!event.callEvent()) {
+ return;
+ }
+ // Paper end - PlayerBedFailEnterEvent
+ // CraftBukkit start - handling bed explosion from below here
+ if (event.getWillExplode()) { // Paper - PlayerBedFailEnterEvent
+ this.explodeBed(finaliblockdata, world, finalblockposition);
+ } else
+ // CraftBukkit end
if (entityhuman_enumbedresult.getMessage() != null) {
- player.displayClientMessage(entityhuman_enumbedresult.getMessage(), true);
+ final net.kyori.adventure.text.Component message = event.getMessage(); // Paper - PlayerBedFailEnterEvent
+ if (message != null) player.displayClientMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), true); // Paper - PlayerBedFailEnterEvent
}
+ } // Paper - PlayerBedFailEnterEvent
});
return InteractionResult.SUCCESS_SERVER;
+ }
+ }
+ }
+
+ // CraftBukkit start
+ private InteractionResult explodeBed(BlockState iblockdata, Level world, BlockPos blockposition) {
+ {
+ {
+ org.bukkit.block.BlockState blockState = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition).getState(); // CraftBukkit - capture BlockState before remove block
+ world.removeBlock(blockposition, false);
+ BlockPos blockposition1 = blockposition.relative(((Direction) iblockdata.getValue(BedBlock.FACING)).getOpposite());
+
+ if (world.getBlockState(blockposition1).getBlock() == this) {
+ world.removeBlock(blockposition1, false);
+ }
+
+ Vec3 vec3d = blockposition.getCenter();
+
+ world.explode((Entity) null, world.damageSources().badRespawnPointExplosion(vec3d, blockState), (ExplosionDamageCalculator) null, vec3d, 5.0F, true, Level.ExplosionInteraction.BLOCK); // CraftBukkit - add state
+ return InteractionResult.SUCCESS;
}
}
}
+ // CraftBukkit end
public static boolean canSetSpawn(Level world) {
- return world.dimensionType().bedWorks();
+ return world.dimensionType().bedWorks(); // Paper - actually check if the bed works
}
private boolean kickVillagerOutOfBed(Level world, BlockPos pos) {
@@ -320,6 +361,11 @@
BlockPos blockposition1 = pos.relative((Direction) state.getValue(BedBlock.FACING));
world.setBlock(blockposition1, (BlockState) state.setValue(BedBlock.PART, BedPart.HEAD), 3);
+ // CraftBukkit start - SPIGOT-7315: Don't updated if we capture block states
+ if (world.captureBlockStates) {
+ return;
+ }
+ // CraftBukkit end
world.blockUpdated(pos, Blocks.AIR);
state.updateNeighbourShapes(world, pos, 3);
}

View File

@@ -0,0 +1,79 @@
--- a/net/minecraft/world/level/block/BeehiveBlock.java
+++ b/net/minecraft/world/level/block/BeehiveBlock.java
@@ -94,8 +94,8 @@
}
@Override
- public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) {
- super.playerDestroy(world, player, pos, state, blockEntity, tool);
+ public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion
+ super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion
if (!world.isClientSide && blockEntity instanceof BeehiveBlockEntity tileentitybeehive) {
if (!EnchantmentHelper.hasTag(tool, EnchantmentTags.PREVENTS_BEE_SPAWNS_WHEN_MINING)) {
tileentitybeehive.emptyAllLivingFromHive(player, state, BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY);
@@ -103,7 +103,7 @@
this.angerNearbyBees(world, pos);
}
- CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer) player, state, tool, tileentitybeehive.getOccupantCount());
+ // CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer) player, state, tool, tileentitybeehive.getOccupantCount()); // Paper - Trigger bee_nest_destroyed trigger in the correct place; moved until after items are dropped
}
}
@@ -133,7 +133,7 @@
if (entitybee.getTarget() == null) {
Player entityhuman = (Player) Util.getRandom(list1, world.random);
- entitybee.setTarget(entityhuman);
+ entitybee.setTarget(entityhuman, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit
}
}
}
@@ -141,7 +141,7 @@
}
public static void dropHoneycomb(Level world, BlockPos pos) {
- popResource(world, pos, new ItemStack(Items.HONEYCOMB, 3));
+ popResource(world, pos, new ItemStack(Items.HONEYCOMB, 3)); // Paper - Add PlayerShearBlockEvent; conflict on change, item needs to be set below
}
@Override
@@ -153,8 +153,19 @@
Item item = stack.getItem();
if (stack.is(Items.SHEARS)) {
+ // Paper start - Add PlayerShearBlockEvent
+ io.papermc.paper.event.block.PlayerShearBlockEvent event = new io.papermc.paper.event.block.PlayerShearBlockEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand), new java.util.ArrayList<>());
+ event.getDrops().add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(new ItemStack(Items.HONEYCOMB, 3)));
+ if (!event.callEvent()) {
+ return InteractionResult.PASS;
+ }
+ // Paper end
world.playSound(player, player.getX(), player.getY(), player.getZ(), SoundEvents.BEEHIVE_SHEAR, SoundSource.BLOCKS, 1.0F, 1.0F);
- BeehiveBlock.dropHoneycomb(world, pos);
+ // Paper start - Add PlayerShearBlockEvent
+ for (org.bukkit.inventory.ItemStack itemDrop : event.getDrops()) {
+ popResource(world, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemDrop));
+ }
+ // Paper end - Add PlayerShearBlockEvent
stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand));
flag = true;
world.gameEvent((Entity) player, (Holder) GameEvent.SHEAR, pos);
@@ -297,7 +308,7 @@
ItemStack itemstack = new ItemStack(this);
itemstack.applyComponents(tileentitybeehive.collectComponents());
- itemstack.set(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY.with(BeehiveBlock.HONEY_LEVEL, (Comparable) i));
+ itemstack.set(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY.with(BeehiveBlock.HONEY_LEVEL, i)); // CraftBukkit - decompile error
ItemEntity entityitem = new ItemEntity(world, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), itemstack);
entityitem.setDefaultPickUpDelay();
@@ -332,7 +343,7 @@
ItemStack itemstack = super.getCloneItemStack(world, pos, state, includeData);
if (includeData) {
- itemstack.set(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY.with(BeehiveBlock.HONEY_LEVEL, (Comparable) ((Integer) state.getValue(BeehiveBlock.HONEY_LEVEL))));
+ itemstack.set(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY.with(BeehiveBlock.HONEY_LEVEL, ((Integer) state.getValue(BeehiveBlock.HONEY_LEVEL)))); // CraftBukkit - decompile error
}
return itemstack;

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/BellBlock.java
+++ b/net/minecraft/world/level/block/BellBlock.java
@@ -148,6 +148,11 @@
if (direction == null) {
direction = (Direction) world.getBlockState(pos).getValue(BellBlock.FACING);
}
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBellRingEvent(world, pos, direction, entity)) {
+ return false;
+ }
+ // CraftBukkit end
((BellBlockEntity) tileentity).onHit(direction);
world.playSound((Player) null, pos, SoundEvents.BELL_BLOCK, SoundSource.BLOCKS, 2.0F, 1.0F);

View File

@@ -0,0 +1,116 @@
--- a/net/minecraft/world/level/block/BigDripleafBlock.java
+++ b/net/minecraft/world/level/block/BigDripleafBlock.java
@@ -44,6 +44,10 @@
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityInteractEvent;
+// CraftBukkit end
public class BigDripleafBlock extends HorizontalDirectionalBlock implements BonemealableBlock, SimpleWaterloggedBlock {
@@ -119,7 +123,7 @@
@Override
protected void onProjectileHit(Level world, BlockState state, BlockHitResult hit, Projectile projectile) {
- this.setTiltAndScheduleTick(state, world, hit.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN);
+ this.setTiltAndScheduleTick(state, world, hit.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN, projectile); // CraftBukkit
}
@Override
@@ -176,9 +180,23 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (!world.isClientSide) {
if (state.getValue(BigDripleafBlock.TILT) == Tilt.NONE && BigDripleafBlock.canEntityTilt(pos, entity) && !world.hasNeighborSignal(pos)) {
- this.setTiltAndScheduleTick(state, world, pos, Tilt.UNSTABLE, (SoundEvent) null);
+ // CraftBukkit start - tilt dripleaf
+ org.bukkit.event.Cancellable cancellable;
+ if (entity instanceof Player) {
+ cancellable = CraftEventFactory.callPlayerInteractEvent((Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null);
+ } else {
+ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()));
+ world.getCraftServer().getPluginManager().callEvent((EntityInteractEvent) cancellable);
+ }
+
+ if (cancellable.isCancelled()) {
+ return;
+ }
+ this.setTiltAndScheduleTick(state, world, pos, Tilt.UNSTABLE, (SoundEvent) null, entity);
+ // CraftBukkit end
}
}
@@ -192,9 +210,9 @@
Tilt tilt = (Tilt) state.getValue(BigDripleafBlock.TILT);
if (tilt == Tilt.UNSTABLE) {
- this.setTiltAndScheduleTick(state, world, pos, Tilt.PARTIAL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN);
+ this.setTiltAndScheduleTick(state, world, pos, Tilt.PARTIAL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN, null); // CraftBukkit
} else if (tilt == Tilt.PARTIAL) {
- this.setTiltAndScheduleTick(state, world, pos, Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN);
+ this.setTiltAndScheduleTick(state, world, pos, Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN, null); // CraftBukkit
} else if (tilt == Tilt.FULL) {
BigDripleafBlock.resetTilt(state, world, pos);
}
@@ -220,36 +238,46 @@
return entity.onGround() && entity.position().y > (double) ((float) pos.getY() + 0.6875F);
}
- private void setTiltAndScheduleTick(BlockState state, Level world, BlockPos pos, Tilt tilt, @Nullable SoundEvent sound) {
- BigDripleafBlock.setTilt(state, world, pos, tilt);
- if (sound != null) {
- BigDripleafBlock.playTiltSound(world, pos, sound);
+ // CraftBukkit start
+ private void setTiltAndScheduleTick(BlockState iblockdata, Level world, BlockPos blockposition, Tilt tilt, @Nullable SoundEvent soundeffect, @Nullable Entity entity) {
+ if (!BigDripleafBlock.setTilt(iblockdata, world, blockposition, tilt, entity)) return;
+ // CraftBukkit end
+ if (soundeffect != null) {
+ BigDripleafBlock.playTiltSound(world, blockposition, soundeffect);
}
int i = BigDripleafBlock.DELAY_UNTIL_NEXT_TILT_STATE.getInt(tilt);
if (i != -1) {
- world.scheduleTick(pos, (Block) this, i);
+ world.scheduleTick(blockposition, (Block) this, i);
}
}
private static void resetTilt(BlockState state, Level world, BlockPos pos) {
- BigDripleafBlock.setTilt(state, world, pos, Tilt.NONE);
+ BigDripleafBlock.setTilt(state, world, pos, Tilt.NONE, null); // CraftBukkit
if (state.getValue(BigDripleafBlock.TILT) != Tilt.NONE) {
BigDripleafBlock.playTiltSound(world, pos, SoundEvents.BIG_DRIPLEAF_TILT_UP);
}
}
- private static void setTilt(BlockState state, Level world, BlockPos pos, Tilt tilt) {
- Tilt tilt1 = (Tilt) state.getValue(BigDripleafBlock.TILT);
+ // CraftBukkit start
+ private static boolean setTilt(BlockState iblockdata, Level world, BlockPos blockposition, Tilt tilt, @Nullable Entity entity) {
+ if (entity != null) {
+ if (!CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition, iblockdata.setValue(BigDripleafBlock.TILT, tilt))) {
+ return false;
+ }
+ }
+ // CraftBukkit end
+ Tilt tilt1 = (Tilt) iblockdata.getValue(BigDripleafBlock.TILT);
- world.setBlock(pos, (BlockState) state.setValue(BigDripleafBlock.TILT, tilt), 2);
+ world.setBlock(blockposition, (BlockState) iblockdata.setValue(BigDripleafBlock.TILT, tilt), 2);
if (tilt.causesVibration() && tilt != tilt1) {
- world.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_CHANGE, pos);
+ world.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_CHANGE, blockposition);
}
+ return true; // CraftBukkit
}
@Override

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/world/level/block/BlastFurnaceBlock.java
+++ b/net/minecraft/world/level/block/BlastFurnaceBlock.java
@@ -45,8 +45,7 @@
@Override
protected void openContainer(Level world, BlockPos pos, Player player) {
BlockEntity blockEntity = world.getBlockEntity(pos);
- if (blockEntity instanceof BlastFurnaceBlockEntity) {
- player.openMenu((MenuProvider)blockEntity);
+ if (blockEntity instanceof BlastFurnaceBlockEntity && player.openMenu((MenuProvider)blockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_BLAST_FURNACE);
}
}

View File

@@ -0,0 +1,154 @@
--- a/net/minecraft/world/level/block/Block.java
+++ b/net/minecraft/world/level/block/Block.java
@@ -88,6 +88,21 @@
public static final int UPDATE_LIMIT = 512;
protected final StateDefinition<Block, BlockState> stateDefinition;
private BlockState defaultBlockState;
+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
+ public final boolean isDestroyable() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
+ this != Blocks.BARRIER &&
+ this != Blocks.BEDROCK &&
+ this != Blocks.END_PORTAL_FRAME &&
+ this != Blocks.END_PORTAL &&
+ this != Blocks.END_GATEWAY &&
+ this != Blocks.COMMAND_BLOCK &&
+ this != Blocks.REPEATING_COMMAND_BLOCK &&
+ this != Blocks.CHAIN_COMMAND_BLOCK &&
+ this != Blocks.STRUCTURE_BLOCK &&
+ this != Blocks.JIGSAW;
+ }
+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
@Nullable
private Item item;
private static final int CACHE_SIZE = 256;
@@ -295,12 +310,38 @@
}
+ // Paper start - Add BlockBreakBlockEvent
+ public static boolean dropResources(BlockState state, LevelAccessor levelAccessor, BlockPos pos, @Nullable BlockEntity blockEntity, BlockPos source) {
+ if (levelAccessor instanceof ServerLevel serverLevel) {
+ List<org.bukkit.inventory.ItemStack> items = new java.util.ArrayList<>();
+ for (ItemStack drop : Block.getDrops(state, serverLevel, pos, blockEntity)) {
+ items.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(drop));
+ }
+ Block block = state.getBlock(); // Paper - Properly handle xp dropping
+ io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, pos), org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, source), items);
+ event.setExpToDrop(block.getExpDrop(state, serverLevel, pos, net.minecraft.world.item.ItemStack.EMPTY, true)); // Paper - Properly handle xp dropping
+ event.callEvent();
+ for (org.bukkit.inventory.ItemStack drop : event.getDrops()) {
+ popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop));
+ }
+ state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
+ block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping
+ }
+ return true;
+ }
+ // Paper end - Add BlockBreakBlockEvent
+
public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) {
+ // Paper start - Properly handle xp dropping
+ dropResources(state, world, pos, blockEntity, entity, tool, true);
+ }
+ public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool, boolean dropExperience) {
+ // Paper end - Properly handle xp dropping
if (world instanceof ServerLevel) {
Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> {
Block.popResource(world, pos, itemstack1);
});
- state.spawnAfterBreak((ServerLevel) world, pos, tool, true);
+ state.spawnAfterBreak((ServerLevel) world, pos, tool, dropExperience); // Paper - Properly handle xp dropping
}
}
@@ -340,7 +381,13 @@
ItemEntity entityitem = (ItemEntity) itemEntitySupplier.get();
entityitem.setDefaultPickUpDelay();
- world.addFreshEntity(entityitem);
+ // CraftBukkit start
+ if (world.captureDrops != null) {
+ world.captureDrops.add(entityitem);
+ } else {
+ world.addFreshEntity(entityitem);
+ }
+ // CraftBukkit end
return;
}
}
@@ -348,8 +395,13 @@
}
public void popExperience(ServerLevel world, BlockPos pos, int size) {
+ // Paper start - add entity parameter
+ popExperience(world, pos, size, null);
+ }
+ public void popExperience(ServerLevel world, BlockPos pos, int size, net.minecraft.world.entity.Entity entity) {
+ // Paper end - add entity parameter
if (world.getGameRules().getBoolean(GameRules.RULE_DOBLOCKDROPS)) {
- ExperienceOrb.award(world, Vec3.atCenterOf(pos), size);
+ ExperienceOrb.award(world, Vec3.atCenterOf(pos), size, org.bukkit.entity.ExperienceOrb.SpawnReason.BLOCK_BREAK, entity); // Paper
}
}
@@ -367,10 +419,18 @@
return this.defaultBlockState();
}
+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - fix drops not preventing stats/food exhaustion
public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) {
+ // Paper start - fix drops not preventing stats/food exhaustion
+ this.playerDestroy(world, player, pos, state, blockEntity, tool, true, true);
+ }
+ public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) {
+ // Paper end - fix drops not preventing stats/food exhaustion
player.awardStat(Stats.BLOCK_MINED.get(this));
- player.causeFoodExhaustion(0.005F);
- Block.dropResources(state, world, pos, blockEntity, player, tool);
+ player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); // CraftBukkit - EntityExhaustionEvent
+ if (includeDrops) { // Paper - fix drops not preventing stats/food exhaustion
+ Block.dropResources(state, world, pos, blockEntity, player, tool, dropExp); // Paper - Properly handle xp dropping
+ } // Paper - fix drops not preventing stats/food exhaustion
}
public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {}
@@ -490,15 +550,35 @@
return this.builtInRegistryHolder;
}
- protected void tryDropExperience(ServerLevel world, BlockPos pos, ItemStack tool, IntProvider experience) {
- int i = EnchantmentHelper.processBlockExperience(world, tool, experience.sample(world.getRandom()));
+ // CraftBukkit start
+ protected int tryDropExperience(ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, IntProvider intprovider) {
+ int i = EnchantmentHelper.processBlockExperience(worldserver, itemstack, intprovider.sample(worldserver.getRandom()));
if (i > 0) {
- this.popExperience(world, pos, i);
+ // this.popExperience(worldserver, blockposition, i);
+ return i;
}
+ return 0;
}
+ public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) {
+ return 0;
+ }
+ // CraftBukkit end
+
+ // Spigot start
+ public static float range(float min, float value, float max) {
+ if (value < min) {
+ return min;
+ }
+ if (value > max) {
+ return max;
+ }
+ return value;
+ }
+ // Spigot end
+
private static record ShapePairKey(VoxelShape first, VoxelShape second) {
public boolean equals(Object object) {

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/world/level/block/BrewingStandBlock.java
+++ b/net/minecraft/world/level/block/BrewingStandBlock.java
@@ -68,8 +68,7 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (!world.isClientSide && world.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStandBlockEntity) {
- player.openMenu(brewingStandBlockEntity);
+ if (!world.isClientSide && world.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStandBlockEntity && player.openMenu(brewingStandBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_BREWINGSTAND);
}

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/BubbleColumnBlock.java
+++ b/net/minecraft/world/level/block/BubbleColumnBlock.java
@@ -48,6 +48,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
BlockState blockState = world.getBlockState(pos.above());
if (blockState.isAir()) {
entity.onAboveBubbleCol(state.getValue(DRAG_DOWN));

View File

@@ -0,0 +1,17 @@
--- a/net/minecraft/world/level/block/BuddingAmethystBlock.java
+++ b/net/minecraft/world/level/block/BuddingAmethystBlock.java
@@ -45,7 +45,13 @@
if (block != null) {
BlockState iblockdata2 = (BlockState) ((BlockState) block.defaultBlockState().setValue(AmethystClusterBlock.FACING, enumdirection)).setValue(AmethystClusterBlock.WATERLOGGED, iblockdata1.getFluidState().getType() == Fluids.WATER);
- world.setBlockAndUpdate(blockposition1, iblockdata2);
+ // Paper start - Have Amethyst throw both spread and grow events
+ if (block == Blocks.SMALL_AMETHYST_BUD) {
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, iblockdata2); // CraftBukkit
+ } else {
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition1, iblockdata2);
+ }
+ // Paper end - Have Amethyst throw both spread and grow events
}
}

View File

@@ -0,0 +1,27 @@
--- a/net/minecraft/world/level/block/BushBlock.java
+++ b/net/minecraft/world/level/block/BushBlock.java
@@ -6,6 +6,7 @@
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.ScheduledTickAccess;
import net.minecraft.world.level.block.state.BlockBehaviour;
@@ -27,7 +28,15 @@
@Override
protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {
- return !state.canSurvive(world, pos) ? Blocks.AIR.defaultBlockState() : super.updateShape(state, world, tickView, pos, direction, neighborPos, neighborState, random);
+ // CraftBukkit start
+ if (!state.canSurvive(world, pos)) {
+ // Suppress during worldgen
+ if (!(world instanceof net.minecraft.server.level.ServerLevel world1 && world1.hasPhysicsEvent) || !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(world1, pos).isCancelled()) { // Paper
+ return Blocks.AIR.defaultBlockState();
+ }
+ }
+ return super.updateShape(state, world, tickView, pos, direction, neighborPos, neighborState, random);
+ // CraftBukkit end
}
@Override

View File

@@ -0,0 +1,78 @@
--- a/net/minecraft/world/level/block/ButtonBlock.java
+++ b/net/minecraft/world/level/block/ButtonBlock.java
@@ -34,6 +34,10 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import org.bukkit.event.block.BlockRedstoneEvent;
+import org.bukkit.event.entity.EntityInteractEvent;
+// CraftBukkit end
public class ButtonBlock extends FaceAttachedHorizontalDirectionalBlock {
@@ -126,6 +130,19 @@
if ((Boolean) state.getValue(ButtonBlock.POWERED)) {
return InteractionResult.CONSUME;
} else {
+ // CraftBukkit start
+ boolean powered = ((Boolean) state.getValue(ButtonBlock.POWERED));
+ org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ int old = (powered) ? 15 : 0;
+ int current = (!powered) ? 15 : 0;
+
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+
+ if ((eventRedstone.getNewCurrent() > 0) != (!powered)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
this.press(state, world, pos, player);
return InteractionResult.SUCCESS;
}
@@ -191,17 +208,43 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (!world.isClientSide && this.type.canButtonBeActivatedByArrows() && !(Boolean) state.getValue(ButtonBlock.POWERED)) {
this.checkPressed(state, world, pos);
}
}
protected void checkPressed(BlockState state, Level world, BlockPos pos) {
- AbstractArrow entityarrow = this.type.canButtonBeActivatedByArrows() ? (AbstractArrow) world.getEntitiesOfClass(AbstractArrow.class, state.getShape(world, pos).bounds().move(pos)).stream().findFirst().orElse((Object) null) : null;
+ AbstractArrow entityarrow = this.type.canButtonBeActivatedByArrows() ? (AbstractArrow) world.getEntitiesOfClass(AbstractArrow.class, state.getShape(world, pos).bounds().move(pos)).stream().findFirst().orElse(null) : null; // CraftBukkit - decompile error
boolean flag = entityarrow != null;
boolean flag1 = (Boolean) state.getValue(ButtonBlock.POWERED);
+ // CraftBukkit start - Call interact event when arrows turn on wooden buttons
+ if (flag1 != flag && flag) {
+ org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ EntityInteractEvent event = new EntityInteractEvent(entityarrow.getBukkitEntity(), block);
+ world.getCraftServer().getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) {
+ return;
+ }
+ }
+ // CraftBukkit end
+
if (flag != flag1) {
+ // CraftBukkit start
+ boolean powered = flag1;
+ org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ int old = (powered) ? 15 : 0;
+ int current = (!powered) ? 15 : 0;
+
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+
+ if ((flag && eventRedstone.getNewCurrent() <= 0) || (!flag && eventRedstone.getNewCurrent() > 0)) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) state.setValue(ButtonBlock.POWERED, flag), 3);
this.updateNeighbours(state, world, pos);
this.playSound((Player) null, world, pos, flag);

View File

@@ -0,0 +1,42 @@
--- a/net/minecraft/world/level/block/CactusBlock.java
+++ b/net/minecraft/world/level/block/CactusBlock.java
@@ -22,6 +22,7 @@
import net.minecraft.world.level.redstone.Orientation;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
public class CactusBlock extends Block {
@@ -61,16 +62,17 @@
;
}
- if (i < 3) {
+ if (i < world.paperConfig().maxGrowthHeight.cactus) { // Paper - Configurable cactus/bamboo/reed growth height
int j = (Integer) state.getValue(CactusBlock.AGE);
- if (j == 15) {
- world.setBlockAndUpdate(blockposition1, this.defaultBlockState());
+ int modifier = world.spigotConfig.cactusModifier; // Spigot - SPIGOT-7159: Better modifier resolution
+ if (j >= 15 || (modifier != 100 && random.nextFloat() < (modifier / (100.0f * 16)))) { // Spigot - SPIGOT-7159: Better modifier resolution
+ CraftEventFactory.handleBlockGrowEvent(world, blockposition1, this.defaultBlockState()); // CraftBukkit
BlockState iblockdata1 = (BlockState) state.setValue(CactusBlock.AGE, 0);
world.setBlock(pos, iblockdata1, 4);
world.neighborChanged(iblockdata1, blockposition1, this, (Orientation) null, false);
- } else {
+ } else if (modifier == 100 || random.nextFloat() < (modifier / (100.0f * 16))) { // Spigot - SPIGOT-7159: Better modifier resolution
world.setBlock(pos, (BlockState) state.setValue(CactusBlock.AGE, j + 1), 4);
}
@@ -120,7 +122,8 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
- entity.hurt(world.damageSources().cactus(), 1.0F);
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
+ entity.hurt(world.damageSources().cactus().directBlock(world, pos), 1.0F); // CraftBukkit
}
@Override

View File

@@ -0,0 +1,47 @@
--- a/net/minecraft/world/level/block/CakeBlock.java
+++ b/net/minecraft/world/level/block/CakeBlock.java
@@ -66,6 +66,12 @@
if (block instanceof CandleBlock) {
CandleBlock candleblock = (CandleBlock) block;
+ // Paper start - call change block event
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, CandleCakeBlock.byCandle(candleblock))) {
+ player.containerMenu.sendAllDataToRemote(); // update inv because candle could decrease
+ return InteractionResult.TRY_WITH_EMPTY_HAND;
+ }
+ // Paper end - call change block event
stack.consume(1, player);
world.playSound((Player) null, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0F, 1.0F);
world.setBlockAndUpdate(pos, CandleCakeBlock.byCandle(candleblock));
@@ -97,10 +103,29 @@
if (!player.canEat(false)) {
return InteractionResult.PASS;
} else {
+ // Paper start - call change block event
+ int i = state.getValue(CakeBlock.BITES);
+ final BlockState newState = i < MAX_BITES ? state.setValue(CakeBlock.BITES, i + 1) : world.getFluidState(pos).createLegacyBlock();
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, newState)) {
+ ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity().sendHealthUpdate();
+ return InteractionResult.PASS; // return a non-consume result to cake blocks don't drop their candles
+ }
+ // Paper end - call change block event
player.awardStat(Stats.EAT_CAKE_SLICE);
- player.getFoodData().eat(2, 0.1F);
- int i = (Integer) state.getValue(CakeBlock.BITES);
+ // CraftBukkit start
+ // entityhuman.getFoodData().eat(2, 0.1F);
+ int oldFoodLevel = player.getFoodData().foodLevel;
+ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(player, 2 + oldFoodLevel);
+
+ if (!event.isCancelled()) {
+ player.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F);
+ }
+
+ ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity().sendHealthUpdate();
+ // CraftBukkit end
+ // Paper - move up
+
world.gameEvent((Entity) player, (Holder) GameEvent.EAT, pos);
if (i < 6) {
world.setBlock(pos, (BlockState) state.setValue(CakeBlock.BITES, i + 1), 3);

View File

@@ -0,0 +1,25 @@
--- a/net/minecraft/world/level/block/CampfireBlock.java
+++ b/net/minecraft/world/level/block/CampfireBlock.java
@@ -112,8 +112,9 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if ((Boolean) state.getValue(CampfireBlock.LIT) && entity instanceof LivingEntity) {
- entity.hurt(world.damageSources().campfire(), (float) this.fireDamage);
+ entity.hurt(world.damageSources().campfire().directBlock(world, pos), (float) this.fireDamage); // CraftBukkit
}
super.entityInside(state, world, pos, entity);
@@ -219,6 +220,11 @@
if (world instanceof ServerLevel worldserver) {
if (projectile.isOnFire() && projectile.mayInteract(worldserver, blockposition) && !(Boolean) state.getValue(CampfireBlock.LIT) && !(Boolean) state.getValue(CampfireBlock.WATERLOGGED)) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition, projectile).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(blockposition, (BlockState) state.setValue(BlockStateProperties.LIT, true), 11);
}
}

View File

@@ -0,0 +1,13 @@
--- a/net/minecraft/world/level/block/CartographyTableBlock.java
+++ b/net/minecraft/world/level/block/CartographyTableBlock.java
@@ -32,8 +32,9 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_CARTOGRAPHY_TABLE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;

View File

@@ -0,0 +1,29 @@
--- a/net/minecraft/world/level/block/CarvedPumpkinBlock.java
+++ b/net/minecraft/world/level/block/CarvedPumpkinBlock.java
@@ -24,6 +24,9 @@
import net.minecraft.world.level.block.state.pattern.BlockPatternBuilder;
import net.minecraft.world.level.block.state.predicate.BlockStatePredicate;
import net.minecraft.world.level.block.state.properties.EnumProperty;
+// CraftBukkit start
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
+// CraftBukkit end
public class CarvedPumpkinBlock extends HorizontalDirectionalBlock {
@@ -87,9 +90,14 @@
}
private static void spawnGolemInWorld(Level world, BlockPattern.BlockPatternMatch patternResult, Entity entity, BlockPos pos) {
- CarvedPumpkinBlock.clearPatternBlocks(world, patternResult);
+ // clearPatternBlocks(world, shapedetector_shapedetectorcollection); // CraftBukkit - moved down
entity.moveTo((double) pos.getX() + 0.5D, (double) pos.getY() + 0.05D, (double) pos.getZ() + 0.5D, 0.0F, 0.0F);
- world.addFreshEntity(entity);
+ // CraftBukkit start
+ if (!world.addFreshEntity(entity, (entity.getType() == EntityType.SNOW_GOLEM) ? SpawnReason.BUILD_SNOWMAN : SpawnReason.BUILD_IRONGOLEM)) {
+ return;
+ }
+ CarvedPumpkinBlock.clearPatternBlocks(world, patternResult); // CraftBukkit - from above
+ // CraftBukkit end
Iterator iterator = world.getEntitiesOfClass(ServerPlayer.class, entity.getBoundingBox().inflate(5.0D)).iterator();
while (iterator.hasNext()) {

View File

@@ -0,0 +1,56 @@
--- a/net/minecraft/world/level/block/CauldronBlock.java
+++ b/net/minecraft/world/level/block/CauldronBlock.java
@@ -12,6 +12,9 @@
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
+// CraftBukkit start
+import org.bukkit.event.block.CauldronLevelChangeEvent;
+// CraftBukkit end
public class CauldronBlock extends AbstractCauldronBlock {
@@ -41,9 +44,19 @@
public void handlePrecipitation(BlockState state, Level world, BlockPos pos, Biome.Precipitation precipitation) {
if (CauldronBlock.shouldHandlePrecipitation(world, precipitation)) {
if (precipitation == Biome.Precipitation.RAIN) {
+ // Paper start - Call CauldronLevelChangeEvent
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.WATER_CAULDRON.defaultBlockState(), null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL, false)) { // avoid duplicate game event
+ return;
+ }
+ // Paper end - Call CauldronLevelChangeEvent
world.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState());
world.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_CHANGE, pos);
} else if (precipitation == Biome.Precipitation.SNOW) {
+ // Paper start - Call CauldronLevelChangeEvent
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState(), null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL, false)) { // avoid duplicate game event
+ return;
+ }
+ // Paper end - Call CauldronLevelChangeEvent
world.setBlockAndUpdate(pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState());
world.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_CHANGE, pos);
}
@@ -62,13 +75,19 @@
if (fluid == Fluids.WATER) {
iblockdata1 = Blocks.WATER_CAULDRON.defaultBlockState();
- world.setBlockAndUpdate(pos, iblockdata1);
- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(iblockdata1));
+ // Paper start - Call CauldronLevelChangeEvent; don't send level event or game event if cancelled
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit
+ return;
+ }
+ // Paper end - Call CauldronLevelChangeEvent
world.levelEvent(1047, pos, 0);
} else if (fluid == Fluids.LAVA) {
iblockdata1 = Blocks.LAVA_CAULDRON.defaultBlockState();
- world.setBlockAndUpdate(pos, iblockdata1);
- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(iblockdata1));
+ // Paper start - Call CauldronLevelChangeEvent; don't send level event or game event if cancelled
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit
+ return;
+ }
+ // Paper end - Call CauldronLevelChangeEvent
world.levelEvent(1046, pos, 0);
}

View File

@@ -0,0 +1,42 @@
--- a/net/minecraft/world/level/block/CaveVines.java
+++ b/net/minecraft/world/level/block/CaveVines.java
@@ -19,6 +19,13 @@
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import java.util.Collections;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.player.PlayerHarvestBlockEvent;
+// CraftBukkit end
+
public interface CaveVines {
VoxelShape SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
@@ -26,7 +33,24 @@
static InteractionResult use(@Nullable Entity picker, BlockState state, Level world, BlockPos pos) {
if ((Boolean) state.getValue(CaveVines.BERRIES)) {
- Block.popResource(world, pos, new ItemStack(Items.GLOW_BERRIES, 1));
+ // CraftBukkit start
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(picker, pos, (BlockState) state.setValue(CaveVines.BERRIES, false))) {
+ return InteractionResult.SUCCESS;
+ }
+
+ if (picker instanceof Player) {
+ PlayerHarvestBlockEvent event = CraftEventFactory.callPlayerHarvestBlockEvent(world, pos, (Player) picker, net.minecraft.world.InteractionHand.MAIN_HAND, Collections.singletonList(new ItemStack(Items.GLOW_BERRIES, 1)));
+ if (event.isCancelled()) {
+ return InteractionResult.SUCCESS; // We need to return a success either way, because making it PASS or FAIL will result in a bug where cancelling while harvesting w/ block in hand places block
+ }
+ for (org.bukkit.inventory.ItemStack itemStack : event.getItemsHarvested()) {
+ Block.popResource(world, pos, CraftItemStack.asNMSCopy(itemStack));
+ }
+ } else {
+ Block.popResource(world, pos, new ItemStack(Items.GLOW_BERRIES, 1));
+ }
+ // CraftBukkit end
+
float f = Mth.randomBetween(world.random, 0.8F, 1.2F);
world.playSound((Player) null, pos, SoundEvents.CAVE_VINES_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, f);

View File

@@ -0,0 +1,22 @@
--- a/net/minecraft/world/level/block/CaveVinesBlock.java
+++ b/net/minecraft/world/level/block/CaveVinesBlock.java
@@ -50,9 +50,18 @@
return to.setValue(BERRIES, from.getValue(BERRIES));
}
+ // Paper start - Fix Spigot growth modifiers
@Override
+ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) {
+ final boolean value = random.nextFloat() < (level != null ? (0.11F * (level.spigotConfig.glowBerryModifier / 100.0F)) : 0.11F);
+ return (BlockState) super.getGrowIntoState(state, random).setValue(CaveVinesBlock.BERRIES, value);
+ }
+ // Paper end - Fix Spigot growth modifiers
+
+ @Override
protected BlockState getGrowIntoState(BlockState state, RandomSource random) {
- return super.getGrowIntoState(state, random).setValue(BERRIES, Boolean.valueOf(random.nextFloat() < 0.11F));
+ // Paper start - Fix Spigot growth modifiers
+ return this.getGrowIntoState(state, random, null);
}
@Override

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/CeilingHangingSignBlock.java
+++ b/net/minecraft/world/level/block/CeilingHangingSignBlock.java
@@ -159,6 +159,6 @@
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level world, BlockState state, BlockEntityType<T> type) {
- return createTickerHelper(type, BlockEntityType.HANGING_SIGN, SignBlockEntity::tick);
+ return null; // Craftbukkit - remove unnecessary sign ticking
}
}

View File

@@ -0,0 +1,11 @@
--- a/net/minecraft/world/level/block/ChangeOverTimeBlock.java
+++ b/net/minecraft/world/level/block/ChangeOverTimeBlock.java
@@ -20,7 +20,7 @@
if (random.nextFloat() < 0.05688889F) {
this.getNextState(state, world, pos, random).ifPresent((iblockdata1) -> {
- world.setBlockAndUpdate(pos, iblockdata1);
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, pos, iblockdata1); // CraftBukkit
});
}

View File

@@ -0,0 +1,118 @@
--- a/net/minecraft/world/level/block/ChestBlock.java
+++ b/net/minecraft/world/level/block/ChestBlock.java
@@ -91,24 +91,7 @@
public Optional<MenuProvider> acceptDouble(final ChestBlockEntity first, final ChestBlockEntity second) {
final CompoundContainer inventorylargechest = new CompoundContainer(first, second);
- return Optional.of(new MenuProvider(this) {
- @Nullable
- @Override
- public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
- if (first.canOpen(player) && second.canOpen(player)) {
- first.unpackLootTable(playerInventory.player);
- second.unpackLootTable(playerInventory.player);
- return ChestMenu.sixRows(syncId, playerInventory, inventorylargechest);
- } else {
- return null;
- }
- }
-
- @Override
- public Component getDisplayName() {
- return (Component) (first.hasCustomName() ? first.getDisplayName() : (second.hasCustomName() ? second.getDisplayName() : Component.translatable("container.chestDouble")));
- }
- });
+ return Optional.of(new DoubleInventory(first, second, inventorylargechest)); // CraftBukkit // CraftBukkit - decompile error
}
public Optional<MenuProvider> acceptSingle(ChestBlockEntity single) {
@@ -118,8 +101,40 @@
@Override
public Optional<MenuProvider> acceptNone() {
return Optional.empty();
+ }
+ };
+
+ // CraftBukkit start
+ public static class DoubleInventory implements MenuProvider {
+
+ private final ChestBlockEntity tileentitychest;
+ private final ChestBlockEntity tileentitychest1;
+ public final CompoundContainer inventorylargechest;
+
+ public DoubleInventory(ChestBlockEntity tileentitychest, ChestBlockEntity tileentitychest1, CompoundContainer inventorylargechest) {
+ this.tileentitychest = tileentitychest;
+ this.tileentitychest1 = tileentitychest1;
+ this.inventorylargechest = inventorylargechest;
+ }
+
+ @Nullable
+ @Override
+ public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) {
+ if (this.tileentitychest.canOpen(player) && this.tileentitychest1.canOpen(player)) {
+ this.tileentitychest.unpackLootTable(playerInventory.player);
+ this.tileentitychest1.unpackLootTable(playerInventory.player);
+ return ChestMenu.sixRows(syncId, playerInventory, this.inventorylargechest);
+ } else {
+ return null;
+ }
}
+
+ @Override
+ public Component getDisplayName() {
+ return (Component) (this.tileentitychest.hasCustomName() ? this.tileentitychest.getDisplayName() : (this.tileentitychest1.hasCustomName() ? this.tileentitychest1.getDisplayName() : Component.translatable("container.chestDouble")));
+ }
};
+ // CraftBukkit end
@Override
public MapCodec<? extends ChestBlock> codec() {
@@ -232,8 +247,7 @@
if (world instanceof ServerLevel worldserver) {
MenuProvider itileinventory = this.getMenuProvider(state, world, pos);
- if (itileinventory != null) {
- player.openMenu(itileinventory);
+ if (itileinventory != null && player.openMenu(itileinventory).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(this.getOpenChestStat());
PiglinAi.angerNearbyPiglins(worldserver, player, true);
}
@@ -257,7 +271,7 @@
@Override
public DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> combine(BlockState state, Level world, BlockPos pos, boolean ignoreBlocked) {
- BiPredicate bipredicate;
+ BiPredicate<LevelAccessor, BlockPos> bipredicate; // CraftBukkit - decompile error
if (ignoreBlocked) {
bipredicate = (generatoraccess, blockposition1) -> {
@@ -273,9 +287,16 @@
@Nullable
@Override
public MenuProvider getMenuProvider(BlockState state, Level world, BlockPos pos) {
- return (MenuProvider) ((Optional) this.combine(state, world, pos, false).apply(ChestBlock.MENU_PROVIDER_COMBINER)).orElse((Object) null);
+ // CraftBukkit start
+ return this.getMenuProvider(state, world, pos, false);
}
+ @Nullable
+ public MenuProvider getMenuProvider(BlockState iblockdata, Level world, BlockPos blockposition, boolean ignoreObstructions) {
+ return (MenuProvider) ((Optional) this.combine(iblockdata, world, blockposition, ignoreObstructions).apply(ChestBlock.MENU_PROVIDER_COMBINER)).orElse((Object) null);
+ // CraftBukkit end
+ }
+
public static DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction> opennessCombiner(final LidBlockEntity progress) {
return new DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction>() {
public Float2FloatFunction acceptDouble(ChestBlockEntity first, ChestBlockEntity second) {
@@ -321,6 +342,11 @@
}
private static boolean isCatSittingOnChest(LevelAccessor world, BlockPos pos) {
+ // Paper start - Option to disable chest cat detection
+ if (world.getMinecraftWorld().paperConfig().entities.behavior.disableChestCatDetection) {
+ return false;
+ }
+ // Paper end - Option to disable chest cat detection
List<Cat> list = world.getEntitiesOfClass(Cat.class, new AABB((double) pos.getX(), (double) (pos.getY() + 1), (double) pos.getZ(), (double) (pos.getX() + 1), (double) (pos.getY() + 2), (double) (pos.getZ() + 1)));
if (!list.isEmpty()) {

View File

@@ -0,0 +1,73 @@
--- a/net/minecraft/world/level/block/ChorusFlowerBlock.java
+++ b/net/minecraft/world/level/block/ChorusFlowerBlock.java
@@ -23,6 +23,8 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
+
public class ChorusFlowerBlock extends Block {
public static final MapCodec<ChorusFlowerBlock> CODEC = RecordCodecBuilder.mapCodec((instance) -> {
@@ -103,8 +105,12 @@
}
if (flag && ChorusFlowerBlock.allNeighborsEmpty(world, blockposition1, (Direction) null) && world.isEmptyBlock(pos.above(2))) {
- world.setBlock(pos, ChorusPlantBlock.getStateWithConnections(world, pos, this.plant.defaultBlockState()), 2);
- this.placeGrownFlower(world, blockposition1, i);
+ // CraftBukkit start - add event
+ if (CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, this.defaultBlockState().setValue(ChorusFlowerBlock.AGE, Integer.valueOf(i)), 2)) {
+ world.setBlock(pos, ChorusPlantBlock.getStateWithConnections(world, pos, this.plant.defaultBlockState()), 2);
+ this.placeGrownFlower(world, blockposition1, i);
+ }
+ // CraftBukkit end
} else if (i < 4) {
j = random.nextInt(4);
if (flag1) {
@@ -118,18 +124,30 @@
BlockPos blockposition2 = pos.relative(enumdirection);
if (world.isEmptyBlock(blockposition2) && world.isEmptyBlock(blockposition2.below()) && ChorusFlowerBlock.allNeighborsEmpty(world, blockposition2, enumdirection.getOpposite())) {
- this.placeGrownFlower(world, blockposition2, i + 1);
- flag2 = true;
+ // CraftBukkit start - add event
+ if (CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition2, this.defaultBlockState().setValue(ChorusFlowerBlock.AGE, Integer.valueOf(i + 1)), 2)) {
+ this.placeGrownFlower(world, blockposition2, i + 1);
+ flag2 = true;
+ }
+ // CraftBukkit end
}
}
if (flag2) {
world.setBlock(pos, ChorusPlantBlock.getStateWithConnections(world, pos, this.plant.defaultBlockState()), 2);
} else {
- this.placeDeadFlower(world, pos);
+ // CraftBukkit start - add event
+ if (CraftEventFactory.handleBlockGrowEvent(world, pos, this.defaultBlockState().setValue(ChorusFlowerBlock.AGE, Integer.valueOf(5)), 2)) {
+ this.placeDeadFlower(world, pos);
+ }
+ // CraftBukkit end
}
} else {
- this.placeDeadFlower(world, pos);
+ // CraftBukkit start - add event
+ if (CraftEventFactory.handleBlockGrowEvent(world, pos, this.defaultBlockState().setValue(ChorusFlowerBlock.AGE, Integer.valueOf(5)), 2)) {
+ this.placeDeadFlower(world, pos);
+ }
+ // CraftBukkit end
}
}
@@ -267,6 +285,11 @@
if (world instanceof ServerLevel worldserver) {
if (projectile.mayInteract(worldserver, blockposition) && projectile.mayBreak(worldserver)) {
+ // CraftBukkit
+ if (!CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state
+ return;
+ }
+ // CraftBukkit end
world.destroyBlock(blockposition, true, projectile);
}
}

View File

@@ -0,0 +1,34 @@
--- a/net/minecraft/world/level/block/ChorusPlantBlock.java
+++ b/net/minecraft/world/level/block/ChorusPlantBlock.java
@@ -38,6 +38,7 @@
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return this.defaultBlockState(); // Paper - add option to disable block updates
return getStateWithConnections(ctx.getLevel(), ctx.getClickedPos(), this.defaultBlockState());
}
@@ -68,6 +69,7 @@
BlockState neighborState,
RandomSource random
) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return state; // Paper - add option to disable block updates
if (!state.canSurvive(world, pos)) {
tickView.scheduleTick(pos, this, 1);
return super.updateShape(state, world, tickView, pos, direction, neighborPos, neighborState, random);
@@ -79,6 +81,7 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return; // Paper - add option to disable block updates
if (!state.canSurvive(world, pos)) {
world.destroyBlock(pos, true);
}
@@ -86,6 +89,7 @@
@Override
protected boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableChorusPlantUpdates) return true; // Paper - add option to disable block updates
BlockState blockState = world.getBlockState(pos.below());
boolean bl = !world.getBlockState(pos.above()).isAir() && !blockState.isAir();

View File

@@ -0,0 +1,33 @@
--- a/net/minecraft/world/level/block/CocoaBlock.java
+++ b/net/minecraft/world/level/block/CocoaBlock.java
@@ -20,6 +20,7 @@
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
public class CocoaBlock extends HorizontalDirectionalBlock implements BonemealableBlock {
@@ -57,11 +58,11 @@
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
- if (world.random.nextInt(5) == 0) {
+ if (world.random.nextFloat() < (world.spigotConfig.cocoaModifier / (100.0f * 5))) { // Spigot - SPIGOT-7159: Better modifier resolution
int i = (Integer) state.getValue(CocoaBlock.AGE);
if (i < 2) {
- world.setBlock(pos, (BlockState) state.setValue(CocoaBlock.AGE, i + 1), 2);
+ CraftEventFactory.handleBlockGrowEvent(world, pos, (BlockState) state.setValue(CocoaBlock.AGE, i + 1), 2); // CraftBukkkit
}
}
@@ -131,7 +132,7 @@
@Override
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
- world.setBlock(pos, (BlockState) state.setValue(CocoaBlock.AGE, (Integer) state.getValue(CocoaBlock.AGE) + 1), 2);
+ CraftEventFactory.handleBlockGrowEvent(world, pos, (BlockState) state.setValue(CocoaBlock.AGE, (Integer) state.getValue(CocoaBlock.AGE) + 1), 2); // CraftBukkit
}
@Override

View File

@@ -0,0 +1,37 @@
--- a/net/minecraft/world/level/block/CommandBlock.java
+++ b/net/minecraft/world/level/block/CommandBlock.java
@@ -31,6 +31,8 @@
import net.minecraft.world.phys.BlockHitResult;
import org.slf4j.Logger;
+import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
+
public class CommandBlock extends BaseEntityBlock implements GameMasterBlock {
public static final MapCodec<CommandBlock> CODEC = RecordCodecBuilder.mapCodec((instance) -> {
@@ -78,7 +80,16 @@
private void setPoweredAndUpdate(Level world, BlockPos pos, CommandBlockEntity blockEntity, boolean powered) {
boolean flag1 = blockEntity.isPowered();
+ // CraftBukkit start
+ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ int old = flag1 ? 15 : 0;
+ int current = powered ? 15 : 0;
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+ powered = eventRedstone.getNewCurrent() > 0;
+ // CraftBukkit end
+
if (powered != flag1) {
blockEntity.setPowered(powered);
if (powered) {
@@ -141,7 +152,7 @@
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
BlockEntity tileentity = world.getBlockEntity(pos);
- if (tileentity instanceof CommandBlockEntity && player.canUseGameMasterBlocks()) {
+ if (tileentity instanceof CommandBlockEntity && (player.canUseGameMasterBlocks() || (player.isCreative() && player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
player.openCommandBlock((CommandBlockEntity) tileentity);
return InteractionResult.SUCCESS;
} else {

View File

@@ -0,0 +1,39 @@
--- a/net/minecraft/world/level/block/ComparatorBlock.java
+++ b/net/minecraft/world/level/block/ComparatorBlock.java
@@ -27,6 +27,7 @@
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.ticks.TickPriority;
+import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
public class ComparatorBlock extends DiodeBlock implements EntityBlock {
@@ -110,7 +111,8 @@
@Nullable
private ItemFrame getItemFrame(Level world, Direction facing, BlockPos pos) {
- List<ItemFrame> list = world.getEntitiesOfClass(ItemFrame.class, new AABB((double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), (double) (pos.getX() + 1), (double) (pos.getY() + 1), (double) (pos.getZ() + 1)), (entityitemframe) -> {
+ // CraftBukkit - decompile error
+ List<ItemFrame> list = world.getEntitiesOfClass(ItemFrame.class, new AABB((double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), (double) (pos.getX() + 1), (double) (pos.getY() + 1), (double) (pos.getZ() + 1)), (java.util.function.Predicate<ItemFrame>) (entityitemframe) -> {
return entityitemframe != null && entityitemframe.getDirection() == facing;
});
@@ -163,8 +165,18 @@
boolean flag1 = (Boolean) state.getValue(ComparatorBlock.POWERED);
if (flag1 && !flag) {
+ // CraftBukkit start
+ if (CraftEventFactory.callRedstoneChange(world, pos, 15, 0).getNewCurrent() != 0) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) state.setValue(ComparatorBlock.POWERED, false), 2);
} else if (!flag1 && flag) {
+ // CraftBukkit start
+ if (CraftEventFactory.callRedstoneChange(world, pos, 0, 15).getNewCurrent() != 15) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) state.setValue(ComparatorBlock.POWERED, true), 2);
}

View File

@@ -0,0 +1,184 @@
--- a/net/minecraft/world/level/block/ComposterBlock.java
+++ b/net/minecraft/world/level/block/ComposterBlock.java
@@ -41,6 +41,10 @@
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder;
+import org.bukkit.craftbukkit.util.DummyGeneratorAccess;
+// CraftBukkit end
public class ComposterBlock extends Block implements WorldlyContainerHolder {
@@ -241,6 +245,11 @@
if (i < 8 && ComposterBlock.COMPOSTABLES.containsKey(stack.getItem())) {
if (i < 7 && !world.isClientSide) {
BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, stack);
+ // Paper start - handle cancelled events
+ if (iblockdata1 == null) {
+ return InteractionResult.PASS;
+ }
+ // Paper end
world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0);
player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
@@ -269,7 +278,19 @@
int i = (Integer) state.getValue(ComposterBlock.LEVEL);
if (i < 7 && ComposterBlock.COMPOSTABLES.containsKey(stack.getItem())) {
- BlockState iblockdata1 = ComposterBlock.addItem(user, state, world, pos, stack);
+ // CraftBukkit start
+ double rand = world.getRandom().nextDouble();
+ BlockState iblockdata1 = null; // Paper
+ if (false && (state == iblockdata1 || !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(user, pos, iblockdata1))) { // Paper - move event call into addItem
+ return state;
+ }
+ iblockdata1 = ComposterBlock.addItem(user, state, world, pos, stack, rand);
+ // Paper start - handle cancelled events
+ if (iblockdata1 == null) {
+ return state;
+ }
+ // Paper end
+ // CraftBukkit end
stack.shrink(1);
return iblockdata1;
@@ -279,6 +300,14 @@
}
public static BlockState extractProduce(Entity user, BlockState state, Level world, BlockPos pos) {
+ // CraftBukkit start
+ if (user != null && !(user instanceof Player)) {
+ BlockState iblockdata1 = ComposterBlock.empty(user, state, DummyGeneratorAccess.INSTANCE, pos);
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(user, pos, iblockdata1)) {
+ return state;
+ }
+ }
+ // CraftBukkit end
if (!world.isClientSide) {
Vec3 vec3d = Vec3.atLowerCornerWithOffset(pos, 0.5D, 1.01D, 0.5D).offsetRandom(world.random, 0.7F);
ItemEntity entityitem = new ItemEntity(world, vec3d.x(), vec3d.y(), vec3d.z(), new ItemStack(Items.BONE_MEAL));
@@ -301,20 +330,47 @@
return iblockdata1;
}
+ @Nullable // Paper
static BlockState addItem(@Nullable Entity user, BlockState state, LevelAccessor world, BlockPos pos, ItemStack stack) {
- int i = (Integer) state.getValue(ComposterBlock.LEVEL);
- float f = ComposterBlock.COMPOSTABLES.getFloat(stack.getItem());
+ // CraftBukkit start
+ return ComposterBlock.addItem(user, state, world, pos, stack, world.getRandom().nextDouble());
+ }
- if ((i != 0 || f <= 0.0F) && world.getRandom().nextDouble() >= (double) f) {
- return state;
- } else {
- int j = i + 1;
- BlockState iblockdata1 = (BlockState) state.setValue(ComposterBlock.LEVEL, j);
+ @Nullable // Paper - make it nullable
+ static BlockState addItem(@Nullable Entity entity, BlockState iblockdata, LevelAccessor generatoraccess, BlockPos blockposition, ItemStack itemstack, double rand) {
+ // CraftBukkit end
+ int i = (Integer) iblockdata.getValue(ComposterBlock.LEVEL);
+ float f = ComposterBlock.COMPOSTABLES.getFloat(itemstack.getItem());
- world.setBlock(pos, iblockdata1, 3);
- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(user, iblockdata1));
+ // Paper start - Add CompostItemEvent and EntityCompostItemEvent
+ boolean willRaiseLevel = !((i != 0 || f <= 0.0F) && rand >= (double) f);
+ final io.papermc.paper.event.block.CompostItemEvent event;
+ if (entity == null) {
+ event = new io.papermc.paper.event.block.CompostItemEvent(org.bukkit.craftbukkit.block.CraftBlock.at(generatoraccess, blockposition), itemstack.getBukkitStack(), willRaiseLevel);
+ } else {
+ event = new io.papermc.paper.event.entity.EntityCompostItemEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(generatoraccess, blockposition), itemstack.getBukkitStack(), willRaiseLevel);
+ }
+ if (!event.callEvent()) { // check for cancellation of entity event (non entity event can't be cancelled cause of hoppers)
+ return null;
+ }
+ willRaiseLevel = event.willRaiseLevel();
+
+ if (!willRaiseLevel) {
+ // Paper end - Add CompostItemEvent and EntityCompostItemEvent
+ return iblockdata;
+ } else {
+ int j = i + 1;
+ BlockState iblockdata1 = (BlockState) iblockdata.setValue(ComposterBlock.LEVEL, j);
+ // Paper start - move the EntityChangeBlockEvent here to avoid conflict later for the compost events
+ if (entity != null && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition, iblockdata1)) {
+ return null;
+ }
+ // Paper end
+
+ generatoraccess.setBlock(blockposition, iblockdata1, 3);
+ generatoraccess.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, iblockdata1));
if (j == 7) {
- world.scheduleTick(pos, state.getBlock(), 20);
+ generatoraccess.scheduleTick(blockposition, iblockdata.getBlock(), 20);
}
return iblockdata1;
@@ -354,7 +410,8 @@
public WorldlyContainer getContainer(BlockState state, LevelAccessor world, BlockPos pos) {
int i = (Integer) state.getValue(ComposterBlock.LEVEL);
- return (WorldlyContainer) (i == 8 ? new ComposterBlock.OutputContainer(state, world, pos, new ItemStack(Items.BONE_MEAL)) : (i < 7 ? new ComposterBlock.InputContainer(state, world, pos) : new ComposterBlock.EmptyContainer()));
+ // CraftBukkit - empty generatoraccess, blockposition
+ return (WorldlyContainer) (i == 8 ? new ComposterBlock.OutputContainer(state, world, pos, new ItemStack(Items.BONE_MEAL)) : (i < 7 ? new ComposterBlock.InputContainer(state, world, pos) : new ComposterBlock.EmptyContainer(world, pos)));
}
public static class OutputContainer extends SimpleContainer implements WorldlyContainer {
@@ -369,6 +426,7 @@
this.state = state;
this.level = world;
this.pos = pos;
+ this.bukkitOwner = new CraftBlockInventoryHolder(world, pos, this); // CraftBukkit
}
@Override
@@ -393,8 +451,15 @@
@Override
public void setChanged() {
+ // CraftBukkit start - allow putting items back (eg cancelled InventoryMoveItemEvent)
+ if (this.isEmpty()) {
ComposterBlock.empty((Entity) null, this.state, this.level, this.pos);
this.changed = true;
+ } else {
+ this.level.setBlock(this.pos, this.state, 3);
+ this.changed = false;
+ }
+ // CraftBukkit end
}
}
@@ -407,6 +472,7 @@
public InputContainer(BlockState state, LevelAccessor world, BlockPos pos) {
super(1);
+ this.bukkitOwner = new CraftBlockInventoryHolder(world, pos, this); // CraftBukkit
this.state = state;
this.level = world;
this.pos = pos;
@@ -439,6 +505,11 @@
if (!itemstack.isEmpty()) {
this.changed = true;
BlockState iblockdata = ComposterBlock.addItem((Entity) null, this.state, this.level, this.pos, itemstack);
+ // Paper start - Add CompostItemEvent and EntityCompostItemEvent
+ if (iblockdata == null) {
+ return;
+ }
+ // Paper end - Add CompostItemEvent and EntityCompostItemEvent
this.level.levelEvent(1500, this.pos, iblockdata != this.state ? 1 : 0);
this.removeItemNoUpdate(0);
@@ -449,8 +520,9 @@
public static class EmptyContainer extends SimpleContainer implements WorldlyContainer {
- public EmptyContainer() {
+ public EmptyContainer(LevelAccessor generatoraccess, BlockPos blockposition) { // CraftBukkit
super(0);
+ this.bukkitOwner = new CraftBlockInventoryHolder(generatoraccess, blockposition, this); // CraftBukkit
}
@Override

View File

@@ -0,0 +1,76 @@
--- a/net/minecraft/world/level/block/ConcretePowderBlock.java
+++ b/net/minecraft/world/level/block/ConcretePowderBlock.java
@@ -15,6 +15,11 @@
import net.minecraft.world.level.ScheduledTickAccess;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlockState;
+import org.bukkit.craftbukkit.block.CraftBlockStates;
+import org.bukkit.event.block.BlockFormEvent;
+// CraftBukkit end
public class ConcretePowderBlock extends FallingBlock {
@@ -38,7 +43,7 @@
@Override
public void onLand(Level world, BlockPos pos, BlockState fallingBlockState, BlockState currentStateInPos, FallingBlockEntity fallingBlockEntity) {
if (ConcretePowderBlock.shouldSolidify(world, pos, currentStateInPos)) {
- world.setBlock(pos, this.concrete.defaultBlockState(), 3);
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, pos, this.concrete.defaultBlockState(), 3); // CraftBukkit
}
}
@@ -49,7 +54,24 @@
BlockPos blockposition = ctx.getClickedPos();
BlockState iblockdata = world.getBlockState(blockposition);
- return ConcretePowderBlock.shouldSolidify(world, blockposition, iblockdata) ? this.concrete.defaultBlockState() : super.getStateForPlacement(ctx);
+ // CraftBukkit start
+ if (!ConcretePowderBlock.shouldSolidify(world, blockposition, iblockdata)) {
+ return super.getStateForPlacement(ctx);
+ }
+
+ // TODO: An event factory call for methods like this
+ CraftBlockState blockState = CraftBlockStates.getBlockState(world, blockposition);
+ blockState.setData(this.concrete.defaultBlockState());
+
+ BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState);
+ world.getServer().server.getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ return blockState.getHandle();
+ }
+
+ return super.getStateForPlacement(ctx);
+ // CraftBukkit end
}
private static boolean shouldSolidify(BlockGetter world, BlockPos pos, BlockState state) {
@@ -85,7 +107,25 @@
@Override
protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {
- return ConcretePowderBlock.touchesLiquid(world, pos) ? this.concrete.defaultBlockState() : super.updateShape(state, world, tickView, pos, direction, neighborPos, neighborState, random);
+ // CraftBukkit start
+ if (ConcretePowderBlock.touchesLiquid(world, pos)) {
+ // Suppress during worldgen
+ if (!(world instanceof Level world1)) {
+ return this.concrete.defaultBlockState();
+ }
+ CraftBlockState blockState = CraftBlockStates.getBlockState(world1, pos);
+ blockState.setData(this.concrete.defaultBlockState());
+
+ BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState);
+ world1.getCraftServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ return blockState.getHandle();
+ }
+ }
+
+ return super.updateShape(state, world, tickView, pos, direction, neighborPos, neighborState, random);
+ // CraftBukkit end
}
@Override

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/CoralBlock.java
+++ b/net/minecraft/world/level/block/CoralBlock.java
@@ -40,6 +40,11 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (!this.scanForWater(world, pos)) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, this.deadBlock.defaultBlockState()).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, this.deadBlock.defaultBlockState(), 2);
}

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/CoralFanBlock.java
+++ b/net/minecraft/world/level/block/CoralFanBlock.java
@@ -41,6 +41,11 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (!scanForWater(state, world, pos)) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, this.deadBlock.defaultBlockState().setValue(CoralFanBlock.WATERLOGGED, false)).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) this.deadBlock.defaultBlockState().setValue(CoralFanBlock.WATERLOGGED, false), 2);
}

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/CoralPlantBlock.java
+++ b/net/minecraft/world/level/block/CoralPlantBlock.java
@@ -46,6 +46,11 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (!scanForWater(state, world, pos)) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, this.deadBlock.defaultBlockState().setValue(CoralPlantBlock.WATERLOGGED, false)).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) this.deadBlock.defaultBlockState().setValue(CoralPlantBlock.WATERLOGGED, false), 2);
}

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/CoralWallFanBlock.java
+++ b/net/minecraft/world/level/block/CoralWallFanBlock.java
@@ -41,6 +41,11 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (!scanForWater(state, world, pos)) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, this.deadBlock.defaultBlockState().setValue(CoralWallFanBlock.WATERLOGGED, false).setValue(CoralWallFanBlock.FACING, state.getValue(CoralWallFanBlock.FACING))).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) ((BlockState) this.deadBlock.defaultBlockState().setValue(CoralWallFanBlock.WATERLOGGED, false)).setValue(CoralWallFanBlock.FACING, (Direction) state.getValue(CoralWallFanBlock.FACING)), 2);
}

View File

@@ -0,0 +1,89 @@
--- a/net/minecraft/world/level/block/CrafterBlock.java
+++ b/net/minecraft/world/level/block/CrafterBlock.java
@@ -12,6 +12,7 @@
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
+import net.minecraft.world.CompoundContainer;
import net.minecraft.world.Container;
import net.minecraft.world.Containers;
import net.minecraft.world.InteractionResult;
@@ -39,6 +40,12 @@
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.CrafterCraftEvent;
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
+import org.bukkit.inventory.Inventory;
+// CraftBukkit end
public class CrafterBlock extends BaseEntityBlock {
@@ -189,6 +196,13 @@
RecipeHolder<CraftingRecipe> recipeholder = (RecipeHolder) optional.get();
ItemStack itemstack = ((CraftingRecipe) recipeholder.value()).assemble(craftinginput, world.registryAccess());
+ // CraftBukkit start
+ CrafterCraftEvent event = CraftEventFactory.callCrafterCraftEvent(pos, world, crafterblockentity, itemstack, recipeholder);
+ if (event.isCancelled()) {
+ return;
+ }
+ itemstack = CraftItemStack.asNMSCopy(event.getResult());
+ // CraftBukkit end
if (itemstack.isEmpty()) {
world.levelEvent(1050, pos, 0);
} else {
@@ -227,7 +241,25 @@
ItemStack itemstack1 = stack.copy();
if (iinventory != null && (iinventory instanceof CrafterBlockEntity || stack.getCount() > iinventory.getMaxStackSize(stack))) {
+ // CraftBukkit start - InventoryMoveItemEvent
+ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack1);
+
+ Inventory destinationInventory;
+ // Have to special case large chests as they work oddly
+ if (iinventory instanceof CompoundContainer) {
+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ } else {
+ destinationInventory = iinventory.getOwner().getInventory();
+ }
+
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true);
+ world.getCraftServer().getPluginManager().callEvent(event);
+ itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
while (!itemstack1.isEmpty()) {
+ if (event.isCancelled()) {
+ break;
+ }
+ // CraftBukkit end
ItemStack itemstack2 = itemstack1.copyWithCount(1);
ItemStack itemstack3 = HopperBlockEntity.addItem(blockEntity, iinventory, itemstack2, enumdirection.getOpposite());
@@ -238,7 +270,25 @@
itemstack1.shrink(1);
}
} else if (iinventory != null) {
+ // CraftBukkit start - InventoryMoveItemEvent
+ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack1);
+
+ Inventory destinationInventory;
+ // Have to special case large chests as they work oddly
+ if (iinventory instanceof CompoundContainer) {
+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ } else {
+ destinationInventory = iinventory.getOwner().getInventory();
+ }
+
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true);
+ world.getCraftServer().getPluginManager().callEvent(event);
+ itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
while (!itemstack1.isEmpty()) {
+ if (event.isCancelled()) {
+ break;
+ }
+ // CraftBukkit end
int i = itemstack1.getCount();
itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, itemstack1, enumdirection.getOpposite());

View File

@@ -0,0 +1,13 @@
--- a/net/minecraft/world/level/block/CraftingTableBlock.java
+++ b/net/minecraft/world/level/block/CraftingTableBlock.java
@@ -31,8 +31,9 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_CRAFTING_TABLE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;

View File

@@ -0,0 +1,59 @@
--- a/net/minecraft/world/level/block/CropBlock.java
+++ b/net/minecraft/world/level/block/CropBlock.java
@@ -21,6 +21,7 @@
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
public class CropBlock extends BushBlock implements BonemealableBlock {
@@ -82,9 +83,26 @@
if (i < this.getMaxAge()) {
float f = CropBlock.getGrowthSpeed(this, world, pos);
- if (random.nextInt((int) (25.0F / f) + 1) == 0) {
- world.setBlock(pos, this.getStateForAge(i + 1), 2);
+ // Spigot start
+ int modifier;
+ if (this == Blocks.BEETROOTS) {
+ modifier = world.spigotConfig.beetrootModifier;
+ } else if (this == Blocks.CARROTS) {
+ modifier = world.spigotConfig.carrotModifier;
+ } else if (this == Blocks.POTATOES) {
+ modifier = world.spigotConfig.potatoModifier;
+ // Paper start - Fix Spigot growth modifiers
+ } else if (this == Blocks.TORCHFLOWER_CROP) {
+ modifier = world.spigotConfig.torchFlowerModifier;
+ // Paper end - Fix Spigot growth modifiers
+ } else {
+ modifier = world.spigotConfig.wheatModifier;
}
+
+ if (random.nextFloat() < (modifier / (100.0f * (Math.floor((25.0F / f) + 1))))) { // Spigot - SPIGOT-7159: Better modifier resolution
+ // Spigot end
+ CraftEventFactory.handleBlockGrowEvent(world, pos, this.getStateForAge(i + 1), 2); // CraftBukkit
+ }
}
}
@@ -98,7 +116,7 @@
i = j;
}
- world.setBlock(pos, this.getStateForAge(i), 2);
+ CraftEventFactory.handleBlockGrowEvent(world, pos, this.getStateForAge(i), 2); // CraftBukkit
}
protected int getBonemealAgeIncrease(Level world) {
@@ -160,8 +178,9 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (world instanceof ServerLevel worldserver) {
- if (entity instanceof Ravager && worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
+ if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit
worldserver.destroyBlock(pos, true, entity);
}
}

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/DaylightDetectorBlock.java
+++ b/net/minecraft/world/level/block/DaylightDetectorBlock.java
@@ -74,6 +74,7 @@
i = Mth.clamp(i, 0, 15);
if ((Integer) state.getValue(DaylightDetectorBlock.POWER) != i) {
+ i = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, pos, ((Integer) state.getValue(DaylightDetectorBlock.POWER)), i).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent
world.setBlock(pos, (BlockState) state.setValue(DaylightDetectorBlock.POWER, i), 3);
}

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/DecoratedPotBlock.java
+++ b/net/minecraft/world/level/block/DecoratedPotBlock.java
@@ -240,6 +240,11 @@
if (world instanceof ServerLevel worldserver) {
if (projectile.mayInteract(worldserver, blockposition) && projectile.mayBreak(worldserver)) {
+ // CraftBukkit start - call EntityChangeBlockEvent
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, this.getFluidState(state).createLegacyBlock())) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(blockposition, (BlockState) state.setValue(DecoratedPotBlock.CRACKED, true), 4);
world.destroyBlock(blockposition, true, projectile);
}

View File

@@ -0,0 +1,44 @@
--- a/net/minecraft/world/level/block/DetectorRailBlock.java
+++ b/net/minecraft/world/level/block/DetectorRailBlock.java
@@ -26,6 +26,7 @@
import net.minecraft.world.level.block.state.properties.RailShape;
import net.minecraft.world.level.redstone.Orientation;
import net.minecraft.world.phys.AABB;
+import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
public class DetectorRailBlock extends BaseRailBlock {
@@ -51,6 +52,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (!world.isClientSide) {
if (!(Boolean) state.getValue(DetectorRailBlock.POWERED)) {
this.checkPressed(world, pos, state);
@@ -77,6 +79,7 @@
private void checkPressed(Level world, BlockPos pos, BlockState state) {
if (this.canSurvive(state, world, pos)) {
+ if (state.getBlock() != this) { return; } // Paper - Fix some rails connecting improperly
boolean flag = (Boolean) state.getValue(DetectorRailBlock.POWERED);
boolean flag1 = false;
List<AbstractMinecart> list = this.getInteractingMinecartOfType(world, pos, AbstractMinecart.class, (entity) -> {
@@ -88,7 +91,17 @@
}
BlockState iblockdata1;
+ // CraftBukkit start
+ if (flag != flag1) {
+ org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, flag ? 15 : 0, flag1 ? 15 : 0);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+
+ flag1 = eventRedstone.getNewCurrent() > 0;
+ }
+ // CraftBukkit end
+
if (flag1 && !flag) {
iblockdata1 = (BlockState) state.setValue(DetectorRailBlock.POWERED, true);
world.setBlock(pos, iblockdata1, 3);

View File

@@ -0,0 +1,29 @@
--- a/net/minecraft/world/level/block/DiodeBlock.java
+++ b/net/minecraft/world/level/block/DiodeBlock.java
@@ -23,6 +23,7 @@
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.ticks.TickPriority;
+import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
public abstract class DiodeBlock extends HorizontalDirectionalBlock {
@@ -59,8 +60,18 @@
boolean flag1 = this.shouldTurnOn(world, pos, state);
if (flag && !flag1) {
+ // CraftBukkit start
+ if (CraftEventFactory.callRedstoneChange(world, pos, 15, 0).getNewCurrent() != 0) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) state.setValue(DiodeBlock.POWERED, false), 2);
} else if (!flag) {
+ // CraftBukkit start
+ if (CraftEventFactory.callRedstoneChange(world, pos, 0, 15).getNewCurrent() != 15) {
+ return;
+ }
+ // CraftBukkit end
world.setBlock(pos, (BlockState) state.setValue(DiodeBlock.POWERED, true), 2);
if (!flag1) {
world.scheduleTick(pos, (Block) this, this.getDelay(state), TickPriority.VERY_HIGH);

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/world/level/block/DirtPathBlock.java
+++ b/net/minecraft/world/level/block/DirtPathBlock.java
@@ -51,6 +51,11 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
+ // CraftBukkit start - do not fade if the block is valid here
+ if (state.canSurvive(world, pos)) {
+ return;
+ }
+ // CraftBukkit end
FarmBlock.turnToDirt((Entity) null, state, world, pos);
}

View File

@@ -0,0 +1,61 @@
--- a/net/minecraft/world/level/block/DispenserBlock.java
+++ b/net/minecraft/world/level/block/DispenserBlock.java
@@ -52,6 +52,7 @@
private static final DefaultDispenseItemBehavior DEFAULT_BEHAVIOR = new DefaultDispenseItemBehavior();
public static final Map<Item, DispenseItemBehavior> DISPENSER_REGISTRY = new IdentityHashMap();
private static final int TRIGGER_DURATION = 4;
+ public static boolean eventFired = false; // CraftBukkit
@Override
public MapCodec<? extends DispenserBlock> codec() {
@@ -79,8 +80,9 @@
if (tileentity instanceof DispenserBlockEntity) {
DispenserBlockEntity tileentitydispenser = (DispenserBlockEntity) tileentity;
- player.openMenu(tileentitydispenser);
+ if (player.openMenu(tileentitydispenser).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(tileentitydispenser instanceof DropperBlockEntity ? Stats.INSPECT_DROPPER : Stats.INSPECT_DISPENSER);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
}
@@ -88,7 +90,7 @@
}
public void dispenseFrom(ServerLevel world, BlockState state, BlockPos pos) {
- DispenserBlockEntity tileentitydispenser = (DispenserBlockEntity) world.getBlockEntity(pos, BlockEntityType.DISPENSER).orElse((Object) null);
+ DispenserBlockEntity tileentitydispenser = (DispenserBlockEntity) world.getBlockEntity(pos, BlockEntityType.DISPENSER).orElse(null); // CraftBukkit - decompile error
if (tileentitydispenser == null) {
DispenserBlock.LOGGER.warn("Ignoring dispensing attempt for Dispenser without matching block entity at {}", pos);
@@ -97,13 +99,17 @@
int i = tileentitydispenser.getRandomSlot(world.random);
if (i < 0) {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(world, pos)) { // Paper - Add BlockFailedDispenseEvent
world.levelEvent(1001, pos, 0);
world.gameEvent((Holder) GameEvent.BLOCK_ACTIVATE, pos, GameEvent.Context.of(tileentitydispenser.getBlockState()));
+ } // Paper - Add BlockFailedDispenseEvent
} else {
ItemStack itemstack = tileentitydispenser.getItem(i);
DispenseItemBehavior idispensebehavior = this.getDispenseMethod(world, itemstack);
if (idispensebehavior != DispenseItemBehavior.NOOP) {
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(world, pos, itemstack, i)) return; // Paper - Add BlockPreDispenseEvent
+ DispenserBlock.eventFired = false; // CraftBukkit - reset event status
tileentitydispenser.setItem(i, idispensebehavior.dispense(sourceblock, itemstack));
}
@@ -111,6 +117,12 @@
}
}
+ // Paper start - Fix NPE with equippable and items without behavior
+ public static DispenseItemBehavior getDispenseBehavior(BlockSource pointer, ItemStack stack) {
+ return ((DispenserBlock) pointer.state().getBlock()).getDispenseMethod(pointer.level(), stack);
+ }
+ // Paper end - Fix NPE with equippable and items without behavior
+
protected DispenseItemBehavior getDispenseMethod(Level world, ItemStack stack) {
if (!stack.isItemEnabled(world.enabledFeatures())) {
return DispenserBlock.DEFAULT_BEHAVIOR;

View File

@@ -0,0 +1,37 @@
--- a/net/minecraft/world/level/block/DoorBlock.java
+++ b/net/minecraft/world/level/block/DoorBlock.java
@@ -38,6 +38,7 @@
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
public class DoorBlock extends Block {
@@ -222,9 +223,24 @@
@Override
protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, @Nullable Orientation wireOrientation, boolean notify) {
- boolean flag1 = world.hasNeighborSignal(pos) || world.hasNeighborSignal(pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
+ // CraftBukkit start
+ BlockPos otherHalf = pos.relative(state.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN);
- if (!this.defaultBlockState().is(sourceBlock) && flag1 != (Boolean) state.getValue(DoorBlock.POWERED)) {
+ org.bukkit.World bworld = world.getWorld();
+ org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ org.bukkit.block.Block blockTop = bworld.getBlockAt(otherHalf.getX(), otherHalf.getY(), otherHalf.getZ());
+
+ int power = bukkitBlock.getBlockPower();
+ int powerTop = blockTop.getBlockPower();
+ if (powerTop > power) power = powerTop;
+ int oldPower = (Boolean) state.getValue(DoorBlock.POWERED) ? 15 : 0;
+
+ if (oldPower == 0 ^ power == 0) {
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+
+ boolean flag1 = eventRedstone.getNewCurrent() > 0;
+ // CraftBukkit end
if (flag1 != (Boolean) state.getValue(DoorBlock.OPEN)) {
this.playSound((Entity) null, world, pos, flag1);
world.gameEvent((Entity) null, (Holder) (flag1 ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE), pos);

View File

@@ -0,0 +1,16 @@
--- a/net/minecraft/world/level/block/DoubleBlockCombiner.java
+++ b/net/minecraft/world/level/block/DoubleBlockCombiner.java
@@ -34,7 +34,12 @@
return new DoubleBlockCombiner.NeighborCombineResult.Single<>(blockEntity);
} else {
BlockPos blockPos = pos.relative(directionMapper.apply(state));
- BlockState blockState = world.getBlockState(blockPos);
+ // Paper start - Don't load Chunks from Hoppers and other things
+ BlockState blockState = world.getBlockStateIfLoaded(blockPos);
+ if (blockState == null) {
+ return new DoubleBlockCombiner.NeighborCombineResult.Single<>(blockEntity);
+ }
+ // Paper end - Don't load Chunks from Hoppers and other things
if (blockState.is(state.getBlock())) {
DoubleBlockCombiner.BlockType blockType2 = typeMapper.apply(blockState);
if (blockType2 != DoubleBlockCombiner.BlockType.SINGLE

View File

@@ -0,0 +1,21 @@
--- a/net/minecraft/world/level/block/DoublePlantBlock.java
+++ b/net/minecraft/world/level/block/DoublePlantBlock.java
@@ -98,11 +98,16 @@
}
@Override
- public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) {
- super.playerDestroy(world, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, tool);
+ public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion
+ super.playerDestroy(world, player, pos, Blocks.AIR.defaultBlockState(), blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion
}
protected static void preventDropFromBottomPart(Level world, BlockPos pos, BlockState state, Player player) {
+ // CraftBukkit start
+ if (((net.minecraft.server.level.ServerLevel)world).hasPhysicsEvent && org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(world, pos).isCancelled()) { // Paper
+ return;
+ }
+ // CraftBukkit end
DoubleBlockHalf blockpropertydoubleblockhalf = (DoubleBlockHalf) state.getValue(DoublePlantBlock.HALF);
if (blockpropertydoubleblockhalf == DoubleBlockHalf.UPPER) {

View File

@@ -0,0 +1,29 @@
--- a/net/minecraft/world/level/block/DragonEggBlock.java
+++ b/net/minecraft/world/level/block/DragonEggBlock.java
@@ -15,6 +15,7 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit
public class DragonEggBlock extends FallingBlock {
@@ -53,6 +54,18 @@
BlockPos blockposition1 = pos.offset(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16));
if (world.getBlockState(blockposition1).isAir() && worldborder.isWithinBounds(blockposition1)) {
+ // CraftBukkit start
+ org.bukkit.block.Block from = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ org.bukkit.block.Block to = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ());
+ BlockFromToEvent event = new BlockFromToEvent(from, to);
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) {
+ return;
+ }
+
+ blockposition1 = new BlockPos(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ());
+ // CraftBukkit end
if (world.isClientSide) {
for (int j = 0; j < 128; ++j) {
double d0 = world.random.nextDouble();

View File

@@ -0,0 +1,21 @@
--- a/net/minecraft/world/level/block/DropExperienceBlock.java
+++ b/net/minecraft/world/level/block/DropExperienceBlock.java
@@ -31,9 +31,16 @@
@Override
protected void spawnAfterBreak(BlockState state, ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) {
super.spawnAfterBreak(state, world, pos, tool, dropExperience);
- if (dropExperience) {
- this.tryDropExperience(world, pos, tool, this.xpRange);
+ // CraftBukkit start - Delegate to getExpDrop
+ }
+
+ @Override
+ public int getExpDrop(BlockState iblockdata, ServerLevel worldserver, BlockPos blockposition, ItemStack itemstack, boolean flag) {
+ if (flag) {
+ return this.tryDropExperience(worldserver, blockposition, itemstack, this.xpRange);
}
+ return 0;
+ // CraftBukkit end
}
}

View File

@@ -0,0 +1,75 @@
--- a/net/minecraft/world/level/block/DropperBlock.java
+++ b/net/minecraft/world/level/block/DropperBlock.java
@@ -8,6 +8,7 @@
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.core.dispenser.DispenseItemBehavior;
import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.CompoundContainer;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
@@ -19,12 +20,15 @@
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import org.slf4j.Logger;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
+// CraftBukkit end
public class DropperBlock extends DispenserBlock {
private static final Logger LOGGER = LogUtils.getLogger();
public static final MapCodec<DropperBlock> CODEC = simpleCodec(DropperBlock::new);
- private static final DispenseItemBehavior DISPENSE_BEHAVIOUR = new DefaultDispenseItemBehavior();
+ private static final DispenseItemBehavior DISPENSE_BEHAVIOUR = new DefaultDispenseItemBehavior(true); // CraftBukkit
@Override
public MapCodec<DropperBlock> codec() {
@@ -47,7 +51,7 @@
@Override
public void dispenseFrom(ServerLevel world, BlockState state, BlockPos pos) {
- DispenserBlockEntity tileentitydispenser = (DispenserBlockEntity) world.getBlockEntity(pos, BlockEntityType.DROPPER).orElse((Object) null);
+ DispenserBlockEntity tileentitydispenser = (DispenserBlockEntity) world.getBlockEntity(pos, BlockEntityType.DROPPER).orElse(null); // CraftBukkit - decompile error
if (tileentitydispenser == null) {
DropperBlock.LOGGER.warn("Ignoring dispensing attempt for Dropper without matching block entity at {}", pos);
@@ -56,6 +60,7 @@
int i = tileentitydispenser.getRandomSlot(world.random);
if (i < 0) {
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(world, pos)) // Paper - Add BlockFailedDispenseEvent
world.levelEvent(1001, pos, 0);
} else {
ItemStack itemstack = tileentitydispenser.getItem(i);
@@ -66,10 +71,28 @@
ItemStack itemstack1;
if (iinventory == null) {
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(world, pos, itemstack, i)) return; // Paper - Add BlockPreDispenseEvent
itemstack1 = DropperBlock.DISPENSE_BEHAVIOUR.dispense(sourceblock, itemstack);
} else {
- itemstack1 = HopperBlockEntity.addItem(tileentitydispenser, iinventory, itemstack.copyWithCount(1), enumdirection.getOpposite());
- if (itemstack1.isEmpty()) {
+ // CraftBukkit start - Fire event when pushing items into other inventories
+ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.copyWithCount(1));
+
+ org.bukkit.inventory.Inventory destinationInventory;
+ // Have to special case large chests as they work oddly
+ if (iinventory instanceof CompoundContainer) {
+ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ } else {
+ destinationInventory = iinventory.getOwner().getInventory();
+ }
+
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentitydispenser.getOwner().getInventory(), oitemstack, destinationInventory, true);
+ world.getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+ itemstack1 = HopperBlockEntity.addItem(tileentitydispenser, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection.getOpposite());
+ if (event.getItem().equals(oitemstack) && itemstack1.isEmpty()) {
+ // CraftBukkit end
itemstack1 = itemstack.copy();
itemstack1.shrink(1);
} else {

View File

@@ -0,0 +1,34 @@
--- a/net/minecraft/world/level/block/EndGatewayBlock.java
+++ b/net/minecraft/world/level/block/EndGatewayBlock.java
@@ -22,6 +22,9 @@
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.portal.TeleportTransition;
import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import org.bukkit.event.player.PlayerTeleportEvent;
+// CraftBukkit end
public class EndGatewayBlock extends BaseEntityBlock implements Portal {
@@ -89,7 +92,12 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (entity.canUsePortal(false)) {
+ // Paper start - call EntityPortalEnterEvent
+ org.bukkit.event.entity.EntityPortalEnterEvent event = new org.bukkit.event.entity.EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.END_GATEWAY); // Paper - add portal type
+ if (!event.callEvent()) return;
+ // Paper end - call EntityPortalEnterEvent
BlockEntity tileentity = world.getBlockEntity(pos);
if (!world.isClientSide && tileentity instanceof TheEndGatewayBlockEntity) {
@@ -112,7 +120,7 @@
if (tileentity instanceof TheEndGatewayBlockEntity tileentityendgateway) {
Vec3 vec3d = tileentityendgateway.getPortalPosition(world, pos);
- return vec3d == null ? null : (entity instanceof ThrownEnderpearl ? new TeleportTransition(world, vec3d, Vec3.ZERO, 0.0F, 0.0F, Set.of(), TeleportTransition.PLACE_PORTAL_TICKET) : new TeleportTransition(world, vec3d, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), TeleportTransition.PLACE_PORTAL_TICKET));
+ return vec3d == null ? null : (entity instanceof ThrownEnderpearl ? new TeleportTransition(world, vec3d, Vec3.ZERO, 0.0F, 0.0F, Set.of(), TeleportTransition.PLACE_PORTAL_TICKET, PlayerTeleportEvent.TeleportCause.END_GATEWAY) : new TeleportTransition(world, vec3d, Vec3.ZERO, 0.0F, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), TeleportTransition.PLACE_PORTAL_TICKET, PlayerTeleportEvent.TeleportCause.END_GATEWAY)); // CraftBukkit
} else {
return null;
}

View File

@@ -0,0 +1,91 @@
--- a/net/minecraft/world/level/block/EndPortalBlock.java
+++ b/net/minecraft/world/level/block/EndPortalBlock.java
@@ -19,12 +19,23 @@
import net.minecraft.world.level.block.entity.TheEndPortalBlockEntity;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.feature.EndPlatformFeature;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.portal.TeleportTransition;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import java.util.List;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.event.CraftPortalEvent;
+import org.bukkit.craftbukkit.util.CraftLocation;
+import org.bukkit.event.entity.EntityPortalEnterEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.event.player.PlayerTeleportEvent;
+// CraftBukkit end
public class EndPortalBlock extends BaseEntityBlock implements Portal {
@@ -57,10 +68,17 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (entity.canUsePortal(false)) {
+ // CraftBukkit start - Entity in portal
+ EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()), org.bukkit.PortalType.ENDER); // Paper - add portal type
+ world.getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) return; // Paper - make cancellable
+ // CraftBukkit end
if (!world.isClientSide && world.dimension() == Level.END && entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
+ if (world.paperConfig().misc.disableEndCredits) entityplayer.seenCredits = true; // Paper - Option to disable end credits
if (!entityplayer.seenCredits) {
entityplayer.showEndCredits();
return;
@@ -74,11 +92,11 @@
@Override
public TeleportTransition getPortalDestination(ServerLevel world, Entity entity, BlockPos pos) {
- ResourceKey<Level> resourcekey = world.dimension() == Level.END ? Level.OVERWORLD : Level.END;
+ ResourceKey<Level> resourcekey = world.getTypeKey() == LevelStem.END ? Level.OVERWORLD : Level.END; // CraftBukkit - SPIGOT-6152: send back to main overworld in custom ends
ServerLevel worldserver1 = world.getServer().getLevel(resourcekey);
if (worldserver1 == null) {
- return null;
+ return null; // Paper - keep previous behavior of not firing PlayerTeleportEvent if the target world doesn't exist
} else {
boolean flag = resourcekey == Level.END;
BlockPos blockposition1 = flag ? ServerLevel.END_SPAWN_POINT : worldserver1.getSharedSpawnPos();
@@ -87,7 +105,7 @@
Set set;
if (flag) {
- EndPlatformFeature.createEndPlatform(worldserver1, BlockPos.containing(vec3d).below(), true);
+ EndPlatformFeature.createEndPlatform(worldserver1, BlockPos.containing(vec3d).below(), true, entity); // CraftBukkit
f = Direction.WEST.toYRot();
set = Relative.union(Relative.DELTA, Set.of(Relative.X_ROT));
if (entity instanceof ServerPlayer) {
@@ -99,13 +117,21 @@
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
- return entityplayer.findRespawnPositionAndUseSpawnBlock(false, TeleportTransition.DO_NOTHING);
+ return entityplayer.findRespawnPositionAndUseSpawnBlock(false, TeleportTransition.DO_NOTHING, PlayerRespawnEvent.RespawnReason.END_PORTAL); // CraftBukkit
}
vec3d = entity.adjustSpawnLocation(worldserver1, blockposition1).getBottomCenter();
}
- return new TeleportTransition(worldserver1, vec3d, Vec3.ZERO, f, 0.0F, set, TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET));
+ // CraftBukkit start
+ CraftPortalEvent event = entity.callPortalEvent(entity, CraftLocation.toBukkit(vec3d, worldserver1.getWorld(), f, entity.getXRot()), PlayerTeleportEvent.TeleportCause.END_PORTAL, 0, 0);
+ if (event == null) {
+ return null;
+ }
+ Location to = event.getTo();
+
+ return new TeleportTransition(((CraftWorld) to.getWorld()).getHandle(), CraftLocation.toVec3D(to), entity.getDeltaMovement(), to.getYaw(), to.getPitch(), set, TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET), PlayerTeleportEvent.TeleportCause.END_PORTAL);
+ // CraftBukkit end
}
}

View File

@@ -0,0 +1,25 @@
--- a/net/minecraft/world/level/block/EnderChestBlock.java
+++ b/net/minecraft/world/level/block/EnderChestBlock.java
@@ -78,14 +78,16 @@
PlayerEnderChestContainer playerEnderChestContainer = player.getEnderChestInventory();
if (playerEnderChestContainer != null && world.getBlockEntity(pos) instanceof EnderChestBlockEntity enderChestBlockEntity) {
BlockPos blockPos = pos.above();
- if (world.getBlockState(blockPos).isRedstoneConductor(world, blockPos)) {
+ if (world.getBlockState(blockPos).isRedstoneConductor(world, blockPos)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic
return InteractionResult.SUCCESS;
} else {
- if (world instanceof ServerLevel serverLevel) {
- playerEnderChestContainer.setActiveChest(enderChestBlockEntity);
- player.openMenu(
- new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE)
- );
+ // Paper start - Fix InventoryOpenEvent cancellation - moved up;
+ playerEnderChestContainer.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations
+ if (world instanceof ServerLevel serverLevel && player.openMenu(
+ new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE)
+ ).isPresent()) {
+ // Paper end - Fix InventoryOpenEvent cancellation - moved up;
+ // Paper - Fix InventoryOpenEvent cancellation - moved up;
player.awardStat(Stats.OPEN_ENDERCHEST);
PiglinAi.angerNearbyPiglins(serverLevel, player, true);
}

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/EyeblossomBlock.java
+++ b/net/minecraft/world/level/block/EyeblossomBlock.java
@@ -100,6 +100,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (!world.isClientSide()
&& world.getDifficulty() != Difficulty.PEACEFUL
&& entity instanceof Bee bee

View File

@@ -0,0 +1,112 @@
--- a/net/minecraft/world/level/block/FarmBlock.java
+++ b/net/minecraft/world/level/block/FarmBlock.java
@@ -29,6 +29,10 @@
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import org.bukkit.event.entity.EntityInteractEvent;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+// CraftBukkit end
public class FarmBlock extends Block {
@@ -89,31 +93,56 @@
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
int i = (Integer) state.getValue(FarmBlock.MOISTURE);
+ if (i > 0 && world.paperConfig().tickRates.wetFarmland != 1 && (world.paperConfig().tickRates.wetFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.wetFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks
+ if (i == 0 && world.paperConfig().tickRates.dryFarmland != 1 && (world.paperConfig().tickRates.dryFarmland < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.dryFarmland != 0)) { return; } // Paper - Configurable random tick rates for blocks
if (!FarmBlock.isNearWater(world, pos) && !world.isRainingAt(pos.above())) {
if (i > 0) {
- world.setBlock(pos, (BlockState) state.setValue(FarmBlock.MOISTURE, i - 1), 2);
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, pos, (BlockState) state.setValue(FarmBlock.MOISTURE, i - 1), 2); // CraftBukkit
} else if (!FarmBlock.shouldMaintainFarmland(world, pos)) {
FarmBlock.turnToDirt((Entity) null, state, world, pos);
}
} else if (i < 7) {
- world.setBlock(pos, (BlockState) state.setValue(FarmBlock.MOISTURE, 7), 2);
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, pos, (BlockState) state.setValue(FarmBlock.MOISTURE, 7), 2); // CraftBukkit
}
}
@Override
public void fallOn(Level world, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
+ super.fallOn(world, state, pos, entity, fallDistance); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage.
if (world instanceof ServerLevel worldserver) {
if (world.random.nextFloat() < fallDistance - 0.5F && entity instanceof LivingEntity && (entity instanceof Player || worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) && entity.getBbWidth() * entity.getBbWidth() * entity.getBbHeight() > 0.512F) {
+ // CraftBukkit start - Interact soil
+ org.bukkit.event.Cancellable cancellable;
+ if (entity instanceof Player) {
+ cancellable = CraftEventFactory.callPlayerInteractEvent((Player) entity, org.bukkit.event.block.Action.PHYSICAL, pos, null, null, null);
+ } else {
+ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()));
+ world.getCraftServer().getPluginManager().callEvent((EntityInteractEvent) cancellable);
+ }
+
+ if (cancellable.isCancelled()) {
+ return;
+ }
+
+ if (!CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.DIRT.defaultBlockState())) {
+ return;
+ }
+ // CraftBukkit end
FarmBlock.turnToDirt(entity, state, world, pos);
}
}
- super.fallOn(world, state, pos, entity, fallDistance);
+ // super.fallOn(world, iblockdata, blockposition, entity, f); // CraftBukkit - moved up
}
public static void turnToDirt(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) {
+ // CraftBukkit start
+ if (CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
BlockState iblockdata1 = pushEntitiesUp(state, Blocks.DIRT.defaultBlockState(), world, pos);
world.setBlockAndUpdate(pos, iblockdata1);
@@ -125,19 +154,28 @@
}
private static boolean isNearWater(LevelReader world, BlockPos pos) {
- Iterator iterator = BlockPos.betweenClosed(pos.offset(-4, 0, -4), pos.offset(4, 1, 4)).iterator();
+ // Paper start - Perf: remove abstract block iteration
+ int xOff = pos.getX();
+ int yOff = pos.getY();
+ int zOff = pos.getZ();
- BlockPos blockposition1;
-
- do {
- if (!iterator.hasNext()) {
- return false;
+ for (int dz = -4; dz <= 4; ++dz) {
+ int z = dz + zOff;
+ for (int dx = -4; dx <= 4; ++dx) {
+ int x = xOff + dx;
+ for (int dy = 0; dy <= 1; ++dy) {
+ int y = dy + yOff;
+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)world.getChunk(x >> 4, z >> 4);
+ net.minecraft.world.level.material.FluidState fluid = chunk.getBlockStateFinal(x, y, z).getFluidState();
+ if (fluid.is(FluidTags.WATER)) {
+ return true;
+ }
+ }
}
+ }
- blockposition1 = (BlockPos) iterator.next();
- } while (!world.getFluidState(blockposition1).is(FluidTags.WATER));
-
- return true;
+ return false;
+ // Paper end - Perf: remove abstract block iteration
}
@Override

View File

@@ -0,0 +1,20 @@
--- a/net/minecraft/world/level/block/FenceGateBlock.java
+++ b/net/minecraft/world/level/block/FenceGateBlock.java
@@ -173,6 +173,17 @@
protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, @Nullable Orientation wireOrientation, boolean notify) {
if (!world.isClientSide) {
boolean flag1 = world.hasNeighborSignal(pos);
+ // CraftBukkit start
+ boolean oldPowered = state.getValue(FenceGateBlock.POWERED);
+ if (oldPowered != flag1) {
+ int newPower = flag1 ? 15 : 0;
+ int oldPower = oldPowered ? 15 : 0;
+ org.bukkit.block.Block bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos);
+ org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(bukkitBlock, oldPower, newPower);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+ flag1 = eventRedstone.getNewCurrent() > 0;
+ }
+ // CraftBukkit end
if ((Boolean) state.getValue(FenceGateBlock.POWERED) != flag1) {
world.setBlock(pos, (BlockState) ((BlockState) state.setValue(FenceGateBlock.POWERED, flag1)).setValue(FenceGateBlock.OPEN, flag1), 2);

View File

@@ -0,0 +1,205 @@
--- a/net/minecraft/world/level/block/FireBlock.java
+++ b/net/minecraft/world/level/block/FireBlock.java
@@ -14,6 +14,7 @@
import net.minecraft.tags.BiomeTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.context.BlockPlaceContext;
+import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
@@ -28,6 +29,12 @@
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.craftbukkit.block.CraftBlockState;
+import org.bukkit.craftbukkit.block.CraftBlockStates;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.block.BlockBurnEvent;
+import org.bukkit.event.block.BlockFadeEvent;
+// CraftBukkit end
public class FireBlock extends BaseFireBlock {
@@ -100,7 +107,25 @@
@Override
protected BlockState updateShape(BlockState state, LevelReader world, ScheduledTickAccess tickView, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {
- return this.canSurvive(state, world, pos) ? this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)) : Blocks.AIR.defaultBlockState();
+ // CraftBukkit start
+ if (!(world instanceof ServerLevel)) return this.canSurvive(state, world, pos) ? (BlockState) this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)) : Blocks.AIR.defaultBlockState(); // Paper - don't fire events in world generation
+ if (!this.canSurvive(state, world, pos)) {
+ // Suppress during worldgen
+ if (!(world instanceof Level world1)) {
+ return Blocks.AIR.defaultBlockState();
+ }
+ CraftBlockState blockState = CraftBlockStates.getBlockState(world1, pos);
+ blockState.setData(Blocks.AIR.defaultBlockState());
+
+ BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState);
+ world1.getCraftServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ return blockState.getHandle();
+ }
+ }
+ return this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)); // Paper - don't fire events in world generation; diff on change, see "don't fire events in world generation"
+ // CraftBukkit end
}
@Override
@@ -146,10 +171,10 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
- world.scheduleTick(pos, (Block) this, FireBlock.getFireTickDelay(world.random));
+ world.scheduleTick(pos, (Block) this, FireBlock.getFireTickDelay(world)); // Paper - Add fire-tick-delay option
if (world.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) {
if (!state.canSurvive(world, pos)) {
- world.removeBlock(pos, false);
+ this.fireExtinguished(world, pos); // CraftBukkit - invalid place location
}
BlockState iblockdata1 = world.getBlockState(pos.below());
@@ -157,7 +182,7 @@
int i = (Integer) state.getValue(FireBlock.AGE);
if (!flag && world.isRaining() && this.isNearRain(world, pos) && random.nextFloat() < 0.2F + (float) i * 0.03F) {
- world.removeBlock(pos, false);
+ this.fireExtinguished(world, pos); // CraftBukkit - extinguished by rain
} else {
int j = Math.min(15, i + random.nextInt(3) / 2);
@@ -171,14 +196,14 @@
BlockPos blockposition1 = pos.below();
if (!world.getBlockState(blockposition1).isFaceSturdy(world, blockposition1, Direction.UP) || i > 3) {
- world.removeBlock(pos, false);
+ this.fireExtinguished(world, pos); // CraftBukkit
}
return;
}
if (i == 15 && random.nextInt(4) == 0 && !this.canBurn(world.getBlockState(pos.below()))) {
- world.removeBlock(pos, false);
+ this.fireExtinguished(world, pos); // CraftBukkit
return;
}
}
@@ -186,12 +211,14 @@
boolean flag1 = world.getBiome(pos).is(BiomeTags.INCREASED_FIRE_BURNOUT);
int k = flag1 ? -50 : 0;
- this.checkBurnOut(world, pos.east(), 300 + k, random, i);
- this.checkBurnOut(world, pos.west(), 300 + k, random, i);
- this.checkBurnOut(world, pos.below(), 250 + k, random, i);
- this.checkBurnOut(world, pos.above(), 250 + k, random, i);
- this.checkBurnOut(world, pos.north(), 300 + k, random, i);
- this.checkBurnOut(world, pos.south(), 300 + k, random, i);
+ // CraftBukkit start - add source blockposition to burn calls
+ this.trySpread(world, pos.east(), 300 + k, random, i, pos);
+ this.trySpread(world, pos.west(), 300 + k, random, i, pos);
+ this.trySpread(world, pos.below(), 250 + k, random, i, pos);
+ this.trySpread(world, pos.above(), 250 + k, random, i, pos);
+ this.trySpread(world, pos.north(), 300 + k, random, i, pos);
+ this.trySpread(world, pos.south(), 300 + k, random, i, pos);
+ // CraftBukkit end
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
for (int l = -1; l <= 1; ++l) {
@@ -217,7 +244,15 @@
if (i2 > 0 && random.nextInt(k1) <= i2 && (!world.isRaining() || !this.isNearRain(world, blockposition_mutableblockposition))) {
int j2 = Math.min(15, i + random.nextInt(5) / 4);
- world.setBlock(blockposition_mutableblockposition, this.getStateWithAge(world, blockposition_mutableblockposition, j2), 3);
+ // CraftBukkit start - Call to stop spread of fire
+ if (world.getBlockState(blockposition_mutableblockposition).getBlock() != Blocks.FIRE) {
+ if (CraftEventFactory.callBlockIgniteEvent(world, blockposition_mutableblockposition, pos).isCancelled()) {
+ continue;
+ }
+
+ CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition_mutableblockposition, this.getStateWithAge(world, blockposition_mutableblockposition, j2), 3); // CraftBukkit
+ }
+ // CraftBukkit end
}
}
}
@@ -241,24 +276,47 @@
return state.hasProperty(BlockStateProperties.WATERLOGGED) && (Boolean) state.getValue(BlockStateProperties.WATERLOGGED) ? 0 : this.igniteOdds.getInt(state.getBlock());
}
- private void checkBurnOut(Level world, BlockPos pos, int spreadFactor, RandomSource random, int currentAge) {
- int k = this.getBurnOdds(world.getBlockState(pos));
+ private void trySpread(Level world, BlockPos blockposition, int i, RandomSource randomsource, int j, BlockPos sourceposition) { // CraftBukkit add sourceposition
+ int k = this.getBurnOdds(world.getBlockState(blockposition));
- if (random.nextInt(spreadFactor) < k) {
- BlockState iblockdata = world.getBlockState(pos);
+ if (randomsource.nextInt(i) < k) {
+ BlockState iblockdata = world.getBlockState(blockposition);
- if (random.nextInt(currentAge + 10) < 5 && !world.isRainingAt(pos)) {
- int l = Math.min(currentAge + random.nextInt(5) / 4, 15);
+ // CraftBukkit start
+ org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
+ org.bukkit.block.Block sourceBlock = world.getWorld().getBlockAt(sourceposition.getX(), sourceposition.getY(), sourceposition.getZ());
- world.setBlock(pos, this.getStateWithAge(world, pos, l), 3);
+ BlockBurnEvent event = new BlockBurnEvent(theBlock, sourceBlock);
+ world.getCraftServer().getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) {
+ return;
+ }
+
+ if (iblockdata.getBlock() instanceof TntBlock && !CraftEventFactory.callTNTPrimeEvent(world, blockposition, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.FIRE, null, sourceposition)) {
+ return;
+ }
+ // CraftBukkit end
+
+ if (randomsource.nextInt(j + 10) < 5 && !world.isRainingAt(blockposition)) {
+ int l = Math.min(j + randomsource.nextInt(5) / 4, 15);
+
+ world.setBlock(blockposition, this.getStateWithAge(world, blockposition, l), 3);
} else {
- world.removeBlock(pos, false);
+ if(iblockdata.getBlock() != Blocks.TNT) world.removeBlock(blockposition, false); // Paper - TNTPrimeEvent; We might be cancelling it below, move the setAir down
}
Block block = iblockdata.getBlock();
if (block instanceof TntBlock) {
- TntBlock.explode(world, pos);
+ // Paper start - TNTPrimeEvent
+ org.bukkit.block.Block tntBlock = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition);
+ if (!new com.destroystokyo.paper.event.block.TNTPrimeEvent(tntBlock, com.destroystokyo.paper.event.block.TNTPrimeEvent.PrimeReason.FIRE, null).callEvent()) {
+ return;
+ }
+ world.removeBlock(blockposition, false);
+ // Paper end - TNTPrimeEvent
+ TntBlock.explode(world, blockposition);
}
}
@@ -310,13 +368,15 @@
}
@Override
- protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
- super.onPlace(state, world, pos, oldState, notify);
- world.scheduleTick(pos, (Block) this, FireBlock.getFireTickDelay(world.random));
+ // CraftBukkit start - context
+ protected void onPlace(BlockState iblockdata, Level world, BlockPos blockposition, BlockState iblockdata1, boolean flag, UseOnContext context) {
+ super.onPlace(iblockdata, world, blockposition, iblockdata1, flag, context);
+ // CraftBukkit end
+ world.scheduleTick(blockposition, (Block) this, FireBlock.getFireTickDelay(world)); // Paper - Add fire-tick-delay option
}
- private static int getFireTickDelay(RandomSource random) {
- return 30 + random.nextInt(10);
+ private static int getFireTickDelay(Level world) { // Paper - Add fire-tick-delay option
+ return world.paperConfig().environment.fireTickDelay + world.random.nextInt(10); // Paper - Add fire-tick-delay option
}
@Override

View File

@@ -0,0 +1,40 @@
--- a/net/minecraft/world/level/block/FlowerPotBlock.java
+++ b/net/minecraft/world/level/block/FlowerPotBlock.java
@@ -63,6 +63,18 @@
} else if (!this.isEmpty()) {
return InteractionResult.CONSUME;
} else {
+ // Paper start - Add PlayerFlowerPotManipulateEvent
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos);
+ org.bukkit.inventory.ItemStack placedStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack);
+
+ io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent event = new io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent((org.bukkit.entity.Player) player.getBukkitEntity(), block, placedStack, true);
+ if (!event.callEvent()) {
+ // Update client
+ player.containerMenu.sendAllDataToRemote();
+
+ return InteractionResult.CONSUME;
+ }
+ // Paper end - Add PlayerFlowerPotManipulateEvent
world.setBlock(pos, blockState, 3);
world.gameEvent(player, GameEvent.BLOCK_CHANGE, pos);
player.awardStat(Stats.POT_FLOWER);
@@ -77,6 +89,18 @@
return InteractionResult.CONSUME;
} else {
ItemStack itemStack = new ItemStack(this.potted);
+ // Paper start - Add PlayerFlowerPotManipulateEvent
+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos);
+ org.bukkit.inventory.ItemStack pottedStack = new org.bukkit.inventory.ItemStack(org.bukkit.craftbukkit.block.CraftBlockType.minecraftToBukkit(this.potted));
+
+ io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent event = new io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent((org.bukkit.entity.Player) player.getBukkitEntity(), block, pottedStack, false);
+ if (!event.callEvent()) {
+ // Update client
+ player.containerMenu.sendAllDataToRemote();
+
+ return InteractionResult.PASS;
+ }
+ // Paper end - Add PlayerFlowerPotManipulateEvent
if (!player.addItem(itemStack)) {
player.drop(itemStack, false);
}

View File

@@ -0,0 +1,31 @@
--- a/net/minecraft/world/level/block/FrogspawnBlock.java
+++ b/net/minecraft/world/level/block/FrogspawnBlock.java
@@ -89,6 +89,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (entity.getType().equals(EntityType.FALLING_BLOCK)) {
this.destroyBlock(world, pos);
}
@@ -101,6 +102,11 @@
}
private void hatchFrogspawn(ServerLevel world, BlockPos pos, RandomSource random) {
+ // Paper start - Call BlockFadeEvent
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.AIR.defaultBlockState()).isCancelled()) {
+ return;
+ }
+ // Paper end - Call BlockFadeEvent
this.destroyBlock(world, pos);
world.playSound(null, pos, SoundEvents.FROGSPAWN_HATCH, SoundSource.BLOCKS, 1.0F, 1.0F);
this.spawnTadpoles(world, pos, random);
@@ -121,7 +127,7 @@
int k = random.nextInt(1, 361);
tadpole.moveTo(d, (double)pos.getY() - 0.5, e, (float)k, 0.0F);
tadpole.setPersistenceRequired();
- world.addFreshEntity(tadpole);
+ world.addFreshEntity(tadpole, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); // Paper - use correct spawn reason
}
}
}

View File

@@ -0,0 +1,24 @@
--- a/net/minecraft/world/level/block/FrostedIceBlock.java
+++ b/net/minecraft/world/level/block/FrostedIceBlock.java
@@ -42,6 +42,7 @@
@Override
protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
+ if (!world.paperConfig().environment.frostedIce.enabled) return; // Paper - Frosted ice options
if ((random.nextInt(3) == 0 || this.fewerNeigboursThan(world, pos, 4))
&& world.getMaxLocalRawBrightness(pos) > 11 - state.getValue(AGE) - state.getLightBlock()
&& this.slightlyMelt(state, world, pos)) {
@@ -51,11 +52,11 @@
mutableBlockPos.setWithOffset(pos, direction);
BlockState blockState = world.getBlockState(mutableBlockPos);
if (blockState.is(this) && !this.slightlyMelt(blockState, world, mutableBlockPos)) {
- world.scheduleTick(mutableBlockPos, this, Mth.nextInt(random, 20, 40));
+ world.scheduleTick(mutableBlockPos, this, Mth.nextInt(random, world.paperConfig().environment.frostedIce.delay.min, world.paperConfig().environment.frostedIce.delay.max)); // Paper - Frosted ice options
}
}
} else {
- world.scheduleTick(pos, this, Mth.nextInt(random, 20, 40));
+ world.scheduleTick(pos, this, Mth.nextInt(random, world.paperConfig().environment.frostedIce.delay.min, world.paperConfig().environment.frostedIce.delay.max)); // Paper - Frosted ice options
}
}

View File

@@ -0,0 +1,16 @@
--- a/net/minecraft/world/level/block/FungusBlock.java
+++ b/net/minecraft/world/level/block/FungusBlock.java
@@ -74,6 +74,13 @@
@Override
public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
this.getFeature(world).ifPresent((holder) -> {
+ // CraftBukkit start
+ if (this == Blocks.WARPED_FUNGUS) {
+ SaplingBlock.treeType = org.bukkit.TreeType.WARPED_FUNGUS;
+ } else if (this == Blocks.CRIMSON_FUNGUS) {
+ SaplingBlock.treeType = org.bukkit.TreeType.CRIMSON_FUNGUS;
+ }
+ // CraftBukkit end
((ConfiguredFeature) holder.value()).place(world, world.getChunkSource().getGenerator(), random, pos);
});
}

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/world/level/block/FurnaceBlock.java
+++ b/net/minecraft/world/level/block/FurnaceBlock.java
@@ -45,8 +45,7 @@
@Override
protected void openContainer(Level world, BlockPos pos, Player player) {
BlockEntity blockEntity = world.getBlockEntity(pos);
- if (blockEntity instanceof FurnaceBlockEntity) {
- player.openMenu((MenuProvider)blockEntity);
+ if (blockEntity instanceof FurnaceBlockEntity && player.openMenu((MenuProvider)blockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_FURNACE);
}
}

View File

@@ -0,0 +1,13 @@
--- a/net/minecraft/world/level/block/GrindstoneBlock.java
+++ b/net/minecraft/world/level/block/GrindstoneBlock.java
@@ -152,8 +152,9 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_GRINDSTONE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;

View File

@@ -0,0 +1,39 @@
--- a/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
+++ b/net/minecraft/world/level/block/GrowingPlantHeadBlock.java
@@ -44,16 +44,34 @@
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
- if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < this.growPerTickProbability) {
+ // Spigot start
+ int modifier;
+ if (this == Blocks.KELP) {
+ modifier = world.spigotConfig.kelpModifier;
+ } else if (this == Blocks.TWISTING_VINES) {
+ modifier = world.spigotConfig.twistingVinesModifier;
+ } else if (this == Blocks.WEEPING_VINES) {
+ modifier = world.spigotConfig.weepingVinesModifier;
+ } else {
+ modifier = world.spigotConfig.caveVinesModifier;
+ }
+ if ((Integer) state.getValue(GrowingPlantHeadBlock.AGE) < 25 && random.nextDouble() < ((modifier / 100.0D) * this.growPerTickProbability)) { // Spigot - SPIGOT-7159: Better modifier resolution
+ // Spigot end
BlockPos blockposition1 = pos.relative(this.growthDirection);
if (this.canGrowInto(world.getBlockState(blockposition1))) {
- world.setBlockAndUpdate(blockposition1, this.getGrowIntoState(state, world.random));
+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, this.getGrowIntoState(state, world.random, world)); // CraftBukkit // Paper - Fix Spigot growth modifiers
}
}
}
+ // Paper start - Fix Spigot growth modifiers
+ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) {
+ return this.getGrowIntoState(state, random);
+ }
+ // Paper end - Fix Spigot growth modifiers
+
protected BlockState getGrowIntoState(BlockState state, RandomSource random) {
return (BlockState) state.cycle(GrowingPlantHeadBlock.AGE);
}

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/HoneyBlock.java
+++ b/net/minecraft/world/level/block/HoneyBlock.java
@@ -60,6 +60,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (this.isSlidingDown(pos, entity)) {
this.maybeDoSlideAchievement(entity, pos);
this.doSlideMovement(entity);

View File

@@ -0,0 +1,20 @@
--- a/net/minecraft/world/level/block/HopperBlock.java
+++ b/net/minecraft/world/level/block/HopperBlock.java
@@ -125,8 +125,7 @@
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (!world.isClientSide && world.getBlockEntity(pos) instanceof HopperBlockEntity hopperBlockEntity) {
- player.openMenu(hopperBlockEntity);
+ if (!world.isClientSide && world.getBlockEntity(pos) instanceof HopperBlockEntity hopperBlockEntity && player.openMenu(hopperBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INSPECT_HOPPER);
}
@@ -178,6 +177,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
BlockEntity blockEntity = world.getBlockEntity(pos);
if (blockEntity instanceof HopperBlockEntity) {
HopperBlockEntity.entityInside(world, pos, state, entity, (HopperBlockEntity)blockEntity);

View File

@@ -0,0 +1,34 @@
--- a/net/minecraft/world/level/block/HugeMushroomBlock.java
+++ b/net/minecraft/world/level/block/HugeMushroomBlock.java
@@ -45,6 +45,7 @@
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return this.defaultBlockState(); // Paper - add option to disable block updates
BlockGetter blockGetter = ctx.getLevel();
BlockPos blockPos = ctx.getClickedPos();
return this.defaultBlockState()
@@ -67,6 +68,7 @@
BlockState neighborState,
RandomSource random
) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return state; // Paper - add option to disable block updates
return neighborState.is(this)
? state.setValue(PROPERTY_BY_DIRECTION.get(direction), Boolean.valueOf(false))
: super.updateShape(state, world, tickView, pos, direction, neighborPos, neighborState, random);
@@ -74,6 +76,7 @@
@Override
protected BlockState rotate(BlockState state, Rotation rotation) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return state; // Paper - add option to disable block updates
return state.setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.NORTH)), state.getValue(NORTH))
.setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.SOUTH)), state.getValue(SOUTH))
.setValue(PROPERTY_BY_DIRECTION.get(rotation.rotate(Direction.EAST)), state.getValue(EAST))
@@ -84,6 +87,7 @@
@Override
protected BlockState mirror(BlockState state, Mirror mirror) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableMushroomBlockUpdates) return state; // Paper - add option to disable block updates
return state.setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.NORTH)), state.getValue(NORTH))
.setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.SOUTH)), state.getValue(SOUTH))
.setValue(PROPERTY_BY_DIRECTION.get(mirror.mirror(Direction.EAST)), state.getValue(EAST))

View File

@@ -0,0 +1,30 @@
--- a/net/minecraft/world/level/block/IceBlock.java
+++ b/net/minecraft/world/level/block/IceBlock.java
@@ -34,8 +34,13 @@
}
@Override
- public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) {
- super.playerDestroy(world, player, pos, state, blockEntity, tool);
+ public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool, boolean includeDrops, boolean dropExp) { // Paper - fix drops not preventing stats/food exhaustion
+ super.playerDestroy(world, player, pos, state, blockEntity, tool, includeDrops, dropExp); // Paper - fix drops not preventing stats/food exhaustion
+ // Paper start - Improve Block#breakNaturally API
+ this.afterDestroy(world, pos, tool);
+ }
+ public void afterDestroy(Level world, BlockPos pos, ItemStack tool) {
+ // Paper end - Improve Block#breakNaturally API
if (!EnchantmentHelper.hasTag(tool, EnchantmentTags.PREVENTS_ICE_MELTING)) {
if (world.dimensionType().ultraWarm()) {
world.removeBlock(pos, false);
@@ -60,6 +65,11 @@
}
protected void melt(BlockState state, Level world, BlockPos pos) {
+ // CraftBukkit start
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, world.dimensionType().ultraWarm() ? Blocks.AIR.defaultBlockState() : Blocks.WATER.defaultBlockState()).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
if (world.dimensionType().ultraWarm()) {
world.removeBlock(pos, false);
} else {

View File

@@ -0,0 +1,19 @@
--- a/net/minecraft/world/level/block/InfestedBlock.java
+++ b/net/minecraft/world/level/block/InfestedBlock.java
@@ -19,6 +19,7 @@
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit
public class InfestedBlock extends Block {
@@ -54,7 +55,7 @@
if (entitysilverfish != null) {
entitysilverfish.moveTo((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D, 0.0F, 0.0F);
- world.addFreshEntity(entitysilverfish);
+ world.addFreshEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason
entitysilverfish.spawnAnim();
}

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/world/level/block/LavaCauldronBlock.java
+++ b/net/minecraft/world/level/block/LavaCauldronBlock.java
@@ -32,6 +32,7 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (this.isEntityInsideContent(state, pos, entity)) {
entity.lavaHurt();
}

View File

@@ -0,0 +1,127 @@
--- a/net/minecraft/world/level/block/LayeredCauldronBlock.java
+++ b/net/minecraft/world/level/block/LayeredCauldronBlock.java
@@ -17,6 +17,11 @@
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlockState;
+import org.bukkit.craftbukkit.block.CraftBlockStates;
+import org.bukkit.event.block.CauldronLevelChangeEvent;
+// CraftBukkit end
public class LayeredCauldronBlock extends AbstractCauldronBlock {
@@ -62,41 +67,86 @@
@Override
protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
if (world instanceof ServerLevel worldserver) {
if (entity.isOnFire() && this.isEntityInsideContent(state, pos, entity)) {
- entity.clearFire();
- if (entity.mayInteract(worldserver, pos)) {
- this.handleEntityOnFireInside(state, world, pos);
+ // CraftBukkit start - moved down
+ // entity.clearFire();
+ if ((entity instanceof net.minecraft.world.entity.player.Player || worldserver.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_MOBGRIEFING)) && entity.mayInteract(worldserver, pos)) { // Paper - Fixes MC-248588
+ if (this.handleEntityOnFireInsideWithEvent(state, world, pos, entity)) { // Paper - fix powdered snow cauldron extinguishing entities
+ entity.clearFire();
+ }
+ // CraftBukkit end
}
}
}
}
- private void handleEntityOnFireInside(BlockState state, Level world, BlockPos pos) {
+ // CraftBukkit start
+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - fix powdered snow cauldron extinguishing entities; use #handleEntityOnFireInsideWithEvent
+ private boolean handleEntityOnFireInside(BlockState iblockdata, Level world, BlockPos blockposition, Entity entity) {
if (this.precipitationType == Biome.Precipitation.SNOW) {
- LayeredCauldronBlock.lowerFillLevel((BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, (Integer) state.getValue(LayeredCauldronBlock.LEVEL)), world, pos);
+ return LayeredCauldronBlock.lowerFillLevel((BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, (Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL)), world, blockposition, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH);
} else {
- LayeredCauldronBlock.lowerFillLevel(state, world, pos);
+ return LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH);
+ // CraftBukkit end
}
}
+ // Paper start - fix powdered snow cauldron extinguishing entities
+ protected boolean handleEntityOnFireInsideWithEvent(BlockState state, Level world, BlockPos pos, Entity entity) {
+ if (this.precipitationType == Biome.Precipitation.SNOW) {
+ return LayeredCauldronBlock.lowerFillLevel((BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, (Integer) state.getValue(LayeredCauldronBlock.LEVEL)), world, pos, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH);
+ } else {
+ return LayeredCauldronBlock.lowerFillLevel(state, world, pos, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH);
+ }
+ }
+ // Paper end - fix powdered snow cauldron extinguishing entities
public static void lowerFillLevel(BlockState state, Level world, BlockPos pos) {
- int i = (Integer) state.getValue(LayeredCauldronBlock.LEVEL) - 1;
- BlockState iblockdata1 = i == 0 ? Blocks.CAULDRON.defaultBlockState() : (BlockState) state.setValue(LayeredCauldronBlock.LEVEL, i);
+ // CraftBukkit start
+ LayeredCauldronBlock.lowerFillLevel(state, world, pos, null, CauldronLevelChangeEvent.ChangeReason.UNKNOWN);
+ }
- world.setBlockAndUpdate(pos, iblockdata1);
- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(iblockdata1));
+ public static boolean lowerFillLevel(BlockState iblockdata, Level world, BlockPos blockposition, Entity entity, CauldronLevelChangeEvent.ChangeReason reason) {
+ int i = (Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL) - 1;
+ BlockState iblockdata1 = i == 0 ? Blocks.CAULDRON.defaultBlockState() : (BlockState) iblockdata.setValue(LayeredCauldronBlock.LEVEL, i);
+
+ return LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata1, entity, reason);
}
+ // CraftBukkit start
+ // Paper start - Call CauldronLevelChangeEvent
+ public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, @javax.annotation.Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason) { // Paper - entity is nullable
+ return changeLevel(iblockdata, world, blockposition, newBlock, entity, reason, true);
+ }
+
+ public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, @javax.annotation.Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent) { // Paper - entity is nullable
+ // Paper end - Call CauldronLevelChangeEvent
+ CraftBlockState newState = CraftBlockStates.getBlockState(world, blockposition);
+ newState.setData(newBlock);
+
+ CauldronLevelChangeEvent event = new CauldronLevelChangeEvent(
+ world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()),
+ (entity == null) ? null : entity.getBukkitEntity(), reason, newState
+ );
+ world.getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+ newState.update(true);
+ if (sendGameEvent) world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(newBlock)); // Paper - Call CauldronLevelChangeEvent
+ return true;
+ }
+ // CraftBukkit end
+
@Override
public void handlePrecipitation(BlockState state, Level world, BlockPos pos, Biome.Precipitation precipitation) {
if (CauldronBlock.shouldHandlePrecipitation(world, precipitation) && (Integer) state.getValue(LayeredCauldronBlock.LEVEL) != 3 && precipitation == this.precipitationType) {
BlockState iblockdata1 = (BlockState) state.cycle(LayeredCauldronBlock.LEVEL);
- world.setBlockAndUpdate(pos, iblockdata1);
- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(iblockdata1));
+ LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL); // CraftBukkit
}
}
@@ -115,8 +165,11 @@
if (!this.isFull(state)) {
BlockState iblockdata1 = (BlockState) state.setValue(LayeredCauldronBlock.LEVEL, (Integer) state.getValue(LayeredCauldronBlock.LEVEL) + 1);
- world.setBlockAndUpdate(pos, iblockdata1);
- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(iblockdata1));
+ // CraftBukkit start
+ if (!LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) {
+ return;
+ }
+ // CraftBukkit end
world.levelEvent(1047, pos, 0);
}
}

View File

@@ -0,0 +1,25 @@
--- a/net/minecraft/world/level/block/LeavesBlock.java
+++ b/net/minecraft/world/level/block/LeavesBlock.java
@@ -26,6 +26,7 @@
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit
public class LeavesBlock extends Block implements SimpleWaterloggedBlock {
@@ -59,6 +60,14 @@
@Override
protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
if (this.decaying(state)) {
+ // CraftBukkit start
+ LeavesDecayEvent event = new LeavesDecayEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()));
+ world.getCraftServer().getPluginManager().callEvent(event);
+
+ if (event.isCancelled() || world.getBlockState(pos).getBlock() != this) {
+ return;
+ }
+ // CraftBukkit end
dropResources(state, world, pos);
world.removeBlock(pos, false);
}

View File

@@ -0,0 +1,69 @@
--- a/net/minecraft/world/level/block/LecternBlock.java
+++ b/net/minecraft/world/level/block/LecternBlock.java
@@ -153,7 +153,24 @@
BlockEntity tileentity = world.getBlockEntity(pos);
if (tileentity instanceof LecternBlockEntity tileentitylectern) {
- tileentitylectern.setBook(stack.consumeAndReturn(1, user));
+ // Paper start - Add PlayerInsertLecternBookEvent
+ ItemStack eventSourcedBookStack = null;
+ if (user instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) {
+ final io.papermc.paper.event.player.PlayerInsertLecternBookEvent event = new io.papermc.paper.event.player.PlayerInsertLecternBookEvent(
+ serverPlayer.getBukkitEntity(),
+ org.bukkit.craftbukkit.block.CraftBlock.at(world, pos),
+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack.copyWithCount(1))
+ );
+ if (!event.callEvent()) return;
+ eventSourcedBookStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getBook());
+ }
+ if (eventSourcedBookStack == null) {
+ eventSourcedBookStack = stack.consumeAndReturn(1, user);
+ } else {
+ stack.consume(1, user);
+ }
+ tileentitylectern.setBook(eventSourcedBookStack);
+ // Paper end - Add PlayerInsertLecternBookEvent
LecternBlock.resetBookState(user, world, pos, state, true);
world.playSound((Player) null, pos, SoundEvents.BOOK_PUT, SoundSource.BLOCKS, 1.0F, 1.0F);
}
@@ -175,6 +192,16 @@
}
private static void changePowered(Level world, BlockPos pos, BlockState state, boolean powered) {
+ // Paper start - Call BlockRedstoneEvent properly
+ final int currentRedstoneLevel = state.getValue(LecternBlock.POWERED) ? 15 : 0, targetRedstoneLevel = powered ? 15 : 0;
+ if (currentRedstoneLevel != targetRedstoneLevel) {
+ final org.bukkit.event.block.BlockRedstoneEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, pos, currentRedstoneLevel, targetRedstoneLevel);
+
+ if (event.getNewCurrent() != targetRedstoneLevel) {
+ return;
+ }
+ }
+ // Paper end - Call BlockRedstoneEvent properly
world.setBlock(pos, (BlockState) state.setValue(LecternBlock.POWERED, powered), 3);
LecternBlock.updateBelow(world, pos, state);
}
@@ -206,11 +233,12 @@
}
private void popBook(BlockState state, Level world, BlockPos pos) {
- BlockEntity tileentity = world.getBlockEntity(pos);
+ BlockEntity tileentity = world.getBlockEntity(pos, false); // CraftBukkit - don't validate, type may be changed already
if (tileentity instanceof LecternBlockEntity tileentitylectern) {
Direction enumdirection = (Direction) state.getValue(LecternBlock.FACING);
ItemStack itemstack = tileentitylectern.getBook().copy();
+ if (itemstack.isEmpty()) return; // CraftBukkit - SPIGOT-5500
float f = 0.25F * (float) enumdirection.getStepX();
float f1 = 0.25F * (float) enumdirection.getStepZ();
ItemEntity entityitem = new ItemEntity(world, (double) pos.getX() + 0.5D + (double) f, (double) (pos.getY() + 1), (double) pos.getZ() + 0.5D + (double) f1, itemstack);
@@ -282,8 +310,7 @@
private void openScreen(Level world, BlockPos pos, Player player) {
BlockEntity tileentity = world.getBlockEntity(pos);
- if (tileentity instanceof LecternBlockEntity) {
- player.openMenu((LecternBlockEntity) tileentity);
+ if (tileentity instanceof LecternBlockEntity && player.openMenu((LecternBlockEntity) tileentity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_LECTERN);
}

View File

@@ -0,0 +1,31 @@
--- a/net/minecraft/world/level/block/LeverBlock.java
+++ b/net/minecraft/world/level/block/LeverBlock.java
@@ -31,6 +31,7 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
public class LeverBlock extends FaceAttachedHorizontalDirectionalBlock {
@@ -102,6 +103,20 @@
LeverBlock.makeParticle(iblockdata1, world, pos, 1.0F);
}
} else {
+ // CraftBukkit start - Interact Lever
+ boolean powered = state.getValue(LeverBlock.POWERED); // Old powered state
+ org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+ int old = (powered) ? 15 : 0;
+ int current = (!powered) ? 15 : 0;
+
+ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
+ world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+
+ if ((eventRedstone.getNewCurrent() > 0) != (!powered)) {
+ return InteractionResult.SUCCESS;
+ }
+ // CraftBukkit end
+
this.pull(state, world, pos, (Player) null);
}

Some files were not shown because too many files have changed in this diff Show More