Read/write palette based on block/biome size variables

This commit is contained in:
Nassim Jahnke 2021-09-16 18:12:33 +02:00
parent c5e27b89af
commit 73093c0ff2
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
13 changed files with 120 additions and 31 deletions

View File

@ -116,6 +116,11 @@ public class IntArrayMappings implements Mappings {
oldToNew[id] = newId; oldToNew[id] = newId;
} }
@Override
public int size() {
return oldToNew.length;
}
public int[] getOldToNew() { public int[] getOldToNew() {
return oldToNew; return oldToNew;
} }

View File

@ -40,4 +40,6 @@ public interface Mappings {
* @throws IndexOutOfBoundsException if the unmapped id is invalid * @throws IndexOutOfBoundsException if the unmapped id is invalid
*/ */
void setNewId(int id, int newId); void setNewId(int id, int newId);
int size();
} }

View File

@ -147,4 +147,8 @@ public interface EntityTracker {
* @param currentWorld name of the current world * @param currentWorld name of the current world
*/ */
void setCurrentWorld(String currentWorld); void setCurrentWorld(String currentWorld);
int biomesSent();
void setBiomesSent(int biomesSent);
} }

View File

@ -23,15 +23,21 @@
package com.viaversion.viaversion.api.minecraft.chunks; package com.viaversion.viaversion.api.minecraft.chunks;
public enum PaletteType { public enum PaletteType {
BLOCKS(8), BLOCKS(ChunkSection.SIZE, 8),
BIOMES(2); BIOMES(4 * 4 * 4, 2);
private final int maxSize;
private final int highestBitsPerValue; private final int highestBitsPerValue;
PaletteType(final int highestBitsPerValue) { PaletteType(final int maxSize, final int highestBitsPerValue) {
this.maxSize = maxSize;
this.highestBitsPerValue = highestBitsPerValue; this.highestBitsPerValue = highestBitsPerValue;
} }
public int maxSize() {
return maxSize;
}
public int highestBitsPerValue() { public int highestBitsPerValue() {
return highestBitsPerValue; return highestBitsPerValue;
} }

View File

@ -30,23 +30,28 @@ import io.netty.buffer.ByteBuf;
public final class ChunkSectionType1_18 extends Type<ChunkSection> { public final class ChunkSectionType1_18 extends Type<ChunkSection> {
public ChunkSectionType1_18() { private final PaletteType1_18 blockPaletteType;
private final PaletteType1_18 biomePaletteType;
public ChunkSectionType1_18(final int globalPaletteBlockBits, final int globalPaletteBiomeBits) {
super("Chunk Section Type", ChunkSection.class); super("Chunk Section Type", ChunkSection.class);
this.blockPaletteType = new PaletteType1_18(PaletteType.BLOCKS, globalPaletteBlockBits);
this.biomePaletteType = new PaletteType1_18(PaletteType.BIOMES, globalPaletteBiomeBits);
} }
@Override @Override
public ChunkSection read(final ByteBuf buffer) throws Exception { public ChunkSection read(final ByteBuf buffer) throws Exception {
final ChunkSection chunkSection = new ChunkSectionImpl(); final ChunkSection chunkSection = new ChunkSectionImpl();
chunkSection.setNonAirBlocksCount(buffer.readShort()); chunkSection.setNonAirBlocksCount(buffer.readShort());
chunkSection.addPalette(PaletteType.BLOCKS, Types1_18.BLOCK_PALETTE_TYPE.read(buffer)); chunkSection.addPalette(PaletteType.BLOCKS, blockPaletteType.read(buffer));
chunkSection.addPalette(PaletteType.BIOMES, Types1_18.BIOME_PALETTE_TYPE.read(buffer)); chunkSection.addPalette(PaletteType.BIOMES, biomePaletteType.read(buffer));
return chunkSection; return chunkSection;
} }
@Override @Override
public void write(final ByteBuf buffer, final ChunkSection section) throws Exception { public void write(final ByteBuf buffer, final ChunkSection section) throws Exception {
buffer.writeShort(section.getNonAirBlocksCount()); buffer.writeShort(section.getNonAirBlocksCount());
Types1_18.BLOCK_PALETTE_TYPE.write(buffer, section.palette(PaletteType.BLOCKS)); blockPaletteType.write(buffer, section.palette(PaletteType.BLOCKS));
Types1_18.BIOME_PALETTE_TYPE.write(buffer, section.palette(PaletteType.BIOMES)); biomePaletteType.write(buffer, section.palette(PaletteType.BIOMES));
} }
} }

View File

@ -22,29 +22,30 @@
*/ */
package com.viaversion.viaversion.api.type.types.version; package com.viaversion.viaversion.api.type.types.version;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette; import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.DataPaletteImpl; 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.PartialType;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.CompactArrayUtil; import com.viaversion.viaversion.util.CompactArrayUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType> { public final class PaletteType1_18 extends Type<DataPalette> {
private static final int GLOBAL_PALETTE = 15; private final int globalPaletteBits;
private final PaletteType type;
public PaletteType1_18(final PaletteType type) { public PaletteType1_18(final PaletteType type, final int globalPaletteBits) {
super(type, DataPalette.class); super(DataPalette.class);
this.globalPaletteBits = globalPaletteBits;
this.type = type;
} }
@Override @Override
public DataPalette read(final ByteBuf buffer, final PaletteType type) throws Exception { public DataPalette read(final ByteBuf buffer) throws Exception {
int bitsPerValue = buffer.readByte(); int bitsPerValue = buffer.readByte();
final int originalBitsPerValue = bitsPerValue; final int originalBitsPerValue = bitsPerValue;
if (bitsPerValue > type.highestBitsPerValue()) { if (bitsPerValue > type.highestBitsPerValue()) {
bitsPerValue = GLOBAL_PALETTE; bitsPerValue = globalPaletteBits;
} }
// Read palette // Read palette
@ -57,7 +58,7 @@ public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType>
return palette; return palette;
} }
if (bitsPerValue != GLOBAL_PALETTE) { if (bitsPerValue != globalPaletteBits) {
final int paletteLength = Type.VAR_INT.readPrimitive(buffer); final int paletteLength = Type.VAR_INT.readPrimitive(buffer);
palette = new DataPaletteImpl(paletteLength); palette = new DataPaletteImpl(paletteLength);
for (int i = 0; i < paletteLength; i++) { for (int i = 0; i < paletteLength; i++) {
@ -71,7 +72,7 @@ public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType>
final long[] values = new long[Type.VAR_INT.readPrimitive(buffer)]; final long[] values = new long[Type.VAR_INT.readPrimitive(buffer)];
if (values.length > 0) { if (values.length > 0) {
final char valuesPerLong = (char) (64 / bitsPerValue); final char valuesPerLong = (char) (64 / bitsPerValue);
final int expectedLength = (ChunkSection.SIZE + valuesPerLong - 1) / valuesPerLong; final int expectedLength = (type.maxSize() + valuesPerLong - 1) / valuesPerLong;
if (values.length != expectedLength) { if (values.length != expectedLength) {
throw new IllegalStateException("Palette data length (" + values.length + ") does not match expected length (" + expectedLength + ")! bitsPerValue=" + bitsPerValue + ", originalBitsPerValue=" + originalBitsPerValue); throw new IllegalStateException("Palette data length (" + values.length + ") does not match expected length (" + expectedLength + ")! bitsPerValue=" + bitsPerValue + ", originalBitsPerValue=" + originalBitsPerValue);
} }
@ -79,14 +80,14 @@ public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType>
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
values[i] = buffer.readLong(); values[i] = buffer.readLong();
} }
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, ChunkSection.SIZE, values, CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.maxSize(), values,
bitsPerValue == GLOBAL_PALETTE ? palette::setIdAt : palette::setPaletteIndexAt); bitsPerValue == globalPaletteBits ? palette::setIdAt : palette::setPaletteIndexAt);
} }
return palette; return palette;
} }
@Override @Override
public void write(final ByteBuf buffer, final PaletteType type, final DataPalette palette) throws Exception { public void write(final ByteBuf buffer, final DataPalette palette) throws Exception {
int bitsPerValue; int bitsPerValue;
if (palette.size() > 1) { if (palette.size() > 1) {
// 1, 2, and 3 bit linear palettes can't be read by the client // 1, 2, and 3 bit linear palettes can't be read by the client
@ -96,7 +97,7 @@ public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType>
} }
if (bitsPerValue > type.highestBitsPerValue()) { if (bitsPerValue > type.highestBitsPerValue()) {
bitsPerValue = GLOBAL_PALETTE; bitsPerValue = globalPaletteBits;
} }
} else { } else {
bitsPerValue = 0; bitsPerValue = 0;
@ -111,7 +112,7 @@ public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType>
return; return;
} }
if (bitsPerValue != GLOBAL_PALETTE) { if (bitsPerValue != globalPaletteBits) {
// Write pallete // Write pallete
Type.VAR_INT.writePrimitive(buffer, palette.size()); Type.VAR_INT.writePrimitive(buffer, palette.size());
for (int i = 0; i < palette.size(); i++) { for (int i = 0; i < palette.size(); i++) {
@ -119,7 +120,7 @@ public final class PaletteType1_18 extends PartialType<DataPalette, PaletteType>
} }
} }
final long[] data = CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, ChunkSection.SIZE, bitsPerValue == GLOBAL_PALETTE ? palette::idAt : palette::paletteIndexAt); final long[] data = CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, type.maxSize(), bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt);
Type.VAR_INT.writePrimitive(buffer, data.length); Type.VAR_INT.writePrimitive(buffer, data.length);
for (final long l : data) { for (final long l : data) {
buffer.writeLong(l); buffer.writeLong(l);

View File

@ -30,8 +30,5 @@ import com.viaversion.viaversion.api.type.Type;
public final class Types1_18 { public final class Types1_18 {
public static final Type<ChunkSection> CHUNK_SECTION = new ChunkSectionType1_18();
public static final Type<BlockEntity> BLOCK_ENTITY = new BlockEntityType1_18(); public static final Type<BlockEntity> BLOCK_ENTITY = new BlockEntityType1_18();
public static final Type<DataPalette> BLOCK_PALETTE_TYPE = new PaletteType1_18(PaletteType.BLOCKS);
public static final Type<DataPalette> BIOME_PALETTE_TYPE = new PaletteType1_18(PaletteType.BIOMES);
} }

View File

@ -38,6 +38,7 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
private int currentWorldSectionHeight = 16; private int currentWorldSectionHeight = 16;
private int currentMinY; private int currentMinY;
private String currentWorld; private String currentWorld;
private int biomesSent = -1;
public EntityTrackerBase(UserConnection connection, @Nullable EntityType playerType) { public EntityTrackerBase(UserConnection connection, @Nullable EntityType playerType) {
this(connection, playerType, false); this(connection, playerType, false);
@ -147,4 +148,14 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
public void setCurrentWorld(final String currentWorld) { public void setCurrentWorld(final String currentWorld) {
this.currentWorld = currentWorld; this.currentWorld = currentWorld;
} }
@Override
public int biomesSent() {
return biomesSent;
}
@Override
public void setBiomesSent(int biomesSent) {
this.biomesSent = biomesSent;
}
} }

View File

@ -54,6 +54,7 @@ public final class EntityPackets extends EntityRewriter<Protocol1_18To1_17_1> {
map(Type.NBT); // Current dimension data map(Type.NBT); // Current dimension data
map(Type.STRING); // World map(Type.STRING); // World
handler(worldDataTrackerHandler(1)); handler(worldDataTrackerHandler(1));
handler(biomeSizeTracker());
} }
}); });

View File

@ -38,6 +38,7 @@ import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.BlockEntityIds;
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.Protocol1_18To1_17_1; import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.Protocol1_18To1_17_1;
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.storage.ChunkLightStorage; import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.storage.ChunkLightStorage;
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.types.Chunk1_18Type; import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.types.Chunk1_18Type;
import com.viaversion.viaversion.util.MathUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -166,7 +167,9 @@ public final class WorldPackets {
} }
final Chunk chunk = new Chunk1_18(oldChunk.getX(), oldChunk.getZ(), oldChunk.getSections(), oldChunk.getHeightMap(), blockEntities); final Chunk chunk = new Chunk1_18(oldChunk.getX(), oldChunk.getZ(), oldChunk.getSections(), oldChunk.getHeightMap(), blockEntities);
wrapper.write(new Chunk1_18Type(tracker.currentWorldSectionHeight()), chunk); wrapper.write(new Chunk1_18Type(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().size()),
MathUtil.ceilLog2(tracker.biomesSent())), chunk);
// Get and remove light stored, there's only full chunk packets //TODO Only get, not remove if we find out people re-send full chunk packets without re-sending light // Get and remove light stored, there's only full chunk packets //TODO Only get, not remove if we find out people re-send full chunk packets without re-sending light
final ChunkLightStorage lightStorage = wrapper.user().get(ChunkLightStorage.class); final ChunkLightStorage lightStorage = wrapper.user().get(ChunkLightStorage.class);

View File

@ -19,12 +19,14 @@ package com.viaversion.viaversion.protocols.protocol1_18to1_17_1.types;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity; import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk; import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_18; import com.viaversion.viaversion.api.minecraft.chunks.Chunk1_18;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection; import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType; import com.viaversion.viaversion.api.type.types.minecraft.BaseChunkType;
import com.viaversion.viaversion.api.type.types.version.ChunkSectionType1_18;
import com.viaversion.viaversion.api.type.types.version.Types1_18; import com.viaversion.viaversion.api.type.types.version.Types1_18;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -32,11 +34,13 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public final class Chunk1_18Type extends Type<Chunk> { public final class Chunk1_18Type extends Type<Chunk> {
private final ChunkSectionType1_18 sectionType;
private final int ySectionCount; private final int ySectionCount;
public Chunk1_18Type(final int ySectionCount) { public Chunk1_18Type(final int ySectionCount, final int globalPaletteBlockBits, final int globalPaletteBiomeBits) {
super(Chunk.class); super(Chunk.class);
Preconditions.checkArgument(ySectionCount > 0); Preconditions.checkArgument(ySectionCount > 0);
this.sectionType = new ChunkSectionType1_18(globalPaletteBlockBits, globalPaletteBiomeBits);
this.ySectionCount = ySectionCount; this.ySectionCount = ySectionCount;
} }
@ -51,9 +55,12 @@ public final class Chunk1_18Type extends Type<Chunk> {
final ChunkSection[] sections = new ChunkSection[ySectionCount]; final ChunkSection[] sections = new ChunkSection[ySectionCount];
try { try {
for (int i = 0; i < ySectionCount; i++) { for (int i = 0; i < ySectionCount; i++) {
sections[i] = Types1_18.CHUNK_SECTION.read(sectionsBuf); sections[i] = sectionType.read(sectionsBuf);
} }
} finally { } finally {
if (sectionsBuf.readableBytes() > 0) {
Via.getPlatform().getLogger().warning("Found " + sectionsBuf.readableBytes() + " more bytes than expected while reading the chunk: " + chunkX + "/" + chunkZ);
}
sectionsBuf.release(); sectionsBuf.release();
} }
@ -76,7 +83,7 @@ public final class Chunk1_18Type extends Type<Chunk> {
final ByteBuf sectionBuffer = buffer.alloc().buffer(); final ByteBuf sectionBuffer = buffer.alloc().buffer();
try { try {
for (final ChunkSection section : chunk.getSections()) { for (final ChunkSection section : chunk.getSections()) {
Types1_18.CHUNK_SECTION.write(sectionBuffer, section); sectionType.write(sectionBuffer, section);
} }
sectionBuffer.readerIndex(0); sectionBuffer.readerIndex(0);
Type.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes()); Type.VAR_INT.writePrimitive(buffer, sectionBuffer.readableBytes());

View File

@ -19,6 +19,7 @@ package com.viaversion.viaversion.rewriter;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag; import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.Via;
@ -397,6 +398,15 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
}; };
} }
public PacketHandler biomeSizeTracker() {
return wrapper -> {
final CompoundTag registry = wrapper.get(Type.NBT, 0);
final CompoundTag biomeRegistry = registry.get("minecraft:worldgen/biome");
final ListTag biomes = biomeRegistry.get("value");
tracker(wrapper.user()).setBiomesSent(biomes.size());
};
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Sub 1.14.1 methods // Sub 1.14.1 methods

View File

@ -0,0 +1,37 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2021 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 <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viaversion.util;
public final class MathUtil {
/**
* Primitive method to return the ceiled log to the base of 2 for the given number.
*
* @param i number to ceillog
* @return ceiled log2 of the given number
*/
public static int ceilLog2(final int i) {
int j = 1;
int k = 0;
while (j < i) {
j *= 2;
k++;
}
return k;
}
}