Gjallarhorn: Implement combined chunk format for storing many chunks in one map.

This commit is contained in:
Kenneth Endfinger 2022-02-20 06:11:14 -05:00
parent 6bcddb15b5
commit f0c344ca1f
No known key found for this signature in database
GPG Key ID: C4E68E5647420E10
10 changed files with 109 additions and 24 deletions

View File

@ -1,17 +1,23 @@
package cloud.kubelet.foundation.gjallarhorn.commands
import cloud.kubelet.foundation.gjallarhorn.export.ChunkExportLoader
import cloud.kubelet.foundation.gjallarhorn.export.CombinedChunkFormat
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
import cloud.kubelet.foundation.gjallarhorn.state.BlockLogTracker
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
import cloud.kubelet.foundation.gjallarhorn.state.SparseBlockStateMap
import cloud.kubelet.foundation.gjallarhorn.util.savePngFile
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.requireObject
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.enum
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.path
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import org.jetbrains.exposed.sql.Database
class ChunkExportLoaderCommand : CliktCommand("Chunk Export Loader", name = "chunk-export-loader") {
@ -21,17 +27,35 @@ class ChunkExportLoaderCommand : CliktCommand("Chunk Export Loader", name = "chu
private val world by argument("world")
private val chunkLoadLimit by option("--chunk-limit", help = "Chunk Limit").int()
private val render by option("--render", help = "Render Top Down Image").enum<ImageRenderType> { it.id }
private val loadCombinedFormat by option("--load-combined-format").flag()
private val saveCombinedFormat by option("--save-combined-format").flag()
override fun run() {
val tracker = BlockLogTracker(isConcurrent = true)
val loader = ChunkExportLoader(tracker = tracker)
loader.loadAllChunksForWorld(exportDirectoryPath, world, fast = true, limit = chunkLoadLimit)
if (render != null) {
val format: CombinedChunkFormat
val combinedFormatFile = exportDirectoryPath.resolve("combined.json").toFile()
format = if (loadCombinedFormat) {
Json.decodeFromStream(CombinedChunkFormat.serializer(), combinedFormatFile.inputStream())
} else {
val tracker = BlockLogTracker(isConcurrent = true)
val loader = ChunkExportLoader(tracker = tracker)
loader.loadAllChunksForWorld(exportDirectoryPath, world, fast = true, limit = chunkLoadLimit)
val expanse = BlockExpanse.zeroOffsetAndMax(tracker.calculateZeroBlockOffset(), tracker.calculateMaxBlock())
val map = tracker.buildBlockMap(expanse.offset)
val renderer = render!!.createNewRenderer(expanse, db)
val image = renderer.render(ChangelogSlice.none, map)
CombinedChunkFormat(expanse, map)
}
if (render != null) {
val renderer = render!!.createNewRenderer(format.expanse, db)
val image = renderer.render(ChangelogSlice.none, format.map)
image.savePngFile("full.png")
}
if (saveCombinedFormat) {
if (combinedFormatFile.exists()) {
combinedFormatFile.delete()
}
Json.encodeToStream(CombinedChunkFormat.serializer(), format, combinedFormatFile.outputStream())
}
}
}

View File

@ -1,9 +1,6 @@
package cloud.kubelet.foundation.gjallarhorn.export
import cloud.kubelet.foundation.gjallarhorn.state.BlockCoordinate
import cloud.kubelet.foundation.gjallarhorn.state.BlockLogTracker
import cloud.kubelet.foundation.gjallarhorn.state.BlockState
import cloud.kubelet.foundation.gjallarhorn.state.SparseBlockStateMap
import cloud.kubelet.foundation.gjallarhorn.state.*
import cloud.kubelet.foundation.heimdall.export.ExportedChunk
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream

View File

@ -0,0 +1,10 @@
package cloud.kubelet.foundation.gjallarhorn.export
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
import cloud.kubelet.foundation.gjallarhorn.state.SparseBlockStateMap
@kotlinx.serialization.Serializable
class CombinedChunkFormat(
val expanse: BlockExpanse,
val map: SparseBlockStateMap
)

View File

@ -1,7 +1,9 @@
package cloud.kubelet.foundation.gjallarhorn.state
import java.util.*
import kotlinx.serialization.Serializable
@Serializable
data class BlockCoordinate(
val x: Long,
val y: Long,

View File

@ -5,10 +5,10 @@ import cloud.kubelet.foundation.gjallarhorn.util.minOfAll
import java.util.*
import kotlin.math.absoluteValue
open class BlockCoordinateSparseMap<T> : BlockCoordinateStore<T> {
private var internalBlocks = TreeMap<Long, TreeMap<Long, TreeMap<Long, T>>>()
open class BlockCoordinateSparseMap<T>(blocks: Map<Long, Map<Long, Map<Long, T>>> = mutableMapOf()) : BlockCoordinateStore<T> {
private var internalBlocks = blocks
val blocks: TreeMap<Long, TreeMap<Long, TreeMap<Long, T>>>
val blocks: Map<Long, Map<Long, Map<Long, T>>>
get() = internalBlocks
override fun get(position: BlockCoordinate): T? = internalBlocks[position.x]?.get(position.z)?.get(position.z)
@ -16,11 +16,11 @@ open class BlockCoordinateSparseMap<T> : BlockCoordinateStore<T> {
override fun getXSection(x: Long): Map<Long, Map<Long, T>>? = internalBlocks[x]
override fun put(position: BlockCoordinate, value: T) {
internalBlocks.getOrPut(position.x) {
TreeMap()
}.getOrPut(position.z) {
TreeMap()
}[position.y] = value
(((internalBlocks as MutableMap).getOrPut(position.x) {
mutableMapOf()
} as MutableMap).getOrPut(position.z) {
mutableMapOf()
} as MutableMap)[position.y] = value
}
override fun createOrModify(position: BlockCoordinate, create: () -> T, modify: (T) -> Unit) {
@ -52,11 +52,11 @@ open class BlockCoordinateSparseMap<T> : BlockCoordinateStore<T> {
}
fun applyCoordinateOffset(offset: BlockCoordinate) {
val root = TreeMap<Long, TreeMap<Long, TreeMap<Long, T>>>()
val root = mutableMapOf<Long, MutableMap<Long, MutableMap<Long, T>>>()
internalBlocks = internalBlocks.map { xSection ->
val zSectionMap = TreeMap<Long, TreeMap<Long, T>>()
val zSectionMap = mutableMapOf<Long, MutableMap<Long, T>>()
(xSection.key + offset.x) to xSection.value.map { zSection ->
val ySectionMap = TreeMap<Long, T>()
val ySectionMap = mutableMapOf<Long, T>()
(zSection.key + offset.z) to zSection.value.mapKeys {
(it.key + offset.y)
}.toMap(ySectionMap)

View File

@ -1,6 +1,9 @@
package cloud.kubelet.foundation.gjallarhorn.state
class BlockExpanse(
import kotlinx.serialization.Serializable
@Serializable
data class BlockExpanse(
val offset: BlockCoordinate,
val size: BlockCoordinate
) {

View File

@ -1,8 +1,10 @@
package cloud.kubelet.foundation.gjallarhorn.state
import kotlinx.serialization.Serializable
import java.util.concurrent.ConcurrentHashMap
class BlockState(val type: String) {
@Serializable(BlockStateSerializer::class)
data class BlockState(val type: String) {
companion object {
private val cache = ConcurrentHashMap<String, BlockState>()

View File

@ -0,0 +1,20 @@
package cloud.kubelet.foundation.gjallarhorn.state
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class BlockStateSerializer : KSerializer<BlockState> {
override val descriptor: SerialDescriptor
get() = String.serializer().descriptor
override fun deserialize(decoder: Decoder): BlockState {
return BlockState.cached(decoder.decodeString())
}
override fun serialize(encoder: Encoder, value: BlockState) {
encoder.encodeString(value.type)
}
}

View File

@ -1,3 +1,7 @@
package cloud.kubelet.foundation.gjallarhorn.state
class SparseBlockStateMap : BlockCoordinateSparseMap<BlockState>()
import kotlinx.serialization.Serializable
@Serializable(SparseBlockStateMapSerializer::class)
class SparseBlockStateMap(blocks: Map<Long, Map<Long, Map<Long, BlockState>>> = mutableMapOf()) :
BlockCoordinateSparseMap<BlockState>(blocks)

View File

@ -0,0 +1,23 @@
package cloud.kubelet.foundation.gjallarhorn.state
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class SparseBlockStateMapSerializer : KSerializer<SparseBlockStateMap> {
private val internal = MapSerializer(Long.serializer(), MapSerializer(Long.serializer(), MapSerializer(Long.serializer(), BlockState.serializer())))
override val descriptor: SerialDescriptor
get() = internal.descriptor
override fun deserialize(decoder: Decoder): SparseBlockStateMap {
val data = internal.deserialize(decoder)
return SparseBlockStateMap(data)
}
override fun serialize(encoder: Encoder, value: SparseBlockStateMap) {
internal.serialize(encoder, value.blocks)
}
}