WIP PaletteStorage (only works with Bits per entry of 15)

This commit is contained in:
themode 2020-11-11 06:06:28 +01:00
parent b6bf6a17ba
commit 4dfe01ea2f
4 changed files with 169 additions and 108 deletions

View File

@ -10,6 +10,7 @@ import net.minestom.server.data.SerializableData;
import net.minestom.server.data.SerializableDataImpl;
import net.minestom.server.entity.pathfinding.PFBlockDescription;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.instance.palette.PaletteStorage;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.BlockPosition;
@ -35,8 +36,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
*/
public class DynamicChunk extends Chunk {
private static final int BITS_PER_ENTRY = 15;
/**
* Represents the version which will be present in the serialized output.
* Used to define which deserializer to use.
@ -47,10 +46,9 @@ public class DynamicChunk extends Chunk {
// WARNING: those arrays are NOT thread-safe
// and modifying them can cause issue with block data, update, block entity and the cached chunk packet
//protected final short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
protected PaletteStorage blockPalette = new PaletteStorage(15);
protected final short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
protected long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
// Used to get all blocks with data (no null)
// Key is still chunk coordinates (see #getBlockIndex)
protected final Int2ObjectMap<Data> blocksData = new Int2ObjectOpenHashMap<>();
@ -63,8 +61,8 @@ public class DynamicChunk extends Chunk {
// Block entities
protected final Set<Integer> blockEntities = new CopyOnWriteArraySet<>();
public DynamicChunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ) {
super(instance, biomes, chunkX, chunkZ, true);
public DynamicChunk(@Nullable Biome[] biomes, int chunkX, int chunkZ) {
super(biomes, chunkX, chunkZ, true);
}
@Override
@ -83,12 +81,12 @@ public class DynamicChunk extends Chunk {
// True if the block is not complete air without any custom block capabilities
final boolean hasBlock = blockStateId != 0 || customBlockId != 0;
if (hasBlock) {
setBlockAt(x, y, z, blockStateId);
this.blockPalette.setBlockAt(x, y, z, blockStateId);
this.customBlocksId[index] = customBlockId;
} else {
// Block has been deleted, clear cache and return
setBlockAt(x, y, z, (short) 0);
this.blockPalette.setBlockAt(x, y, z, (short) 0);
//this.blocksStateId[index] = 0; // Set to air
this.customBlocksId[index] = 0; // Remove custom block
@ -155,8 +153,7 @@ public class DynamicChunk extends Chunk {
@Override
public short getBlockStateId(int x, int y, int z) {
final int index = getBlockIndex(x, y, z);
return getBlockAt(x, y, z);
return this.blockPalette.getBlockAt(x, y, z);
}
@Override
@ -168,14 +165,13 @@ public class DynamicChunk extends Chunk {
@Override
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customBlockId) {
final int blockIndex = getBlockIndex(x, y, z);
setBlockAt(x, y, z, blockStateId);
this.blockPalette.setBlockAt(x, y, z, blockStateId);
this.customBlocksId[blockIndex] = customBlockId;
}
@Override
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
final int blockIndex = getBlockIndex(x, y, z);
setBlockAt(x, y, z, blockStateId);
this.blockPalette.setBlockAt(x, y, z, blockStateId);
}
@Override
@ -246,7 +242,7 @@ public class DynamicChunk extends Chunk {
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
final int index = getBlockIndex(x, y, z);
final short blockStateId = getBlockAt(x, y, z);
final short blockStateId = blockPalette.getBlockAt(x, y, z);
final short customBlockId = customBlocksId[index];
// No block at the position
@ -384,8 +380,7 @@ public class DynamicChunk extends Chunk {
fullDataPacket.biomes = biomes.clone();
fullDataPacket.chunkX = chunkX;
fullDataPacket.chunkZ = chunkZ;
fullDataPacket.bitsPerEntry = BITS_PER_ENTRY;
fullDataPacket.sectionBlocks = sectionBlocks.clone();
fullDataPacket.paletteStorage = blockPalette.copy();
fullDataPacket.customBlocksId = customBlocksId.clone();
fullDataPacket.blockEntities = new HashSet<>(blockEntities);
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
@ -394,9 +389,9 @@ public class DynamicChunk extends Chunk {
@NotNull
@Override
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ);
dynamicChunk.sectionBlocks = sectionBlocks.clone();
public Chunk copy(int chunkX, int chunkZ) {
DynamicChunk dynamicChunk = new DynamicChunk(biomes.clone(), chunkX, chunkZ);
dynamicChunk.blockPalette = blockPalette.copy();
ArrayUtils.copyToDestination(customBlocksId, dynamicChunk.customBlocksId);
dynamicChunk.blocksData.putAll(blocksData);
dynamicChunk.updatableBlocks.addAll(updatableBlocks);
@ -405,82 +400,4 @@ public class DynamicChunk extends Chunk {
return dynamicChunk;
}
private void setBlockAt(int x, int y, int z, short blockId) {
x %= 16;
if (x < 0) {
x = CHUNK_SIZE_X + x;
}
z %= 16;
if (z < 0) {
z = CHUNK_SIZE_Z + z;
}
final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
int sectionY = y % CHUNK_SECTION_SIZE;
int sectionIndex = (((sectionY * 16) + z) * 16) + x;
final int index = sectionIndex / valuesPerLong;
final int bitIndex = sectionIndex % valuesPerLong * BITS_PER_ENTRY;
final int section = y / CHUNK_SECTION_SIZE;
if (sectionBlocks[section].length == 0) {
sectionBlocks[section] = new long[getSize()];
}
long[] sectionBlock = sectionBlocks[section];
//System.out.println("test1 " + binary(sectionBlock[index]));
//System.out.println("test2 " + binary(((long) blockId << (bitIndex))));
sectionBlock[index] |= ((long) blockId << (bitIndex));
}
private static String binary(long value) {
return "0b" + Long.toBinaryString(value);
}
private short getBlockAt(int x, int y, int z) {
x %= 16;
if (x < 0) {
x = CHUNK_SIZE_X + x;
}
z %= 16;
if (z < 0) {
z = CHUNK_SIZE_Z + z;
}
final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
int sectionY = y % CHUNK_SECTION_SIZE;
int sectionIndex = (((sectionY * 16) + z) * 16) + x;
final int index = sectionIndex / valuesPerLong;
final int bitIndex = sectionIndex % valuesPerLong * BITS_PER_ENTRY;
final int section = y / CHUNK_SECTION_SIZE;
long[] blocks = sectionBlocks[section];
if (blocks.length == 0) {
return 0;
}
long mask = (1 << (bitIndex)) - 1;
/*System.out.println("data " + index + " " + bitIndex + " " + sectionIndex);
System.out.println("POS " + x + " " + y + " " + z);
System.out.println("mask " + binary(mask));
System.out.println("bin " + binary(blocks[index]));
System.out.println("result " + ((blocks[index] >> bitIndex) & mask));*/
return (short) (blocks[index] >> bitIndex & mask);
}
private int getSize() {
final int blockCount = 16 * 16 * 16; // A whole chunk section
final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
final int arraySize = blockCount / valuesPerLong;
return arraySize;
}
}

View File

@ -0,0 +1,146 @@
package net.minestom.server.instance.palette;
import static net.minestom.server.instance.Chunk.*;
public class PaletteStorage {
private int bitsPerEntry;
private int valuesPerLong;
protected long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
public PaletteStorage(int bitsPerEntry) {
this.bitsPerEntry = bitsPerEntry;
this.valuesPerLong = Long.SIZE / bitsPerEntry;
}
public void setBlockAt(int x, int y, int z, short blockId) {
x %= 16;
if (x < 0) {
x = CHUNK_SIZE_X + x;
}
z %= 16;
if (z < 0) {
z = CHUNK_SIZE_Z + z;
}
int sectionY = y % CHUNK_SECTION_SIZE;
int sectionIndex = (((sectionY * 16) + z) * 16) + x;
final int index = sectionIndex / valuesPerLong;
final int bitIndex = (sectionIndex % valuesPerLong) * bitsPerEntry;
final int section = y / CHUNK_SECTION_SIZE;
if (sectionBlocks[section].length == 0) {
sectionBlocks[section] = new long[getSize()];
}
long[] sectionBlock = sectionBlocks[section];
long block = sectionBlock[index];
{
if (blockId != 0) {
long shiftCount = (long) bitIndex;
long cacheMask = (1L << shiftCount) - 1L;
long cache = block & cacheMask;
/*System.out.println("blockId "+blockId);
System.out.println("bitIndex "+bitIndex);
System.out.println("block "+binary(block));
System.out.println("mask "+binary(cacheMask));
System.out.println("cache "+binary(cache));*/
block = block >> shiftCount << shiftCount;
//System.out.println("block "+binary(block));
block = block | (long) blockId;
//System.out.println("block2 "+binary(block));
block = (block << shiftCount);
//System.out.println("block3 "+binary(block));
block = block | cache;
//System.out.println("block4 "+binary(block));
sectionBlock[index] = block;
}
}
}
public short getBlockAt(int x, int y, int z) {
x %= 16;
if (x < 0) {
x = CHUNK_SIZE_X + x;
}
z %= 16;
if (z < 0) {
z = CHUNK_SIZE_Z + z;
}
int sectionY = y % CHUNK_SECTION_SIZE;
int sectionIndex = (((sectionY * 16) + z) * 16) + x;
final int index = sectionIndex / valuesPerLong;
final int bitIndex = sectionIndex % valuesPerLong * bitsPerEntry;
final int section = y / CHUNK_SECTION_SIZE;
long[] blocks = sectionBlocks[section];
if (blocks.length == 0) {
return 0;
}
long value = blocks[index] >> bitIndex;
long mask = (1L << ((long) bitIndex + (long) bitsPerEntry)) - 1L;
long finalValue;
System.out.println("index " + index);
System.out.println("bitIndex " + bitIndex);
System.out.println("test1 " + binary(value));
System.out.println("test2 " + binary(mask));
{
mask = mask >> bitIndex << bitIndex;
System.out.println("test3 " + binary(mask));
finalValue = value & mask >> bitIndex;
}
System.out.println("final " + binary(finalValue));
/*System.out.println("data " + index + " " + bitIndex + " " + sectionIndex);
System.out.println("POS " + x + " " + y + " " + z);
System.out.println("mask " + binary(mask));
System.out.println("bin " + binary(blocks[index]));
System.out.println("result " + ((blocks[index] >> bitIndex) & mask));*/
return (short) (finalValue);
}
private int getSize() {
final int blockCount = 16 * 16 * 16; // A whole chunk section
final int arraySize = (blockCount + valuesPerLong - 1) / valuesPerLong;
//System.out.println("size " + arraySize);
return arraySize;
}
public int getBitsPerEntry() {
return bitsPerEntry;
}
public long[][] getSectionBlocks() {
return sectionBlocks;
}
public PaletteStorage copy() {
PaletteStorage paletteStorage = new PaletteStorage(bitsPerEntry);
paletteStorage.sectionBlocks = sectionBlocks.clone();
return paletteStorage;
}
private static String binary(long value) {
return "0b" + Long.toBinaryString(value);
}
}

View File

@ -7,6 +7,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.data.Data;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.instance.palette.PaletteStorage;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.BlockPosition;
@ -27,8 +28,7 @@ public class ChunkDataPacket implements ServerPacket {
public Biome[] biomes;
public int chunkX, chunkZ;
public int bitsPerEntry;
public long[][] sectionBlocks;
public PaletteStorage paletteStorage;
public short[] customBlocksId;
public Set<Integer> blockEntities;
@ -37,7 +37,7 @@ public class ChunkDataPacket implements ServerPacket {
public int[] sections;
private static final byte CHUNK_SECTION_COUNT = 16;
private static final int MAX_BITS_PER_ENTRY = 15;
private static final int MAX_BITS_PER_ENTRY = 16;
private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * MAX_BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
@Override
@ -50,11 +50,11 @@ public class ChunkDataPacket implements ServerPacket {
ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
final long[] section = sectionBlocks[i];
final long[] section = paletteStorage.getSectionBlocks()[i];
if (section.length > 0) { // section contains at least one block
//if (true) {
mask |= 1 << i;
Utils.writeBlocks(blocks, section, bitsPerEntry);
Utils.writeBlocks(blocks, section, paletteStorage.getBitsPerEntry());
} else {
mask |= 0;
}

View File

@ -23,8 +23,6 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.ping.ResponseDataConsumer;
import net.minestom.server.storage.StorageLocation;
import net.minestom.server.storage.StorageOptions;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
import net.minestom.server.utils.time.TimeUnit;
@ -39,12 +37,12 @@ public class PlayerInit {
private static final Inventory inventory;
static {
StorageLocation storageLocation = MinecraftServer.getStorageManager().getLocation("instance_data", new StorageOptions().setCompression(true));
//StorageLocation storageLocation = MinecraftServer.getStorageManager().getLocation("instance_data", new StorageOptions().setCompression(true));
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
NoiseTestGenerator noiseTestGenerator = new NoiseTestGenerator();
instanceContainer = MinecraftServer.getInstanceManager().createInstanceContainer(DimensionType.OVERWORLD, storageLocation);
instanceContainer = MinecraftServer.getInstanceManager().createInstanceContainer(DimensionType.OVERWORLD);
instanceContainer.enableAutoChunkLoad(true);
instanceContainer.setChunkGenerator(noiseTestGenerator);
instanceContainer.setChunkGenerator(chunkGeneratorDemo);
// Load some chunks beforehand
final int loopStart = -3;
@ -174,7 +172,7 @@ public class PlayerInit {
ItemStack itemStack = new ItemStack(Material.DIAMOND_PICKAXE, (byte) 64);
NbtDataImpl data = new NbtDataImpl();
data.set("testc",2);
data.set("testc", 2);
itemStack.setData(data);
player.getInventory().addItemStack(itemStack);