Merge pull request #1092 from Gerrygames/chunk-refactor

Chunk refactor
This commit is contained in:
Myles 2018-11-17 13:14:27 +00:00 committed by GitHub
commit 66d836739c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 575 additions and 1549 deletions

View File

@ -0,0 +1,24 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@AllArgsConstructor
@Data
public class BaseChunk implements Chunk {
protected int x;
protected int z;
protected boolean groundUp;
protected int bitmask;
protected ChunkSection[] sections;
protected byte[] biomeData;
protected List<CompoundTag> blockEntities;
@Override
public boolean isBiomeData() {
return biomeData != null;
}
}

View File

@ -9,15 +9,15 @@ public interface Chunk {
int getZ();
ChunkSection[] getSections();
boolean isGroundUp();
boolean isBiomeData();
byte[] getBiomeData();
int getBitmask();
ChunkSection[] getSections();
byte[] getBiomeData();
List<CompoundTag> getBlockEntities();
boolean isGroundUp();
}

View File

@ -0,0 +1,41 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
public class Chunk1_8 extends BaseChunk {
@Getter
private boolean unloadPacket = false;
public Chunk1_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.
*
* @param x coord
* @param z coord
*/
public Chunk1_8(int x, int z) {
this(x, z, true, 0, new ChunkSection[16], null, new ArrayList<CompoundTag>());
this.unloadPacket = true;
}
/**
* Does this chunks have biome data
*
* @return True if the chunks has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && groundUp;
}
@Override
public boolean isBiomeData() {
return biomeData != null;
}
}

View File

@ -1,73 +1,221 @@
package us.myles.ViaVersion.api.minecraft.chunks;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public interface ChunkSection {
public class ChunkSection {
/**
* Gets a block state id (&lt; 1.13: block_id &lt;&lt; 4 | data &amp; 0xF)
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @return Block raw id
* Size (dimensions) of blocks in a chunks section.
*/
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)
private List<Integer> palette = new ArrayList<>();
private Map<Integer, Integer> inversePalette = new HashMap<>();
private final int[] blocks;
private NibbleArray blockLight;
private NibbleArray skyLight;
@Getter
@Setter
private int nonAirBlocksCount;
public ChunkSection() {
this.blocks = new int[SIZE];
this.blockLight = new NibbleArray(SIZE);
palette.add(0);
inversePalette.put(0, 0);
}
/**
* Set a block in the chunks
* This method will not update non-air blocks count
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @param type The block id
* @param type The type 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];
}
public int getPaletteSize() {
return palette.size();
}
public int getPaletteEntry(int index) {
if (index < 0 || index >= palette.size()) throw new IndexOutOfBoundsException();
return palette.get(index);
}
public void setPaletteEntry(int index, int id) {
if (index < 0 || index >= palette.size()) throw new IndexOutOfBoundsException();
palette.set(index, id);
inversePalette.put(id, index);
}
public void replacePaletteEntry(int oldId, int newId) {
Integer index = inversePalette.remove(oldId);
if (index == null) return;
inversePalette.put(newId, index);
for (int i = 0; i < palette.size(); i++) {
if (palette.get(i) == oldId) palette.set(i, newId);
}
}
public void addPaletteEntry(int id) {
inversePalette.put(id, palette.size());
palette.add(id);
}
public void clearPalette() {
palette.clear();
inversePalette.clear();
}
/**
* Set a block state in the chunk
* This method will not update non-air blocks count
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @param blockState The block state id
* @param idx Index
* @param id The raw or flat id of the block
*/
void setFlatBlock(int x, int y, int z, int blockState);
public void setFlatBlock(int idx, int id) {
Integer index = inversePalette.get(id);
if (index == null) {
index = palette.size();
palette.add(id);
inversePalette.put(id, index);
}
blocks[idx] = index;
}
/**
* Gets a block id (without data)
* /!\ YOU SHOULD NOT USE THIS ON 1.13
* Set the block light array
*
* @param x Block X
* @param y Block Y
* @param z Block Z
* @return Block id (without data)
* @param data The value to set the block light to
*/
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 &lt; 1.13 format to a buffer.
* Set the sky light array
*
* @param output The buffer to write to.
* @throws Exception Throws if it failed to write.
* @param data The value to set the sky light to
*/
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.
* @throws Exception Throws if it failed to write.
* @param output The buffer to write to
*/
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();
/**
* Check if sky light is present
*
* @return True if skylight is present
*/
public boolean hasSkyLight() {
return skyLight != null;
}
void writeSkyLight(ByteBuf output) throws Exception;
List<Integer> getPalette();
public boolean hasBlockLight() {
return blockLight != null;
}
}

View File

@ -0,0 +1,109 @@
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;
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();
// 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
chunkSection.clearPalette();
for (int i = 0; i < paletteLength; i++) {
chunkSection.addPaletteEntry(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 {
int bitsPerBlock = 4;
while (chunkSection.getPaletteSize() > 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, chunkSection.getPaletteSize());
for (int i = 0; i < chunkSection.getPaletteSize(); i++) {
Type.VAR_INT.write(buffer, chunkSection.getPaletteEntry(i));
}
}
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);
}
}
}

View File

@ -0,0 +1,40 @@
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.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
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();
chunkSection.clearPalette();
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 UnsupportedOperationException();
}
}

View File

@ -0,0 +1,106 @@
package us.myles.ViaVersion.api.type.types.version;
import com.google.common.collect.BiMap;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
import us.myles.ViaVersion.api.type.Type;
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();
// 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
chunkSection.clearPalette();
for (int i = 0; i < paletteLength; i++) {
if (bitsPerBlock != 13) {
chunkSection.addPaletteEntry(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 {
int bitsPerBlock = 4;
while (chunkSection.getPaletteSize() > 1 << bitsPerBlock) {
bitsPerBlock += 1;
}
long maxEntryValue = (1L << bitsPerBlock) - 1;
buffer.writeByte(bitsPerBlock);
// Write pallet
Type.VAR_INT.write(buffer, chunkSection.getPaletteSize());
for (int i = 0; i < chunkSection.getPaletteSize(); i++) {
Type.VAR_INT.write(buffer, chunkSection.getPaletteEntry(i));
}
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);
}
}
}

View File

@ -1,5 +1,6 @@
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.type.Type;
@ -15,4 +16,6 @@ public class Types1_13 {
* Metadata type for 1.13
*/
public static final Type<Metadata> METADATA = new Metadata1_13Type();
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_13();
}

View File

@ -1,5 +1,6 @@
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.type.Type;
@ -15,4 +16,6 @@ public class Types1_8 {
* Metadata type for 1.8
*/
public static final Type<Metadata> METADATA = new Metadata1_8Type();
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_8();
}

View File

@ -1,5 +1,6 @@
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.type.Type;
@ -15,4 +16,6 @@ public class Types1_9 {
* Metadata type for 1.9
*/
public static final Type<Metadata> METADATA = new Metadata1_9Type();
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_9();
}

View File

@ -27,16 +27,11 @@ public class WorldPackets {
Chunk chunk = wrapper.passthrough(new Chunk1_13Type(clientWorld));
for (ChunkSection section : chunk.getSections()) {
if (section != null) {
for (int i = 0; i < section.getPalette().size(); i++) {
section.getPalette().set(
i,
Protocol1_13_1To1_13.getNewBlockStateId(section.getPalette().get(i))
);
}
if (section == null) continue;
for (int i = 0; i < section.getPaletteSize(); i++) {
section.setPaletteEntry(i, Protocol1_13_1To1_13.getNewBlockStateId(section.getPaletteEntry(i)));
}
}
}
});
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -2,6 +2,7 @@ package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.packets;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.common.base.Optional;
import com.google.common.collect.BiMap;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
@ -241,20 +242,20 @@ public class WorldPackets {
boolean willStoreAnyBlock = false;
for (int p = 0; p < section.getPalette().size(); p++) {
int old = section.getPalette().get(p);
for (int p = 0; p < section.getPaletteSize(); p++) {
int old = section.getPaletteEntry(p);
int newId = toNewId(old);
if (storage.isWelcome(newId)) {
willStoreAnyBlock = true;
}
section.getPalette().set(p, newId);
section.setPaletteEntry(p, newId);
}
if (willStoreAnyBlock) {
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
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)) {
storage.store(new Position(
(long) (x + (chunk.getX() << 4)),

View File

@ -4,13 +4,13 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.minecraft.Environment;
import us.myles.ViaVersion.api.minecraft.chunks.BaseChunk;
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.Type;
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.protocols.protocol1_13to1_12_2.chunks.ChunkSection1_13;
import us.myles.ViaVersion.api.type.types.version.Types1_13;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import java.util.ArrayList;
@ -33,7 +33,7 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
Type.VAR_INT.read(input);
BitSet usedSections = new BitSet(16);
ChunkSection1_13[] sections = new ChunkSection1_13[16];
ChunkSection[] sections = new ChunkSection[16];
// Calculate section count from bitmask
for (int i = 0; i < 16; i++) {
if ((primaryBitmask & (1 << i)) != 0) {
@ -44,9 +44,8 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
// Read sections
for (int i = 0; i < 16; i++) {
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;
section.readBlocks(input);
section.readBlockLight(input);
if (world.getEnvironment() == Environment.NORMAL) {
section.readSkyLight(input);
@ -71,7 +70,7 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
}
}
return new Chunk1_13(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
return new BaseChunk(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
}
@Override
@ -86,7 +85,7 @@ public class Chunk1_13Type extends PartialType<Chunk, ClientWorld> {
for (int i = 0; i < 16; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set
section.writeBlocks1_13(buf);
Types1_13.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here.

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -4,13 +4,13 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.minecraft.Environment;
import us.myles.ViaVersion.api.minecraft.chunks.BaseChunk;
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.Type;
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.protocols.protocol1_9_1_2to1_9_3_4.chunks.ChunkSection1_9_3_4;
import us.myles.ViaVersion.api.type.types.version.Types1_9;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import java.util.ArrayList;
@ -34,7 +34,7 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
Type.VAR_INT.read(input);
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
for (int i = 0; i < 16; i++) {
if ((primaryBitmask & (1 << i)) != 0) {
@ -45,9 +45,8 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
// Read sections
for (int i = 0; i < 16; i++) {
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;
section.readBlocks(input);
section.readBlockLight(input);
if (world.getEnvironment() == Environment.NORMAL) {
section.readSkyLight(input);
@ -69,7 +68,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 BaseChunk(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, nbtData);
}
@Override
@ -84,7 +83,7 @@ public class Chunk1_9_3_4Type extends PartialType<Chunk, ClientWorld> {
for (int i = 0; i < 16; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set
section.writeBlocks(buf);
Types1_9.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here.

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -4,15 +4,15 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.minecraft.Environment;
import us.myles.ViaVersion.api.minecraft.chunks.BaseChunk;
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.Type;
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.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 java.util.ArrayList;
@ -37,7 +37,7 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
int size = Type.VAR_INT.read(input);
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
for (int i = 0; i < 16; i++) {
if ((primaryBitmask & (1 << i)) != 0) {
@ -48,17 +48,14 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
// Read sections
for (int i = 0; i < 16; i++) {
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;
section.readBlocks(input);
section.readBlockLight(input);
if (world.getEnvironment() == Environment.NORMAL) {
section.readSkyLight(input);
}
if (replacePistons) {
if (section.getPalette().contains(36)) {
section.getPalette().set(section.getPalette().indexOf(36), replacementId);
}
section.replacePaletteEntry(36, replacementId);
}
}
@ -67,7 +64,7 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
input.readBytes(biomeData);
}
return new Chunk1_9_1_2(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, new ArrayList<CompoundTag>());
return new BaseChunk(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, new ArrayList<CompoundTag>());
}
@Override
@ -82,7 +79,7 @@ public class Chunk1_9_1_2Type extends PartialType<Chunk, ClientWorld> {
for (int i = 0; i < 16; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue; // Section not set
section.writeBlocks(buf);
Types1_9.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here.

View File

@ -1,55 +0,0 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
@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;
/**
* Chunk unload.
*
* @param x coord
* @param z coord
*/
public Chunk1_9to1_8(int x, int z) {
this(x, z, true, 0, new ChunkSection1_9to1_8[16], null,
new ArrayList<CompoundTag>());
this.unloadPacket = true;
}
/**
* Does this chunks have biome data
*
* @return True if the chunks has biome data
*/
public boolean hasBiomeData() {
return biomeData != null && groundUp;
}
@Override
public boolean isBiomeData() {
return biomeData != null;
}
@Override
public int getBitmask() {
return primaryBitmask;
}
}

View File

@ -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;
}
}

View File

@ -16,7 +16,7 @@ import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.ItemRewriter;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.CommandBlockProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.Effect;
@ -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.EntityTracker;
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.util.List;
@ -121,16 +121,20 @@ public class WorldPackets {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientChunks clientChunks = wrapper.user().get(ClientChunks.class);
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.passthrough(new ChunkType(clientChunks));
Chunk1_9to1_8Type type = new Chunk1_9to1_8Type(clientChunks);
Chunk1_8 chunk = (Chunk1_8) wrapper.read(type);
if (chunk.isUnloadPacket()) {
wrapper.setId(0x1D);
wrapper.write(Type.INT, chunk.getX());
wrapper.write(Type.INT, chunk.getZ());
// Remove commandBlocks on chunk unload
CommandBlockProvider provider = Via.getManager().getProviders().get(CommandBlockProvider.class);
provider.unloadChunk(wrapper.user(), chunk.getX(), chunk.getZ());
} else {
wrapper.write(type, chunk);
// eat any other data (Usually happens with unload packets)
}
// eat any other data (Usually happens with unload packets)
wrapper.read(Type.REMAINING_BYTES);
}
});

View File

@ -4,23 +4,22 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import io.netty.buffer.ByteBuf;
import us.myles.ViaVersion.api.Via;
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.Type;
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.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.ChunkSection1_9to1_8;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk1_8;
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.BitSet;
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.
*/
@ -34,7 +33,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
*/
private static final int BIOME_DATA_LENGTH = 256;
public ChunkType(ClientChunks chunks) {
public Chunk1_9to1_8Type(ClientChunks chunks) {
super(chunks, Chunk.class);
}
@ -61,7 +60,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
// Data to be read
BitSet usedSections = new BitSet(16);
ChunkSection1_9to1_8[] sections = new ChunkSection1_9to1_8[16];
ChunkSection[] sections = new ChunkSection[16];
byte[] biomeData = null;
// Calculate section count from bitmask
@ -78,7 +77,7 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
if (sectionCount == 0 && groundUp && !isBulkPacket && param.getLoadedChunks().contains(chunkHash)) {
// This is a chunks unload packet
param.getLoadedChunks().remove(chunkHash);
return new Chunk1_9to1_8(chunkX, chunkZ);
return new Chunk1_8(chunkX, chunkZ);
}
int startIndex = input.readerIndex();
@ -87,41 +86,27 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
// Read blocks
for (int i = 0; i < SECTION_COUNT; i++) {
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;
// Read block data and convert to short buffer
byte[] blockData = new byte[ChunkSection1_9to1_8.SIZE * 2];
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);
if (replacePistons) {
section.replacePaletteEntry(36, replacementId);
}
}
// Read block light
for (int i = 0; i < SECTION_COUNT; i++) {
if (!usedSections.get(i)) continue; // Section not set, has no light
byte[] blockLightArray = new byte[ChunkSection1_9to1_8.LIGHT_LENGTH];
input.readBytes(blockLightArray);
sections[i].setBlockLight(blockLightArray);
sections[i].readBlockLight(input);
}
// Read sky light
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++) {
if (!usedSections.get(i)) continue; // Section not set, has no light
byte[] skyLightArray = new byte[ChunkSection1_9to1_8.LIGHT_LENGTH];
input.readBytes(skyLightArray);
sections[i].setSkyLight(skyLightArray);
bytesLeft -= ChunkSection1_9to1_8.LIGHT_LENGTH;
sections[i].readSkyLight(input);
bytesLeft -= ChunkSection.LIGHT_LENGTH;
}
}
@ -138,26 +123,26 @@ public class ChunkType extends PartialType<Chunk, ClientChunks> {
}
// Return chunks
return new Chunk1_9to1_8(chunkX, chunkZ, groundUp, bitmask, sections, biomeData, new ArrayList<CompoundTag>());
return new Chunk1_8(chunkX, chunkZ, groundUp, bitmask, sections, biomeData, new ArrayList<CompoundTag>());
}
@Override
public void write(ByteBuf output, ClientChunks param, Chunk input) throws Exception {
if (!(input instanceof Chunk1_9to1_8)) throw new Exception("Incompatible chunk, " + input.getClass());
if (!(input instanceof Chunk1_8)) throw new Exception("Incompatible chunk, " + input.getClass());
Chunk1_9to1_8 chunk = (Chunk1_9to1_8) input;
Chunk1_8 chunk = (Chunk1_8) input;
// Write primary info
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
if (chunk.isUnloadPacket()) return;
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();
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
section.writeBlocks(buf);
Types1_9.CHUNK_SECTION.write(buf, section);
section.writeBlockLight(buf);
if (!section.hasSkyLight()) continue; // No sky light, we're done here.