WIP chunk storage rework (use an array instead of a map) hope that this is working

This commit is contained in:
Felix Cravic 2020-04-26 16:08:04 +02:00
parent 18019277e6
commit 005d95e97b
11 changed files with 133 additions and 100 deletions

View File

@ -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;

View File

@ -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,19 +253,27 @@ 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);
short blockId = getBlockId(x, y, z);
short customBlockId = getCustomBlockId(x, y, z);
boolean isCustomBlock = customBlockId != 0;
short id = isCustomBlock ? customBlockId : blockId;
if (id == 0)
continue;
Data data = blocksData.get(index);
boolean hasData = data != null;
dos.writeInt(index); // Chunk coord
// Chunk coord
dos.writeInt(x);
dos.writeInt(y);
dos.writeInt(z);
dos.writeBoolean(isCustomBlock); // Determine the type of the ID
dos.writeShort(id);
@ -280,7 +283,8 @@ public class Chunk implements Viewable {
dos.writeInt(d.length);
dos.write(d);
}
}
}
}
byte[] result = output.toByteArray();

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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;