mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2024-11-22 01:55:47 +01:00
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:
parent
77f0826ec8
commit
deab6a0cac
@ -32,6 +32,15 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
|
|||||||
private static final int VALUE_BITS = 0x7F;
|
private static final int VALUE_BITS = 0x7F;
|
||||||
private static final int MULTI_BYTE_BITS = ~VALUE_BITS;
|
private static final int MULTI_BYTE_BITS = ~VALUE_BITS;
|
||||||
private static final int MAX_BYTES = 5;
|
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() {
|
public VarIntType() {
|
||||||
super("VarInt", Integer.class);
|
super("VarInt", Integer.class);
|
||||||
@ -61,15 +70,6 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
|
|||||||
buffer.writeByte(value);
|
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
|
* @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();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int varIntLength(final int value) {
|
||||||
|
return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.viaversion.viaversion.api.type.types.chunk;
|
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.ChunkSection;
|
||||||
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
|
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionImpl;
|
||||||
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
|
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));
|
blockPaletteType.write(buffer, section.palette(PaletteType.BLOCKS));
|
||||||
biomePaletteType.write(buffer, section.palette(PaletteType.BIOMES));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,15 +52,11 @@ public final class ChunkType1_18 extends Type<Chunk> {
|
|||||||
final CompoundTag heightMap = Types.NAMED_COMPOUND_TAG.read(buffer);
|
final CompoundTag heightMap = Types.NAMED_COMPOUND_TAG.read(buffer);
|
||||||
|
|
||||||
// Read sections
|
// 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];
|
final ChunkSection[] sections = new ChunkSection[ySectionCount];
|
||||||
try {
|
|
||||||
for (int i = 0; i < ySectionCount; i++) {
|
for (int i = 0; i < ySectionCount; i++) {
|
||||||
sections[i] = sectionType.read(sectionsBuf);
|
sections[i] = sectionType.read(sectionsBuf);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
sectionsBuf.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
|
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
|
||||||
final List<BlockEntity> blockEntities = new ArrayList<>(blockEntitiesLength);
|
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());
|
Types.NAMED_COMPOUND_TAG.write(buffer, chunk.getHeightMap());
|
||||||
|
|
||||||
final ByteBuf sectionBuffer = buffer.alloc().buffer();
|
Types.VAR_INT.writePrimitive(buffer, sectionType.serializedSize(chunk));
|
||||||
try {
|
|
||||||
for (final ChunkSection section : chunk.getSections()) {
|
for (final ChunkSection section : chunk.getSections()) {
|
||||||
sectionType.write(sectionBuffer, section);
|
sectionType.write(buffer, section);
|
||||||
}
|
|
||||||
sectionBuffer.readerIndex(0);
|
|
||||||
Types.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
|
|
||||||
buffer.writeBytes(sectionBuffer);
|
|
||||||
} finally {
|
|
||||||
sectionBuffer.release(); // release buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
|
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
|
||||||
|
@ -52,15 +52,11 @@ public final class ChunkType1_20_2 extends Type<Chunk> {
|
|||||||
final CompoundTag heightMap = Types.COMPOUND_TAG.read(buffer);
|
final CompoundTag heightMap = Types.COMPOUND_TAG.read(buffer);
|
||||||
|
|
||||||
// Read sections
|
// 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];
|
final ChunkSection[] sections = new ChunkSection[ySectionCount];
|
||||||
try {
|
|
||||||
for (int i = 0; i < ySectionCount; i++) {
|
for (int i = 0; i < ySectionCount; i++) {
|
||||||
sections[i] = sectionType.read(sectionsBuf);
|
sections[i] = sectionType.read(sectionsBuf);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
sectionsBuf.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
|
final int blockEntitiesLength = Types.VAR_INT.readPrimitive(buffer);
|
||||||
final List<BlockEntity> blockEntities = new ArrayList<>(blockEntitiesLength);
|
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());
|
Types.COMPOUND_TAG.write(buffer, chunk.getHeightMap());
|
||||||
|
|
||||||
final ByteBuf sectionBuffer = buffer.alloc().buffer();
|
Types.VAR_INT.writePrimitive(buffer, sectionType.serializedSize(chunk));
|
||||||
try {
|
|
||||||
for (final ChunkSection section : chunk.getSections()) {
|
for (final ChunkSection section : chunk.getSections()) {
|
||||||
sectionType.write(sectionBuffer, section);
|
sectionType.write(buffer, section);
|
||||||
}
|
|
||||||
sectionBuffer.readerIndex(0);
|
|
||||||
Types.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());
|
|
||||||
buffer.writeBytes(sectionBuffer);
|
|
||||||
} finally {
|
|
||||||
sectionBuffer.release(); // release buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
|
Types.VAR_INT.writePrimitive(buffer, chunk.blockEntities().size());
|
||||||
|
@ -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.minecraft.chunks.PaletteType;
|
||||||
import com.viaversion.viaversion.api.type.Type;
|
import com.viaversion.viaversion.api.type.Type;
|
||||||
import com.viaversion.viaversion.api.type.Types;
|
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.CompactArrayUtil;
|
||||||
import com.viaversion.viaversion.util.MathUtil;
|
import com.viaversion.viaversion.util.MathUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
@ -94,13 +95,7 @@ public final class PaletteType1_18 extends Type<DataPalette> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1, 2, and 3 bit linear block palettes can't be read by the client
|
final int bitsPerValue = bitsPerValue(size);
|
||||||
final int min = type == PaletteType.BLOCKS ? 4 : 1;
|
|
||||||
int bitsPerValue = Math.max(min, MathUtil.ceilLog2(size));
|
|
||||||
if (bitsPerValue > type.highestBitsPerValue()) {
|
|
||||||
bitsPerValue = globalPaletteBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.writeByte(bitsPerValue);
|
buffer.writeByte(bitsPerValue);
|
||||||
|
|
||||||
if (bitsPerValue != globalPaletteBits) {
|
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));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user