Merge branch '1.18' into 'world-renderer'

Support for 1.18

See merge request bixilon/minosoft!28
This commit is contained in:
Bixilon 2021-11-21 19:35:18 +01:00
commit 0dca6587c6
42 changed files with 579 additions and 253 deletions

View File

@ -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 {
@ -37,9 +39,9 @@ data class DimensionProperties(
} else {
height / ProtocolDefinition.SECTION_HEIGHT_Y
}
val sections = highestSection - lowestSection
val lightLevels = FloatArray(16)
val sections = highestSection - lowestSection
init {
val ambientLight = 0.0f // ToDo: 0.1 in nether
@ -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,
)
}

View File

@ -29,7 +29,7 @@ import java.util.*
data class StatusEffect(
override val resourceLocation: ResourceLocation,
val category: StatusEffectCategories,
val category: StatusEffectCategories?,
override val translationKey: ResourceLocation?,
val color: RGBColor,
val attributes: Map<ResourceLocation, EntityAttributeModifier>,
@ -55,7 +55,7 @@ data class StatusEffect(
return StatusEffect(
resourceLocation = resourceLocation,
category = StatusEffectCategories[data["category"].unsafeCast<String>()],
category = data["category"]?.unsafeCast<String>()?.let { return@let StatusEffectCategories[it] },
translationKey = data["translation_key"]?.toResourceLocation(),
color = data["color"].unsafeCast<Int>().asRGBColor(),
attributes = attributes.toMap(),

View File

@ -154,7 +154,7 @@ open class Item(
"SkullItem" -> SkullItem(resourceLocation, registries, data)
"NetherStarItem" -> NetherStarItem(resourceLocation, registries, data)
"FireworkItem" -> FireworkItem(resourceLocation, registries, data)
"FireworkChargeItem" -> FireworkChargeItem(resourceLocation, registries, data)
"FireworkChargeItem", "FireworkRocketItem" -> FireworkChargeItem(resourceLocation, registries, data)
"EnchantedBookItem" -> EnchantedBookItem(resourceLocation, registries, data)
"ArmorStandItem" -> ArmorStandItem(resourceLocation, registries, data)
"DyeableHorseArmorItem" -> DyeableHorseArmorItem(resourceLocation, registries, data)

View File

@ -16,7 +16,6 @@ package de.bixilon.minosoft.data.registries.registries.registry
import de.bixilon.minosoft.util.collections.Clearable
interface AbstractRegistry<T> : Iterable<T>, Clearable, Parentable<AbstractRegistry<T>> {
val size: Int
operator fun get(any: Any?): T?

View File

@ -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<BlockState> {
override var parent: AbstractRegistry<BlockState>? = null
class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry<BlockState?> {
override var parent: AbstractRegistry<BlockState?>? = null
private val idMap: MutableMap<Int, BlockState> = mutableMapOf()
override val size: Int
@ -66,7 +66,7 @@ class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry<BlockState>
return forceGet(id)
}
override fun getId(value: BlockState): Int {
override fun getId(value: BlockState?): Int {
TODO("Not yet implemented")
}
}

View File

@ -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<Chunk> = world.getChunkNeighbours(chunkPosition).unsafeCast()
val cacheBiomeAccessor = world.cacheBiomeAccessor
if (cacheBiomeAccessor != null && biomesInitialized && neighboursLoaded) {

View File

@ -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<RegistrySectionDataProvider<BlockState?>?>? = null,
var blocks: Array<SectionDataProvider<BlockState?>?>? = null,
var blockEntities: Map<Vec3i, BlockEntity>? = null,
var biomeSource: BiomeSource? = null,
var light: Array<ByteArray?>? = null,

View File

@ -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<BlockState?>,
var biomes: RegistrySectionDataProvider<Biome>,
var blockEntities: SectionDataProvider<BlockEntity?>,
var light: ByteArray, // packed (skyLight: 0xF0, blockLight: 0x0F)
var blocks: SectionDataProvider<BlockState?> = SectionDataProvider(checkSize = true),
var biomes: SectionDataProvider<Biome> = SectionDataProvider(checkSize = false),
var blockEntities: SectionDataProvider<BlockEntity?> = SectionDataProvider(checkSize = false),
var light: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION), // packed (skyLight: 0xF0, blockLight: 0x0F)
) {
constructor(registries: Registries) : this(RegistrySectionDataProvider<BlockState?>(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

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<Array<Biome>?>,
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))
}
}

View File

@ -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<T>(
data: Array<Any?>? = null,
class SectionDataProvider<T>(
data: Array<T>? = null,
val checkSize: Boolean = false,
) : Iterable<T> {
protected var data = data
protected var data: Array<Any?>? = data?.unsafeCast()
private set
protected val lock = ReadWriteLock() // lock while reading (blocks writing)
var count: Int = 0
@ -191,9 +192,9 @@ open class SectionDataProvider<T>(
unlock()
}
open fun copy(): SectionDataProvider<T> {
fun copy(): SectionDataProvider<T> {
acquire()
val clone = SectionDataProvider<T>(data?.clone())
val clone = SectionDataProvider<T>(data?.clone()?.unsafeCast())
release()
return clone

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<T>(
private val edgeBits: Int,
val palette: Palette<T>,
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 <reified V : T> unpack(): Array<V> {
val array: Array<V?> = arrayOfNulls(data.size)
for (i in array.indices) {
array[i] = palette.get(data.get(i)) as V
}
return array as Array<V>
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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 <T> read(buffer: PlayInByteBuffer, registry: AbstractRegistry<T>, paletteFactory: PaletteFactory): PalettedContainer<T> {
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)
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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()
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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!" }
}
}

View File

@ -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)
}
}
}

View File

@ -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<T>(
val registry: AbstractRegistry<T>,
data: Array<Any?>? = null,
checkSize: Boolean = false,
) : SectionDataProvider<T>(data, checkSize = checkSize) {
class ArrayPalette<T>(private val registry: AbstractRegistry<T>, override val bits: Int) : Palette<T> {
private var array: Array<Any?> = 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<Any?> = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION)
for ((index, id) in ids.withIndex()) {
data[index] = registry[id]
}
setData(data as Array<T>)
}
override fun copy(): RegistrySectionDataProvider<T> {
acquire()
val clone = RegistrySectionDataProvider(registry, data?.clone())
release()
return clone
override fun get(index: Int): T {
return array[index] as T
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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 <T : Any?> createPalette(registry: AbstractRegistry<T>, bits: Int): Palette<T> {
return when (bits) {
0 -> SingularPalette(registry)
1, 2, 3 -> ArrayPalette(registry, bits)
else -> RegistryPalette(registry)
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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 <T : Any?> createPalette(registry: AbstractRegistry<T>, bits: Int): Palette<T> {
return when (bits) {
0 -> SingularPalette(registry)
1, 2, 3, 4 -> ArrayPalette(registry, 4)
5, 6, 7, 8 -> ArrayPalette(registry, bits)
else -> RegistryPalette(registry)
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<T> {
val bits: Int
fun read(buffer: PlayInByteBuffer)
fun get(index: Int): T
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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 <T> createPalette(registry: AbstractRegistry<T>, bits: Int): Palette<T>
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<T>(private val registry: AbstractRegistry<T>) : Palette<T> {
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
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<T>(private val registry: AbstractRegistry<T>) : Palette<T> {
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
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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()
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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
}
}

View File

@ -232,6 +232,7 @@ class AudioPlayer(
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Unloaded OpenAL!" }
}
@Deprecated("Refactoring needed")
private fun loadSounds() {
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
val data = connection.assetsManager.readJsonAsset(SOUNDS_INDEX_FILE)

View File

@ -24,7 +24,6 @@ import de.bixilon.minosoft.gui.rendering.system.base.shader.Shader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
import glm_.vec2.Vec2i
import org.lwjgl.system.MemoryUtil.memAllocFloat
import java.nio.ByteBuffer
import java.nio.FloatBuffer
@ -88,9 +87,9 @@ interface RenderSystem {
fun createShader(resourceLocation: ResourceLocation): Shader
fun createVertexBuffer(structure: MeshStruct, data: FloatBuffer, primitiveType: PrimitiveTypes = PrimitiveTypes.TRIANGLE): FloatVertexBuffer
fun createVertexBuffer(structure: MeshStruct, data: FloatBuffer, primitiveType: PrimitiveTypes = preferredPrimitiveType): FloatVertexBuffer
fun createIntUniformBuffer(bindingIndex: Int = 0, data: IntArray = IntArray(0)): IntUniformBuffer
fun createFloatUniformBuffer(bindingIndex: Int = 0, data: FloatBuffer = memAllocFloat(0)): FloatUniformBuffer
fun createFloatUniformBuffer(bindingIndex: Int = 0, data: FloatBuffer): FloatUniformBuffer
fun createTextureManager(): TextureManager

View File

@ -2,15 +2,13 @@ package de.bixilon.minosoft.gui.rendering.system.base.phases
import de.bixilon.minosoft.gui.rendering.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.gui.rendering.system.base.RenderingCapabilities
interface TranslucentDrawable : Renderer {
val skipTranslucent: Boolean
get() = false
fun setupTranslucent() {
renderSystem.reset(blending = true) // ToDo: This is just a translucent workaround
renderSystem.enable(RenderingCapabilities.BLENDING)
renderSystem.reset(blending = true)
renderSystem.setBlendFunc(BlendingFunctions.SOURCE_ALPHA, BlendingFunctions.ONE_MINUS_SOURCE_ALPHA, BlendingFunctions.ONE, BlendingFunctions.ZERO)
}

View File

@ -30,6 +30,7 @@ class ClientSettingsC2SP(
val skinParts: Set<SkinParts> = setOf(*SkinParts.VALUES),
val mainHand: Hands = Hands.MAIN,
val disableTextFiltering: Boolean = true,
val allowListing: Boolean = true,
) : PlayC2SPacket {
override fun write(buffer: PlayOutByteBuffer) {
@ -53,10 +54,13 @@ class ClientSettingsC2SP(
if (buffer.versionId >= ProtocolVersions.V_21W07A) {
buffer.writeBoolean(disableTextFiltering)
}
if (buffer.versionId >= ProtocolVersions.V_21W44A) {
buffer.writeBoolean(allowListing)
}
}
override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_OUT, LogLevels.VERBOSE) { "Client settings (locale=$locale, renderDistance=$viewDistance)" }
Log.log(LogMessageType.NETWORK_PACKETS_OUT, LogLevels.VERBOSE) { "Client settings (locale=$locale, viewDistance=$viewDistance)" }
}
enum class SkinParts {

View File

@ -18,6 +18,7 @@ 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_21W37A
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
@ -30,7 +31,11 @@ class BlockEntityMetaDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
} else {
buffer.readBlockPosition()
}
val type = buffer.connection.registries.blockEntityMetaDataTypeRegistry[buffer.readUnsignedByte()].resourceLocation
val type = if (buffer.versionId >= V_21W37A) {
buffer.connection.registries.blockEntityTypeRegistry[buffer.readVarInt()].resourceLocation
} else {
buffer.connection.registries.blockEntityMetaDataTypeRegistry[buffer.readUnsignedByte()].resourceLocation
}
val nbt = buffer.readNBT().asCompound()
override fun handle(connection: PlayConnection) {

View File

@ -20,11 +20,10 @@ import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
import de.bixilon.minosoft.datafixer.BlockEntityFixer.fix
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
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.*
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.Util
@ -49,16 +48,16 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
init {
val dimension = buffer.connection.world.dimension!!
chunkPosition = Vec2i(buffer.readInt(), buffer.readInt())
if (buffer.versionId < ProtocolVersions.V_20W45A) {
chunkPosition = buffer.readChunkPosition()
if (buffer.versionId < V_20W45A) {
isFullChunk = !buffer.readBoolean()
}
if (buffer.versionId < ProtocolVersions.V_14W26A) {
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) {
val decompressed: PlayInByteBuffer = if (buffer.versionId < V_14W28A) {
Util.decompress(buffer.readByteArray(buffer.readInt()), buffer.connection)
} else {
buffer
@ -70,44 +69,47 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
this.chunkData.replace(chunkData)
}
} else {
if (buffer.versionId >= ProtocolVersions.V_1_16_PRE7 && buffer.versionId < ProtocolVersions.V_1_16_2_PRE2) {
if (buffer.versionId in V_1_16_PRE7 until 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))
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
}
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 >= ProtocolVersions.V_18W44A) {
if (buffer.versionId >= V_18W44A) {
heightMap = buffer.readNBT()?.compoundCast()
}
if (!isFullChunk) {
if (!isFullChunk && buffer.versionId < V_21W37A) {
this.chunkData.biomeSource = SpatialBiomeArray(buffer.readBiomeArray())
}
val size = buffer.readVarInt()
val lastPos = buffer.pointer
val chunkData = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask, null, !isFullChunk, dimension.hasSkyLight)
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 + lastPos
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<Vec3i, BlockEntity> = mutableMapOf()
val positionOffset = Vec3i.of(chunkPosition, dimension.lowestSection, Vec3i.EMPTY)
val blockEntitiesCount = buffer.readVarInt()
for (i in 0 until blockEntitiesCount) {
for (i in 0 until buffer.readVarInt()) {
val nbt = buffer.readNBT().asCompound()
val position = Vec3i(nbt["x"]!!.toInt(), nbt["y"]!!.toInt(), nbt["z"]!!.toInt()) - positionOffset
val resourceLocation = ResourceLocation(nbt["id"].unsafeCast()).fix()
@ -121,6 +123,25 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
}
this.chunkData.blockEntities = blockEntities
}
else -> {
val blockEntities: MutableMap<Vec3i, BlockEntity> = 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() ?: continue
val entity = type.build(buffer.connection)
entity.updateNBT(nbt)
blockEntities[Vec3i(xz shr 4, y, xz and 0x0F)] = entity
}
this.chunkData.blockEntities = blockEntities
}
}
if (buffer.versionId >= V_21W37A) {
this.chunkData.replace(ChunkLightDataS2CP(buffer) { chunkPosition }.chunkData)
}
}
}
@ -131,7 +152,6 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
}
val chunk = connection.world.getOrCreateChunk(chunkPosition)
chunk.setData(chunkData)
connection.fireEvent(ChunkDataChangeEvent(connection, this))
}
override fun log() {

View File

@ -27,8 +27,8 @@ import de.bixilon.minosoft.util.logging.LogMessageType
import glm_.vec2.Vec2i
import java.util.*
class ChunkLightDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val chunkPosition: Vec2i = Vec2i(buffer.readVarInt(), buffer.readVarInt())
class ChunkLightDataS2CP(buffer: PlayInByteBuffer, chunkPositionGetter: () -> Vec2i = { Vec2i(buffer.readVarInt(), buffer.readVarInt()) }) : PlayS2CPacket() {
val chunkPosition: Vec2i = chunkPositionGetter()
var trustEdges: Boolean = false
private set
val chunkData: ChunkData

View File

@ -70,6 +70,8 @@ class JoinGameS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
private set
var world: ResourceLocation? = null
private set
var simulationDistance: Int = -1
private set
init {
entityId = buffer.readInt()
@ -131,6 +133,9 @@ class JoinGameS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
if (buffer.versionId >= ProtocolVersions.V_19W13A) {
viewDistance = buffer.readVarInt()
}
if (buffer.versionId >= ProtocolVersions.V_21W40A) {
simulationDistance = buffer.readVarInt()
}
if (buffer.versionId >= ProtocolVersions.V_20W20A) {
buffer.readBoolean() // isDebug
if (buffer.readBoolean()) {

View File

@ -0,0 +1,27 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
class SimulationDistanceSetS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val simulationDistance: Int = buffer.readVarInt()
override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Simulation distance set (viewDistance=$simulationDistance)" }
}
}

View File

@ -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

View File

@ -275,6 +275,7 @@ class PacketTypes {
PLAY_ADVANCEMENT_PROGRESS({ TODO() }),
PLAY_VIBRATION_SIGNAL({ VibrationSignalS2CP(it) }),
PLAY_PING({ PingS2CP(it) }),
PLAY_SIMULATION_DISTANCE({ SimulationDistanceSetS2CP(it) }),
;
init {

View File

@ -16,6 +16,19 @@ package de.bixilon.minosoft.protocol.protocol;
@SuppressWarnings("unused")
public class ProtocolVersions {
public static final int
V_1_18_PRE5 = 807,
V_1_18_PRE4 = 806,
V_1_18_PRE3 = 805,
V_1_18_PRE2 = 804,
V_1_18_PRE1 = 803,
V_21W44A = 802,
V_21W43A = 801,
V_21W42A = 800,
V_21W41A = 799,
V_21W40A = 798,
V_21W39A = 797,
V_21W38A = 796,
V_21W37A = 795,
V_1_17_1 = 794,
V_1_17_1_RC2 = 793,
V_1_17_1_RC1 = 792,

View File

@ -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()
}
}

View File

@ -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<RegistrySectionDataProvider<BlockState?>?> = arrayOfNulls(dimension.sections)
val sectionBlocks: Array<SectionDataProvider<BlockState?>?> = arrayOfNulls(dimension.sections)
for ((sectionIndex, sectionHeight) in (dimension.lowestSection until dimension.highestSection).withIndex()) {
if (!sectionBitMask[sectionIndex]) {
continue
}
val blocks = arrayOfNulls<BlockState>(ProtocolDefinition.BLOCKS_PER_SECTION)
val blocks: Array<BlockState?> = 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<RegistrySectionDataProvider<BlockState?>?> = arrayOfNulls(dimension.sections)
val sectionBlocks: Array<SectionDataProvider<BlockState?>?> = 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<RegistrySectionDataProvider<BlockState?>?> = arrayOfNulls(dimension.sections)
val sectionBlocks: Array<SectionDataProvider<BlockState?>?> = arrayOfNulls(dimension.sections)
val light: Array<ByteArray?> = arrayOfNulls(dimension.sections)
var lightReceived = 0
val biomes: Array<Array<Biome>?> = 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<BlockState?> = PalettedContainerReader.read(buffer, buffer.connection.registries.blockStateRegistry, paletteFactory = BlockStatePaletteFactory)
val data = buffer.readLongArray()
val blocks = arrayOfNulls<BlockState>(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)
if (blockContainer.palette !is SingularPalette<*> || blockContainer.palette.item != null) {
sectionBlocks[sectionHeight - dimension.lowestSection] = SectionDataProvider(blockContainer.unpack(), checkSize = true)
}
} else {
val startLong = blockNumber / (Long.SIZE_BITS / palette.bitsPerBlock)
val startOffset = blockNumber % (Long.SIZE_BITS / palette.bitsPerBlock) * palette.bitsPerBlock
data[startLong] ushr startOffset
if (buffer.versionId >= V_21W37A) {
val biomeContainer: PalettedContainer<Biome> = PalettedContainerReader.read(buffer, buffer.connection.registries.biomeRegistry, paletteFactory = BiomePaletteFactory)
biomes[sectionHeight - dimension.lowestSection] = biomeContainer.unpack()
}
blockId = blockId and individualValueMask.toLong()
val block = palette.blockById(blockId.toInt()) ?: continue
blocks[blockNumber] = block
}
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)
}

View File

@ -25,6 +25,7 @@ object LightUtil {
val EMPTY_LIGHT_ARRAY = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION / 2)
fun readLightPacket(buffer: PlayInByteBuffer, skyLightMask: BitSet, blockLightMask: BitSet, dimension: DimensionProperties): ChunkData {
// ToDo
val skyLight = if (dimension.hasSkyLight || buffer.versionId > V_1_16) { // ToDo: find out version
readLightArray(buffer, skyLightMask, dimension)
} else {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long