mirror of
https://github.com/GayPizzaSpecifications/foundation.git
synced 2025-08-03 13:31:32 +00:00
Remove heimdall and tool project.
This commit is contained in:
@ -1,7 +0,0 @@
|
||||
dependencies {
|
||||
api("org.postgresql:postgresql:42.3.1")
|
||||
api("org.jetbrains.exposed:exposed-jdbc:0.36.2")
|
||||
api("org.jetbrains.exposed:exposed-java-time:0.36.2")
|
||||
api("com.zaxxer:HikariCP:5.0.0")
|
||||
compileOnly(project(":foundation-core"))
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall
|
||||
|
||||
fun String.sqlSplitStatements(): List<String> {
|
||||
val statements = mutableListOf<String>()
|
||||
val buffer = StringBuilder()
|
||||
fun flush() {
|
||||
val trimmed = buffer.toString().trim()
|
||||
if (trimmed.isNotEmpty()) {
|
||||
statements.add(trimmed)
|
||||
}
|
||||
}
|
||||
for (line in lines()) {
|
||||
if (line.trim() == "--") {
|
||||
flush()
|
||||
} else {
|
||||
buffer.append(line).append("\n")
|
||||
}
|
||||
}
|
||||
flush()
|
||||
return statements
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall
|
||||
|
||||
import com.charleskorn.kaml.Yaml
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import gay.pizza.foundation.core.FoundationCorePlugin
|
||||
import gay.pizza.foundation.core.Util
|
||||
import gay.pizza.foundation.heimdall.buffer.BufferFlushThread
|
||||
import gay.pizza.foundation.heimdall.buffer.EventBuffer
|
||||
import gay.pizza.foundation.heimdall.event.BlockBreak
|
||||
import gay.pizza.foundation.heimdall.event.BlockPlace
|
||||
import gay.pizza.foundation.heimdall.event.EntityKill
|
||||
import gay.pizza.foundation.heimdall.event.PlayerAdvancement
|
||||
import gay.pizza.foundation.heimdall.event.PlayerDeath
|
||||
import gay.pizza.foundation.heimdall.event.PlayerPosition
|
||||
import gay.pizza.foundation.heimdall.event.PlayerSession
|
||||
import gay.pizza.foundation.heimdall.event.WorldChange
|
||||
import gay.pizza.foundation.heimdall.export.ExportChunksCommand
|
||||
import gay.pizza.foundation.heimdall.model.HeimdallConfig
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.block.BlockBreakEvent
|
||||
import org.bukkit.event.block.BlockPlaceEvent
|
||||
import org.bukkit.event.entity.EntityDeathEvent
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.bukkit.event.player.PlayerAdvancementDoneEvent
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.player.PlayerMoveEvent
|
||||
import org.bukkit.event.player.PlayerQuitEvent
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.postgresql.Driver
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.io.path.inputStream
|
||||
|
||||
class FoundationHeimdallPlugin : JavaPlugin(), Listener {
|
||||
private lateinit var config: HeimdallConfig
|
||||
private lateinit var pool: HikariDataSource
|
||||
internal var db: Database? = null
|
||||
|
||||
private val buffer = EventBuffer()
|
||||
private val bufferFlushThread = BufferFlushThread(this, buffer)
|
||||
|
||||
private val playerJoinTimes = ConcurrentHashMap<UUID, Instant>()
|
||||
|
||||
private val legacyComponentSerializer = LegacyComponentSerializer.builder().build()
|
||||
|
||||
override fun onEnable() {
|
||||
val exportChunksCommand = getCommand("export_all_chunks") ?: throw Exception("Failed to get export_all_chunks command")
|
||||
exportChunksCommand.setExecutor(ExportChunksCommand(this))
|
||||
|
||||
val foundation = server.pluginManager.getPlugin("Foundation") as FoundationCorePlugin
|
||||
|
||||
val configPath = Util.copyDefaultConfig<FoundationHeimdallPlugin>(
|
||||
slF4JLogger,
|
||||
foundation.pluginDataPath,
|
||||
"heimdall.yaml"
|
||||
)
|
||||
config = Yaml.default.decodeFromStream(HeimdallConfig.serializer(), configPath.inputStream())
|
||||
if (!config.enabled) {
|
||||
slF4JLogger.info("Heimdall is not enabled.")
|
||||
return
|
||||
}
|
||||
slF4JLogger.info("Heimdall is enabled.")
|
||||
if (!Driver.isRegistered()) {
|
||||
Driver.register()
|
||||
}
|
||||
pool = HikariDataSource(HikariConfig().apply {
|
||||
jdbcUrl = config.db.url
|
||||
username = config.db.username
|
||||
password = config.db.password
|
||||
schema = "heimdall"
|
||||
maximumPoolSize = 10
|
||||
idleTimeout = Duration.ofMinutes(5).toMillis()
|
||||
maxLifetime = Duration.ofMinutes(10).toMillis()
|
||||
})
|
||||
val initMigrationContent = FoundationHeimdallPlugin::class.java.getResourceAsStream(
|
||||
"/init.sql"
|
||||
)?.readAllBytes()?.decodeToString() ?: throw RuntimeException("Unable to find Heimdall init.sql")
|
||||
|
||||
val statements = initMigrationContent.sqlSplitStatements()
|
||||
|
||||
pool.connection.use { conn ->
|
||||
conn.autoCommit = false
|
||||
try {
|
||||
for (statementAsString in statements) {
|
||||
conn.prepareStatement(statementAsString).use {
|
||||
it.execute()
|
||||
}
|
||||
}
|
||||
conn.commit()
|
||||
} catch (e: Exception) {
|
||||
conn.rollback()
|
||||
throw e
|
||||
} finally {
|
||||
conn.autoCommit = true
|
||||
}
|
||||
}
|
||||
|
||||
db = Database.connect(pool)
|
||||
server.pluginManager.registerEvents(this, this)
|
||||
bufferFlushThread.start()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerMove(event: PlayerMoveEvent) = buffer.push(PlayerPosition(event))
|
||||
|
||||
@EventHandler
|
||||
fun onBlockBroken(event: BlockPlaceEvent) = buffer.push(BlockPlace(event))
|
||||
|
||||
@EventHandler
|
||||
fun onBlockBroken(event: BlockBreakEvent) = buffer.push(BlockBreak(event))
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerJoin(event: PlayerJoinEvent) {
|
||||
playerJoinTimes[event.player.uniqueId] = Instant.now()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerQuit(event: PlayerQuitEvent) {
|
||||
val startTime = playerJoinTimes.remove(event.player.uniqueId) ?: return
|
||||
val endTime = Instant.now()
|
||||
buffer.push(PlayerSession(event.player.uniqueId, event.player.name, startTime, endTime))
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDeath(event: PlayerDeathEvent) {
|
||||
val deathMessage = event.deathMessage()
|
||||
val deathMessageString = if (deathMessage != null) {
|
||||
legacyComponentSerializer.serialize(deathMessage)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
buffer.push(PlayerDeath(event, deathMessageString))
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerAdvancementDone(event: PlayerAdvancementDoneEvent) = buffer.push(PlayerAdvancement(event))
|
||||
|
||||
@EventHandler
|
||||
fun onWorldLoad(event: PlayerChangedWorldEvent) = buffer.push(
|
||||
WorldChange(
|
||||
event.player.uniqueId,
|
||||
event.from.uid,
|
||||
event.from.name,
|
||||
event.player.world.uid,
|
||||
event.player.world.name
|
||||
)
|
||||
)
|
||||
|
||||
@EventHandler
|
||||
fun onEntityDeath(event: EntityDeathEvent) {
|
||||
val killer = event.entity.killer ?: return
|
||||
buffer.push(
|
||||
EntityKill(
|
||||
killer.uniqueId,
|
||||
killer.location,
|
||||
event.entity.uniqueId,
|
||||
event.entityType.key.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
bufferFlushThread.stop()
|
||||
val endTime = Instant.now()
|
||||
for (playerId in playerJoinTimes.keys().toList()) {
|
||||
val startTime = playerJoinTimes.remove(playerId) ?: continue
|
||||
buffer.push(PlayerSession(
|
||||
playerId,
|
||||
server.getPlayer(playerId)?.name ?: "__unknown__",
|
||||
startTime,
|
||||
endTime
|
||||
))
|
||||
}
|
||||
bufferFlushThread.flush()
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.buffer
|
||||
|
||||
import gay.pizza.foundation.heimdall.FoundationHeimdallPlugin
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventBuffer) {
|
||||
private val running = AtomicBoolean(false)
|
||||
private var thread: Thread? = null
|
||||
|
||||
fun start() {
|
||||
running.set(true)
|
||||
val thread = Thread {
|
||||
plugin.slF4JLogger.info("Buffer Flusher Started")
|
||||
while (running.get()) {
|
||||
flush()
|
||||
Thread.sleep(5000)
|
||||
}
|
||||
plugin.slF4JLogger.info("Buffer Flusher Stopped")
|
||||
}
|
||||
thread.name = "Heimdall Buffer Flush"
|
||||
thread.isDaemon = false
|
||||
thread.start()
|
||||
this.thread = thread
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
running.set(false)
|
||||
thread?.join()
|
||||
}
|
||||
|
||||
fun flush() {
|
||||
try {
|
||||
val db = plugin.db
|
||||
if (db == null) {
|
||||
buffer.clear()
|
||||
return
|
||||
}
|
||||
transaction(plugin.db) {
|
||||
val count = buffer.flush(this)
|
||||
if (count > 0) {
|
||||
plugin.slF4JLogger.debug("Flushed $count Events")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
plugin.slF4JLogger.warn("Failed to flush buffer.", e)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.buffer
|
||||
|
||||
import gay.pizza.foundation.heimdall.event.HeimdallEvent
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
|
||||
class EventBuffer {
|
||||
private var events = mutableListOf<HeimdallEvent>()
|
||||
|
||||
fun flush(transaction: Transaction): Long {
|
||||
val referenceOfEvents = events
|
||||
this.events = mutableListOf()
|
||||
var count = 0L
|
||||
while (referenceOfEvents.isNotEmpty()) {
|
||||
val event = referenceOfEvents.removeAt(0)
|
||||
event.store(transaction)
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun push(event: HeimdallEvent) {
|
||||
events.add(event)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
events = mutableListOf()
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.BlockBreakTable
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.event.block.BlockBreakEvent
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class BlockBreak(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val location: Location,
|
||||
val material: Material,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
constructor(event: BlockBreakEvent) : this(event.player.uniqueId, event.block.location, event.block.type)
|
||||
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
BlockBreakTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[world] = location.world.uid
|
||||
it[x] = location.x
|
||||
it[y] = location.y
|
||||
it[z] = location.z
|
||||
it[pitch] = location.pitch.toDouble()
|
||||
it[yaw] = location.yaw.toDouble()
|
||||
it[block] = material.key.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.BlockPlaceTable
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.event.block.BlockPlaceEvent
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class BlockPlace(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val location: Location,
|
||||
val material: Material,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
constructor(event: BlockPlaceEvent) : this(event.player.uniqueId, event.block.location, event.block.type)
|
||||
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
BlockPlaceTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[world] = location.world.uid
|
||||
it[x] = location.x
|
||||
it[y] = location.y
|
||||
it[z] = location.z
|
||||
it[pitch] = location.pitch.toDouble()
|
||||
it[yaw] = location.yaw.toDouble()
|
||||
it[block] = material.key.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.EntityKillTable
|
||||
import org.bukkit.Location
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class EntityKill(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val location: Location,
|
||||
val entityUniqueIdentity: UUID,
|
||||
val entityTypeName: String,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
EntityKillTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[world] = location.world.uid
|
||||
it[x] = location.x
|
||||
it[y] = location.y
|
||||
it[z] = location.z
|
||||
it[pitch] = location.pitch.toDouble()
|
||||
it[yaw] = location.yaw.toDouble()
|
||||
it[entity] = entityUniqueIdentity
|
||||
it[entityType] = entityTypeName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
|
||||
abstract class HeimdallEvent {
|
||||
abstract fun store(transaction: Transaction)
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.PlayerAdvancementTable
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.advancement.Advancement
|
||||
import org.bukkit.event.player.PlayerAdvancementDoneEvent
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class PlayerAdvancement(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val location: Location,
|
||||
val advancement: Advancement,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
constructor(event: PlayerAdvancementDoneEvent) : this(event.player.uniqueId, event.player.location, event.advancement)
|
||||
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
PlayerAdvancementTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[world] = location.world.uid
|
||||
it[x] = location.x
|
||||
it[y] = location.y
|
||||
it[z] = location.z
|
||||
it[pitch] = location.pitch.toDouble()
|
||||
it[yaw] = location.yaw.toDouble()
|
||||
it[advancement] = this@PlayerAdvancement.advancement.key.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.PlayerDeathTable
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class PlayerDeath(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val location: Location,
|
||||
val experienceLevel: Float,
|
||||
val deathMessage: String?,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
constructor(event: PlayerDeathEvent, deathMessage: String? = null) : this(
|
||||
event.player.uniqueId,
|
||||
event.player.location,
|
||||
event.player.exp,
|
||||
deathMessage
|
||||
)
|
||||
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
PlayerDeathTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[world] = location.world.uid
|
||||
it[x] = location.x
|
||||
it[y] = location.y
|
||||
it[z] = location.z
|
||||
it[pitch] = location.pitch.toDouble()
|
||||
it[yaw] = location.yaw.toDouble()
|
||||
it[experience] = experienceLevel.toDouble()
|
||||
it[message] = deathMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.PlayerPositionTable
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.event.player.PlayerMoveEvent
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class PlayerPosition(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val location: Location,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
constructor(event: PlayerMoveEvent) : this(event.player.uniqueId, event.to)
|
||||
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
PlayerPositionTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[world] = location.world.uid
|
||||
it[x] = location.x
|
||||
it[y] = location.y
|
||||
it[z] = location.z
|
||||
it[pitch] = location.pitch.toDouble()
|
||||
it[yaw] = location.yaw.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.PlayerSessionTable
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class PlayerSession(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val playerName: String,
|
||||
val startTimeInstant: Instant,
|
||||
val endTimeInstant: Instant
|
||||
) : HeimdallEvent() {
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
PlayerSessionTable.insert {
|
||||
it[id] = UUID.randomUUID()
|
||||
it[player] = playerUniqueIdentity
|
||||
it[name] = playerName
|
||||
it[startTime] = startTimeInstant
|
||||
it[endTime] = endTimeInstant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.event
|
||||
|
||||
import gay.pizza.foundation.heimdall.table.WorldChangeTable
|
||||
import org.jetbrains.exposed.sql.Transaction
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
class WorldChange(
|
||||
val playerUniqueIdentity: UUID,
|
||||
val fromWorldId: UUID,
|
||||
val fromWorldActualName: String,
|
||||
val toWorldId: UUID,
|
||||
val toWorldActualName: String,
|
||||
val timestamp: Instant = Instant.now()
|
||||
) : HeimdallEvent() {
|
||||
override fun store(transaction: Transaction) {
|
||||
transaction.apply {
|
||||
WorldChangeTable.insert {
|
||||
it[time] = timestamp
|
||||
it[player] = playerUniqueIdentity
|
||||
it[fromWorld] = fromWorldId
|
||||
it[fromWorldName] = fromWorldActualName
|
||||
it[toWorld] = toWorldId
|
||||
it[toWorldName] = toWorldActualName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.export
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.encodeToStream
|
||||
import org.bukkit.Chunk
|
||||
import org.bukkit.ChunkSnapshot
|
||||
import org.bukkit.Server
|
||||
import org.bukkit.World
|
||||
import org.bukkit.plugin.Plugin
|
||||
import java.io.File
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
class ChunkExporter(private val plugin: Plugin, private val server: Server, val world: World) {
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
fun exportLoadedChunksAsync() {
|
||||
exportChunkListAsync(world.loadedChunks.toList())
|
||||
}
|
||||
|
||||
private fun exportChunkListAsync(chunks: List<Chunk>) {
|
||||
plugin.slF4JLogger.info("Exporting ${chunks.size} Chunks")
|
||||
val snapshots = chunks.map { it.chunkSnapshot }
|
||||
Thread {
|
||||
for (snapshot in snapshots) {
|
||||
exportChunkSnapshot(snapshot)
|
||||
}
|
||||
plugin.slF4JLogger.info("Exported ${chunks.size} Chunks")
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun exportChunkSnapshot(snapshot: ChunkSnapshot) {
|
||||
val sections = mutableListOf<ExportedChunkSection>()
|
||||
val yRange = world.minHeight until world.maxHeight
|
||||
val chunkRange = 0..15
|
||||
for (x in chunkRange) {
|
||||
for (z in chunkRange) {
|
||||
sections.add(exportChunkSection(snapshot, yRange, x, z))
|
||||
}
|
||||
}
|
||||
|
||||
val exported = ExportedChunk(snapshot.x, snapshot.z, sections)
|
||||
saveChunkSnapshot(snapshot, exported)
|
||||
}
|
||||
|
||||
private fun saveChunkSnapshot(snapshot: ChunkSnapshot, chunk: ExportedChunk) {
|
||||
val file = File("exported_chunks/${snapshot.worldName}_chunk_${snapshot.x}_${snapshot.z}.json.gz")
|
||||
if (!file.parentFile.exists()) {
|
||||
file.parentFile.mkdirs()
|
||||
}
|
||||
|
||||
val fileOutputStream = file.outputStream()
|
||||
val gzipOutputStream = GZIPOutputStream(fileOutputStream)
|
||||
json.encodeToStream(ExportedChunk.serializer(), chunk, gzipOutputStream)
|
||||
gzipOutputStream.close()
|
||||
}
|
||||
|
||||
private fun exportChunkSection(snapshot: ChunkSnapshot, yRange: IntRange, x: Int, z: Int): ExportedChunkSection {
|
||||
val blocks = mutableListOf<ExportedBlock>()
|
||||
for (y in yRange) {
|
||||
val blockData = snapshot.getBlockData(x, y, z)
|
||||
val block = ExportedBlock(blockData.material.key.toString())
|
||||
blocks.add(block)
|
||||
}
|
||||
return ExportedChunkSection(x, z, blocks)
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.export
|
||||
|
||||
import org.bukkit.command.Command
|
||||
import org.bukkit.command.CommandExecutor
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.plugin.Plugin
|
||||
|
||||
class ExportChunksCommand(private val plugin: Plugin) : CommandExecutor {
|
||||
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
|
||||
plugin.slF4JLogger.info("Exporting All Chunks")
|
||||
for (world in sender.server.worlds) {
|
||||
val export = ChunkExporter(plugin, sender.server, world)
|
||||
export.exportLoadedChunksAsync()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.export
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ExportedBlock(
|
||||
val type: String
|
||||
)
|
@ -1,10 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.export
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ExportedChunk(
|
||||
val x: Int,
|
||||
val z: Int,
|
||||
val sections: List<ExportedChunkSection>
|
||||
)
|
@ -1,10 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.export
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ExportedChunkSection(
|
||||
val x: Int,
|
||||
val z: Int,
|
||||
val blocks: List<ExportedBlock>
|
||||
)
|
@ -1,16 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class HeimdallConfig(
|
||||
val enabled: Boolean = false,
|
||||
val db: DbConfig
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DbConfig(
|
||||
val url: String,
|
||||
val username: String,
|
||||
val password: String
|
||||
)
|
@ -1,16 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object BlockBreakTable : Table("block_breaks") {
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val world = uuid("world")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
val block = text("block")
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object BlockPlaceTable : Table("block_places") {
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val world = uuid("world")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
val block = text("block")
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object EntityKillTable : Table("entity_kills") {
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val entity = uuid("entity")
|
||||
val world = uuid("world")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
val entityType = text("entity_type")
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object PlayerAdvancementTable : Table("player_advancements") {
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val world = uuid("world")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
val advancement = text("advancement")
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object PlayerDeathTable : Table("player_deaths") {
|
||||
val time = timestamp("time")
|
||||
val world = uuid("world")
|
||||
val player = uuid("player")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
val experience = double("experience")
|
||||
val message = text("message").nullable()
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object PlayerPositionTable : Table("player_positions") {
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val world = uuid("world")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object PlayerSessionTable : Table("player_sessions") {
|
||||
val id = uuid("id")
|
||||
val player = uuid("player")
|
||||
val name = text("name")
|
||||
val startTime = timestamp("start")
|
||||
val endTime = timestamp("end")
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.table
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object WorldChangeTable : Table("world_changes") {
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val fromWorld = uuid("from_world")
|
||||
val toWorld = uuid("to_world")
|
||||
val fromWorldName = text("from_world_name")
|
||||
val toWorldName = text("to_world_name")
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package gay.pizza.foundation.heimdall.view
|
||||
|
||||
import org.jetbrains.exposed.sql.Table
|
||||
import org.jetbrains.exposed.sql.javatime.timestamp
|
||||
|
||||
object BlockChangeView : Table("block_changes") {
|
||||
val isBreak = bool("break")
|
||||
val time = timestamp("time")
|
||||
val player = uuid("player")
|
||||
val world = uuid("world")
|
||||
val x = double("x")
|
||||
val y = double("y")
|
||||
val z = double("z")
|
||||
val pitch = double("pitch")
|
||||
val yaw = double("yaw")
|
||||
val block = text("block")
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# Whether Heimdall should be enabled for tracking events.
|
||||
enabled: false
|
||||
|
||||
# Database connection information.
|
||||
db:
|
||||
# JDBC URL
|
||||
url: "jdbc:postgresql://localhost/foundation"
|
||||
# JDBC Username
|
||||
username: "foundation"
|
||||
# JDBC Password
|
||||
password: "foundation"
|
@ -1,147 +0,0 @@
|
||||
create extension if not exists "uuid-ossp";
|
||||
--
|
||||
create extension if not exists timescaledb;
|
||||
--
|
||||
create schema if not exists heimdall;
|
||||
--
|
||||
create table if not exists heimdall.player_positions (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
world uuid not null,
|
||||
x double precision not null,
|
||||
y double precision not null,
|
||||
z double precision not null,
|
||||
pitch double precision not null,
|
||||
yaw double precision not null,
|
||||
PRIMARY KEY (time, player, world)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.player_positions', 'time', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
alter table heimdall.player_positions set (
|
||||
timescaledb.compress,
|
||||
timescaledb.compress_segmentby = 'player,world',
|
||||
timescaledb.compress_orderby = 'time'
|
||||
);
|
||||
--
|
||||
select add_compression_policy('heimdall.player_positions', interval '3 days', if_not_exists => true);
|
||||
--
|
||||
create table if not exists heimdall.block_breaks (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
world uuid not null,
|
||||
x double precision not null,
|
||||
y double precision not null,
|
||||
z double precision not null,
|
||||
pitch double precision not null,
|
||||
yaw double precision not null,
|
||||
block text not null,
|
||||
PRIMARY KEY (time, player, world)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.block_breaks', 'time', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
create table if not exists heimdall.block_places (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
world uuid not null,
|
||||
x double precision not null,
|
||||
y double precision not null,
|
||||
z double precision not null,
|
||||
pitch double precision not null,
|
||||
yaw double precision not null,
|
||||
block text not null,
|
||||
PRIMARY KEY (time, player, world)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.block_places', 'time', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
create table if not exists heimdall.player_sessions (
|
||||
id uuid not null,
|
||||
player uuid not null,
|
||||
name text not null,
|
||||
"start" timestamp not null,
|
||||
"end" timestamp not null,
|
||||
primary key (id, player, start)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.player_sessions', 'start', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
create table if not exists heimdall.world_changes (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
from_world uuid not null,
|
||||
from_world_name text not null,
|
||||
to_world uuid not null,
|
||||
to_world_name text not null,
|
||||
primary key (time, player)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.world_changes', 'time', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
create table if not exists heimdall.player_deaths (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
world uuid not null,
|
||||
x double precision not null,
|
||||
y double precision not null,
|
||||
z double precision not null,
|
||||
pitch double precision not null,
|
||||
yaw double precision not null,
|
||||
experience double precision not null,
|
||||
message text null,
|
||||
primary key (time, player)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.player_deaths', 'time', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
create table if not exists heimdall.player_advancements (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
world uuid not null,
|
||||
x double precision not null,
|
||||
y double precision not null,
|
||||
z double precision not null,
|
||||
pitch double precision not null,
|
||||
yaw double precision not null,
|
||||
advancement text not null,
|
||||
primary key (time, player, advancement)
|
||||
);
|
||||
--
|
||||
select create_hypertable('heimdall.player_advancements', 'time', 'player', 4, if_not_exists => TRUE);
|
||||
--
|
||||
create table if not exists heimdall.entity_kills (
|
||||
time timestamp not null,
|
||||
player uuid not null,
|
||||
entity uuid not null,
|
||||
world uuid not null,
|
||||
x double precision not null,
|
||||
y double precision not null,
|
||||
z double precision not null,
|
||||
pitch double precision not null,
|
||||
yaw double precision not null,
|
||||
entity_type text not null,
|
||||
primary key (time, entity, player)
|
||||
);
|
||||
--
|
||||
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.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;
|
@ -1,15 +0,0 @@
|
||||
name: Foundation-Heimdall
|
||||
version: '${version}'
|
||||
main: gay.pizza.foundation.heimdall.FoundationHeimdallPlugin
|
||||
api-version: 1.18
|
||||
prefix: Foundation-Heimdall
|
||||
load: STARTUP
|
||||
depend:
|
||||
- Foundation
|
||||
authors:
|
||||
- kubelet
|
||||
commands:
|
||||
export_all_chunks:
|
||||
description: Export All Chunks
|
||||
usage: /export_all_chunks
|
||||
permission: foundation.heimdall.command.export_all_chunks
|
Reference in New Issue
Block a user