refactor noise biome accessor, tests

This commit is contained in:
Moritz Zwerger 2023-12-06 16:08:54 +01:00
parent d4682fe0dc
commit cc5b2f8006
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
25 changed files with 332 additions and 83 deletions

View File

@ -20,6 +20,8 @@ import de.bixilon.kutil.observer.DataObserver
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.world.biome.WorldBiomes
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
import de.bixilon.minosoft.data.world.border.WorldBorder import de.bixilon.minosoft.data.world.border.WorldBorder
import de.bixilon.minosoft.data.world.chunk.ChunkSection.Companion.getIndex import de.bixilon.minosoft.data.world.chunk.ChunkSection.Companion.getIndex
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
@ -48,14 +50,16 @@ object WorldTestUtil {
world::entities.forceSet(WorldEntities()) world::entities.forceSet(WorldEntities())
world::view.forceSet(TEST_WORLD_VIEW) world::view.forceSet(TEST_WORLD_VIEW)
world::time.forceSet(DataObserver(WorldTime())) world::time.forceSet(DataObserver(WorldTime()))
world::biomes.forceSet(WorldBiomes(world))
return world return world
} }
fun World.initialize(size: Int) { fun World.initialize(size: Int, biome: (ChunkPosition) -> BiomeSource) {
for (x in -size..size) { for (x in -size..size) {
for (z in -size..size) { for (z in -size..size) {
chunks.create(ChunkPosition(x, z)) val position = ChunkPosition(x, z)
chunks.create(position, biome.invoke(position))
} }
} }
} }

View File

@ -0,0 +1,149 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.WorldTestUtil.initialize
import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
import de.bixilon.minosoft.data.world.biome.source.DummyBiomeSource
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil.createConnection
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import org.testng.Assert.*
import org.testng.annotations.Test
@Test(groups = ["biomes"])
class WorldBiomesTest {
private val b1 = Biome(minosoft("b1"), 0.0f, 0.0f)
private val b2 = Biome(minosoft("b2"), 0.0f, 0.0f)
private val b3 = Biome(minosoft("b3"), 0.0f, 0.0f)
private fun create(noise: ((PlayConnection) -> NoiseBiomeAccessor)?, source: (ChunkPosition) -> BiomeSource): World {
val connection = createConnection(0)
connection.world.biomes.noise = noise?.invoke(connection)
connection.world.initialize(1, source)
return connection.world
}
fun `simple biome getting at origin chunk`() {
val world = create(null) { if (it.x == 0 && it.y == 0) PositionedSource(InChunkPosition(1, 2, 3), b1, b3) else DummyBiomeSource(b2) }
assertEquals(world.biomes[1, 2, 3], b1)
assertEquals(world.biomes[1, 2, 4], b3)
assertEquals(world.biomes[16, 2, 4], b2)
assertEquals(world.biomes[BlockPosition(1, 2, 3)], b1)
assertEquals(world.biomes[BlockPosition(1, 2, 4)], b3)
assertEquals(world.biomes[BlockPosition(16, 2, 4)], b2)
val chunk = world.chunks[0, 0]!!
assertEquals(world.biomes.getBiome(1, 2, 3, chunk), b1)
assertEquals(world.biomes.getBiome(1, 2, 4, chunk), b3)
assertEquals(chunk.getBiome(1, 2, 3), b1)
assertEquals(chunk.getBiome(1, 2, 4), b3)
}
fun `simple biome getting at chunk -1, -1`() {
val world = create(null) { if (it.x == -1 && it.y == -1) PositionedSource(InChunkPosition(15, 2, 14), b1, b3) else DummyBiomeSource(b2) }
assertEquals(world.biomes[-1, 2, -2], b1)
assertEquals(world.biomes[-1, 2, -6], b3)
assertEquals(world.biomes[1, 2, 6], b2)
assertEquals(world.biomes[BlockPosition(-1, 2, -2)], b1)
val chunk = world.chunks[-1, -1]!!
assertEquals(world.biomes.getBiome(15, 2, 14, chunk), b1)
assertEquals(world.biomes.getBiome(15, 2, 13, chunk), b3)
assertEquals(chunk.getBiome(15, 2, 14), b1)
assertEquals(chunk.getBiome(15, 2, 13), b3)
}
fun `ensure no caching is done without noise`() {
val source = CounterSource(b1)
val world = create(null) { source }
val chunk = world.chunks[0, 0]!!
chunk.getOrPut(0)
assertEquals(source.counter, 0)
assertEquals(world.biomes[1, 2, 3], b1)
assertEquals(source.counter, 1)
assertFalse(chunk.cacheBiomes)
assertEquals(chunk[0]!!.biomes[1, 2, 3], null)
}
fun `ensure caching is done with noise`() {
val source = CounterSource(b1)
val world = create({ FastNoiseAccessor(it) }) { source }
val chunk = world.chunks[0, 0]!!
chunk.getOrPut(0)
assertEquals(source.counter, 4096)
assertEquals(world.biomes[1, 2, 3], b1)
assertEquals(source.counter, 4096)
assertTrue(chunk.cacheBiomes)
assertEquals(chunk[0]!!.biomes[1, 2, 3], b1)
}
fun `ensure world position is converted to chunk position`() {
val source = VerifyPositionSource(b1)
val world = create(null) { source }
assertEquals(world.biomes[1, 2, 3], b1)
assertEquals(world.biomes[16, 2, 4], b1)
assertEquals(world.biomes[-4, 2, -4], b1)
assertEquals(world.biomes[-4, -2, -4], b1)
assertEquals(world.biomes[-4, 1024, -4], b1)
}
private class PositionedSource(
val position: InChunkPosition,
val biome: Biome?,
val fallback: Biome?,
) : BiomeSource {
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
if (x != position.x || y != position.y || z != position.z) return fallback
return biome
}
}
private class CounterSource(val biome: Biome?) : BiomeSource {
var counter = 0
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
counter++
return biome
}
}
private class VerifyPositionSource(val biome: Biome?) : BiomeSource {
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
if (x < 0 || x > 15) throw IllegalArgumentException("Invalid x: $x")
if (y < 0 || y > 255) throw IllegalArgumentException("Invalid y: $y")
if (z < 0 || z > 15) throw IllegalArgumentException("Invalid z: $z")
return biome
}
}
private class FastNoiseAccessor(connection: PlayConnection) : NoiseBiomeAccessor(connection, 0L) {
override fun get(x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?): Biome? {
return chunk.biomeSource.getBiome(x, y, z)
}
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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.
* *
@ -14,12 +14,14 @@
package de.bixilon.minosoft.data.world.biome.accessor package de.bixilon.minosoft.data.world.biome.accessor
import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.world.biome.accessor.noise.VoronoiBiomeAccessor
import de.bixilon.minosoft.test.ITUtil.allocate
import org.objenesis.ObjenesisStd import org.objenesis.ObjenesisStd
import org.testng.Assert.assertEquals import org.testng.Assert.assertEquals
import org.testng.annotations.Test import org.testng.annotations.Test
@Test(groups = ["biome"]) @Test(groups = ["biome"])
class NoiseBiomeAccessorTest { class VoronoiBiomeAccessorTest {
private val OBJENESIS = ObjenesisStd() private val OBJENESIS = ObjenesisStd()
fun testBiomeNoise1() { fun testBiomeNoise1() {
@ -51,7 +53,7 @@ class NoiseBiomeAccessorTest {
} }
private fun calculate(x: Int, y: Int, z: Int, seed: Long): Vec3i { private fun calculate(x: Int, y: Int, z: Int, seed: Long): Vec3i {
val accessor = OBJENESIS.newInstance(NoiseBiomeAccessor::class.java) val accessor = VoronoiBiomeAccessor::class.java.allocate()
return accessor.getBiomePosition(seed, x, y, z) return accessor.getBiomePosition(seed, x, y, z)
} }
} }

View File

@ -21,6 +21,7 @@ import de.bixilon.kutil.reflection.ReflectionUtil.jvmField
import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.biome.WorldBiomes
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkLight import de.bixilon.minosoft.data.world.chunk.light.ChunkLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
@ -48,6 +49,7 @@ object LightTestingUtil {
val world = objenesis.newInstance(World::class.java) val world = objenesis.newInstance(World::class.java)
world::dimension.forceSet(DataObserver(DimensionProperties(skyLight = true))) world::dimension.forceSet(DataObserver(DimensionProperties(skyLight = true)))
world::connection.forceSet(createConnection()) world::connection.forceSet(createConnection())
world::biomes.forceSet(WorldBiomes(world))
return world return world
} }

View File

@ -17,7 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0 import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor import de.bixilon.minosoft.data.world.biome.accessor.noise.VoronoiBiomeAccessor
import de.bixilon.minosoft.data.world.biome.source.BiomeSource import de.bixilon.minosoft.data.world.biome.source.BiomeSource
import de.bixilon.minosoft.data.world.biome.source.DummyBiomeSource import de.bixilon.minosoft.data.world.biome.source.DummyBiomeSource
import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
@ -428,7 +428,7 @@ class ChunkManagerTest {
fun noBiomeCache() { fun noBiomeCache() {
val manager = create() val manager = create()
manager.world.cacheBiomeAccessor = null manager.world.biomes.noise = null
val matrix = manager.createMatrix() val matrix = manager.createMatrix()
val chunk = matrix[1][1] val chunk = matrix[1][1]
@ -438,7 +438,7 @@ class ChunkManagerTest {
fun noiseBiomeCache() { fun noiseBiomeCache() {
val manager = create() val manager = create()
val biome = Biome(minosoft("test"), 0.0f, 0.0f, null, null, null, null, null) val biome = Biome(minosoft("test"), 0.0f, 0.0f, null, null, null, null, null)
manager.world.cacheBiomeAccessor = NoiseBiomeAccessor(manager.world.connection, 0L) manager.world.biomes.noise = VoronoiBiomeAccessor(manager.world.connection, 0L)
val source = SpatialBiomeArray(Array(1024) { biome }) val source = SpatialBiomeArray(Array(1024) { biome })
val matrix = manager.createMatrix(source) val matrix = manager.createMatrix(source)
@ -446,7 +446,7 @@ class ChunkManagerTest {
assertEquals(chunk.getOrPut(0)!!.biomes.count, 4096) assertEquals(chunk.getOrPut(0)!!.biomes.count, 4096)
assertEquals(chunk.getOrPut(0)!!.biomes[0], biome) assertEquals(chunk.getOrPut(0)!!.biomes[0], biome)
assertEquals(manager.world.getBiome(BlockPosition(5, 5, 5)), biome) assertEquals(manager.world.biomes.getBiome(BlockPosition(5, 5, 5)), biome)
assertEquals(chunk.getBiome(BlockPosition(5, 5, 5)), biome) assertEquals(chunk.getBiome(BlockPosition(5, 5, 5)), biome)
} }
} }

View File

@ -30,6 +30,7 @@ import de.bixilon.minosoft.data.language.lang.LanguageList
import de.bixilon.minosoft.data.registries.registries.Registries import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.world.WorldTestUtil.createWorld import de.bixilon.minosoft.data.world.WorldTestUtil.createWorld
import de.bixilon.minosoft.data.world.WorldTestUtil.initialize import de.bixilon.minosoft.data.world.WorldTestUtil.initialize
import de.bixilon.minosoft.data.world.biome.source.DummyBiomeSource
import de.bixilon.minosoft.modding.event.master.EventMaster import de.bixilon.minosoft.modding.event.master.EventMaster
import de.bixilon.minosoft.protocol.network.network.client.test.TestNetwork import de.bixilon.minosoft.protocol.network.network.client.test.TestNetwork
import de.bixilon.minosoft.protocol.versions.Versions import de.bixilon.minosoft.protocol.versions.Versions
@ -93,7 +94,7 @@ object ConnectionTestUtil {
CAMERA.forceSet(connection, ConnectionCamera(connection)) CAMERA.forceSet(connection, ConnectionCamera(connection))
connection.camera.init() connection.camera.init()
if (worldSize > 0) { if (worldSize > 0) {
connection.world.initialize(worldSize) connection.world.initialize(worldSize) { DummyBiomeSource(null) }
} }
return connection return connection
} }

View File

@ -120,7 +120,7 @@ abstract class DoublePlant(identifier: ResourceLocation, settings: BlockSettings
override val tintProvider: TintProvider? = null override val tintProvider: TintProvider? = null
override fun initTint(manager: TintManager) { override fun initTint(manager: TintManager) {
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grassTintCalculator)) this::tintProvider.forceSet(TallGrassTintCalculator(manager.grass))
} }
companion object : BlockFactory<TallGrass> { companion object : BlockFactory<TallGrass> {
@ -135,7 +135,7 @@ abstract class DoublePlant(identifier: ResourceLocation, settings: BlockSettings
override val tintProvider: TintProvider? = null override val tintProvider: TintProvider? = null
override fun initTint(manager: TintManager) { override fun initTint(manager: TintManager) {
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grassTintCalculator)) this::tintProvider.forceSet(TallGrassTintCalculator(manager.grass))
} }
companion object : BlockFactory<LargeFern> { companion object : BlockFactory<LargeFern> {
@ -173,7 +173,7 @@ abstract class DoublePlant(identifier: ResourceLocation, settings: BlockSettings
} }
override fun initTint(manager: TintManager) { override fun initTint(manager: TintManager) {
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grassTintCalculator)) // TODO: only tint if lower block is tinted this::tintProvider.forceSet(TallGrassTintCalculator(manager.grass)) // TODO: only tint if lower block is tinted
} }
private class Model : PickedBlockRender { private class Model : PickedBlockRender {

View File

@ -47,7 +47,7 @@ abstract class LeavesBlock(identifier: ResourceLocation, settings: BlockSettings
override val tintProvider: TintProvider? = null override val tintProvider: TintProvider? = null
override fun initTint(manager: TintManager) { override fun initTint(manager: TintManager) {
this::tintProvider.forceSet(manager.foliageTintCalculator) this::tintProvider.forceSet(manager.foliage)
} }
override fun buildState(version: Version, settings: BlockStateSettings): BlockState { override fun buildState(version: Version, settings: BlockStateSettings): BlockState {

View File

@ -20,7 +20,6 @@ import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.kutil.random.RandomUtil.nextInt import de.bixilon.kutil.random.RandomUtil.nextInt
import de.bixilon.minosoft.data.entities.block.BlockEntity import de.bixilon.minosoft.data.entities.block.BlockEntity
import de.bixilon.minosoft.data.entities.entities.Entity import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.blocks.types.properties.rendering.RandomDisplayTickable import de.bixilon.minosoft.data.registries.blocks.types.properties.rendering.RandomDisplayTickable
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
@ -28,8 +27,7 @@ import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.registries.shapes.aabb.AABB import de.bixilon.minosoft.data.registries.shapes.aabb.AABB
import de.bixilon.minosoft.data.world.audio.AbstractAudioPlayer import de.bixilon.minosoft.data.world.audio.AbstractAudioPlayer
import de.bixilon.minosoft.data.world.audio.WorldAudioPlayer import de.bixilon.minosoft.data.world.audio.WorldAudioPlayer
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor import de.bixilon.minosoft.data.world.biome.WorldBiomes
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.border.WorldBorder import de.bixilon.minosoft.data.world.border.WorldBorder
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
@ -57,10 +55,10 @@ import java.util.*
*/ */
class World( class World(
val connection: PlayConnection, val connection: PlayConnection,
) : BiomeAccessor, WorldAudioPlayer, WorldParticleRenderer { ) : WorldAudioPlayer, WorldParticleRenderer {
val lock = SimpleLock() val lock = SimpleLock()
val random = Random() val random = Random()
var cacheBiomeAccessor: NoiseBiomeAccessor? = null val biomes = WorldBiomes(this)
val chunks = ChunkManager(this, 1000, 100) val chunks = ChunkManager(this, 1000, 100)
val entities = WorldEntities() val entities = WorldEntities()
var hardcore by observed(false) var hardcore by observed(false)
@ -149,14 +147,6 @@ class World(
chunks[position.chunkPosition]?.set(position.inChunkPosition, entity) // TODO: fire event if needed chunks[position.chunkPosition]?.set(position.inChunkPosition, entity) // TODO: fire event if needed
} }
override fun getBiome(blockPosition: BlockPosition): Biome? {
return chunks[blockPosition.chunkPosition]?.getBiome(blockPosition.inChunkPosition)
}
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
return this.chunks[Vec2i(x shr 4, z shr 4)]?.getBiome(x and 0x0F, y, z and 0x0F)
}
fun tick() { fun tick() {
val simulationDistance = view.simulationDistance val simulationDistance = view.simulationDistance
val cameraPosition = connection.player.physics.positionInfo.chunkPosition val cameraPosition = connection.player.physics.positionInfo.chunkPosition

View File

@ -16,7 +16,7 @@ package de.bixilon.minosoft.data.world.audio
import de.bixilon.kotlinglm.vec3.Vec3d import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
@Deprecated("") @Deprecated("use world.audio")
interface WorldAudioPlayer : AbstractAudioPlayer { interface WorldAudioPlayer : AbstractAudioPlayer {
val audioPlayer: AbstractAudioPlayer? val audioPlayer: AbstractAudioPlayer?

View File

@ -0,0 +1,52 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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
import de.bixilon.kotlinglm.func.common.clamp
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
class WorldBiomes(val world: World) : BiomeAccessor {
var noise: NoiseBiomeAccessor? = null
set(value) {
field = value
resetCache()
}
operator fun get(position: BlockPosition) = getBiome(position)
override fun getBiome(position: BlockPosition) = getBiome(position.x, position.y, position.z)
operator fun get(x: Int, y: Int, z: Int) = getBiome(x, y, z)
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
val chunk = world.chunks[x shr 4, z shr 4] ?: return null
return getBiome(x and 0x0F, y.clamp(world.dimension.minY, world.dimension.maxY), z and 0x0F, chunk)
}
fun getBiome(x: Int, y: Int, z: Int, chunk: Chunk): Biome? {
val noise = this.noise ?: return chunk.biomeSource.getBiome(x, y, z)
chunk[y.sectionHeight]?.let { return it.biomes[x, y, z] } // access cache
return noise.get(x, y, z, chunk)
}
fun resetCache() {
// TODO
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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.
* *
@ -19,8 +19,8 @@ import de.bixilon.minosoft.data.registries.biomes.Biome
interface BiomeAccessor { interface BiomeAccessor {
fun getBiome(blockPosition: Vec3i): Biome? { fun getBiome(position: Vec3i): Biome? {
return getBiome(blockPosition.x, blockPosition.y, blockPosition.z) return getBiome(position.x, position.y, position.z)
} }
fun getBiome(x: Int, y: Int, z: Int): Biome? fun getBiome(x: Int, y: Int, z: Int): Biome?

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 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.
* *
@ -19,7 +19,7 @@ import de.bixilon.minosoft.data.registries.biomes.Biome
object NullBiomeAccessor : BiomeAccessor { object NullBiomeAccessor : BiomeAccessor {
override fun getBiome(blockPosition: Vec3i): Biome? { override fun getBiome(position: Vec3i): Biome? {
return null return null
} }

View File

@ -0,0 +1,49 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.accessor.noise
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_19W36A
abstract class NoiseBiomeAccessor(
connection: PlayConnection,
val seed: Long = 0L,
) {
protected val world = connection.world
protected var fastNoise = false
init {
val profile = connection.profiles.rendering
profile.performance::fastBiomeNoise.observe(this, true) { fastNoise = it }
}
abstract fun get(x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?): Biome?
fun get(x: Int, y: Int, z: Int, chunk: Chunk): Biome? {
val neighbours = chunk.neighbours.get() ?: return null
return get((chunk.chunkPosition.x shl 4) or x, y, (chunk.chunkPosition.y shl 4) or z, chunk.chunkPosition.x, chunk.chunkPosition.y, chunk, neighbours)
}
companion object {
fun get(connection: PlayConnection, seed: Long): NoiseBiomeAccessor? = when {
connection.version < V_19W36A -> null
else -> VoronoiBiomeAccessor(connection, seed)
}
}
}

View File

@ -11,30 +11,22 @@
* 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.biome.accessor package de.bixilon.minosoft.data.world.biome.accessor.noise
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.kutil.math.simple.DoubleMath.square import de.bixilon.kutil.math.simple.DoubleMath.square
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
class NoiseBiomeAccessor( open class VoronoiBiomeAccessor(
private val connection: PlayConnection, connection: PlayConnection,
var hashedSeed: Long = 0L, seed: Long = 0L,
) { ) : NoiseBiomeAccessor(connection, seed) {
private val world = connection.world
private var fastNoise = false
init { override fun get(x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?): Biome? {
val profile = connection.profiles.rendering
profile.performance::fastBiomeNoise.observe(this, true) { fastNoise = it }
}
fun getBiome(x: Int, y: Int, z: Int, chunkPositionX: Int, chunkPositionZ: Int, chunk: Chunk, neighbours: Array<Chunk>?): Biome? {
val biomeY = if (world.dimension.supports3DBiomes) y else 0 val biomeY = if (world.dimension.supports3DBiomes) y else 0
if (fastNoise) { if (fastNoise) {
@ -44,7 +36,7 @@ class NoiseBiomeAccessor(
return source.data[(biomeY and 0x0F) shr 2 and 0x3F shl 4 or ((z and 0x0F) shr 2 and 0x03 shl 2) or ((x and 0x0F) shr 2 and 0x03)] return source.data[(biomeY and 0x0F) shr 2 and 0x3F shl 4 or ((z and 0x0F) shr 2 and 0x03 shl 2) or ((x and 0x0F) shr 2 and 0x03)]
} }
return getBiome(hashedSeed, x, biomeY, z, chunkPositionX, chunkPositionZ, chunk, neighbours) return getBiome(seed, x, biomeY, z, chunkPositionX, chunkPositionZ, chunk, neighbours)
} }
@ -156,17 +148,17 @@ class NoiseBiomeAccessor(
ret = next(ret, y) ret = next(ret, y)
ret = next(ret, z) ret = next(ret, z)
val xFractionSalt = distribute(ret) val xSalt = distribute(ret)
ret = next(ret, seed) ret = next(ret, seed)
val yFractionSalt = distribute(ret) val ySalt = distribute(ret)
ret = next(ret, seed) ret = next(ret, seed)
val zFractionSalt = distribute(ret) val zSalt = distribute(ret)
return (xFraction + xFractionSalt).square() + (yFraction + yFractionSalt).square() + (zFraction + zFractionSalt).square() return (xFraction + xSalt).square() + (yFraction + ySalt).square() + (zFraction + zSalt).square()
} }
private fun distribute(seed: Long): Double { private fun distribute(seed: Long): Double {

View File

@ -19,7 +19,7 @@ import de.bixilon.kutil.cast.CastUtil.unsafeNull
import de.bixilon.kutil.reflection.ReflectionUtil.jvmField import de.bixilon.kutil.reflection.ReflectionUtil.jvmField
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.world.biome.accessor.NoiseBiomeAccessor import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.SectionLight import de.bixilon.minosoft.data.world.chunk.light.SectionLight
import de.bixilon.minosoft.data.world.container.SectionDataProvider import de.bixilon.minosoft.data.world.container.SectionDataProvider
@ -67,7 +67,7 @@ class ChunkSection(
} }
} }
fun buildBiomeCache(neighbours: Array<Chunk>, biomeAccessor: NoiseBiomeAccessor) { fun buildBiomeCache(neighbours: Array<Chunk>, noise: NoiseBiomeAccessor) {
val chunkPositionX = chunk.chunkPosition.x val chunkPositionX = chunk.chunkPosition.x
val chunkPositionZ = chunk.chunkPosition.y val chunkPositionZ = chunk.chunkPosition.y
val blockOffset = Vec3i.of(chunk.chunkPosition, sectionHeight) val blockOffset = Vec3i.of(chunk.chunkPosition, sectionHeight)
@ -76,7 +76,7 @@ class ChunkSection(
val z = blockOffset.z val z = blockOffset.z
val biomes: Array<Biome?> = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) val biomes: Array<Biome?> = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION)
for (index in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) { for (index in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) {
biomes[index] = biomeAccessor.getBiome(x + (index and 0x0F), y + ((index shr 8) and 0x0F), z + ((index shr 4) and 0x0F), chunkPositionX, chunkPositionZ, chunk, neighbours) biomes[index] = noise.get(x + (index and 0x0F), y + ((index shr 8) and 0x0F), z + ((index shr 4) and 0x0F), chunkPositionX, chunkPositionZ, chunk, neighbours)
} }
this.biomes.setData(biomes.cast()) this.biomes.setData(biomes.cast())
} }

View File

@ -52,7 +52,7 @@ class Chunk(
val light = ChunkLight(this) val light = ChunkLight(this)
val minSection = world.dimension.minSection val minSection = world.dimension.minSection
val maxSection = world.dimension.maxSection val maxSection = world.dimension.maxSection
val cacheBiomes = world.cacheBiomeAccessor != null val cacheBiomes = world.biomes.noise != null
val neighbours = ChunkNeighbours(this) val neighbours = ChunkNeighbours(this)
@ -187,7 +187,7 @@ class Chunk(
section.blocks.unsafeSetSection(section) section.blocks.unsafeSetSection(section)
val neighbours = this.neighbours.get() val neighbours = this.neighbours.get()
if (neighbours != null) { if (neighbours != null) {
this.neighbours.completeSection(neighbours, section, sectionHeight, world.cacheBiomeAccessor) this.neighbours.completeSection(neighbours, section, sectionHeight, world.biomes.noise)
} }
sections[index] = section sections[index] = section
@ -230,11 +230,10 @@ class Chunk(
override fun getBiome(x: Int, y: Int, z: Int): Biome? { override fun getBiome(x: Int, y: Int, z: Int): Biome? {
val y = y.clamp(world.dimension.minY, world.dimension.maxY) val y = y.clamp(world.dimension.minY, world.dimension.maxY)
if (cacheBiomes) { if (!cacheBiomes) {
val section = this[y.sectionHeight] ?: return connection.world.cacheBiomeAccessor?.getBiome((chunkPosition.x shl 4) or x, y, (chunkPosition.y shl 4) or z, chunkPosition.x, chunkPosition.y, this, this.neighbours.get()) return biomeSource.getBiome(x, y, z)
return section.biomes[x, y.inSectionHeight, z]
} }
return biomeSource.getBiome(x and 0x0F, y, z and 0x0F) return connection.world.biomes.getBiome(x, y, z, this)
} }
} }

View File

@ -18,7 +18,7 @@ import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.exception.Broken import de.bixilon.kutil.exception.Broken
import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.chunk.ChunkSection import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition
@ -70,19 +70,19 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
remove(getIndex(offset)) remove(getIndex(offset))
} }
fun completeSection(neighbours: Array<Chunk>, section: ChunkSection, sectionHeight: SectionHeight, biomeCacheAccessor: NoiseBiomeAccessor?) { fun completeSection(neighbours: Array<Chunk>, section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) {
section.neighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, sectionHeight) section.neighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, sectionHeight)
if (biomeCacheAccessor != null) { if (noise != null) {
section.buildBiomeCache(neighbours, biomeCacheAccessor) section.buildBiomeCache(neighbours, noise)
} }
} }
private fun complete(neighbours: Array<Chunk>) { private fun complete(neighbours: Array<Chunk>) {
val biomeCacheAccessor = chunk.world.cacheBiomeAccessor val noise = chunk.world.biomes.noise
for ((index, section) in chunk.sections.withIndex()) { for ((index, section) in chunk.sections.withIndex()) {
if (section == null) continue if (section == null) continue
val sectionHeight = index + chunk.minSection val sectionHeight = index + chunk.minSection
completeSection(neighbours, section, sectionHeight, biomeCacheAccessor) completeSection(neighbours, section, sectionHeight, noise)
} }
chunk.light.recalculate(false) chunk.light.recalculate(false)
chunk.light.propagateFromNeighbours(fireEvent = false, fireSameChunkEvent = false) chunk.light.propagateFromNeighbours(fireEvent = false, fireSameChunkEvent = false)

View File

@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.world.particle
import de.bixilon.minosoft.gui.rendering.particle.types.Particle import de.bixilon.minosoft.gui.rendering.particle.types.Particle
@Deprecated("") @Deprecated("use world.particle")
interface WorldParticleRenderer : AbstractParticleRenderer { interface WorldParticleRenderer : AbstractParticleRenderer {
val particleRenderer: AbstractParticleRenderer? val particleRenderer: AbstractParticleRenderer?

View File

@ -28,10 +28,10 @@ import de.bixilon.minosoft.gui.rendering.tint.tints.redstone.RedstoneWireTintCal
object DefaultTints { object DefaultTints {
fun init(manager: TintManager) { fun init(manager: TintManager) {
manager.applyTo(setOf(MinecraftBlocks.POTTED_FERN), manager.grassTintCalculator) manager.applyTo(setOf(MinecraftBlocks.POTTED_FERN), manager.grass)
manager.applyTo(setOf(MinecraftBlocks.REDSTONE_WIRE), RedstoneWireTintCalculator) manager.applyTo(setOf(MinecraftBlocks.REDSTONE_WIRE), RedstoneWireTintCalculator)
manager.applyTo(setOf(MinecraftBlocks.WATER_CAULDRON, MinecraftBlocks.CAULDRON), WaterTintProvider) manager.applyTo(setOf(MinecraftBlocks.WATER_CAULDRON, MinecraftBlocks.CAULDRON), WaterTintProvider)
manager.applyTo(setOf(MinecraftBlocks.SUGAR_CANE), SugarCaneTintCalculator(manager.grassTintCalculator)) manager.applyTo(setOf(MinecraftBlocks.SUGAR_CANE), SugarCaneTintCalculator(manager.grass))
manager.applyTo(setOf(MinecraftBlocks.ATTACHED_MELON_STEM, MinecraftBlocks.ATTACHED_PUMPKIN_STEM), StaticTintProvider(0xE0C71C)) manager.applyTo(setOf(MinecraftBlocks.ATTACHED_MELON_STEM, MinecraftBlocks.ATTACHED_PUMPKIN_STEM), StaticTintProvider(0xE0C71C))
manager.applyTo(setOf(MinecraftBlocks.MELON_STEM, MinecraftBlocks.PUMPKIN_STEM), StemTintCalculator) manager.applyTo(setOf(MinecraftBlocks.MELON_STEM, MinecraftBlocks.PUMPKIN_STEM), StemTintCalculator)
manager.applyTo(setOf(MinecraftBlocks.LILY_PAD), StaticTintProvider(block = 0x208030, item = 0x71C35C)) manager.applyTo(setOf(MinecraftBlocks.LILY_PAD), StaticTintProvider(block = 0x208030, item = 0x71C35C))

View File

@ -28,12 +28,12 @@ import de.bixilon.minosoft.gui.rendering.tint.tints.plants.FoliageTintCalculator
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
class TintManager(val connection: PlayConnection) { class TintManager(val connection: PlayConnection) {
val grassTintCalculator = GrassTintCalculator() val grass = GrassTintCalculator()
val foliageTintCalculator = FoliageTintCalculator() val foliage = FoliageTintCalculator()
fun init(assetsManager: AssetsManager) { fun init(assetsManager: AssetsManager) {
grassTintCalculator.init(assetsManager) grass.init(assetsManager)
foliageTintCalculator.init(assetsManager) foliage.init(assetsManager)
for (block in connection.registries.block) { for (block in connection.registries.block) {
if (block !is TintedBlock) continue if (block !is TintedBlock) continue
@ -61,7 +61,8 @@ class TintManager(val connection: PlayConnection) {
fun getParticleTint(state: BlockState, x: Int, y: Int, z: Int): Int? { fun getParticleTint(state: BlockState, x: Int, y: Int, z: Int): Int? {
if (state.block !is TintedBlock) return null if (state.block !is TintedBlock) return null
val tintProvider = state.block.tintProvider ?: return null val tintProvider = state.block.tintProvider ?: return null
val biome = connection.world.getBiome(x, y, z) // TODO: cache chunk
val biome = connection.world.biomes.getBiome(x, y, z)
return tintProvider.getParticleColor(state, biome, x, y, z) return tintProvider.getParticleColor(state, biome, x, y, z)
} }

View File

@ -20,6 +20,6 @@ import de.bixilon.minosoft.gui.rendering.tint.TintedBlock
interface GrassTinted : TintedBlock { interface GrassTinted : TintedBlock {
override fun initTint(manager: TintManager) { override fun initTint(manager: TintManager) {
this::class.java.getDeclaredField("tintProvider").forceSet(this, manager.grassTintCalculator) this::class.java.getDeclaredField("tintProvider").forceSet(this, manager.grass)
} }
} }

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.data.entities.GlobalPosition
import de.bixilon.minosoft.data.entities.data.types.GlobalPositionEntityDataType import de.bixilon.minosoft.data.entities.data.types.GlobalPositionEntityDataType
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.difficulty.Difficulties import de.bixilon.minosoft.data.world.difficulty.Difficulties
import de.bixilon.minosoft.modding.event.events.DimensionChangeEvent import de.bixilon.minosoft.modding.event.events.DimensionChangeEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -180,9 +180,7 @@ class InitializeS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
connection.world.entities.clear(connection, local = true) connection.world.entities.clear(connection, local = true)
connection.world.entities.add(entityId, null, playerEntity) connection.world.entities.add(entityId, null, playerEntity)
if (connection.version.versionId >= ProtocolVersions.V_19W36A && !connection.profiles.rendering.performance.fastBiomeNoise) { connection.world.biomes.noise = NoiseBiomeAccessor.get(connection, hashedSeed)
connection.world.cacheBiomeAccessor = NoiseBiomeAccessor(connection, hashedSeed)
}
connection.world.border.reset() connection.world.border.reset()
if (connection.version < V_1_20_2_PRE1) { if (connection.version < V_1_20_2_PRE1) {

View File

@ -18,6 +18,7 @@ import de.bixilon.minosoft.data.entities.GlobalPosition
import de.bixilon.minosoft.data.entities.data.types.GlobalPositionEntityDataType import de.bixilon.minosoft.data.entities.data.types.GlobalPositionEntityDataType
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.difficulty.Difficulties import de.bixilon.minosoft.data.world.difficulty.Difficulties
import de.bixilon.minosoft.modding.event.events.DimensionChangeEvent import de.bixilon.minosoft.modding.event.events.DimensionChangeEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -114,7 +115,7 @@ class RespawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
} }
connection.world.dimension = dimension connection.world.dimension = dimension
connection.world.name = world connection.world.name = world
connection.world.cacheBiomeAccessor?.hashedSeed = hashedSeed connection.world.biomes.noise = NoiseBiomeAccessor.get(connection, hashedSeed)
connection.state = PlayConnectionStates.SPAWNING connection.state = PlayConnectionStates.SPAWNING
if (dimensionChange) { if (dimensionChange) {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk package de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
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.buffers.play.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
@ -23,7 +24,7 @@ class ChunkBiomeS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
val data = buffer.readArray { ChunkBiomeData(buffer.readLongChunkPosition(), buffer.readByteArray()) } val data = buffer.readArray { ChunkBiomeData(buffer.readLongChunkPosition(), buffer.readByteArray()) }
class ChunkBiomeData( data class ChunkBiomeData(
val position: Vec2i, val position: Vec2i,
val data: ByteArray, val data: ByteArray,
) )
@ -33,7 +34,15 @@ class ChunkBiomeS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
return Vec2i(long.toInt(), (long shr 32).toInt()) return Vec2i(long.toInt(), (long shr 32).toInt())
} }
override fun handle(connection: PlayConnection) {
if (data.isEmpty()) return
for ((position, data) in data) {
val chunk = connection.world.chunks[position] ?: continue
// TODO: handle // TODO: handle
}
connection.world.biomes.resetCache()
}
override fun log(reducedLog: Boolean) { override fun log(reducedLog: Boolean) {
Log.log(LogMessageType.NETWORK_IN, level = LogLevels.VERBOSE) { "Chunk biome (data=$data)" } Log.log(LogMessageType.NETWORK_IN, level = LogLevels.VERBOSE) { "Chunk biome (data=$data)" }