mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-02 13:10:55 +00:00
Heimdall: Player Names Table, Gjallarhorn: Block State Image Rendering
This commit is contained in:
parent
9386dc7c56
commit
e681df1e65
@ -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
|
package cloud.kubelet.foundation.gjallarhorn
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
class BlockStateTracker {
|
class BlockStateTracker {
|
||||||
val blocks = HashMap<BlockPosition, BlockState>()
|
val blocks = HashMap<BlockPosition, BlockState>()
|
||||||
@ -14,21 +14,22 @@ class BlockStateTracker {
|
|||||||
blocks.remove(position)
|
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 xOffset = if (x < 0) x.absoluteValue else 0
|
||||||
val x: Long,
|
val yOffset = if (y < 0) y.absoluteValue else 0
|
||||||
val y: Long,
|
val zOffset = if (z < 0) z.absoluteValue else 0
|
||||||
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
|
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
|
package cloud.kubelet.foundation.gjallarhorn.commands
|
||||||
|
|
||||||
import cloud.kubelet.foundation.gjallarhorn.BlockStateTracker
|
import cloud.kubelet.foundation.gjallarhorn.*
|
||||||
import cloud.kubelet.foundation.gjallarhorn.compose
|
|
||||||
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.flag
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import java.io.File
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-log") {
|
class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-log") {
|
||||||
private val db by requireObject<Database>()
|
private val db by requireObject<Database>()
|
||||||
private val timeAsString by option("--time", help = "Replay Time")
|
private val timeAsString by option("--time", help = "Replay Time")
|
||||||
|
private val render by option("--render", help = "Enable Render Mode").flag()
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val filter = compose(
|
val filter = compose(
|
||||||
@ -32,18 +35,25 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo
|
|||||||
val z = row[BlockChangeView.z]
|
val z = row[BlockChangeView.z]
|
||||||
val block = row[BlockChangeView.block]
|
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) {
|
if (changeIsBreak) {
|
||||||
tracker.delete(location)
|
tracker.delete(location)
|
||||||
} else {
|
} else {
|
||||||
tracker.place(location, BlockStateTracker.BlockState(block))
|
tracker.place(location, BlockState(block))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println("x,y,z,block")
|
if (render) {
|
||||||
for ((position, block) in tracker.blocks) {
|
val image = BlockStateImage()
|
||||||
println("${position.x},${position.y},${position.z},${block.type}")
|
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())
|
||||||
|
}
|
@ -137,7 +137,14 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
fun onEntityDeath(event: EntityDeathEvent) {
|
fun onEntityDeath(event: EntityDeathEvent) {
|
||||||
val killer = event.entity.killer ?: return
|
val killer = event.entity.killer ?: return
|
||||||
buffer.push(EntityKill(killer.uniqueId, killer.location, event.entity.uniqueId, event.entityType.key.toString()))
|
buffer.push(
|
||||||
|
EntityKill(
|
||||||
|
killer.uniqueId,
|
||||||
|
killer.location,
|
||||||
|
event.entity.uniqueId,
|
||||||
|
event.entityType.key.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
@ -145,7 +152,12 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
|
|||||||
val endTime = Instant.now()
|
val endTime = Instant.now()
|
||||||
for (playerId in playerJoinTimes.keys().toList()) {
|
for (playerId in playerJoinTimes.keys().toList()) {
|
||||||
val startTime = playerJoinTimes.remove(playerId) ?: continue
|
val startTime = playerJoinTimes.remove(playerId) ?: continue
|
||||||
buffer.push(PlayerSession(playerId, server.getPlayer(playerId)?.name ?: "__unknown__", startTime, endTime))
|
buffer.push(PlayerSession(
|
||||||
|
playerId,
|
||||||
|
server.getPlayer(playerId)?.name ?: "__unknown__",
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
))
|
||||||
}
|
}
|
||||||
bufferFlushThread.flush()
|
bufferFlushThread.flush()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
--
|
|
||||||
create extension if not exists "uuid-ossp";
|
create extension if not exists "uuid-ossp";
|
||||||
--
|
--
|
||||||
create extension if not exists timescaledb;
|
create extension if not exists timescaledb;
|
||||||
@ -119,4 +118,22 @@ create table if not exists heimdall.entity_kills (
|
|||||||
--
|
--
|
||||||
select create_hypertable('heimdall.entity_kills', 'time', 'player', 4, if_not_exists => TRUE);
|
select create_hypertable('heimdall.entity_kills', 'time', 'player', 4, if_not_exists => TRUE);
|
||||||
--
|
--
|
||||||
create or replace view heimdall.block_changes as select true as break, * from heimdall.block_breaks union all select false as break, * from heimdall.block_places;
|
create or replace view heimdall.block_changes as
|
||||||
|
select true as break, *
|
||||||
|
from heimdall.block_breaks
|
||||||
|
union all
|
||||||
|
select false as break, * from heimdall.block_places;
|
||||||
|
--
|
||||||
|
create or replace view heimdall.player_names as
|
||||||
|
with unique_player_ids as (
|
||||||
|
select distinct player
|
||||||
|
from heimdall.player_sessions
|
||||||
|
)
|
||||||
|
select player, (
|
||||||
|
select name
|
||||||
|
from heimdall.player_sessions
|
||||||
|
where player = unique_player_ids.player
|
||||||
|
order by "end" desc
|
||||||
|
limit 1
|
||||||
|
) as name
|
||||||
|
from unique_player_ids;
|
||||||
|
Loading…
Reference in New Issue
Block a user