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 minY: Int = 0,
val hasCeiling: Boolean = false, val hasCeiling: Boolean = false,
val ultraWarm: 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 supports3DBiomes: Boolean = true,
) { ) {
val height = logicalHeight + minY
val lowestSection = if (minY < 0) { val lowestSection = if (minY < 0) {
(minY + 1) / ProtocolDefinition.SECTION_HEIGHT_Y - 1 (minY + 1) / ProtocolDefinition.SECTION_HEIGHT_Y - 1
} else { } else {
@ -37,9 +39,9 @@ data class DimensionProperties(
} else { } else {
height / ProtocolDefinition.SECTION_HEIGHT_Y height / ProtocolDefinition.SECTION_HEIGHT_Y
} }
val sections = highestSection - lowestSection
val lightLevels = FloatArray(16) val lightLevels = FloatArray(16)
val sections = highestSection - lowestSection
init { init {
val ambientLight = 0.0f // ToDo: 0.1 in nether val ambientLight = 0.0f // ToDo: 0.1 in nether
@ -69,7 +71,7 @@ data class DimensionProperties(
minY = data["min_y"]?.toInt() ?: 0, minY = data["min_y"]?.toInt() ?: 0,
hasCeiling = data["has_ceiling"]?.toBoolean() ?: false, hasCeiling = data["has_ceiling"]?.toBoolean() ?: false,
ultraWarm = data["ultrawarm"]?.toBoolean() ?: false, ultraWarm = data["ultrawarm"]?.toBoolean() ?: false,
height = data["height"]?.toInt() ?: 256, dataHeight = data["height"]?.toInt() ?: 256,
supports3DBiomes = data["supports_3d_biomes"]?.toBoolean() ?: true, supports3DBiomes = data["supports_3d_biomes"]?.toBoolean() ?: true,
) )
} }

View File

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

View File

@ -154,7 +154,7 @@ open class Item(
"SkullItem" -> SkullItem(resourceLocation, registries, data) "SkullItem" -> SkullItem(resourceLocation, registries, data)
"NetherStarItem" -> NetherStarItem(resourceLocation, registries, data) "NetherStarItem" -> NetherStarItem(resourceLocation, registries, data)
"FireworkItem" -> FireworkItem(resourceLocation, registries, data) "FireworkItem" -> FireworkItem(resourceLocation, registries, data)
"FireworkChargeItem" -> FireworkChargeItem(resourceLocation, registries, data) "FireworkChargeItem", "FireworkRocketItem" -> FireworkChargeItem(resourceLocation, registries, data)
"EnchantedBookItem" -> EnchantedBookItem(resourceLocation, registries, data) "EnchantedBookItem" -> EnchantedBookItem(resourceLocation, registries, data)
"ArmorStandItem" -> ArmorStandItem(resourceLocation, registries, data) "ArmorStandItem" -> ArmorStandItem(resourceLocation, registries, data)
"DyeableHorseArmorItem" -> DyeableHorseArmorItem(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 import de.bixilon.minosoft.util.collections.Clearable
interface AbstractRegistry<T> : Iterable<T>, Clearable, Parentable<AbstractRegistry<T>> { interface AbstractRegistry<T> : Iterable<T>, Clearable, Parentable<AbstractRegistry<T>> {
val size: Int val size: Int
operator fun get(any: Any?): T? 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.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry<BlockState> { class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry<BlockState?> {
override var parent: AbstractRegistry<BlockState>? = null override var parent: AbstractRegistry<BlockState?>? = null
private val idMap: MutableMap<Int, BlockState> = mutableMapOf() private val idMap: MutableMap<Int, BlockState> = mutableMapOf()
override val size: Int override val size: Int
@ -66,7 +66,7 @@ class BlockStateRegistry(var flattened: Boolean) : AbstractRegistry<BlockState>
return forceGet(id) return forceGet(id)
} }
override fun getId(value: BlockState): Int { override fun getId(value: BlockState?): Int {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
} }

View File

@ -105,7 +105,7 @@ class Chunk(
data.blocks?.let { data.blocks?.let {
for ((index, blocks) in it.withIndex()) { for ((index, blocks) in it.withIndex()) {
blocks ?: continue blocks ?: continue
val section = getOrPut(index - lowestSection) val section = getOrPut(index + lowestSection)
section.blocks = blocks section.blocks = blocks
} }
blocksInitialized = true blocksInitialized = true
@ -148,7 +148,7 @@ class Chunk(
var section = sections[sectionIndex] var section = sections[sectionIndex]
if (section == null) { if (section == null) {
section = ChunkSection(connection.registries) section = ChunkSection()
val neighbours: Array<Chunk> = world.getChunkNeighbours(chunkPosition).unsafeCast() val neighbours: Array<Chunk> = world.getChunkNeighbours(chunkPosition).unsafeCast()
val cacheBiomeAccessor = world.cacheBiomeAccessor val cacheBiomeAccessor = world.cacheBiomeAccessor
if (cacheBiomeAccessor != null && biomesInitialized && neighboursLoaded) { 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.entities.block.BlockEntity
import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.world.biome.source.BiomeSource 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 import glm_.vec3.Vec3i
class ChunkData( class ChunkData(
var blocks: Array<RegistrySectionDataProvider<BlockState?>?>? = null, var blocks: Array<SectionDataProvider<BlockState?>?>? = null,
var blockEntities: Map<Vec3i, BlockEntity>? = null, var blockEntities: Map<Vec3i, BlockEntity>? = null,
var biomeSource: BiomeSource? = null, var biomeSource: BiomeSource? = null,
var light: Array<ByteArray?>? = 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.entities.block.BlockEntity
import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.blocks.BlockState 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.biome.accessor.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider
import de.bixilon.minosoft.data.world.container.SectionDataProvider import de.bixilon.minosoft.data.world.container.SectionDataProvider
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -30,14 +28,12 @@ import glm_.vec3.Vec3i
* Collection of 16x16x16 blocks * Collection of 16x16x16 blocks
*/ */
class ChunkSection( class ChunkSection(
var blocks: RegistrySectionDataProvider<BlockState?>, var blocks: SectionDataProvider<BlockState?> = SectionDataProvider(checkSize = true),
var biomes: RegistrySectionDataProvider<Biome>, var biomes: SectionDataProvider<Biome> = SectionDataProvider(checkSize = false),
var blockEntities: SectionDataProvider<BlockEntity?>, var blockEntities: SectionDataProvider<BlockEntity?> = SectionDataProvider(checkSize = false),
var light: ByteArray, // packed (skyLight: 0xF0, blockLight: 0x0F) 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) { fun tick(connection: PlayConnection, chunkPosition: Vec2i, sectionHeight: Int) {
if (blockEntities.isEmpty) { if (blockEntities.isEmpty) {
return 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.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.ReadWriteLock import de.bixilon.minosoft.util.ReadWriteLock
import glm_.vec3.Vec3i import glm_.vec3.Vec3i
open class SectionDataProvider<T>( class SectionDataProvider<T>(
data: Array<Any?>? = null, data: Array<T>? = null,
val checkSize: Boolean = false, val checkSize: Boolean = false,
) : Iterable<T> { ) : Iterable<T> {
protected var data = data protected var data: Array<Any?>? = data?.unsafeCast()
private set private set
protected val lock = ReadWriteLock() // lock while reading (blocks writing) protected val lock = ReadWriteLock() // lock while reading (blocks writing)
var count: Int = 0 var count: Int = 0
@ -191,9 +192,9 @@ open class SectionDataProvider<T>(
unlock() unlock()
} }
open fun copy(): SectionDataProvider<T> { fun copy(): SectionDataProvider<T> {
acquire() acquire()
val clone = SectionDataProvider<T>(data?.clone()) val clone = SectionDataProvider<T>(data?.clone()?.unsafeCast())
release() release()
return clone 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 * 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. * 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. * 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 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 { companion object {
fun choosePalette(bitsPerBlock: Int, buffer: PlayInByteBuffer): Palette { fun create(versionId: Int, bits: Int, size: Int): PaletteData {
if (bitsPerBlock <= 4) { return when (bits) {
return IndirectPalette(4, buffer) 0 -> EmptyPaletteData(size)
} else if (bitsPerBlock <= 8) { else -> ArrayPaletteData(versionId, bits, size)
return IndirectPalette(bitsPerBlock, buffer)
} }
return DirectPalette(buffer)
} }
} }
} }

View File

@ -11,34 +11,23 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft. * 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.data.registries.registries.registry.AbstractRegistry
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
class RegistrySectionDataProvider<T>( class ArrayPalette<T>(private val registry: AbstractRegistry<T>, override val bits: Int) : Palette<T> {
val registry: AbstractRegistry<T>, private var array: Array<Any?> = arrayOfNulls(0)
data: Array<Any?>? = null,
checkSize: Boolean = false, override fun read(buffer: PlayInByteBuffer) {
) : SectionDataProvider<T>(data, checkSize = checkSize) { array = arrayOfNulls(buffer.readVarInt())
for (i in array.indices) {
array[i] = registry[buffer.readVarInt()]
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun setIdData(ids: IntArray) { override fun get(index: Int): T {
val data: Array<Any?> = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) return array[index] as T
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
} }
} }

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!" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.INFO) { "Unloaded OpenAL!" }
} }
@Deprecated("Refactoring needed")
private fun loadSounds() { private fun loadSounds() {
Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" } Log.log(LogMessageType.AUDIO_LOADING, LogLevels.VERBOSE) { "Loading sounds.json" }
val data = connection.assetsManager.readJsonAsset(SOUNDS_INDEX_FILE) 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.system.base.texture.TextureManager
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import org.lwjgl.system.MemoryUtil.memAllocFloat
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.FloatBuffer import java.nio.FloatBuffer
@ -88,9 +87,9 @@ interface RenderSystem {
fun createShader(resourceLocation: ResourceLocation): Shader 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 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 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.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.gui.rendering.system.base.RenderingCapabilities
interface TranslucentDrawable : Renderer { interface TranslucentDrawable : Renderer {
val skipTranslucent: Boolean val skipTranslucent: Boolean
get() = false get() = false
fun setupTranslucent() { fun setupTranslucent() {
renderSystem.reset(blending = true) // ToDo: This is just a translucent workaround renderSystem.reset(blending = true)
renderSystem.enable(RenderingCapabilities.BLENDING)
renderSystem.setBlendFunc(BlendingFunctions.SOURCE_ALPHA, BlendingFunctions.ONE_MINUS_SOURCE_ALPHA, BlendingFunctions.ONE, BlendingFunctions.ZERO) 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 skinParts: Set<SkinParts> = setOf(*SkinParts.VALUES),
val mainHand: Hands = Hands.MAIN, val mainHand: Hands = Hands.MAIN,
val disableTextFiltering: Boolean = true, val disableTextFiltering: Boolean = true,
val allowListing: Boolean = true,
) : PlayC2SPacket { ) : PlayC2SPacket {
override fun write(buffer: PlayOutByteBuffer) { override fun write(buffer: PlayOutByteBuffer) {
@ -53,10 +54,13 @@ class ClientSettingsC2SP(
if (buffer.versionId >= ProtocolVersions.V_21W07A) { if (buffer.versionId >= ProtocolVersions.V_21W07A) {
buffer.writeBoolean(disableTextFiltering) buffer.writeBoolean(disableTextFiltering)
} }
if (buffer.versionId >= ProtocolVersions.V_21W44A) {
buffer.writeBoolean(allowListing)
}
} }
override fun log() { 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 { 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.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer 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.protocol.protocol.ProtocolVersions.V_21W37A
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
@ -30,7 +31,11 @@ class BlockEntityMetaDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
} else { } else {
buffer.readBlockPosition() 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() val nbt = buffer.readNBT().asCompound()
override fun handle(connection: PlayConnection) { 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.datafixer.BlockEntityFixer.fix
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY 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.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer 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.toInt
import de.bixilon.minosoft.util.KUtil.unsafeCast import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.Util import de.bixilon.minosoft.util.Util
@ -49,16 +48,16 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
init { init {
val dimension = buffer.connection.world.dimension!! val dimension = buffer.connection.world.dimension!!
chunkPosition = Vec2i(buffer.readInt(), buffer.readInt()) chunkPosition = buffer.readChunkPosition()
if (buffer.versionId < ProtocolVersions.V_20W45A) { if (buffer.versionId < V_20W45A) {
isFullChunk = !buffer.readBoolean() isFullChunk = !buffer.readBoolean()
} }
if (buffer.versionId < ProtocolVersions.V_14W26A) { if (buffer.versionId < V_14W26A) { // ToDo
val sectionBitMask = BitSet.valueOf(buffer.readByteArray(2)) val sectionBitMask = BitSet.valueOf(buffer.readByteArray(2))
val addBitMask = BitSet.valueOf(buffer.readByteArray(2)) val addBitMask = BitSet.valueOf(buffer.readByteArray(2))
// decompress chunk data // 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) Util.decompress(buffer.readByteArray(buffer.readInt()), buffer.connection)
} else { } else {
buffer buffer
@ -70,56 +69,78 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
this.chunkData.replace(chunkData) this.chunkData.replace(chunkData)
} }
} else { } 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??? buffer.readBoolean() // ToDo: ignore old data???
} }
val sectionBitMask: BitSet = when { val sectionBitMask = when {
buffer.versionId < ProtocolVersions.V_15W34C -> { buffer.versionId < V_15W34C -> BitSet.valueOf(buffer.readByteArray(2))
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 < ProtocolVersions.V_15W36D -> { buffer.versionId < V_21W37A -> BitSet.valueOf(buffer.readLongArray())
BitSet.valueOf(buffer.readByteArray(4)) else -> null
}
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() heightMap = buffer.readNBT()?.compoundCast()
} }
if (!isFullChunk) { if (!isFullChunk && buffer.versionId < V_21W37A) {
this.chunkData.biomeSource = SpatialBiomeArray(buffer.readBiomeArray()) this.chunkData.biomeSource = SpatialBiomeArray(buffer.readBiomeArray())
} }
val size = buffer.readVarInt() val size = buffer.readVarInt()
val lastPos = buffer.pointer val lastBufferPosition = buffer.pointer
val chunkData = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask, null, !isFullChunk, dimension.hasSkyLight)
if (chunkData == null) { if (buffer.versionId < V_21W37A) {
unloadChunk = true val chunkData = ChunkUtil.readChunkPacket(buffer, dimension, sectionBitMask!!, null, !isFullChunk, dimension.hasSkyLight)
} else { if (chunkData == null) {
this.chunkData.replace(chunkData) unloadChunk = true
} } else {
// 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 this.chunkData.replace(chunkData)
buffer.pointer = size + lastPos
if (buffer.versionId >= ProtocolVersions.V_1_9_4) {
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) {
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()
val type = buffer.connection.registries.blockEntityTypeRegistry[resourceLocation] ?: let {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.WARN) { "Unknown block entity: $resourceLocation" }
null
} ?: continue
val entity = type.build(buffer.connection)
entity.updateNBT(nbt)
blockEntities[position] = entity
} }
this.chunkData.blockEntities = blockEntities } 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)
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()
val type = buffer.connection.registries.blockEntityTypeRegistry[resourceLocation] ?: let {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.WARN) { "Unknown block entity: $resourceLocation" }
null
} ?: continue
val entity = type.build(buffer.connection)
entity.updateNBT(nbt)
blockEntities[position] = entity
}
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) val chunk = connection.world.getOrCreateChunk(chunkPosition)
chunk.setData(chunkData) chunk.setData(chunkData)
connection.fireEvent(ChunkDataChangeEvent(connection, this))
} }
override fun log() { override fun log() {

View File

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

View File

@ -70,6 +70,8 @@ class JoinGameS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
private set private set
var world: ResourceLocation? = null var world: ResourceLocation? = null
private set private set
var simulationDistance: Int = -1
private set
init { init {
entityId = buffer.readInt() entityId = buffer.readInt()
@ -131,6 +133,9 @@ class JoinGameS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
if (buffer.versionId >= ProtocolVersions.V_19W13A) { if (buffer.versionId >= ProtocolVersions.V_19W13A) {
viewDistance = buffer.readVarInt() viewDistance = buffer.readVarInt()
} }
if (buffer.versionId >= ProtocolVersions.V_21W40A) {
simulationDistance = buffer.readVarInt()
}
if (buffer.versionId >= ProtocolVersions.V_20W20A) { if (buffer.versionId >= ProtocolVersions.V_20W20A) {
buffer.readBoolean() // isDebug buffer.readBoolean() // isDebug
if (buffer.readBoolean()) { 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 return array
} }
fun readLongArray(target: LongArray, size: Int = readVarInt()) {
for (i in 0 until size) {
target[i] = readLong()
}
}
fun readVarLong(): Long { fun readVarLong(): Long {
var byteCount = 0 var byteCount = 0

View File

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

View File

@ -16,6 +16,19 @@ package de.bixilon.minosoft.protocol.protocol;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ProtocolVersions { public class ProtocolVersions {
public static final int 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 = 794,
V_1_17_1_RC2 = 793, V_1_17_1_RC2 = 793,
V_1_17_1_RC1 = 792, 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 de.bixilon.minosoft.util.KUtil.decide
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import kotlin.math.ceil
import kotlin.math.floor import kotlin.math.floor
import kotlin.math.ln
object MMath { object MMath {
private const val LN_2 = 0.69314718056 // ln(2.0)
fun clamp(value: Vec2i, min: Vec2i, max: Vec2i): Vec2i { fun clamp(value: Vec2i, min: Vec2i, max: Vec2i): Vec2i {
value.x = clamp(value.x, min.x, max.x) value.x = clamp(value.x, min.x, max.x)
@ -118,4 +121,8 @@ object MMath {
val int = this.toInt() val int = this.toInt()
return (this > int).decide(int + 1, int) 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.Chunk
import de.bixilon.minosoft.data.world.ChunkData import de.bixilon.minosoft.data.world.ChunkData
import de.bixilon.minosoft.data.world.ChunkSection 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.biome.source.XZBiomeArray
import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider import de.bixilon.minosoft.data.world.container.SectionDataProvider
import de.bixilon.minosoft.data.world.palette.Palette.Companion.choosePalette 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.gui.rendering.util.vec.vec2.Vec2iUtil.abs
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.* import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*
import de.bixilon.minosoft.util.KUtil.unsafeCast
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import java.util.* import java.util.*
@ -63,13 +67,13 @@ object ChunkUtil {
// parse data // parse data
var arrayPosition = 0 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()) { for ((sectionIndex, sectionHeight) in (dimension.lowestSection until dimension.highestSection).withIndex()) {
if (!sectionBitMask[sectionIndex]) { if (!sectionBitMask[sectionIndex]) {
continue 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) { for (blockNumber in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) {
var blockId = (blockData[arrayPosition].toInt() and 0xFF) shl 4 var blockId = (blockData[arrayPosition].toInt() and 0xFF) shl 4
@ -95,7 +99,7 @@ object ChunkUtil {
blocks[blockNumber] = buffer.connection.registries.blockStateRegistry[blockId] ?: continue 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 chunkData.blocks = sectionBlocks
return chunkData return chunkData
@ -129,7 +133,7 @@ object ChunkUtil {
} }
var arrayPos = 0 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 for ((sectionIndex, sectionHeight) in (dimension.lowestSection until dimension.highestSection).withIndex()) { // max sections per chunks in chunk column
if (!sectionBitMask[sectionIndex]) { if (!sectionBitMask[sectionIndex]) {
continue continue
@ -140,56 +144,38 @@ object ChunkUtil {
val block = buffer.connection.registries.blockStateRegistry[blockId] ?: continue val block = buffer.connection.registries.blockStateRegistry[blockId] ?: continue
blocks[blockNumber] = block blocks[blockNumber] = block
} }
sectionBlocks[sectionHeight] = RegistrySectionDataProvider(buffer.connection.registries.blockStateRegistry.unsafeCast(), blocks.unsafeCast(), true) sectionBlocks[sectionHeight] = SectionDataProvider(blocks, true)
} }
chunkData.blocks = sectionBlocks chunkData.blocks = sectionBlocks
return chunkData 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 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) val light: Array<ByteArray?> = arrayOfNulls(dimension.sections)
var lightReceived = 0 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 for ((sectionIndex, sectionHeight) in (dimension.lowestSection until (sectionBitMask?.length() ?: dimension.highestSection)).withIndex()) { // max sections per chunks in chunk column
if (!sectionBitMask[sectionIndex]) { if (sectionBitMask?.get(sectionIndex) == false) {
continue continue
} }
if (buffer.versionId >= V_18W43A) { 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() if (blockContainer.palette !is SingularPalette<*> || blockContainer.palette.item != null) {
sectionBlocks[sectionHeight - dimension.lowestSection] = SectionDataProvider(blockContainer.unpack(), checkSize = true)
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)
}
} 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 (buffer.versionId >= V_21W37A) {
val biomeContainer: PalettedContainer<Biome> = PalettedContainerReader.read(buffer, buffer.connection.registries.biomeRegistry, paletteFactory = BiomePaletteFactory)
biomes[sectionHeight - dimension.lowestSection] = biomeContainer.unpack()
}
if (buffer.versionId < V_18W43A) { if (buffer.versionId < V_18W43A) {
val blockLight = buffer.readByteArray(ProtocolDefinition.BLOCKS_PER_SECTION / 2) 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) light[sectionHeight - dimension.lowestSection] = LightUtil.mergeLight(blockLight, skyLight ?: LightUtil.EMPTY_LIGHT_ARRAY)
lightReceived++ lightReceived++
} }
sectionBlocks[sectionHeight - dimension.lowestSection] = RegistrySectionDataProvider(buffer.connection.registries.blockStateRegistry.unsafeCast(), blocks.unsafeCast(), true)
} }
chunkData.blocks = sectionBlocks chunkData.blocks = sectionBlocks
if (lightReceived > 0) { if (lightReceived > 0) {
chunkData.light = light 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) chunkData.biomeSource = readLegacyBiomeArray(buffer)
} }

View File

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