diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/biome/WorldBiomesTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/biome/WorldBiomesTest.kt index 2bcca08ba..ab65ace86 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/biome/WorldBiomesTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/biome/WorldBiomesTest.kt @@ -90,9 +90,11 @@ class WorldBiomesTest { val chunk = world.chunks[0, 0]!! chunk.getOrPut(0) - assertEquals(source.counter, 4096) + assertEquals(source.counter, 0) // biomes ore on demand assertEquals(world.biomes[1, 2, 3], b1) - assertEquals(source.counter, 4096) + assertEquals(source.counter, 1) + assertEquals(world.biomes[1, 2, 3], b1) + assertEquals(source.counter, 1) // don't query again assertTrue(chunk.cacheBiomes) assertEquals(chunk[0]!!.biomes[1, 2, 3], b1) } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/manager/ChunkManagerTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/manager/ChunkManagerTest.kt index 7ae2ae47a..e96847b88 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/manager/ChunkManagerTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/manager/ChunkManagerTest.kt @@ -15,12 +15,14 @@ package de.bixilon.minosoft.data.world.chunk.manager import de.bixilon.kotlinglm.vec2.Vec2i 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.stone.StoneTest0 import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft 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 +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.ChunkPrototype import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours @@ -31,13 +33,13 @@ import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkCreateUpdate import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkUnloadUpdate import de.bixilon.minosoft.data.world.chunk.update.chunk.NeighbourChangeUpdate import de.bixilon.minosoft.data.world.chunk.update.chunk.prototype.PrototypeChangeUpdate -import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider 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.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil.createConnection +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import org.testng.Assert.* import org.testng.annotations.Test @@ -191,9 +193,9 @@ class ChunkManagerTest { manager[ChunkPosition(0, 1)]!![3, 16, 3] = StoneTest0.state manager[ChunkPosition(0, 0)] = ChunkPrototype(blocks = arrayOf( - BlockSectionDataProvider(null).apply { this[3, 3, 3] = StoneTest0.state }, - BlockSectionDataProvider(null).apply { this[3, 3, 3] = StoneTest0.state }, - BlockSectionDataProvider(null).apply { this[3, 3, 3] = StoneTest0.state }, + arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION).apply { this[getIndex(3, 3, 3)] = StoneTest0.state }, + arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION).apply { this[getIndex(3, 3, 3)] = StoneTest0.state }, + arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION).apply { this[getIndex(3, 3, 3)] = StoneTest0.state }, null, null, null, ), blockEntities = emptyMap(), @@ -399,7 +401,7 @@ class ChunkManagerTest { fired++ } - manager.set(ChunkPosition(1, 1), ChunkPrototype(blocks = Array(16) { if (it == 0) BlockSectionDataProvider(null) else null }), false) + manager.set(ChunkPosition(1, 1), ChunkPrototype(blocks = Array(16) { if (it == 0) arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) else null }), false) assertNotNull(manager[ChunkPosition(1, 1)]) @@ -443,8 +445,10 @@ class ChunkManagerTest { val matrix = manager.createMatrix(source) val chunk = matrix[1][1] - assertEquals(chunk.getOrPut(0)!!.biomes.count, 4096) - assertEquals(chunk.getOrPut(0)!!.biomes[0], biome) + val section = chunk.getOrPut(0)!!.biomes + assertEquals(section[3, 3, 3], biome) + assertEquals(section[3, 3, 3], biome) + assertEquals(section[0], biome) assertEquals(manager.world.biomes.getBiome(BlockPosition(5, 5, 5)), biome) assertEquals(chunk.getBiome(BlockPosition(5, 5, 5)), biome) diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProviderTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProviderTest.kt index abe47609c..7cc3f29d2 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProviderTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProviderTest.kt @@ -1,7 +1,22 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + package de.bixilon.minosoft.data.world.container.block import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0 +import de.bixilon.minosoft.data.world.chunk.ChunkSection +import de.bixilon.minosoft.test.ITUtil.allocate import org.testng.Assert.* import org.testng.annotations.Test @@ -9,7 +24,8 @@ import org.testng.annotations.Test class BlockSectionDataProviderTest { private fun create(): BlockSectionDataProvider { - return BlockSectionDataProvider(null) + val section = ChunkSection::class.java.allocate() + return BlockSectionDataProvider(null, section) } fun `initial empty`() { diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/protocol/packets/s2c/play/chunk/ChunkS2CPTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/protocol/packets/s2c/play/chunk/ChunkS2CPTest.kt index 2e1781833..f1ab2b717 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/protocol/packets/s2c/play/chunk/ChunkS2CPTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/protocol/packets/s2c/play/chunk/ChunkS2CPTest.kt @@ -19,8 +19,10 @@ import de.bixilon.kutil.observer.DataObserver import de.bixilon.kutil.reflection.ReflectionUtil.forceSet import de.bixilon.mbf.MBFBinaryReader import de.bixilon.minosoft.data.registries.blocks.MinecraftBlocks +import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.types.building.stone.StoneBlock import de.bixilon.minosoft.data.registries.dimension.DimensionProperties +import de.bixilon.minosoft.data.world.chunk.ChunkSection.Companion.getIndex import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil.createConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk.ChunkS2CP @@ -37,6 +39,10 @@ class ChunkS2CPTest { registries.update(version, mbf.data.unsafeCast()) } + private operator fun Array.get(x: Int, y: Int, z: Int): BlockState? { + return this[getIndex(x, y, z)] + } + private fun read(name: String, version: String, connection: PlayConnection = createConnection(version = version), dimension: DimensionProperties): ChunkS2CP { val data = ChunkS2CPTest::class.java.getResourceAsStream("/packets/chunk/$name.bin")!!.readAllBytes() connection.world::dimension.forceSet(DataObserver(dimension)) diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt index bbe27ab64..dc343ba3e 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt @@ -14,19 +14,14 @@ package de.bixilon.minosoft.data.world.chunk import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec3.Vec3i -import de.bixilon.kutil.array.ArrayUtil.cast -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.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 +import de.bixilon.minosoft.data.world.container.biome.BiomeSectionDataProvider import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider import de.bixilon.minosoft.gui.rendering.util.VecUtil.of import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import java.util.* /** @@ -34,12 +29,12 @@ import java.util.* */ class ChunkSection( val sectionHeight: Int, - chunk: Chunk? = null, - var blocks: BlockSectionDataProvider = BlockSectionDataProvider(chunk?.lock), - var biomes: SectionDataProvider = SectionDataProvider(chunk?.lock, checkSize = false), - var blockEntities: SectionDataProvider = SectionDataProvider(chunk?.lock, checkSize = true), + val chunk: Chunk, ) { - val chunk: Chunk = chunk ?: unsafeNull() + var blocks = BlockSectionDataProvider(chunk.lock, this) + var biomes = BiomeSectionDataProvider(chunk.lock, this) + var blockEntities: SectionDataProvider = SectionDataProvider(chunk.lock, checkSize = true) + var light = SectionLight(this) var neighbours: Array? = null @@ -66,14 +61,6 @@ class ChunkSection( } } - fun buildBiomeCache(noise: NoiseBiomeAccessor) { - val biomes: Array = arrayOfNulls(ProtocolDefinition.BLOCKS_PER_SECTION) - for (index in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) { - biomes[index] = noise.get(index and 0x0F, (index shr 8) and 0x0F, (index shr 4) and 0x0F, chunk) - } - this.biomes.setData(biomes.cast()) - } - fun clear() { blocks.clear() @@ -81,16 +68,7 @@ class ChunkSection( blockEntities.clear() } - fun updateChunk(chunk: Chunk) { - CHUNK[this] = chunk - blocks.lock = chunk.lock - // biomes? - blockEntities.lock = chunk.lock - } - companion object { - private val CHUNK = ChunkSection::chunk.jvmField - inline val Vec3i.index: Int get() = getIndex(x, y, z) diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt index 1c9f9eebd..b492fcc8a 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt @@ -28,7 +28,6 @@ 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.update.block.ChunkLocalBlockUpdate import de.bixilon.minosoft.data.world.chunk.update.block.SingleBlockUpdate -import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider.Companion.unsafeSetSection import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.InChunkPosition import de.bixilon.minosoft.data.world.positions.SectionHeight @@ -44,7 +43,6 @@ import java.util.* class Chunk( val connection: PlayConnection, val chunkPosition: ChunkPosition, - var sections: Array, var biomeSource: BiomeSource, ) : Iterable, BiomeAccessor { val lock = ThreadLock() @@ -53,6 +51,7 @@ class Chunk( val minSection = world.dimension.minSection val maxSection = world.dimension.maxSection val cacheBiomes = world.biomes.noise != null + var sections: Array = arrayOfNulls(world.dimension.sections) val neighbours = ChunkNeighbours(this) @@ -184,7 +183,6 @@ class Chunk( var section = sections[index] // get another time, it might have changed already if (section == null) { section = ChunkSection(sectionHeight, chunk = this) - section.blocks.unsafeSetSection(section) val neighbours = this.neighbours.get() if (neighbours != null) { this.neighbours.completeSection(neighbours, section, sectionHeight, world.biomes.noise) diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt index d429b15d4..56880344a 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt @@ -15,20 +15,18 @@ package de.bixilon.minosoft.data.world.chunk.chunk import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.kutil.json.JsonObject -import de.bixilon.kutil.reflection.ReflectionUtil.forceSet -import de.bixilon.kutil.reflection.ReflectionUtil.jvmField import de.bixilon.minosoft.config.StaticConfiguration +import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.types.entity.BlockWithEntity import de.bixilon.minosoft.data.world.biome.source.BiomeSource import de.bixilon.minosoft.data.world.chunk.ChunkSection -import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import it.unimi.dsi.fastutil.ints.IntOpenHashSet class ChunkPrototype( - var blocks: Array? = null, + var blocks: Array?>? = null, var blockEntities: Map? = null, var biomeSource: BiomeSource? = null, var light: Array? = null, @@ -53,28 +51,23 @@ class ChunkPrototype( val dimension = connection.world.dimension - val sections: Array = arrayOfNulls(dimension.sections) val light = this.light - for ((index, provider) in blocks.withIndex()) { - if (provider == null) continue - val section = ChunkSection(index + dimension.minSection, null, provider) - SECTION[provider] = section + val chunk = Chunk(connection, position, biomeSource) + + for ((index, blockData) in blocks.withIndex()) { + if (blockData == null) continue + val section = ChunkSection(index + dimension.minSection, chunk) + section.blocks.setData(blockData) if (!StaticConfiguration.IGNORE_SERVER_LIGHT) { light?.get(index)?.let { section.light.light = it } } - sections[index] = section + chunk.sections[index] = section } - this.blockEntities.update(dimension.minSection, sections, null, connection) + this.blockEntities.update(dimension.minSection, chunk, null, connection) - val chunk = Chunk(connection, position, sections, biomeSource) - - for (section in sections) { - if (section == null) continue - section.updateChunk(chunk) - } if (!StaticConfiguration.IGNORE_SERVER_LIGHT) { this.topLight?.let { chunk.light.top.update(it) } this.bottomLight?.let { chunk.light.bottom.update(it) } @@ -84,7 +77,7 @@ class ChunkPrototype( return chunk } - private fun Array.update(chunk: Chunk, replace: Boolean, affected: IntOpenHashSet) { + private fun Array?>.update(chunk: Chunk, replace: Boolean, affected: IntOpenHashSet) { for ((index, provider) in this.withIndex()) { var section = chunk.sections[index] val sectionHeight = index - chunk.minSection @@ -101,16 +94,15 @@ class ChunkPrototype( } else { section.blockEntities.clear() } - SECTION.forceSet(provider, section) - section.blocks = provider + section.blocks.setData(provider) affected += sectionHeight } } - private fun Map?.update(minSection: Int, sections: Array, affected: IntOpenHashSet?, connection: PlayConnection) { + private fun Map?.update(minSection: Int, chunk: Chunk, affected: IntOpenHashSet?, connection: PlayConnection) { val position = Vec3i() - for ((index, section) in sections.withIndex()) { + for ((index, section) in chunk.sections.withIndex()) { if (section == null || section.blocks.isEmpty) continue val blocks = section.blocks val sectionHeight = (index + minSection) @@ -142,7 +134,7 @@ class ChunkPrototype( fun updateChunk(chunk: Chunk, replace: Boolean): IntOpenHashSet? { val affected = IntOpenHashSet() this.blocks?.update(chunk, replace, affected) - this.blockEntities?.update(chunk.minSection, chunk.sections, affected, chunk.connection) + this.blockEntities?.update(chunk.minSection, chunk, affected, chunk.connection) this.biomeSource?.let { chunk.biomeSource = it } // TODO: invalidate cache @@ -155,8 +147,4 @@ class ChunkPrototype( if (affected.isEmpty()) return null return affected } - - private companion object { - private val SECTION = BlockSectionDataProvider::section.jvmField - } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt index 86f7f85bc..eec270837 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt @@ -161,7 +161,7 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity: fun create(position: ChunkPosition, biome: BiomeSource = DummyBiomeSource(null)): Chunk { if (!world.isValidPosition(position)) throw IllegalArgumentException("Chunk position $position is not valid!") chunks.lock.lock() - val chunk = chunks.unsafe.getOrPut(position) { Chunk(world.connection, position, arrayOfNulls(world.dimension.sections), biome) } + val chunk = chunks.unsafe.getOrPut(position) { Chunk(world.connection, position, biome) } val updates = onChunkCreate(chunk) chunks.lock.unlock() diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt index d579b5262..ceafb4e90 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt @@ -72,9 +72,6 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable { fun completeSection(neighbours: Array, section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) { section.neighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, sectionHeight) - if (noise != null) { - section.buildBiomeCache(noise) - } } private fun complete(neighbours: Array) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt index d68c1c238..292badb09 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt @@ -23,36 +23,25 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition open class SectionDataProvider( var lock: Lock?, - data: Array? = null, val checkSize: Boolean = false, - calculateInitial: Boolean = true, ) : Iterable { - protected var data: Array? = data?.unsafeCast() + protected var data: Array? = null private set var count: Int = 0 private set val isEmpty: Boolean get() = count == 0 - lateinit var minPosition: Vec3i + var minPosition = Vec3i(ProtocolDefinition.CHUNK_SECTION_SIZE) private set - lateinit var maxPosition: Vec3i + var maxPosition = Vec3i.EMPTY private set - init { - if (data != null && calculateInitial) { - recalculate() - } else { - minPosition = Vec3i(ProtocolDefinition.CHUNK_SECTION_SIZE) - maxPosition = Vec3i.EMPTY - } - } - @Suppress("UNCHECKED_CAST") - operator fun get(index: Int): T { + open operator fun get(index: Int): T { return data?.get(index) as T } - operator fun get(x: Int, y: Int, z: Int): T { + open operator fun get(x: Int, y: Int, z: Int): T { return get(ChunkSection.getIndex(x, y, z)) } @@ -193,13 +182,6 @@ open class SectionDataProvider( lock?.unlock() } - fun copy(): SectionDataProvider { - lock?.acquire() - val clone = SectionDataProvider(null, data?.clone()?.unsafeCast()) - lock?.release() - - return clone - } fun clear() { lock?.lock() diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt new file mode 100644 index 000000000..4f730c24e --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt @@ -0,0 +1,41 @@ +/* + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.container.biome + +import de.bixilon.kutil.concurrent.lock.Lock +import de.bixilon.minosoft.data.registries.biomes.Biome +import de.bixilon.minosoft.data.world.chunk.ChunkSection +import de.bixilon.minosoft.data.world.container.SectionDataProvider + +class BiomeSectionDataProvider( + lock: Lock? = null, + val section: ChunkSection, +) : SectionDataProvider(lock, false) { + + override fun get(index: Int): Biome? { + var biome = super.get(index) + if (biome != null) return biome + biome = section.chunk.world.biomes.noise?.get(index and 0x0F, (index shr 8) and 0x0F, (index shr 4) and 0x0F, section.chunk) + unsafeSet(index, biome) + return biome + } + + override fun get(x: Int, y: Int, z: Int): Biome? { + var biome = super.get(x, y, z) + if (biome != null) return biome + biome = section.chunk.world.biomes.noise?.get(x, y, z, section.chunk) + unsafeSet(x, y, z, biome) + return biome + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProvider.kt index 08a8608ee..27c324e41 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/block/BlockSectionDataProvider.kt @@ -13,9 +13,7 @@ package de.bixilon.minosoft.data.world.container.block -import de.bixilon.kutil.cast.CastUtil.unsafeNull import de.bixilon.kutil.concurrent.lock.Lock -import de.bixilon.kutil.reflection.ReflectionUtil.jvmField import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.types.fluid.FluidHolder import de.bixilon.minosoft.data.registries.fluid.fluids.WaterFluid.Companion.isWaterlogged @@ -25,9 +23,8 @@ import de.bixilon.minosoft.data.world.container.SectionDataProvider class BlockSectionDataProvider( lock: Lock? = null, - data: Array? = null, -) : SectionDataProvider(lock, data, true, false) { - val section: ChunkSection = unsafeNull() + val section: ChunkSection, +) : SectionDataProvider(lock, true) { val occlusion = SectionOcclusion(this) var fluidCount = 0 private set @@ -89,13 +86,4 @@ class BlockSectionDataProvider( } return this.isWaterlogged() } - - companion object { - private val sections = BlockSectionDataProvider::section.jvmField - - @Deprecated("properly integrate in constructor") - fun BlockSectionDataProvider.unsafeSetSection(section: ChunkSection) { - this@Companion.sections[this] = section - } - } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt index 77956e7e5..6697ab57a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt @@ -131,7 +131,7 @@ class SolidSectionMesher( } - val tints = tints.getBlockTint(state, chunk.getBiome(position.x and 0x0F, y, position.z and 0x0F), x, y, z) + val tints = tints.getBlockTint(state, chunk, x, position.y, z) var rendered = false model?.render(position, floatOffset, mesh, random, state, neighbourBlocks, light, tints, blockEntity.unsafeCast())?.let { if (it) rendered = true } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/tint/TintManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/tint/TintManager.kt index 8d756cf39..6efc4d921 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/tint/TintManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/tint/TintManager.kt @@ -17,7 +17,6 @@ import de.bixilon.kotlinglm.vec3.Vec3i import de.bixilon.kutil.primitive.IntUtil.toInt import de.bixilon.minosoft.assets.AssetsManager import de.bixilon.minosoft.data.container.stack.ItemStack -import de.bixilon.minosoft.data.registries.biomes.Biome import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.fluid.Fluid import de.bixilon.minosoft.data.text.formatting.color.RGBColor @@ -47,10 +46,11 @@ class TintManager(val connection: PlayConnection) { DefaultTints.init(this) } - fun getBlockTint(state: BlockState, biome: Biome?, x: Int, y: Int, z: Int): IntArray? { + fun getBlockTint(state: BlockState, chunk: Chunk?, x: Int, y: Int, z: Int): IntArray? { if (state.block !is TintedBlock) return null val tintProvider = state.block.tintProvider ?: return null val tints = IntArray(if (tintProvider is MultiTintProvider) tintProvider.tints else 1) + val biome = chunk?.getBiome(x, y, z) for (tintIndex in tints.indices) { tints[tintIndex] = tintProvider.getBlockColor(state, biome, x, y, z, tintIndex) @@ -70,10 +70,6 @@ class TintManager(val connection: PlayConnection) { return getParticleTint(blockState, position.x, position.y, position.z) } - fun getBlockTint(blockState: BlockState, biome: Biome? = null, blockPosition: Vec3i): IntArray? { - return getBlockTint(blockState, biome, blockPosition.x, blockPosition.y, blockPosition.z) - } - fun getFluidTint(chunk: Chunk, fluid: Fluid, height: Float, x: Int, y: Int, z: Int): Int? { val biome = chunk.getBiome(x and 0x0F, y, z and 0x0F) return fluid.model?.tint?.getFluidTint(fluid, biome, height, x, y, z) diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt index 758a3d98e..5c74f92f3 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt @@ -24,7 +24,6 @@ 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.ChunkPrototype import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours -import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider 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 @@ -74,7 +73,7 @@ object ChunkUtil { // parse data var index = 0 - val sections: Array = arrayOfNulls(dimension.sections) + val sections: Array?> = arrayOfNulls(dimension.sections) for ((sectionIndex, sectionHeight) in (dimension.minSection..dimension.maxSection).withIndex()) { if (!sectionBitMask[sectionIndex]) { continue @@ -106,7 +105,7 @@ object ChunkUtil { blocks[yzx] = buffer.connection.registries.blockState.getOrNull(blockId) ?: continue } - sections[sectionHeight] = BlockSectionDataProvider(data = blocks) + sections[sectionHeight] = blocks } chunkData.blocks = sections return chunkData @@ -139,7 +138,7 @@ object ChunkUtil { } var index = 0 - val sections: Array = arrayOfNulls(dimension.sections) + val sections: Array?> = arrayOfNulls(dimension.sections) for ((sectionIndex, sectionHeight) in (dimension.minSection..dimension.maxSection).withIndex()) { // max sections per chunks in chunk column if (!sectionBitMask[sectionIndex]) { continue @@ -150,7 +149,7 @@ object ChunkUtil { val block = buffer.connection.registries.blockState.getOrNull(blockId) ?: continue blocks[yzx] = block } - sections[sectionHeight] = BlockSectionDataProvider(data = blocks) + sections[sectionHeight] = blocks } chunkData.blocks = sections return chunkData @@ -158,7 +157,7 @@ object ChunkUtil { fun readPaletteChunk(buffer: PlayInByteBuffer, dimension: DimensionProperties, sectionBitMask: BitSet?, complete: Boolean, skylight: Boolean = false): ChunkPrototype { val chunkData = ChunkPrototype() - val sectionBlocks: Array = arrayOfNulls(dimension.sections) + val sectionBlocks: Array?> = arrayOfNulls(dimension.sections) val light: Array = arrayOfNulls(dimension.sections) var lightReceived = 0 val biomes: Array?> = arrayOfNulls(dimension.sections) @@ -176,8 +175,8 @@ object ChunkUtil { val blockContainer: PalettedContainer = PalettedContainerReader.read(buffer, buffer.connection.registries.blockState, paletteFactory = BlockStatePaletteFactory) if (!blockContainer.isEmpty) { - val unpacked = BlockSectionDataProvider(data = blockContainer.unpack()) - if (!unpacked.isEmpty) { + val unpacked = blockContainer.unpack() + if (!unpacked.isEmpty()) { sectionBlocks[sectionHeight - dimension.minSection] = unpacked } } @@ -311,4 +310,12 @@ object ChunkUtil { fun ChunkPosition.isInViewDistance(viewDistance: Int, cameraPosition: ChunkPosition): Boolean { return abs(this.x - cameraPosition.x) <= viewDistance && abs(this.y - cameraPosition.y) <= viewDistance } + + private fun Array.isEmpty(): Boolean { + for (entry in this) { + if (entry == null) continue + return false + } + return true + } }