Don't allocate bufs in 1.18/1.20 chunk types

Unlike Mojang, we don't sneak a byte array of data outside of the actual packet reading, so there's no reason to not just use the original buffer.
Slicing doesn't allocate anything while also setting the reader index correctly, as also used in the previous types
This commit is contained in:
Nassim Jahnke 2024-11-05 12:03:16 +01:00
parent 77f0826ec8
commit deab6a0cac
No known key found for this signature in database
GPG Key ID: EF6771C01F6EF02F
5 changed files with 73 additions and 50 deletions

View File

@ -32,6 +32,15 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
private static final int VALUE_BITS = 0x7F;
private static final int MULTI_BYTE_BITS = ~VALUE_BITS;
private static final int MAX_BYTES = 5;
private static final int[] VAR_INT_LENGTHS = new int[65];
static {
// Copied from Velocity https://github.com/PaperMC/Velocity/blob/08a42b3723633ea5eb6b96c0bb42180f3c2b07eb/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java#L166
for (int i = 0; i <= 32; ++i) {
VAR_INT_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d);
}
VAR_INT_LENGTHS[32] = 1; // Special case for the number 0.
}
public VarIntType() {
super("VarInt", Integer.class);
@ -61,15 +70,6 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
buffer.writeByte(value);
}
public static int varIntLength(int value) {
int length = 1;
while ((value & MULTI_BYTE_BITS) != 0) {
length++;
value >>>= 7;
}
return length;
}
/**
* @deprecated use {@link #readPrimitive(ByteBuf)} for manual reading to avoid wrapping
*/
@ -97,4 +97,8 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
}
throw new UnsupportedOperationException();
}
public static int varIntLength(final int value) {
return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)];
}
}

View File

@ -22,6 +22,7 @@
*/
package com.viaversion.viaversion.api.type.types.chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
@ -54,4 +55,14 @@ public final class ChunkSectionType1_18 extends Type<ChunkSection> {
blockPaletteType.write(buffer, section.palette(PaletteType.BLOCKS));
biomePaletteType.write(buffer, section.palette(PaletteType.BIOMES));
}
public int serializedSize(final Chunk chunk) {
int length = 0;
for (final ChunkSection section : chunk.getSections()) {
length += Short.BYTES
+ blockPaletteType.serializedSize(section.palette(PaletteType.BLOCKS))
+ biomePaletteType.serializedSize(section.palette(PaletteType.BIOMES));
}
return length;
}
}

View File

@ -52,15 +52,11 @@ public final class ChunkType1_18 extends Type<Chunk> {
final CompoundTag heightMap = Types.NAMED_COMPOUND_TAG.read(buffer);
// Read sections
final ByteBuf sectionsBuf = buffer.readBytes(Types.VAR_INT.readPrimitive(buffer));
final ByteBuf sectionsBuf = buffer.readSlice(Types.VAR_INT.readPrimitive(buffer));
final ChunkSection[] sections = new ChunkSection[ySectionCount];
try {
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
} finally {
sectionsBuf.release();
}
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
final List<BlockEntity> blockEntities = new ArrayList<>(blockEntitiesLength);
@ -78,16 +74,9 @@ public final class ChunkType1_18 extends Type<Chunk> {
Types.NAMED_COMPOUND_TAG.write(buffer, chunk.getHeightMap());
final ByteBuf sectionBuffer = buffer.alloc().buffer();
try {
Types.VAR_INT.writePrimitive(buffer, sectionType.serializedSize(chunk));
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(sectionBuffer, section);
}
sectionBuffer.readerIndex(0);
Types.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
buffer.writeBytes(sectionBuffer);
} finally {
sectionBuffer.release(); // release buffer
sectionType.write(buffer, section);
}
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());

View File

@ -52,15 +52,11 @@ public final class ChunkType1_20_2 extends Type<Chunk> {
final CompoundTag heightMap = Types.COMPOUND_TAG.read(buffer);
// Read sections
final ByteBuf sectionsBuf = buffer.readBytes(Types.VAR_INT.readPrimitive(buffer));
final ByteBuf sectionsBuf = buffer.readSlice(Types.VAR_INT.readPrimitive(buffer));
final ChunkSection[] sections = new ChunkSection[ySectionCount];
try {
for (int i = 0; i < ySectionCount; i++) {
sections[i] = sectionType.read(sectionsBuf);
}
} finally {
sectionsBuf.release();
}
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
final List<BlockEntity> blockEntities = new ArrayList<>(blockEntitiesLength);
@ -78,16 +74,9 @@ public final class ChunkType1_20_2 extends Type<Chunk> {
Types.COMPOUND_TAG.write(buffer, chunk.getHeightMap());
final ByteBuf sectionBuffer = buffer.alloc().buffer();
try {
Types.VAR_INT.writePrimitive(buffer, sectionType.serializedSize(chunk));
for (final ChunkSection section : chunk.getSections()) {
sectionType.write(sectionBuffer, section);
}
sectionBuffer.readerIndex(0);
Types.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
buffer.writeBytes(sectionBuffer);
} finally {
sectionBuffer.release(); // release buffer
sectionType.write(buffer, section);
}
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());

View File

@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.minecraft.chunks.DataPaletteImpl;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.VarIntType;
import com.viaversion.viaversion.util.CompactArrayUtil;
import com.viaversion.viaversion.util.MathUtil;
import io.netty.buffer.ByteBuf;
@ -94,13 +95,7 @@ public final class PaletteType1_18 extends Type<DataPalette> {
return;
}
// 1, 2, and 3 bit linear block palettes can't be read by the client
final int min = type == PaletteType.BLOCKS ? 4 : 1;
int bitsPerValue = Math.max(min, MathUtil.ceilLog2(size));
if (bitsPerValue > type.highestBitsPerValue()) {
bitsPerValue = globalPaletteBits;
}
final int bitsPerValue = bitsPerValue(size);
buffer.writeByte(bitsPerValue);
if (bitsPerValue != globalPaletteBits) {
@ -113,4 +108,39 @@ public final class PaletteType1_18 extends Type<DataPalette> {
Types.LONG_ARRAY_PRIMITIVE.write(buffer, CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, type.size(), bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt));
}
private int bitsPerValue(final int size) {
// 1, 2, and 3 bit linear block palettes can't be read by the client
final int min = type == PaletteType.BLOCKS ? 4 : 1;
int bitsPerValue = Math.max(min, MathUtil.ceilLog2(size));
if (bitsPerValue > type.highestBitsPerValue()) {
bitsPerValue = globalPaletteBits;
}
return bitsPerValue;
}
public int serializedSize(final DataPalette palette) {
// This is a bit of extra work, but worth it to avoid otherwise having to allocate and write to an extra buffer.
// On top of saving memory, it provides small but measurable speedup compared to writing to a separate buffer and then back
final int size = palette.size();
final int bitsPerValue = bitsPerValue(size);
int serializedTypesSize = 0;
int serializedValuesSize = 1; // At least one byte for 0 length
if (size == 1) {
serializedTypesSize = VarIntType.varIntLength(palette.idByIndex(0));
} else {
if (bitsPerValue != globalPaletteBits) {
serializedTypesSize = VarIntType.varIntLength(size);
for (int i = 0; i < size; i++) {
serializedTypesSize += VarIntType.varIntLength(palette.idByIndex(i));
}
}
final int valuesPerLong = 64 / bitsPerValue;
final int values = (type.size() + valuesPerLong - 1) / valuesPerLong;
serializedValuesSize = VarIntType.varIntLength(values) + (Long.BYTES * values);
}
return Byte.BYTES + serializedTypesSize + serializedValuesSize;
}
}