diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java
index 425ec4e40..1e130ad9b 100644
--- a/src/main/java/net/minestom/server/entity/Entity.java
+++ b/src/main/java/net/minestom/server/entity/Entity.java
@@ -597,10 +597,9 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P
/**
* Each entity has an unique id (server-wide) which will change after a restart.
- *
- * All entities can be retrieved by calling {@link Entity#getEntity(int)}.
*
* @return the unique entity id
+ * @see Entity#getEntity(int) to retrive an entity based on its id
*/
public int getEntityId() {
return id;
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 16384213e..f85493e24 100644
--- a/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java
+++ b/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java
@@ -1,11 +1,18 @@
package net.minestom.server.instance.batch;
-import kotlin.collections.ArrayDeque;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.longs.LongList;
import net.minestom.server.data.Data;
import net.minestom.server.instance.*;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.utils.block.CustomBlockUtils;
+import net.minestom.server.utils.callback.OptionalCallback;
import net.minestom.server.utils.chunk.ChunkCallback;
+import net.minestom.server.utils.chunk.ChunkUtils;
+import net.minestom.server.utils.validate.Check;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -21,48 +28,68 @@ import java.util.List;
*/
public class ChunkBatch implements InstanceBatch {
- private static final int INITIAL_SIZE = (Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SIZE_Y * Chunk.CHUNK_SIZE_Z) / 2;
-
private final InstanceContainer instance;
private final Chunk chunk;
// Need to be synchronized manually
- private final ArrayDeque dataList = new ArrayDeque<>(INITIAL_SIZE);
+ // Format: blockIndex/blockStateId/customBlockId (32/16/16 bits)
+ private final LongList blocks = new LongArrayList();
- public ChunkBatch(InstanceContainer instance, Chunk chunk) {
+ // Need to be synchronized manually
+ // block index - data
+ private final Int2ObjectMap blockDataMap = new Int2ObjectOpenHashMap<>();
+
+ public ChunkBatch(@NotNull InstanceContainer instance, @NotNull Chunk chunk) {
this.instance = instance;
this.chunk = chunk;
}
@Override
- public void setBlockStateId(int x, int y, int z, short blockStateId, Data data) {
+ public void setBlockStateId(int x, int y, int z, short blockStateId, @Nullable Data 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) {
+ public void setCustomBlock(int x, int y, int z, short customBlockId, @Nullable Data data) {
final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId);
+ Check.notNull(customBlock, "The custom block with the id " + customBlockId + " does not exist!");
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) {
+ public void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) {
addBlockData((byte) x, y, (byte) z, blockStateId, customBlockId, 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
- final BlockData blockData = new BlockData(x, y, z, blockStateId, customBlockId, data);
- synchronized (dataList) {
- this.dataList.add(blockData);
+ private void addBlockData(byte x, int y, byte z, short blockStateId, short customBlockId, @Nullable Data data) {
+ final int index = ChunkUtils.getBlockIndex(x, y, z);
+
+ if (data != null) {
+ synchronized (blockDataMap) {
+ this.blockDataMap.put(index, data);
+ }
+ }
+
+ long value = index;
+ value = (value << 16) | blockStateId;
+ value = (value << 16) | customBlockId;
+
+ synchronized (blocks) {
+ this.blocks.add(value);
}
}
- public void flushChunkGenerator(ChunkGenerator chunkGenerator, @Nullable ChunkCallback callback) {
- BLOCK_BATCH_POOL.execute(() -> {
- final List populators = chunkGenerator.getPopulators();
- final boolean hasPopulator = populators != null && !populators.isEmpty();
+ public void flushChunkGenerator(@NotNull ChunkGenerator chunkGenerator, @Nullable ChunkCallback callback) {
+ final List populators = chunkGenerator.getPopulators();
+ final boolean hasPopulator = populators != null && !populators.isEmpty();
+ // Check if there is anything to process
+ if (blocks.isEmpty() && !hasPopulator) {
+ OptionalCallback.execute(callback, chunk);
+ return;
+ }
+
+ BLOCK_BATCH_POOL.execute(() -> {
chunkGenerator.generateChunkData(this, chunk.getChunkX(), chunk.getChunkZ());
singleThreadFlush(hasPopulator ? null : callback, true);
@@ -104,8 +131,8 @@ public class ChunkBatch implements InstanceBatch {
* Resets the chunk batch by removing all the entries.
*/
public void clearData() {
- synchronized (dataList) {
- this.dataList.clear();
+ synchronized (blocks) {
+ this.blocks.clear();
}
}
@@ -116,13 +143,18 @@ public class ChunkBatch implements InstanceBatch {
* @param safeCallback true to run the callback in the instance update thread, otherwise run in the current one
*/
private void singleThreadFlush(@Nullable ChunkCallback callback, boolean safeCallback) {
+ if (blocks.isEmpty()) {
+ OptionalCallback.execute(callback, chunk);
+ return;
+ }
+
synchronized (chunk) {
if (!chunk.isLoaded())
return;
- synchronized (dataList) {
- for (BlockData data : dataList) {
- data.apply(chunk);
+ synchronized (blocks) {
+ for (long block : blocks) {
+ apply(chunk, block);
}
}
@@ -141,25 +173,29 @@ public class ChunkBatch implements InstanceBatch {
}
}
- private static class BlockData {
+ /**
+ * Places a block which is encoded in a long.
+ *
+ * @param chunk the chunk to place the block on
+ * @param value the block data
+ */
+ private void apply(@NotNull Chunk chunk, long value) {
+ final short customBlockId = (short) (value & 0xFF);
+ final short blockId = (short) (value >> 16 & 0xFF);
+ final int index = (int) (value >> 32 & 0xFFFF);
- private final int x, y, z;
- private final short blockStateId;
- private final short customBlockId;
- private final Data data;
-
- private BlockData(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) {
- this.x = x;
- this.y = y;
- this.z = z;
- this.blockStateId = blockStateId;
- this.customBlockId = customBlockId;
- this.data = data;
+ Data data = null;
+ if (!blockDataMap.isEmpty()) {
+ synchronized (blockDataMap) {
+ data = blockDataMap.get(index);
+ }
}
- public void apply(Chunk chunk) {
- chunk.UNSAFE_setBlock(x, y, z, blockStateId, customBlockId, data, CustomBlockUtils.hasUpdate(customBlockId));
- }
+ chunk.UNSAFE_setBlock(ChunkUtils.blockIndexToChunkPositionX(index),
+ ChunkUtils.blockIndexToChunkPositionY(index),
+ ChunkUtils.blockIndexToChunkPositionZ(index),
+ blockId, customBlockId, data, CustomBlockUtils.hasUpdate(customBlockId));
+
}
diff --git a/src/main/java/net/minestom/server/inventory/Inventory.java b/src/main/java/net/minestom/server/inventory/Inventory.java
index 1d64a7511..52c3e8a15 100644
--- a/src/main/java/net/minestom/server/inventory/Inventory.java
+++ b/src/main/java/net/minestom/server/inventory/Inventory.java
@@ -186,7 +186,6 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
setItemStackInternal(i, ItemStack.getAirItem());
}
// Send the cleared inventory to viewers
- // TODO cached packet with empty content
update();
}
diff --git a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java
index 56a3d6275..7b014f713 100644
--- a/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java
+++ b/src/main/java/net/minestom/server/utils/chunk/ChunkUtils.java
@@ -196,9 +196,7 @@ public final class ChunkUtils {
* @return the X coordinate of the block index
*/
public static int blockIndexToPositionX(int index, int chunkX) {
- int x = (byte) (index & 0xF);
- x += Chunk.CHUNK_SIZE_X * chunkX;
- return x;
+ return (int) blockIndexToChunkPositionX(index) + Chunk.CHUNK_SIZE_X * chunkX;
}
/**
@@ -219,9 +217,7 @@ public final class ChunkUtils {
* @return the Z coordinate of the block index
*/
public static int blockIndexToPositionZ(int index, int chunkZ) {
- int z = (byte) (index >> 12 & 0xF);
- z += Chunk.CHUNK_SIZE_Z * chunkZ;
- return z;
+ return (int) blockIndexToChunkPositionZ(index) + Chunk.CHUNK_SIZE_Z * chunkZ;
}
/**
@@ -231,7 +227,7 @@ public final class ChunkUtils {
* @return the chunk position X (O-15) of the specified index
*/
public static byte blockIndexToChunkPositionX(int index) {
- return (byte) blockIndexToPositionX(index, 0);
+ return (byte) (index & 0xF);
}
/**
@@ -241,7 +237,7 @@ public final class ChunkUtils {
* @return the chunk position Y (O-255) of the specified index
*/
public static short blockIndexToChunkPositionY(int index) {
- return (short) blockIndexToPositionY(index);
+ return (short) (index >>> 4 & 0xFF);
}
/**
@@ -251,7 +247,7 @@ public final class ChunkUtils {
* @return the chunk position Z (O-15) of the specified index
*/
public static byte blockIndexToChunkPositionZ(int index) {
- return (byte) blockIndexToPositionZ(index, 0);
+ return (byte) (index >> 12 & 0xF);
}
}
diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java
index d48d58588..0979c7b54 100644
--- a/src/test/java/demo/Main.java
+++ b/src/test/java/demo/Main.java
@@ -6,7 +6,6 @@ import demo.blocks.UpdatableBlockDemo;
import demo.commands.*;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager;
-import net.minestom.server.extras.bungee.BungeeCordProxy;
import net.minestom.server.extras.optifine.OptifineSupport;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule;
@@ -53,7 +52,7 @@ public class Main {
OptifineSupport.enable();
//VelocityProxy.enable("rBeJJ79W4MVU");
- BungeeCordProxy.enable();
+ //BungeeCordProxy.enable();
// MojangAuth.init();