Mirror von
https://github.com/ViaVersion/ViaVersion.git
synchronisiert 2024-12-27 08:30:09 +01:00
Use types for ChunkSections
Dieser Commit ist enthalten in:
Ursprung
f4b6b1795e
Commit
14377ca354
@ -1,23 +1,23 @@
|
|||||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface Chunk {
|
@Data
|
||||||
int getX();
|
@AllArgsConstructor
|
||||||
|
public class Chunk {
|
||||||
|
protected int x;
|
||||||
|
protected int z;
|
||||||
|
protected boolean groundUp;
|
||||||
|
protected int bitmask;
|
||||||
|
protected ChunkSection[] sections;
|
||||||
|
protected byte[] biomeData;
|
||||||
|
protected List<CompoundTag> blockEntities;
|
||||||
|
|
||||||
int getZ();
|
public boolean isBiomeData() {
|
||||||
|
return biomeData != null;
|
||||||
ChunkSection[] getSections();
|
}
|
||||||
|
|
||||||
boolean isGroundUp();
|
|
||||||
|
|
||||||
boolean isBiomeData();
|
|
||||||
|
|
||||||
byte[] getBiomeData();
|
|
||||||
|
|
||||||
int getBitmask();
|
|
||||||
|
|
||||||
List<CompoundTag> getBlockEntities();
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,32 @@
|
|||||||
package us.myles.ViaVersion.api.minecraft.chunks;
|
package us.myles.ViaVersion.api.minecraft.chunks;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ChunkSection {
|
public class ChunkSection {
|
||||||
/**
|
/**
|
||||||
* Gets a block state id (< 1.13: block_id << 4 | data & 0xF)
|
* Size (dimensions) of blocks in a chunks section.
|
||||||
*
|
|
||||||
* @param x Block X
|
|
||||||
* @param y Block Y
|
|
||||||
* @param z Block Z
|
|
||||||
* @return Block raw id
|
|
||||||
*/
|
*/
|
||||||
int getBlock(int x, int y, int z);
|
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
||||||
|
/**
|
||||||
|
* Length of the sky and block light nibble arrays.
|
||||||
|
*/
|
||||||
|
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
||||||
|
/**
|
||||||
|
* Length of the block data array.
|
||||||
|
*/
|
||||||
|
private final List<Integer> palette = Lists.newArrayList();
|
||||||
|
private final int[] blocks;
|
||||||
|
private NibbleArray blockLight;
|
||||||
|
private NibbleArray skyLight;
|
||||||
|
|
||||||
|
public ChunkSection() {
|
||||||
|
this.blocks = new int[SIZE];
|
||||||
|
this.blockLight = new NibbleArray(SIZE);
|
||||||
|
palette.add(0); // AIR
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a block in the chunks
|
* Set a block in the chunks
|
||||||
@ -21,53 +34,149 @@ public interface ChunkSection {
|
|||||||
* @param x Block X
|
* @param x Block X
|
||||||
* @param y Block Y
|
* @param y Block Y
|
||||||
* @param z Block Z
|
* @param z Block Z
|
||||||
* @param type The block id
|
* @param type The type of the block
|
||||||
* @param data The data value of the block
|
* @param data The data value of the block
|
||||||
*/
|
*/
|
||||||
void setBlock(int x, int y, int z, int type, int data);
|
public void setBlock(int x, int y, int z, int type, int data) {
|
||||||
|
setFlatBlock(index(x, y, z), type << 4 | (data & 0xF));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlatBlock(int x, int y, int z, int type) {
|
||||||
|
setFlatBlock(index(x, y, z), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlockId(int x, int y, int z) {
|
||||||
|
return getFlatBlock(x, y, z) >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBlockData(int x, int y, int z) {
|
||||||
|
return getFlatBlock(x, y, z) & 0xF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFlatBlock(int x, int y, int z) {
|
||||||
|
int index = blocks[index(x, y, z)];
|
||||||
|
return palette.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFlatBlock(int idx) {
|
||||||
|
int index = blocks[idx];
|
||||||
|
return palette.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlock(int idx, int type, int data) {
|
||||||
|
setFlatBlock(idx, type << 4 | (data & 0xF));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaletteIndex(int idx, int index) {
|
||||||
|
blocks[idx] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPaletteIndex(int idx) {
|
||||||
|
return blocks[idx];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a block state in the chunk
|
* Set a block in the chunks based on the index
|
||||||
*
|
*
|
||||||
* @param x Block X
|
* @param idx Index
|
||||||
* @param y Block Y
|
* @param id The raw or flat id of the block
|
||||||
* @param z Block Z
|
|
||||||
* @param blockState The block state id
|
|
||||||
*/
|
*/
|
||||||
void setFlatBlock(int x, int y, int z, int blockState);
|
public void setFlatBlock(int idx, int id) {
|
||||||
|
int index = palette.indexOf(id);
|
||||||
|
if (index == -1) {
|
||||||
|
index = palette.size();
|
||||||
|
palette.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks[idx] = index;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a block id (without data)
|
* Set the block light array
|
||||||
* /!\ YOU SHOULD NOT USE THIS ON 1.13
|
|
||||||
*
|
*
|
||||||
* @param x Block X
|
* @param data The value to set the block light to
|
||||||
* @param y Block Y
|
|
||||||
* @param z Block Z
|
|
||||||
* @return Block id (without data)
|
|
||||||
*/
|
*/
|
||||||
int getBlockId(int x, int y, int z);
|
public void setBlockLight(byte[] data) {
|
||||||
|
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
||||||
|
if (this.blockLight == null) {
|
||||||
|
this.blockLight = new NibbleArray(data);
|
||||||
|
} else {
|
||||||
|
this.blockLight.setHandle(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the blocks in < 1.13 format to a buffer.
|
* Set the sky light array
|
||||||
*
|
*
|
||||||
* @param output The buffer to write to.
|
* @param data The value to set the sky light to
|
||||||
* @throws Exception Throws if it failed to write.
|
|
||||||
*/
|
*/
|
||||||
void writeBlocks(ByteBuf output) throws Exception;
|
public void setSkyLight(byte[] data) {
|
||||||
|
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
||||||
|
if (this.skyLight == null) {
|
||||||
|
this.skyLight = new NibbleArray(data);
|
||||||
|
} else {
|
||||||
|
this.skyLight.setHandle(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBlockLight() {
|
||||||
|
return blockLight == null ? null : blockLight.getHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSkyLight() {
|
||||||
|
return skyLight == null ? null : skyLight.getHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readBlockLight(ByteBuf input) {
|
||||||
|
if (this.blockLight == null) {
|
||||||
|
this.blockLight = new NibbleArray(LIGHT_LENGTH * 2);
|
||||||
|
}
|
||||||
|
input.readBytes(this.blockLight.getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readSkyLight(ByteBuf input) {
|
||||||
|
if (this.skyLight == null) {
|
||||||
|
this.skyLight = new NibbleArray(LIGHT_LENGTH * 2);
|
||||||
|
}
|
||||||
|
input.readBytes(this.skyLight.getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int index(int x, int y, int z) {
|
||||||
|
return y << 8 | z << 4 | x;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the blocks in 1.13 format to a buffer.
|
* Write the block light to a buffer
|
||||||
*
|
*
|
||||||
* @param output The buffer to write to.
|
* @param output The buffer to write to
|
||||||
* @throws Exception Throws if it failed to write.
|
|
||||||
*/
|
*/
|
||||||
void writeBlocks1_13(ByteBuf output) throws Exception;
|
public void writeBlockLight(ByteBuf output) {
|
||||||
|
output.writeBytes(blockLight.getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
void writeBlockLight(ByteBuf output) throws Exception;
|
/**
|
||||||
|
* Write the sky light to a buffer
|
||||||
|
*
|
||||||
|
* @param output The buffer to write to
|
||||||
|
*/
|
||||||
|
public void writeSkyLight(ByteBuf output) {
|
||||||
|
output.writeBytes(skyLight.getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
boolean hasSkyLight();
|
public List<Integer> getPalette() {
|
||||||
|
return palette;
|
||||||
|
}
|
||||||
|
|
||||||
void writeSkyLight(ByteBuf output) throws Exception;
|
/**
|
||||||
|
* Check if sky light is present
|
||||||
|
*
|
||||||
|
* @return True if skylight is present
|
||||||
|
*/
|
||||||
|
public boolean hasSkyLight() {
|
||||||
|
return skyLight != null;
|
||||||
|
}
|
||||||
|
|
||||||
List<Integer> getPalette();
|
public boolean hasBlockLight() {
|
||||||
|
return blockLight != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
package us.myles.ViaVersion.api.type.types.version;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChunkSectionType1_13 extends Type<ChunkSection> {
|
||||||
|
|
||||||
|
public ChunkSectionType1_13() {
|
||||||
|
super("Chunk Section Type", ChunkSection.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkSection read(ByteBuf buffer) throws Exception {
|
||||||
|
ChunkSection chunkSection = new ChunkSection();
|
||||||
|
List<Integer> palette = chunkSection.getPalette();
|
||||||
|
palette.clear();
|
||||||
|
|
||||||
|
// Reaad bits per block
|
||||||
|
int bitsPerBlock = buffer.readUnsignedByte();
|
||||||
|
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
||||||
|
|
||||||
|
if (bitsPerBlock == 0) {
|
||||||
|
bitsPerBlock = 14;
|
||||||
|
}
|
||||||
|
if (bitsPerBlock < 4) {
|
||||||
|
bitsPerBlock = 4;
|
||||||
|
}
|
||||||
|
if (bitsPerBlock > 8) {
|
||||||
|
bitsPerBlock = 14;
|
||||||
|
}
|
||||||
|
int paletteLength = bitsPerBlock == 14 ? 0 : Type.VAR_INT.read(buffer);
|
||||||
|
// Read palette
|
||||||
|
for (int i = 0; i < paletteLength; i++) {
|
||||||
|
palette.add(Type.VAR_INT.read(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read blocks
|
||||||
|
long[] blockData = new long[Type.VAR_INT.read(buffer)];
|
||||||
|
if (blockData.length > 0) {
|
||||||
|
for (int i = 0; i < blockData.length; i++) {
|
||||||
|
blockData[i] = buffer.readLong();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ChunkSection.SIZE; i++) {
|
||||||
|
int bitIndex = i * bitsPerBlock;
|
||||||
|
int startIndex = bitIndex / 64;
|
||||||
|
int endIndex = ((i + 1) * bitsPerBlock - 1) / 64;
|
||||||
|
int startBitSubIndex = bitIndex % 64;
|
||||||
|
int val;
|
||||||
|
if (startIndex == endIndex) {
|
||||||
|
val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue);
|
||||||
|
} else {
|
||||||
|
int endBitSubIndex = 64 - startBitSubIndex;
|
||||||
|
val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitsPerBlock == 14) {
|
||||||
|
chunkSection.setFlatBlock(i, val);
|
||||||
|
} else {
|
||||||
|
chunkSection.setPaletteIndex(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, ChunkSection chunkSection) throws Exception {
|
||||||
|
List<Integer> palette = chunkSection.getPalette();
|
||||||
|
|
||||||
|
int bitsPerBlock = 4;
|
||||||
|
while (palette.size() > 1 << bitsPerBlock) {
|
||||||
|
bitsPerBlock += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitsPerBlock > 8) {
|
||||||
|
bitsPerBlock = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
||||||
|
buffer.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
|
|
||||||
|
// Write pallet (or not)
|
||||||
|
if (bitsPerBlock != 14) {
|
||||||
|
Type.VAR_INT.write(buffer, palette.size());
|
||||||
|
for (int mappedId : palette) {
|
||||||
|
Type.VAR_INT.write(buffer, mappedId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = (int) Math.ceil(ChunkSection.SIZE * bitsPerBlock / 64.0);
|
||||||
|
Type.VAR_INT.write(buffer, length);
|
||||||
|
long[] data = new long[length];
|
||||||
|
for (int index = 0; index < ChunkSection.SIZE; index++) {
|
||||||
|
int value = bitsPerBlock == 14 ? chunkSection.getFlatBlock(index) : chunkSection.getPaletteIndex(index);
|
||||||
|
int bitIndex = index * bitsPerBlock;
|
||||||
|
int startIndex = bitIndex / 64;
|
||||||
|
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
||||||
|
int startBitSubIndex = bitIndex % 64;
|
||||||
|
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
||||||
|
if (startIndex != endIndex) {
|
||||||
|
int endBitSubIndex = 64 - startBitSubIndex;
|
||||||
|
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (long l : data) {
|
||||||
|
buffer.writeLong(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package us.myles.ViaVersion.api.type.types.version;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChunkSectionType1_8 extends Type<ChunkSection> {
|
||||||
|
|
||||||
|
public ChunkSectionType1_8() {
|
||||||
|
super("Chunk Section Type", ChunkSection.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkSection read(ByteBuf buffer) throws Exception {
|
||||||
|
ChunkSection chunkSection = new ChunkSection();
|
||||||
|
List<Integer> palette = chunkSection.getPalette();
|
||||||
|
palette.clear();
|
||||||
|
|
||||||
|
byte[] blockData = new byte[ChunkSection.SIZE * 2];
|
||||||
|
buffer.readBytes(blockData);
|
||||||
|
ShortBuffer blockBuf = ByteBuffer.wrap(blockData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < ChunkSection.SIZE; i++) {
|
||||||
|
int mask = blockBuf.get();
|
||||||
|
int type = mask >> 4;
|
||||||
|
int data = mask & 0xF;
|
||||||
|
chunkSection.setBlock(i, type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, ChunkSection chunkSection) throws Exception {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package us.myles.ViaVersion.api.type.types.version;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChunkSectionType1_9 extends Type<ChunkSection> {
|
||||||
|
|
||||||
|
public ChunkSectionType1_9() {
|
||||||
|
super("Chunk Section Type", ChunkSection.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkSection read(ByteBuf buffer) throws Exception {
|
||||||
|
ChunkSection chunkSection = new ChunkSection();
|
||||||
|
List<Integer> palette = chunkSection.getPalette();
|
||||||
|
palette.clear();
|
||||||
|
|
||||||
|
// Reaad bits per block
|
||||||
|
int bitsPerBlock = buffer.readUnsignedByte();
|
||||||
|
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
||||||
|
|
||||||
|
if (bitsPerBlock == 0) {
|
||||||
|
bitsPerBlock = 13;
|
||||||
|
}
|
||||||
|
if (bitsPerBlock < 4) {
|
||||||
|
bitsPerBlock = 4;
|
||||||
|
}
|
||||||
|
if (bitsPerBlock > 8) {
|
||||||
|
bitsPerBlock = 13;
|
||||||
|
}
|
||||||
|
int paletteLength = Type.VAR_INT.read(buffer);
|
||||||
|
// Read palette
|
||||||
|
for (int i = 0; i < paletteLength; i++) {
|
||||||
|
if (bitsPerBlock != 13) {
|
||||||
|
palette.add(Type.VAR_INT.read(buffer));
|
||||||
|
} else {
|
||||||
|
Type.VAR_INT.read(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read blocks
|
||||||
|
long[] blockData = new long[Type.VAR_INT.read(buffer)];
|
||||||
|
if (blockData.length > 0) {
|
||||||
|
for (int i = 0; i < blockData.length; i++) {
|
||||||
|
blockData[i] = buffer.readLong();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ChunkSection.SIZE; i++) {
|
||||||
|
int bitIndex = i * bitsPerBlock;
|
||||||
|
int startIndex = bitIndex / 64;
|
||||||
|
int endIndex = ((i + 1) * bitsPerBlock - 1) / 64;
|
||||||
|
int startBitSubIndex = bitIndex % 64;
|
||||||
|
int val;
|
||||||
|
if (startIndex == endIndex) {
|
||||||
|
val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue);
|
||||||
|
} else {
|
||||||
|
int endBitSubIndex = 64 - startBitSubIndex;
|
||||||
|
val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitsPerBlock == 13) {
|
||||||
|
chunkSection.setBlock(i, val >> 4, val & 0xF);
|
||||||
|
} else {
|
||||||
|
chunkSection.setPaletteIndex(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuf buffer, ChunkSection chunkSection) throws Exception {
|
||||||
|
List<Integer> palette = chunkSection.getPalette();
|
||||||
|
|
||||||
|
int bitsPerBlock = 4;
|
||||||
|
while (palette.size() > 1 << bitsPerBlock) {
|
||||||
|
bitsPerBlock += 1;
|
||||||
|
}
|
||||||
|
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
||||||
|
buffer.writeByte(bitsPerBlock);
|
||||||
|
|
||||||
|
// Write pallet
|
||||||
|
Type.VAR_INT.write(buffer, palette.size());
|
||||||
|
for (int mappedId : palette) {
|
||||||
|
Type.VAR_INT.write(buffer, mappedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = (int) Math.ceil(ChunkSection.SIZE * bitsPerBlock / 64.0);
|
||||||
|
Type.VAR_INT.write(buffer, length);
|
||||||
|
long[] data = new long[length];
|
||||||
|
for (int index = 0; index < ChunkSection.SIZE; index++) {
|
||||||
|
int value = chunkSection.getPaletteIndex(index);
|
||||||
|
int bitIndex = index * bitsPerBlock;
|
||||||
|
int startIndex = bitIndex / 64;
|
||||||
|
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
||||||
|
int startBitSubIndex = bitIndex % 64;
|
||||||
|
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
||||||
|
if (startIndex != endIndex) {
|
||||||
|
int endBitSubIndex = 64 - startBitSubIndex;
|
||||||
|
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (long l : data) {
|
||||||
|
buffer.writeLong(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package us.myles.ViaVersion.api.type.types.version;
|
package us.myles.ViaVersion.api.type.types.version;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
|
|
||||||
@ -15,4 +16,6 @@ public class Types1_13 {
|
|||||||
* Metadata type for 1.13
|
* Metadata type for 1.13
|
||||||
*/
|
*/
|
||||||
public static final Type<Metadata> METADATA = new Metadata1_13Type();
|
public static final Type<Metadata> METADATA = new Metadata1_13Type();
|
||||||
|
|
||||||
|
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_13();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package us.myles.ViaVersion.api.type.types.version;
|
package us.myles.ViaVersion.api.type.types.version;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
|
|
||||||
@ -15,4 +16,6 @@ public class Types1_8 {
|
|||||||
* Metadata type for 1.8
|
* Metadata type for 1.8
|
||||||
*/
|
*/
|
||||||
public static final Type<Metadata> METADATA = new Metadata1_8Type();
|
public static final Type<Metadata> METADATA = new Metadata1_8Type();
|
||||||
|
|
||||||
|
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_8();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package us.myles.ViaVersion.api.type.types.version;
|
package us.myles.ViaVersion.api.type.types.version;
|
||||||
|
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
import us.myles.ViaVersion.api.minecraft.metadata.Metadata;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
|
|
||||||
@ -15,4 +16,6 @@ public class Types1_9 {
|
|||||||
* Metadata type for 1.9
|
* Metadata type for 1.9
|
||||||
*/
|
*/
|
||||||
public static final Type<Metadata> METADATA = new Metadata1_9Type();
|
public static final Type<Metadata> METADATA = new Metadata1_9Type();
|
||||||
|
|
||||||
|
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_9();
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.chunks;
|
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class Chunk1_13 implements Chunk {
|
|
||||||
private int x;
|
|
||||||
private int z;
|
|
||||||
private boolean groundUp;
|
|
||||||
private int bitmask;
|
|
||||||
private ChunkSection1_13[] sections;
|
|
||||||
private byte[] biomeData;
|
|
||||||
private List<CompoundTag> blockEntities;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBiomeData() {
|
|
||||||
return biomeData != null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,311 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.chunks;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
|
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ChunkSection1_13 implements ChunkSection {
|
|
||||||
/**
|
|
||||||
* Size (dimensions) of blocks in a chunks section.
|
|
||||||
*/
|
|
||||||
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
|
||||||
/**
|
|
||||||
* Length of the sky and block light nibble arrays.
|
|
||||||
*/
|
|
||||||
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
|
||||||
/**
|
|
||||||
* Length of the block data array.
|
|
||||||
*/
|
|
||||||
private final List<Integer> palette = Lists.newArrayList();
|
|
||||||
private final int[] blocks;
|
|
||||||
private final NibbleArray blockLight;
|
|
||||||
private NibbleArray skyLight;
|
|
||||||
|
|
||||||
public ChunkSection1_13() {
|
|
||||||
this.blocks = new int[SIZE];
|
|
||||||
this.blockLight = new NibbleArray(SIZE);
|
|
||||||
palette.add(0); // AIR
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlock(int x, int y, int z, int type, int data) {
|
|
||||||
setBlock(index(x, y, z), type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFlatBlock(int x, int y, int z, int type) {
|
|
||||||
int index = palette.indexOf(type);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[index(x, y, z)] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlockId(int x, int y, int z) {
|
|
||||||
return getBlock(x, y, z) >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlock(int x, int y, int z) {
|
|
||||||
int index = blocks[index(x, y, z)];
|
|
||||||
return palette.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks based on the index
|
|
||||||
*
|
|
||||||
* @param idx Index
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int idx, int type, int data) {
|
|
||||||
int hash = type << 4 | (data & 0xF);
|
|
||||||
int index = palette.indexOf(hash);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[idx] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the block light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the block light to
|
|
||||||
*/
|
|
||||||
public void setBlockLight(byte[] data) {
|
|
||||||
blockLight.setHandle(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the sky light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the sky light to
|
|
||||||
*/
|
|
||||||
public void setSkyLight(byte[] data) {
|
|
||||||
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
|
||||||
this.skyLight = new NibbleArray(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int index(int x, int y, int z) {
|
|
||||||
return y << 8 | z << 4 | x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read blocks from input stream.
|
|
||||||
* This reads all the block related data:
|
|
||||||
* <ul>
|
|
||||||
* <li>Block length/palette type</li>
|
|
||||||
* <li>Palette</li>
|
|
||||||
* <li>Block hashes/palette reference</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from.
|
|
||||||
* @throws Exception If it fails to read
|
|
||||||
*/
|
|
||||||
public void readBlocks(ByteBuf input) throws Exception {
|
|
||||||
palette.clear();
|
|
||||||
|
|
||||||
// Read bits per block
|
|
||||||
int bitsPerBlock = input.readUnsignedByte();
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
|
|
||||||
boolean directPalette = false;
|
|
||||||
|
|
||||||
if (bitsPerBlock == 0) {
|
|
||||||
bitsPerBlock = 14;
|
|
||||||
}
|
|
||||||
if (bitsPerBlock < 4) {
|
|
||||||
bitsPerBlock = 4;
|
|
||||||
}
|
|
||||||
if (bitsPerBlock >= 9) {
|
|
||||||
directPalette = true;
|
|
||||||
bitsPerBlock = 14;
|
|
||||||
}
|
|
||||||
|
|
||||||
int paletteLength = directPalette ? 0 : Type.VAR_INT.read(input);
|
|
||||||
// Read palette
|
|
||||||
for (int i = 0; i < paletteLength; i++) {
|
|
||||||
palette.add(Type.VAR_INT.read(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read blocks
|
|
||||||
// Long[] blockData = Type.LONG_ARRAY.read(input);
|
|
||||||
long[] blockData = new long[Type.VAR_INT.read(input)];
|
|
||||||
for (int i = 0; i < blockData.length; i++) {
|
|
||||||
blockData[i] = input.readLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blockData.length > 0) {
|
|
||||||
for (int i = 0; i < blocks.length; i++) {
|
|
||||||
int bitIndex = i * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex >> 6; // /64
|
|
||||||
int endIndex = ((i + 1) * bitsPerBlock - 1) >> 6; // /64
|
|
||||||
int startBitSubIndex = bitIndex & 0x3F; // % 64
|
|
||||||
int val;
|
|
||||||
if (startIndex == endIndex) {
|
|
||||||
val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue);
|
|
||||||
} else {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (directPalette) {
|
|
||||||
int type = val >> 4;
|
|
||||||
int data = val & 0xF;
|
|
||||||
|
|
||||||
setBlock(i, type, data);
|
|
||||||
} else {
|
|
||||||
blocks[i] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read block light from buffer.
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from
|
|
||||||
*/
|
|
||||||
public void readBlockLight(ByteBuf input) {
|
|
||||||
byte[] handle = new byte[LIGHT_LENGTH];
|
|
||||||
input.readBytes(handle);
|
|
||||||
blockLight.setHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read sky light from buffer.
|
|
||||||
* Note: Only sent in overworld!
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from
|
|
||||||
*/
|
|
||||||
public void readSkyLight(ByteBuf input) {
|
|
||||||
byte[] handle = new byte[LIGHT_LENGTH];
|
|
||||||
input.readBytes(handle);
|
|
||||||
if (skyLight != null) {
|
|
||||||
skyLight.setHandle(handle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.skyLight = new NibbleArray(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void writeBlocks(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock += 1;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBlocks1_13(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock++;
|
|
||||||
}
|
|
||||||
boolean directPalette = false;
|
|
||||||
if (bitsPerBlock >= 9) {
|
|
||||||
bitsPerBlock = 14;
|
|
||||||
directPalette = true;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
if (!directPalette) {
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = directPalette ? palette.get(blocks[index]) : blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the block light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void writeBlockLight(ByteBuf output) {
|
|
||||||
output.writeBytes(blockLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the sky light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void writeSkyLight(ByteBuf output) {
|
|
||||||
output.writeBytes(skyLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if sky light is present
|
|
||||||
*
|
|
||||||
* @return True if skylight is present
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean hasSkyLight() {
|
|
||||||
return skyLight != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Integer> getPalette() {
|
|
||||||
return palette;
|
|
||||||
}
|
|
||||||
}
|
|
@ -254,7 +254,7 @@ public class WorldPackets {
|
|||||||
for (int x = 0; x < 16; x++) {
|
for (int x = 0; x < 16; x++) {
|
||||||
for (int y = 0; y < 16; y++) {
|
for (int y = 0; y < 16; y++) {
|
||||||
for (int z = 0; z < 16; z++) {
|
for (int z = 0; z < 16; z++) {
|
||||||
int block = section.getBlock(x, y, z);
|
int block = section.getFlatBlock(x, y, z);
|
||||||
if (storage.isWelcome(block)) {
|
if (storage.isWelcome(block)) {
|
||||||
storage.store(new Position(
|
storage.store(new Position(
|
||||||
(long) (x + (chunk.getX() << 4)),
|
(long) (x + (chunk.getX() << 4)),
|
||||||
|
@ -9,8 +9,7 @@ import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|||||||
import us.myles.ViaVersion.api.type.PartialType;
|
import us.myles.ViaVersion.api.type.PartialType;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.chunks.Chunk1_13;
|
import us.myles.ViaVersion.api.type.types.version.Types1_13;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.chunks.ChunkSection1_13;
|
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -33,7 +32,7 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
Type.VAR_INT.read(input);
|
Type.VAR_INT.read(input);
|
||||||
|
|
||||||
BitSet usedSections = new BitSet(16);
|
BitSet usedSections = new BitSet(16);
|
||||||
ChunkSection1_13[] sections = new ChunkSection1_13[16];
|
ChunkSection[] sections = new ChunkSection[16];
|
||||||
// Calculate section count from bitmask
|
// Calculate section count from bitmask
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
if ((primaryBitmask & (1 << i)) != 0) {
|
if ((primaryBitmask & (1 << i)) != 0) {
|
||||||
@ -44,9 +43,8 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
// Read sections
|
// Read sections
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
if (!usedSections.get(i)) continue; // Section not set
|
if (!usedSections.get(i)) continue; // Section not set
|
||||||
ChunkSection1_13 section = new ChunkSection1_13();
|
ChunkSection section = Types1_13.CHUNK_SECTION.read(input);
|
||||||
sections[i] = section;
|
sections[i] = section;
|
||||||
section.readBlocks(input);
|
|
||||||
section.readBlockLight(input);
|
section.readBlockLight(input);
|
||||||
if (world.getEnvironment() == Environment.NORMAL) {
|
if (world.getEnvironment() == Environment.NORMAL) {
|
||||||
section.readSkyLight(input);
|
section.readSkyLight(input);
|
||||||
@ -71,7 +69,7 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Chunk1_13(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
|
return new Chunk(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,7 +84,7 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
ChunkSection section = chunk.getSections()[i];
|
ChunkSection section = chunk.getSections()[i];
|
||||||
if (section == null) continue; // Section not set
|
if (section == null) continue; // Section not set
|
||||||
section.writeBlocks1_13(buf);
|
Types1_13.CHUNK_SECTION.write(buf, section);
|
||||||
section.writeBlockLight(buf);
|
section.writeBlockLight(buf);
|
||||||
|
|
||||||
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks;
|
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class Chunk1_9_3_4 implements Chunk {
|
|
||||||
private int x;
|
|
||||||
private int z;
|
|
||||||
private boolean groundUp;
|
|
||||||
private int bitmask;
|
|
||||||
private ChunkSection1_9_3_4[] sections;
|
|
||||||
private byte[] biomeData;
|
|
||||||
private List<CompoundTag> blockEntities;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBiomeData() {
|
|
||||||
return biomeData != null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,368 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
|
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ChunkSection1_9_3_4 implements ChunkSection {
|
|
||||||
/**
|
|
||||||
* Size (dimensions) of blocks in a chunks section.
|
|
||||||
*/
|
|
||||||
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
|
||||||
/**
|
|
||||||
* Length of the sky and block light nibble arrays.
|
|
||||||
*/
|
|
||||||
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
|
||||||
/**
|
|
||||||
* Length of the block data array.
|
|
||||||
*/
|
|
||||||
private final List<Integer> palette = Lists.newArrayList();
|
|
||||||
private final int[] blocks;
|
|
||||||
private final NibbleArray blockLight;
|
|
||||||
private NibbleArray skyLight;
|
|
||||||
|
|
||||||
public ChunkSection1_9_3_4() {
|
|
||||||
this.blocks = new int[SIZE];
|
|
||||||
this.blockLight = new NibbleArray(SIZE);
|
|
||||||
palette.add(0); // AIR
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks
|
|
||||||
*
|
|
||||||
* @param x Block X
|
|
||||||
* @param y Block Y
|
|
||||||
* @param z Block Z
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int x, int y, int z, int type, int data) {
|
|
||||||
setBlock(index(x, y, z), type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a flat block in the chunks
|
|
||||||
*
|
|
||||||
* @param x Block X
|
|
||||||
* @param y Block Y
|
|
||||||
* @param z Block Z
|
|
||||||
* @param type The type of the block
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setFlatBlock(int x, int y, int z, int type) {
|
|
||||||
int index = palette.indexOf(type);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[index(x, y, z)] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlockId(int x, int y, int z) {
|
|
||||||
return getBlock(x, y, z) >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlock(int x, int y, int z) {
|
|
||||||
int index = blocks[index(x, y, z)];
|
|
||||||
return palette.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks based on the index
|
|
||||||
*
|
|
||||||
* @param idx Index
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int idx, int type, int data) {
|
|
||||||
int hash = type << 4 | (data & 0xF);
|
|
||||||
int index = palette.indexOf(hash);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[idx] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the block light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the block light to
|
|
||||||
*/
|
|
||||||
public void setBlockLight(byte[] data) {
|
|
||||||
blockLight.setHandle(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the sky light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the sky light to
|
|
||||||
*/
|
|
||||||
public void setSkyLight(byte[] data) {
|
|
||||||
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
|
||||||
this.skyLight = new NibbleArray(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int index(int x, int y, int z) {
|
|
||||||
return y << 8 | z << 4 | x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read blocks from input stream.
|
|
||||||
* This reads all the block related data:
|
|
||||||
* <ul>
|
|
||||||
* <li>Block length/palette type</li>
|
|
||||||
* <li>Palette</li>
|
|
||||||
* <li>Block hashes/palette reference</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from.
|
|
||||||
* @throws Exception If it fails to read
|
|
||||||
*/
|
|
||||||
public void readBlocks(ByteBuf input) throws Exception {
|
|
||||||
palette.clear();
|
|
||||||
|
|
||||||
// Read bits per block
|
|
||||||
int bitsPerBlock = input.readUnsignedByte();
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
|
|
||||||
if (bitsPerBlock == 0) {
|
|
||||||
bitsPerBlock = 13;
|
|
||||||
}
|
|
||||||
if (bitsPerBlock < 4) {
|
|
||||||
bitsPerBlock = 4;
|
|
||||||
}
|
|
||||||
if (bitsPerBlock > 8) {
|
|
||||||
bitsPerBlock = 13;
|
|
||||||
}
|
|
||||||
int paletteLength = Type.VAR_INT.read(input);
|
|
||||||
// Read palette
|
|
||||||
for (int i = 0; i < paletteLength; i++) {
|
|
||||||
if (bitsPerBlock != 13) {
|
|
||||||
palette.add(Type.VAR_INT.read(input));
|
|
||||||
} else {
|
|
||||||
Type.VAR_INT.read(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read blocks
|
|
||||||
//Long[] blockData = Type.LONG_ARRAY.read(input);
|
|
||||||
long[] blockData = new long[Type.VAR_INT.read(input)];
|
|
||||||
for (int i = 0; i < blockData.length; i++) {
|
|
||||||
blockData[i] = input.readLong();
|
|
||||||
}
|
|
||||||
if (blockData.length > 0) {
|
|
||||||
for (int i = 0; i < blocks.length; i++) {
|
|
||||||
int bitIndex = i * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((i + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
int val;
|
|
||||||
if (startIndex == endIndex) {
|
|
||||||
val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue);
|
|
||||||
} else {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitsPerBlock == 13) {
|
|
||||||
int type = val >> 4;
|
|
||||||
int data = val & 0xF;
|
|
||||||
|
|
||||||
setBlock(i, type, data);
|
|
||||||
} else {
|
|
||||||
blocks[i] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read block light from buffer.
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from
|
|
||||||
*/
|
|
||||||
public void readBlockLight(ByteBuf input) {
|
|
||||||
byte[] handle = new byte[LIGHT_LENGTH];
|
|
||||||
input.readBytes(handle);
|
|
||||||
blockLight.setHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read sky light from buffer.
|
|
||||||
* Note: Only sent in overworld!
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from
|
|
||||||
*/
|
|
||||||
public void readSkyLight(ByteBuf input) {
|
|
||||||
byte[] handle = new byte[LIGHT_LENGTH];
|
|
||||||
input.readBytes(handle);
|
|
||||||
if (skyLight != null) {
|
|
||||||
skyLight.setHandle(handle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.skyLight = new NibbleArray(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the blocks to a buffer.
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to.
|
|
||||||
* @throws Exception Throws if it failed to write.
|
|
||||||
*/
|
|
||||||
public void writeBlocks(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock += 1;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBlocks1_13(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock++;
|
|
||||||
}
|
|
||||||
boolean directPalette = false;
|
|
||||||
if (bitsPerBlock >= 9) {
|
|
||||||
bitsPerBlock = 14;
|
|
||||||
directPalette = true;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
if (!directPalette) {
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = directPalette ? palette.get(blocks[index]) : blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the block light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
public void writeBlockLight(ByteBuf output) {
|
|
||||||
output.writeBytes(blockLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the sky light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
public void writeSkyLight(ByteBuf output) {
|
|
||||||
output.writeBytes(skyLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if sky light is present
|
|
||||||
*
|
|
||||||
* @return True if skylight is present
|
|
||||||
*/
|
|
||||||
public boolean hasSkyLight() {
|
|
||||||
return skyLight != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get expected size of this chunks section.
|
|
||||||
*
|
|
||||||
* @return Amount of bytes sent by this section
|
|
||||||
* @throws Exception If it failed to calculate bits properly
|
|
||||||
*/
|
|
||||||
public int getExpectedSize() throws Exception {
|
|
||||||
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
|
|
||||||
int bytes = 1; // bits per block
|
|
||||||
bytes += paletteBytes(); // palette
|
|
||||||
bytes += countBytes(bitsPerBlock == 16 ? SIZE * 2 : SIZE); // block data length
|
|
||||||
bytes += (palette.size() > 255 ? 2 : 1) * SIZE; // block data
|
|
||||||
bytes += LIGHT_LENGTH; // block light
|
|
||||||
bytes += hasSkyLight() ? LIGHT_LENGTH : 0; // sky light
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int paletteBytes() throws Exception {
|
|
||||||
// Count bytes used by pallet
|
|
||||||
int bytes = countBytes(palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
bytes += countBytes(mappedId);
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int countBytes(int value) throws Exception {
|
|
||||||
// Count amount of bytes that would be sent if the value were sent as a VarInt
|
|
||||||
if ((value & (~0 << 7)) == 0)
|
|
||||||
return 1;
|
|
||||||
if ((value & (~0 << 14)) == 0)
|
|
||||||
return 2;
|
|
||||||
if ((value & (~0 << 21)) == 0)
|
|
||||||
return 3;
|
|
||||||
if ((value & (~0 << 28)) == 0)
|
|
||||||
return 4;
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Integer> getPalette() {
|
|
||||||
return palette;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,8 +9,7 @@ import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|||||||
import us.myles.ViaVersion.api.type.PartialType;
|
import us.myles.ViaVersion.api.type.PartialType;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks.Chunk1_9_3_4;
|
import us.myles.ViaVersion.api.type.types.version.Types1_9;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.chunks.ChunkSection1_9_3_4;
|
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -34,7 +33,7 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
Type.VAR_INT.read(input);
|
Type.VAR_INT.read(input);
|
||||||
|
|
||||||
BitSet usedSections = new BitSet(16);
|
BitSet usedSections = new BitSet(16);
|
||||||
ChunkSection1_9_3_4[] sections = new ChunkSection1_9_3_4[16];
|
ChunkSection[] sections = new ChunkSection[16];
|
||||||
// Calculate section count from bitmask
|
// Calculate section count from bitmask
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
if ((primaryBitmask & (1 << i)) != 0) {
|
if ((primaryBitmask & (1 << i)) != 0) {
|
||||||
@ -45,9 +44,8 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
// Read sections
|
// Read sections
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
if (!usedSections.get(i)) continue; // Section not set
|
if (!usedSections.get(i)) continue; // Section not set
|
||||||
ChunkSection1_9_3_4 section = new ChunkSection1_9_3_4();
|
ChunkSection section = Types1_9.CHUNK_SECTION.read(input);
|
||||||
sections[i] = section;
|
sections[i] = section;
|
||||||
section.readBlocks(input);
|
|
||||||
section.readBlockLight(input);
|
section.readBlockLight(input);
|
||||||
if (world.getEnvironment() == Environment.NORMAL) {
|
if (world.getEnvironment() == Environment.NORMAL) {
|
||||||
section.readSkyLight(input);
|
section.readSkyLight(input);
|
||||||
@ -69,7 +67,7 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
|
return new Chunk(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -84,7 +82,7 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
ChunkSection section = chunk.getSections()[i];
|
ChunkSection section = chunk.getSections()[i];
|
||||||
if (section == null) continue; // Section not set
|
if (section == null) continue; // Section not set
|
||||||
section.writeBlocks(buf);
|
Types1_9.CHUNK_SECTION.write(buf, section);
|
||||||
section.writeBlockLight(buf);
|
section.writeBlockLight(buf);
|
||||||
|
|
||||||
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks;
|
|
||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class Chunk1_9_1_2 implements Chunk {
|
|
||||||
private int x;
|
|
||||||
private int z;
|
|
||||||
private boolean groundUp;
|
|
||||||
private int bitmask;
|
|
||||||
private final ChunkSection1_9_1_2[] sections;
|
|
||||||
private byte[] biomeData;
|
|
||||||
private List<CompoundTag> blockEntities;
|
|
||||||
|
|
||||||
public boolean isBiomeData() {
|
|
||||||
return biomeData != null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,360 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
|
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ChunkSection1_9_1_2 implements ChunkSection {
|
|
||||||
/**
|
|
||||||
* Size (dimensions) of blocks in a chunks section.
|
|
||||||
*/
|
|
||||||
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
|
||||||
/**
|
|
||||||
* Length of the sky and block light nibble arrays.
|
|
||||||
*/
|
|
||||||
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
|
||||||
/**
|
|
||||||
* Length of the block data array.
|
|
||||||
*/
|
|
||||||
private final List<Integer> palette = Lists.newArrayList();
|
|
||||||
private final int[] blocks;
|
|
||||||
private final NibbleArray blockLight;
|
|
||||||
private NibbleArray skyLight;
|
|
||||||
|
|
||||||
public ChunkSection1_9_1_2() {
|
|
||||||
this.blocks = new int[SIZE];
|
|
||||||
this.blockLight = new NibbleArray(SIZE);
|
|
||||||
palette.add(0); // AIR
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks
|
|
||||||
*
|
|
||||||
* @param x Block X
|
|
||||||
* @param y Block Y
|
|
||||||
* @param z Block Z
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int x, int y, int z, int type, int data) {
|
|
||||||
setBlock(index(x, y, z), type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFlatBlock(int x, int y, int z, int type) {
|
|
||||||
int index = palette.indexOf(type);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[index(x, y, z)] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlockId(int x, int y, int z) {
|
|
||||||
return getBlock(x, y, z) >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlock(int x, int y, int z) {
|
|
||||||
int index = blocks[index(x, y, z)];
|
|
||||||
return palette.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks based on the index
|
|
||||||
*
|
|
||||||
* @param idx Index
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int idx, int type, int data) {
|
|
||||||
int hash = type << 4 | (data & 0xF);
|
|
||||||
int index = palette.indexOf(hash);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[idx] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the block light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the block light to
|
|
||||||
*/
|
|
||||||
public void setBlockLight(byte[] data) {
|
|
||||||
blockLight.setHandle(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the sky light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the sky light to
|
|
||||||
*/
|
|
||||||
public void setSkyLight(byte[] data) {
|
|
||||||
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
|
||||||
this.skyLight = new NibbleArray(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int index(int x, int y, int z) {
|
|
||||||
return y << 8 | z << 4 | x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read blocks from input stream.
|
|
||||||
* This reads all the block related data:
|
|
||||||
* <ul>
|
|
||||||
* <li>Block length/palette type</li>
|
|
||||||
* <li>Palette</li>
|
|
||||||
* <li>Block hashes/palette reference</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from.
|
|
||||||
* @throws Exception If it failed to read properly
|
|
||||||
*/
|
|
||||||
public void readBlocks(ByteBuf input) throws Exception {
|
|
||||||
palette.clear();
|
|
||||||
|
|
||||||
// Reaad bits per block
|
|
||||||
int bitsPerBlock = input.readUnsignedByte();
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
|
|
||||||
if (bitsPerBlock == 0) {
|
|
||||||
bitsPerBlock = 13;
|
|
||||||
}
|
|
||||||
if (bitsPerBlock < 4) {
|
|
||||||
bitsPerBlock = 4;
|
|
||||||
}
|
|
||||||
if (bitsPerBlock > 8) {
|
|
||||||
bitsPerBlock = 13;
|
|
||||||
}
|
|
||||||
int paletteLength = Type.VAR_INT.read(input);
|
|
||||||
// Read palette
|
|
||||||
for (int i = 0; i < paletteLength; i++) {
|
|
||||||
if (bitsPerBlock != 13) {
|
|
||||||
palette.add(Type.VAR_INT.read(input));
|
|
||||||
} else {
|
|
||||||
Type.VAR_INT.read(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read blocks
|
|
||||||
// Long[] blockData = Type.LONG_ARRAY.read(input);
|
|
||||||
long[] blockData = new long[Type.VAR_INT.read(input)];
|
|
||||||
for (int i = 0; i < blockData.length; i++) {
|
|
||||||
blockData[i] = input.readLong();
|
|
||||||
}
|
|
||||||
if (blockData.length > 0) {
|
|
||||||
for (int i = 0; i < blocks.length; i++) {
|
|
||||||
int bitIndex = i * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((i + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
int val;
|
|
||||||
if (startIndex == endIndex) {
|
|
||||||
val = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue);
|
|
||||||
} else {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
val = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitsPerBlock == 13) {
|
|
||||||
int type = val >> 4;
|
|
||||||
int data = val & 0xF;
|
|
||||||
|
|
||||||
setBlock(i, type, data);
|
|
||||||
} else {
|
|
||||||
blocks[i] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read block light from buffer.
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from
|
|
||||||
*/
|
|
||||||
public void readBlockLight(ByteBuf input) {
|
|
||||||
byte[] handle = new byte[LIGHT_LENGTH];
|
|
||||||
input.readBytes(handle);
|
|
||||||
blockLight.setHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read sky light from buffer.
|
|
||||||
* Note: Only sent in overworld!
|
|
||||||
*
|
|
||||||
* @param input The buffer to read from
|
|
||||||
*/
|
|
||||||
public void readSkyLight(ByteBuf input) {
|
|
||||||
byte[] handle = new byte[LIGHT_LENGTH];
|
|
||||||
input.readBytes(handle);
|
|
||||||
if (skyLight != null) {
|
|
||||||
skyLight.setHandle(handle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.skyLight = new NibbleArray(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the blocks to a buffer.
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to.
|
|
||||||
* @throws Exception Throws if it failed to write.
|
|
||||||
*/
|
|
||||||
public void writeBlocks(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock += 1;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBlocks1_13(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock++;
|
|
||||||
}
|
|
||||||
boolean directPalette = false;
|
|
||||||
if (bitsPerBlock >= 9) {
|
|
||||||
bitsPerBlock = 14;
|
|
||||||
directPalette = true;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
if (!directPalette) {
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = directPalette ? palette.get(blocks[index]) : blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the block light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
public void writeBlockLight(ByteBuf output) {
|
|
||||||
output.writeBytes(blockLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the sky light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
public void writeSkyLight(ByteBuf output) {
|
|
||||||
output.writeBytes(skyLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Integer> getPalette() {
|
|
||||||
return palette;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if sky light is present
|
|
||||||
*
|
|
||||||
* @return True if skylight is present
|
|
||||||
*/
|
|
||||||
public boolean hasSkyLight() {
|
|
||||||
return skyLight != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get expected size of this chunks section.
|
|
||||||
*
|
|
||||||
* @return Amount of bytes sent by this section
|
|
||||||
* @throws Exception If it failed to calculate bits properly
|
|
||||||
*/
|
|
||||||
public int getExpectedSize() throws Exception {
|
|
||||||
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
|
|
||||||
int bytes = 1; // bits per block
|
|
||||||
bytes += paletteBytes(); // palette
|
|
||||||
bytes += countBytes(bitsPerBlock == 16 ? SIZE * 2 : SIZE); // block data length
|
|
||||||
bytes += (palette.size() > 255 ? 2 : 1) * SIZE; // block data
|
|
||||||
bytes += LIGHT_LENGTH; // block light
|
|
||||||
bytes += hasSkyLight() ? LIGHT_LENGTH : 0; // sky light
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int paletteBytes() throws Exception {
|
|
||||||
// Count bytes used by pallet
|
|
||||||
int bytes = countBytes(palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
bytes += countBytes(mappedId);
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int countBytes(int value) throws Exception {
|
|
||||||
// Count amount of bytes that would be sent if the value were sent as a VarInt
|
|
||||||
if ((value & (~0 << 7)) == 0)
|
|
||||||
return 1;
|
|
||||||
if ((value & (~0 << 14)) == 0)
|
|
||||||
return 2;
|
|
||||||
if ((value & (~0 << 21)) == 0)
|
|
||||||
return 3;
|
|
||||||
if ((value & (~0 << 28)) == 0)
|
|
||||||
return 4;
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,10 +9,9 @@ import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|||||||
import us.myles.ViaVersion.api.type.PartialType;
|
import us.myles.ViaVersion.api.type.PartialType;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
||||||
|
import us.myles.ViaVersion.api.type.types.version.Types1_9;
|
||||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
|
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks.Chunk1_9_1_2;
|
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.chunks.ChunkSection1_9_1_2;
|
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -37,7 +36,7 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
int size = Type.VAR_INT.read(input);
|
int size = Type.VAR_INT.read(input);
|
||||||
|
|
||||||
BitSet usedSections = new BitSet(16);
|
BitSet usedSections = new BitSet(16);
|
||||||
ChunkSection1_9_1_2[] sections = new ChunkSection1_9_1_2[16];
|
ChunkSection[] sections = new ChunkSection[16];
|
||||||
// Calculate section count from bitmask
|
// Calculate section count from bitmask
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
if ((primaryBitmask & (1 << i)) != 0) {
|
if ((primaryBitmask & (1 << i)) != 0) {
|
||||||
@ -48,9 +47,8 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
// Read sections
|
// Read sections
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
if (!usedSections.get(i)) continue; // Section not set
|
if (!usedSections.get(i)) continue; // Section not set
|
||||||
ChunkSection1_9_1_2 section = new ChunkSection1_9_1_2();
|
ChunkSection section = Types1_9.CHUNK_SECTION.read(input);
|
||||||
sections[i] = section;
|
sections[i] = section;
|
||||||
section.readBlocks(input);
|
|
||||||
section.readBlockLight(input);
|
section.readBlockLight(input);
|
||||||
if (world.getEnvironment() == Environment.NORMAL) {
|
if (world.getEnvironment() == Environment.NORMAL) {
|
||||||
section.readSkyLight(input);
|
section.readSkyLight(input);
|
||||||
@ -67,7 +65,7 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
input.readBytes(biomeData);
|
input.readBytes(biomeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Chunk1_9_1_2(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, new ArrayList<CompoundTag>());
|
return new Chunk(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, new ArrayList<CompoundTag>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -82,7 +80,7 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
|
|||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
ChunkSection section = chunk.getSections()[i];
|
ChunkSection section = chunk.getSections()[i];
|
||||||
if (section == null) continue; // Section not set
|
if (section == null) continue; // Section not set
|
||||||
section.writeBlocks(buf);
|
Types1_9.CHUNK_SECTION.write(buf, section);
|
||||||
section.writeBlockLight(buf);
|
section.writeBlockLight(buf);
|
||||||
|
|
||||||
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
||||||
|
@ -2,26 +2,20 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks;
|
|||||||
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.ToString;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
public class Chunk1_9to1_8 extends Chunk {
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
|
||||||
public class Chunk1_9to1_8 implements Chunk {
|
|
||||||
private final int x;
|
|
||||||
private final int z;
|
|
||||||
private final boolean groundUp;
|
|
||||||
private final int primaryBitmask;
|
|
||||||
private final ChunkSection1_9to1_8[] sections;
|
|
||||||
private final byte[] biomeData;
|
|
||||||
private final List<CompoundTag> blockEntities;
|
|
||||||
private boolean unloadPacket = false;
|
private boolean unloadPacket = false;
|
||||||
|
|
||||||
|
public Chunk1_9to1_8(int x, int z, boolean groundUp, int bitmask, ChunkSection[] sections, byte[] biomeData, List<CompoundTag> blockEntities) {
|
||||||
|
super(x, z, groundUp, bitmask, sections, biomeData, blockEntities);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chunk unload.
|
* Chunk unload.
|
||||||
*
|
*
|
||||||
@ -29,8 +23,7 @@ public class Chunk1_9to1_8 implements Chunk {
|
|||||||
* @param z coord
|
* @param z coord
|
||||||
*/
|
*/
|
||||||
public Chunk1_9to1_8(int x, int z) {
|
public Chunk1_9to1_8(int x, int z) {
|
||||||
this(x, z, true, 0, new ChunkSection1_9to1_8[16], null,
|
this(x, z, true, 0, new ChunkSection[16], null, new ArrayList<CompoundTag>());
|
||||||
new ArrayList<CompoundTag>());
|
|
||||||
this.unloadPacket = true;
|
this.unloadPacket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,9 +40,4 @@ public class Chunk1_9to1_8 implements Chunk {
|
|||||||
public boolean isBiomeData() {
|
public boolean isBiomeData() {
|
||||||
return biomeData != null;
|
return biomeData != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBitmask() {
|
|
||||||
return primaryBitmask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,263 +0,0 @@
|
|||||||
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray;
|
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ChunkSection1_9to1_8 implements ChunkSection {
|
|
||||||
/**
|
|
||||||
* Size (dimensions) of blocks in a chunks section.
|
|
||||||
*/
|
|
||||||
public static final int SIZE = 16 * 16 * 16; // width * depth * height
|
|
||||||
/**
|
|
||||||
* Length of the sky and block light nibble arrays.
|
|
||||||
*/
|
|
||||||
public static final int LIGHT_LENGTH = 16 * 16 * 16 / 2; // size * size * size / 2 (nibble bit count)
|
|
||||||
/**
|
|
||||||
* Length of the block data array.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private final List<Integer> palette = Lists.newArrayList();
|
|
||||||
private final int[] blocks;
|
|
||||||
private final NibbleArray blockLight;
|
|
||||||
private NibbleArray skyLight;
|
|
||||||
|
|
||||||
public ChunkSection1_9to1_8() {
|
|
||||||
this.blocks = new int[SIZE];
|
|
||||||
this.blockLight = new NibbleArray(SIZE);
|
|
||||||
palette.add(0); // AIR
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks
|
|
||||||
*
|
|
||||||
* @param x Block X
|
|
||||||
* @param y Block Y
|
|
||||||
* @param z Block Z
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int x, int y, int z, int type, int data) {
|
|
||||||
setBlock(index(x, y, z), type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFlatBlock(int x, int y, int z, int type) {
|
|
||||||
int index = palette.indexOf(type);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[index(x, y, z)] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlockId(int x, int y, int z) {
|
|
||||||
return getBlock(x, y, z) >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBlock(int x, int y, int z) {
|
|
||||||
int index = blocks[index(x, y, z)];
|
|
||||||
return palette.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a block in the chunks based on the index
|
|
||||||
*
|
|
||||||
* @param idx Index
|
|
||||||
* @param type The type of the block
|
|
||||||
* @param data The data value of the block
|
|
||||||
*/
|
|
||||||
public void setBlock(int idx, int type, int data) {
|
|
||||||
int hash = type << 4 | (data & 0xF);
|
|
||||||
int index = palette.indexOf(hash);
|
|
||||||
if (index == -1) {
|
|
||||||
index = palette.size();
|
|
||||||
palette.add(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks[idx] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the block light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the block light to
|
|
||||||
*/
|
|
||||||
public void setBlockLight(byte[] data) {
|
|
||||||
blockLight.setHandle(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the sky light array
|
|
||||||
*
|
|
||||||
* @param data The value to set the sky light to
|
|
||||||
*/
|
|
||||||
public void setSkyLight(byte[] data) {
|
|
||||||
if (data.length != LIGHT_LENGTH) throw new IllegalArgumentException("Data length != " + LIGHT_LENGTH);
|
|
||||||
this.skyLight = new NibbleArray(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int index(int x, int y, int z) {
|
|
||||||
return y << 8 | z << 4 | x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the blocks to a buffer.
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to.
|
|
||||||
* @throws Exception Throws if it failed to write.
|
|
||||||
*/
|
|
||||||
public void writeBlocks(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock += 1;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeBlocks1_13(ByteBuf output) throws Exception {
|
|
||||||
// Write bits per block
|
|
||||||
int bitsPerBlock = 4;
|
|
||||||
while (palette.size() > 1 << bitsPerBlock) {
|
|
||||||
bitsPerBlock++;
|
|
||||||
}
|
|
||||||
boolean directPalette = false;
|
|
||||||
if (bitsPerBlock >= 9) {
|
|
||||||
bitsPerBlock = 14;
|
|
||||||
directPalette = true;
|
|
||||||
}
|
|
||||||
long maxEntryValue = (1L << bitsPerBlock) - 1;
|
|
||||||
output.writeByte(bitsPerBlock);
|
|
||||||
|
|
||||||
// Write pallet (or not)
|
|
||||||
if (!directPalette) {
|
|
||||||
Type.VAR_INT.write(output, palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
Type.VAR_INT.write(output, mappedId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = (int) Math.ceil(SIZE * bitsPerBlock / 64.0);
|
|
||||||
Type.VAR_INT.write(output, length);
|
|
||||||
long[] data = new long[length];
|
|
||||||
for (int index = 0; index < blocks.length; index++) {
|
|
||||||
int value = directPalette ? palette.get(blocks[index]) : blocks[index];
|
|
||||||
int bitIndex = index * bitsPerBlock;
|
|
||||||
int startIndex = bitIndex / 64;
|
|
||||||
int endIndex = ((index + 1) * bitsPerBlock - 1) / 64;
|
|
||||||
int startBitSubIndex = bitIndex % 64;
|
|
||||||
data[startIndex] = data[startIndex] & ~(maxEntryValue << startBitSubIndex) | ((long) value & maxEntryValue) << startBitSubIndex;
|
|
||||||
if (startIndex != endIndex) {
|
|
||||||
int endBitSubIndex = 64 - startBitSubIndex;
|
|
||||||
data[endIndex] = data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & maxEntryValue) >> endBitSubIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (long l : data) {
|
|
||||||
output.writeLong(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the block light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
public void writeBlockLight(ByteBuf output) {
|
|
||||||
output.writeBytes(blockLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the sky light to a buffer
|
|
||||||
*
|
|
||||||
* @param output The buffer to write to
|
|
||||||
*/
|
|
||||||
public void writeSkyLight(ByteBuf output) {
|
|
||||||
output.writeBytes(skyLight.getHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Integer> getPalette() {
|
|
||||||
return palette;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if sky light is present
|
|
||||||
*
|
|
||||||
* @return True if skylight is present
|
|
||||||
*/
|
|
||||||
public boolean hasSkyLight() {
|
|
||||||
return skyLight != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get expected size of this chunks section.
|
|
||||||
*
|
|
||||||
* @return Amount of bytes sent by this section
|
|
||||||
* @throws Exception If it failed to calculate bits properly
|
|
||||||
*/
|
|
||||||
public int getExpectedSize() throws Exception {
|
|
||||||
int bitsPerBlock = palette.size() > 255 ? 16 : 8;
|
|
||||||
int bytes = 1; // bits per block
|
|
||||||
bytes += paletteBytes(); // palette
|
|
||||||
bytes += countBytes(bitsPerBlock == 16 ? SIZE * 2 : SIZE); // block data length
|
|
||||||
bytes += (palette.size() > 255 ? 2 : 1) * SIZE; // block data
|
|
||||||
bytes += LIGHT_LENGTH; // block light
|
|
||||||
bytes += hasSkyLight() ? LIGHT_LENGTH : 0; // sky light
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int paletteBytes() throws Exception {
|
|
||||||
// Count bytes used by pallet
|
|
||||||
int bytes = countBytes(palette.size());
|
|
||||||
for (int mappedId : palette) {
|
|
||||||
bytes += countBytes(mappedId);
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int countBytes(int value) throws Exception {
|
|
||||||
// Count amount of bytes that would be sent if the value were sent as a VarInt
|
|
||||||
if ((value & (~0 << 7)) == 0)
|
|
||||||
return 1;
|
|
||||||
if ((value & (~0 << 14)) == 0)
|
|
||||||
return 2;
|
|
||||||
if ((value & (~0 << 21)) == 0)
|
|
||||||
return 3;
|
|
||||||
if ((value & (~0 << 28)) == 0)
|
|
||||||
return 4;
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.SoundEffect;
|
|||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
|
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
|
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.PlaceBlockTracker;
|
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.PlaceBlockTracker;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.ChunkType;
|
import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.Chunk1_9to1_8Type;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -121,7 +121,7 @@ public class WorldPackets {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(PacketWrapper wrapper) throws Exception {
|
public void handle(PacketWrapper wrapper) throws Exception {
|
||||||
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
|
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
|
||||||
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.passthrough(new ChunkType(clientChunks));
|
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.passthrough(new Chunk1_9to1_8Type(clientChunks));
|
||||||
if (chunk.isUnloadPacket()) {
|
if (chunk.isUnloadPacket()) {
|
||||||
wrapper.setId(0x1D);
|
wrapper.setId(0x1D);
|
||||||
|
|
||||||
|
@ -4,23 +4,22 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import us.myles.ViaVersion.api.Via;
|
import us.myles.ViaVersion.api.Via;
|
||||||
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
|
||||||
|
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
|
||||||
import us.myles.ViaVersion.api.type.PartialType;
|
import us.myles.ViaVersion.api.type.PartialType;
|
||||||
import us.myles.ViaVersion.api.type.Type;
|
import us.myles.ViaVersion.api.type.Type;
|
||||||
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType;
|
||||||
|
import us.myles.ViaVersion.api.type.types.version.Types1_8;
|
||||||
|
import us.myles.ViaVersion.api.type.types.version.Types1_9;
|
||||||
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
|
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8;
|
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.ChunkSection1_9to1_8;
|
|
||||||
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
|
import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.ShortBuffer;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class ChunkType extends PartialType<Chunk, ClientChunks> {
|
public class Chunk1_9to1_8Type extends PartialType<Chunk, ClientChunks> {
|
||||||
/**
|
/**
|
||||||
* Amount of sections in a chunks.
|
* Amount of sections in a chunks.
|
||||||
*/
|
*/
|
||||||
@ -34,7 +33,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
|
|||||||
*/
|
*/
|
||||||
private static final int BIOME_DATA_LENGTH = 256;
|
private static final int BIOME_DATA_LENGTH = 256;
|
||||||
|
|
||||||
public ChunkType(ClientChunks chunks) {
|
public Chunk1_9to1_8Type(ClientChunks chunks) {
|
||||||
super(chunks, Chunk.class);
|
super(chunks, Chunk.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
|
|||||||
|
|
||||||
// Data to be read
|
// Data to be read
|
||||||
BitSet usedSections = new BitSet(16);
|
BitSet usedSections = new BitSet(16);
|
||||||
ChunkSection1_9to1_8[] sections = new ChunkSection1_9to1_8[16];
|
ChunkSection[] sections = new ChunkSection[16];
|
||||||
byte[] biomeData = null;
|
byte[] biomeData = null;
|
||||||
|
|
||||||
// Calculate section count from bitmask
|
// Calculate section count from bitmask
|
||||||
@ -87,41 +86,27 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
|
|||||||
// Read blocks
|
// Read blocks
|
||||||
for (int i = 0; i < SECTION_COUNT; i++) {
|
for (int i = 0; i < SECTION_COUNT; i++) {
|
||||||
if (!usedSections.get(i)) continue; // Section not set
|
if (!usedSections.get(i)) continue; // Section not set
|
||||||
ChunkSection1_9to1_8 section = new ChunkSection1_9to1_8();
|
ChunkSection section = Types1_8.CHUNK_SECTION.read(input);
|
||||||
sections[i] = section;
|
sections[i] = section;
|
||||||
|
|
||||||
// Read block data and convert to short buffer
|
if (replacePistons && section.getPalette().contains(36)) {
|
||||||
byte[] blockData = new byte[ChunkSection1_9to1_8.SIZE * 2];
|
section.getPalette().set(section.getPalette().indexOf(36), replacementId);
|
||||||
input.readBytes(blockData);
|
|
||||||
ShortBuffer blockBuf = ByteBuffer.wrap(blockData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
|
||||||
|
|
||||||
for (int j = 0; j < ChunkSection1_9to1_8.SIZE; j++) {
|
|
||||||
int mask = blockBuf.get();
|
|
||||||
int type = mask >> 4;
|
|
||||||
int data = mask & 0xF;
|
|
||||||
if (replacePistons && type == 36)
|
|
||||||
type = replacementId;
|
|
||||||
section.setBlock(j, type, data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read block light
|
// Read block light
|
||||||
for (int i = 0; i < SECTION_COUNT; i++) {
|
for (int i = 0; i < SECTION_COUNT; i++) {
|
||||||
if (!usedSections.get(i)) continue; // Section not set, has no light
|
if (!usedSections.get(i)) continue; // Section not set, has no light
|
||||||
byte[] blockLightArray = new byte[ChunkSection1_9to1_8.LIGHT_LENGTH];
|
sections[i].readBlockLight(input);
|
||||||
input.readBytes(blockLightArray);
|
|
||||||
sections[i].setBlockLight(blockLightArray);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read sky light
|
// Read sky light
|
||||||
int bytesLeft = dataLength - (input.readerIndex() - startIndex);
|
int bytesLeft = dataLength - (input.readerIndex() - startIndex);
|
||||||
if (bytesLeft >= ChunkSection1_9to1_8.LIGHT_LENGTH) {
|
if (bytesLeft >= ChunkSection.LIGHT_LENGTH) {
|
||||||
for (int i = 0; i < SECTION_COUNT; i++) {
|
for (int i = 0; i < SECTION_COUNT; i++) {
|
||||||
if (!usedSections.get(i)) continue; // Section not set, has no light
|
if (!usedSections.get(i)) continue; // Section not set, has no light
|
||||||
byte[] skyLightArray = new byte[ChunkSection1_9to1_8.LIGHT_LENGTH];
|
sections[i].readSkyLight(input);
|
||||||
input.readBytes(skyLightArray);
|
bytesLeft -= ChunkSection.LIGHT_LENGTH;
|
||||||
sections[i].setSkyLight(skyLightArray);
|
|
||||||
bytesLeft -= ChunkSection1_9to1_8.LIGHT_LENGTH;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,13 +136,13 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
|
|||||||
output.writeInt(chunk.getZ());
|
output.writeInt(chunk.getZ());
|
||||||
if (chunk.isUnloadPacket()) return;
|
if (chunk.isUnloadPacket()) return;
|
||||||
output.writeByte(chunk.isGroundUp() ? 0x01 : 0x00);
|
output.writeByte(chunk.isGroundUp() ? 0x01 : 0x00);
|
||||||
Type.VAR_INT.write(output, chunk.getPrimaryBitmask());
|
Type.VAR_INT.write(output, chunk.getBitmask());
|
||||||
|
|
||||||
ByteBuf buf = output.alloc().buffer();
|
ByteBuf buf = output.alloc().buffer();
|
||||||
for (int i = 0; i < SECTION_COUNT; i++) {
|
for (int i = 0; i < SECTION_COUNT; i++) {
|
||||||
ChunkSection1_9to1_8 section = chunk.getSections()[i];
|
ChunkSection section = chunk.getSections()[i];
|
||||||
if (section == null) continue; // Section not set
|
if (section == null) continue; // Section not set
|
||||||
section.writeBlocks(buf);
|
Types1_9.CHUNK_SECTION.write(buf, section);
|
||||||
section.writeBlockLight(buf);
|
section.writeBlockLight(buf);
|
||||||
|
|
||||||
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
if (!section.hasSkyLight()) continue; // No sky light, we're done here.
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren