mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-03 05:30:55 +00:00
Gjallarhorn: Implement combined chunk format for storing many chunks in one map.
This commit is contained in:
@ -1,17 +1,23 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.commands
|
package cloud.kubelet.foundation.gjallarhorn.commands
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.export.ChunkExportLoader
|
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.BlockExpanse
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockLogTracker
|
import cloud.kubelet.foundation.gjallarhorn.state.BlockLogTracker
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
||||||
|
import cloud.kubelet.foundation.gjallarhorn.state.SparseBlockStateMap
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.savePngFile
|
import cloud.kubelet.foundation.gjallarhorn.util.savePngFile
|
||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.core.requireObject
|
import com.github.ajalt.clikt.core.requireObject
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
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.options.option
|
||||||
import com.github.ajalt.clikt.parameters.types.enum
|
import com.github.ajalt.clikt.parameters.types.enum
|
||||||
import com.github.ajalt.clikt.parameters.types.int
|
import com.github.ajalt.clikt.parameters.types.int
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
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
|
import org.jetbrains.exposed.sql.Database
|
||||||
|
|
||||||
class ChunkExportLoaderCommand : CliktCommand("Chunk Export Loader", name = "chunk-export-loader") {
|
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 world by argument("world")
|
||||||
private val chunkLoadLimit by option("--chunk-limit", help = "Chunk Limit").int()
|
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 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() {
|
override fun run() {
|
||||||
val tracker = BlockLogTracker(isConcurrent = true)
|
val format: CombinedChunkFormat
|
||||||
val loader = ChunkExportLoader(tracker = tracker)
|
|
||||||
loader.loadAllChunksForWorld(exportDirectoryPath, world, fast = true, limit = chunkLoadLimit)
|
val combinedFormatFile = exportDirectoryPath.resolve("combined.json").toFile()
|
||||||
if (render != null) {
|
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 expanse = BlockExpanse.zeroOffsetAndMax(tracker.calculateZeroBlockOffset(), tracker.calculateMaxBlock())
|
||||||
val map = tracker.buildBlockMap(expanse.offset)
|
val map = tracker.buildBlockMap(expanse.offset)
|
||||||
val renderer = render!!.createNewRenderer(expanse, db)
|
CombinedChunkFormat(expanse, map)
|
||||||
val image = renderer.render(ChangelogSlice.none, map)
|
}
|
||||||
|
|
||||||
|
if (render != null) {
|
||||||
|
val renderer = render!!.createNewRenderer(format.expanse, db)
|
||||||
|
val image = renderer.render(ChangelogSlice.none, format.map)
|
||||||
image.savePngFile("full.png")
|
image.savePngFile("full.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (saveCombinedFormat) {
|
||||||
|
if (combinedFormatFile.exists()) {
|
||||||
|
combinedFormatFile.delete()
|
||||||
|
}
|
||||||
|
Json.encodeToStream(CombinedChunkFormat.serializer(), format, combinedFormatFile.outputStream())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.export
|
package cloud.kubelet.foundation.gjallarhorn.export
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockCoordinate
|
import cloud.kubelet.foundation.gjallarhorn.state.*
|
||||||
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.heimdall.export.ExportedChunk
|
import cloud.kubelet.foundation.heimdall.export.ExportedChunk
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.decodeFromStream
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
|
@ -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
|
||||||
|
)
|
@ -1,7 +1,9 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.state
|
package cloud.kubelet.foundation.gjallarhorn.state
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class BlockCoordinate(
|
data class BlockCoordinate(
|
||||||
val x: Long,
|
val x: Long,
|
||||||
val y: Long,
|
val y: Long,
|
||||||
|
@ -5,10 +5,10 @@ import cloud.kubelet.foundation.gjallarhorn.util.minOfAll
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
open class BlockCoordinateSparseMap<T> : BlockCoordinateStore<T> {
|
open class BlockCoordinateSparseMap<T>(blocks: Map<Long, Map<Long, Map<Long, T>>> = mutableMapOf()) : BlockCoordinateStore<T> {
|
||||||
private var internalBlocks = TreeMap<Long, TreeMap<Long, TreeMap<Long, 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
|
get() = internalBlocks
|
||||||
|
|
||||||
override fun get(position: BlockCoordinate): T? = internalBlocks[position.x]?.get(position.z)?.get(position.z)
|
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 getXSection(x: Long): Map<Long, Map<Long, T>>? = internalBlocks[x]
|
||||||
|
|
||||||
override fun put(position: BlockCoordinate, value: T) {
|
override fun put(position: BlockCoordinate, value: T) {
|
||||||
internalBlocks.getOrPut(position.x) {
|
(((internalBlocks as MutableMap).getOrPut(position.x) {
|
||||||
TreeMap()
|
mutableMapOf()
|
||||||
}.getOrPut(position.z) {
|
} as MutableMap).getOrPut(position.z) {
|
||||||
TreeMap()
|
mutableMapOf()
|
||||||
}[position.y] = value
|
} as MutableMap)[position.y] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createOrModify(position: BlockCoordinate, create: () -> T, modify: (T) -> Unit) {
|
override fun createOrModify(position: BlockCoordinate, create: () -> T, modify: (T) -> Unit) {
|
||||||
@ -52,11 +52,11 @@ open class BlockCoordinateSparseMap<T> : BlockCoordinateStore<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun applyCoordinateOffset(offset: BlockCoordinate) {
|
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 ->
|
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 ->
|
(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 {
|
(zSection.key + offset.z) to zSection.value.mapKeys {
|
||||||
(it.key + offset.y)
|
(it.key + offset.y)
|
||||||
}.toMap(ySectionMap)
|
}.toMap(ySectionMap)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.state
|
package cloud.kubelet.foundation.gjallarhorn.state
|
||||||
|
|
||||||
class BlockExpanse(
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BlockExpanse(
|
||||||
val offset: BlockCoordinate,
|
val offset: BlockCoordinate,
|
||||||
val size: BlockCoordinate
|
val size: BlockCoordinate
|
||||||
) {
|
) {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.state
|
package cloud.kubelet.foundation.gjallarhorn.state
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class BlockState(val type: String) {
|
@Serializable(BlockStateSerializer::class)
|
||||||
|
data class BlockState(val type: String) {
|
||||||
companion object {
|
companion object {
|
||||||
private val cache = ConcurrentHashMap<String, BlockState>()
|
private val cache = ConcurrentHashMap<String, BlockState>()
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,7 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.state
|
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)
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user