diff --git a/patches/server/0003-Add-BlockDestroyedByNeighborEvent.patch b/patches/server/0003-Add-BlockDestroyedByNeighborEvent.patch new file mode 100644 index 000000000..6eac51249 --- /dev/null +++ b/patches/server/0003-Add-BlockDestroyedByNeighborEvent.patch @@ -0,0 +1,143 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cryptite +Date: Mon, 10 Apr 2023 07:30:29 -0500 +Subject: [PATCH] Add BlockDestroyedByNeighborEvent + + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +index 58f972832c39a27a8ccd606f9144e1c54adbf6f3..4f00c2e8d6ff3a03a334542f699c5e35bfd03ce8 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -419,6 +419,7 @@ public class ServerPlayerGameMode { + this.level.captureDrops = new ArrayList<>(); + // CraftBukkit end + BlockState iblockdata1 = block.playerWillDestroy(this.level, pos, iblockdata, this.player); ++ level.pendingPlayerBlockEvents.put(pos, new Level.PendingBlockEvent(pos, this.player)); // Paper + boolean flag = this.level.removeBlock(pos, false); + + if (flag) { +@@ -446,6 +447,7 @@ public class ServerPlayerGameMode { + // CraftBukkit start + java.util.List itemsToDrop = this.level.captureDrops; // Paper - store current list + this.level.captureDrops = null; // Paper - Remove this earlier so that we can actually drop stuff ++ level.pendingPlayerBlockEvents.remove(pos); // Paper + if (event.isDropItems()) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, itemsToDrop); // Paper - use stored ref + } +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index de277d61b718fe07a87d75a2547bb1c7f8553aa1..625a852db818d95365ad7ae56e6b1de541bbdada 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -378,6 +378,7 @@ public final class ItemStack { + CompoundTag oldData = this.getTagClone(); + int oldCount = this.getCount(); + ServerLevel world = (ServerLevel) context.getLevel(); ++ if (entityhuman != null) world.pendingPlayerBlockEvents.put(blockposition, new Level.PendingBlockEvent(blockposition, entityhuman)); // Paper + + if (!(item instanceof BucketItem/* || item instanceof SolidBucketItem*/)) { // if not bucket // Paper - capture block states for snow buckets + world.captureBlockStates = true; +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index fe62e872f0c989f612dcbfc58894bd1787345d25..465bbaeff36f4d03db40fcaf9b6ab98550316afb 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -187,6 +187,27 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions + public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here + ++ // Paper start - Holder class used to track what Player is responsible the last block event ++ public static class PendingBlockEvent { ++ ++ public final BlockPos block; ++ public final Player player; ++ public @Nullable BlockPos sourceBlock; ++ ++ public PendingBlockEvent(BlockPos block, Player player) { ++ this(block, player, null); ++ } ++ ++ public PendingBlockEvent(BlockPos block, Player player, @Nullable BlockPos sourceBlock) { ++ this.block = block; ++ this.player = player; ++ this.sourceBlock = sourceBlock; ++ } ++ ++ } ++ public final Map pendingPlayerBlockEvents = new HashMap<>(); ++ // Paper end ++ + // Paper start - fix and optimise world upgrading + // copied from below + public static ResourceKey getDimensionKey(DimensionType manager) { +@@ -1056,6 +1077,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + if (!this.preventPoiUpdated) { + this.onBlockStateChange(blockposition, iblockdata1, iblockdata2); + } ++ pendingPlayerBlockEvents.remove(blockposition); // Paper + // CraftBukkit end + } + } +@@ -1077,6 +1099,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + if (iblockdata.isAir()) { + return false; + } else { ++ // Paper start ++ PendingBlockEvent blockEvent = pendingPlayerBlockEvents.get(pos); ++ if (blockEvent != null && blockEvent.sourceBlock != null) { ++ io.papermc.paper.event.block.BlockDestroyedByNeighborEvent event = ++ new io.papermc.paper.event.block.BlockDestroyedByNeighborEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this, pos), ++ (org.bukkit.entity.Player) blockEvent.player.getBukkitEntity(), ++ org.bukkit.craftbukkit.block.CraftBlock.at(this, blockEvent.sourceBlock)); ++ event.callEvent(); ++ } ++ // Paper end ++ + FluidState fluid = this.getFluidState(pos); + // Paper start - 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, +diff --git a/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java b/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java +index 81d2140351775ad55546af52eb635ccdc8509d89..0ed9f6ae968c06e63e1431ed1ce153dd1e90e908 100644 +--- a/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/DoublePlantBlock.java +@@ -112,6 +112,15 @@ public class DoublePlantBlock extends BushBlock { + BlockPos blockposition1 = pos.below(); + BlockState iblockdata1 = world.getBlockState(blockposition1); + ++ Level.PendingBlockEvent blockEvent = world.pendingPlayerBlockEvents.remove(pos); ++ if (blockEvent != null) { ++ io.papermc.paper.event.block.BlockDestroyedByNeighborEvent event = ++ new io.papermc.paper.event.block.BlockDestroyedByNeighborEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition1), ++ (org.bukkit.entity.Player) blockEvent.player.getBukkitEntity(), ++ org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)); ++ if (!event.callEvent()) return; ++ } ++ + if (iblockdata1.is(state.getBlock()) && iblockdata1.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.LOWER) { + BlockState iblockdata2 = iblockdata1.getFluidState().is((Fluid) Fluids.WATER) ? Blocks.WATER.defaultBlockState() : Blocks.AIR.defaultBlockState(); + +diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +index 3ab8b99837b1d1faea722c598b0228b2780be8b1..45bd7b9e684f71d9186a33277e5772dc7e04e9da 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -1242,11 +1242,22 @@ public abstract class BlockBehaviour implements FeatureElement { + BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); + Direction[] aenumdirection = BlockBehaviour.UPDATE_SHAPE_ORDER; + int k = aenumdirection.length; ++ BlockState blockState = world.getBlockState(pos); // Slice + + for (int l = 0; l < k; ++l) { + Direction enumdirection = aenumdirection[l]; + + blockposition_mutableblockposition.setWithOffset(pos, enumdirection); ++ // Paper start - Propagate the PendingBlockEvent from the current blockPos to the next block ++ // if it will break (!canSurvive) ++ if (blockState.getBukkitMaterial() == org.bukkit.Material.AIR && !world.getBlockState(blockposition_mutableblockposition).canSurvive(world, blockposition_mutableblockposition)) { ++ Level.PendingBlockEvent blockEvent = ((Level) world).pendingPlayerBlockEvents.get(pos); ++ if (blockEvent != null) { ++ BlockPos blockPosCopy = blockposition_mutableblockposition.immutable(); ++ ((Level) world).pendingPlayerBlockEvents.put(blockPosCopy, new Level.PendingBlockEvent(blockPosCopy, blockEvent.player, pos)); ++ } ++ } ++ // Paper end + world.neighborShapeChanged(enumdirection.getOpposite(), this.asState(), blockposition_mutableblockposition, pos, flags, maxUpdateDepth); + } + diff --git a/patches/server/0007-Add-PlayerGetRespawnLocationEvent.patch b/patches/server/0007-Add-PlayerGetRespawnLocationEvent.patch index 936933157..feebd59e6 100644 --- a/patches/server/0007-Add-PlayerGetRespawnLocationEvent.patch +++ b/patches/server/0007-Add-PlayerGetRespawnLocationEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add PlayerGetRespawnLocationEvent diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index e2eb3feb786d6348b6d45dd9babb7b36eebccd6e..5c7721aa768ec3b2ad659b7242681b4a04153db9 100644 +index e2eb3feb786d6348b6d45dd9babb7b36eebccd6e..f54e00f11e92d63aa241a3b8693afa5f1797d1ab 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -867,16 +867,24 @@ public abstract class PlayerList { +@@ -867,49 +867,57 @@ public abstract class PlayerList { // CraftBukkit start - fire PlayerRespawnEvent if (location == null) { @@ -40,11 +40,18 @@ index e2eb3feb786d6348b6d45dd9babb7b36eebccd6e..5c7721aa768ec3b2ad659b7242681b4a + optional = Optional.empty(); + } - if (optional.isPresent()) { - BlockState iblockdata = worldserver1.getBlockState(blockposition); -@@ -885,13 +893,13 @@ public abstract class PlayerList { - Vec3 vec3d = (Vec3) optional.get(); - float f1; +- if (optional.isPresent()) { +- BlockState iblockdata = worldserver1.getBlockState(blockposition); +- boolean flag3 = iblockdata.is(Blocks.RESPAWN_ANCHOR); +- isAnchorSpawn = flag3; // Paper - Fix PlayerRespawnEvent +- Vec3 vec3d = (Vec3) optional.get(); +- float f1; ++ if (optional.isPresent()) { ++ BlockState iblockdata = worldserver1.getBlockState(blockposition); ++ boolean flag3 = iblockdata.is(Blocks.RESPAWN_ANCHOR); ++ isAnchorSpawn = flag3; // Paper - Fix PlayerRespawnEvent ++ Vec3 vec3d = (Vec3) optional.get(); ++ float f1; - if (!iblockdata.is(BlockTags.BEDS) && !flag3) { - f1 = f; @@ -60,5 +67,36 @@ index e2eb3feb786d6348b6d45dd9babb7b36eebccd6e..5c7721aa768ec3b2ad659b7242681b4a + f1 = (float) Mth.wrapDegrees(Mth.atan2(vec3d1.z, vec3d1.x) * 57.2957763671875D - 90.0D); + } - // entityplayer1.setRespawnPosition(worldserver1.dimension(), blockposition, f, flag1, false); // CraftBukkit - not required, just copies old location into reused entity - flag2 = !flag && flag3; +- // entityplayer1.setRespawnPosition(worldserver1.dimension(), blockposition, f, flag1, false); // CraftBukkit - not required, just copies old location into reused entity +- flag2 = !flag && flag3; +- isBedSpawn = true; +- location = CraftLocation.toBukkit(vec3d, worldserver1.getWorld(), f1, 0.0F); +- } else if (blockposition != null) { +- entityplayer1.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F)); +- entityplayer1.setRespawnPosition(null, null, 0f, false, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN); // CraftBukkit - SPIGOT-5988: Clear respawn location when obstructed // Paper - Add PlayerSetSpawnEvent ++ // entityplayer1.setRespawnPosition(worldserver1.dimension(), blockposition, f, flag1, false); // CraftBukkit - not required, just copies old location into reused entity ++ flag2 = !flag && flag3; ++ isBedSpawn = true; ++ location = CraftLocation.toBukkit(vec3d, worldserver1.getWorld(), f1, 0.0F); ++ } else if (blockposition != null) { ++ entityplayer1.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F)); ++ entityplayer1.setRespawnPosition(null, null, 0f, false, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN); // CraftBukkit - SPIGOT-5988: Clear respawn location when obstructed // Paper - Add PlayerSetSpawnEvent ++ } + } +- } + +- if (location == null) { +- worldserver1 = this.server.getLevel(Level.OVERWORLD); +- blockposition = entityplayer1.getSpawnPoint(worldserver1); +- location = CraftLocation.toBukkit(blockposition, worldserver1.getWorld(), worldserver1.levelData.getSpawnAngle(), 0.0F).add(0.5F, 0.1F, 0.5F); // Paper - Expose world spawn angle ++ if (location == null) { ++ worldserver1 = this.server.getLevel(Level.OVERWORLD); ++ blockposition = entityplayer1.getSpawnPoint(worldserver1); ++ location = CraftLocation.toBukkit(blockposition, worldserver1.getWorld(), worldserver1.levelData.getSpawnAngle(), 0.0F).add(0.5F, 0.1F, 0.5F); // Paper - Expose world spawn angle ++ } + } + +- Player respawnPlayer = entityplayer1.getBukkitEntity(); + PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn && !isAnchorSpawn, isAnchorSpawn, reason, com.google.common.collect.ImmutableSet.builder().add(respawnFlags)); // Paper - PlayerRespawnEvent changes + this.cserver.getPluginManager().callEvent(respawnEvent); + // Spigot Start