mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-03 05:30:55 +00:00
Gjallarhorn: Implement render loop.
This commit is contained in:
@ -1,15 +1,13 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.commands
|
package cloud.kubelet.foundation.gjallarhorn.commands
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.render.BlockDiversityRenderer
|
import cloud.kubelet.foundation.gjallarhorn.render.*
|
||||||
import cloud.kubelet.foundation.gjallarhorn.render.BlockHeightMapRenderer
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.render.PlayerLocationShareRenderer
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.render.BlockImageRenderer
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.*
|
import cloud.kubelet.foundation.gjallarhorn.state.*
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.compose
|
import cloud.kubelet.foundation.gjallarhorn.util.compose
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.savePngFile
|
import cloud.kubelet.foundation.gjallarhorn.util.savePngFile
|
||||||
import cloud.kubelet.foundation.heimdall.view.BlockChangeView
|
import cloud.kubelet.foundation.heimdall.view.BlockChangeView
|
||||||
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.options.default
|
||||||
import com.github.ajalt.clikt.parameters.options.flag
|
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.options.required
|
import com.github.ajalt.clikt.parameters.options.required
|
||||||
@ -25,6 +23,7 @@ import java.awt.Font
|
|||||||
import java.awt.font.TextLayout
|
import java.awt.font.TextLayout
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor
|
import java.util.concurrent.ScheduledThreadPoolExecutor
|
||||||
|
|
||||||
class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name = "block-change-timelapse") {
|
class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name = "block-change-timelapse") {
|
||||||
@ -47,15 +46,33 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name
|
|||||||
private val fromCoordinate by option("--trim-from", help = "Trim From Coordinate")
|
private val fromCoordinate by option("--trim-from", help = "Trim From Coordinate")
|
||||||
private val toCoordinate by option("--trim-to", help = "Trim To Coordinate")
|
private val toCoordinate by option("--trim-to", help = "Trim To Coordinate")
|
||||||
|
|
||||||
|
private val parallelPoolSize by option("--pool-size", help = "Task Pool Size").int().default(8)
|
||||||
|
private val inMemoryRender by option("--in-memory-render", help = "Render Images to Memory").flag()
|
||||||
|
private val shouldRenderLoop by option("--loop-render", help = "Loop Render").flag()
|
||||||
|
private val quadPixelNoop by option("--quad-pixel-noop", help = "Disable Quad Pixel Render").flag()
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(BlockChangeTimelapseCommand::class.java)
|
private val logger = LoggerFactory.getLogger(BlockChangeTimelapseCommand::class.java)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val threadPoolExecutor = ScheduledThreadPoolExecutor(16)
|
if (quadPixelNoop) {
|
||||||
|
BlockGridRenderer.globalQuadPixelNoop = true
|
||||||
|
}
|
||||||
|
val threadPoolExecutor = ScheduledThreadPoolExecutor(parallelPoolSize)
|
||||||
|
if (shouldRenderLoop) {
|
||||||
|
while (true) {
|
||||||
|
perform(threadPoolExecutor)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
perform(threadPoolExecutor)
|
||||||
|
}
|
||||||
|
threadPoolExecutor.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun perform(threadPoolExecutor: ScheduledThreadPoolExecutor) {
|
||||||
val trim = maybeBuildTrim()
|
val trim = maybeBuildTrim()
|
||||||
val filter = compose(
|
val filter = compose(
|
||||||
combine = { a, b -> a and b },
|
combine = { a, b -> a and b },
|
||||||
{ trim?.first?.x != null } to { BlockChangeView.x greaterEq trim!!.first.x },
|
{ trim?.first?.x != null } to { BlockChangeView.x greaterEq trim!!.first.x },
|
||||||
{ trim?.first?.z != null } to { BlockChangeView.z greaterEq trim!!.first.z },
|
{ trim?.first?.z != null } to { BlockChangeView.z greaterEq trim!!.first.z },
|
||||||
{ trim?.second?.x != null } to { BlockChangeView.x lessEq trim!!.second.x },
|
{ trim?.second?.x != null } to { BlockChangeView.x lessEq trim!!.second.x },
|
||||||
{ trim?.second?.z != null } to { BlockChangeView.z lessEq trim!!.second.z }
|
{ trim?.second?.z != null } to { BlockChangeView.z lessEq trim!!.second.z }
|
||||||
@ -77,6 +94,12 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name
|
|||||||
|
|
||||||
val imagePadCount = slices.size.toString().length
|
val imagePadCount = slices.size.toString().length
|
||||||
|
|
||||||
|
val inMemoryPool = if (inMemoryRender) {
|
||||||
|
ConcurrentHashMap<ChangelogSlice, BufferedImage>()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val pool = BlockMapRenderPool(
|
val pool = BlockMapRenderPool(
|
||||||
changelog = changelog,
|
changelog = changelog,
|
||||||
blockTrackMode = if (considerAirBlocks) BlockTrackMode.AirOnDelete else BlockTrackMode.RemoveOnDelete,
|
blockTrackMode = if (considerAirBlocks) BlockTrackMode.AirOnDelete else BlockTrackMode.RemoveOnDelete,
|
||||||
@ -96,16 +119,19 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name
|
|||||||
layout.draw(graphics, 60f, 60f)
|
layout.draw(graphics, 60f, 60f)
|
||||||
graphics.dispose()
|
graphics.dispose()
|
||||||
val index = slices.indexOf(slice) + 1
|
val index = slices.indexOf(slice) + 1
|
||||||
val suffix = "-${index.toString().padStart(imagePadCount, '0')}"
|
if (inMemoryRender) {
|
||||||
result.savePngFile("${render.id}${suffix}.png")
|
inMemoryPool?.put(slice, result)
|
||||||
|
} else {
|
||||||
|
val suffix = "-${index.toString().padStart(imagePadCount, '0')}"
|
||||||
|
result.savePngFile("${render.id}${suffix}.png")
|
||||||
|
}
|
||||||
logger.info("Rendered Timelapse Slice $index")
|
logger.info("Rendered Timelapse Slice $index")
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.render(slices)
|
pool.render(slices)
|
||||||
threadPoolExecutor.shutdown()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun maybeBuildTrim(): Pair<BlockCoordinate, BlockCoordinate>? {
|
private fun maybeBuildTrim(): Pair<BlockCoordinate, BlockCoordinate>? {
|
||||||
if (fromCoordinate == null || toCoordinate == null) {
|
if (fromCoordinate == null || toCoordinate == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.render
|
package cloud.kubelet.foundation.gjallarhorn.render
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
|
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockMap
|
import cloud.kubelet.foundation.gjallarhorn.state.BlockStateMap
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.BlockColorKey
|
import cloud.kubelet.foundation.gjallarhorn.util.BlockColorKey
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.defaultBlockColorMap
|
import cloud.kubelet.foundation.gjallarhorn.util.defaultBlockColorMap
|
||||||
@ -12,7 +12,7 @@ class BlockDiversityRenderer(val expanse: BlockExpanse, quadPixelSize: Int = def
|
|||||||
BlockGridRenderer(quadPixelSize) {
|
BlockGridRenderer(quadPixelSize) {
|
||||||
private val blockColorKey = BlockColorKey(defaultBlockColorMap)
|
private val blockColorKey = BlockColorKey(defaultBlockColorMap)
|
||||||
|
|
||||||
override fun render(slice: ChangelogSlice, map: BlockMap): BufferedImage = buildPixelQuadImage(expanse) { graphics, x, z ->
|
override fun render(slice: ChangelogSlice, map: BlockStateMap): BufferedImage = buildPixelQuadImage(expanse) { graphics, x, z ->
|
||||||
val maybeYBlocks = map.blocks[x]?.get(z)
|
val maybeYBlocks = map.blocks[x]?.get(z)
|
||||||
if (maybeYBlocks == null) {
|
if (maybeYBlocks == null) {
|
||||||
setPixelQuad(graphics, x, z, Color.white)
|
setPixelQuad(graphics, x, z, Color.white)
|
||||||
|
@ -8,6 +8,9 @@ import java.awt.image.BufferedImage
|
|||||||
|
|
||||||
abstract class BlockGridRenderer(val quadPixelSize: Int = defaultQuadPixelSize) : BlockImageRenderer {
|
abstract class BlockGridRenderer(val quadPixelSize: Int = defaultQuadPixelSize) : BlockImageRenderer {
|
||||||
protected fun setPixelQuad(graphics: Graphics2D, x: Long, z: Long, color: Color) {
|
protected fun setPixelQuad(graphics: Graphics2D, x: Long, z: Long, color: Color) {
|
||||||
|
if (globalQuadPixelNoop) {
|
||||||
|
return
|
||||||
|
}
|
||||||
drawSquare(graphics, x * quadPixelSize, z * quadPixelSize, quadPixelSize.toLong(), color)
|
drawSquare(graphics, x * quadPixelSize, z * quadPixelSize, quadPixelSize.toLong(), color)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,5 +42,6 @@ abstract class BlockGridRenderer(val quadPixelSize: Int = defaultQuadPixelSize)
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val defaultQuadPixelSize = 4
|
const val defaultQuadPixelSize = 4
|
||||||
|
var globalQuadPixelNoop = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.render
|
package cloud.kubelet.foundation.gjallarhorn.render
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
|
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockMap
|
import cloud.kubelet.foundation.gjallarhorn.state.BlockStateMap
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.FloatClamp
|
import cloud.kubelet.foundation.gjallarhorn.util.FloatClamp
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
|
||||||
class BlockHeightMapRenderer(val expanse: BlockExpanse, quadPixelSize: Int = defaultQuadPixelSize) :
|
class BlockHeightMapRenderer(val expanse: BlockExpanse, quadPixelSize: Int = defaultQuadPixelSize) :
|
||||||
BlockHeatMapRenderer(quadPixelSize) {
|
BlockHeatMapRenderer(quadPixelSize) {
|
||||||
override fun render(slice: ChangelogSlice, map: BlockMap): BufferedImage {
|
override fun render(slice: ChangelogSlice, map: BlockStateMap): BufferedImage {
|
||||||
val yMin = map.blocks.minOf { xSection -> xSection.value.minOf { zSection -> zSection.value.minOf { it.key } } }
|
val yMin = map.blocks.minOf { xSection -> xSection.value.minOf { zSection -> zSection.value.minOf { it.key } } }
|
||||||
val yMax = map.blocks.maxOf { xSection -> xSection.value.maxOf { zSection -> zSection.value.maxOf { it.key } } }
|
val yMax = map.blocks.maxOf { xSection -> xSection.value.maxOf { zSection -> zSection.value.maxOf { it.key } } }
|
||||||
val clamp = FloatClamp(yMin, yMax)
|
val clamp = FloatClamp(yMin, yMax)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.render
|
package cloud.kubelet.foundation.gjallarhorn.render
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockMap
|
import cloud.kubelet.foundation.gjallarhorn.state.BlockStateMap
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
||||||
|
|
||||||
interface BlockMapRenderer<T> {
|
interface BlockMapRenderer<T> {
|
||||||
fun render(slice: ChangelogSlice, map: BlockMap): T
|
fun render(slice: ChangelogSlice, map: BlockStateMap): T
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.render
|
package cloud.kubelet.foundation.gjallarhorn.render
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockCoordinate
|
import cloud.kubelet.foundation.gjallarhorn.state.*
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.BlockMap
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.util.BlockColorKey
|
import cloud.kubelet.foundation.gjallarhorn.util.BlockColorKey
|
||||||
import cloud.kubelet.foundation.heimdall.table.PlayerPositionTable
|
import cloud.kubelet.foundation.heimdall.table.PlayerPositionTable
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
@ -12,37 +9,44 @@ import org.jetbrains.exposed.sql.select
|
|||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class PlayerLocationShareRenderer(
|
class PlayerLocationShareRenderer(
|
||||||
val expanse: BlockExpanse,
|
val expanse: BlockExpanse,
|
||||||
val db: Database,
|
val db: Database,
|
||||||
quadPixelSize: Int = defaultQuadPixelSize) : BlockGridRenderer(quadPixelSize) {
|
quadPixelSize: Int = defaultQuadPixelSize
|
||||||
|
) : BlockGridRenderer(quadPixelSize) {
|
||||||
private val colorKey = BlockColorKey(mapOf())
|
private val colorKey = BlockColorKey(mapOf())
|
||||||
|
|
||||||
override fun render(slice: ChangelogSlice, map: BlockMap): BufferedImage {
|
override fun render(slice: ChangelogSlice, map: BlockStateMap): BufferedImage {
|
||||||
val start = slice.relativeChangeRange.start
|
val start = slice.relativeChangeRange.start
|
||||||
val end = slice.relativeChangeRange.endInclusive
|
val end = slice.relativeChangeRange.endInclusive
|
||||||
|
|
||||||
val playersToUniquePositions = transaction(db) {
|
val playerSparseMap = BlockCoordinateSparseMap<MutableList<UUID>>()
|
||||||
|
val allPlayerIds = HashSet<UUID>()
|
||||||
|
transaction(db) {
|
||||||
PlayerPositionTable.select {
|
PlayerPositionTable.select {
|
||||||
(PlayerPositionTable.time greater start) and
|
(PlayerPositionTable.time greater start) and
|
||||||
(PlayerPositionTable.time lessEq end)
|
(PlayerPositionTable.time lessEq end)
|
||||||
}.map {
|
}.forEach {
|
||||||
val x = it[PlayerPositionTable.x].toLong()
|
val x = it[PlayerPositionTable.x].toLong()
|
||||||
val y = it[PlayerPositionTable.y].toLong()
|
val y = it[PlayerPositionTable.y].toLong()
|
||||||
val z = it[PlayerPositionTable.z].toLong()
|
val z = it[PlayerPositionTable.z].toLong()
|
||||||
val coordinate = expanse.offset.applyAsOffset(BlockCoordinate(x, y, z))
|
val coordinate = expanse.offset.applyAsOffset(BlockCoordinate(x, y, z))
|
||||||
it[PlayerPositionTable.player] to coordinate
|
val player = it[PlayerPositionTable.player]
|
||||||
}.distinct()
|
playerSparseMap.createOrModify(
|
||||||
|
coordinate,
|
||||||
|
create = { mutableListOf(player) },
|
||||||
|
modify = { players -> players.add(player) })
|
||||||
|
allPlayerIds.add(player)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val colorOfPlayers = playersToUniquePositions.map { it.first }
|
val colorOfPlayers = allPlayerIds.associateWith { colorKey.map(it.toString()) }
|
||||||
.distinct()
|
|
||||||
.associateWith { colorKey.map(it.toString()) }
|
|
||||||
|
|
||||||
return buildPixelQuadImage(expanse) { g, x, z ->
|
return buildPixelQuadImage(expanse) { g, x, z ->
|
||||||
val players = playersToUniquePositions.filter { it.second.x == x && it.second.z == z }.map { it.first }.distinct()
|
val players = playerSparseMap.getVerticalSection(x, z)?.flatMap { it.value }?.distinct()
|
||||||
if (players.isNotEmpty()) {
|
if (players != null) {
|
||||||
setPixelQuad(g, x, z, colorOfPlayers[players.first()]!!)
|
setPixelQuad(g, x, z, colorOfPlayers[players.first()]!!)
|
||||||
} else {
|
} else {
|
||||||
setPixelQuad(g, x, z, Color.white)
|
setPixelQuad(g, x, z, Color.white)
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package cloud.kubelet.foundation.gjallarhorn.state
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
open class BlockCoordinateSparseMap<T> {
|
||||||
|
val blocks = TreeMap<Long, TreeMap<Long, TreeMap<Long, T>>>()
|
||||||
|
|
||||||
|
fun get(position: BlockCoordinate): T? = blocks[position.x]?.get(position.z)?.get(position.z)
|
||||||
|
fun getVerticalSection(x: Long, z: Long): Map<Long, T>? = blocks[x]?.get(z)
|
||||||
|
fun getXSection(x: Long): Map<Long, Map<Long, T>>? = blocks[x]
|
||||||
|
|
||||||
|
fun put(position: BlockCoordinate, value: T) {
|
||||||
|
blocks.getOrPut(position.x) {
|
||||||
|
TreeMap()
|
||||||
|
}.getOrPut(position.z) {
|
||||||
|
TreeMap()
|
||||||
|
}[position.y] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createOrModify(position: BlockCoordinate, create: () -> T, modify: (T) -> Unit) {
|
||||||
|
val existing = get(position)
|
||||||
|
if (existing == null) {
|
||||||
|
put(position, create())
|
||||||
|
} else {
|
||||||
|
modify(existing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,8 +39,8 @@ class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOn
|
|||||||
fun isEmpty() = blocks.isEmpty()
|
fun isEmpty() = blocks.isEmpty()
|
||||||
fun isNotEmpty() = !isEmpty()
|
fun isNotEmpty() = !isEmpty()
|
||||||
|
|
||||||
fun buildBlockMap(offset: BlockCoordinate = BlockCoordinate.zero): BlockMap {
|
fun buildBlockMap(offset: BlockCoordinate = BlockCoordinate.zero): BlockStateMap {
|
||||||
val map = BlockMap()
|
val map = BlockStateMap()
|
||||||
blocks.forEach { (position, state) ->
|
blocks.forEach { (position, state) ->
|
||||||
val realPosition = offset.applyAsOffset(position)
|
val realPosition = offset.applyAsOffset(position)
|
||||||
map.put(realPosition, state)
|
map.put(realPosition, state)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package cloud.kubelet.foundation.gjallarhorn.state
|
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class BlockMap {
|
|
||||||
val blocks = TreeMap<Long, TreeMap<Long, TreeMap<Long, BlockState>>>()
|
|
||||||
|
|
||||||
fun put(position: BlockCoordinate, state: BlockState) {
|
|
||||||
blocks.getOrPut(position.x) {
|
|
||||||
TreeMap()
|
|
||||||
}.getOrPut(position.z) {
|
|
||||||
TreeMap()
|
|
||||||
}[position.y] = state
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,9 @@ package cloud.kubelet.foundation.gjallarhorn.state
|
|||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.render.BlockMapRenderer
|
import cloud.kubelet.foundation.gjallarhorn.render.BlockMapRenderer
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor
|
||||||
|
|
||||||
class BlockMapRenderPool<T>(
|
class BlockMapRenderPool<T>(
|
||||||
val changelog: BlockChangelog,
|
val changelog: BlockChangelog,
|
||||||
@ -51,7 +53,11 @@ class BlockMapRenderPool<T>(
|
|||||||
delegate.onAllPlaybackComplete(this, trackers)
|
delegate.onAllPlaybackComplete(this, trackers)
|
||||||
|
|
||||||
for (future in renderJobFutures.values) {
|
for (future in renderJobFutures.values) {
|
||||||
future.get()
|
try {
|
||||||
|
future.get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.error("Failed to render slice.", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package cloud.kubelet.foundation.gjallarhorn.state
|
||||||
|
|
||||||
|
class BlockStateMap : BlockCoordinateSparseMap<BlockState>()
|
Reference in New Issue
Block a user