WIP support for infinite chunk height

This commit is contained in:
TheMode 2021-06-05 07:56:16 +02:00
parent 31d2f3488b
commit 6b5125bcff
6 changed files with 64 additions and 70 deletions

View File

@ -15,7 +15,6 @@ import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryReader;
@ -23,7 +22,6 @@ import net.minestom.server.utils.chunk.ChunkCallback;
import net.minestom.server.utils.chunk.ChunkSupplier;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.player.PlayerUtils;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biomes.BiomeManager;
import org.jetbrains.annotations.NotNull;
@ -54,12 +52,9 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
protected static final BiomeManager BIOME_MANAGER = MinecraftServer.getBiomeManager();
public static final int CHUNK_SIZE_X = 16;
public static final int CHUNK_SIZE_Y = 256;
public static final int CHUNK_SIZE_Z = 16;
public static final int CHUNK_SECTION_SIZE = 16;
public static final int CHUNK_SECTION_COUNT = CHUNK_SIZE_Y / CHUNK_SECTION_SIZE;
public static final int BIOME_COUNT = 1024; // 4x4x4 blocks group
private final UUID identifier;
@ -394,10 +389,10 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
UpdateLightPacket updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
updateLightPacket.chunkX = getChunkX();
updateLightPacket.chunkZ = getChunkZ();
updateLightPacket.skyLightMask = 0b111111111111111111;
updateLightPacket.emptySkyLightMask = 0b000000000000000000;
updateLightPacket.blockLightMask = 0b000000000000000000;
updateLightPacket.emptyBlockLightMask = 0b111111111111111111;
updateLightPacket.skyLightMask = 0b111111111111111111;
updateLightPacket.emptySkyLightMask = 0b000000000000000000;
updateLightPacket.blockLightMask = 0b000000000000000000;
updateLightPacket.emptyBlockLightMask = 0b111111111111111111;
byte[] bytes = new byte[2048];
Arrays.fill(bytes, (byte) 0xFF);
final List<byte[]> temp = new ArrayList<>(18);
@ -538,9 +533,6 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
public synchronized void sendChunkSectionUpdate(int section, @NotNull Player player) {
if (!PlayerUtils.isNettyClient(player))
return;
Check.argCondition(!MathUtils.isBetween(section, 0, CHUNK_SECTION_COUNT),
"The chunk section " + section + " does not exist");
player.getPlayerConnection().sendPacket(createChunkSectionUpdatePacket(section));
}
@ -553,9 +545,7 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
@NotNull
protected ChunkDataPacket createChunkSectionUpdatePacket(int section) {
ChunkDataPacket chunkDataPacket = createChunkPacket();
int[] sections = new int[CHUNK_SECTION_COUNT];
sections[section] = 1;
chunkDataPacket.sections = sections;
// TODO
return chunkDataPacket;
}

View File

@ -249,7 +249,7 @@ public class DynamicChunk extends Chunk {
// Loop all blocks
for (byte x = 0; x < CHUNK_SIZE_X; x++) {
for (short y = 0; y < CHUNK_SIZE_Y; y++) {
for (short y = 0; y < 256; y++) { // TODO increase max size
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
final int index = getBlockIndex(x, y, z);

View File

@ -258,10 +258,7 @@ public class ChunkBatch implements Batch<ChunkCallback> {
private void updateChunk(@NotNull Instance instance, Chunk chunk, IntSet updatedSections, @Nullable ChunkCallback callback, boolean safeCallback) {
// Refresh chunk for viewers
ChunkDataPacket chunkDataPacket = chunk.createChunkPacket();
int[] sections = new int[Chunk.CHUNK_SECTION_COUNT];
for (int section : updatedSections)
sections[section] = 1;
chunkDataPacket.sections = sections;
// TODO update all sections from `updatedSections`
PacketUtils.sendGroupedPacket(chunk.getViewers(), chunkDataPacket);
if (instance instanceof InstanceContainer) {

View File

@ -1,16 +1,16 @@
package net.minestom.server.instance.palette;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.clone.CloneUtils;
import net.minestom.server.utils.clone.PublicCloneable;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import static net.minestom.server.instance.Chunk.CHUNK_SECTION_COUNT;
import static net.minestom.server.instance.Chunk.CHUNK_SECTION_SIZE;
/**
@ -21,7 +21,7 @@ import static net.minestom.server.instance.Chunk.CHUNK_SECTION_SIZE;
*/
public class PaletteStorage implements PublicCloneable<PaletteStorage> {
private Section[] sections = new Section[CHUNK_SECTION_COUNT];
private Int2ObjectRBTreeMap<Section> sectionMap = new Int2ObjectRBTreeMap<>();
private final int defaultBitsPerEntry;
private final int defaultBitsIncrement;
@ -40,28 +40,21 @@ public class PaletteStorage implements PublicCloneable<PaletteStorage> {
}
public void setBlockAt(int x, int y, int z, short blockId) {
if (!MathUtils.isBetween(y, 0, Chunk.CHUNK_SIZE_Y - 1)) {
return;
}
final int sectionIndex = ChunkUtils.getSectionAt(y);
x = toChunkCoordinate(x);
z = toChunkCoordinate(z);
Section section = sections[sectionIndex];
Section section = getSection(sectionIndex);
if (section == null) {
section = new Section(defaultBitsPerEntry, defaultBitsIncrement);
sections[sectionIndex] = section;
setSection(sectionIndex, section);
}
section.setBlockAt(x, y, z, blockId);
}
public short getBlockAt(int x, int y, int z) {
if (y < 0 || y >= Chunk.CHUNK_SIZE_Y) {
return 0;
}
final int sectionIndex = ChunkUtils.getSectionAt(y);
final Section section = sections[sectionIndex];
final Section section = getSection(sectionIndex);
if (section == null) {
return Block.AIR.getBlockId();
}
@ -71,8 +64,16 @@ public class PaletteStorage implements PublicCloneable<PaletteStorage> {
return section.getBlockAt(x, y, z);
}
public Section[] getSections() {
return sections;
public Int2ObjectRBTreeMap<Section> getSectionMap() {
return sectionMap;
}
public @Nullable Collection<Section> getSections() {
return sectionMap.values();
}
public @Nullable Section getSection(int section) {
return sectionMap.get(section);
}
/**
@ -82,18 +83,14 @@ public class PaletteStorage implements PublicCloneable<PaletteStorage> {
* is composed of almost-empty sections since the loop will not stop until a non-air block is discovered.
*/
public synchronized void clean() {
for (Section section : sections) {
section.clean();
}
getSections().forEach(Section::clean);
}
/**
* Clears all the data in the palette and data array.
*/
public void clear() {
for (Section section : sections) {
section.clear();
}
getSections().forEach(Section::clear);
}
@NotNull
@ -101,7 +98,8 @@ public class PaletteStorage implements PublicCloneable<PaletteStorage> {
public PaletteStorage clone() {
try {
PaletteStorage paletteStorage = (PaletteStorage) super.clone();
paletteStorage.sections = CloneUtils.cloneArray(sections, Section[]::new);
// TODO deep clone
paletteStorage.sectionMap = sectionMap.clone();//CloneUtils.cloneArray(sections, Section[]::new);
return paletteStorage;
} catch (CloneNotSupportedException e) {
MinecraftServer.getExceptionManager().handleException(e);
@ -109,6 +107,10 @@ public class PaletteStorage implements PublicCloneable<PaletteStorage> {
}
}
private void setSection(int sectionIndex, Section section) {
this.sectionMap.put(sectionIndex, section);
}
/**
* Converts a world coordinate to a chunk one.
*
@ -123,5 +125,4 @@ public class PaletteStorage implements PublicCloneable<PaletteStorage> {
return xz;
}
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.network.packet.server.play;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2LongRBTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
@ -44,8 +45,6 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
public IntSet blockEntities;
public Int2ObjectMap<Data> blocksData;
public int[] sections = new int[0];
private static final byte CHUNK_SECTION_COUNT = 16;
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;
@ -79,26 +78,26 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
writer.writeInt(chunkX);
writer.writeInt(chunkZ);
int mask = 0;
ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
final boolean fullChunk = sections.length == 0;
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
final Section section = paletteStorage.getSections()[i];
if (section == null) {
// Section not loaded
continue;
}
if (section.getBlocks().length > 0) { // section contains at least one block
mask |= 1 << i;
Utils.writeSectionBlocks(blocks, section);
}
}
Int2LongRBTreeMap maskMap = new Int2LongRBTreeMap();
for (var entry : paletteStorage.getSectionMap().int2ObjectEntrySet()) {
final int index = entry.getIntKey();
final Section section = entry.getValue();
final int lengthIndex = index % 64;
final int maskIndex = index / (16 + 1);
long mask = maskMap.get(maskIndex);
mask |= 1L << lengthIndex;
maskMap.put(maskIndex, mask);
Utils.writeSectionBlocks(blocks, section);
}
// TODO variable size
writer.writeVarInt(1);
writer.writeLong(mask);
writer.writeVarInt(maskMap.size());
maskMap.values().forEach(writer::writeLong);
// TODO: don't hardcode heightmaps
// Heightmap
@ -168,7 +167,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
int maskCount = reader.readVarInt();
long[] masks = new long[maskCount];
for(int i=0;i<maskCount;i++){
for (int i = 0; i < maskCount; i++) {
masks[i] = reader.readLong();
}
try {
@ -195,8 +194,8 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
byte bitsPerEntry = reader.readByte();
// Resize palette if necessary
if (bitsPerEntry > paletteStorage.getSections()[section].getBitsPerEntry()) {
paletteStorage.getSections()[section].resize(bitsPerEntry);
if (bitsPerEntry > paletteStorage.getSection(section).getBitsPerEntry()) {
paletteStorage.getSection(section).resize(bitsPerEntry);
}
// Retrieve palette values
@ -204,14 +203,14 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
int paletteSize = reader.readVarInt();
for (int i = 0; i < paletteSize; i++) {
final int paletteValue = reader.readVarInt();
paletteStorage.getSections()[section].getPaletteBlockMap().put((short) i, (short) paletteValue);
paletteStorage.getSections()[section].getBlockPaletteMap().put((short) paletteValue, (short) i);
paletteStorage.getSection(section).getPaletteBlockMap().put((short) i, (short) paletteValue);
paletteStorage.getSection(section).getBlockPaletteMap().put((short) paletteValue, (short) i);
}
}
// Read blocks
int dataLength = reader.readVarInt();
long[] data = paletteStorage.getSections()[section].getBlocks();
long[] data = paletteStorage.getSection(section).getBlocks();
for (int i = 0; i < dataLength; i++) {
data[i] = reader.readLong();
}

View File

@ -22,6 +22,13 @@ public class ChunkGeneratorDemo implements ChunkGenerator {
batch.setBlock(x, y, z, Block.STONE);
}
}
for (short x = 0; x < Chunk.CHUNK_SIZE_X; x++)
for (short z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
for (short y = 300; y < 500; y++) {
batch.setBlock(x, y, z, Block.STONE);
}
}
}
@Override