diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index ec8defa44..42d6cd84c 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -9,6 +9,7 @@ val viaProxy: Configuration by configurations.creating
dependencies {
compileOnly("io.netty:netty-all:4.0.20.Final")
compileOnly("com.google.guava:guava:17.0")
+ compileOnly("com.velocitypowered:velocity-native:3.4.0-SNAPSHOT")
viaProxy("net.raphimc:ViaProxy:[3.0.0,4.0.0)") {
isTransitive = false
diff --git a/common/src/main/java/com/viaversion/viarewind/api/minecraft/ExtendedBlockStorage.java b/common/src/main/java/com/viaversion/viarewind/api/minecraft/ExtendedBlockStorage.java
deleted file mode 100644
index 2b5c3c5a9..000000000
--- a/common/src/main/java/com/viaversion/viarewind/api/minecraft/ExtendedBlockStorage.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
- * Copyright (C) 2018-2025 ViaVersion and contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.viaversion.viarewind.api.minecraft;
-
-import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
-import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
-
-public class ExtendedBlockStorage {
- private final byte[] blockLSBArray = new byte[ChunkSection.SIZE];
-
- private final NibbleArray blockMetadataArray = new NibbleArray(this.blockLSBArray.length);
- private final NibbleArray blockLightArray = new NibbleArray(this.blockLSBArray.length);
-
- private NibbleArray blockMSBArray;
- private NibbleArray skyLightArray;
-
- public ExtendedBlockStorage(final boolean skylight) {
- if (skylight) {
- this.skyLightArray = new NibbleArray(this.blockLSBArray.length);
- }
- }
-
- public void setBlockId(final int x, final int y, final int z, final int value) {
- this.blockLSBArray[ChunkSection.index(x, y, z)] = (byte) (value & 255);
- if (value > 255) {
- this.getOrCreateBlockMSBArray().set(x, y, z, (value & 0xF00) >> 8);
- } else if (this.blockMSBArray != null) {
- this.blockMSBArray.set(x, y, z, 0);
- }
- }
-
- public void setBlockMetadata(final int x, final int y, final int z, final int value) {
- this.blockMetadataArray.set(x, y, z, value);
- }
-
- public boolean hasBlockMSBArray() {
- return this.blockMSBArray != null;
- }
-
- public byte[] getBlockLSBArray() {
- return this.blockLSBArray;
- }
-
- public NibbleArray getOrCreateBlockMSBArray() {
- if (this.blockMSBArray == null) {
- return this.blockMSBArray = new NibbleArray(this.blockLSBArray.length);
- }
- return this.blockMSBArray;
- }
-
- public NibbleArray getBlockMetadataArray() {
- return this.blockMetadataArray;
- }
-
- public NibbleArray getBlockLightArray() {
- return this.blockLightArray;
- }
-
- public NibbleArray getSkyLightArray() {
- return this.skyLightArray;
- }
-}
diff --git a/common/src/main/java/com/viaversion/viarewind/api/type/chunk/BulkChunkType1_7_6.java b/common/src/main/java/com/viaversion/viarewind/api/type/chunk/BulkChunkType1_7_6.java
index 679538ab1..f14ee56d8 100644
--- a/common/src/main/java/com/viaversion/viarewind/api/type/chunk/BulkChunkType1_7_6.java
+++ b/common/src/main/java/com/viaversion/viarewind/api/type/chunk/BulkChunkType1_7_6.java
@@ -17,11 +17,13 @@
*/
package com.viaversion.viarewind.api.type.chunk;
+import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.compressor.CompressorUtil;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
-import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.Type;
-import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
public class BulkChunkType1_7_6 extends Type {
@@ -38,40 +40,51 @@ public Chunk[] read(ByteBuf byteBuf) {
}
@Override
- public void write(ByteBuf byteBuf, Chunk[] chunks) {
+ public void write(ByteBuf buffer, Chunk[] chunks) {
final int chunkCount = chunks.length;
- final int[] chunkX = new int[chunkCount];
- final int[] chunkZ = new int[chunkCount];
- final short[] primaryBitMask = new short[chunkCount];
- final short[] additionalBitMask = new short[chunkCount];
+ final int[] addBitMasks = new int[chunkCount];
+ int totalSize = 0;
+ boolean anySkyLight = false;
- final byte[][] dataArrays = new byte[chunkCount][];
- int dataSize = 0;
+ for (Chunk chunk : chunks) {
+ if (ChunkType1_7_6.hasSkyLight(chunk)) {
+ anySkyLight = true;
+ break;
+ }
+ }
for (int i = 0; i < chunkCount; i++) {
- final Chunk chunk = chunks[i];
- Pair chunkData;
- try {
- chunkData = ChunkType1_7_6.serialize(chunk);
- final byte[] data = chunkData.key();
- dataArrays[i] = data;
- dataSize += data.length;
- } catch (Exception e) {
- throw new RuntimeException("Unable to serialize chunk", e);
- }
- chunkX[i] = chunk.getX();
- chunkZ[i] = chunk.getZ();
- primaryBitMask[i] = (short) chunk.getBitmask();
- additionalBitMask[i] = chunkData.value();
+ Chunk chunk = chunks[i];
+ addBitMasks[i] = ChunkType1_7_6.getAddBitMask(chunk);
+ boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
+
+ totalSize += ChunkType1_7_6.calcSize(
+ chunk.getBitmask(),
+ addBitMasks[i],
+ anySkyLight,
+ biomes
+ );
}
- final byte[] data = new byte[dataSize];
- int destPos = 0;
- for (final byte[] array : dataArrays) {
- System.arraycopy(array, 0, data, destPos, array.length);
- destPos += array.length;
+ final byte[] data = new byte[totalSize];
+ int offset = 0;
+
+ for (int i = 0; i < chunkCount; i++) {
+ Chunk chunk = chunks[i];
+ boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
+
+ offset = ChunkType1_7_6.serialize(
+ chunk,
+ data,
+ offset,
+ addBitMasks[i],
+ anySkyLight,
+ biomes
+ );
}
+ buffer.writeShort(chunkCount);
+
final Deflater deflater = new Deflater();
byte[] compressedData;
int compressedSize;
@@ -84,27 +97,16 @@ public void write(ByteBuf byteBuf, Chunk[] chunks) {
deflater.end();
}
- byteBuf.writeShort(chunkCount);
- byteBuf.writeInt(compressedSize);
-
- boolean skyLight = false;
- for (Chunk chunk : chunks) {
- for (ChunkSection section : chunk.getSections()) {
- if (section != null && section.getLight().hasSkyLight()) {
- skyLight = true;
- break;
- }
- }
- }
-
- byteBuf.writeBoolean(skyLight); // hasSkyLight
- byteBuf.writeBytes(compressedData, 0, compressedSize);
+ buffer.writeInt(compressedSize);
+ buffer.writeBoolean(anySkyLight);
+ buffer.writeBytes(compressedData, 0, compressedSize);;
for (int i = 0; i < chunkCount; i++) {
- byteBuf.writeInt(chunkX[i]);
- byteBuf.writeInt(chunkZ[i]);
- byteBuf.writeShort(primaryBitMask[i]);
- byteBuf.writeShort(additionalBitMask[i]);
+ Chunk chunk = chunks[i];
+ buffer.writeInt(chunk.getX());
+ buffer.writeInt(chunk.getZ());
+ buffer.writeShort(chunk.getBitmask());
+ buffer.writeShort(addBitMasks[i]);
}
}
}
diff --git a/common/src/main/java/com/viaversion/viarewind/api/type/chunk/ChunkType1_7_6.java b/common/src/main/java/com/viaversion/viarewind/api/type/chunk/ChunkType1_7_6.java
index 83577f2ea..636627a69 100644
--- a/common/src/main/java/com/viaversion/viarewind/api/type/chunk/ChunkType1_7_6.java
+++ b/common/src/main/java/com/viaversion/viarewind/api/type/chunk/ChunkType1_7_6.java
@@ -17,14 +17,16 @@
*/
package com.viaversion.viarewind.api.type.chunk;
-import com.viaversion.viarewind.api.minecraft.ExtendedBlockStorage;
+import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.compressor.CompressorUtil;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
+import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
-import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
-import java.io.IOException;
+import io.netty.buffer.Unpooled;
+
+import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
@@ -38,143 +40,163 @@ public ChunkType1_7_6() {
super(Chunk.class);
}
- public static Pair serialize(final Chunk chunk) throws IOException {
- final ExtendedBlockStorage[] storageArrays = new ExtendedBlockStorage[16];
- for (int i = 0; i < storageArrays.length; i++) {
- final ChunkSection section = chunk.getSections()[i];
- if (section != null) {
- final ExtendedBlockStorage storage = storageArrays[i] = new ExtendedBlockStorage(section.getLight().hasSkyLight());
- for (int x = 0; x < 16; x++) {
- for (int z = 0; z < 16; z++) {
- for (int y = 0; y < 16; y++) {
- final int flatBlock = section.palette(PaletteType.BLOCKS).idAt(x, y, z);
- storage.setBlockId(x, y, z, flatBlock >> 4);
- storage.setBlockMetadata(x, y, z, flatBlock & 15);
- }
- }
- }
- storage.getBlockLightArray().setHandle(section.getLight().getBlockLight());
- if (section.getLight().hasSkyLight()) {
- storage.getSkyLightArray().setHandle(section.getLight().getSkyLight());
- }
- }
- }
+ @Override
+ public Chunk read(ByteBuf byteBuf) {
+ throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/Chunk1_7_6Type.java
+ }
+ @Override
+ public void write(ByteBuf buffer, Chunk chunk) {
+ final int bitmask = chunk.getBitmask();
+ final int addBitmask = getAddBitMask(chunk);
+ final boolean hasSkyLight = hasSkyLight(chunk);
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
- final int totalSize = calculateSize(storageArrays, chunk.getBitmask(), biomes);
- final byte[] output = new byte[totalSize];
- int index = 0;
+ final int size = calcSize(bitmask, addBitmask, hasSkyLight, biomes);
+ final byte[] data = new byte[size];
+
+ serialize(chunk, data, 0, addBitmask, hasSkyLight, biomes);
+
+ buffer.writeInt(chunk.getX());
+ buffer.writeInt(chunk.getZ());
+ buffer.writeBoolean(chunk.isFullChunk());
+ buffer.writeShort(bitmask);
+ buffer.writeShort(addBitmask);
+
+ final Deflater deflater = new Deflater();
+ byte[] compressedData;
+ int compressedSize;
+ try {
+ deflater.setInput(data, 0, data.length);
+ deflater.finish();
+ compressedData = new byte[data.length];
+ compressedSize = deflater.deflate(compressedData);
+ } finally {
+ deflater.end();
+ }
+
+ buffer.writeInt(compressedSize);
+ buffer.writeBytes(compressedData, 0, compressedSize);
+ }
- for (int i = 0; i < storageArrays.length; i++) {
- if ((chunk.getBitmask() & 1 << i) != 0) {
- final byte[] blockLSBArray = storageArrays[i].getBlockLSBArray();
- System.arraycopy(blockLSBArray, 0, output, index, blockLSBArray.length);
- index += blockLSBArray.length;
+ public static int serialize(Chunk chunk, byte[] output, int offset, int addBitmask, boolean writeSkyLight, boolean biomes) {
+ final ChunkSection[] sections = chunk.getSections();
+ final int bitmask = chunk.getBitmask();
+
+ for (int i = 0; i < 16; i++) {
+ if ((bitmask & (1 << i)) != 0) {
+ final ChunkSection section = sections[i];
+ final DataPalette palette = section.palette(PaletteType.BLOCKS);
+ for (int j = 0; j < SIZE; j++) {
+ final int block = palette.idAt(j);
+ output[offset++] = (byte) ((block >> 4) & 0xFF);
+ }
}
}
- for (int i = 0; i < storageArrays.length; i++) {
- if ((chunk.getBitmask() & 1 << i) != 0) {
- final byte[] blockMetadataArray = storageArrays[i].getBlockMetadataArray().getHandle();
- System.arraycopy(blockMetadataArray, 0, output, index, blockMetadataArray.length);
- index += blockMetadataArray.length;
+ for (int i = 0; i < 16; i++) {
+ if ((bitmask & (1 << i)) != 0) {
+ final ChunkSection section = sections[i];
+ final DataPalette palette = section.palette(PaletteType.BLOCKS);
+ for (int j = 0; j < ChunkSection.SIZE; j += 2) {
+ final int meta1 = palette.idAt(j) & 0xF;
+ final int meta2 = palette.idAt(j + 1) & 0xF;
+ output[offset++] = (byte) (meta1 | (meta2 << 4));
+ }
}
}
- for (int i = 0; i < storageArrays.length; i++) {
- if ((chunk.getBitmask() & 1 << i) != 0) {
- final byte[] blockLightArray = storageArrays[i].getBlockLightArray().getHandle();
- System.arraycopy(blockLightArray, 0, output, index, blockLightArray.length);
- index += blockLightArray.length;
+ for (int i = 0; i < 16; i++) {
+ if ((bitmask & (1 << i)) != 0) {
+ final byte[] blockLight = sections[i].getLight().getBlockLight();
+ System.arraycopy(blockLight, 0, output, offset, LIGHT_LENGTH);
+ offset += LIGHT_LENGTH;
}
}
- for (int i = 0; i < storageArrays.length; i++) {
- if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].getSkyLightArray() != null) {
- final byte[] skyLightArray = storageArrays[i].getSkyLightArray().getHandle();
- System.arraycopy(skyLightArray, 0, output, index, skyLightArray.length);
- index += skyLightArray.length;
+ if (writeSkyLight) {
+ for (int i = 0; i < 16; i++) {
+ if ((bitmask & (1 << i)) != 0) {
+ if (sections[i].getLight().hasSkyLight()) {
+ final byte[] skyLight = sections[i].getLight().getSkyLight();
+ System.arraycopy(skyLight, 0, output, offset, LIGHT_LENGTH);
+ }
+ offset += LIGHT_LENGTH;
+ }
}
}
- short additionalBitMask = 0;
- for (int i = 0; i < storageArrays.length; i++) {
- if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].hasBlockMSBArray()) {
- additionalBitMask |= (short) (1 << i);
- final byte[] blockMSBArray = storageArrays[i].getOrCreateBlockMSBArray().getHandle();
- System.arraycopy(blockMSBArray, 0, output, index, blockMSBArray.length);
- index += blockMSBArray.length;
+ if (addBitmask != 0) {
+ for (int i = 0; i < 16; i++) {
+ if ((bitmask & (1 << i)) != 0 && (addBitmask & (1 << i)) != 0) {
+ final ChunkSection section = sections[i];
+ final DataPalette palette = section.palette(PaletteType.BLOCKS);
+ for (int j = 0; j < SIZE; j += 2) {
+ final int add1 = (palette.idAt(j) >> 12) & 0xF;
+ final int add2 = (palette.idAt(j + 1) >> 12) & 0xF;
+ output[offset++] = (byte) (add1 | (add2 << 4));
+ }
+ }
}
}
- if (biomes) {
- for (int biome : chunk.getBiomeData()) {
- output[index++] = (byte) biome;
+ if (biomes && chunk.getBiomeData() != null) {
+ final int[] biomeData = chunk.getBiomeData();
+ for (int biome : biomeData) {
+ output[offset++] = (byte) biome;
}
}
- return new Pair<>(output, additionalBitMask);
+ return offset;
}
- private static int calculateSize(final ExtendedBlockStorage[] storageArrays, final int bitmask, final boolean biomes) {
- int totalSize = 0;
- for (int i = 0; i < storageArrays.length; i++) {
- if ((bitmask & 1 << i) != 0) {
- totalSize += SIZE; // Block lsb array
- totalSize += SIZE / 2; // Block metadata array
- totalSize += LIGHT_LENGTH; // Block light array
+ public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
+ int size = 0;
+ int sections = Integer.bitCount(bitmask);
- if (storageArrays[i].getSkyLightArray() != null) {
- totalSize += LIGHT_LENGTH;
- }
+ size += sections * SIZE;
+ size += sections * (SIZE / 2);
+ size += sections * LIGHT_LENGTH;
- if (storageArrays[i].hasBlockMSBArray()) {
- totalSize += SIZE / 2; // Block msb array
- }
- }
+ if (hasSkyLight) {
+ size += sections * LIGHT_LENGTH;
+ }
+
+ if (addBitmask != 0) {
+ size += Integer.bitCount(addBitmask) * (SIZE / 2);
}
+
if (biomes) {
- totalSize += 256;
+ size += 256;
}
- return totalSize;
- }
- @Override
- public Chunk read(ByteBuf byteBuf) {
- throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/Chunk1_7_6Type.java
+ return size;
}
- @Override
- public void write(ByteBuf output, Chunk chunk) {
- Pair chunkData;
- try {
- chunkData = serialize(chunk);
- } catch (IOException e) {
- throw new RuntimeException("Unable to serialize chunk", e);
+ public static int getAddBitMask(Chunk chunk) {
+ int addBitMask = 0;
+ for (int i = 0; i < 16; i++) {
+ if ((chunk.getBitmask() & (1 << i)) != 0) {
+ final ChunkSection section = chunk.getSections()[i];
+ final DataPalette palette = section.palette(PaletteType.BLOCKS);
+ for (int j = 0; j < SIZE; j++) {
+ final int id = palette.idAt(j);
+ if ((id >> 12) != 0) {
+ addBitMask |= (1 << i);
+ break;
+ }
+ }
+ }
}
- final byte[] data = chunkData.key();
- final short additionalBitMask = chunkData.value();
+ return addBitMask;
+ }
- final Deflater deflater = new Deflater();
- byte[] compressedData;
- int compressedSize;
- try {
- deflater.setInput(data, 0, data.length);
- deflater.finish();
- compressedData = new byte[data.length];
- compressedSize = deflater.deflate(compressedData);
- } finally {
- deflater.end();
+ public static boolean hasSkyLight(Chunk chunk) {
+ for (ChunkSection section : chunk.getSections()) {
+ if (section != null && section.getLight().hasSkyLight()) {
+ return true;
+ }
}
-
- output.writeInt(chunk.getX());
- output.writeInt(chunk.getZ());
- output.writeBoolean(chunk.isFullChunk());
- output.writeShort(chunk.getBitmask());
- output.writeShort(additionalBitMask);
- output.writeInt(compressedSize);
- output.writeBytes(compressedData, 0, compressedSize);
+ return false;
}
-
}
diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/Protocol1_8To1_7_6_10.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/Protocol1_8To1_7_6_10.java
index cb6fe94a4..1ce4d13f8 100644
--- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/Protocol1_8To1_7_6_10.java
+++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/Protocol1_8To1_7_6_10.java
@@ -54,7 +54,6 @@
import java.util.concurrent.TimeUnit;
public class Protocol1_8To1_7_6_10 extends BackwardsProtocol {
-
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.8", "1.7.10");
private final BlockItemPacketRewriter1_8 itemRewriter = new BlockItemPacketRewriter1_8(this);
diff --git a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/provider/compression/CompressionDecoder.java b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/provider/compression/CompressionDecoder.java
index 7a81b717e..91e25971f 100644
--- a/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/provider/compression/CompressionDecoder.java
+++ b/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/provider/compression/CompressionDecoder.java
@@ -17,18 +17,15 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
+import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.compressor.CompressorUtil;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
-import java.util.zip.Inflater;
public class CompressionDecoder extends MessageToMessageDecoder {
- private final Inflater inflater = new Inflater();
-
private int threshold;
public CompressionDecoder(final int threshold) {
@@ -55,21 +52,12 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List