mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 01:16:46 -04:00
calculate biome cache on demand, refactor chunk section constructor
This makes things way faster and uses less memory. Biome is just needed for tinted blocks (and maybe sky color), most blocks don't require it. Calculating the biome is expensive (at least when joining).
This commit is contained in:
parent
c4cd6c4856
commit
7f32520f6e
@ -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)
|
||||
}
|
||||
|
@ -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<BlockState>(ProtocolDefinition.BLOCKS_PER_SECTION).apply { this[getIndex(3, 3, 3)] = StoneTest0.state },
|
||||
arrayOfNulls<BlockState>(ProtocolDefinition.BLOCKS_PER_SECTION).apply { this[getIndex(3, 3, 3)] = StoneTest0.state },
|
||||
arrayOfNulls<BlockState>(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)
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.container.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`() {
|
||||
|
@ -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<BlockState?>.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))
|
||||
|
@ -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<Biome> = SectionDataProvider(chunk?.lock, checkSize = false),
|
||||
var blockEntities: SectionDataProvider<BlockEntity?> = 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<BlockEntity?> = SectionDataProvider(chunk.lock, checkSize = true)
|
||||
|
||||
var light = SectionLight(this)
|
||||
var neighbours: Array<ChunkSection?>? = null
|
||||
|
||||
@ -66,14 +61,6 @@ class ChunkSection(
|
||||
}
|
||||
}
|
||||
|
||||
fun buildBiomeCache(noise: NoiseBiomeAccessor) {
|
||||
val biomes: Array<Biome?> = 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)
|
||||
|
||||
|
@ -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<ChunkSection?>,
|
||||
var biomeSource: BiomeSource,
|
||||
) : Iterable<ChunkSection?>, 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<ChunkSection?> = 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)
|
||||
|
@ -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<BlockSectionDataProvider?>? = null,
|
||||
var blocks: Array<Array<BlockState?>?>? = null,
|
||||
var blockEntities: Map<Vec3i, JsonObject>? = null,
|
||||
var biomeSource: BiomeSource? = null,
|
||||
var light: Array<ByteArray?>? = null,
|
||||
@ -53,28 +51,23 @@ class ChunkPrototype(
|
||||
|
||||
val dimension = connection.world.dimension
|
||||
|
||||
val sections: Array<ChunkSection?> = 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<BlockSectionDataProvider?>.update(chunk: Chunk, replace: Boolean, affected: IntOpenHashSet) {
|
||||
private fun Array<Array<BlockState?>?>.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<Vec3i, JsonObject>?.update(minSection: Int, sections: Array<ChunkSection?>, affected: IntOpenHashSet?, connection: PlayConnection) {
|
||||
private fun Map<Vec3i, JsonObject>?.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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -72,9 +72,6 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
|
||||
|
||||
fun completeSection(neighbours: Array<Chunk>, section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) {
|
||||
section.neighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, sectionHeight)
|
||||
if (noise != null) {
|
||||
section.buildBiomeCache(noise)
|
||||
}
|
||||
}
|
||||
|
||||
private fun complete(neighbours: Array<Chunk>) {
|
||||
|
@ -23,36 +23,25 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
|
||||
open class SectionDataProvider<T>(
|
||||
var lock: Lock?,
|
||||
data: Array<T>? = null,
|
||||
val checkSize: Boolean = false,
|
||||
calculateInitial: Boolean = true,
|
||||
) : Iterable<T> {
|
||||
protected var data: Array<Any?>? = data?.unsafeCast()
|
||||
protected var data: Array<Any?>? = 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<T>(
|
||||
lock?.unlock()
|
||||
}
|
||||
|
||||
fun copy(): SectionDataProvider<T> {
|
||||
lock?.acquire()
|
||||
val clone = SectionDataProvider<T>(null, data?.clone()?.unsafeCast())
|
||||
lock?.release()
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
lock?.lock()
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.container.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<Biome?>(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
|
||||
}
|
||||
}
|
@ -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<BlockState?>? = null,
|
||||
) : SectionDataProvider<BlockState?>(lock, data, true, false) {
|
||||
val section: ChunkSection = unsafeNull()
|
||||
val section: ChunkSection,
|
||||
) : SectionDataProvider<BlockState?>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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<BlockSectionDataProvider?> = arrayOfNulls(dimension.sections)
|
||||
val sections: Array<Array<BlockState?>?> = 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<BlockSectionDataProvider?> = arrayOfNulls(dimension.sections)
|
||||
val sections: Array<Array<BlockState?>?> = 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<BlockSectionDataProvider?> = arrayOfNulls(dimension.sections)
|
||||
val sectionBlocks: Array<Array<BlockState?>?> = arrayOfNulls(dimension.sections)
|
||||
val light: Array<ByteArray?> = arrayOfNulls(dimension.sections)
|
||||
var lightReceived = 0
|
||||
val biomes: Array<Array<Biome?>?> = arrayOfNulls(dimension.sections)
|
||||
@ -176,8 +175,8 @@ object ChunkUtil {
|
||||
val blockContainer: PalettedContainer<BlockState?> = 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<BlockState?>()
|
||||
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<BlockState?>.isEmpty(): Boolean {
|
||||
for (entry in this) {
|
||||
if (entry == null) continue
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user