improve block entity handling, fix some bugs, improve performance of fast biomes

This commit is contained in:
Bixilon 2021-11-14 22:22:47 +01:00
parent 20062dc0ea
commit ee02484494
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 56 additions and 35 deletions

View File

@ -18,7 +18,6 @@ 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
@ -26,6 +25,9 @@ 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 de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import glm_.vec2.Vec2i
import glm_.vec3.Vec3i
@ -42,6 +44,7 @@ class Chunk(
var topLight: IntArray? = null
val lowestSection = connection.world.dimension!!.lowestSection
val highestSection = connection.world.dimension!!.highestSection
val cacheBiomes = connection.world.cacheBiomeAccessor != null
var blocksInitialized = false // All block data was received
var biomesInitialized = false // All biome data is initialized (aka. cache built, or similar)
@ -106,6 +109,11 @@ class Chunk(
}
blocksInitialized = true
}
data.blockEntities?.let {
for ((position, blockEntity) in it) {
setBlockEntity(position, blockEntity)
}
}
data.light?.let {
for ((index, light) in it.withIndex()) {
light ?: continue
@ -124,8 +132,12 @@ class Chunk(
}
data.biomeSource?.let {
this.biomeSource = it
if (!cacheBiomes) {
biomesInitialized = true
}
}
connection.world.onChunkUpdate(chunkPosition, this)
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.UNKNOWN, chunkPosition, this))
}
@Synchronized
@ -170,11 +182,13 @@ class Chunk(
}
fun buildBiomeCache() {
val start = System.nanoTime()
val cacheBiomeAccessor = connection.world.cacheBiomeAccessor ?: return
check(!biomesInitialized) { "Biome cache already initialized!" }
check(cacheBiomes) { "Cache is disabled!" }
check(neighboursLoaded)
// isEmpty
// ToDo: Return if isEmpty
val neighbours: Array<Chunk> = connection.world.getChunkNeighbours(chunkPosition).unsafeCast()
for ((sectionIndex, section) in sections!!.withIndex()) {
@ -183,23 +197,16 @@ class Chunk(
section.buildBiomeCache(chunkPosition, sectionHeight, this, neighbours, cacheBiomeAccessor)
}
biomesInitialized = true
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.UNKNOWN, chunkPosition, this))
val delta = System.nanoTime() - start
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Took ${delta}ns, ${delta / 1000}µs, ${delta / 1000_000}ms" }
}
override fun iterator(): Iterator<ChunkSection?> {
return sections!!.iterator()
}
override fun getBiome(blockPosition: Vec3i): Biome? {
if (connection.world.cacheBiomeAccessor != null) {
val sectionHeight = blockPosition.sectionHeight
return get(sectionHeight)?.biomes?.get(blockPosition.x, sectionHeight, blockPosition.z)
}
return biomeSource?.getBiome(blockPosition.inChunkPosition)
}
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
if (connection.world.cacheBiomeAccessor != null) {
if (cacheBiomes) {
val sectionHeight = y.sectionHeight
return get(sectionHeight)?.biomes?.get(x, sectionHeight, z)
}

View File

@ -13,12 +13,15 @@
package de.bixilon.minosoft.data.world
import de.bixilon.minosoft.data.entities.block.BlockEntity
import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
import de.bixilon.minosoft.data.world.container.RegistrySectionDataProvider
import glm_.vec3.Vec3i
class ChunkData(
var blocks: Array<RegistrySectionDataProvider<BlockState?>?>? = null,
var blockEntities: Map<Vec3i, BlockEntity>? = null,
var biomeSource: BiomeSource? = null,
var light: Array<IntArray?>? = null,
var bottomLight: IntArray? = null,
@ -28,6 +31,7 @@ class ChunkData(
@Synchronized
fun replace(data: ChunkData) {
data.blocks?.let { this.blocks = it }
data.blockEntities?.let { this.blockEntities = it }
data.biomeSource?.let { this.biomeSource = it }
data.light?.let { this.light = it }
data.bottomLight?.let { this.bottomLight = it }

View File

@ -45,9 +45,9 @@ class ChunkSection(
acquire()
var blockEntity: BlockEntity?
for (index in 0 until ProtocolDefinition.BLOCKS_PER_SECTION) {
blockEntity = blockEntities[index] ?: continue
blockEntity = blockEntities.unsafeGet(index) ?: continue
val position = Vec3i.of(chunkPosition, sectionHeight, index.indexPosition)
val blockState = blocks[index] ?: continue
val blockState = blocks.unsafeGet(index) ?: continue
blockEntity.tick(connection, blockState, position)
}
release()

View File

@ -34,6 +34,7 @@ import de.bixilon.minosoft.gui.rendering.util.VecUtil.minus
import de.bixilon.minosoft.gui.rendering.util.VecUtil.plus
import de.bixilon.minosoft.modding.event.EventInitiators
import de.bixilon.minosoft.modding.event.events.BlockSetEvent
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
import de.bixilon.minosoft.modding.event.events.ChunkUnloadEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
@ -125,8 +126,11 @@ class World(
fun unloadChunk(chunkPosition: Vec2i) {
chunks.remove(chunkPosition) ?: return
for (neighbour in getChunkNeighbours(chunkPosition)) {
neighbour?.neighboursLoaded = false
val neighbourPositions = ChunkUtil.getChunkNeighbourPositions(chunkPosition)
for (neighbourPosition in neighbourPositions) {
val neighbour = this[neighbourPosition] ?: continue
neighbour.neighboursLoaded = false
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.UNKNOWN, neighbourPosition, neighbour))
}
connection.fireEvent(ChunkUnloadEvent(connection, EventInitiators.UNKNOWN, chunkPosition))
}
@ -270,8 +274,10 @@ class World(
}
if (neighbours.fullyLoaded) {
chunk.neighboursLoaded = true
if (cacheBiomeAccessor != null) {
chunk.buildBiomeCache()
}
}
for ((index, neighbourPosition) in neighbourPositions.withIndex()) {
if (neighbourPosition == chunkPosition) {
continue
@ -289,9 +295,13 @@ class World(
}
}
if (biomeSourceLoaded) {
neighbourChunk.neighboursLoaded = true
neighbourChunk.neighboursLoaded = true // ToDo: only if fully loaded not just biomes
if (cacheBiomeAccessor != null) {
neighbourChunk.buildBiomeCache()
}
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.UNKNOWN, neighbourPosition, neighbourChunk))
}
}
}

View File

@ -39,6 +39,16 @@ open class SectionDataProvider<T>(
return value
}
@Suppress("UNCHECKED_CAST")
fun unsafeGet(index: Int): T {
return data[index] as T
}
@Suppress("UNCHECKED_CAST")
fun unsafeGet(x: Int, y: Int, z: Int): T {
return data[y shl 8 or (z shl 4) or x] as T
}
private fun recalculateCount() {
var count = 0
for (value in data) {

View File

@ -271,6 +271,7 @@ class DebugHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<GridLayout>
this@DebugWorldInfo += AutoTextElement(hudRenderer, 1) { BaseComponent("Sky properties ", connection.world.dimension?.skyProperties) }
this@DebugWorldInfo += AutoTextElement(hudRenderer, 1) { BaseComponent("Biome ", connection.world.getBiome(blockPosition)) }
this@DebugWorldInfo += AutoTextElement(hudRenderer, 1) { with(connection.world.getLight(blockPosition)) { BaseComponent("Light block=", (this and 0x0F), ", sky=", (this ushr 4)) } }
this@DebugWorldInfo += AutoTextElement(hudRenderer, 1) { BaseComponent("Fully loaded: ", chunk.isFullyLoaded) }
lastChunk = chunk
}

View File

@ -21,7 +21,6 @@ import de.bixilon.minosoft.datafixer.BlockEntityFixer.fix
import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
import de.bixilon.minosoft.modding.event.EventInitiators
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
import de.bixilon.minosoft.modding.event.events.ChunkUnloadEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
@ -41,7 +40,6 @@ import glm_.vec3.Vec3i
import java.util.*
class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val blockEntities: MutableMap<Vec3i, BlockEntity> = mutableMapOf()
val chunkPosition: Vec2i
var chunkData: ChunkData? = ChunkData()
private set
@ -107,6 +105,7 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
buffer.pointer = size + lastPos
}
if (buffer.versionId >= ProtocolVersions.V_1_9_4) {
val blockEntities: MutableMap<Vec3i, BlockEntity> = mutableMapOf()
val positionOffset = Vec3i.of(chunkPosition, dimension.lowestSection, Vec3i.EMPTY)
val blockEntitiesCount = buffer.readVarInt()
for (i in 0 until blockEntitiesCount) {
@ -121,6 +120,7 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
entity.updateNBT(nbt)
blockEntities[position] = entity
}
chunkData!!.blockEntities = blockEntities
}
}
}
@ -129,10 +129,6 @@ class ChunkDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
chunkData?.let {
val chunk = connection.world.getOrCreateChunk(chunkPosition)
chunk.setData(chunkData!!, !isFullChunk)
for ((position, blockEntity) in blockEntities) {
chunk.setBlockEntity(position, blockEntity)
}
connection.fireEvent(ChunkDataChangeEvent(connection, this))
} ?: let {
connection.world.unloadChunk(chunkPosition)
connection.fireEvent(ChunkUnloadEvent(connection, EventInitiators.SERVER, chunkPosition))

View File

@ -15,8 +15,6 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.world.ChunkData
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.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
@ -68,7 +66,5 @@ class ChunkLightDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
override fun handle(connection: PlayConnection) {
val chunk = connection.world.getOrCreateChunk(chunkPosition)
chunk.setData(chunkData)
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.SERVER, chunkPosition, chunk))
}
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.protocol.packets.s2c.play
import com.google.common.collect.HashBiMap
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.Difficulties
import de.bixilon.minosoft.data.abilities.Gamemodes
import de.bixilon.minosoft.data.registries.DefaultRegistries
@ -161,7 +162,7 @@ class JoinGameS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
connection.world.entities.add(entityId, null, playerEntity)
connection.world.hashedSeed = hashedSeed
if (connection.version.versionId >= ProtocolVersions.V_19W36A) {
if (connection.version.versionId >= ProtocolVersions.V_19W36A && !Minosoft.config.config.game.graphics.fastBiomeNoise) {
connection.world.cacheBiomeAccessor = NoiseBiomeAccessor(connection.world)
}
TimeWorker.addTask(TimeWorkerTask(150, true) { // ToDo: Temp workaround

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.data.world.ChunkData
import de.bixilon.minosoft.modding.event.EventInitiators
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
import de.bixilon.minosoft.modding.event.events.ChunkUnloadEvent
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
@ -74,7 +73,6 @@ class MassChunkDataS2CP() : PlayS2CPacket() {
data?.let {
val chunk = connection.world.getOrCreateChunk(chunkPosition)
chunk.setData(data)
connection.fireEvent(ChunkDataChangeEvent(connection, EventInitiators.SERVER, chunkPosition, chunk))
} ?: let {
// unload chunk
connection.world.unloadChunk(chunkPosition)

View File

@ -262,6 +262,4 @@ object ChunkUtil {
neighbourChunks[6][sectionHeight],
)
}
}