From c879c1753db1987e6ce936a4d3839124f2e9204d Mon Sep 17 00:00:00 2001 From: mworzala Date: Sun, 16 Jul 2023 07:48:05 -0400 Subject: [PATCH] hollow-cube/prevent-block-updates (cherry picked from commit 55a13490497ed2107bf5a46f1591707e38549966) --- .../src/main/java/net/minestom/demo/Main.java | 2 + .../java/net/minestom/demo/PlayerInit.java | 3 + .../placement/DripstonePlacementRule.java | 62 +++++++++++++++++++ .../event/player/PlayerBlockPlaceEvent.java | 18 ++++++ .../minestom/server/instance/Instance.java | 34 +++++++++- .../server/instance/InstanceContainer.java | 21 ++++--- .../server/instance/SharedInstance.java | 12 ++-- .../listener/BlockPlacementListener.java | 4 +- 8 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 demo/src/main/java/net/minestom/demo/block/placement/DripstonePlacementRule.java diff --git a/demo/src/main/java/net/minestom/demo/Main.java b/demo/src/main/java/net/minestom/demo/Main.java index 363990328..2ea4c1a76 100644 --- a/demo/src/main/java/net/minestom/demo/Main.java +++ b/demo/src/main/java/net/minestom/demo/Main.java @@ -6,6 +6,7 @@ import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; import net.minestom.demo.block.TestBlockHandler; +import net.minestom.demo.block.placement.DripstonePlacementRule; import net.minestom.demo.commands.*; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; @@ -28,6 +29,7 @@ public class Main { MinecraftServer minecraftServer = MinecraftServer.init(); BlockManager blockManager = MinecraftServer.getBlockManager(); + blockManager.registerBlockPlacementRule(new DripstonePlacementRule()); blockManager.registerHandler(TestBlockHandler.INSTANCE.getNamespaceId(), () -> TestBlockHandler.INSTANCE); CommandManager commandManager = MinecraftServer.getCommandManager(); diff --git a/demo/src/main/java/net/minestom/demo/PlayerInit.java b/demo/src/main/java/net/minestom/demo/PlayerInit.java index a4702693e..fd701b11e 100644 --- a/demo/src/main/java/net/minestom/demo/PlayerInit.java +++ b/demo/src/main/java/net/minestom/demo/PlayerInit.java @@ -133,6 +133,9 @@ public class PlayerInit { event.getInstance().setBlock(event.getPosition(), block); + }) + .addListener(PlayerBlockPlaceEvent.class, event -> { + event.setDoBlockUpdates(false); }); static { diff --git a/demo/src/main/java/net/minestom/demo/block/placement/DripstonePlacementRule.java b/demo/src/main/java/net/minestom/demo/block/placement/DripstonePlacementRule.java new file mode 100644 index 000000000..ed5cbb480 --- /dev/null +++ b/demo/src/main/java/net/minestom/demo/block/placement/DripstonePlacementRule.java @@ -0,0 +1,62 @@ +package net.minestom.demo.block.placement; + +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.rule.BlockPlacementRule; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class DripstonePlacementRule extends BlockPlacementRule { + private static final String PROP_VERTICAL_DIRECTION = "vertical_direction"; // Tip, frustum, middle(0 or more), base + private static final String PROP_THICKNESS = "thickness"; + + public DripstonePlacementRule() { + super(Block.POINTED_DRIPSTONE); + } + + @Override + public @Nullable Block blockPlace(@NotNull PlacementState placementState) { + var blockFace = placementState.blockFace(); + var y = placementState.cursorPosition().y(); + return block.withProperty(PROP_VERTICAL_DIRECTION, switch (blockFace) { + case TOP -> "up"; + case BOTTOM -> "down"; + default -> y < 0.5 ? "up" : "down"; + }); + } + + @Override + public @NotNull Block blockUpdate(@NotNull UpdateState updateState) { + return updateState.currentBlock() + .withProperty(PROP_THICKNESS, getThickness(updateState)); + } + + private @NotNull String getThickness(@NotNull UpdateState updateState) { + var direction = updateState.currentBlock().getProperty(PROP_VERTICAL_DIRECTION).equals("up"); + var abovePosition = updateState.blockPosition().add(0, direction ? 1 : -1, 0); + var aboveBlock = updateState.instance().getBlock(abovePosition, Block.Getter.Condition.TYPE); + + // If there is no dripstone above, it is always a tip + if (aboveBlock.id() != Block.POINTED_DRIPSTONE.id()) + return "tip"; + // If there is an opposite facing dripstone above, it is always a merged tip + if ((direction ? "down" : "up").equals(aboveBlock.getProperty(PROP_VERTICAL_DIRECTION))) + return "tip_merge"; + + // If the dripstone above this is a tip, it is a frustum + var aboveThickness = aboveBlock.getProperty(PROP_THICKNESS); + if ("tip".equals(aboveThickness) || "tip_merge".equals(aboveThickness)) + return "frustum"; + + // At this point we know that there is a dripstone above, and that the dripstone is facing the same direction. + var belowPosition = updateState.blockPosition().add(0, direction ? -1 : 1, 0); + var belowBlock = updateState.instance().getBlock(belowPosition, Block.Getter.Condition.TYPE); + + // If there is no dripstone below, it is always a base + if (belowBlock.id() != Block.POINTED_DRIPSTONE.id()) + return "base"; + + // Otherwise it is a middle + return "middle"; + } +} + diff --git a/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java b/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java index ef5d636c0..5613ee00c 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerBlockPlaceEvent.java @@ -21,6 +21,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C private final Player.Hand hand; private boolean consumeBlock; + private boolean doBlockUpdates; private boolean cancelled; @@ -33,6 +34,7 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C this.blockPosition = blockPosition; this.hand = hand; this.consumeBlock = true; + this.doBlockUpdates = true; } /** @@ -94,6 +96,22 @@ public class PlayerBlockPlaceEvent implements PlayerInstanceEvent, BlockEvent, C return consumeBlock; } + /** + * Should the place trigger updates (on self and neighbors) + * @param doBlockUpdates true if this placement should do block updates + */ + public void setDoBlockUpdates(boolean doBlockUpdates) { + this.doBlockUpdates = doBlockUpdates; + } + + /** + * Should the place trigger updates (on self and neighbors) + * @return true if this placement should do block updates + */ + public boolean shouldDoBlockUpdates() { + return doBlockUpdates; + } + @Override public boolean isCancelled() { return cancelled; diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index c2a6510fb..44383b540 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -153,8 +153,24 @@ public abstract class Instance implements Block.Getter, Block.Setter, this.scheduler.scheduleNextTick(() -> callback.accept(this)); } + @Override + public void setBlock(int x, int y, int z, @NotNull Block block) { + setBlock(x, y, z, block, true); + } + + public void setBlock(@NotNull Point blockPosition, @NotNull Block block, boolean doBlockUpdates) { + setBlock(blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(), block, doBlockUpdates); + } + + public abstract void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates); + @ApiStatus.Internal - public abstract boolean placeBlock(@NotNull BlockHandler.Placement placement); + public boolean placeBlock(@NotNull BlockHandler.Placement placement) { + return placeBlock(placement, true); + } + + @ApiStatus.Internal + public abstract boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates); /** * Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent} @@ -165,7 +181,21 @@ public abstract class Instance implements Block.Getter, Block.Setter, * @return true if the block has been broken, false if it has been cancelled */ @ApiStatus.Internal - public abstract boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace); + public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) { + return breakBlock(player, blockPosition, blockFace, true); + } + + /** + * Does call {@link net.minestom.server.event.player.PlayerBlockBreakEvent} + * and send particle packets + * + * @param player the {@link Player} who break the block + * @param blockPosition the position of the broken block + * @param doBlockUpdates true to do block updates, false otherwise + * @return true if the block has been broken, false if it has been cancelled + */ + @ApiStatus.Internal + public abstract boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates); /** * Forces the generation of a {@link Chunk}, even if no file and {@link ChunkGenerator} are defined. diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 851a82d2d..68e14c6ad 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -100,14 +100,14 @@ public class InstanceContainer extends Instance { } @Override - public void setBlock(int x, int y, int z, @NotNull Block block) { + public void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates) { Chunk chunk = getChunkAt(x, z); if (chunk == null) { Check.stateCondition(!hasEnabledAutoChunkLoad(), "Tried to set a block to an unloaded chunk with auto chunk load disabled"); chunk = loadChunk(getChunkCoordinate(x), getChunkCoordinate(z)).join(); } - if (isLoaded(chunk)) UNSAFE_setBlock(chunk, x, y, z, block, null, null); + if (isLoaded(chunk)) UNSAFE_setBlock(chunk, x, y, z, block, null, null, doBlockUpdates); } /** @@ -122,7 +122,8 @@ public class InstanceContainer extends Instance { * @param block the block to place */ private synchronized void UNSAFE_setBlock(@NotNull Chunk chunk, int x, int y, int z, @NotNull Block block, - @Nullable BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy) { + @Nullable BlockHandler.Placement placement, @Nullable BlockHandler.Destroy destroy, + boolean doBlockUpdates) { if (chunk.isReadOnly()) return; synchronized (chunk) { // Refresh the last block change time @@ -137,7 +138,7 @@ public class InstanceContainer extends Instance { // Change id based on neighbors final BlockPlacementRule blockPlacementRule = MinecraftServer.getBlockManager().getBlockPlacementRule(block); - if (blockPlacementRule != null) { + if (blockPlacementRule != null && doBlockUpdates) { block = blockPlacementRule.blockUpdate(new BlockPlacementRule.UpdateState(this, blockPosition, block)); } @@ -145,7 +146,9 @@ public class InstanceContainer extends Instance { chunk.setBlock(x, y, z, block, placement, destroy); // Refresh neighbors since a new block has been placed - executeNeighboursBlockPlacementRule(blockPosition); + if (doBlockUpdates) { + executeNeighboursBlockPlacementRule(blockPosition); + } // Refresh player chunk block { @@ -160,17 +163,17 @@ public class InstanceContainer extends Instance { } @Override - public boolean placeBlock(@NotNull BlockHandler.Placement placement) { + public boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates) { final Point blockPosition = placement.getBlockPosition(); final Chunk chunk = getChunkAt(blockPosition); if (!isLoaded(chunk)) return false; UNSAFE_setBlock(chunk, blockPosition.blockX(), blockPosition.blockY(), blockPosition.blockZ(), - placement.getBlock(), placement, null); + placement.getBlock(), placement, null, doBlockUpdates); return true; } @Override - public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) { + public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates) { final Chunk chunk = getChunkAt(blockPosition); Check.notNull(chunk, "You cannot break blocks in a null chunk!"); if (chunk.isReadOnly()) return false; @@ -192,7 +195,7 @@ public class InstanceContainer extends Instance { // Break or change the broken block based on event result final Block resultBlock = blockBreakEvent.getResultBlock(); UNSAFE_setBlock(chunk, x, y, z, resultBlock, null, - new BlockHandler.PlayerDestroy(block, this, blockPosition, player)); + new BlockHandler.PlayerDestroy(block, this, blockPosition, player), doBlockUpdates); // Send the block break effect packet PacketUtils.sendGroupedPacket(chunk.getViewers(), new EffectPacket(2001 /*Block break + block break sound*/, blockPosition, block.stateId(), false), diff --git a/src/main/java/net/minestom/server/instance/SharedInstance.java b/src/main/java/net/minestom/server/instance/SharedInstance.java index a276f003e..925252634 100644 --- a/src/main/java/net/minestom/server/instance/SharedInstance.java +++ b/src/main/java/net/minestom/server/instance/SharedInstance.java @@ -27,18 +27,18 @@ public class SharedInstance extends Instance { } @Override - public void setBlock(int x, int y, int z, @NotNull Block block) { - this.instanceContainer.setBlock(x, y, z, block); + public void setBlock(int x, int y, int z, @NotNull Block block, boolean doBlockUpdates) { + this.instanceContainer.setBlock(x, y, z, block, doBlockUpdates); } @Override - public boolean placeBlock(@NotNull BlockHandler.Placement placement) { - return instanceContainer.placeBlock(placement); + public boolean placeBlock(@NotNull BlockHandler.Placement placement, boolean doBlockUpdates) { + return instanceContainer.placeBlock(placement, doBlockUpdates); } @Override - public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace) { - return instanceContainer.breakBlock(player, blockPosition, blockFace); + public boolean breakBlock(@NotNull Player player, @NotNull Point blockPosition, @NotNull BlockFace blockFace, boolean doBlockUpdates) { + return instanceContainer.breakBlock(player, blockPosition, blockFace, doBlockUpdates); } @Override diff --git a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java index a8bc73141..28e895ea3 100644 --- a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java +++ b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java @@ -153,7 +153,7 @@ public class BlockPlacementListener { // BlockPlacementRule check Block resultBlock = playerBlockPlaceEvent.getBlock(); final BlockPlacementRule blockPlacementRule = BLOCK_MANAGER.getBlockPlacementRule(resultBlock); - if (blockPlacementRule != null) { + if (blockPlacementRule != null && playerBlockPlaceEvent.shouldDoBlockUpdates()) { // Get id from block placement rule instead of the event resultBlock = blockPlacementRule.blockPlace(new BlockPlacementRule.PlacementState( instance, resultBlock, blockFace, @@ -168,7 +168,7 @@ public class BlockPlacementListener { // Place the block player.sendPacket(new AcknowledgeBlockChangePacket(packet.sequence())); instance.placeBlock(new BlockHandler.PlayerPlacement(resultBlock, instance, placementPosition, player, hand, blockFace, - packet.cursorPositionX(), packet.cursorPositionY(), packet.cursorPositionZ())); + packet.cursorPositionX(), packet.cursorPositionY(), packet.cursorPositionZ()), playerBlockPlaceEvent.shouldDoBlockUpdates()); // Block consuming if (playerBlockPlaceEvent.doesConsumeBlock()) { // Consume the block in the player's hand