diff --git a/lophine-server/minecraft-patches/features/0010-Leaves-Configurable-trading-with-the-void.patch b/lophine-server/minecraft-patches/features/0010-Leaves-Configurable-trading-with-the-void.patch index ce576b4..dd16abe 100644 --- a/lophine-server/minecraft-patches/features/0010-Leaves-Configurable-trading-with-the-void.patch +++ b/lophine-server/minecraft-patches/features/0010-Leaves-Configurable-trading-with-the-void.patch @@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 985ddea9d74a37f58f32a27375f6f7e6af0f2192..9796c449f924b8fd9c0dd6bb02cf196641e495ed 100644 +index 923aca94520f456a13d4e4eb23e1499b3dbc920f..0ee9e51c5abfd9269bcf0bb29c4b8394b27af02a 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -2901,7 +2901,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe diff --git a/lophine-server/minecraft-patches/features/0015-Leaves-Servux-Protocol.patch b/lophine-server/minecraft-patches/features/0015-Leaves-Servux-Protocol.patch index 2cdfbfd..7f90f8a 100644 --- a/lophine-server/minecraft-patches/features/0015-Leaves-Servux-Protocol.patch +++ b/lophine-server/minecraft-patches/features/0015-Leaves-Servux-Protocol.patch @@ -23,7 +23,7 @@ index d0d90a25a10bbecfffceee1992af88c60d14fd87..187e48e78e69c80b25710f193813335e + // Leaves end - servux } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 9796c449f924b8fd9c0dd6bb02cf196641e495ed..4f1976c47a087bf4f36687124ef29ca7b664f682 100644 +index 0ee9e51c5abfd9269bcf0bb29c4b8394b27af02a..d1a799199a68771ae3130a30a59625587c11b0b0 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -2368,6 +2368,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe diff --git a/lophine-server/minecraft-patches/features/0033-Leaves-Catch-update-suppression-crash.patch b/lophine-server/minecraft-patches/features/0033-Leaves-Catch-update-suppression-crash.patch new file mode 100644 index 0000000..b840ba1 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0033-Leaves-Catch-update-suppression-crash.patch @@ -0,0 +1,217 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bacteriawa +Date: Sat, 15 Nov 2025 01:06:20 +0800 +Subject: [PATCH] Leaves: Catch update suppression crash + +Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/network/protocol/PacketUtils.java b/net/minecraft/network/protocol/PacketUtils.java +index 7280ca2250ade4166a1a883b205bbc89f4e00d1a..06268ac3420fc03a798a20bb617ba89a032df527 100644 +--- a/net/minecraft/network/protocol/PacketUtils.java ++++ b/net/minecraft/network/protocol/PacketUtils.java +@@ -30,7 +30,20 @@ public class PacketUtils { + final int packetTimerId = profiler.getOrCreateTimerAndStart(() -> "Packet Handler: ".concat(io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(packet.getClass().getName()))); try { // Folia - profiler + packet.handle(processor); + } finally { profiler.stopTimer(packetTimerId); } // Folia - profiler ++ // Leaves start - update suppression crash fix ++ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) { ++ if (processor instanceof net.minecraft.server.network.ServerGamePacketListenerImpl gamePacketListener) { ++ exception.providePlayer(gamePacketListener.player); ++ } ++ exception.consume(); + } catch (Exception var4) { ++ if (var4.getCause() instanceof org.leavesmc.leaves.util.UpdateSuppressionException exception) { ++ if (processor instanceof net.minecraft.server.network.ServerGamePacketListenerImpl gamePacketListener) { ++ exception.providePlayer(gamePacketListener.player); ++ } ++ exception.consume(); ++ } ++ // Leaves end - update suppression crash fix + if (var4 instanceof ReportedException reportedException && reportedException.getCause() instanceof OutOfMemoryError) { + throw makeReportedException(var4, packet, processor); + } +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 3c94631f468f4ee1b46de9981c17171e5901d745..f7a2a19c0832ca90536d09de23f4ec9a7d4a1521 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1878,8 +1878,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements ca.spottedleaf.moonrise.patch + if (ret != null) { + return ret; + } +- throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner); ++ // Leaves start - update suppression crash fix ++ IllegalArgumentException iae = new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner); ++ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) { ++ org.leavesmc.leaves.util.UpdateSuppressionException exception = new org.leavesmc.leaves.util.UpdateSuppressionException(null, null, null, null, iae); ++ if (exception.getStackTrace()[1].getClassName().startsWith("net.minecraft")) { ++ throw exception; ++ } ++ } ++ throw iae; ++ // Leaves end - update suppression crash fix + // Paper end - optimise blockstate property access + } + +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index a536dc4e09b22b69bcdf59871a9cccfcf8129395..a557045888b153bb046a25d276d811b9146a7874 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -378,7 +378,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + if (blockState == state) { + return null; + } else { +- Block block = state.getBlock(); ++ Block block = state.getBlock(); try { // Leaves start - update suppression crash fix + this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state); + this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state); + this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR).update(i, y, i2, state); +@@ -450,6 +450,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + this.markUnsaved(); + return blockState; + } ++ } catch (org.leavesmc.leaves.util.UpdateSuppressionException e) { e.provideBlock(level, pos, block); throw e; } // Leaves - update suppression crash fix + } + } + } +diff --git a/net/minecraft/world/level/redstone/NeighborUpdater.java b/net/minecraft/world/level/redstone/NeighborUpdater.java +index 714e291b17856aacdac7db992fc18e6ae662f659..01285691a2e0bf72b94969e9045f7ab42a9161bf 100644 +--- a/net/minecraft/world/level/redstone/NeighborUpdater.java ++++ b/net/minecraft/world/level/redstone/NeighborUpdater.java +@@ -63,9 +63,22 @@ public interface NeighborUpdater { + } finally { if (levelChunk != null) levelChunk.getChunkHot().stopTickingAndCount(); } // KioCG + // Spigot start + } catch (StackOverflowError ex) { ++ // Leaves start - update suppression crash fix ++ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) { ++ throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, level, neighborBlock, null, ex); ++ } + level.lastPhysicsProblem = pos.immutable(); + // Spigot end + } catch (Throwable var9) { ++ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) { ++ if (var9 instanceof org.leavesmc.leaves.util.UpdateSuppressionException ue) { ++ ue.provideBlock(level, pos, neighborBlock); ++ throw ue; ++ } else { ++ throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, level, neighborBlock, null, var9); ++ } ++ } ++ // Leaves end - update suppression crash fix + CrashReport crashReport = CrashReport.forThrowable(var9, "Exception while updating neighbours"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Block being updated"); + crashReportCategory.setDetail( diff --git a/lophine-server/minecraft-patches/features/0034-Leaves-CCE-update-suppression.patch b/lophine-server/minecraft-patches/features/0034-Leaves-CCE-update-suppression.patch new file mode 100644 index 0000000..7dd7788 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0034-Leaves-CCE-update-suppression.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bacteriawa +Date: Sat, 15 Nov 2025 01:12:39 +0800 +Subject: [PATCH] Leaves: CCE update suppression + +Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java +index a26cd86aedcaf7ab606f8543e08360cbe9f2d1e6..27ccce4b4d8b4cb8be48bfe5d55366abe4ea3a38 100644 +--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java ++++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java +@@ -185,7 +185,9 @@ public class ShulkerBoxBlock extends BaseEntityBlock { + protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) { + // Leaves start - update suppression crash fix + try { +- return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos)); ++ return fun.bm.lophine.config.modules.experiment.RedStoneConfig.cce ? ++ AbstractContainerMenu.getRedstoneSignalFromContainer((net.minecraft.world.Container) level.getBlockEntity(pos)) : // Leaves - make cce happy(?) ++ AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos)); + } catch (ClassCastException ex) { + if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) { + throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, null, this, null, ex); diff --git a/lophine-server/minecraft-patches/features/0035-Leaves-Redstone-ignore-upwards-update.patch b/lophine-server/minecraft-patches/features/0035-Leaves-Redstone-ignore-upwards-update.patch new file mode 100644 index 0000000..0a64d10 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0035-Leaves-Redstone-ignore-upwards-update.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: violetc <58360096+s-yh-china@users.noreply.github.com> +Date: Wed, 14 Jun 2023 12:07:07 +0800 +Subject: [PATCH] Leaves: Redstone ignore upwards update + +Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/world/level/block/ComparatorBlock.java b/net/minecraft/world/level/block/ComparatorBlock.java +index afecf6bb6152b4d9dfee4cd8c5b1d798cbf723b3..a5bbe83b079276a3ab497e8a725713721db83aa1 100644 +--- a/net/minecraft/world/level/block/ComparatorBlock.java ++++ b/net/minecraft/world/level/block/ComparatorBlock.java +@@ -58,6 +58,7 @@ public class ComparatorBlock extends DiodeBlock implements EntityBlock { + BlockState neighborState, + RandomSource random + ) { ++ if (fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate && direction == Direction.DOWN) return state; // Leaves - behavior to 1.20.1 + return direction == Direction.DOWN && !this.canSurviveOn(level, neighborPos, neighborState) + ? Blocks.AIR.defaultBlockState() + : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); +diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java +index 4b37335e91a51b9a58ce0bce94b61ab5afbc6486..555a00e402da3d9126925135d030b7bccd8e5327 100644 +--- a/net/minecraft/world/level/block/RedStoneWireBlock.java ++++ b/net/minecraft/world/level/block/RedStoneWireBlock.java +@@ -179,7 +179,7 @@ public class RedStoneWireBlock extends Block { + RandomSource random + ) { + if (direction == Direction.DOWN) { +- return !this.canSurviveOn(level, neighborPos, neighborState) ? Blocks.AIR.defaultBlockState() : state; ++ return fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate ? state : !this.canSurviveOn(level, neighborPos, neighborState) ? Blocks.AIR.defaultBlockState() : state; // Leaves - behavior to 1.19 + } else if (direction == Direction.UP) { + return this.getConnectionState(level, state, pos); + } else { +@@ -239,7 +239,7 @@ public class RedStoneWireBlock extends Block { + BlockPos blockPos = pos.relative(direction); + BlockState blockState = level.getBlockState(blockPos); + if (nonNormalCubeAbove) { +- boolean flag = blockState.getBlock() instanceof TrapDoorBlock || this.canSurviveOn(level, blockPos, blockState); ++ boolean flag = (!fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate && blockState.getBlock() instanceof TrapDoorBlock) || this.canSurviveOn(level, blockPos, blockState); // Leaves - behavior to 1.19 + if (flag && shouldConnectTo(level.getBlockState(blockPos.above()))) { + if (blockState.isFaceSturdy(level, blockPos, direction.getOpposite())) { + return RedstoneSide.UP; +diff --git a/net/minecraft/world/level/block/RepeaterBlock.java b/net/minecraft/world/level/block/RepeaterBlock.java +index 1d45564253258eebdf10470dd7eefa644a4151ae..4b8a0f7353984d8997ab3b702227ae8d46863d7e 100644 +--- a/net/minecraft/world/level/block/RepeaterBlock.java ++++ b/net/minecraft/world/level/block/RepeaterBlock.java +@@ -68,6 +68,7 @@ public class RepeaterBlock extends DiodeBlock { + BlockState neighborState, + RandomSource random + ) { ++ if (fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate && direction == Direction.DOWN) return state; // Leaves - behavior to 1.20.1 + if (direction == Direction.DOWN && !this.canSurviveOn(level, neighborPos, neighborState)) { + return Blocks.AIR.defaultBlockState(); + } else { diff --git a/lophine-server/minecraft-patches/features/0036-Instant-Block-Updater.patch b/lophine-server/minecraft-patches/features/0036-Instant-Block-Updater.patch new file mode 100644 index 0000000..a0a5cc7 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0036-Instant-Block-Updater.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Mon, 17 Nov 2025 18:41:08 +0800 +Subject: [PATCH] Instant Block Updater + + +diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java +index f3d07efc347ff3cd06011484af0e75b5d93e6ec7..52f520667abba68af88834a771b6bcb40d1bd806 100644 +--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java ++++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java +@@ -519,7 +519,7 @@ public final class RegionizedWorldData { + this.world = world; + this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true); + this.fluidLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, false); +- this.neighborUpdater = new CollectingNeighborUpdater(world, world.neighbourUpdateMax); ++ this.neighborUpdater = fun.bm.lophine.config.modules.experiment.RedStoneConfig.instantBlockUpdater ? new net.minecraft.world.level.redstone.InstantNeighborUpdater(world) : new CollectingNeighborUpdater(world, world.neighbourUpdateMax); // Lophine - Instant Block Updater + this.nearbyPlayers = new NearbyPlayers(world); + this.wireHandler = new alternate.current.wire.WireHandler(world); + this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE); diff --git a/lophine-server/minecraft-patches/features/0037-Revert-TrapDoorBlock-changes-form-paperMC.patch b/lophine-server/minecraft-patches/features/0037-Revert-TrapDoorBlock-changes-form-paperMC.patch new file mode 100644 index 0000000..c08d776 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0037-Revert-TrapDoorBlock-changes-form-paperMC.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Tue, 18 Nov 2025 00:24:32 +0800 +Subject: [PATCH] Revert TrapDoorBlock changes form paperMC + + +diff --git a/net/minecraft/world/level/block/TrapDoorBlock.java b/net/minecraft/world/level/block/TrapDoorBlock.java +index a2140d22dc58aa2df1afbeb5170d04fd65739d55..ea79c471c03b6908710d5943f2ff378eaba4099e 100644 +--- a/net/minecraft/world/level/block/TrapDoorBlock.java ++++ b/net/minecraft/world/level/block/TrapDoorBlock.java +@@ -128,37 +128,7 @@ public class TrapDoorBlock extends HorizontalDirectionalBlock implements SimpleW + if (!level.isClientSide) { + boolean hasNeighborSignal = level.hasNeighborSignal(pos); + if (hasNeighborSignal != state.getValue(POWERED)) { +- // CraftBukkit start +- org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); +- int power = bblock.getBlockPower(); +- int oldPower = state.getValue(TrapDoorBlock.OPEN) ? 15 : 0; +- +- if (oldPower == 0 ^ power == 0 || neighborBlock.defaultBlockState().isSignalSource()) { +- org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(bblock, oldPower, power); +- level.getCraftServer().getPluginManager().callEvent(eventRedstone); +- hasNeighborSignal = eventRedstone.getNewCurrent() > 0; +- } +- // CraftBukkit end +- // Paper start - break redstone on trapdoors early +- boolean open = state.getValue(TrapDoorBlock.OPEN) != hasNeighborSignal; +- // note: this must run before any state for this block/its neighbours are written to the world +- // we allow the redstone event to fire so that plugins can block +- if (hasNeighborSignal && open) { // if we are now powered and it caused the trap door to open +- // in this case, first check for the redstone on top first +- BlockPos abovePos = pos.above(); +- BlockState above = level.getBlockState(abovePos); +- if (above.getBlock() instanceof RedStoneWireBlock) { +- level.setBlock(abovePos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS | Block.UPDATE_NEIGHBORS); +- Block.popResource(level, abovePos, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.REDSTONE)); +- // now check that this didn't change our state +- if (level.getBlockState(pos) != state) { +- // our state was changed, so we cannot propagate this update +- return; +- } +- } +- } +- if (open) { +- // Paper end - break redstone on trapdoors early ++ if (state.getValue(OPEN) != hasNeighborSignal) { + state = state.setValue(OPEN, hasNeighborSignal); + this.playSound(null, level, pos, hasNeighborSignal); + } diff --git a/lophine-server/minecraft-patches/features/0038-Leaves-Prevent-loss-of-item-drops-due-to-update-supp.patch b/lophine-server/minecraft-patches/features/0038-Leaves-Prevent-loss-of-item-drops-due-to-update-supp.patch new file mode 100644 index 0000000..f04104d --- /dev/null +++ b/lophine-server/minecraft-patches/features/0038-Leaves-Prevent-loss-of-item-drops-due-to-update-supp.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Tue, 18 Nov 2025 00:35:13 +0800 +Subject: [PATCH] Leaves: Prevent loss of item drops due to update suppression + when breaking blocks + +Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc14e2e0225009cf9919072f7f/leaves-server/minecraft-patches/features/0090-Servux-Protocol.patch) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/server/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java +index ee7bdc3adbeb04cf6ca630282f31a21e52bdb215..58f6c3e83c80b4b419ba22da81a7880be5b5d551 100644 +--- a/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -369,7 +369,18 @@ public class ServerPlayerGameMode { + this.level.getCurrentWorldData().captureDrops = new java.util.ArrayList<>(); // Folia - region threading + // CraftBukkit end + BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player); +- boolean flag = this.level.removeBlock(pos, false); ++ // Leaves start - Prevent loss of item drops due to update suppression when breaking blocks ++ boolean flag; ++ org.leavesmc.leaves.util.UpdateSuppressionException ex = null; ++ try { ++ flag = this.level.removeBlock(pos, false); ++ } catch (org.leavesmc.leaves.util.UpdateSuppressionException e) { ++ ex = e; ++ ex.provideBlock(level, pos, block); ++ ex.providePlayer(this.player); ++ flag = false; ++ } ++ // Leaves end - Prevent loss of item drops due to update suppression when breaking blocks + if (flag) { + block.destroy(this.level, pos, blockState1); + } +@@ -397,6 +408,7 @@ public class ServerPlayerGameMode { + if (event.isDropItems()) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, itemsToDrop); // Paper - capture all item additions to the world + } ++ if (ex != null) throw ex; // Leaves - Prevent loss of item drops due to update suppression when breaking blocks + + // Drop event experience + if (flag) { diff --git a/lophine-server/minecraft-patches/features/0039-Leaves-Old-Block-remove-behaviour.patch b/lophine-server/minecraft-patches/features/0039-Leaves-Old-Block-remove-behaviour.patch new file mode 100644 index 0000000..3af431a --- /dev/null +++ b/lophine-server/minecraft-patches/features/0039-Leaves-Old-Block-remove-behaviour.patch @@ -0,0 +1,807 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Tue, 18 Nov 2025 23:11:26 +0800 +Subject: [PATCH] Leaves: Old Block remove behaviour + +Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com> +As a part of : Leaves (https://github.com/LeavesMC/Leaves) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/world/Containers.java b/net/minecraft/world/Containers.java +index da10ca5ef12be1a834adda9243c082dadde24ec0..f682930f765d04f0a278b0648c2773ab5d4d740e 100644 +--- a/net/minecraft/world/Containers.java ++++ b/net/minecraft/world/Containers.java +@@ -51,4 +51,15 @@ public class Containers { + public static void updateNeighboursAfterDestroy(BlockState state, Level level, BlockPos pos) { + level.updateNeighbourForOutputSignal(pos, state.getBlock()); + } ++ ++ // Leaves start - behaviour 1.21.1- ++ public static void dropContentsOnDestroy(BlockState state, BlockState newState, Level level, BlockPos pos) { ++ if (!state.is(newState.getBlock())) { ++ if (level.getBlockEntity(pos) instanceof Container container) { ++ dropContents(level, pos, container); ++ level.updateNeighbourForOutputSignal(pos, state.getBlock()); ++ } ++ } ++ } ++ // Leaves end - behaviour 1.21.1- + } +diff --git a/net/minecraft/world/level/block/AbstractFurnaceBlock.java b/net/minecraft/world/level/block/AbstractFurnaceBlock.java +index d52eeff0d564a24bddb873750c81e3fe999f16f0..aa2597d825f93b0b0623ab7fae29e45de3f3ebcc 100644 +--- a/net/minecraft/world/level/block/AbstractFurnaceBlock.java ++++ b/net/minecraft/world/level/block/AbstractFurnaceBlock.java +@@ -51,6 +51,26 @@ public abstract class AbstractFurnaceBlock extends BaseEntityBlock { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof AbstractFurnaceBlockEntity) { ++ if (level instanceof ServerLevel) { ++ Containers.dropContents(level, pos, (AbstractFurnaceBlockEntity)blockEntity); ++ ((AbstractFurnaceBlockEntity)blockEntity).getRecipesToAwardAndPopExperience((ServerLevel)level, net.minecraft.world.phys.Vec3.atCenterOf(pos)); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ level.updateNeighbourForOutputSignal(pos, this); ++ } else { ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/BarrelBlock.java b/net/minecraft/world/level/block/BarrelBlock.java +index f7820265f10b78674acd13e6204b9212fd27b038..700bd0f6a4f4fd6cd85b3e666fff1b3fd0a96b8e 100644 +--- a/net/minecraft/world/level/block/BarrelBlock.java ++++ b/net/minecraft/world/level/block/BarrelBlock.java +@@ -49,6 +49,14 @@ public class BarrelBlock extends BaseEntityBlock { + return InteractionResult.SUCCESS; + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/BasePressurePlateBlock.java b/net/minecraft/world/level/block/BasePressurePlateBlock.java +index 42ee3f32fe44c1f0680c994a69201f7bd7792673..19214c236bca4e454e3bbe7dc50e00b66398c119 100644 +--- a/net/minecraft/world/level/block/BasePressurePlateBlock.java ++++ b/net/minecraft/world/level/block/BasePressurePlateBlock.java +@@ -125,6 +125,19 @@ public abstract class BasePressurePlateBlock extends Block { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving && !state.is(newState.getBlock())) { ++ if (this.getSignalForState(state) > 0) { ++ this.updateNeighbours(level, pos); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston && this.getSignalForState(state) > 0) { +diff --git a/net/minecraft/world/level/block/BaseRailBlock.java b/net/minecraft/world/level/block/BaseRailBlock.java +index 5f99e18244501ed2d85be77b71f48cc93058cdb7..6bf50d9c3921b15f40b4fba273abeb4c2e0f30b3 100644 +--- a/net/minecraft/world/level/block/BaseRailBlock.java ++++ b/net/minecraft/world/level/block/BaseRailBlock.java +@@ -121,6 +121,23 @@ public abstract class BaseRailBlock extends Block implements SimpleWaterloggedBl + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving) { ++ super.onRemove(state, level, pos, newState, isMoving); ++ if (state.getValue(this.getShapeProperty()).isSlope()) { ++ level.updateNeighborsAt(pos.above(), this); ++ } ++ ++ if (this.isStraight) { ++ level.updateNeighborsAt(pos, this); ++ level.updateNeighborsAt(pos.below(), this); ++ } ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston) { +diff --git a/net/minecraft/world/level/block/BrewingStandBlock.java b/net/minecraft/world/level/block/BrewingStandBlock.java +index f563f8ea63e67f808802baa4c6a700e803c045a0..0cc4479d9d3f6aea280c9f88997d141dc24917a9 100644 +--- a/net/minecraft/world/level/block/BrewingStandBlock.java ++++ b/net/minecraft/world/level/block/BrewingStandBlock.java +@@ -78,6 +78,14 @@ public class BrewingStandBlock extends BaseEntityBlock { + level.addParticle(ParticleTypes.SMOKE, d, d1, d2, 0.0, 0.0, 0.0); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/ButtonBlock.java b/net/minecraft/world/level/block/ButtonBlock.java +index 66c589bc633d32ebf36b1ab55ba6250ca2ddd6f6..f96d3c7b7ec15973aca3aabe056608fd5efb856b 100644 +--- a/net/minecraft/world/level/block/ButtonBlock.java ++++ b/net/minecraft/world/level/block/ButtonBlock.java +@@ -132,6 +132,19 @@ public class ButtonBlock extends FaceAttachedHorizontalDirectionalBlock { + return isOn ? this.type.buttonClickOn() : this.type.buttonClickOff(); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving && !state.is(newState.getBlock())) { ++ if (state.getValue(POWERED)) { ++ this.updateNeighbours(state, level, pos); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston && state.getValue(POWERED)) { +diff --git a/net/minecraft/world/level/block/CampfireBlock.java b/net/minecraft/world/level/block/CampfireBlock.java +index 028e2ad8bcb23b3f9f80a5ec551204bb2d7db1ae..c9e2becc0644de707e0bd251318813c50fc82be2 100644 +--- a/net/minecraft/world/level/block/CampfireBlock.java ++++ b/net/minecraft/world/level/block/CampfireBlock.java +@@ -105,6 +105,20 @@ public class CampfireBlock extends BaseEntityBlock implements SimpleWaterloggedB + return InteractionResult.TRY_WITH_EMPTY_HAND; + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof CampfireBlockEntity) { ++ net.minecraft.world.Containers.dropContents(level, pos, ((CampfireBlockEntity)blockEntity).getItems()); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier) { + if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(level, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent +diff --git a/net/minecraft/world/level/block/ChestBlock.java b/net/minecraft/world/level/block/ChestBlock.java +index c4937d1b482e2ec60961bda62ad6cc155f0ce8f7..818b1a5df0e9a0566210e4b3512ad856515e385f 100644 +--- a/net/minecraft/world/level/block/ChestBlock.java ++++ b/net/minecraft/world/level/block/ChestBlock.java +@@ -244,6 +244,14 @@ public class ChestBlock extends AbstractChestBlock implements + return blockState.is(this) && blockState.getValue(TYPE) == ChestType.SINGLE ? blockState.getValue(FACING) : null; + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/ChiseledBookShelfBlock.java b/net/minecraft/world/level/block/ChiseledBookShelfBlock.java +index eb315a50a83dc7230d7ad66f4daeb0d632679941..7bccf15310f6851875bfd77d4c66f4ce863b65e1 100644 +--- a/net/minecraft/world/level/block/ChiseledBookShelfBlock.java ++++ b/net/minecraft/world/level/block/ChiseledBookShelfBlock.java +@@ -180,6 +180,27 @@ public class ChiseledBookShelfBlock extends BaseEntityBlock { + SLOT_OCCUPIED_PROPERTIES.forEach(property -> builder.add(property)); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ if (!state.is(newState.getBlock())) { ++ if (level.getBlockEntity(pos) instanceof ChiseledBookShelfBlockEntity chiseledBookShelfBlockEntity && !chiseledBookShelfBlockEntity.isEmpty()) { ++ for (int i = 0; i < 6; i++) { ++ ItemStack item = chiseledBookShelfBlockEntity.getItem(i); ++ if (!item.isEmpty()) { ++ Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), item); ++ } ++ } ++ ++ chiseledBookShelfBlockEntity.clearContent(); ++ level.updateNeighbourForOutputSignal(pos, this); ++ } ++ ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/CrafterBlock.java b/net/minecraft/world/level/block/CrafterBlock.java +index caf31218a1aff84367da126ee1dc46b8c8a1e5d1..59272e7223aff61d44c86776091ca74891839a45 100644 +--- a/net/minecraft/world/level/block/CrafterBlock.java ++++ b/net/minecraft/world/level/block/CrafterBlock.java +@@ -129,6 +129,14 @@ public class CrafterBlock extends BaseEntityBlock { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/CreakingHeartBlock.java b/net/minecraft/world/level/block/CreakingHeartBlock.java +index 73885449a3316face50292756de2a3f298d3d111..333e18e4ead0bb6ef9681ce27ec156f79821af7c 100644 +--- a/net/minecraft/world/level/block/CreakingHeartBlock.java ++++ b/net/minecraft/world/level/block/CreakingHeartBlock.java +@@ -152,6 +152,16 @@ public class CreakingHeartBlock extends BaseEntityBlock { + builder.add(AXIS, STATE, NATURAL); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ if (level.getBlockEntity(pos) instanceof CreakingHeartBlockEntity creakingHeartBlockEntity) { ++ creakingHeartBlockEntity.removeProtector(null); ++ } ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/DecoratedPotBlock.java b/net/minecraft/world/level/block/DecoratedPotBlock.java +index f1570f1f4f7e43aacbcffef8bcf8ef27d46899da..6afd1c618112234ce4940105582a4399e20ab34c 100644 +--- a/net/minecraft/world/level/block/DecoratedPotBlock.java ++++ b/net/minecraft/world/level/block/DecoratedPotBlock.java +@@ -165,6 +165,14 @@ public class DecoratedPotBlock extends BaseEntityBlock implements SimpleWaterlog + return new DecoratedPotBlockEntity(pos, state); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/DiodeBlock.java b/net/minecraft/world/level/block/DiodeBlock.java +index 0ab376b4207b695e92c324b8a5f9818d3f4accde..bcceb8f17294fb9f275ab6b3a28c75cb423394f2 100644 +--- a/net/minecraft/world/level/block/DiodeBlock.java ++++ b/net/minecraft/world/level/block/DiodeBlock.java +@@ -176,6 +176,16 @@ public abstract class DiodeBlock extends HorizontalDirectionalBlock { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this instanceof ComparatorBlock && !oldState.is(Blocks.COMPARATOR)) org.leavesmc.leaves.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracking.notifyNearbyBlockEntitiesAboutNewComparator(level, pos); // Leaves - Lithium Sleeping Block Entity + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving && !state.is(newState.getBlock())) { ++ super.onRemove(state, level, pos, newState, isMoving); ++ this.updateNeighborsInFront(level, pos, state); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston) { +diff --git a/net/minecraft/world/level/block/DispenserBlock.java b/net/minecraft/world/level/block/DispenserBlock.java +index 7ff6255fcd50344cd6ac7f8a67d61fd59c85e413..7a23d27ce76ae6b90d01c4dc7af416bb6b6516ea 100644 +--- a/net/minecraft/world/level/block/DispenserBlock.java ++++ b/net/minecraft/world/level/block/DispenserBlock.java +@@ -147,6 +147,14 @@ public class DispenserBlock extends BaseEntityBlock { + return this.defaultBlockState().setValue(FACING, context.getNearestLookingDirection().getOpposite()); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/HopperBlock.java b/net/minecraft/world/level/block/HopperBlock.java +index 912d1ba98809b4469e1468e4855bb1bc91bb4540..0f2b344b385b7726c3c23b68da8a2329180fe034 100644 +--- a/net/minecraft/world/level/block/HopperBlock.java ++++ b/net/minecraft/world/level/block/HopperBlock.java +@@ -137,6 +137,14 @@ public class HopperBlock extends BaseEntityBlock implements org.leavesmc.leaves. + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ Containers.dropContentsOnDestroy(state, newState, level, pos); ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/JukeboxBlock.java b/net/minecraft/world/level/block/JukeboxBlock.java +index 56cf6528f0cd9b8528490d7cee9a1f0e54108ef9..955854e5b3a18b8f1441145554a97c5454a65d12 100644 +--- a/net/minecraft/world/level/block/JukeboxBlock.java ++++ b/net/minecraft/world/level/block/JukeboxBlock.java +@@ -73,6 +73,19 @@ public class JukeboxBlock extends BaseEntityBlock { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ if (level.getBlockEntity(pos) instanceof JukeboxBlockEntity jukeboxBlockEntity) { ++ jukeboxBlockEntity.popOutTheItem(); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/LecternBlock.java b/net/minecraft/world/level/block/LecternBlock.java +index 5a9b601b7bf7e80b04ebd8f5c8b7d121031132c7..b714d3c7649384a8fdbe0a713e2193e241c80618 100644 +--- a/net/minecraft/world/level/block/LecternBlock.java ++++ b/net/minecraft/world/level/block/LecternBlock.java +@@ -197,6 +197,36 @@ public class LecternBlock extends BaseEntityBlock { + changePowered(level, pos, state, false); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ if (state.getValue(HAS_BOOK)) { ++ this.popBook(state, level, pos); ++ } ++ ++ if (state.getValue(POWERED)) { ++ updateBelow(level, pos, state); ++ } ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ ++ private void popBook(BlockState state, Level level, BlockPos pos) { ++ if (level.getBlockEntity(pos) instanceof LecternBlockEntity lecternBlockEntity) { // CraftBukkit - don't validate, type may be changed already // Leaves - the method with validate arg already removed by paper... ++ Direction direction = state.getValue(FACING); ++ ItemStack itemStack = lecternBlockEntity.getBook().copy(); ++ if (itemStack.isEmpty()) return; // CraftBukkit - SPIGOT-5500 ++ float f = 0.25F * direction.getStepX(); ++ float f1 = 0.25F * direction.getStepZ(); ++ net.minecraft.world.entity.item.ItemEntity itemEntity = new net.minecraft.world.entity.item.ItemEntity(level, pos.getX() + 0.5 + f, pos.getY() + 1, pos.getZ() + 0.5 + f1, itemStack); ++ itemEntity.setDefaultPickUpDelay(); ++ level.addFreshEntity(itemEntity); ++ lecternBlockEntity.clearContent(); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (state.getValue(POWERED)) { +diff --git a/net/minecraft/world/level/block/LeverBlock.java b/net/minecraft/world/level/block/LeverBlock.java +index 76f2a29f37187344324d68941307d58e6343b6ae..3645ca9589aa1a74134180c9918a6f67bc64ce7a 100644 +--- a/net/minecraft/world/level/block/LeverBlock.java ++++ b/net/minecraft/world/level/block/LeverBlock.java +@@ -125,6 +125,19 @@ public class LeverBlock extends FaceAttachedHorizontalDirectionalBlock { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving && !state.is(newState.getBlock())) { ++ if (state.getValue(POWERED)) { ++ this.updateNeighbours(state, level, pos); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston && state.getValue(POWERED)) { +diff --git a/net/minecraft/world/level/block/LightningRodBlock.java b/net/minecraft/world/level/block/LightningRodBlock.java +index d702d234111c2a34585f3532b5bb4bdb1501b82a..d9bedb99590a860a7ebb1d68861dc175ca3f4a2b 100644 +--- a/net/minecraft/world/level/block/LightningRodBlock.java ++++ b/net/minecraft/world/level/block/LightningRodBlock.java +@@ -120,6 +120,18 @@ public class LightningRodBlock extends RodBlock implements SimpleWaterloggedBloc + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ if (!state.is(newState.getBlock())) { ++ if (state.getValue(POWERED)) { ++ this.updateNeighbours(state, level, pos); ++ } ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (state.getValue(POWERED)) { +diff --git a/net/minecraft/world/level/block/ObserverBlock.java b/net/minecraft/world/level/block/ObserverBlock.java +index bd2aa00ce8b78c16f6107064dd00bfbb072df0df..6db3a21bcc37ae79f82b19ee0a851f539d4a654d 100644 +--- a/net/minecraft/world/level/block/ObserverBlock.java ++++ b/net/minecraft/world/level/block/ObserverBlock.java +@@ -127,6 +127,17 @@ public class ObserverBlock extends DirectionalBlock { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ if (state.getValue(POWERED) && level.getBlockTicks().hasScheduledTick(pos, this)) { ++ this.updateNeighborsInFront(level, pos, state.setValue(POWERED, Boolean.FALSE)); ++ } ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (state.getValue(POWERED) && level.getBlockTicks().hasScheduledTick(pos, this)) { +diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java +index 555a00e402da3d9126925135d030b7bccd8e5327..20cec51bcead34f95ed6195a7762cea7ce280914 100644 +--- a/net/minecraft/world/level/block/RedStoneWireBlock.java ++++ b/net/minecraft/world/level/block/RedStoneWireBlock.java +@@ -369,6 +369,27 @@ public class RedStoneWireBlock extends Block { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving && !state.is(newState.getBlock())) { ++ super.onRemove(state, level, pos, newState, isMoving); ++ for (Direction direction : Direction.values()) { ++ level.updateNeighborsAt(pos.relative(direction), this); ++ } ++ ++ // Paper start - optimize redstone - replace call to updatePowerStrength ++ if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { ++ level.getWireHandler().onWireRemoved(pos, state); // Alternate Current ++ } else { ++ this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft ++ } ++ // Paper end - optimize redstone ++ this.updateNeighborsOfNeighboringWires(level, pos); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston) { +diff --git a/net/minecraft/world/level/block/RedstoneTorchBlock.java b/net/minecraft/world/level/block/RedstoneTorchBlock.java +index df4f1b18deb8bb4a04e740bbbeb6a89cb1ff8e71..7f165a4e2935cf4638b14379a0d1aca1b65ef84a 100644 +--- a/net/minecraft/world/level/block/RedstoneTorchBlock.java ++++ b/net/minecraft/world/level/block/RedstoneTorchBlock.java +@@ -53,6 +53,15 @@ public class RedstoneTorchBlock extends BaseTorchBlock { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving) { ++ this.notifyNeighbors(level, pos, state); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston) { +diff --git a/net/minecraft/world/level/block/SculkSensorBlock.java b/net/minecraft/world/level/block/SculkSensorBlock.java +index f0101e88140f480c1a94f899744991f78c9e3756..fa9cb4c40a41eea7fd63a4513d0b0f39067de9ba 100644 +--- a/net/minecraft/world/level/block/SculkSensorBlock.java ++++ b/net/minecraft/world/level/block/SculkSensorBlock.java +@@ -129,6 +129,18 @@ public class SculkSensorBlock extends BaseEntityBlock implements SimpleWaterlogg + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ if (!state.is(newState.getBlock())) { ++ if (getPhase(state) == SculkSensorPhase.ACTIVE) { ++ updateNeighbours(level, pos, state); ++ } ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (getPhase(state) == SculkSensorPhase.ACTIVE) { +diff --git a/net/minecraft/world/level/block/SculkShriekerBlock.java b/net/minecraft/world/level/block/SculkShriekerBlock.java +index 757f8453e147875ab9f14d9726bb734ef27447c9..d0558d0e33f3d6e25af2dd71650d723e8997dae6 100644 +--- a/net/minecraft/world/level/block/SculkShriekerBlock.java ++++ b/net/minecraft/world/level/block/SculkShriekerBlock.java +@@ -68,6 +68,16 @@ public class SculkShriekerBlock extends BaseEntityBlock implements SimpleWaterlo + super.stepOn(level, pos, state, entity); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ if (level instanceof ServerLevel serverLevel && state.getValue(SHRIEKING) && !state.is(newState.getBlock())) { ++ serverLevel.getBlockEntity(pos, BlockEntityType.SCULK_SHRIEKER).ifPresent(sculkShrieker -> sculkShrieker.tryRespond(serverLevel)); ++ } ++ super.onRemove(state, level, pos, newState, movedByPiston); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + if (state.getValue(SHRIEKING)) { +diff --git a/net/minecraft/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java +index 27ccce4b4d8b4cb8be48bfe5d55366abe4ea3a38..2e6d462fb27ad1e538164934417ce07cc0e603c2 100644 +--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java ++++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java +@@ -152,6 +152,19 @@ public class ShulkerBoxBlock extends BaseEntityBlock { + // Paper end - re-set loot table if it was cleared + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof ShulkerBoxBlockEntity) { ++ level.updateNeighbourForOutputSignal(pos, state.getBlock()); ++ } ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + Containers.updateNeighboursAfterDestroy(state, level, pos); +diff --git a/net/minecraft/world/level/block/TripWireBlock.java b/net/minecraft/world/level/block/TripWireBlock.java +index c8f793d1cdeef7e3e0d5fceb45a4507d542e4b33..27f382f6c663b2954a6fb966ece05ba021fcd6b1 100644 +--- a/net/minecraft/world/level/block/TripWireBlock.java ++++ b/net/minecraft/world/level/block/TripWireBlock.java +@@ -108,6 +108,16 @@ public class TripWireBlock extends Block { + } + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent adjacent tripwires from updating ++ if (!isMoving && !state.is(newState.getBlock())) { ++ this.updateSource(level, pos, state.setValue(POWERED, Boolean.TRUE)); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().blockUpdates.disableTripwireUpdates) return; // Paper - prevent adjacent tripwires from updating +diff --git a/net/minecraft/world/level/block/TripWireHookBlock.java b/net/minecraft/world/level/block/TripWireHookBlock.java +index 3f412b08e7d40d577efb7be291dd14826ba21f0e..30c65a0906646fc12cd842336ec4f3e2aaf31717 100644 +--- a/net/minecraft/world/level/block/TripWireHookBlock.java ++++ b/net/minecraft/world/level/block/TripWireHookBlock.java +@@ -245,6 +245,25 @@ public class TripWireHookBlock extends Block { + level.updateNeighborsAt(pos.relative(opposite), block, orientation); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!isMoving && !state.is(newState.getBlock())) { ++ boolean attachedValue = state.getValue(ATTACHED); ++ boolean poweredValue = state.getValue(POWERED); ++ if (attachedValue || poweredValue) { ++ calculateState(level, pos, state, true, false, -1, null); ++ } ++ ++ if (poweredValue) { ++ notifyNeighbors(this, level, pos, state.getValue(FACING)); ++ } ++ ++ super.onRemove(state, level, pos, newState, isMoving); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + if (!movedByPiston) { +diff --git a/net/minecraft/world/level/block/entity/BlockEntityType.java b/net/minecraft/world/level/block/entity/BlockEntityType.java +index 7b8631abf8adcdb889d412b30eca10faf044032a..13134dfe9f548b87fd6be4683bf3386c522aba6a 100644 +--- a/net/minecraft/world/level/block/entity/BlockEntityType.java ++++ b/net/minecraft/world/level/block/entity/BlockEntityType.java +@@ -267,7 +267,7 @@ public class BlockEntityType { + } + + public boolean isValid(BlockState state) { +- return this.validBlocks.contains(state.getBlock()); ++ return fun.bm.lophine.config.modules.experiment.RedStoneConfig.oldBlockRemoveBehaviour || this.validBlocks.contains(state.getBlock()); // Leaves - behaviour 1.21.1- + } + + @Deprecated +diff --git a/net/minecraft/world/level/block/piston/MovingPistonBlock.java b/net/minecraft/world/level/block/piston/MovingPistonBlock.java +index 05bbc2e59384702439548a988e128a85f1adbe82..af516d5b6bd31de2c6a388ec8001126e2a351cf3 100644 +--- a/net/minecraft/world/level/block/piston/MovingPistonBlock.java ++++ b/net/minecraft/world/level/block/piston/MovingPistonBlock.java +@@ -67,6 +67,18 @@ public class MovingPistonBlock extends BaseEntityBlock { + return createTickerHelper(blockEntityType, BlockEntityType.PISTON, PistonMovingBlockEntity::tick); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ BlockEntity blockEntity = level.getBlockEntity(pos); ++ if (blockEntity instanceof PistonMovingBlockEntity) { ++ ((PistonMovingBlockEntity)blockEntity).finalTick(); ++ } ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + public void destroy(LevelAccessor level, BlockPos pos, BlockState state) { + BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite()); +diff --git a/net/minecraft/world/level/block/piston/PistonHeadBlock.java b/net/minecraft/world/level/block/piston/PistonHeadBlock.java +index 6c789e56f21f01252c21786cfeb48d88485b5636..e24a61a4e2dac0159d52f07c93ddf860f4bfb8f7 100644 +--- a/net/minecraft/world/level/block/piston/PistonHeadBlock.java ++++ b/net/minecraft/world/level/block/piston/PistonHeadBlock.java +@@ -78,6 +78,19 @@ public class PistonHeadBlock extends DirectionalBlock { + return super.playerWillDestroy(level, pos, state, player); + } + ++ // Leaves start - behaviour 1.21.1- ++ @Override ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { ++ if (!state.is(newState.getBlock())) { ++ super.onRemove(state, level, pos, newState, isMoving); ++ BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite()); ++ if (this.isFittingBase(state, level.getBlockState(blockPos))) { ++ level.destroyBlock(blockPos, true); ++ } ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + @Override + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + BlockPos blockPos = pos.relative(state.getValue(FACING).getOpposite()); +diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java +index e9749b66239d7562ecf22002bfbaa95df197050f..6e3841ed012eee16dcaa61f70edfc1d870374a56 100644 +--- a/net/minecraft/world/level/block/state/BlockBehaviour.java ++++ b/net/minecraft/world/level/block/state/BlockBehaviour.java +@@ -172,6 +172,15 @@ public abstract class BlockBehaviour implements FeatureElement, org.leavesmc.lea + org.spigotmc.AsyncCatcher.catchOp("block onPlace"); // Spigot + } + ++ // Leaves start - behaviour 1.21.1- ++ protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ org.spigotmc.AsyncCatcher.catchOp("block remove"); // Spigot ++ if (state.hasBlockEntity() && !state.is(newState.getBlock())) { ++ level.removeBlockEntity(pos); ++ } ++ } ++ // Leaves end - behaviour 1.21.1- ++ + protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) { + } + +@@ -868,6 +877,12 @@ public abstract class BlockBehaviour implements FeatureElement, org.leavesmc.lea + // CraftBukkit end + } + ++ // Leaves start - behaviour 1.21.1- ++ public void onRemove(Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { ++ this.getBlock().onRemove(this.asState(), level, pos, newState, movedByPiston); ++ } ++ // Leaves end - behaviour 1.21.1- ++ + public void affectNeighborsAfterRemoval(ServerLevel level, BlockPos pos, boolean movedByPiston) { + this.getBlock().affectNeighborsAfterRemoval(this.asState(), level, pos, movedByPiston); + } +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index a557045888b153bb046a25d276d811b9146a7874..568427a952a24012c17f64f62b6545a9d89c8c8a 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -401,20 +401,26 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + boolean flag = !blockState.is(block); + boolean flag1 = (flags & 64) != 0; + boolean flag2 = (flags & 256) == 0; +- if (flag && blockState.hasBlockEntity()) { +- if (!this.level.isClientSide && flag2) { +- BlockEntity blockEntity = this.level.getBlockEntity(pos); +- if (blockEntity != null) { +- blockEntity.preRemoveSideEffects(pos, blockState); ++ // Leaves start - behaviour 1.21.1- ++ if (!fun.bm.lophine.config.modules.experiment.RedStoneConfig.oldBlockRemoveBehaviour) { ++ if (flag && blockState.hasBlockEntity()) { ++ if (!this.level.isClientSide && flag2) { ++ BlockEntity blockEntity = this.level.getBlockEntity(pos); ++ if (blockEntity != null) { ++ blockEntity.preRemoveSideEffects(pos, blockState); ++ } + } +- } + +- this.removeBlockEntity(pos); +- } ++ this.removeBlockEntity(pos); ++ } + +- if ((flag || block instanceof BaseRailBlock) && this.level instanceof ServerLevel serverLevel && ((flags & 1) != 0 || flag1)) { +- blockState.affectNeighborsAfterRemoval(serverLevel, pos, flag1); ++ if ((flag || block instanceof BaseRailBlock) && this.level instanceof ServerLevel serverLevel && ((flags & 1) != 0 || flag1)) { ++ blockState.affectNeighborsAfterRemoval(serverLevel, pos, flag1); ++ } ++ } else { ++ blockState.onRemove(this.level, pos, state, flag1); + } ++ // Leaves end - behaviour 1.21.1- + + if (!section.getBlockState(i, i1, i2).is(block)) { + return null; diff --git a/lophine-server/minecraft-patches/features/0040-Leaves-Do-not-reset-placed-block-on-exception-Do-not.patch b/lophine-server/minecraft-patches/features/0040-Leaves-Do-not-reset-placed-block-on-exception-Do-not.patch new file mode 100644 index 0000000..2e2c7c5 --- /dev/null +++ b/lophine-server/minecraft-patches/features/0040-Leaves-Do-not-reset-placed-block-on-exception-Do-not.patch @@ -0,0 +1,70 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Helvetica Volubi +Date: Tue, 18 Nov 2025 23:26:27 +0800 +Subject: [PATCH] Leaves: Do not reset placed block on exception & Do not + prevent block entity and entity crash at LevelChunk + +Co-authored by: MC_XiaoHei +As a part of : Leaves (https://github.com/LeavesMC/Leaves) +Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) + +diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java +index 8f112f382190d606a87f38da8bcdf8dd5f31d1c5..f27915e43df8e627000cda1cb213a95300c98038 100644 +--- a/net/minecraft/world/item/BlockItem.java ++++ b/net/minecraft/world/item/BlockItem.java +@@ -77,20 +77,22 @@ public class BlockItem extends Item { + BlockState blockState = level.getBlockState(clickedPos); + if (blockState.is(placementState.getBlock())) { + blockState = this.updateBlockStateFromTag(clickedPos, level, itemInHand, blockState); ++ // Leaves start - we do not need this + // Paper start - Reset placed block on exception +- try { ++ // try { + this.updateCustomBlockEntityTag(clickedPos, level, player, itemInHand, blockState); + updateBlockEntityComponents(level, clickedPos, itemInHand); +- } catch (Exception ex) { +- ((org.bukkit.craftbukkit.block.CraftBlockState) oldBukkitState).revertPlace(); +- if (player instanceof ServerPlayer serverPlayer) { +- org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), ex); +- serverPlayer.getBukkitEntity().kickPlayer("Packet processing error"); +- return InteractionResult.FAIL; +- } +- throw ex; // Rethrow exception if not placed by a player +- } ++ // } catch (Exception ex) { ++ // ((org.bukkit.craftbukkit.block.CraftBlockState) oldBukkitState).revertPlace(); ++ // if (player instanceof ServerPlayer serverPlayer) { ++ // org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), ex); ++ // serverPlayer.getBukkitEntity().kickPlayer("Packet processing error"); ++ // return InteractionResult.FAIL; ++ // } ++ // throw ex; // Rethrow exception if not placed by a player ++ // } + // Paper end - Reset placed block on exception ++ // Leaves end - we dot not need this + blockState.getBlock().setPlacedBy(level, clickedPos, blockState, player, itemInHand); + // CraftBukkit start + if (bukkitState != null) { +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 568427a952a24012c17f64f62b6545a9d89c8c8a..49a740bf17b2bed8010e347747b170b69ac26929 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -971,12 +971,14 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + + profilerFiller.pop(); + } catch (Throwable var5) { ++ // Leaves start - do not prevent here + // Paper start - Prevent block entity and entity crashes +- final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); +- net.minecraft.server.MinecraftServer.LOGGER.error(msg, var5); +- net.minecraft.world.level.chunk.LevelChunk.this.level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, var5))); // Paper - ServerExceptionEvent +- LevelChunk.this.removeBlockEntity(this.getPos()); ++ // final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ()); ++ // net.minecraft.server.MinecraftServer.LOGGER.error(msg, var5); ++ // net.minecraft.world.level.chunk.LevelChunk.this.level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, var5))); // Paper - ServerExceptionEvent ++ // LevelChunk.this.removeBlockEntity(this.getPos()); + // Paper end - Prevent block entity and entity crashes ++ // Leaves end - do not prevent here + } + } + } diff --git a/lophine-server/src/main/java/fun/bm/lophine/LophineLogger.java b/lophine-server/src/main/java/fun/bm/lophine/LophineLogger.java new file mode 100644 index 0000000..ac453f7 --- /dev/null +++ b/lophine-server/src/main/java/fun/bm/lophine/LophineLogger.java @@ -0,0 +1,10 @@ +package fun.bm.lophine; + +import com.mojang.logging.LogUtils; +import org.slf4j.Logger; + +import java.util.logging.Level; + +public class LophineLogger { + public static final Logger LOGGER = LogUtils.getClassLogger(); // only provided for some Logger can not be initialized +} diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/RedStoneConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/RedStoneConfig.java new file mode 100644 index 0000000..75f3a87 --- /dev/null +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/experiment/RedStoneConfig.java @@ -0,0 +1,33 @@ +package fun.bm.lophine.config.modules.experiment; + +import me.earthme.luminol.config.IConfigModule; +import me.earthme.luminol.config.flags.ConfigClassInfo; +import me.earthme.luminol.config.flags.ConfigInfo; +import me.earthme.luminol.config.flags.HotReloadUnsupported; +import me.earthme.luminol.config.flags.TransformedConfig; +import me.earthme.luminol.enums.EnumConfigCategory; +/* +* This is a config module for redstone in experimental level +* If we think configs from here is stable for future, we will move them to function module directory +*/ +@ConfigClassInfo(category = EnumConfigCategory.EXPERIMENT, name = "redstone") +public class RedStoneConfig implements IConfigModule { + @TransformedConfig(name = "enabled", directory = {"experiment", "redstone-ignore-upwards-update"}) + @ConfigInfo(name = "redstone-ignore-upwards-update", comments = """ + Should the pre-1.20 mechanism be reintroduced: + Redstone dust does not connect to adjacent redstone dust on trapdoors that are open + Pre-1.20.2 mechanism: Redstone dust, redstone repeaters, and redstone comparators do not check for attachment when receiving status updates from below""") + public static boolean redstoneIgnoreUpwardsUpdate = false; + + @TransformedConfig(name = "enabled", directory = {"experiment", "cce-update-suppression"}) + @ConfigInfo(name = "cce-update-suppression", comments = """ + Is it permissible to use ClassCastException for update suppression?""") + public static boolean cce = false; + + @HotReloadUnsupported + @ConfigInfo(name = "instant-block-updater") + public static boolean instantBlockUpdater = false; + + @ConfigInfo(name = "old-block-remove-behaviour") + public static boolean oldBlockRemoveBehaviour = false; +} diff --git a/lophine-server/src/main/java/fun/bm/lophine/config/modules/fixes/UpdateSuppressionCrashFixConfig.java b/lophine-server/src/main/java/fun/bm/lophine/config/modules/fixes/UpdateSuppressionCrashFixConfig.java new file mode 100644 index 0000000..1215a91 --- /dev/null +++ b/lophine-server/src/main/java/fun/bm/lophine/config/modules/fixes/UpdateSuppressionCrashFixConfig.java @@ -0,0 +1,13 @@ +package fun.bm.lophine.config.modules.fixes; + +import me.earthme.luminol.config.IConfigModule; +import me.earthme.luminol.config.flags.ConfigClassInfo; +import me.earthme.luminol.config.flags.ConfigInfo; +import me.earthme.luminol.enums.EnumConfigCategory; + +@ConfigClassInfo(category = EnumConfigCategory.FIXES, name = "update-suppression-crash-fix") +public class UpdateSuppressionCrashFixConfig implements IConfigModule { + @ConfigInfo(name = "enabled", comments = """ + Should crashes caused by update suppression be prevented?""") + public static boolean enabled = true; +} diff --git a/lophine-server/src/main/java/org/leavesmc/leaves/util/UpdateSuppressionException.java b/lophine-server/src/main/java/org/leavesmc/leaves/util/UpdateSuppressionException.java index 4655bb0..dc02760 100644 --- a/lophine-server/src/main/java/org/leavesmc/leaves/util/UpdateSuppressionException.java +++ b/lophine-server/src/main/java/org/leavesmc/leaves/util/UpdateSuppressionException.java @@ -17,7 +17,7 @@ package org.leavesmc.leaves.util; -import com.mojang.logging.LogUtils; +import fun.bm.lophine.LophineLogger; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.Level; @@ -30,7 +30,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.leavesmc.leaves.bot.ServerBot; import org.leavesmc.leaves.event.player.UpdateSuppressionEvent; -import org.slf4j.Logger; import java.util.ArrayList; import java.util.List; @@ -41,7 +40,6 @@ public class UpdateSuppressionException extends RuntimeException { private @Nullable Block source; private @Nullable ServerPlayer player; private final @NotNull Throwable throwable; - private static final Logger LOGGER = LogUtils.getLogger(); public UpdateSuppressionException( @Nullable BlockPos pos, @@ -87,7 +85,7 @@ public class UpdateSuppressionException extends RuntimeException { public void consume() { submitEvent(); - LOGGER.info(getMessage()); + LophineLogger.LOGGER.info(getMessage()); } private void submitEvent() {