diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index a2a3b11ba..bfd35f291 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -63,6 +63,7 @@ public abstract class Chunk implements Viewable, DataContainer { // Options private final boolean shouldGenerate; + private boolean readOnly; // Packet cache private volatile boolean enableCachePacket; @@ -277,6 +278,24 @@ public abstract class Chunk implements Viewable, DataContainer { return shouldGenerate; } + /** + * Gets if this chunk is read-only. + * + * @return true if the chunk is read-only + */ + public boolean isReadOnly() { + return readOnly; + } + + /** + * Changes the read state of the chunk. + * + * @param readOnly true to make the chunk read-only, false otherwise + */ + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + /** * Gets if this chunk automatically cache the latest {@link ChunkDataPacket} version. *

diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 5c88d66e4..bb30b56e6 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -160,6 +160,10 @@ public class InstanceContainer extends Instance { * @param data the {@link Data}, null if none */ private void UNSAFE_setBlock(Chunk chunk, int x, int y, int z, short blockStateId, CustomBlock customBlock, Data data) { + if (chunk.isReadOnly()) { + return; + } + synchronized (chunk) { final boolean isCustomBlock = customBlock != null; @@ -342,6 +346,11 @@ public class InstanceContainer extends Instance { final Chunk chunk = getChunkAt(blockPosition); + // Cancel if the chunk is read-only + if (chunk.isReadOnly()) { + return false; + } + // Chunk unloaded, stop here if (!ChunkUtils.isLoaded(chunk)) return false; diff --git a/src/main/java/net/minestom/server/instance/StaticChunk.java b/src/main/java/net/minestom/server/instance/StaticChunk.java index 683162478..74c3de608 100644 --- a/src/main/java/net/minestom/server/instance/StaticChunk.java +++ b/src/main/java/net/minestom/server/instance/StaticChunk.java @@ -27,6 +27,7 @@ public class StaticChunk extends Chunk { public StaticChunk(Instance instance, Biome[] biomes, int chunkX, int chunkZ, BlockProvider blockProvider) { super(instance, biomes, chunkX, chunkZ, false); this.blockProvider = blockProvider; + setReadOnly(true); } @Override diff --git a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java index 314f72d8a..33f1a4329 100644 --- a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java +++ b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java @@ -80,46 +80,50 @@ public class BlockPlacementListener { boolean refreshChunk = false; if (material.isBlock()) { - final Block block = material.getBlock(); - final Set entities = instance.getChunkEntities(chunk); - // Check if the player is trying to place a block in an entity - boolean intersect = false; - if (block.isSolid()) { - for (Entity entity : entities) { - intersect = entity.getBoundingBox().intersect(blockPosition); - if (intersect) - break; - } - } - - if (!intersect) { - // BlockPlacementRule check - final BlockManager blockManager = MinecraftServer.getBlockManager(); - final BlockPlacementRule blockPlacementRule = blockManager.getBlockPlacementRule(block); - final short blockStateId = blockPlacementRule == null ? block.getBlockId() : - blockPlacementRule.blockPlace(instance, block, blockFace, player); - - PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, blockStateId, (short) 0, blockPosition, packet.hand); - playerBlockPlaceEvent.consumeBlock(player.getGameMode() != GameMode.CREATIVE); - - // BlockPlacementRule check - final boolean canPlace = blockPlacementRule == null || blockPlacementRule.canPlace(instance, blockPosition); - - player.callEvent(PlayerBlockPlaceEvent.class, playerBlockPlaceEvent); - if (!playerBlockPlaceEvent.isCancelled() && canPlace) { - final short customBlockId = playerBlockPlaceEvent.getCustomBlockId(); - if (customBlockId != 0) { - instance.setSeparateBlocks(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), playerBlockPlaceEvent.getBlockStateId(), playerBlockPlaceEvent.getCustomBlockId()); - } else { - instance.setBlockStateId(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), playerBlockPlaceEvent.getBlockStateId()); + if (!chunk.isReadOnly()) { + final Block block = material.getBlock(); + final Set entities = instance.getChunkEntities(chunk); + // Check if the player is trying to place a block in an entity + boolean intersect = false; + if (block.isSolid()) { + for (Entity entity : entities) { + intersect = entity.getBoundingBox().intersect(blockPosition); + if (intersect) + break; } - if (playerBlockPlaceEvent.doesConsumeBlock()) { - // Consume the block in the player's hand - final ItemStack newUsedItem = usedItem.consume(1); + } - if (newUsedItem != null) { - playerInventory.setItemInHand(hand, newUsedItem); + if (!intersect) { + // BlockPlacementRule check + final BlockManager blockManager = MinecraftServer.getBlockManager(); + final BlockPlacementRule blockPlacementRule = blockManager.getBlockPlacementRule(block); + final short blockStateId = blockPlacementRule == null ? block.getBlockId() : + blockPlacementRule.blockPlace(instance, block, blockFace, player); + + PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, blockStateId, (short) 0, blockPosition, packet.hand); + playerBlockPlaceEvent.consumeBlock(player.getGameMode() != GameMode.CREATIVE); + + // BlockPlacementRule check + final boolean canPlace = blockPlacementRule == null || blockPlacementRule.canPlace(instance, blockPosition); + + player.callEvent(PlayerBlockPlaceEvent.class, playerBlockPlaceEvent); + if (!playerBlockPlaceEvent.isCancelled() && canPlace) { + final short customBlockId = playerBlockPlaceEvent.getCustomBlockId(); + if (customBlockId != 0) { + instance.setSeparateBlocks(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), playerBlockPlaceEvent.getBlockStateId(), playerBlockPlaceEvent.getCustomBlockId()); + } else { + instance.setBlockStateId(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), playerBlockPlaceEvent.getBlockStateId()); } + if (playerBlockPlaceEvent.doesConsumeBlock()) { + // Consume the block in the player's hand + final ItemStack newUsedItem = usedItem.consume(1); + + if (newUsedItem != null) { + playerInventory.setItemInHand(hand, newUsedItem); + } + } + } else { + refreshChunk = true; } } else { refreshChunk = true;