mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 19:35:00 -04:00
refactor noise biome accessor, tests
This commit is contained in:
parent
d4682fe0dc
commit
cc5b2f8006
@ -20,6 +20,8 @@ import de.bixilon.kutil.observer.DataObserver
|
||||
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
|
||||
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
||||
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.chunk.ChunkSection.Companion.getIndex
|
||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||
@ -48,14 +50,16 @@ object WorldTestUtil {
|
||||
world::entities.forceSet(WorldEntities())
|
||||
world::view.forceSet(TEST_WORLD_VIEW)
|
||||
world::time.forceSet(DataObserver(WorldTime()))
|
||||
world::biomes.forceSet(WorldBiomes(world))
|
||||
|
||||
return world
|
||||
}
|
||||
|
||||
fun World.initialize(size: Int) {
|
||||
fun World.initialize(size: Int, biome: (ChunkPosition) -> BiomeSource) {
|
||||
for (x in -size..size) {
|
||||
for (z in -size..size) {
|
||||
chunks.create(ChunkPosition(x, z))
|
||||
val position = ChunkPosition(x, z)
|
||||
chunks.create(position, biome.invoke(position))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
@ -14,12 +14,14 @@
|
||||
package de.bixilon.minosoft.data.world.biome.accessor
|
||||
|
||||
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.testng.Assert.assertEquals
|
||||
import org.testng.annotations.Test
|
||||
|
||||
@Test(groups = ["biome"])
|
||||
class NoiseBiomeAccessorTest {
|
||||
class VoronoiBiomeAccessorTest {
|
||||
private val OBJENESIS = ObjenesisStd()
|
||||
|
||||
fun testBiomeNoise1() {
|
||||
@ -51,7 +53,7 @@ class NoiseBiomeAccessorTest {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@ -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.dimension.DimensionProperties
|
||||
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.light.ChunkLight
|
||||
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
|
||||
@ -48,6 +49,7 @@ object LightTestingUtil {
|
||||
val world = objenesis.newInstance(World::class.java)
|
||||
world::dimension.forceSet(DataObserver(DimensionProperties(skyLight = true)))
|
||||
world::connection.forceSet(createConnection())
|
||||
world::biomes.forceSet(WorldBiomes(world))
|
||||
|
||||
return world
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0
|
||||
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.DummyBiomeSource
|
||||
import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
|
||||
@ -428,7 +428,7 @@ class ChunkManagerTest {
|
||||
|
||||
fun noBiomeCache() {
|
||||
val manager = create()
|
||||
manager.world.cacheBiomeAccessor = null
|
||||
manager.world.biomes.noise = null
|
||||
val matrix = manager.createMatrix()
|
||||
|
||||
val chunk = matrix[1][1]
|
||||
@ -438,7 +438,7 @@ class ChunkManagerTest {
|
||||
fun noiseBiomeCache() {
|
||||
val manager = create()
|
||||
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 matrix = manager.createMatrix(source)
|
||||
|
||||
@ -446,7 +446,7 @@ class ChunkManagerTest {
|
||||
assertEquals(chunk.getOrPut(0)!!.biomes.count, 4096)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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.world.WorldTestUtil.createWorld
|
||||
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.protocol.network.network.client.test.TestNetwork
|
||||
import de.bixilon.minosoft.protocol.versions.Versions
|
||||
@ -93,7 +94,7 @@ object ConnectionTestUtil {
|
||||
CAMERA.forceSet(connection, ConnectionCamera(connection))
|
||||
connection.camera.init()
|
||||
if (worldSize > 0) {
|
||||
connection.world.initialize(worldSize)
|
||||
connection.world.initialize(worldSize) { DummyBiomeSource(null) }
|
||||
}
|
||||
return connection
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ abstract class DoublePlant(identifier: ResourceLocation, settings: BlockSettings
|
||||
override val tintProvider: TintProvider? = null
|
||||
|
||||
override fun initTint(manager: TintManager) {
|
||||
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grassTintCalculator))
|
||||
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grass))
|
||||
}
|
||||
|
||||
companion object : BlockFactory<TallGrass> {
|
||||
@ -135,7 +135,7 @@ abstract class DoublePlant(identifier: ResourceLocation, settings: BlockSettings
|
||||
override val tintProvider: TintProvider? = null
|
||||
|
||||
override fun initTint(manager: TintManager) {
|
||||
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grassTintCalculator))
|
||||
this::tintProvider.forceSet(TallGrassTintCalculator(manager.grass))
|
||||
}
|
||||
|
||||
companion object : BlockFactory<LargeFern> {
|
||||
@ -173,7 +173,7 @@ abstract class DoublePlant(identifier: ResourceLocation, settings: BlockSettings
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -47,7 +47,7 @@ abstract class LeavesBlock(identifier: ResourceLocation, settings: BlockSettings
|
||||
override val tintProvider: TintProvider? = null
|
||||
|
||||
override fun initTint(manager: TintManager) {
|
||||
this::tintProvider.forceSet(manager.foliageTintCalculator)
|
||||
this::tintProvider.forceSet(manager.foliage)
|
||||
}
|
||||
|
||||
override fun buildState(version: Version, settings: BlockStateSettings): BlockState {
|
||||
|
@ -20,7 +20,6 @@ import de.bixilon.kutil.observer.DataObserver.Companion.observed
|
||||
import de.bixilon.kutil.random.RandomUtil.nextInt
|
||||
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
||||
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.types.properties.rendering.RandomDisplayTickable
|
||||
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.world.audio.AbstractAudioPlayer
|
||||
import de.bixilon.minosoft.data.world.audio.WorldAudioPlayer
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.WorldBiomes
|
||||
import de.bixilon.minosoft.data.world.border.WorldBorder
|
||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
|
||||
@ -57,10 +55,10 @@ import java.util.*
|
||||
*/
|
||||
class World(
|
||||
val connection: PlayConnection,
|
||||
) : BiomeAccessor, WorldAudioPlayer, WorldParticleRenderer {
|
||||
) : WorldAudioPlayer, WorldParticleRenderer {
|
||||
val lock = SimpleLock()
|
||||
val random = Random()
|
||||
var cacheBiomeAccessor: NoiseBiomeAccessor? = null
|
||||
val biomes = WorldBiomes(this)
|
||||
val chunks = ChunkManager(this, 1000, 100)
|
||||
val entities = WorldEntities()
|
||||
var hardcore by observed(false)
|
||||
@ -149,14 +147,6 @@ class World(
|
||||
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() {
|
||||
val simulationDistance = view.simulationDistance
|
||||
val cameraPosition = connection.player.physics.positionInfo.chunkPosition
|
||||
|
@ -16,7 +16,7 @@ package de.bixilon.minosoft.data.world.audio
|
||||
import de.bixilon.kotlinglm.vec3.Vec3d
|
||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||
|
||||
@Deprecated("")
|
||||
@Deprecated("use world.audio")
|
||||
interface WorldAudioPlayer : AbstractAudioPlayer {
|
||||
val audioPlayer: AbstractAudioPlayer?
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
@ -19,8 +19,8 @@ import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
|
||||
interface BiomeAccessor {
|
||||
|
||||
fun getBiome(blockPosition: Vec3i): Biome? {
|
||||
return getBiome(blockPosition.x, blockPosition.y, blockPosition.z)
|
||||
fun getBiome(position: Vec3i): Biome? {
|
||||
return getBiome(position.x, position.y, position.z)
|
||||
}
|
||||
|
||||
fun getBiome(x: Int, y: Int, z: Int): Biome?
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
@ -19,7 +19,7 @@ import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
|
||||
object NullBiomeAccessor : BiomeAccessor {
|
||||
|
||||
override fun getBiome(blockPosition: Vec3i): Biome? {
|
||||
override fun getBiome(position: Vec3i): Biome? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -11,30 +11,22 @@
|
||||
* 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.vec3.Vec3i
|
||||
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.world.biome.source.SpatialBiomeArray
|
||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
|
||||
class NoiseBiomeAccessor(
|
||||
private val connection: PlayConnection,
|
||||
var hashedSeed: Long = 0L,
|
||||
) {
|
||||
private val world = connection.world
|
||||
private var fastNoise = false
|
||||
open class VoronoiBiomeAccessor(
|
||||
connection: PlayConnection,
|
||||
seed: Long = 0L,
|
||||
) : NoiseBiomeAccessor(connection, seed) {
|
||||
|
||||
init {
|
||||
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? {
|
||||
override fun get(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
|
||||
|
||||
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 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, z)
|
||||
|
||||
val xFractionSalt = distribute(ret)
|
||||
val xSalt = distribute(ret)
|
||||
|
||||
ret = next(ret, seed)
|
||||
|
||||
val yFractionSalt = distribute(ret)
|
||||
val ySalt = distribute(ret)
|
||||
|
||||
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 {
|
@ -19,7 +19,7 @@ import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
||||
import de.bixilon.kutil.reflection.ReflectionUtil.jvmField
|
||||
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
||||
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.light.SectionLight
|
||||
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 chunkPositionZ = chunk.chunkPosition.y
|
||||
val blockOffset = Vec3i.of(chunk.chunkPosition, sectionHeight)
|
||||
@ -76,7 +76,7 @@ class ChunkSection(
|
||||
val z = blockOffset.z
|
||||
val biomes: Array<Biome?> = arrayOfNulls(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())
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class Chunk(
|
||||
val light = ChunkLight(this)
|
||||
val minSection = world.dimension.minSection
|
||||
val maxSection = world.dimension.maxSection
|
||||
val cacheBiomes = world.cacheBiomeAccessor != null
|
||||
val cacheBiomes = world.biomes.noise != null
|
||||
|
||||
val neighbours = ChunkNeighbours(this)
|
||||
|
||||
@ -187,7 +187,7 @@ class Chunk(
|
||||
section.blocks.unsafeSetSection(section)
|
||||
val neighbours = this.neighbours.get()
|
||||
if (neighbours != null) {
|
||||
this.neighbours.completeSection(neighbours, section, sectionHeight, world.cacheBiomeAccessor)
|
||||
this.neighbours.completeSection(neighbours, section, sectionHeight, world.biomes.noise)
|
||||
}
|
||||
|
||||
sections[index] = section
|
||||
@ -230,11 +230,10 @@ class Chunk(
|
||||
|
||||
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
|
||||
val y = y.clamp(world.dimension.minY, world.dimension.maxY)
|
||||
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 section.biomes[x, y.inSectionHeight, z]
|
||||
if (!cacheBiomes) {
|
||||
return biomeSource.getBiome(x, y, z)
|
||||
}
|
||||
return biomeSource.getBiome(x and 0x0F, y, z and 0x0F)
|
||||
return connection.world.biomes.getBiome(x, y, z, this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ import de.bixilon.kotlinglm.vec3.Vec3i
|
||||
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
||||
import de.bixilon.kutil.exception.Broken
|
||||
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.chunk.Chunk
|
||||
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition
|
||||
@ -70,19 +70,19 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
|
||||
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)
|
||||
if (biomeCacheAccessor != null) {
|
||||
section.buildBiomeCache(neighbours, biomeCacheAccessor)
|
||||
if (noise != null) {
|
||||
section.buildBiomeCache(neighbours, noise)
|
||||
}
|
||||
}
|
||||
|
||||
private fun complete(neighbours: Array<Chunk>) {
|
||||
val biomeCacheAccessor = chunk.world.cacheBiomeAccessor
|
||||
val noise = chunk.world.biomes.noise
|
||||
for ((index, section) in chunk.sections.withIndex()) {
|
||||
if (section == null) continue
|
||||
val sectionHeight = index + chunk.minSection
|
||||
completeSection(neighbours, section, sectionHeight, biomeCacheAccessor)
|
||||
completeSection(neighbours, section, sectionHeight, noise)
|
||||
}
|
||||
chunk.light.recalculate(false)
|
||||
chunk.light.propagateFromNeighbours(fireEvent = false, fireSameChunkEvent = false)
|
||||
|
@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.world.particle
|
||||
|
||||
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
|
||||
|
||||
@Deprecated("")
|
||||
@Deprecated("use world.particle")
|
||||
interface WorldParticleRenderer : AbstractParticleRenderer {
|
||||
val particleRenderer: AbstractParticleRenderer?
|
||||
|
||||
|
@ -28,10 +28,10 @@ import de.bixilon.minosoft.gui.rendering.tint.tints.redstone.RedstoneWireTintCal
|
||||
object DefaultTints {
|
||||
|
||||
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.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.MELON_STEM, MinecraftBlocks.PUMPKIN_STEM), StemTintCalculator)
|
||||
manager.applyTo(setOf(MinecraftBlocks.LILY_PAD), StaticTintProvider(block = 0x208030, item = 0x71C35C))
|
||||
|
@ -28,12 +28,12 @@ import de.bixilon.minosoft.gui.rendering.tint.tints.plants.FoliageTintCalculator
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
|
||||
class TintManager(val connection: PlayConnection) {
|
||||
val grassTintCalculator = GrassTintCalculator()
|
||||
val foliageTintCalculator = FoliageTintCalculator()
|
||||
val grass = GrassTintCalculator()
|
||||
val foliage = FoliageTintCalculator()
|
||||
|
||||
fun init(assetsManager: AssetsManager) {
|
||||
grassTintCalculator.init(assetsManager)
|
||||
foliageTintCalculator.init(assetsManager)
|
||||
grass.init(assetsManager)
|
||||
foliage.init(assetsManager)
|
||||
|
||||
for (block in connection.registries.block) {
|
||||
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? {
|
||||
if (state.block !is TintedBlock) 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)
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,6 @@ import de.bixilon.minosoft.gui.rendering.tint.TintedBlock
|
||||
interface GrassTinted : TintedBlock {
|
||||
|
||||
override fun initTint(manager: TintManager) {
|
||||
this::class.java.getDeclaredField("tintProvider").forceSet(this, manager.grassTintCalculator)
|
||||
this::class.java.getDeclaredField("tintProvider").forceSet(this, manager.grass)
|
||||
}
|
||||
}
|
||||
|
@ -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.registries.dimension.DimensionProperties
|
||||
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.modding.event.events.DimensionChangeEvent
|
||||
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.add(entityId, null, playerEntity)
|
||||
if (connection.version.versionId >= ProtocolVersions.V_19W36A && !connection.profiles.rendering.performance.fastBiomeNoise) {
|
||||
connection.world.cacheBiomeAccessor = NoiseBiomeAccessor(connection, hashedSeed)
|
||||
}
|
||||
connection.world.biomes.noise = NoiseBiomeAccessor.get(connection, hashedSeed)
|
||||
connection.world.border.reset()
|
||||
|
||||
if (connection.version < V_1_20_2_PRE1) {
|
||||
|
@ -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.registries.dimension.DimensionProperties
|
||||
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.modding.event.events.DimensionChangeEvent
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
@ -114,7 +115,7 @@ class RespawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||
}
|
||||
connection.world.dimension = dimension
|
||||
connection.world.name = world
|
||||
connection.world.cacheBiomeAccessor?.hashedSeed = hashedSeed
|
||||
connection.world.biomes.noise = NoiseBiomeAccessor.get(connection, hashedSeed)
|
||||
|
||||
connection.state = PlayConnectionStates.SPAWNING
|
||||
if (dimensionChange) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
package de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk
|
||||
|
||||
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.protocol.buffers.play.PlayInByteBuffer
|
||||
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()) }
|
||||
|
||||
|
||||
class ChunkBiomeData(
|
||||
data class ChunkBiomeData(
|
||||
val position: Vec2i,
|
||||
val data: ByteArray,
|
||||
)
|
||||
@ -33,7 +34,15 @@ class ChunkBiomeS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||
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
|
||||
}
|
||||
connection.world.biomes.resetCache()
|
||||
}
|
||||
|
||||
override fun log(reducedLog: Boolean) {
|
||||
Log.log(LogMessageType.NETWORK_IN, level = LogLevels.VERBOSE) { "Chunk biome (data=$data)" }
|
||||
|
Loading…
x
Reference in New Issue
Block a user