chunk reading: check early if data is empty

Now it does not allocate chunk data when the section is truly empty (servers sometimes send the opposite and then it turns out that they are empty)
This commit is contained in:
Moritz Zwerger 2025-02-08 21:06:05 +01:00
parent 1b869e4761
commit d3dfcb2a92
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 39 additions and 18 deletions

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.data.world.container.palette
import de.bixilon.minosoft.data.registries.registries.registry.AbstractRegistry 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.data.PaletteData
import de.bixilon.minosoft.data.world.container.palette.palettes.BlockStatePaletteFactory
import de.bixilon.minosoft.data.world.container.palette.palettes.PaletteFactory import de.bixilon.minosoft.data.world.container.palette.palettes.PaletteFactory
import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer
@ -41,16 +42,27 @@ object PalettedContainerReader {
val data = PaletteData.create(buffer.versionId, palette.bits, factory.containerSize) val data = PaletteData.create(buffer.versionId, palette.bits, factory.containerSize)
try { try {
data.read(buffer) data.read(buffer)
if (factory == BlockStatePaletteFactory && data.isEmpty) { // id 0 is air
return null
}
val container = PalettedContainer(factory.edgeBits, palette, data) val container = PalettedContainer(factory.edgeBits, palette, data)
if (container.isEmpty) return null if (container.isEmpty) return null
val unpacked = container.unpack<T>() val unpacked = container.unpack<T>()
if (unpacked.isEmpty()) return null if (unpacked.isAllNull()) return null
return unpacked return unpacked
} finally { } finally {
data.free() data.free()
} }
} }
fun <T> Array<T>.isAllNull(): Boolean {
for (entry in this) {
if (entry == null) continue
return false
}
return true
}
} }

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.data.world.container.palette.data
import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer
class EmptyPaletteData(override val size: Int) : PaletteData { class EmptyPaletteData(override val size: Int) : PaletteData {
override val isEmpty: Boolean get() = true
override fun free() = Unit override fun free() = Unit

View File

@ -17,6 +17,7 @@ import de.bixilon.minosoft.data.world.container.palette.data.array.ArrayPaletteD
import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer
interface PaletteData { interface PaletteData {
val isEmpty: Boolean
val size: Int val size: Int
fun get(index: Int): Int fun get(index: Int): Int

View File

@ -27,25 +27,40 @@ class ArrayPaletteData(
private val valuesPerLong = Long.SIZE_BITS / elementBits private val valuesPerLong = Long.SIZE_BITS / elementBits
private lateinit var data: LongArray private lateinit var data: LongArray
override var isEmpty = true
init { init {
check(elementBits in 0..32) check(elementBits in 0..32)
} }
override fun read(buffer: PlayInByteBuffer) { private fun calculateLongs(): Int {
val packetSize = buffer.readVarInt() if (versionId < LONG_BIT_SPLITTING_VERSION) {
val size: Int = if (versionId < LONG_BIT_SPLITTING_VERSION) {
val bits = this.size * elementBits val bits = this.size * elementBits
(bits + (Long.SIZE_BITS - 1)) / Long.SIZE_BITS // divide up return (bits + (Long.SIZE_BITS - 1)) / Long.SIZE_BITS // divide up
} else {
(this.size + valuesPerLong - 1) / valuesPerLong
} }
this.data = ALLOCATOR.allocate(this.size) return (this.size + valuesPerLong - 1) / valuesPerLong
if (packetSize != size) { }
private fun checkEmpty(size: Int): Boolean {
for (i in 0 until size) {
if (this.data[i] != 0L) {
return false
}
}
return true
}
override fun read(buffer: PlayInByteBuffer) {
val packetSize = buffer.readVarInt()
val longs = calculateLongs()
this.data = ALLOCATOR.allocate(longs)
if (packetSize != longs) {
buffer.pointer += packetSize * Long.SIZE_BYTES // data is ignored buffer.pointer += packetSize * Long.SIZE_BYTES // data is ignored
return return
} }
buffer.readLongArray(this.data, size) buffer.readLongArray(this.data, longs)
this.isEmpty = checkEmpty(longs)
} }
override operator fun get(index: Int): Int { override operator fun get(index: Int): Int {

View File

@ -305,12 +305,4 @@ object ChunkUtil {
fun ChunkPosition.isInViewDistance(viewDistance: Int, cameraPosition: ChunkPosition): Boolean { fun ChunkPosition.isInViewDistance(viewDistance: Int, cameraPosition: ChunkPosition): Boolean {
return abs(this.x - cameraPosition.x) <= viewDistance && abs(this.y - cameraPosition.y) <= viewDistance return abs(this.x - cameraPosition.x) <= viewDistance && abs(this.y - cameraPosition.y) <= viewDistance
} }
private fun Array<BlockState?>.isEmpty(): Boolean {
for (entry in this) {
if (entry == null) continue
return false
}
return true
}
} }