mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 03:39:37 +00:00 
			
		
		
		
	heimdall: implement precise block change collector
This commit is contained in:
		@ -29,7 +29,7 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
			
		||||
  private val buffer = EventBuffer()
 | 
			
		||||
  private val bufferFlushThread = BufferFlushThread(this, buffer)
 | 
			
		||||
 | 
			
		||||
  private val collectors = mutableListOf<EventCollector<*>>()
 | 
			
		||||
  val collectors = mutableListOf<EventCollector<*>>()
 | 
			
		||||
 | 
			
		||||
  override fun onEnable() {
 | 
			
		||||
    val exportChunksCommand = getCommand("export_all_chunks") ?:
 | 
			
		||||
@ -48,10 +48,10 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
			
		||||
    )
 | 
			
		||||
    config = Yaml.default.decodeFromStream(HeimdallConfig.serializer(), configPath.inputStream())
 | 
			
		||||
    if (!config.enabled) {
 | 
			
		||||
      slF4JLogger.info("Heimdall is not enabled.")
 | 
			
		||||
      slF4JLogger.info("Heimdall tracking is not enabled.")
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    slF4JLogger.info("Heimdall is enabled.")
 | 
			
		||||
    slF4JLogger.info("Heimdall tracking is enabled.")
 | 
			
		||||
    if (!Driver.isRegistered()) {
 | 
			
		||||
      Driver.register()
 | 
			
		||||
    }
 | 
			
		||||
@ -91,7 +91,7 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
			
		||||
    bufferFlushThread.start()
 | 
			
		||||
 | 
			
		||||
    for (collectorProvider in EventCollectorProviders.all) {
 | 
			
		||||
      val collector = collectorProvider.collector(buffer)
 | 
			
		||||
      val collector = collectorProvider.collector(config, buffer)
 | 
			
		||||
      server.pluginManager.registerEvents(collector, this)
 | 
			
		||||
      collectors.add(collector)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,12 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
 | 
			
		||||
  fun start() {
 | 
			
		||||
    running.set(true)
 | 
			
		||||
    val thread = Thread {
 | 
			
		||||
      plugin.slF4JLogger.info("Buffer Flusher Started")
 | 
			
		||||
      plugin.slF4JLogger.info("Buffer flusher started.")
 | 
			
		||||
      while (running.get()) {
 | 
			
		||||
        flush()
 | 
			
		||||
        Thread.sleep(5000)
 | 
			
		||||
      }
 | 
			
		||||
      plugin.slF4JLogger.info("Buffer Flusher Stopped")
 | 
			
		||||
      plugin.slF4JLogger.info("Buffer flusher stopped.")
 | 
			
		||||
    }
 | 
			
		||||
    thread.name = "Heimdall Buffer Flush"
 | 
			
		||||
    thread.isDaemon = false
 | 
			
		||||
@ -31,6 +31,10 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
 | 
			
		||||
 | 
			
		||||
  fun flush() {
 | 
			
		||||
    try {
 | 
			
		||||
      for (collector in plugin.collectors) {
 | 
			
		||||
        collector.beforeBufferFlush()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      val db = plugin.db
 | 
			
		||||
      if (db == null) {
 | 
			
		||||
        buffer.clear()
 | 
			
		||||
@ -39,7 +43,7 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
 | 
			
		||||
      transaction(plugin.db) {
 | 
			
		||||
        val count = buffer.flush(this)
 | 
			
		||||
        if (count > 0) {
 | 
			
		||||
          plugin.slF4JLogger.debug("Flushed $count Events")
 | 
			
		||||
          plugin.slF4JLogger.debug("Flushed $count events.")
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e: Exception) {
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ class EventBuffer : IEventBuffer {
 | 
			
		||||
    var count = 0L
 | 
			
		||||
    while (referenceOfEvents.isNotEmpty()) {
 | 
			
		||||
      val event = referenceOfEvents.removeAt(0)
 | 
			
		||||
      event.store(transaction)
 | 
			
		||||
      event.store(transaction, count.toInt())
 | 
			
		||||
      count++
 | 
			
		||||
    }
 | 
			
		||||
    return count
 | 
			
		||||
@ -22,6 +22,10 @@ class EventBuffer : IEventBuffer {
 | 
			
		||||
    events.add(event)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun pushAll(events: List<HeimdallEvent>) {
 | 
			
		||||
    this.events.addAll(events)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun clear() {
 | 
			
		||||
    events = mutableListOf()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -4,4 +4,5 @@ import gay.pizza.foundation.heimdall.plugin.event.HeimdallEvent
 | 
			
		||||
 | 
			
		||||
interface IEventBuffer {
 | 
			
		||||
  fun push(event: HeimdallEvent)
 | 
			
		||||
  fun pushAll(events: List<HeimdallEvent>)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,68 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.EventPriority
 | 
			
		||||
import org.bukkit.event.block.*
 | 
			
		||||
 | 
			
		||||
class AccurateBlockChangeCollector(val buffer: IEventBuffer) : EventCollector<BlockChange> {
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockPlaced(event: BlockPlaceEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockPlace(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockBreak(event: BlockBreakEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockBreak(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockExplode(event: BlockExplodeEvent) =
 | 
			
		||||
    buffer.pushAll(BlockChangeConversions.blockExplode(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockBurn(event: BlockBurnEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockBurn(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockDamage(event: BlockDamageEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockDamage(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockForm(event: BlockFormEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockForm(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockGrow(event: BlockGrowEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockGrow(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockFade(event: BlockFadeEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockFade(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockIgnite(event: BlockIgniteEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockIgnite(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockSpread(event: BlockSpreadEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockSpread(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onFluidLevelChange(event: FluidLevelChangeEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.fluidLevelChange(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onSpongeAbsorb(event: SpongeAbsorbEvent) =
 | 
			
		||||
    buffer.pushAll(BlockChangeConversions.spongeAbsorb(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onSignChange(event: SignChangeEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.signChange(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onMoistureChange(event: MoistureChangeEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.moistureChange(event))
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockCook(event: BlockCookEvent) =
 | 
			
		||||
    buffer.push(BlockChangeConversions.blockCook(event))
 | 
			
		||||
}
 | 
			
		||||
@ -1,26 +1,14 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.BlockChangeTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.Material
 | 
			
		||||
import org.bukkit.block.Block
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.EventPriority
 | 
			
		||||
import org.bukkit.event.block.BlockBreakEvent
 | 
			
		||||
import org.bukkit.event.block.BlockBurnEvent
 | 
			
		||||
import org.bukkit.event.block.BlockDamageEvent
 | 
			
		||||
import org.bukkit.event.block.BlockDispenseEvent
 | 
			
		||||
import org.bukkit.block.BlockState
 | 
			
		||||
import org.bukkit.block.data.BlockData
 | 
			
		||||
import org.bukkit.event.block.BlockEvent
 | 
			
		||||
import org.bukkit.event.block.BlockExplodeEvent
 | 
			
		||||
import org.bukkit.event.block.BlockFadeEvent
 | 
			
		||||
import org.bukkit.event.block.BlockFormEvent
 | 
			
		||||
import org.bukkit.event.block.BlockGrowEvent
 | 
			
		||||
import org.bukkit.event.block.BlockIgniteEvent
 | 
			
		||||
import org.bukkit.event.block.BlockPlaceEvent
 | 
			
		||||
import org.bukkit.event.block.BlockSpreadEvent
 | 
			
		||||
import org.bukkit.event.block.FluidLevelChangeEvent
 | 
			
		||||
import org.jetbrains.exposed.sql.Transaction
 | 
			
		||||
import org.jetbrains.exposed.sql.insert
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
@ -39,19 +27,25 @@ class BlockChange(
 | 
			
		||||
    isBreak: Boolean = false,
 | 
			
		||||
    cause: String,
 | 
			
		||||
    event: BlockEvent,
 | 
			
		||||
    block: Block = event.block
 | 
			
		||||
    block: Block = event.block,
 | 
			
		||||
    state: BlockState = block.state,
 | 
			
		||||
    data: BlockData = state.blockData
 | 
			
		||||
  ) : this(
 | 
			
		||||
    playerUniqueIdentity = playerUniqueIdentity,
 | 
			
		||||
    cause = cause,
 | 
			
		||||
    location = block.location,
 | 
			
		||||
    material = if (isBreak) Material.AIR else block.type,
 | 
			
		||||
    blockData = if (isBreak) Material.AIR.createBlockData().asString
 | 
			
		||||
      else block.blockData.asString
 | 
			
		||||
    location = block.location.clone(),
 | 
			
		||||
    material = if (isBreak) Material.AIR else data.material,
 | 
			
		||||
    blockData =
 | 
			
		||||
      if (isBreak)
 | 
			
		||||
        Material.AIR.createBlockData().asString
 | 
			
		||||
      else
 | 
			
		||||
        data.asString
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      BlockChangeTable.insert {
 | 
			
		||||
        it[inc] = index
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
        it[block] = material.key.toString()
 | 
			
		||||
        it[data] = this@BlockChange.blockData
 | 
			
		||||
@ -60,123 +54,12 @@ class BlockChange(
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  class Collector(val buffer: IEventBuffer) : EventCollector<BlockChange> {
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockPlaced(event: BlockPlaceEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = event.player.uniqueId,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "place",
 | 
			
		||||
        isBreak = false
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockBreak(event: BlockBreakEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = event.player.uniqueId,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "break",
 | 
			
		||||
        isBreak = true
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockExplode(event: BlockExplodeEvent) = event.blockList().forEach { block ->
 | 
			
		||||
      buffer.push(
 | 
			
		||||
        BlockChange(
 | 
			
		||||
          event = event,
 | 
			
		||||
          cause = "explode",
 | 
			
		||||
          isBreak = true,
 | 
			
		||||
          block = block
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockBurn(event: BlockBurnEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "burn",
 | 
			
		||||
        isBreak = true
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockDamage(event: BlockDamageEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "damage"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockForm(event: BlockFormEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "form"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockGrow(event: BlockGrowEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "grow"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockFade(event: BlockFadeEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "fade"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockIgnite(event: BlockIgniteEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "ignite"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockDispense(event: BlockDispenseEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "dispense"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onBlockSpread(event: BlockSpreadEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "spread"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
    fun onFluidLevelChange(event: FluidLevelChangeEvent) = buffer.push(
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "fluid-level-change"
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<BlockChange> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<BlockChange> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<BlockChange> =
 | 
			
		||||
      if (config.blockChangePrecise) {
 | 
			
		||||
        PreciseBlockChangeCollector(config, buffer)
 | 
			
		||||
      } else {
 | 
			
		||||
        AccurateBlockChangeCollector(buffer)
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,143 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import org.bukkit.event.block.*
 | 
			
		||||
 | 
			
		||||
object BlockChangeConversions {
 | 
			
		||||
  fun blockPlace(event: BlockPlaceEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = event.player.uniqueId,
 | 
			
		||||
      isBreak = false,
 | 
			
		||||
      cause = "place",
 | 
			
		||||
      event = event
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockBreak(event: BlockBreakEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = event.player.uniqueId,
 | 
			
		||||
      isBreak = true,
 | 
			
		||||
      cause = "break",
 | 
			
		||||
      event = event
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockExplode(event: BlockExplodeEvent): List<BlockChange> =
 | 
			
		||||
    mutableListOf<BlockChange>().apply {
 | 
			
		||||
      addAll(event.blockList().map { block ->
 | 
			
		||||
        BlockChange(
 | 
			
		||||
          cause = "explode",
 | 
			
		||||
          event = event,
 | 
			
		||||
          block = block,
 | 
			
		||||
          isBreak = true
 | 
			
		||||
        )
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      add(BlockChange(
 | 
			
		||||
        cause = "exploded",
 | 
			
		||||
        event = event,
 | 
			
		||||
        block = event.block,
 | 
			
		||||
        isBreak = true
 | 
			
		||||
      ))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  fun blockBurn(event: BlockBurnEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      isBreak = true,
 | 
			
		||||
      cause = "burn",
 | 
			
		||||
      event = event
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockDamage(event: BlockDamageEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      cause = "damage",
 | 
			
		||||
      event = event
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockForm(event: BlockFormEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      cause = "form",
 | 
			
		||||
      event = event,
 | 
			
		||||
      state = event.newState
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockGrow(event: BlockGrowEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      cause = "grow",
 | 
			
		||||
      event = event,
 | 
			
		||||
      state = event.newState
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockFade(event: BlockFadeEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      cause = "fade",
 | 
			
		||||
      event = event,
 | 
			
		||||
      state = event.newState
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockIgnite(event: BlockIgniteEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = event.player?.uniqueId,
 | 
			
		||||
      cause = "ignite",
 | 
			
		||||
      event = event
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockSpread(event: BlockSpreadEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      cause = "spread",
 | 
			
		||||
      event = event,
 | 
			
		||||
      state = event.newState
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun fluidLevelChange(event: FluidLevelChangeEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      cause = "fluid-level-change",
 | 
			
		||||
      event = event,
 | 
			
		||||
      data = event.newData
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun signChange(event: SignChangeEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = event.player.uniqueId,
 | 
			
		||||
      cause = "sign-change",
 | 
			
		||||
      event = event
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun spongeAbsorb(event: SpongeAbsorbEvent): List<BlockChange> =
 | 
			
		||||
    event.blocks.map { block ->
 | 
			
		||||
      BlockChange(
 | 
			
		||||
        playerUniqueIdentity = null,
 | 
			
		||||
        event = event,
 | 
			
		||||
        cause = "sponge-absorb",
 | 
			
		||||
        block = block.block,
 | 
			
		||||
        state = block
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  fun moistureChange(event: MoistureChangeEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      event = event,
 | 
			
		||||
      cause = "moisture-change",
 | 
			
		||||
      state = event.newState
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun blockCook(event: BlockCookEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      event = event,
 | 
			
		||||
      cause = "cook"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  fun physics(event: BlockPhysicsEvent): BlockChange =
 | 
			
		||||
    BlockChange(
 | 
			
		||||
      playerUniqueIdentity = null,
 | 
			
		||||
      event = event,
 | 
			
		||||
      data = event.changedBlockData,
 | 
			
		||||
      cause = "physics"
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
@ -2,6 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.EntityKillTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
@ -18,7 +19,7 @@ class EntityKill(
 | 
			
		||||
  val entityTypeName: String,
 | 
			
		||||
  val timestamp: Instant = Instant.now()
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      EntityKillTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
@ -35,7 +36,7 @@ class EntityKill(
 | 
			
		||||
      buffer.push(
 | 
			
		||||
        EntityKill(
 | 
			
		||||
          killer.uniqueId,
 | 
			
		||||
          killer.location,
 | 
			
		||||
          killer.location.clone(),
 | 
			
		||||
          event.entity.uniqueId,
 | 
			
		||||
          event.entityType.key.toString()
 | 
			
		||||
        )
 | 
			
		||||
@ -44,6 +45,6 @@ class EntityKill(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<EntityKill> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<EntityKill> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<EntityKill> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,5 +4,6 @@ import org.bukkit.Server
 | 
			
		||||
import org.bukkit.event.Listener
 | 
			
		||||
 | 
			
		||||
interface EventCollector<T : HeimdallEvent> : Listener {
 | 
			
		||||
  fun beforeBufferFlush() {}
 | 
			
		||||
  fun onPluginDisable(server: Server) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
 | 
			
		||||
interface EventCollectorProvider<T : HeimdallEvent> {
 | 
			
		||||
  fun collector(buffer: EventBuffer): EventCollector<T>
 | 
			
		||||
  fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<T>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,5 +3,5 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
import org.jetbrains.exposed.sql.Transaction
 | 
			
		||||
 | 
			
		||||
abstract class HeimdallEvent {
 | 
			
		||||
  abstract fun store(transaction: Transaction)
 | 
			
		||||
  abstract fun store(transaction: Transaction, index: Int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.PlayerAdvancementTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.advancement.Advancement
 | 
			
		||||
@ -20,11 +21,11 @@ class PlayerAdvancement(
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(event: PlayerAdvancementDoneEvent) : this(
 | 
			
		||||
    event.player.uniqueId,
 | 
			
		||||
    event.player.location,
 | 
			
		||||
    event.player.location.clone(),
 | 
			
		||||
    event.advancement
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      PlayerAdvancementTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
@ -39,6 +40,6 @@ class PlayerAdvancement(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<PlayerAdvancement> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<PlayerAdvancement> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<PlayerAdvancement> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.PlayerDeathTable
 | 
			
		||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
@ -21,12 +22,12 @@ class PlayerDeath(
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(event: PlayerDeathEvent, deathMessage: String? = null) : this(
 | 
			
		||||
    event.player.uniqueId,
 | 
			
		||||
    event.player.location,
 | 
			
		||||
    event.player.location.clone(),
 | 
			
		||||
    event.player.exp,
 | 
			
		||||
    deathMessage
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      PlayerDeathTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
@ -52,6 +53,6 @@ class PlayerDeath(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<PlayerDeath> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<PlayerDeath> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<PlayerDeath> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.PlayerPositionTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
@ -18,10 +19,10 @@ class PlayerPosition(
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(event: PlayerMoveEvent) : this(
 | 
			
		||||
    event.player.uniqueId,
 | 
			
		||||
    event.to
 | 
			
		||||
    event.to.clone()
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      PlayerPositionTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
@ -35,6 +36,6 @@ class PlayerPosition(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<PlayerPosition> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<PlayerPosition> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<PlayerPosition> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.PlayerSessionTable
 | 
			
		||||
import org.bukkit.Server
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
@ -19,7 +20,7 @@ class PlayerSession(
 | 
			
		||||
  val startTimeInstant: Instant,
 | 
			
		||||
  val endTimeInstant: Instant
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      PlayerSessionTable.insert {
 | 
			
		||||
        it[id] = UUID.randomUUID()
 | 
			
		||||
@ -61,6 +62,6 @@ class PlayerSession(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<PlayerSession> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<PlayerSession> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<PlayerSession> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,34 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.EventPriority
 | 
			
		||||
import org.bukkit.event.block.BlockPhysicsEvent
 | 
			
		||||
 | 
			
		||||
class PreciseBlockChangeCollector(val config: HeimdallConfig, val buffer: IEventBuffer) : EventCollector<BlockChange> {
 | 
			
		||||
  private var changes = mutableMapOf<String, BlockChange>()
 | 
			
		||||
 | 
			
		||||
  override fun beforeBufferFlush() {
 | 
			
		||||
    if (config.blockChangePreciseImmediate) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    val changesToInsert = changes
 | 
			
		||||
    changes = mutableMapOf()
 | 
			
		||||
    buffer.pushAll(changesToInsert.values.toList())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @EventHandler(priority = EventPriority.MONITOR)
 | 
			
		||||
  fun onBlockPhysics(event: BlockPhysicsEvent) {
 | 
			
		||||
    val change = BlockChangeConversions.physics(event)
 | 
			
		||||
    if (config.blockChangePreciseImmediate) {
 | 
			
		||||
      buffer.push(change)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    changes[(event.sourceBlock.location.world.name to listOf(
 | 
			
		||||
      event.sourceBlock.location.x.toLong(),
 | 
			
		||||
      event.sourceBlock.location.y.toLong(),
 | 
			
		||||
      event.sourceBlock.location.z.toLong()
 | 
			
		||||
    )).toString()] = change
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -2,6 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
 | 
			
		||||
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.WorldChangeTable
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.player.PlayerChangedWorldEvent
 | 
			
		||||
@ -18,7 +19,7 @@ class WorldChange(
 | 
			
		||||
  val toWorldActualName: String,
 | 
			
		||||
  val timestamp: Instant = Instant.now()
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
  override fun store(transaction: Transaction, index: Int) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      WorldChangeTable.insert {
 | 
			
		||||
        putTimedEvent(it, timestamp)
 | 
			
		||||
@ -45,6 +46,6 @@ class WorldChange(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<WorldChange> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<WorldChange> = Collector(buffer)
 | 
			
		||||
    override fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<WorldChange> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,9 @@ import kotlinx.serialization.Serializable
 | 
			
		||||
@Serializable
 | 
			
		||||
data class HeimdallConfig(
 | 
			
		||||
  val enabled: Boolean = false,
 | 
			
		||||
  val db: DbConfig
 | 
			
		||||
  val db: DbConfig,
 | 
			
		||||
  val blockChangePrecise: Boolean = false,
 | 
			
		||||
  val blockChangePreciseImmediate: Boolean = false
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,16 @@
 | 
			
		||||
# Whether Heimdall should be enabled for tracking events.
 | 
			
		||||
enabled: false
 | 
			
		||||
 | 
			
		||||
# Whether the block change events should be precise.
 | 
			
		||||
# Detail of the cause will be lost but all block updates
 | 
			
		||||
# will be captured utilizing physics recording.
 | 
			
		||||
blockChangePrecise: false
 | 
			
		||||
 | 
			
		||||
# Whether the precise block change collector should
 | 
			
		||||
# immediately insert changes to the buffer. This is not
 | 
			
		||||
# recommended as this may cause large insert operations.
 | 
			
		||||
blockChangePreciseImmediate: false
 | 
			
		||||
 | 
			
		||||
# Database connection information.
 | 
			
		||||
db:
 | 
			
		||||
  # JDBC URL
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ select add_compression_policy('player_positions', interval '3 days', if_not_exis
 | 
			
		||||
--
 | 
			
		||||
create table if not exists block_changes (
 | 
			
		||||
    time timestamp not null,
 | 
			
		||||
    inc int not null,
 | 
			
		||||
    player uuid null,
 | 
			
		||||
    world uuid not null,
 | 
			
		||||
    x double precision not null,
 | 
			
		||||
@ -38,10 +39,10 @@ create table if not exists block_changes (
 | 
			
		||||
    block text not null,
 | 
			
		||||
    data text not null,
 | 
			
		||||
    cause text not null,
 | 
			
		||||
    PRIMARY KEY (time, world, x, y, z)
 | 
			
		||||
    PRIMARY KEY (time, inc)
 | 
			
		||||
);
 | 
			
		||||
--
 | 
			
		||||
select create_hypertable('block_changes', 'time', 'x', 4,  if_not_exists => TRUE);
 | 
			
		||||
select create_hypertable('block_changes', 'time', 'inc', 4,  if_not_exists => TRUE);
 | 
			
		||||
--
 | 
			
		||||
create table if not exists player_sessions (
 | 
			
		||||
    id uuid not null,
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user