Optimize ChunkDataPacket, and having an universal block index method

This commit is contained in:
Felix Cravic 2020-05-27 16:25:05 +02:00
parent 9bf193988f
commit c363b715ca
7 changed files with 97 additions and 87 deletions

View File

@ -18,7 +18,7 @@ import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.SerializerUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.time.CooldownUtils;
import net.minestom.server.utils.time.UpdateOption;
import net.minestom.server.utils.validate.Check;
@ -91,8 +91,8 @@ public class Chunk implements Viewable {
}
public void UNSAFE_removeCustomBlock(int x, int y, int z) {
this.customBlocksId[getBlockIndex(x, y, z)] = 0; // Set to none
int index = SerializerUtils.coordToChunkIndex(x, y, z);
int index = getBlockIndex(x, y, z);
this.customBlocksId[index] = 0; // Set to none
this.blocksData.remove(index);
this.updatableBlocks.remove(index);
@ -102,15 +102,15 @@ public class Chunk implements Viewable {
}
private void setBlock(int x, int y, int z, short blockId, short customId, Data data, UpdateConsumer updateConsumer) {
int index = SerializerUtils.coordToChunkIndex(x, y, z);
int index = getBlockIndex(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(x, y, z, blockId, customId);
this.blocksId[index] = blockId;
this.customBlocksId[index] = customId;
} else {
// Block has been deleted, clear cache and return
this.blocksId[getBlockIndex(x, y, z)] = 0; // Set to air
//this.blocks.remove(index);
this.blocksId[index] = 0; // Set to air
this.blocksData.remove(index);
@ -149,7 +149,7 @@ public class Chunk implements Viewable {
}
public void setBlockData(int x, int y, int z, Data data) {
int index = SerializerUtils.coordToChunkIndex(x, y, z);
int index = getBlockIndex(x, y, z);
if (data != null) {
this.blocksData.put(index, data);
} else {
@ -185,7 +185,7 @@ public class Chunk implements Viewable {
}
protected CustomBlock getCustomBlock(int index) {
int[] pos = SerializerUtils.indexToChunkPosition(index);
int[] pos = ChunkUtils.indexToChunkPosition(index);
return getCustomBlock(pos[0], pos[1], pos[2]);
}
@ -215,7 +215,7 @@ public class Chunk implements Viewable {
}
public Data getData(int x, byte y, int z) {
int index = SerializerUtils.coordToChunkIndex(x, y, z);
int index = getBlockIndex(x, y, z);
return getData(index);
}
@ -243,12 +243,12 @@ public class Chunk implements Viewable {
this.updatableBlocksLastUpdate.put(index, time); // Refresh last update time
int[] blockPos = SerializerUtils.indexToChunkPosition(index);
int[] blockPos = ChunkUtils.indexToPosition(index, chunkX, chunkZ);
int x = blockPos[0];
int y = blockPos[1];
int z = blockPos[2];
BlockPosition blockPosition = new BlockPosition(x + 16 * chunkX, y, z + 16 * chunkZ);
BlockPosition blockPosition = new BlockPosition(x, y, z);
Data data = getData(index);
customBlock.update(instance, blockPosition, data);
}
@ -296,10 +296,10 @@ public class Chunk implements Viewable {
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.coordToChunkIndex(x, y, z);
int index = getBlockIndex(x, y, z);
short blockId = getBlockId(x, y, z);
short customBlockId = getCustomBlockId(x, y, z);
short blockId = blocksId[index];
short customBlockId = customBlocksId[index];
if (blockId == 0 && customBlockId == 0)
continue;
@ -334,15 +334,27 @@ public class Chunk implements Viewable {
public ChunkDataPacket getFreshFullDataPacket() {
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
fullDataPacket.chunk = this;
fullDataPacket.fullChunk = true;
fullDataPacket.biomes = biomes.clone();
fullDataPacket.chunkX = chunkX;
fullDataPacket.chunkZ = chunkZ;
fullDataPacket.blocksId = blocksId.clone();
fullDataPacket.customBlocksId = customBlocksId.clone();
fullDataPacket.blockEntities = new CopyOnWriteArraySet<>(blockEntities);
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
return fullDataPacket;
}
public ChunkDataPacket getFreshPartialDataPacket() {
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
fullDataPacket.chunk = this;
fullDataPacket.fullChunk = false;
fullDataPacket.biomes = biomes.clone();
fullDataPacket.chunkX = chunkX;
fullDataPacket.chunkZ = chunkZ;
fullDataPacket.blocksId = blocksId.clone();
fullDataPacket.customBlocksId = customBlocksId.clone();
fullDataPacket.blockEntities = new CopyOnWriteArraySet<>(blockEntities);
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
return fullDataPacket;
}
@ -408,13 +420,6 @@ public class Chunk implements Viewable {
}
private int getBlockIndex(int x, int y, int z) {
x = x % Chunk.CHUNK_SIZE_X;
z = z % Chunk.CHUNK_SIZE_Z;
x = x < 0 ? Chunk.CHUNK_SIZE_X + x : x;
z = z < 0 ? Chunk.CHUNK_SIZE_Z + z : z;
int index = (((y * 16) + x) * 16) + z;
return index;
return ChunkUtils.getBlockIndex(x, y, z);
}
}

View File

@ -139,9 +139,8 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
}
protected ChunkDataPacket getChunkSectionUpdatePacket(Chunk chunk, int section) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
ChunkDataPacket chunkDataPacket = chunk.getFreshPartialDataPacket();
chunkDataPacket.fullChunk = false;
chunkDataPacket.chunk = chunk;
int[] sections = new int[16];
sections[section] = 1;
chunkDataPacket.sections = sections;

View File

@ -20,7 +20,6 @@ import net.minestom.server.storage.StorageFolder;
import net.minestom.server.timer.TaskRunnable;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.SerializerUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.player.PlayerUtils;
import net.minestom.server.utils.time.TimeUnit;
@ -80,8 +79,6 @@ public class InstanceContainer extends Instance {
boolean isCustomBlock = customBlock != null;
int index = SerializerUtils.coordToChunkIndex(x, y, z);
BlockPosition blockPosition = new BlockPosition(x, y, z);
if (isAlreadyChanged(blockPosition, blockId)) { // do NOT change the block again.
@ -91,6 +88,8 @@ public class InstanceContainer extends Instance {
}
setAlreadyChanged(blockPosition, blockId);
int index = ChunkUtils.getBlockIndex(x, y, z);
// Call the destroy listener if previous block was a custom block
callBlockDestroy(chunk, index, blockPosition);

View File

@ -1,17 +1,20 @@
package net.minestom.server.network.packet.server.play;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
import net.minestom.server.instance.Biome;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.PacketWriter;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.SerializerUtils;
import net.minestom.server.utils.Utils;
import net.minestom.server.utils.buffer.BufferUtils;
import net.minestom.server.utils.buffer.BufferWrapper;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.DoubleTag;
import net.querz.nbt.LongArrayTag;
@ -23,8 +26,19 @@ import java.util.Set;
public class ChunkDataPacket implements ServerPacket {
private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
public boolean fullChunk;
public Chunk chunk;
public Biome[] biomes;
public int chunkX, chunkZ;
public short[] blocksId;
public short[] customBlocksId;
public Set<Integer> blockEntities;
public Int2ObjectMap<Data> blocksData;
//public Chunk chunk;
public int[] sections;
private static final byte CHUNK_SECTION_COUNT = 16;
@ -33,15 +47,15 @@ public class ChunkDataPacket implements ServerPacket {
@Override
public void write(PacketWriter writer) {
writer.writeInt(chunk.getChunkX());
writer.writeInt(chunk.getChunkZ());
writer.writeInt(chunkX);
writer.writeInt(chunkZ);
writer.writeBoolean(fullChunk);
int mask = 0;
BufferWrapper blocks = BufferUtils.getBuffer(MAX_BUFFER_SIZE);
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
short[] section = getSection(chunk, i);
short[] section = getSection(i);
if (section != null) { // section contains at least one block
mask |= 1 << i;
Utils.writeBlocks(blocks, section, BITS_PER_ENTRY);
@ -81,7 +95,6 @@ public class ChunkDataPacket implements ServerPacket {
// Biome data
if (fullChunk) {
Biome[] biomes = chunk.getBiomes();
for (int i = 0; i < biomes.length; i++) {
writer.writeInt(biomes[i].getId());
}
@ -92,18 +105,18 @@ public class ChunkDataPacket implements ServerPacket {
writer.writeBufferAndFree(blocks);
// Block entities
Set<Integer> blockEntities = chunk.getBlockEntities();
writer.writeVarInt(blockEntities.size());
for (Integer index : blockEntities) {
BlockPosition blockPosition = SerializerUtils.indexToChunkBlockPosition(index);
for (int index : blockEntities) {
BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ);
CompoundTag blockEntity = new CompoundTag();
blockEntity.put("x", new DoubleTag(blockPosition.getX() + 16 * chunk.getChunkX()));
blockEntity.put("x", new DoubleTag(blockPosition.getX()));
blockEntity.put("y", new DoubleTag(blockPosition.getY()));
blockEntity.put("z", new DoubleTag(blockPosition.getZ() + 16 * chunk.getChunkZ()));
CustomBlock customBlock = chunk.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
blockEntity.put("z", new DoubleTag(blockPosition.getZ()));
short customBlockId = customBlocksId[index];
CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId);
if (customBlock != null) {
Data data = chunk.getData(blockPosition.getX(), (byte) blockPosition.getY(), blockPosition.getZ());
Data data = blocksData.get(index);
customBlock.writeBlockEntity(blockPosition, data, blockEntity);
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
@ -117,18 +130,20 @@ public class ChunkDataPacket implements ServerPacket {
}
}
private short[] getSection(Chunk chunk, byte section) {
private short[] getSection(byte section) {
short[] blocks = new short[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
boolean empty = true;
for (byte y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
for (byte x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
for (byte z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
short blockId = chunk.getBlockId(x, (y + Chunk.CHUNK_SECTION_SIZE * section), z);
int yPos = (y + Chunk.CHUNK_SECTION_SIZE * section);
int index = ChunkUtils.getBlockIndex(x, yPos, z);
short blockId = blocksId[index];
if (blockId != 0)
empty = false;
int index = (((y * 16) + x) * 16) + z;
blocks[index] = blockId;
int packetIndex = (((y * 16) + x) * 16) + z;
blocks[packetIndex] = blockId;
}
}
}

View File

@ -1,46 +1,7 @@
package net.minestom.server.utils;
import net.minestom.server.instance.Chunk;
public class SerializerUtils {
public static byte[] intToBytes(int value) {
byte[] result = new byte[4];
result[0] = (byte) (value >> 24);
result[1] = (byte) (value >> 16);
result[2] = (byte) (value >> 8);
result[3] = (byte) (value >> 0);
return result;
}
public static int bytesToInt(byte[] value) {
return ((value[0] & 0xFF) << 24) |
((value[1] & 0xFF) << 16) |
((value[2] & 0xFF) << 8) |
((value[3] & 0xFF) << 0);
}
public static int coordToChunkIndex(int x, int y, int z) {
x = x % Chunk.CHUNK_SIZE_X;
z = z % Chunk.CHUNK_SIZE_Z;
short index = (short) (x & 0x000F);
index |= (y << 4) & 0x0FF0;
index |= (z << 12) & 0xF000;
return index & 0xffff;
}
public static int[] indexToChunkPosition(int index) {
int z = (byte) (index >> 12 & 0xF);
int y = (index >>> 4 & 0xFF);
int x = (byte) (index >> 0 & 0xF);
return new int[]{x, y, z};
}
public static BlockPosition indexToChunkBlockPosition(int index) {
int[] pos = indexToChunkPosition(index);
return new BlockPosition(pos[0], pos[1], pos[2]);
}
public static long positionToLong(int x, int y, int z) {
return (((long) x & 0x3FFFFFF) << 38) | (((long) z & 0x3FFFFFF) << 12) | ((long) y & 0xFFF);
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.utils.chunk;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.Position;
@ -83,4 +84,34 @@ public class ChunkUtils {
return visibleChunks;
}
public static int getBlockIndex(int x, int y, int z) {
x = x % Chunk.CHUNK_SIZE_X;
z = z % Chunk.CHUNK_SIZE_Z;
short index = (short) (x & 0x000F);
index |= (y << 4) & 0x0FF0;
index |= (z << 12) & 0xF000;
return index & 0xffff;
}
public static BlockPosition getBlockPosition(int index, int chunkX, int chunkZ) {
int[] pos = indexToPosition(index, chunkX, chunkZ);
return new BlockPosition(pos[0], pos[1], pos[2]);
}
public static int[] indexToPosition(int index, int chunkX, int chunkZ) {
int z = (byte) (index >> 12 & 0xF);
int y = (index >>> 4 & 0xFF);
int x = (byte) (index >> 0 & 0xF);
x += 16 * chunkX;
z += 16 * chunkZ;
return new int[]{x, y, z};
}
public static int[] indexToChunkPosition(int index) {
return indexToPosition(index, 0, 0);
}
}

View File

@ -15,7 +15,7 @@ public class NbtReaderUtils {
byte typeId = reader.readByte();
System.out.println("DEBUG TYPE: " + typeId);
//System.out.println("DEBUG TYPE: " + typeId);
switch (typeId) {
case 0x00: // TAG_End
// End of item NBT