mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +00:00 
			
		
		
		
	Heimdall: Player Names Table, Gjallarhorn: Block State Image Rendering
This commit is contained in:
		@ -0,0 +1,17 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn
 | 
			
		||||
 | 
			
		||||
data class BlockOffset(
 | 
			
		||||
  val x: Long,
 | 
			
		||||
  val y: Long,
 | 
			
		||||
  val z: Long
 | 
			
		||||
) {
 | 
			
		||||
  fun apply(position: BlockPosition) = position.copy(
 | 
			
		||||
    x = position.x + x,
 | 
			
		||||
    y = position.y + y,
 | 
			
		||||
    z = position.z + z
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    val none = BlockOffset(0, 0, 0)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn
 | 
			
		||||
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
data class BlockPosition(
 | 
			
		||||
  val x: Long,
 | 
			
		||||
  val y: Long,
 | 
			
		||||
  val z: Long
 | 
			
		||||
) {
 | 
			
		||||
  override fun equals(other: Any?): Boolean {
 | 
			
		||||
    if (other !is BlockPosition) {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return other.x == x && other.y == y && other.z == z
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun hashCode(): Int = Objects.hash(x, y, z)
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,6 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class BlockState(val type: String)
 | 
			
		||||
@ -0,0 +1,51 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.gjallarhorn.util.RandomColorKey
 | 
			
		||||
import java.awt.Color
 | 
			
		||||
import java.awt.image.BufferedImage
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class BlockStateImage {
 | 
			
		||||
  val blocks = TreeMap<Long, TreeMap<Long, TreeMap<Long, BlockState>>>()
 | 
			
		||||
 | 
			
		||||
  fun put(position: BlockPosition, state: BlockState) {
 | 
			
		||||
    blocks.getOrPut(position.x) {
 | 
			
		||||
      TreeMap()
 | 
			
		||||
    }.getOrPut(position.z) {
 | 
			
		||||
      TreeMap()
 | 
			
		||||
    }[position.y] = state
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun buildBufferedImage(): BufferedImage {
 | 
			
		||||
    val colorKey = RandomColorKey()
 | 
			
		||||
    val xMax = blocks.keys.maxOf { it }
 | 
			
		||||
    val zMax = blocks.maxOf { it.value.maxOf { it.key } }
 | 
			
		||||
 | 
			
		||||
    val bufferedImage = BufferedImage(xMax.toInt() * 2, zMax.toInt() * 2, BufferedImage.TYPE_4BYTE_ABGR)
 | 
			
		||||
    for (x in 0 until xMax) {
 | 
			
		||||
      for (z in 0 until zMax) {
 | 
			
		||||
        fun set(rgb: Int) {
 | 
			
		||||
          bufferedImage.setRGB(x.toInt() * 2, z.toInt() * 2, rgb)
 | 
			
		||||
          bufferedImage.setRGB((x.toInt() * 2) + 1, z.toInt() * 2, rgb)
 | 
			
		||||
          bufferedImage.setRGB(x.toInt() * 2, (z.toInt() * 2) + 1, rgb)
 | 
			
		||||
          bufferedImage.setRGB((x.toInt() * 2) + 1, (z.toInt() * 2) + 1, rgb)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val maybeYBlocks = blocks[x]?.get(z)
 | 
			
		||||
        if (maybeYBlocks == null) {
 | 
			
		||||
          set(Color.white.rgb)
 | 
			
		||||
          continue
 | 
			
		||||
        }
 | 
			
		||||
        val maxBlockState = maybeYBlocks.maxByOrNull { it.key }?.value
 | 
			
		||||
        if (maxBlockState == null) {
 | 
			
		||||
          set(Color.white.rgb)
 | 
			
		||||
          continue
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val color = colorKey.map(maxBlockState.type)
 | 
			
		||||
        set(color.rgb)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return bufferedImage
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn
 | 
			
		||||
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlin.collections.HashMap
 | 
			
		||||
import kotlin.math.absoluteValue
 | 
			
		||||
 | 
			
		||||
class BlockStateTracker {
 | 
			
		||||
  val blocks = HashMap<BlockPosition, BlockState>()
 | 
			
		||||
@ -14,21 +14,22 @@ class BlockStateTracker {
 | 
			
		||||
    blocks.remove(position)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data class BlockState(val type: String)
 | 
			
		||||
  fun calculateZeroBlockOffset(): BlockOffset {
 | 
			
		||||
    val x = blocks.keys.minOf { it.x }
 | 
			
		||||
    val y = blocks.keys.minOf { it.y }
 | 
			
		||||
    val z = blocks.keys.minOf { it.z }
 | 
			
		||||
 | 
			
		||||
  data class BlockPosition(
 | 
			
		||||
    val x: Long,
 | 
			
		||||
    val y: Long,
 | 
			
		||||
    val z: Long
 | 
			
		||||
  ) {
 | 
			
		||||
    override fun equals(other: Any?): Boolean {
 | 
			
		||||
      if (other !is BlockPosition) {
 | 
			
		||||
        return false
 | 
			
		||||
      }
 | 
			
		||||
    val xOffset = if (x < 0) x.absoluteValue else 0
 | 
			
		||||
    val yOffset = if (y < 0) y.absoluteValue else 0
 | 
			
		||||
    val zOffset = if (z < 0) z.absoluteValue else 0
 | 
			
		||||
 | 
			
		||||
      return other.x == x && other.y == y && other.z == z
 | 
			
		||||
    return BlockOffset(xOffset, yOffset, zOffset)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun populate(image: BlockStateImage, offset: BlockOffset = BlockOffset.none) {
 | 
			
		||||
    blocks.forEach { (position, state) ->
 | 
			
		||||
      val realPosition = offset.apply(position)
 | 
			
		||||
      image.put(realPosition, state)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun hashCode(): Int = Objects.hash(x, y, z)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,24 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn.commands
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.gjallarhorn.BlockStateTracker
 | 
			
		||||
import cloud.kubelet.foundation.gjallarhorn.compose
 | 
			
		||||
import cloud.kubelet.foundation.gjallarhorn.*
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.view.BlockChangeView
 | 
			
		||||
import com.github.ajalt.clikt.core.CliktCommand
 | 
			
		||||
import com.github.ajalt.clikt.core.requireObject
 | 
			
		||||
import com.github.ajalt.clikt.parameters.options.flag
 | 
			
		||||
import com.github.ajalt.clikt.parameters.options.option
 | 
			
		||||
import org.jetbrains.exposed.sql.Database
 | 
			
		||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq
 | 
			
		||||
import org.jetbrains.exposed.sql.and
 | 
			
		||||
import org.jetbrains.exposed.sql.select
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import javax.imageio.ImageIO
 | 
			
		||||
 | 
			
		||||
class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-log") {
 | 
			
		||||
  private val db by requireObject<Database>()
 | 
			
		||||
  private val timeAsString by option("--time", help = "Replay Time")
 | 
			
		||||
  private val render by option("--render", help = "Enable Render Mode").flag()
 | 
			
		||||
 | 
			
		||||
  override fun run() {
 | 
			
		||||
    val filter = compose(
 | 
			
		||||
@ -32,18 +35,25 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo
 | 
			
		||||
        val z = row[BlockChangeView.z]
 | 
			
		||||
        val block = row[BlockChangeView.block]
 | 
			
		||||
 | 
			
		||||
        val location = BlockStateTracker.BlockPosition(x.toLong(), y.toLong(), z.toLong())
 | 
			
		||||
        val location = BlockPosition(x.toLong(), y.toLong(), z.toLong())
 | 
			
		||||
        if (changeIsBreak) {
 | 
			
		||||
          tracker.delete(location)
 | 
			
		||||
        } else {
 | 
			
		||||
          tracker.place(location, BlockStateTracker.BlockState(block))
 | 
			
		||||
          tracker.place(location, BlockState(block))
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println("x,y,z,block")
 | 
			
		||||
    for ((position, block) in tracker.blocks) {
 | 
			
		||||
      println("${position.x},${position.y},${position.z},${block.type}")
 | 
			
		||||
    if (render) {
 | 
			
		||||
      val image = BlockStateImage()
 | 
			
		||||
      tracker.populate(image, offset = tracker.calculateZeroBlockOffset())
 | 
			
		||||
      val bufferedImage = image.buildBufferedImage()
 | 
			
		||||
      ImageIO.write(bufferedImage, "png", File("top-down.png"))
 | 
			
		||||
    } else {
 | 
			
		||||
      println("x,y,z,block")
 | 
			
		||||
      for ((position, block) in tracker.blocks) {
 | 
			
		||||
        println("${position.x},${position.y},${position.z},${block.type}")
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
package cloud.kubelet.foundation.gjallarhorn.util
 | 
			
		||||
 | 
			
		||||
import java.awt.Color
 | 
			
		||||
 | 
			
		||||
class RandomColorKey {
 | 
			
		||||
  private val colors = mutableMapOf<String, Color>()
 | 
			
		||||
 | 
			
		||||
  fun map(key: String) = colors.getOrPut(key) { findUniqueColor() }
 | 
			
		||||
 | 
			
		||||
  private fun findUniqueColor(): Color {
 | 
			
		||||
    var random = randomColor()
 | 
			
		||||
    while (colors.values.any { it.rgb == random.rgb }) {
 | 
			
		||||
      random = randomColor()
 | 
			
		||||
    }
 | 
			
		||||
    return random
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun randomColor() = Color((Math.random() * 0x1000000).toInt())
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user