diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index 6b26c5f10..56bf3b91a 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -77,28 +77,12 @@ public abstract class Chunk implements Viewable { this.chunkZ = chunkZ; } - public void UNSAFE_setBlock(int x, int y, int z, short blockStateId, Data data) { - setBlock(x, y, z, blockStateId, (short) 0, data, null); - } - - public void UNSAFE_setCustomBlock(int x, int y, int z, short blockStateId, short customBlockId, Data data) { - final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); - Check.notNull(customBlock, "The custom block " + customBlockId + " does not exist or isn't registered"); - - UNSAFE_setCustomBlock(x, y, z, blockStateId, customBlock, data); - } - - protected void UNSAFE_setCustomBlock(int x, int y, int z, short blockStateId, CustomBlock customBlock, Data data) { - final UpdateConsumer updateConsumer = customBlock.hasUpdate() ? customBlock::update : null; - setBlock(x, y, z, blockStateId, customBlock.getCustomBlockId(), data, updateConsumer); - } - - public abstract void UNSAFE_removeCustomBlock(int x, int y, int z); - /** * Set a block at a position *

- * WARNING: this method is not thread-safe (in order to bring performance improvement with {@link ChunkBatch} & {@link BlockBatch} + * This is used when the previous block has to be destroyed, meaning that it clears the previous data and update method + *

+ * WARNING: this method is not thread-safe (in order to bring performance improvement with {@link ChunkBatch} & {@link BlockBatch}) * The thread-safe version is {@link InstanceContainer#setSeparateBlocks(int, int, int, short, short, Data)} (or any similar instance methods) * * @param x the block X @@ -109,8 +93,16 @@ public abstract class Chunk implements Viewable { * @param data the data of the block, can be null * @param updateConsumer the update method of the block, can be null */ - protected abstract void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer); + public abstract void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer); + /** + * Set the {@link Data} at a position + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @param data the new data + */ public void setBlockData(int x, int y, int z, Data data) { final int index = getBlockIndex(x, y, z); if (data != null) { @@ -120,12 +112,45 @@ public abstract class Chunk implements Viewable { } } + /** + * Get the block state id at a position + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @return the block state id at the position + */ public abstract short getBlockStateId(int x, int y, int z); + /** + * Get the custom block id at a position + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @return the custom block id at the position + */ public abstract short getCustomBlockId(int x, int y, int z); - public abstract CustomBlock getCustomBlock(int x, int y, int z); + /** + * Get the {@link CustomBlock} at a position + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @return the {@link CustomBlock} at the position + */ + public CustomBlock getCustomBlock(int x, int y, int z) { + final short customBlockId = getCustomBlockId(x, y, z); + return customBlockId != 0 ? BLOCK_MANAGER.getCustomBlock(customBlockId) : null; + } + /** + * Get the {@link CustomBlock} at a block index + * + * @param index the block index + * @return the {@link CustomBlock} at the block index + */ protected CustomBlock getCustomBlock(int index) { final int x = ChunkUtils.blockIndexToChunkPositionX(index); final int y = ChunkUtils.blockIndexToChunkPositionY(index); @@ -133,25 +158,48 @@ public abstract class Chunk implements Viewable { return getCustomBlock(x, y, z); } + /** + * Change the block state id and the custom block id at a position + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @param blockStateId the new block state id + * @param customId the new custom block id + */ protected abstract void refreshBlockValue(int x, int y, int z, short blockStateId, short customId); + /** + * Change the block state id at a position (the custom block id stays the same) + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @param blockStateId the new block state id + */ protected abstract void refreshBlockStateId(int x, int y, int z, short blockStateId); - protected void refreshBlockValue(int x, int y, int z, short blockStateId) { - final CustomBlock customBlock = getCustomBlock(x, y, z); - final short customBlockId = customBlock == null ? 0 : customBlock.getCustomBlockId(); - refreshBlockValue(x, y, z, blockStateId, customBlockId); - } - public Data getData(int x, int y, int z) { final int index = getBlockIndex(x, y, z); return getData(index); } + /** + * Get the {@link Data} at a block index + * + * @param index the block index + * @return the {@link Data} at the block index + */ protected Data getData(int index) { return blocksData.get(index); } + /** + * Execute a tick update for all the updatable blocks in this chunk + * + * @param time the time of the update in milliseconds + * @param instance the instance linked to this chunk + */ public synchronized void updateBlocks(long time, Instance instance) { if (updatableBlocks.isEmpty()) return; @@ -181,10 +229,20 @@ public abstract class Chunk implements Viewable { return biomes; } + /** + * Get the chunk X + * + * @return the chunk X + */ public int getChunkX() { return chunkX; } + /** + * Get the chunk Z + * + * @return the chunk Z + */ public int getChunkZ() { return chunkZ; } @@ -455,6 +513,14 @@ public abstract class Chunk implements Viewable { this.loaded = false; } + /** + * Get the index of a position, used to store blocks + * + * @param x the block X + * @param y the block Y + * @param z the block Z + * @return the block index + */ protected int getBlockIndex(int x, int y, int z) { return ChunkUtils.getBlockIndex(x, y, z); } diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 88bb8ccb8..1a7bfe00b 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -7,7 +7,6 @@ import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; import net.minestom.server.data.Data; import net.minestom.server.data.SerializableData; import net.minestom.server.entity.pathfinding.PFBlockDescription; -import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.instance.block.UpdateConsumer; import net.minestom.server.network.packet.server.play.ChunkDataPacket; import net.minestom.server.reader.ChunkReader; @@ -31,19 +30,7 @@ public class DynamicChunk extends Chunk { } @Override - public void UNSAFE_removeCustomBlock(int x, int y, int z) { - final int index = getBlockIndex(x, y, z); - this.customBlocksId[index] = 0; // Set to none - this.blocksData.remove(index); - - this.updatableBlocks.remove(index); - this.updatableBlocksLastUpdate.remove(index); - - this.blockEntities.remove(index); - } - - @Override - protected void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) { + public void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) { { // Update pathfinder @@ -121,16 +108,6 @@ public class DynamicChunk extends Chunk { return customBlocksId[index]; } - @Override - public CustomBlock getCustomBlock(int x, int y, int z) { - final int index = getBlockIndex(x, y, z); - if (!MathUtils.isBetween(index, 0, blocksStateId.length)) { - return null; // TODO: custom invalid block - } - final short id = customBlocksId[index]; - return id != 0 ? BLOCK_MANAGER.getCustomBlock(id) : null; - } - @Override protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customId) { final int blockIndex = getBlockIndex(x, y, z); diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index 447f534f4..785dd5d22 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -13,6 +13,7 @@ import net.minestom.server.instance.batch.ChunkBatch; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.BlockProvider; import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.instance.block.UpdateConsumer; import net.minestom.server.instance.block.rule.BlockPlacementRule; import net.minestom.server.network.packet.server.play.BlockChangePacket; import net.minestom.server.network.packet.server.play.ParticlePacket; @@ -22,6 +23,7 @@ import net.minestom.server.particle.ParticleCreator; import net.minestom.server.storage.StorageLocation; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; +import net.minestom.server.utils.block.CustomBlockUtils; import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.thread.MinestomThread; import net.minestom.server.utils.time.TimeUnit; @@ -135,14 +137,18 @@ public class InstanceContainer extends Instance { // Change id based on neighbors blockStateId = executeBlockPlacementRule(blockStateId, blockPosition); - // Set the block + // Retrieve custom block values + short customBlockId = 0; + UpdateConsumer updateConsumer = null; if (isCustomBlock) { + customBlockId = customBlock.getCustomBlockId(); data = customBlock.createData(this, blockPosition, data); - chunk.UNSAFE_setCustomBlock(x, y, z, blockStateId, customBlock, data); - } else { - chunk.UNSAFE_setBlock(x, y, z, blockStateId, data); + updateConsumer = CustomBlockUtils.getCustomBlockUpdate(customBlock); } + // Set the block + chunk.setBlock(x, y, z, blockStateId, customBlockId, data, updateConsumer); + // Refresh neighbors since a new block has been placed executeNeighboursBlockPlacementRule(blockPosition); @@ -197,7 +203,6 @@ public class InstanceContainer extends Instance { private void callBlockDestroy(Chunk chunk, int index, CustomBlock previousBlock, BlockPosition blockPosition) { final Data previousData = chunk.getData(index); previousBlock.onDestroy(this, blockPosition, previousData); - chunk.UNSAFE_removeCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); } /** diff --git a/src/main/java/net/minestom/server/instance/StaticChunk.java b/src/main/java/net/minestom/server/instance/StaticChunk.java index b12c88267..032c1f3d7 100644 --- a/src/main/java/net/minestom/server/instance/StaticChunk.java +++ b/src/main/java/net/minestom/server/instance/StaticChunk.java @@ -3,7 +3,6 @@ package net.minestom.server.instance; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minestom.server.data.Data; import net.minestom.server.instance.block.BlockProvider; -import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.instance.block.UpdateConsumer; import net.minestom.server.network.packet.server.play.ChunkDataPacket; import net.minestom.server.utils.chunk.ChunkUtils; @@ -21,12 +20,7 @@ public class StaticChunk extends Chunk { } @Override - public void UNSAFE_removeCustomBlock(int x, int y, int z) { - //noop - } - - @Override - protected void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) { + public void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) { //noop } @@ -41,12 +35,6 @@ public class StaticChunk extends Chunk { return 0; } - @Override - public CustomBlock getCustomBlock(int x, int y, int z) { - //noop - return null; - } - @Override protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customId) { //noop diff --git a/src/main/java/net/minestom/server/instance/batch/BlockBatch.java b/src/main/java/net/minestom/server/instance/batch/BlockBatch.java index fb7f3e40b..d37302112 100644 --- a/src/main/java/net/minestom/server/instance/batch/BlockBatch.java +++ b/src/main/java/net/minestom/server/instance/batch/BlockBatch.java @@ -4,11 +4,13 @@ import net.minestom.server.data.Data; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.utils.block.CustomBlockUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; public class BlockBatch implements InstanceBatch { @@ -23,23 +25,23 @@ public class BlockBatch implements InstanceBatch { @Override public synchronized void setBlockStateId(int x, int y, int z, short blockStateId, Data data) { final Chunk chunk = this.instance.getChunkAt(x, z); - addBlockData(chunk, x, y, z, false, blockStateId, (short) 0, data); + addBlockData(chunk, x, y, z, blockStateId, (short) 0, data); } @Override public void setCustomBlock(int x, int y, int z, short customBlockId, Data data) { final Chunk chunk = this.instance.getChunkAt(x, z); final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); - addBlockData(chunk, x, y, z, true, customBlock.getDefaultBlockStateId(), customBlockId, data); + addBlockData(chunk, x, y, z, customBlock.getDefaultBlockStateId(), customBlockId, data); } @Override public synchronized void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, Data data) { final Chunk chunk = this.instance.getChunkAt(x, z); - addBlockData(chunk, x, y, z, true, blockStateId, customBlockId, data); + addBlockData(chunk, x, y, z, blockStateId, customBlockId, data); } - private void addBlockData(Chunk chunk, int x, int y, int z, boolean customBlock, short blockStateId, short customBlockId, Data data) { + private void addBlockData(Chunk chunk, int x, int y, int z, short blockStateId, short customBlockId, Data data) { List blocksData = this.data.get(chunk); if (blocksData == null) blocksData = new ArrayList<>(); @@ -48,7 +50,6 @@ public class BlockBatch implements InstanceBatch { blockData.x = x; blockData.y = y; blockData.z = z; - blockData.hasCustomBlock = customBlock; blockData.blockStateId = blockStateId; blockData.customBlockId = customBlockId; blockData.data = data; @@ -59,48 +60,45 @@ public class BlockBatch implements InstanceBatch { } public void flush(Runnable callback) { - int counter = 0; - for (Map.Entry> entry : data.entrySet()) { - counter++; - final Chunk chunk = entry.getKey(); - final List dataList = entry.getValue(); - final boolean isLast = counter == data.size(); - batchesPool.execute(() -> { - synchronized (chunk) { - if (!chunk.isLoaded()) - return; + synchronized (data) { + AtomicInteger counter = new AtomicInteger(); + for (Map.Entry> entry : data.entrySet()) { + final Chunk chunk = entry.getKey(); + final List dataList = entry.getValue(); + batchesPool.execute(() -> { + synchronized (chunk) { + if (!chunk.isLoaded()) + return; + + for (BlockData data : dataList) { + data.apply(chunk); + } + + // Refresh chunk for viewers + chunk.sendChunkUpdate(); + + final boolean isLast = counter.incrementAndGet() == data.size(); + + if (isLast) { + if (callback != null) + callback.run(); + } - for (BlockData data : dataList) { - data.apply(chunk); } - - // Refresh chunk for viewers - chunk.sendChunkUpdate(); - - if (isLast) { - if (callback != null) - callback.run(); - } - - } - }); + }); + } } } private static class BlockData { private int x, y, z; - private boolean hasCustomBlock; private short blockStateId; private short customBlockId; private Data data; public void apply(Chunk chunk) { - if (!hasCustomBlock) { - chunk.UNSAFE_setBlock(x, y, z, blockStateId, data); - } else { - chunk.UNSAFE_setCustomBlock(x, y, z, blockStateId, customBlockId, data); - } + chunk.setBlock(x, y, z, blockStateId, customBlockId, data, CustomBlockUtils.getCustomBlockUpdate(customBlockId)); } } diff --git a/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java b/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java index 5ed8cb4fd..e58cc5471 100644 --- a/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java +++ b/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java @@ -6,6 +6,7 @@ import net.minestom.server.instance.ChunkGenerator; import net.minestom.server.instance.ChunkPopulator; import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.utils.block.CustomBlockUtils; import java.util.ArrayList; import java.util.Collections; @@ -32,27 +33,26 @@ public class ChunkBatch implements InstanceBatch { @Override public void setBlockStateId(int x, int y, int z, short blockStateId, Data data) { - addBlockData((byte) x, y, (byte) z, false, blockStateId, (short) 0, data); + addBlockData((byte) x, y, (byte) z, blockStateId, (short) 0, data); } @Override public void setCustomBlock(int x, int y, int z, short customBlockId, Data data) { final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); - addBlockData((byte) x, y, (byte) z, true, customBlock.getDefaultBlockStateId(), customBlockId, data); + addBlockData((byte) x, y, (byte) z, customBlock.getDefaultBlockStateId(), customBlockId, data); } @Override public void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, Data data) { - addBlockData((byte) x, y, (byte) z, true, blockStateId, customBlockId, data); + addBlockData((byte) x, y, (byte) z, blockStateId, customBlockId, data); } - private void addBlockData(byte x, int y, byte z, boolean customBlock, short blockStateId, short customBlockId, Data data) { + private void addBlockData(byte x, int y, byte z, short blockStateId, short customBlockId, Data data) { // TODO store a single long with bitwise operators (xyz;boolean,short,short,boolean) with the data in a map BlockData blockData = new BlockData(); blockData.x = x; blockData.y = y; blockData.z = z; - blockData.hasCustomBlock = customBlock; blockData.blockStateId = blockStateId; blockData.customBlockId = customBlockId; blockData.data = data; @@ -92,36 +92,33 @@ public class ChunkBatch implements InstanceBatch { } private void singleThreadFlush(Consumer callback) { - synchronized (chunk) { - if (!chunk.isLoaded()) - return; + synchronized (dataList) { + synchronized (chunk) { + if (!chunk.isLoaded()) + return; - for (BlockData data : dataList) { - data.apply(chunk); + for (BlockData data : dataList) { + data.apply(chunk); + } + + // Refresh chunk for viewers + chunk.sendChunkUpdate(); + + if (callback != null) + callback.accept(chunk); } - - // Refresh chunk for viewers - chunk.sendChunkUpdate(); - - if (callback != null) - callback.accept(chunk); } } private static class BlockData { private int x, y, z; - private boolean hasCustomBlock; private short blockStateId; private short customBlockId; private Data data; public void apply(Chunk chunk) { - if (!hasCustomBlock) { - chunk.UNSAFE_setBlock(x, y, z, blockStateId, data); - } else { - chunk.UNSAFE_setCustomBlock(x, y, z, blockStateId, customBlockId, data); - } + chunk.setBlock(x, y, z, blockStateId, customBlockId, data, CustomBlockUtils.getCustomBlockUpdate(customBlockId)); } } diff --git a/src/main/java/net/minestom/server/utils/block/CustomBlockUtils.java b/src/main/java/net/minestom/server/utils/block/CustomBlockUtils.java new file mode 100644 index 000000000..116364f9a --- /dev/null +++ b/src/main/java/net/minestom/server/utils/block/CustomBlockUtils.java @@ -0,0 +1,33 @@ +package net.minestom.server.utils.block; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.block.BlockManager; +import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.instance.block.UpdateConsumer; + +public class CustomBlockUtils { + + private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); + + /** + * Get the {@link UpdateConsumer} of a custom block id + * + * @param customBlockId the custom block id + * @return the {@link UpdateConsumer} of the custom block + */ + public static UpdateConsumer getCustomBlockUpdate(short customBlockId) { + final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId); + return getCustomBlockUpdate(customBlock); + } + + /** + * Get the {@link UpdateConsumer} of a {@link CustomBlock} + * + * @param customBlock the {@link CustomBlock} + * @return the {@link UpdateConsumer} of the {@link CustomBlock} + */ + public static UpdateConsumer getCustomBlockUpdate(CustomBlock customBlock) { + return customBlock != null && customBlock.hasUpdate() ? customBlock::update : null; + } + +}