diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt index f7086cd..dfbf14d 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt @@ -2,13 +2,14 @@ package cloud.kubelet.foundation.gjallarhorn.render import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse import cloud.kubelet.foundation.gjallarhorn.state.BlockMap -import cloud.kubelet.foundation.gjallarhorn.util.RandomColorKey +import cloud.kubelet.foundation.gjallarhorn.util.BlockColorKey +import cloud.kubelet.foundation.gjallarhorn.util.defaultBlockColorMap import java.awt.Color import java.awt.image.BufferedImage class BlockDiversityRenderer(val expanse: BlockExpanse, quadPixelSize: Int = defaultQuadPixelSize) : BlockGridRenderer(quadPixelSize) { - private val randomColorKey = RandomColorKey() + private val blockColorKey = BlockColorKey(defaultBlockColorMap) override fun render(map: BlockMap): BufferedImage = buildPixelQuadImage(expanse) { graphics, x, z -> val maybeYBlocks = map.blocks[x]?.get(z) @@ -22,7 +23,7 @@ class BlockDiversityRenderer(val expanse: BlockExpanse, quadPixelSize: Int = def return@buildPixelQuadImage } - val color = randomColorKey.map(maxBlockState.type) + val color = blockColorKey.map(maxBlockState.type) setPixelQuad(graphics, x, z, color.rgb) } } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockChangelogSlice.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockChangelogSlice.kt index e80f8c7..33099c8 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockChangelogSlice.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockChangelogSlice.kt @@ -6,10 +6,10 @@ import java.time.Instant data class BlockChangelogSlice(val from: Instant, val to: Instant, val relative: Duration) { constructor(from: Instant, to: Instant) : this(from, to, Duration.ofMillis(to.toEpochMilli() - from.toEpochMilli())) - fun changeResolutionTime(): Instant = to.minus(relative) + val changeResolutionTime: Instant = to.minus(relative) fun isTimeWithin(time: Instant) = time in from..to - fun isRelativeWithin(time: Instant) = time in changeResolutionTime()..to + fun isRelativeWithin(time: Instant) = time in changeResolutionTime..to fun split(): List { val half = relative.dividedBy(2) diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapRenderPool.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapRenderPool.kt index 69a0251..bdc8595 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapRenderPool.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapRenderPool.kt @@ -16,12 +16,12 @@ class BlockMapRenderPool( private val playbackJobFutures = ConcurrentHashMap>() private val renderJobFutures = ConcurrentHashMap>() - fun submitPlaybackJob(slice: BlockChangelogSlice) { + fun submitPlaybackJob(id: String, slice: BlockChangelogSlice) { val future = threadPoolExecutor.submit { try { - runPlaybackSlice(slice) + runPlaybackSlice(id, slice) } catch (e: Exception) { - logger.error("Failed to run playback job for slice $slice", e) + logger.error("Failed to run playback job for slice $id", e) } } playbackJobFutures[slice] = future @@ -41,7 +41,7 @@ class BlockMapRenderPool( fun render(slices: List) { for (slice in slices) { - submitPlaybackJob(slice) + submitPlaybackJob((slices.indexOf(slice) + 1).toString(), slice) } for (future in playbackJobFutures.values) { @@ -55,16 +55,22 @@ class BlockMapRenderPool( } } - private fun runPlaybackSlice(slice: BlockChangelogSlice) { + private fun runPlaybackSlice(id: String, slice: BlockChangelogSlice) { + val start = System.currentTimeMillis() val sliced = changelog.slice(slice) val tracker = BlockLogTracker(blockTrackMode) tracker.replay(sliced) + delegate.postProcessTracker(tracker) if (tracker.isNotEmpty()) { trackers[slice] = tracker } + val end = System.currentTimeMillis() + val timeInMilliseconds = end - start + logger.info("Playback Completed for Slice $id in ${timeInMilliseconds}ms") } interface RenderPoolDelegate { + fun postProcessTracker(tracker: BlockLogTracker) fun buildRenderJobs(pool: BlockMapRenderPool, trackers: MutableMap) } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapTimelapse.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapTimelapse.kt index 5bfc905..5e1bbd5 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapTimelapse.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMapTimelapse.kt @@ -2,6 +2,7 @@ package cloud.kubelet.foundation.gjallarhorn.state import java.time.Duration import java.time.Instant +import java.util.stream.Stream class BlockMapTimelapse(val trim: Pair? = null) : BlockMapRenderPool.RenderPoolDelegate { @@ -28,35 +29,23 @@ class BlockMapTimelapse(val trim: Pair? = n minimumTimeInterval: Duration, slices: List ): List { - return slices.flatMap { slice -> + return slices.parallelStream().flatMap { slice -> val count = changelog.countRelativeChangesInSlice(slice) if (count < targetChangeThreshold || slice.relative < minimumTimeInterval ) { - return@flatMap listOf(slice) + return@flatMap Stream.of(slice) } val split = slice.split() - return@flatMap splitChangelogSlicesWithThreshold(changelog, targetChangeThreshold, minimumTimeInterval, split) - } + return@flatMap splitChangelogSlicesWithThreshold(changelog, targetChangeThreshold, minimumTimeInterval, split).parallelStream() + }.toList() } override fun buildRenderJobs( pool: BlockMapRenderPool, trackers: MutableMap ) { - if (trim != null) { - trackers.values.forEach { tracker -> - tracker.trimOutsideXAndZRange(trim.first, trim.second) - } - } - - for ((slice, tracker) in trackers.entries.toList()) { - if (tracker.isEmpty()) { - trackers.remove(slice) - } - } - val allBlockOffsets = trackers.map { it.value.calculateZeroBlockOffset() } val globalBlockOffset = BlockCoordinate.maxOf(allBlockOffsets) val allBlockMaxes = trackers.map { it.value.calculateMaxBlock() } @@ -65,14 +54,16 @@ class BlockMapTimelapse(val trim: Pair? = n val renderer = pool.rendererFactory(globalBlockExpanse) for ((slice, tracker) in trackers) { - if (tracker.isEmpty()) { - continue - } - pool.submitRenderJob(slice) { val map = tracker.buildBlockMap(globalBlockExpanse.offset) renderer.render(map) } } } + + override fun postProcessTracker(tracker: BlockLogTracker) { + if (trim != null) { + tracker.trimOutsideXAndZRange(trim.first, trim.second) + } + } } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/RandomColorKey.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/BlockColorKey.kt similarity index 67% rename from tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/RandomColorKey.kt rename to tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/BlockColorKey.kt index eefc725..7b501a2 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/RandomColorKey.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/BlockColorKey.kt @@ -3,10 +3,10 @@ package cloud.kubelet.foundation.gjallarhorn.util import java.awt.Color import java.util.concurrent.ConcurrentHashMap -class RandomColorKey { - private val colors = ConcurrentHashMap() +class BlockColorKey(assigned: Map) { + private val colors = ConcurrentHashMap(assigned) - fun map(key: String) = colors.computeIfAbsent(key) { findUniqueColor() } + fun map(key: String): Color = colors.computeIfAbsent(key) { findUniqueColor() } private fun findUniqueColor(): Color { var random = randomColor() diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/BlockColorKeys.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/BlockColorKeys.kt new file mode 100644 index 0000000..1c4c123 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/BlockColorKeys.kt @@ -0,0 +1,17 @@ +package cloud.kubelet.foundation.gjallarhorn.util + +import java.awt.Color + +val defaultBlockColorMap = mapOf( + "minecraft:air" to Color.black, + "minecraft:dirt" to Color.decode("#9b7653"), + "minecraft:farmland" to Color.decode("#5d3f2a"), + "minecraft:stone" to Color.decode("#787366"), + "minecraft:cobblestone" to Color.decode("#c4bca7"), + "minecraft:wheat" to Color.decode("#9e884c"), + "minecraft:carrots" to Color.decode("#f89d40"), + "minecraft:stone_brick_stairs" to Color.decode("#b8a18c"), + "minecraft:dirt_path" to Color.decode("#8f743d"), + "minecraft:deepslate_tiles" to Color.decode("#49494b"), + "minecraft:spruce_planks" to Color.decode("#60492d") +)