From beaeafbb5b86d5e6bd6a98a1c341958510a2e6db Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sat, 20 Nov 2021 20:28:25 +0100 Subject: [PATCH] fix with negative dimensions y > 256, rework chunk reading --- .../dimension/DimensionProperties.kt | 6 +- .../registries/registry/AbstractRegistry.kt | 1 - .../registries/registry/BlockStateRegistry.kt | 6 +- .../de/bixilon/minosoft/data/world/Chunk.kt | 4 +- .../bixilon/minosoft/data/world/ChunkData.kt | 4 +- .../minosoft/data/world/ChunkSection.kt | 12 +- .../world/biome/source/PalettedBiomeArray.kt | 29 ++++ .../world/container/SectionDataProvider.kt | 11 +- .../container/palette/PalettedContainer.kt | 36 +++++ .../palette/PalettedContainerReader.kt | 34 +++++ .../palette/data/ArrayPaletteData.kt | 59 ++++++++ .../palette/data/EmptyPaletteData.kt | 30 ++++ .../palette/data/PaletteData.kt} | 23 ++- .../palettes/ArrayPalette.kt} | 37 ++--- .../palette/palettes/BiomePaletteFactory.kt | 28 ++++ .../palettes/BlockStatePaletteFactory.kt | 29 ++++ .../container/palette/palettes/Palette.kt | 24 +++ .../palette/palettes/PaletteFactory.kt | 23 +++ .../palette/palettes/RegistryPalette.kt | 29 ++++ .../palette/palettes/SingularPalette.kt | 32 ++++ .../data/world/palette/DirectPalette.kt | 42 ------ .../data/world/palette/IndirectPalette.kt | 45 ------ .../packets/s2c/play/ChunkDataS2CP.kt | 139 +++++++++--------- .../protocol/protocol/InByteBuffer.kt | 6 + .../java/de/bixilon/minosoft/util/MMath.kt | 7 + .../bixilon/minosoft/util/chunk/ChunkUtil.kt | 71 ++++----- 26 files changed, 506 insertions(+), 261 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/data/world/biome/source/PalettedBiomeArray.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainer.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainerReader.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/data/ArrayPaletteData.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/data/EmptyPaletteData.kt rename src/main/java/de/bixilon/minosoft/data/world/{palette/Palette.kt => container/palette/data/PaletteData.kt} (61%) rename src/main/java/de/bixilon/minosoft/data/world/container/{RegistrySectionDataProvider.kt => palette/palettes/ArrayPalette.kt} (55%) create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BiomePaletteFactory.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BlockStatePaletteFactory.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/Palette.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/PaletteFactory.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/RegistryPalette.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/SingularPalette.kt delete mode 100644 src/main/java/de/bixilon/minosoft/data/world/palette/DirectPalette.kt delete mode 100644 src/main/java/de/bixilon/minosoft/data/world/palette/IndirectPalette.kt diff --git a/src/main/java/de/bixilon/minosoft/data/registries/dimension/DimensionProperties.kt b/src/main/java/de/bixilon/minosoft/data/registries/dimension/DimensionProperties.kt index 4bae33c95..3a4ef2781 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/dimension/DimensionProperties.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/dimension/DimensionProperties.kt @@ -24,9 +24,11 @@ data class DimensionProperties( val minY: Int = 0, val hasCeiling: Boolean = false, val ultraWarm: Boolean = false, - val height: Int = 256, + @Deprecated("Height does not differ from logical height in 1.18") + val dataHeight: Int = 256, val supports3DBiomes: Boolean = true, ) { + val height = logicalHeight + minY val lowestSection = if (minY < 0) { (minY + 1) / ProtocolDefinition.SECTION_HEIGHT_Y - 1 } else { @@ -69,7 +71,7 @@ data class DimensionProperties( minY = data["min_y"]?.toInt() ?: 0, hasCeiling = data["has_ceiling"]?.toBoolean() ?: false, ultraWarm = data["ultrawarm"]?.toBoolean() ?: false, - height = data["height"]?.toInt() ?: 256, + dataHeight = data["height"]?.toInt() ?: 256, supports3DBiomes = data["supports_3d_biomes"]?.toBoolean() ?: true, ) } diff --git a/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/AbstractRegistry.kt b/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/AbstractRegistry.kt index 8092d274c..e2bd1ebd1 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/AbstractRegistry.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/AbstractRegistry.kt @@ -16,7 +16,6 @@ package de.bixilon.minosoft.data.registries.registries.registry import de.bixilon.minosoft.util.collections.Clearable interface AbstractRegistry : Iterable, Clearable, Parentable> { - val size: Int operator fun get(any: Any?): T? diff --git a/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/BlockStateRegistry.kt b/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/BlockStateRegistry.kt index b81d87923..0e0763325 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/BlockStateRegistry.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/registries/registry/BlockStateRegistry.kt @@ -17,8 +17,8 @@ import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.util.KUtil.toSynchronizedMap -class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry { - override var parent: AbstractRegistry? = null +class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry { + override var parent: AbstractRegistry? = null private val idMap: MutableMap = mutableMapOf() override val size: Int @@ -66,7 +66,7 @@ class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry return forceGet(id) } - override fun getId(value: BlockState): Int { + override fun getId(value: BlockState?): Int { TODO("Not yet implemented") } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt index 0f46342ad..fb9c47dd3 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt @@ -105,7 +105,7 @@ class Chunk( data.blocks?.let { for ((index, blocks) in it.withIndex()) { blocks ?: continue - val section = getOrPut(index - lowestSection) + val section = getOrPut(index + lowestSection) section.blocks = blocks } blocksInitialized = true @@ -148,7 +148,7 @@ class Chunk( var section = sections[sectionIndex] if (section == null) { - section = ChunkSection(connection.registries) + section = ChunkSection() val neighbours: Array = world.getChunkNeighbours(chunkPosition).unsafeCast() val cacheBiomeAccessor = world.cacheBiomeAccessor if (cacheBiomeAccessor != null && biomesInitialized && neighboursLoaded) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/ChunkData.kt b/src/main/java/de/bixilon/minosoft/data/world/ChunkData.kt index ff306d80a..eb3472b4a 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/ChunkData.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/ChunkData.kt @@ -16,11 +16,11 @@ package de.bixilon.minosoft.data.world import de.bixilon.minosoft.data.entities.block.BlockEntity import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.world.biome.source.BiomeSource -import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider +import de.bixilon.minosoft.data.world.container.SectionDataProvider import glm_.vec3.Vec3i class ChunkData( - var blocks: Array?>? = null, + var blocks: Array?>? = null, var blockEntities: Map? = null, var biomeSource: BiomeSource? = null, var light: Array? = null, diff --git a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt index fb5e7c362..4749667d0 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt @@ -15,9 +15,7 @@ package de.bixilon.minosoft.data.world import de.bixilon.minosoft.data.entities.block.BlockEntity import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.blocks.BlockState -import de.bixilon.minosoft.data.registries.registries.Registries import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor -import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider import de.bixilon.minosoft.data.world.container.SectionDataProvider import de.bixilon.minosoft.gui.rendering.util.VecUtil.of import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection @@ -30,14 +28,12 @@ import glm_.vec3.Vec3i * Collection of 16x16x16 blocks */ class ChunkSection( - var blocks: RegistrySectionDataProvider, - var biomes: RegistrySectionDataProvider, - var blockEntities: SectionDataProvider, - var light: ByteArray, // packed (skyLight: 0xF0, blockLight: 0x0F) + var blocks: SectionDataProvider = SectionDataProvider(checkSize = true), + var biomes: SectionDataProvider = SectionDataProvider(checkSize = false), + var blockEntities: SectionDataProvider = SectionDataProvider(checkSize = false), + var light: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION), // packed (skyLight: 0xF0, blockLight: 0x0F) ) { - constructor(registries: Registries) : this(RegistrySectionDataProvider(registries.blockStateRegistry.unsafeCast(), checkSize = true), RegistrySectionDataProvider(registries.biomeRegistry, checkSize = false), SectionDataProvider(checkSize = false), ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION)) - fun tick(connection: PlayConnection, chunkPosition: Vec2i, sectionHeight: Int) { if (blockEntities.isEmpty) { return diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/source/PalettedBiomeArray.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/source/PalettedBiomeArray.kt new file mode 100644 index 000000000..5e031d03a --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/source/PalettedBiomeArray.kt @@ -0,0 +1,29 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.biome.source + +import de.bixilon.minosoft.data.registries.biomes.Biome +import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight + +class PalettedBiomeArray( + private val containers: Array?>, + private val lowestSection: Int, + val edgeBits: Int, +) : BiomeSource { + private val mask = (1 shl edgeBits) - 1 + + override fun getBiome(x: Int, y: Int, z: Int): Biome? { + return containers.getOrNull(y.sectionHeight - lowestSection)?.get(((((y.sectionHeight and mask) shl edgeBits) or (z and mask)) shl edgeBits) or (x and mask)) + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt index 3b60f7a71..409daa5d2 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt @@ -15,14 +15,15 @@ package de.bixilon.minosoft.data.world.container import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import de.bixilon.minosoft.util.KUtil.unsafeCast import de.bixilon.minosoft.util.ReadWriteLock import glm_.vec3.Vec3i -open class SectionDataProvider( - data: Array? = null, +class SectionDataProvider( + data: Array? = null, val checkSize: Boolean = false, ) : Iterable { - protected var data = data + protected var data: Array? = data?.unsafeCast() private set protected val lock = ReadWriteLock() // lock while reading (blocks writing) var count: Int = 0 @@ -191,9 +192,9 @@ open class SectionDataProvider( unlock() } - open fun copy(): SectionDataProvider { + fun copy(): SectionDataProvider { acquire() - val clone = SectionDataProvider(data?.clone()) + val clone = SectionDataProvider(data?.clone()?.unsafeCast()) release() return clone diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainer.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainer.kt new file mode 100644 index 000000000..5a164d694 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainer.kt @@ -0,0 +1,36 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette + +import de.bixilon.minosoft.data.world.container.palette.data.PaletteData +import de.bixilon.minosoft.data.world.container.palette.palettes.Palette + +class PalettedContainer( + private val edgeBits: Int, + val palette: Palette, + val data: PaletteData, +) { + + fun get(x: Int, y: Int, z: Int): T { + return palette.get(data.get((((y shl edgeBits) or z) shl edgeBits) or x)) + } + + inline fun unpack(): Array { + val array: Array = arrayOfNulls(data.size) + for (i in array.indices) { + array[i] = palette.get(data.get(i)) as V + } + return array as Array + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainerReader.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainerReader.kt new file mode 100644 index 000000000..b84c7010b --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/PalettedContainerReader.kt @@ -0,0 +1,34 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette + +import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry +import de.bixilon.minosoft.data.world.container.palette.data.PaletteData +import de.bixilon.minosoft.data.world.container.palette.palettes.PaletteFactory +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer + +object PalettedContainerReader { + + fun read(buffer: PlayInByteBuffer, registry: AbstractRegistry, paletteFactory: PaletteFactory): PalettedContainer { + val bits = buffer.readUnsignedByte() + + val palette = paletteFactory.createPalette(registry, bits) + palette.read(buffer) + + val paletteData = PaletteData.create(buffer.versionId, palette.bits, paletteFactory.containerSize) + paletteData.read(buffer) + + return PalettedContainer(paletteFactory.edgeBits, palette, paletteData) + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/ArrayPaletteData.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/ArrayPaletteData.kt new file mode 100644 index 000000000..2b6de5508 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/ArrayPaletteData.kt @@ -0,0 +1,59 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.data + +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer +import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_1_16 + +class ArrayPaletteData( + val versionId: Int, + val elementBits: Int, + override val size: Int, +) : PaletteData { + init { + check(elementBits in 0..32) + } + + private lateinit var data: LongArray + + + override fun read(buffer: PlayInByteBuffer) { + data = buffer.readLongArray() + } + + override operator fun get(index: Int): Int { + val individualValueMask = (1 shl elementBits) - 1 + + var blockId: Long = if (versionId < V_1_16) { // ToDo: When did this changed? is just a guess + val startLong = index * elementBits / Long.SIZE_BITS + val startOffset = index * elementBits % Long.SIZE_BITS + val endLong = ((index + 1) * elementBits - 1) / Long.SIZE_BITS + + if (startLong == endLong) { + data[startLong] ushr startOffset + } else { + val endOffset = Long.SIZE_BITS - startOffset + data[startLong] ushr startOffset or (data[endLong] shl endOffset) + } + } else { + val startLong = index / (Long.SIZE_BITS / elementBits) + val startOffset = index % (Long.SIZE_BITS / elementBits) * elementBits + data[startLong] ushr startOffset + } + + blockId = blockId and individualValueMask.toLong() + + return blockId.toInt() + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/EmptyPaletteData.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/EmptyPaletteData.kt new file mode 100644 index 000000000..299fde601 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/EmptyPaletteData.kt @@ -0,0 +1,30 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.data + +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer + +class EmptyPaletteData(override val size: Int) : PaletteData { + + override fun get(index: Int): Int { + if (index in 0 until size) { + return 0 + } + throw IndexOutOfBoundsException("Index $index > $size") + } + + override fun read(buffer: PlayInByteBuffer) { + check(buffer.readVarInt() == 0) { "No data expected!" } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/palette/Palette.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/PaletteData.kt similarity index 61% rename from src/main/java/de/bixilon/minosoft/data/world/palette/Palette.kt rename to src/main/java/de/bixilon/minosoft/data/world/container/palette/data/PaletteData.kt index 228161ce1..70d06c019 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/palette/Palette.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/data/PaletteData.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020 Moritz Zwerger + * Copyright (C) 2021 Moritz Zwerger * * 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. * @@ -10,25 +10,24 @@ * * This software is not affiliated with Mojang AB, the original developer of Minecraft. */ -package de.bixilon.minosoft.data.world.palette -import de.bixilon.minosoft.data.registries.blocks.BlockState +package de.bixilon.minosoft.data.world.container.palette.data + import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer -interface Palette { +interface PaletteData { + val size: Int - fun blockById(id: Int): BlockState? + fun get(index: Int): Int - val bitsPerBlock: Int + fun read(buffer: PlayInByteBuffer) companion object { - fun choosePalette(bitsPerBlock: Int, buffer: PlayInByteBuffer): Palette { - if (bitsPerBlock <= 4) { - return IndirectPalette(4, buffer) - } else if (bitsPerBlock <= 8) { - return IndirectPalette(bitsPerBlock, buffer) + fun create(versionId: Int, bits: Int, size: Int): PaletteData { + return when (bits) { + 0 -> EmptyPaletteData(size) + else -> ArrayPaletteData(versionId, bits, size) } - return DirectPalette(buffer) } } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/RegistrySectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/ArrayPalette.kt similarity index 55% rename from src/main/java/de/bixilon/minosoft/data/world/container/RegistrySectionDataProvider.kt rename to src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/ArrayPalette.kt index 58597a13e..8f22ee348 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/RegistrySectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/ArrayPalette.kt @@ -11,34 +11,23 @@ * This software is not affiliated with Mojang AB, the original developer of Minecraft. */ -package de.bixilon.minosoft.data.world.container +package de.bixilon.minosoft.data.world.container.palette.palettes import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer -class RegistrySectionDataProvider( - val registry: AbstractRegistry, - data: Array? = null, - checkSize: Boolean = false, -) : SectionDataProvider(data, checkSize = checkSize) { +class ArrayPalette(private val registry: AbstractRegistry, override val bits: Int) : Palette { + private var array: Array = arrayOfNulls(0) + + override fun read(buffer: PlayInByteBuffer) { + array = arrayOfNulls(buffer.readVarInt()) + for (i in array.indices) { + array[i] = registry[buffer.readVarInt()] + } + } @Suppress("UNCHECKED_CAST") - fun setIdData(ids: IntArray) { - val data: Array = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) - - for ((index, id) in ids.withIndex()) { - data[index] = registry[id] - } - - setData(data as Array) - } - - - override fun copy(): RegistrySectionDataProvider { - acquire() - val clone = RegistrySectionDataProvider(registry, data?.clone()) - release() - - return clone + override fun get(index: Int): T { + return array[index] as T } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BiomePaletteFactory.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BiomePaletteFactory.kt new file mode 100644 index 000000000..f6f0011b8 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BiomePaletteFactory.kt @@ -0,0 +1,28 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.palettes + +import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry + +object BiomePaletteFactory : PaletteFactory { + override val edgeBits = 2 + + override fun createPalette(registry: AbstractRegistry, bits: Int): Palette { + return when (bits) { + 0 -> SingularPalette(registry) + 1, 2, 3 -> ArrayPalette(registry, bits) + else -> RegistryPalette(registry) + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BlockStatePaletteFactory.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BlockStatePaletteFactory.kt new file mode 100644 index 000000000..22257cabf --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/BlockStatePaletteFactory.kt @@ -0,0 +1,29 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.palettes + +import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry + +object BlockStatePaletteFactory : PaletteFactory { + override val edgeBits = 4 + + override fun createPalette(registry: AbstractRegistry, bits: Int): Palette { + return when (bits) { + 0 -> SingularPalette(registry) + 1, 2, 3, 4 -> ArrayPalette(registry, 4) + 5, 6, 7, 8 -> ArrayPalette(registry, bits) + else -> RegistryPalette(registry) + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/Palette.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/Palette.kt new file mode 100644 index 000000000..eeaab0847 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/Palette.kt @@ -0,0 +1,24 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.palettes + +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer + +interface Palette { + val bits: Int + + fun read(buffer: PlayInByteBuffer) + + fun get(index: Int): T +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/PaletteFactory.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/PaletteFactory.kt new file mode 100644 index 000000000..982858e33 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/PaletteFactory.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ +package de.bixilon.minosoft.data.world.container.palette.palettes + +import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry + +interface PaletteFactory { + val edgeBits: Int + val containerSize: Int + get() = 1 shl (edgeBits * 3) + + fun createPalette(registry: AbstractRegistry, bits: Int): Palette +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/RegistryPalette.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/RegistryPalette.kt new file mode 100644 index 000000000..c7eb9b0e5 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/RegistryPalette.kt @@ -0,0 +1,29 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.palettes + +import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer +import de.bixilon.minosoft.util.MMath + +class RegistryPalette(private val registry: AbstractRegistry) : Palette { + override val bits = MMath.ceilLog2(registry.size) + + override fun read(buffer: PlayInByteBuffer) {} + + @Suppress("UNCHECKED_CAST") + override fun get(index: Int): T { + return registry[index] as T + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/SingularPalette.kt b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/SingularPalette.kt new file mode 100644 index 000000000..292acccd0 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/palette/palettes/SingularPalette.kt @@ -0,0 +1,32 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.palette.palettes + +import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer + +class SingularPalette(private val registry: AbstractRegistry) : Palette { + override val bits: Int = 0 + var item: T? = null + private set + + override fun read(buffer: PlayInByteBuffer) { + item = registry[buffer.readVarInt()] + } + + @Suppress("UNCHECKED_CAST") + override fun get(index: Int): T { + return item as T + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/palette/DirectPalette.kt b/src/main/java/de/bixilon/minosoft/data/world/palette/DirectPalette.kt deleted file mode 100644 index 1b69ae6eb..000000000 --- a/src/main/java/de/bixilon/minosoft/data/world/palette/DirectPalette.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Minosoft - * Copyright (C) 2020 Moritz Zwerger - * - * 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 . - * - * This software is not affiliated with Mojang AB, the original developer of Minecraft. - */ -package de.bixilon.minosoft.data.world.palette - -import de.bixilon.minosoft.data.registries.blocks.BlockState -import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection -import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer -import de.bixilon.minosoft.protocol.protocol.ProtocolVersions -import kotlin.math.ceil -import kotlin.math.ln - -class DirectPalette(buffer: PlayInByteBuffer) : Palette { - private var connection: PlayConnection = buffer.connection - - init { - if (buffer.versionId < ProtocolVersions.V_17W47A) { - buffer.readVarInt() - } - } - - override fun blockById(id: Int): BlockState? { - return connection.registries.blockStateRegistry[id] - } - - override val bitsPerBlock: Int - get() { - if (this.connection.version.versionId < ProtocolVersions.V_18W10D) { - return 13 - } - return ceil(ln(connection.registries.blockStateRegistry.size.toDouble()) / ln(2.0)).toInt() - } -} diff --git a/src/main/java/de/bixilon/minosoft/data/world/palette/IndirectPalette.kt b/src/main/java/de/bixilon/minosoft/data/world/palette/IndirectPalette.kt deleted file mode 100644 index 8ef54a4d6..000000000 --- a/src/main/java/de/bixilon/minosoft/data/world/palette/IndirectPalette.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Minosoft - * Copyright (C) 2020 Moritz Zwerger - * - * 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 . - * - * This software is not affiliated with Mojang AB, the original developer of Minecraft. - */ -package de.bixilon.minosoft.data.world.palette - -import de.bixilon.minosoft.config.StaticConfiguration -import de.bixilon.minosoft.data.registries.blocks.BlockState -import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition -import de.bixilon.minosoft.util.logging.Log - -class IndirectPalette( - override val bitsPerBlock: Int, - buffer: PlayInByteBuffer, -) : Palette { - private val connection = buffer.connection - private var palette = buffer.readVarIntArray() - - override fun blockById(id: Int): BlockState? { - var blockId = id - if (blockId < palette.size) { - blockId = palette[blockId] - } - val block = connection.registries.blockStateRegistry[blockId] - - if (StaticConfiguration.DEBUG_MODE && block == null && blockId != ProtocolDefinition.AIR_BLOCK_ID) { - val blockName: String = if (connection.version.isFlattened()) { - blockId.toString() - } else { - "${blockId shr 4}:${blockId and 0x0F} ($blockId)" - } - Log.warn("Server sent unknown block: $blockName") - } - return block - } -} diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ChunkDataS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ChunkDataS2CP.kt index e27863859..c458dce0c 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ChunkDataS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/ChunkDataS2CP.kt @@ -23,9 +23,7 @@ import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer -import de.bixilon.minosoft.protocol.protocol.ProtocolVersions -import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_18W44A -import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_21W37A +import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.* import de.bixilon.minosoft.util.KUtil.toInt import de.bixilon.minosoft.util.KUtil.unsafeCast import de.bixilon.minosoft.util.Util @@ -51,62 +49,64 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { init { val dimension = buffer.connection.world.dimension!! chunkPosition = buffer.readChunkPosition() - if (buffer.versionId < ProtocolVersions.V_20W45A) { + if (buffer.versionId < V_20W45A) { isFullChunk = !buffer.readBoolean() } - when { - buffer.versionId < ProtocolVersions.V_14W26A -> { - val sectionBitMask = BitSet.valueOf(buffer.readByteArray(2)) - val addBitMask = BitSet.valueOf(buffer.readByteArray(2)) + if (buffer.versionId < V_14W26A) { // ToDo + val sectionBitMask = BitSet.valueOf(buffer.readByteArray(2)) + val addBitMask = BitSet.valueOf(buffer.readByteArray(2)) - // decompress chunk data - val decompressed: PlayInByteBuffer = if (buffer.versionId < ProtocolVersions.V_14W28A) { - Util.decompress(buffer.readByteArray(buffer.readInt()), buffer.connection) - } else { - buffer - } - val chunkData = ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMask, addBitMask, !isFullChunk, dimension.hasSkyLight) - if (chunkData == null) { - unloadChunk = true - } else { - this.chunkData.replace(chunkData) - } + // decompress chunk data + val decompressed: PlayInByteBuffer = if (buffer.versionId < V_14W28A) { + Util.decompress(buffer.readByteArray(buffer.readInt()), buffer.connection) + } else { + buffer } - buffer.versionId < V_21W37A -> { - if (buffer.versionId >= ProtocolVersions.V_1_16_PRE7 && buffer.versionId < ProtocolVersions.V_1_16_2_PRE2) { - buffer.readBoolean() // ToDo: ignore old data??? - } - val sectionBitMask: BitSet = when { - buffer.versionId < ProtocolVersions.V_15W34C -> { - BitSet.valueOf(buffer.readByteArray(2)) - } - buffer.versionId < ProtocolVersions.V_15W36D -> { - BitSet.valueOf(buffer.readByteArray(4)) - } - buffer.versionId < ProtocolVersions.V_21W03A -> { - BitSet.valueOf(longArrayOf(buffer.readVarInt().toLong())) - } - else -> { - BitSet.valueOf(buffer.readLongArray()) - } - } - if (buffer.versionId >= V_18W44A) { - heightMap = buffer.readNBT()?.compoundCast() - } - if (!isFullChunk) { - this.chunkData.biomeSource = SpatialBiomeArray(buffer.readBiomeArray()) - } - val size = buffer.readVarInt() - val lastBufferPosition = buffer.pointer - val chunkData = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask, null, !isFullChunk, dimension.hasSkyLight) + val chunkData = ChunkUtil.readChunkPacket(decompressed, dimension, sectionBitMask, addBitMask, !isFullChunk, dimension.hasSkyLight) + if (chunkData == null) { + unloadChunk = true + } else { + this.chunkData.replace(chunkData) + } + } else { + if (buffer.versionId in V_1_16_PRE7 until V_1_16_2_PRE2) { + buffer.readBoolean() // ToDo: ignore old data??? + } + val sectionBitMask = when { + buffer.versionId < V_15W34C -> BitSet.valueOf(buffer.readByteArray(2)) + buffer.versionId < V_15W36D -> BitSet.valueOf(buffer.readByteArray(4)) + buffer.versionId < V_21W03A -> BitSet.valueOf(longArrayOf(buffer.readVarInt().toLong())) + buffer.versionId < V_21W37A -> BitSet.valueOf(buffer.readLongArray()) + else -> null + } + if (buffer.versionId >= V_18W44A) { + heightMap = buffer.readNBT()?.compoundCast() + } + if (!isFullChunk && buffer.versionId < V_21W37A) { + this.chunkData.biomeSource = SpatialBiomeArray(buffer.readBiomeArray()) + } + val size = buffer.readVarInt() + val lastBufferPosition = buffer.pointer + + if (buffer.versionId < V_21W37A) { + val chunkData = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask!!, null, !isFullChunk, dimension.hasSkyLight) if (chunkData == null) { unloadChunk = true } else { this.chunkData.replace(chunkData) } - // set position of the byte buffer, because of some reasons HyPixel makes some weird stuff and sends way to much 0 bytes. (~ 190k), thanks @pokechu22 - buffer.pointer = size + lastBufferPosition - if (buffer.versionId >= ProtocolVersions.V_1_9_4) { + } else { + this.chunkData.replace(ChunkUtil.readPaletteChunk(buffer, dimension, null, isFullChunk = true, containsSkyLight = false)) + } + + // set position to expected read positions; the server sometimes sends a bunch of useless zeros (~ 190k), thanks @pokechu22 + buffer.pointer = size + lastBufferPosition + + // block entities + when { + buffer.versionId < V_1_9_4 -> { + } + buffer.versionId < V_21W37A -> { val blockEntities: MutableMap = mutableMapOf() val positionOffset = Vec3i.of(chunkPosition, dimension.lowestSection, Vec3i.EMPTY) for (i in 0 until buffer.readVarInt()) { @@ -123,31 +123,24 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { } this.chunkData.blockEntities = blockEntities } + else -> { + val blockEntities: MutableMap = mutableMapOf() + + for (i in 0 until buffer.readVarInt()) { + val xz = buffer.readUnsignedByte() + val y = buffer.readShort() + val type = buffer.connection.registries.blockEntityTypeRegistry[buffer.readVarInt()] + val nbt = buffer.readNBT().asCompound() + val entity = type.build(buffer.connection) + entity.updateNBT(nbt) + blockEntities[Vec3i(xz shr 4, y, xz and 0x0F)] = entity + } + this.chunkData.blockEntities = blockEntities + } } - else -> { - heightMap = buffer.readNBT()?.compoundCast() - val sectionBuffer = PlayInByteBuffer(buffer.readByteArray(), buffer.connection) - for (sectionHeight in dimension.lowestSection until dimension.highestSection) { - val nonAirBlocks = sectionBuffer.readShort() - // ToDo: BlockStates, Biomes - } - - val blockEntities: MutableMap = mutableMapOf() - - for (i in 0 until buffer.readVarInt()) { - val xz = buffer.readUnsignedByte() - val y = buffer.readShort() - val type = buffer.connection.registries.blockEntityTypeRegistry[buffer.readVarInt()] - val nbt = buffer.readNBT().asCompound() - val entity = type.build(buffer.connection) - entity.updateNBT(nbt) - blockEntities[Vec3i(xz shr 4, y, xz and 0x0F)] = entity - } - chunkData.blockEntities = blockEntities - - val lightPacket = ChunkLightDataS2CP(buffer) { chunkPosition } - chunkData.replace(lightPacket.chunkData) + if (buffer.versionId >= V_21W37A) { + this.chunkData.replace(ChunkLightDataS2CP(buffer) { chunkPosition }.chunkData) } } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt b/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt index b74a94039..558dc115c 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/InByteBuffer.kt @@ -163,6 +163,12 @@ open class InByteBuffer { return array } + fun readLongArray(target: LongArray, size: Int = readVarInt()) { + for (i in 0 until size) { + target[i] = readLong() + } + } + fun readVarLong(): Long { var byteCount = 0 diff --git a/src/main/java/de/bixilon/minosoft/util/MMath.kt b/src/main/java/de/bixilon/minosoft/util/MMath.kt index 30c589347..22d1511d0 100644 --- a/src/main/java/de/bixilon/minosoft/util/MMath.kt +++ b/src/main/java/de/bixilon/minosoft/util/MMath.kt @@ -15,9 +15,12 @@ package de.bixilon.minosoft.util import de.bixilon.minosoft.util.KUtil.decide import glm_.vec2.Vec2i +import kotlin.math.ceil import kotlin.math.floor +import kotlin.math.ln object MMath { + private const val LN_2 = 0.69314718056 // ln(2.0) fun clamp(value: Vec2i, min: Vec2i, max: Vec2i): Vec2i { value.x = clamp(value.x, min.x, max.x) @@ -118,4 +121,8 @@ object MMath { val int = this.toInt() return (this > int).decide(int + 1, int) } + + fun ceilLog2(value: Int): Int { + return ceil(ln(value.toDouble()) / LN_2).toInt() + } } diff --git a/src/main/java/de/bixilon/minosoft/util/chunk/ChunkUtil.kt b/src/main/java/de/bixilon/minosoft/util/chunk/ChunkUtil.kt index 180f56f5d..25cb87881 100644 --- a/src/main/java/de/bixilon/minosoft/util/chunk/ChunkUtil.kt +++ b/src/main/java/de/bixilon/minosoft/util/chunk/ChunkUtil.kt @@ -20,14 +20,18 @@ import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.world.Chunk import de.bixilon.minosoft.data.world.ChunkData import de.bixilon.minosoft.data.world.ChunkSection +import de.bixilon.minosoft.data.world.biome.source.PalettedBiomeArray import de.bixilon.minosoft.data.world.biome.source.XZBiomeArray -import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider -import de.bixilon.minosoft.data.world.palette.Palette.Companion.choosePalette +import de.bixilon.minosoft.data.world.container.SectionDataProvider +import de.bixilon.minosoft.data.world.container.palette.PalettedContainer +import de.bixilon.minosoft.data.world.container.palette.PalettedContainerReader +import de.bixilon.minosoft.data.world.container.palette.palettes.BiomePaletteFactory +import de.bixilon.minosoft.data.world.container.palette.palettes.BlockStatePaletteFactory +import de.bixilon.minosoft.data.world.container.palette.palettes.SingularPalette import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.abs import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.* -import de.bixilon.minosoft.util.KUtil.unsafeCast import glm_.vec2.Vec2i import java.util.* @@ -63,13 +67,13 @@ object ChunkUtil { // parse data var arrayPosition = 0 - val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) + val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) for ((sectionIndex, sectionHeight) in (dimension.lowestSection until dimension.highestSection).withIndex()) { if (!sectionBitMask[sectionIndex]) { continue } - val blocks = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) + val blocks: Array = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) for (blockNumber in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) { var blockId = (blockData[arrayPosition].toInt() and 0xFF) shl 4 @@ -95,7 +99,7 @@ object ChunkUtil { blocks[blockNumber] = buffer.connection.registries.blockStateRegistry[blockId] ?: continue } - sectionBlocks[sectionHeight] = RegistrySectionDataProvider(buffer.connection.registries.blockStateRegistry.unsafeCast(), blocks.unsafeCast(), true) + sectionBlocks[sectionHeight] = SectionDataProvider(blocks, true) } chunkData.blocks = sectionBlocks return chunkData @@ -129,7 +133,7 @@ object ChunkUtil { } var arrayPos = 0 - val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) + val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) for ((sectionIndex, sectionHeight) in (dimension.lowestSection until dimension.highestSection).withIndex()) { // max sections per chunks in chunk column if (!sectionBitMask[sectionIndex]) { continue @@ -140,56 +144,38 @@ object ChunkUtil { val block = buffer.connection.registries.blockStateRegistry[blockId] ?: continue blocks[blockNumber] = block } - sectionBlocks[sectionHeight] = RegistrySectionDataProvider(buffer.connection.registries.blockStateRegistry.unsafeCast(), blocks.unsafeCast(), true) + sectionBlocks[sectionHeight] = SectionDataProvider(blocks, true) } chunkData.blocks = sectionBlocks return chunkData } - fun readPaletteChunk(buffer: PlayInByteBuffer, dimension: DimensionProperties, sectionBitMask: BitSet, isFullChunk: Boolean, containsSkyLight: Boolean = false): ChunkData { + fun readPaletteChunk(buffer: PlayInByteBuffer, dimension: DimensionProperties, sectionBitMask: BitSet?, isFullChunk: Boolean, containsSkyLight: Boolean = false): ChunkData { val chunkData = ChunkData() - val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) + val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) val light: Array = arrayOfNulls(dimension.sections) var lightReceived = 0 + val biomes: Array?> = arrayOfNulls(dimension.sections) - for ((sectionIndex, sectionHeight) in (dimension.lowestSection until sectionBitMask.length()).withIndex()) { // max sections per chunks in chunk column - if (!sectionBitMask[sectionIndex]) { + for ((sectionIndex, sectionHeight) in (dimension.lowestSection until (sectionBitMask?.length() ?: dimension.highestSection)).withIndex()) { // max sections per chunks in chunk column + if (sectionBitMask?.get(sectionIndex) == false) { continue } if (buffer.versionId >= V_18W43A) { - buffer.readShort() // block count + buffer.readShort() // non-air block count } - val palette = choosePalette(buffer.readUnsignedByte(), buffer) - val individualValueMask = (1 shl palette.bitsPerBlock) - 1 + val blockContainer: PalettedContainer = PalettedContainerReader.read(buffer, buffer.connection.registries.blockStateRegistry, paletteFactory = BlockStatePaletteFactory) - val data = buffer.readLongArray() - - val blocks = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) - for (blockNumber in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) { - var blockId: Long = if (buffer.versionId < V_1_16) { // ToDo: When did this changed? is just a guess - val startLong = blockNumber * palette.bitsPerBlock / Long.SIZE_BITS - val startOffset = blockNumber * palette.bitsPerBlock % Long.SIZE_BITS - val endLong = ((blockNumber + 1) * palette.bitsPerBlock - 1) / Long.SIZE_BITS - - if (startLong == endLong) { - data[startLong] ushr startOffset - } else { - val endOffset = Long.SIZE_BITS - startOffset - data[startLong] ushr startOffset or (data[endLong] shl endOffset) - } - } else { - val startLong = blockNumber / (Long.SIZE_BITS / palette.bitsPerBlock) - val startOffset = blockNumber % (Long.SIZE_BITS / palette.bitsPerBlock) * palette.bitsPerBlock - data[startLong] ushr startOffset - } - - blockId = blockId and individualValueMask.toLong() - - val block = palette.blockById(blockId.toInt()) ?: continue - blocks[blockNumber] = block + if (blockContainer.palette !is SingularPalette<*> || blockContainer.palette.item != null) { + sectionBlocks[sectionHeight - dimension.lowestSection] = SectionDataProvider(blockContainer.unpack(), checkSize = true) } + if (buffer.versionId >= V_21W37A) { + val biomeContainer: PalettedContainer = PalettedContainerReader.read(buffer, buffer.connection.registries.biomeRegistry, paletteFactory = BiomePaletteFactory) + biomes[sectionHeight - dimension.lowestSection] = biomeContainer.unpack() + } + if (buffer.versionId < V_18W43A) { val blockLight = buffer.readByteArray(ProtocolDefinition.BLOCKS_PER_SECTION / 2) @@ -200,14 +186,15 @@ object ChunkUtil { light[sectionHeight - dimension.lowestSection] = LightUtil.mergeLight(blockLight, skyLight ?: LightUtil.EMPTY_LIGHT_ARRAY) lightReceived++ } - sectionBlocks[sectionHeight - dimension.lowestSection] = RegistrySectionDataProvider(buffer.connection.registries.blockStateRegistry.unsafeCast(), blocks.unsafeCast(), true) } chunkData.blocks = sectionBlocks if (lightReceived > 0) { chunkData.light = light } - if (buffer.versionId < V_19W36A && isFullChunk) { + if (buffer.versionId >= V_21W37A) { + chunkData.biomeSource = PalettedBiomeArray(biomes, dimension.lowestSection, BiomePaletteFactory.edgeBits) + } else if (buffer.versionId < V_19W36A && isFullChunk) { chunkData.biomeSource = readLegacyBiomeArray(buffer) }