package org.geysermc.connector.world.chunk; import com.nukkitx.network.VarInts; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.connector.world.chunk.bitarray.BitArray; import org.geysermc.connector.world.chunk.bitarray.BitArrayVersion; import java.util.function.IntConsumer; /** * Adapted from NukkitX: https://github.com/NukkitX/Nukkit */ public class BlockStorage { private static final int SIZE = 4096; private final IntList palette; private BitArray bitArray; public BlockStorage() { this(BitArrayVersion.V2); } public BlockStorage(BitArrayVersion version) { this.bitArray = version.createPalette(SIZE); this.palette = new IntArrayList(16); this.palette.add(0); // Air is at the start of every palette. } private BlockStorage(BitArray bitArray, IntArrayList palette) { this.palette = palette; this.bitArray = bitArray; } private static int getPaletteHeader(BitArrayVersion version, boolean runtime) { return (version.getId() << 1) | (runtime ? 1 : 0); } private static BitArrayVersion getVersionFromHeader(byte header) { return BitArrayVersion.get(header >> 1, true); } public synchronized int getFullBlock(int index) { return this.palette.getInt(this.bitArray.get(index)); } public synchronized void setFullBlock(int index, int runtimeId) { int idx = this.idFor(runtimeId); this.bitArray.set(index, idx); } public synchronized void writeToNetwork(ByteBuf buffer) { buffer.writeByte(getPaletteHeader(bitArray.getVersion(), true)); for (int word : bitArray.getWords()) { buffer.writeIntLE(word); } VarInts.writeInt(buffer, palette.size()); palette.forEach((IntConsumer) id -> VarInts.writeInt(buffer, id)); } private void onResize(BitArrayVersion version) { BitArray newBitArray = version.createPalette(SIZE); for (int i = 0; i < SIZE; i++) { newBitArray.set(i, this.bitArray.get(i)); } this.bitArray = newBitArray; } private int idFor(int runtimeId) { int index = this.palette.indexOf(runtimeId); if (index != -1) { return index; } index = this.palette.size(); this.palette.add(runtimeId); BitArrayVersion version = this.bitArray.getVersion(); if (index > version.getMaxEntryValue()) { BitArrayVersion next = version.next(); if (next != null) { this.onResize(next); } } return index; } public boolean isEmpty() { if (this.palette.size() == 1) { return true; } for (int word : this.bitArray.getWords()) { if (Integer.toUnsignedLong(word) != 0L) { return false; } } return true; } public BlockStorage copy() { return new BlockStorage(this.bitArray.copy(), new IntArrayList(this.palette)); } }