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;