mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-09 07:20:04 -04:00
wip: chunk cache
This commit is contained in:
parent
d4a562e429
commit
5c3b1f31b8
@ -70,7 +70,7 @@ data class DimensionProperties(
|
||||
hasCeiling = data["has_ceiling"]?.toBoolean() ?: false,
|
||||
ultraWarm = data["ultrawarm"]?.toBoolean() ?: false,
|
||||
height = data["height"]?.toInt() ?: 256,
|
||||
supports3DBiomes = data["supports_3d_biomes"]?.toBoolean() ?: false,
|
||||
supports3DBiomes = data["supports_3d_biomes"]?.toBoolean() ?: true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,16 @@
|
||||
package de.bixilon.minosoft.data.world
|
||||
|
||||
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
||||
import de.bixilon.minosoft.data.world.ChunkSection.Companion.index
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkSectionPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
||||
import de.bixilon.minosoft.modding.event.EventInitiators
|
||||
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import glm_.vec2.Vec2i
|
||||
@ -28,21 +33,22 @@ import glm_.vec3.Vec3i
|
||||
*/
|
||||
class Chunk(
|
||||
private val connection: PlayConnection,
|
||||
private val chunkPosition: Vec2i,
|
||||
private var sections: Array<ChunkSection?>? = null,
|
||||
var biomeSource: BiomeSource? = null,
|
||||
) : Iterable<ChunkSection?> {
|
||||
) : Iterable<ChunkSection?>, BiomeAccessor {
|
||||
var bottomLight: IntArray? = null
|
||||
var topLight: IntArray? = null
|
||||
val lowestSection = connection.world.dimension!!.lowestSection
|
||||
val highestSection = connection.world.dimension!!.highestSection
|
||||
|
||||
var blocksInitialized: Boolean = false
|
||||
val biomesInitialized
|
||||
get() = biomeSource != null
|
||||
var blocksInitialized = false // All block data was received
|
||||
var biomesInitialized = false // All biome data is initialized (aka. cache built, or similar)
|
||||
var lightInitialized = false
|
||||
var neighboursLoaded = false
|
||||
|
||||
val isFullyLoaded: Boolean
|
||||
get() = blocksInitialized && biomesInitialized && lightInitialized
|
||||
get() = blocksInitialized && biomesInitialized && lightInitialized && neighboursLoaded
|
||||
|
||||
operator fun get(sectionHeight: Int): ChunkSection? = sections?.getOrNull(sectionHeight - lowestSection)
|
||||
|
||||
@ -115,7 +121,10 @@ class Chunk(
|
||||
topLight = it
|
||||
lightInitialized = true
|
||||
}
|
||||
data.biomeSource?.let { this.biomeSource = it }
|
||||
data.biomeSource?.let {
|
||||
this.biomeSource = it
|
||||
}
|
||||
connection.world.onChunkUpdate(chunkPosition, this)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@ -126,6 +135,10 @@ class Chunk(
|
||||
var section = sections[sectionIndex]
|
||||
if (section == null) {
|
||||
section = ChunkSection(connection.registries)
|
||||
val cacheBiomeAccessor = connection.world.cacheBiomeAccessor
|
||||
if (cacheBiomeAccessor != null && biomesInitialized && neighboursLoaded) {
|
||||
section.buildBiomeCache(chunkPosition, sectionHeight, cacheBiomeAccessor)
|
||||
}
|
||||
sections[sectionIndex] = section
|
||||
}
|
||||
return section
|
||||
@ -154,7 +167,27 @@ class Chunk(
|
||||
return get(position.sectionHeight)?.light?.get(index) ?: 0xFF
|
||||
}
|
||||
|
||||
fun buildBiomeCache() {
|
||||
val cacheBiomeAccessor = connection.world.cacheBiomeAccessor ?: return
|
||||
check(neighboursLoaded)
|
||||
// val neighbours = connection.world.getChunkNeighbours(chunkPosition)
|
||||
for ((sectionIndex, section) in sections!!.withIndex()) {
|
||||
section ?: continue
|
||||
val sectionHeight = sectionIndex + lowestSection
|
||||
section.buildBiomeCache(chunkPosition, sectionHeight, cacheBiomeAccessor)
|
||||
}
|
||||
biomesInitialized = true
|
||||
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.UNKNOWN, chunkPosition, this))
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<ChunkSection?> {
|
||||
return sections!!.iterator()
|
||||
}
|
||||
|
||||
override fun getBiome(blockPosition: Vec3i): Biome? {
|
||||
if (connection.world.cacheBiomeAccessor != null) {
|
||||
return get(blockPosition.sectionHeight)?.biomes?.get(blockPosition.x, blockPosition.sectionHeight, blockPosition.z)
|
||||
}
|
||||
return biomeSource?.getBiome(blockPosition.inChunkPosition)
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import de.bixilon.minosoft.data.entities.block.BlockEntity
|
||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
||||
import de.bixilon.minosoft.data.registries.registries.Registries
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider
|
||||
import de.bixilon.minosoft.data.world.container.SectionDataProvider
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
|
||||
@ -30,7 +31,6 @@ import glm_.vec3.Vec3i
|
||||
*/
|
||||
class ChunkSection(
|
||||
var blocks: RegistrySectionDataProvider<BlockState?>,
|
||||
@Deprecated("TODO")
|
||||
var biomes: RegistrySectionDataProvider<Biome>,
|
||||
var blockEntities: SectionDataProvider<BlockEntity?>,
|
||||
var light: IntArray, // packed (skyLight: 0xF0, blockLight: 0x0F)
|
||||
@ -84,4 +84,10 @@ class ChunkSection(
|
||||
return y shl 8 or (z shl 4) or x
|
||||
}
|
||||
}
|
||||
|
||||
fun buildBiomeCache(chunkPosition: Vec2i, sectionHeight: Int, biomeAccessor: BiomeAccessor) {
|
||||
for (blockIndex in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) {
|
||||
biomes[blockIndex] = biomeAccessor.getBiome(Vec3i.of(chunkPosition, sectionHeight, blockIndex.indexPosition))!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock
|
||||
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
|
||||
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.NullBiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.WorldBiomeAccessor
|
||||
import de.bixilon.minosoft.gui.rendering.particle.ParticleRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
|
||||
import de.bixilon.minosoft.gui.rendering.sound.AudioPlayer
|
||||
@ -39,6 +39,8 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
|
||||
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
|
||||
import de.bixilon.minosoft.util.MMath
|
||||
import de.bixilon.minosoft.util.chunk.ChunkUtil
|
||||
import de.bixilon.minosoft.util.chunk.ChunkUtil.fullyLoaded
|
||||
import de.bixilon.minosoft.util.collections.SynchronizedMap
|
||||
import glm_.func.common.clamp
|
||||
import glm_.vec2.Vec2i
|
||||
@ -55,6 +57,7 @@ import kotlin.random.Random
|
||||
class World(
|
||||
val connection: PlayConnection,
|
||||
) : BiomeAccessor {
|
||||
var cacheBiomeAccessor: BiomeAccessor? = null
|
||||
val chunks: SynchronizedMap<Vec2i, Chunk> = synchronizedMapOf()
|
||||
val entities = WorldEntities()
|
||||
var hardcore = false
|
||||
@ -62,7 +65,7 @@ class World(
|
||||
var difficulty: Difficulties? = null
|
||||
var difficultyLocked = false
|
||||
var hashedSeed = 0L
|
||||
var biomeAccessor: BiomeAccessor = NullBiomeAccessor
|
||||
val biomeAccessor: BiomeAccessor = WorldBiomeAccessor(this)
|
||||
var time = 0L
|
||||
var age = 0L
|
||||
var raining = false
|
||||
@ -82,7 +85,7 @@ class World(
|
||||
}
|
||||
|
||||
fun getOrCreateChunk(chunkPosition: Vec2i): Chunk {
|
||||
return chunks.getOrPut(chunkPosition) { Chunk(connection) }
|
||||
return chunks.getOrPut(chunkPosition) { Chunk(connection, chunkPosition) }
|
||||
}
|
||||
|
||||
fun setBlockState(blockPosition: Vec3i, blockState: BlockState?) {
|
||||
@ -121,6 +124,9 @@ class World(
|
||||
|
||||
fun unloadChunk(chunkPosition: Vec2i) {
|
||||
chunks.remove(chunkPosition) ?: return
|
||||
for (neighbour in getChunkNeighbours(chunkPosition)) {
|
||||
neighbour?.neighboursLoaded = false
|
||||
}
|
||||
connection.fireEvent(ChunkUnloadEvent(connection, EventInitiators.UNKNOWN, chunkPosition))
|
||||
}
|
||||
|
||||
@ -235,6 +241,56 @@ class World(
|
||||
return base * 0.8 + 0.2
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return All 8 neighbour chunks
|
||||
*/
|
||||
fun getChunkNeighbours(neighbourPositions: Array<Vec2i>): Array<Chunk?> {
|
||||
val chunks: Array<Chunk?> = arrayOfNulls(neighbourPositions.size)
|
||||
for ((index, neighbourPosition) in neighbourPositions.withIndex()) {
|
||||
chunks[index] = this[neighbourPosition] ?: continue
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
fun getChunkNeighbours(chunkPosition: Vec2i): Array<Chunk?> {
|
||||
return getChunkNeighbours(ChunkUtil.getChunkNeighbourPositions(chunkPosition))
|
||||
}
|
||||
|
||||
fun onChunkUpdate(chunkPosition: Vec2i, chunk: Chunk = get(chunkPosition)!!) {
|
||||
val neighbourPositions = ChunkUtil.getChunkNeighbourPositions(chunkPosition)
|
||||
val neighbours = getChunkNeighbours(neighbourPositions)
|
||||
if (chunk.neighboursLoaded) {
|
||||
return
|
||||
}
|
||||
if (neighbours.fullyLoaded) {
|
||||
chunk.neighboursLoaded = true
|
||||
chunk.buildBiomeCache()
|
||||
}
|
||||
for ((index, neighbourPosition) in neighbourPositions.withIndex()) {
|
||||
if (neighbourPosition == chunkPosition) {
|
||||
continue
|
||||
}
|
||||
val neighbourChunk = neighbours[index] ?: continue
|
||||
|
||||
if (neighbourChunk.neighboursLoaded) {
|
||||
continue
|
||||
}
|
||||
var biomeSourceLoaded = true
|
||||
for (neighbourNeighbourChunk in getChunkNeighbours(neighbourPosition)) {
|
||||
if (neighbourNeighbourChunk?.biomeSource == null) {
|
||||
biomeSourceLoaded = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (biomeSourceLoaded) {
|
||||
neighbourChunk.neighboursLoaded = true
|
||||
neighbourChunk.buildBiomeCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val MAX_SIZE = 29999999
|
||||
const val MAX_SIZEf = MAX_SIZE.toFloat()
|
||||
|
@ -14,16 +14,14 @@
|
||||
package de.bixilon.minosoft.data.world.biome.accessor
|
||||
|
||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
|
||||
import de.bixilon.minosoft.data.world.World
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inChunkPosition
|
||||
import glm_.vec3.Vec3i
|
||||
|
||||
class BlockBiomeAccessor(private val world: World) : BiomeAccessor {
|
||||
class WorldBiomeAccessor(val world: World) : BiomeAccessor {
|
||||
|
||||
override fun getBiome(blockPosition: Vec3i): Biome? {
|
||||
val biomePosition = blockPosition.inChunkPosition
|
||||
return world[blockPosition.chunkPosition]?.biomeSource?.getBiome(biomePosition)
|
||||
return world[blockPosition.chunkPosition]?.getBiome(blockPosition.inChunkPosition)
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import glm_.vec3.Vec3i
|
||||
|
||||
class XZBiomeArray(private val biomes: Array<Biome>) : BiomeSource {
|
||||
|
||||
init {
|
||||
check(biomes.size == ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z) { "Biome array size does not match the xz block count!" }
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import de.bixilon.minosoft.data.registries.ResourceLocation
|
||||
import de.bixilon.minosoft.data.registries.dimension.Dimension
|
||||
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
|
||||
import de.bixilon.minosoft.data.registries.other.game.event.handlers.gamemode.GamemodeChangeEvent
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.BlockBiomeAccessor
|
||||
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
||||
import de.bixilon.minosoft.modding.channels.DefaultPluginChannels
|
||||
import de.bixilon.minosoft.modding.event.EventInitiators
|
||||
@ -162,10 +161,8 @@ class JoinGameS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
|
||||
connection.world.entities.add(entityId, null, playerEntity)
|
||||
connection.world.hashedSeed = hashedSeed
|
||||
connection.world.biomeAccessor = if (connection.version.versionId < ProtocolVersions.V_19W36A) {
|
||||
BlockBiomeAccessor(connection.world)
|
||||
} else {
|
||||
NoiseBiomeAccessor(connection.world)
|
||||
if (connection.version.versionId >= ProtocolVersions.V_19W36A) {
|
||||
connection.world.cacheBiomeAccessor = NoiseBiomeAccessor(connection.world)
|
||||
}
|
||||
TimeWorker.addTask(TimeWorkerTask(150, true) { // ToDo: Temp workaround
|
||||
connection.sendPacket(ClientSettingsC2SP("en_us"))
|
||||
|
@ -16,7 +16,9 @@ package de.bixilon.minosoft.util.chunk
|
||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||
import de.bixilon.minosoft.data.registries.blocks.BlockState
|
||||
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
|
||||
import de.bixilon.minosoft.data.world.Chunk
|
||||
import de.bixilon.minosoft.data.world.ChunkData
|
||||
import de.bixilon.minosoft.data.world.ChunkSection
|
||||
import de.bixilon.minosoft.data.world.biome.source.XZBiomeArray
|
||||
import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider
|
||||
import de.bixilon.minosoft.data.world.palette.Palette.Companion.choosePalette
|
||||
@ -24,6 +26,7 @@ import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.*
|
||||
import de.bixilon.minosoft.util.KUtil.unsafeCast
|
||||
import glm_.vec2.Vec2i
|
||||
import java.util.*
|
||||
|
||||
|
||||
@ -221,4 +224,44 @@ object ChunkUtil {
|
||||
}
|
||||
return XZBiomeArray(biomes.toTypedArray())
|
||||
}
|
||||
|
||||
val Array<Chunk?>.fullyLoaded: Boolean
|
||||
get() {
|
||||
for (neighbour in this) {
|
||||
if (neighbour?.isFullyLoaded != true) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
fun getChunkNeighbourPositions(chunkPosition: Vec2i): Array<Vec2i> {
|
||||
return arrayOf(
|
||||
chunkPosition + Vec2i(-1, -1),
|
||||
chunkPosition + Vec2i(-1, 0),
|
||||
chunkPosition + Vec2i(-1, 1),
|
||||
chunkPosition + Vec2i(0, -1),
|
||||
chunkPosition + Vec2i(0, 1),
|
||||
chunkPosition + Vec2i(1, -1),
|
||||
chunkPosition + Vec2i(1, 0),
|
||||
chunkPosition + Vec2i(1, 1),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param neighbourChunks: **Fully loaded** neighbour chunks
|
||||
*/
|
||||
private fun getSectionNeighbours(neighbourChunks: Array<Chunk>, chunk: Chunk, sectionHeight: Int): Array<ChunkSection?> {
|
||||
return arrayOf(
|
||||
chunk[sectionHeight - 1],
|
||||
chunk[sectionHeight + 1],
|
||||
neighbourChunks[3][sectionHeight],
|
||||
neighbourChunks[4][sectionHeight],
|
||||
neighbourChunks[1][sectionHeight],
|
||||
neighbourChunks[6][sectionHeight],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user