diff --git a/src/main/java/net/minestom/server/entity/pathfinding/AStarPathfinder.java b/src/main/java/net/minestom/server/entity/pathfinding/AStarPathfinder.java index 925309e70..0e7cb8370 100644 --- a/src/main/java/net/minestom/server/entity/pathfinding/AStarPathfinder.java +++ b/src/main/java/net/minestom/server/entity/pathfinding/AStarPathfinder.java @@ -12,6 +12,7 @@ import java.util.List; public class AStarPathfinder { // TODO ladder, jump, etc... + // TODO include BoundingBox support private boolean canClimbLadder; private boolean canSwim; diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index 8f3ab6f32..835b5e7cb 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -37,9 +37,9 @@ public class Chunk implements Viewable { private Biome biome; private int chunkX, chunkZ; - // Int represent the chunk coord of the block - // value is: 2 bytes -> blockId | 2 bytes -> customBlockId (filled with 0 if isn't) - private Int2IntMap blocks = new Int2IntOpenHashMap(16 * 16 * 16); // Start with the size of a full chunk section + // blocks id based on coord + private short[][][] blocksId = new short[CHUNK_SIZE_X][CHUNK_SIZE_Y][CHUNK_SIZE_Z]; + private short[][][] customBlocksId = new short[CHUNK_SIZE_X][CHUNK_SIZE_Y][CHUNK_SIZE_Z]; // Used to get all blocks with data (no null) // Key is still chunk coord @@ -65,39 +65,41 @@ public class Chunk implements Viewable { this.chunkZ = chunkZ; } - public void UNSAFE_setBlock(int index, short blockId, Data data) { - setBlock(index, blockId, (short) 0, data, null); + public void UNSAFE_setBlock(int x, int y, int z, short blockId, Data data) { + setBlock(x, y, z, blockId, (short) 0, data, null); } - public void UNSAFE_setBlock(int index, short blockId) { - UNSAFE_setBlock(index, blockId, null); + public void UNSAFE_setBlock(int x, int y, int z, short blockId) { + UNSAFE_setBlock(x, y, z, blockId, null); } - public void UNSAFE_setCustomBlock(int index, short customBlockId, Data data) { + public void UNSAFE_setCustomBlock(int x, int y, int z, short customBlockId, Data data) { CustomBlock customBlock = BLOCK_MANAGER.getBlock(customBlockId); if (customBlock == null) throw new IllegalArgumentException("The custom block " + customBlockId + " does not exist or isn't registered"); - setCustomBlock(index, customBlock, data); + setCustomBlock(x, y, z, customBlock, data); } - public void UNSAFE_setCustomBlock(int index, short customBlockId) { - UNSAFE_setCustomBlock(index, customBlockId, null); + public void UNSAFE_setCustomBlock(int x, int y, int z, short customBlockId) { + UNSAFE_setCustomBlock(x, y, z, customBlockId, null); } - private void setCustomBlock(int index, CustomBlock customBlock, Data data) { + private void setCustomBlock(int x, int y, int z, CustomBlock customBlock, Data data) { UpdateConsumer updateConsumer = customBlock.hasUpdate() ? customBlock::update : null; - setBlock(index, customBlock.getBlockId(), customBlock.getId(), data, updateConsumer); + setBlock(x, y, z, customBlock.getBlockId(), customBlock.getId(), data, updateConsumer); } - private void setBlock(int index, short blockId, short customId, Data data, UpdateConsumer updateConsumer) { + private void setBlock(int x, int y, int z, short blockId, short customId, Data data, UpdateConsumer updateConsumer) { + int index = SerializerUtils.chunkCoordToIndex(x, y, z); if (blockId != 0 || (blockId == 0 && customId != 0 && updateConsumer != null)) { // Allow custom air block for update purpose, refused if no update consumer has been found - refreshBlockValue(index, blockId, customId); + refreshBlockValue(x, y, z, blockId, customId); } else { // Block has been deleted, clear cache and return - this.blocks.remove(index); + this.blocksId[x][y][z] = 0; // Set to air + //this.blocks.remove(index); this.blocksData.remove(index); @@ -135,7 +137,7 @@ public class Chunk implements Viewable { this.packetUpdated = false; } - public void setBlockData(byte x, byte y, byte z, Data data) { + public void setBlockData(int x, int y, int z, Data data) { int index = SerializerUtils.chunkCoordToIndex(x, y, z); if (data != null) { this.blocksData.put(index, data); @@ -144,42 +146,35 @@ public class Chunk implements Viewable { } } - public short getBlockId(byte x, byte y, byte z) { - int index = SerializerUtils.chunkCoordToIndex(x, y, z); - int value = getBlockValue(index); - return (short) (value >>> 16); + public short getBlockId(int x, int y, int z) { + short id = blocksId[x][y][z]; + return id; } - public CustomBlock getCustomBlock(byte x, byte y, byte z) { - int index = SerializerUtils.chunkCoordToIndex(x, y, z); - return getCustomBlock(index); + public short getCustomBlockId(int x, int y, int z) { + short id = customBlocksId[x][y][z]; + return id; } - protected CustomBlock getCustomBlock(int index) { - int value = getBlockValue(index); - short id = (short) (value & 0xffff); + public CustomBlock getCustomBlock(int x, int y, int z) { + short id = customBlocksId[x][y][z]; return id != 0 ? BLOCK_MANAGER.getBlock(id) : null; } - protected void refreshBlockValue(int index, short blockId, short customId) { - int value = createBlockValue(blockId, customId); - this.blocks.put(index, value); + protected CustomBlock getCustomBlock(int index) { + byte[] pos = SerializerUtils.indexToChunkPosition(index); + return getCustomBlock(pos[0], pos[1], pos[2]); } - protected void refreshBlockValue(int index, short blockId) { - CustomBlock customBlock = getCustomBlock(index); + protected void refreshBlockValue(int x, int y, int z, short blockId, short customId) { + this.blocksId[x][y][z] = blockId; + this.customBlocksId[x][y][z] = customId; + } + + protected void refreshBlockValue(int x, int y, int z, short blockId) { + CustomBlock customBlock = getCustomBlock(x, y, z); short customBlockId = customBlock == null ? 0 : customBlock.getId(); - refreshBlockValue(index, blockId, customBlockId); - } - - public int createBlockValue(short blockId, short customId) { - // Merge blockType and customId to one unique Integer (16/16 bits) - int value = (blockId << 16 | customId & 0xFFFF); - return value; - } - - private int getBlockValue(int index) { - return blocks.getOrDefault(index, 0); + refreshBlockValue(x, y, z, blockId, customBlockId); } public Data getData(byte x, byte y, byte z) { @@ -258,29 +253,38 @@ public class Chunk implements Viewable { DataOutputStream dos = new DataOutputStream(output); dos.writeByte(biome.getId()); - for (Int2IntMap.Entry entry : blocks.int2IntEntrySet()) { - int index = entry.getIntKey(); - int value = entry.getIntValue(); + for (byte x = 0; x < CHUNK_SIZE_X; x++) { + for (short y = 0; y < CHUNK_SIZE_Y; y++) { + for (byte z = 0; z < CHUNK_SIZE_Z; z++) { + int index = SerializerUtils.chunkCoordToIndex(x, y, z); - short blockId = (short) (value >>> 16); - short customBlockId = (short) (value & 0xffff); - boolean isCustomBlock = customBlockId != 0; - short id = isCustomBlock ? customBlockId : blockId; + short blockId = getBlockId(x, y, z); + short customBlockId = getCustomBlockId(x, y, z); + boolean isCustomBlock = customBlockId != 0; + short id = isCustomBlock ? customBlockId : blockId; - Data data = blocksData.get(index); - boolean hasData = data != null; + if (id == 0) + continue; - dos.writeInt(index); // Chunk coord - dos.writeBoolean(isCustomBlock); // Determine the type of the ID - dos.writeShort(id); + Data data = blocksData.get(index); + boolean hasData = data != null; - dos.writeBoolean(hasData); - if (hasData) { - byte[] d = data.getSerializedData(); - dos.writeInt(d.length); - dos.write(d); + // Chunk coord + dos.writeInt(x); + dos.writeInt(y); + dos.writeInt(z); + + dos.writeBoolean(isCustomBlock); // Determine the type of the ID + dos.writeShort(id); + + dos.writeBoolean(hasData); + if (hasData) { + byte[] d = data.getSerializedData(); + dos.writeInt(d.length); + dos.write(d); + } + } } - } byte[] result = output.toByteArray(); diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 37386fc8a..830b99544 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -157,7 +157,12 @@ public abstract class Instance implements BlockModifier, DataContainer { public short getBlockId(int x, int y, int z) { Chunk chunk = getChunkAt(x, z); - return chunk.getBlockId((byte) (x % 16), (byte) y, (byte) (z % 16)); + x = x % 16; + z = z % 16; + + x = ChunkUtils.refreshChunkXZ(x); + z = ChunkUtils.refreshChunkXZ(z); + return chunk.getBlockId(x, y, z); } public short getBlockId(float x, float y, float z) { @@ -170,7 +175,12 @@ public abstract class Instance implements BlockModifier, DataContainer { public CustomBlock getCustomBlock(int x, int y, int z) { Chunk chunk = getChunkAt(x, z); - return chunk.getCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16)); + x = x % 16; + z = z % 16; + + x = ChunkUtils.refreshChunkXZ(x); + z = ChunkUtils.refreshChunkXZ(z); + return chunk.getCustomBlock(x, y, z); } public CustomBlock getCustomBlock(BlockPosition blockPosition) { @@ -192,7 +202,12 @@ public abstract class Instance implements BlockModifier, DataContainer { public Data getBlockData(int x, int y, int z) { Chunk chunk = getChunkAt(x, z); - return chunk.getData((byte) (x % 16), (byte) y, (byte) (z % 16)); + x = x % 16; + z = z % 16; + + x = ChunkUtils.refreshChunkXZ(x); + z = ChunkUtils.refreshChunkXZ(z); + return chunk.getData((byte) x, (byte) y, (byte) z); } public Data getBlockData(BlockPosition blockPosition) { diff --git a/src/main/java/net/minestom/server/instance/InstanceContainer.java b/src/main/java/net/minestom/server/instance/InstanceContainer.java index c8b71ec85..3e60527d1 100644 --- a/src/main/java/net/minestom/server/instance/InstanceContainer.java +++ b/src/main/java/net/minestom/server/instance/InstanceContainer.java @@ -48,9 +48,12 @@ public class InstanceContainer extends Instance { Chunk chunk = getChunkAt(x, z); synchronized (chunk) { - byte chunkX = (byte) (x % 16); - byte chunkY = (byte) y; - byte chunkZ = (byte) (z % 16); + int chunkX = x % 16; + int chunkY = y; + int chunkZ = z % 16; + + chunkX = ChunkUtils.refreshChunkXZ(chunkX); + chunkZ = ChunkUtils.refreshChunkXZ(chunkZ); int index = SerializerUtils.chunkCoordToIndex(chunkX, chunkY, chunkZ); @@ -60,7 +63,7 @@ public class InstanceContainer extends Instance { blockId = executeBlockPlacementRule(blockId, blockPosition); - chunk.UNSAFE_setBlock(index, blockId, data); + chunk.UNSAFE_setBlock(chunkX, chunkY, chunkZ, blockId, data); executeNeighboursBlockPlacementRule(blockPosition); @@ -72,9 +75,14 @@ public class InstanceContainer extends Instance { public synchronized void setCustomBlock(int x, int y, int z, short blockId, Data data) { Chunk chunk = getChunkAt(x, z); synchronized (chunk) { - byte chunkX = (byte) (x % 16); - byte chunkY = (byte) y; - byte chunkZ = (byte) (z % 16); + + int chunkX = x % 16; + int chunkY = y; + int chunkZ = z % 16; + + chunkX = ChunkUtils.refreshChunkXZ(chunkX); + chunkZ = ChunkUtils.refreshChunkXZ(chunkZ); + int index = SerializerUtils.chunkCoordToIndex(chunkX, chunkY, chunkZ); callBlockDestroy(chunk, index, x, y, z); @@ -83,7 +91,7 @@ public class InstanceContainer extends Instance { blockId = executeBlockPlacementRule(blockId, blockPosition); - chunk.UNSAFE_setCustomBlock(index, blockId, data); + chunk.UNSAFE_setCustomBlock(chunkX, chunkY, chunkZ, blockId, data); executeNeighboursBlockPlacementRule(blockPosition); @@ -103,7 +111,7 @@ public class InstanceContainer extends Instance { byte chunkZ = (byte) (z % 16); int index = SerializerUtils.chunkCoordToIndex(chunkX, chunkY, chunkZ); - chunk.refreshBlockValue(index, blockId); + chunk.refreshBlockValue(chunkX, chunkY, chunkZ, blockId); // TODO instead of sending a block change packet each time, cache changed blocks and flush them every tick with a MultiBlockChangePacket sendBlockChange(chunk, x, y, z, blockId); @@ -159,7 +167,15 @@ public class InstanceContainer extends Instance { @Override public void breakBlock(Player player, BlockPosition blockPosition) { Chunk chunk = getChunkAt(blockPosition); - short blockId = chunk.getBlockId((byte) (blockPosition.getX() % 16), (byte) blockPosition.getY(), (byte) (blockPosition.getZ() % 16)); + + int blockX = blockPosition.getX(); + int blockY = blockPosition.getY(); + int blockZ = blockPosition.getZ(); + + blockX = ChunkUtils.refreshChunkXZ(blockX); + blockZ = ChunkUtils.refreshChunkXZ(blockZ); + + short blockId = chunk.getBlockId((byte) (blockX % 16), blockY, (byte) (blockZ % 16)); if (blockId == 0) { sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player); return; 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 5664338a9..781f070cf 100644 --- a/src/main/java/net/minestom/server/instance/batch/BlockBatch.java +++ b/src/main/java/net/minestom/server/instance/batch/BlockBatch.java @@ -3,7 +3,6 @@ package net.minestom.server.instance.batch; import net.minestom.server.data.Data; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.InstanceContainer; -import net.minestom.server.utils.SerializerUtils; import java.util.ArrayList; import java.util.HashMap; @@ -89,11 +88,10 @@ public class BlockBatch implements InstanceBatch { private Data data; public void apply(Chunk chunk) { - int index = SerializerUtils.chunkCoordToIndex((byte) x, (byte) y, (byte) z); if (!isCustomBlock) { - chunk.UNSAFE_setBlock(index, blockId, data); + chunk.UNSAFE_setBlock(x, y, z, blockId, data); } else { - chunk.UNSAFE_setCustomBlock(index, blockId, data); + chunk.UNSAFE_setCustomBlock(x, y, z, blockId, data); } } 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 142e2eef8..63486ed20 100644 --- a/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java +++ b/src/main/java/net/minestom/server/instance/batch/ChunkBatch.java @@ -4,7 +4,6 @@ import net.minestom.server.data.Data; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.ChunkGenerator; import net.minestom.server.instance.InstanceContainer; -import net.minestom.server.utils.SerializerUtils; import java.util.ArrayList; import java.util.Collections; @@ -31,15 +30,15 @@ public class ChunkBatch implements InstanceBatch { @Override public void setBlock(int x, int y, int z, short blockId, Data data) { - addBlockData((byte) x, (byte) y, (byte) z, false, blockId, data); + addBlockData((byte) x, y, (byte) z, false, blockId, data); } @Override public void setCustomBlock(int x, int y, int z, short blockId, Data data) { - addBlockData((byte) x, (byte) y, (byte) z, true, blockId, data); + addBlockData((byte) x, y, (byte) z, true, blockId, data); } - private void addBlockData(byte x, byte y, byte z, boolean customBlock, short blockId, Data data) { + private void addBlockData(byte x, int y, byte z, boolean customBlock, short blockId, Data data) { BlockData blockData = new BlockData(); blockData.x = x; blockData.y = y; @@ -81,17 +80,16 @@ public class ChunkBatch implements InstanceBatch { private class BlockData { - private byte x, y, z; + private int x, y, z; private boolean isCustomBlock; private short blockId; private Data data; public void apply(Chunk chunk) { - int index = SerializerUtils.chunkCoordToIndex(x, y, z); if (!isCustomBlock) { - chunk.UNSAFE_setBlock(index, blockId, data); + chunk.UNSAFE_setBlock(x, y, z, blockId, data); } else { - chunk.UNSAFE_setCustomBlock(index, blockId, data); + chunk.UNSAFE_setCustomBlock(x, y, z, blockId, data); } } diff --git a/src/main/java/net/minestom/server/io/ChunkReader.java b/src/main/java/net/minestom/server/io/ChunkReader.java index 6e9a4c45a..1f05f85a6 100644 --- a/src/main/java/net/minestom/server/io/ChunkReader.java +++ b/src/main/java/net/minestom/server/io/ChunkReader.java @@ -6,7 +6,6 @@ import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.batch.ChunkBatch; import net.minestom.server.utils.CompressionUtils; -import net.minestom.server.utils.SerializerUtils; import java.io.ByteArrayInputStream; import java.io.DataInputStream; @@ -29,7 +28,10 @@ public class ChunkReader { chunkBatch = instance.createChunkBatch(chunk); while (true) { - int index = stream.readInt(); + int x = stream.readInt(); + int y = stream.readInt(); + int z = stream.readInt(); + boolean isCustomBlock = stream.readBoolean(); short blockId = stream.readShort(); boolean hasData = stream.readBoolean(); @@ -42,10 +44,6 @@ public class ChunkReader { data = DataReader.readData(dataArray, false); } - byte[] chunkPos = SerializerUtils.indexToChunkPosition(index); - byte x = chunkPos[0]; - byte y = chunkPos[1]; - byte z = chunkPos[2]; if (isCustomBlock) { chunkBatch.setCustomBlock(x, y, z, blockId, data); } else { diff --git a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java index 6379ce6ab..4984a1424 100644 --- a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java +++ b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java @@ -26,8 +26,6 @@ import java.util.Set; public class BlockPlacementListener { - private Instance instance; - public static void listener(ClientPlayerBlockPlacementPacket packet, Player player) { PlayerInventory playerInventory = player.getInventory(); Player.Hand hand = packet.hand; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java index 58c309454..5acb99d92 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java @@ -24,7 +24,7 @@ public class ChunkDataPacket implements ServerPacket { public Chunk chunk; public int[] sections; - private static final int CHUNK_SECTION_COUNT = 16; + private static final byte CHUNK_SECTION_COUNT = 16; private static final int BITS_PER_ENTRY = 14; private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES; @@ -36,7 +36,7 @@ public class ChunkDataPacket implements ServerPacket { int mask = 0; BufferWrapper blocks = BufferUtils.getBuffer(MAX_BUFFER_SIZE); - for (int i = 0; i < CHUNK_SECTION_COUNT; i++) { + for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) { if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) { short[] section = getSection(chunk, i); if (section != null) { // section contains at least one block @@ -109,14 +109,13 @@ public class ChunkDataPacket implements ServerPacket { } } - private short[] getSection(Chunk chunk, int section) { + private short[] getSection(Chunk chunk, byte section) { short[] blocks = new short[16 * 16 * 16]; boolean empty = true; - for (byte y = 0; y < 16; y++) { for (byte x = 0; x < 16; x++) { for (byte z = 0; z < 16; z++) { - short blockId = chunk.getBlockId(x, (byte) (y + 16 * section), z); + short blockId = chunk.getBlockId(x, (y + 16 * section), z); if (blockId != 0) empty = false; diff --git a/src/main/java/net/minestom/server/utils/ChunkUtils.java b/src/main/java/net/minestom/server/utils/ChunkUtils.java index a15c32ef1..f2554f8f5 100644 --- a/src/main/java/net/minestom/server/utils/ChunkUtils.java +++ b/src/main/java/net/minestom/server/utils/ChunkUtils.java @@ -1,5 +1,6 @@ package net.minestom.server.utils; +import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; public class ChunkUtils { @@ -12,6 +13,11 @@ public class ChunkUtils { return Math.floorDiv(xz, 16); } + public static int refreshChunkXZ(int xz) { + /// suppose CHUNK_SIZE_X == CHUNK_SIZE_Z + return xz = xz < 0 ? Chunk.CHUNK_SIZE_X + xz : xz; + } + public static long getChunkIndex(int chunkX, int chunkZ) { return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL); } diff --git a/src/main/java/net/minestom/server/utils/SerializerUtils.java b/src/main/java/net/minestom/server/utils/SerializerUtils.java index d86ca78f0..dd72b96be 100644 --- a/src/main/java/net/minestom/server/utils/SerializerUtils.java +++ b/src/main/java/net/minestom/server/utils/SerializerUtils.java @@ -18,7 +18,7 @@ public class SerializerUtils { ((value[3] & 0xFF) << 0); } - public static int chunkCoordToIndex(byte x, byte y, byte z) { + public static int chunkCoordToIndex(int x, int y, int z) { short index = (short) (x & 0x000F); index |= (y << 4) & 0x0FF0; index |= (z << 12) & 0xF000;