heimdall: implement precise block change collector

This commit is contained in:
2023-02-09 03:44:43 -05:00
parent eaa3888821
commit ef822f9217
21 changed files with 326 additions and 167 deletions

View File

@ -4,4 +4,5 @@ object BlockChangeTable : PlayerTimedLocalEventTable("block_changes") {
val block = text("block") val block = text("block")
val data = text("data") val data = text("data")
val cause = text("cause") val cause = text("cause")
val inc = integer("inc")
} }

View File

@ -29,7 +29,7 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
private val buffer = EventBuffer() private val buffer = EventBuffer()
private val bufferFlushThread = BufferFlushThread(this, buffer) private val bufferFlushThread = BufferFlushThread(this, buffer)
private val collectors = mutableListOf<EventCollector<*>>() val collectors = mutableListOf<EventCollector<*>>()
override fun onEnable() { override fun onEnable() {
val exportChunksCommand = getCommand("export_all_chunks") ?: val exportChunksCommand = getCommand("export_all_chunks") ?:
@ -48,10 +48,10 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
) )
config = Yaml.default.decodeFromStream(HeimdallConfig.serializer(), configPath.inputStream()) config = Yaml.default.decodeFromStream(HeimdallConfig.serializer(), configPath.inputStream())
if (!config.enabled) { if (!config.enabled) {
slF4JLogger.info("Heimdall is not enabled.") slF4JLogger.info("Heimdall tracking is not enabled.")
return return
} }
slF4JLogger.info("Heimdall is enabled.") slF4JLogger.info("Heimdall tracking is enabled.")
if (!Driver.isRegistered()) { if (!Driver.isRegistered()) {
Driver.register() Driver.register()
} }
@ -91,7 +91,7 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
bufferFlushThread.start() bufferFlushThread.start()
for (collectorProvider in EventCollectorProviders.all) { for (collectorProvider in EventCollectorProviders.all) {
val collector = collectorProvider.collector(buffer) val collector = collectorProvider.collector(config, buffer)
server.pluginManager.registerEvents(collector, this) server.pluginManager.registerEvents(collector, this)
collectors.add(collector) collectors.add(collector)
} }

View File

@ -11,12 +11,12 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
fun start() { fun start() {
running.set(true) running.set(true)
val thread = Thread { val thread = Thread {
plugin.slF4JLogger.info("Buffer Flusher Started") plugin.slF4JLogger.info("Buffer flusher started.")
while (running.get()) { while (running.get()) {
flush() flush()
Thread.sleep(5000) Thread.sleep(5000)
} }
plugin.slF4JLogger.info("Buffer Flusher Stopped") plugin.slF4JLogger.info("Buffer flusher stopped.")
} }
thread.name = "Heimdall Buffer Flush" thread.name = "Heimdall Buffer Flush"
thread.isDaemon = false thread.isDaemon = false
@ -31,6 +31,10 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
fun flush() { fun flush() {
try { try {
for (collector in plugin.collectors) {
collector.beforeBufferFlush()
}
val db = plugin.db val db = plugin.db
if (db == null) { if (db == null) {
buffer.clear() buffer.clear()
@ -39,7 +43,7 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
transaction(plugin.db) { transaction(plugin.db) {
val count = buffer.flush(this) val count = buffer.flush(this)
if (count > 0) { if (count > 0) {
plugin.slF4JLogger.debug("Flushed $count Events") plugin.slF4JLogger.debug("Flushed $count events.")
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -12,7 +12,7 @@ class EventBuffer : IEventBuffer {
var count = 0L var count = 0L
while (referenceOfEvents.isNotEmpty()) { while (referenceOfEvents.isNotEmpty()) {
val event = referenceOfEvents.removeAt(0) val event = referenceOfEvents.removeAt(0)
event.store(transaction) event.store(transaction, count.toInt())
count++ count++
} }
return count return count
@ -22,6 +22,10 @@ class EventBuffer : IEventBuffer {
events.add(event) events.add(event)
} }
override fun pushAll(events: List<HeimdallEvent>) {
this.events.addAll(events)
}
fun clear() { fun clear() {
events = mutableListOf() events = mutableListOf()
} }

View File

@ -4,4 +4,5 @@ import gay.pizza.foundation.heimdall.plugin.event.HeimdallEvent
interface IEventBuffer { interface IEventBuffer {
fun push(event: HeimdallEvent) fun push(event: HeimdallEvent)
fun pushAll(events: List<HeimdallEvent>)
} }

View File

@ -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))
}

View File

@ -1,26 +1,14 @@
package gay.pizza.foundation.heimdall.plugin.event package gay.pizza.foundation.heimdall.plugin.event
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer 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 gay.pizza.foundation.heimdall.table.BlockChangeTable
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.block.Block import org.bukkit.block.Block
import org.bukkit.event.EventHandler import org.bukkit.block.BlockState
import org.bukkit.event.EventPriority import org.bukkit.block.data.BlockData
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.event.block.BlockEvent 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.Transaction
import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.insert
import java.time.Instant import java.time.Instant
@ -39,19 +27,25 @@ class BlockChange(
isBreak: Boolean = false, isBreak: Boolean = false,
cause: String, cause: String,
event: BlockEvent, event: BlockEvent,
block: Block = event.block block: Block = event.block,
state: BlockState = block.state,
data: BlockData = state.blockData
) : this( ) : this(
playerUniqueIdentity = playerUniqueIdentity, playerUniqueIdentity = playerUniqueIdentity,
cause = cause, cause = cause,
location = block.location, location = block.location.clone(),
material = if (isBreak) Material.AIR else block.type, material = if (isBreak) Material.AIR else data.material,
blockData = if (isBreak) Material.AIR.createBlockData().asString blockData =
else block.blockData.asString if (isBreak)
Material.AIR.createBlockData().asString
else
data.asString
) )
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
BlockChangeTable.insert { BlockChangeTable.insert {
it[inc] = index
putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity) putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
it[block] = material.key.toString() it[block] = material.key.toString()
it[data] = this@BlockChange.blockData 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> { 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)
}
} }
} }

View File

@ -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"
)
}

View File

@ -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.EventBuffer
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import gay.pizza.foundation.heimdall.table.EntityKillTable import gay.pizza.foundation.heimdall.table.EntityKillTable
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
@ -18,7 +19,7 @@ class EntityKill(
val entityTypeName: String, val entityTypeName: String,
val timestamp: Instant = Instant.now() val timestamp: Instant = Instant.now()
) : HeimdallEvent() { ) : HeimdallEvent() {
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
EntityKillTable.insert { EntityKillTable.insert {
putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity) putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
@ -35,7 +36,7 @@ class EntityKill(
buffer.push( buffer.push(
EntityKill( EntityKill(
killer.uniqueId, killer.uniqueId,
killer.location, killer.location.clone(),
event.entity.uniqueId, event.entity.uniqueId,
event.entityType.key.toString() event.entityType.key.toString()
) )
@ -44,6 +45,6 @@ class EntityKill(
} }
companion object : EventCollectorProvider<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)
} }
} }

View File

@ -4,5 +4,6 @@ import org.bukkit.Server
import org.bukkit.event.Listener import org.bukkit.event.Listener
interface EventCollector<T : HeimdallEvent> : Listener { interface EventCollector<T : HeimdallEvent> : Listener {
fun beforeBufferFlush() {}
fun onPluginDisable(server: Server) {} fun onPluginDisable(server: Server) {}
} }

View File

@ -1,7 +1,8 @@
package gay.pizza.foundation.heimdall.plugin.event package gay.pizza.foundation.heimdall.plugin.event
import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.EventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
interface EventCollectorProvider<T : HeimdallEvent> { interface EventCollectorProvider<T : HeimdallEvent> {
fun collector(buffer: EventBuffer): EventCollector<T> fun collector(config: HeimdallConfig, buffer: EventBuffer): EventCollector<T>
} }

View File

@ -3,5 +3,5 @@ package gay.pizza.foundation.heimdall.plugin.event
import org.jetbrains.exposed.sql.Transaction import org.jetbrains.exposed.sql.Transaction
abstract class HeimdallEvent { abstract class HeimdallEvent {
abstract fun store(transaction: Transaction) abstract fun store(transaction: Transaction, index: Int)
} }

View File

@ -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.EventBuffer
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import gay.pizza.foundation.heimdall.table.PlayerAdvancementTable import gay.pizza.foundation.heimdall.table.PlayerAdvancementTable
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.advancement.Advancement import org.bukkit.advancement.Advancement
@ -20,11 +21,11 @@ class PlayerAdvancement(
) : HeimdallEvent() { ) : HeimdallEvent() {
constructor(event: PlayerAdvancementDoneEvent) : this( constructor(event: PlayerAdvancementDoneEvent) : this(
event.player.uniqueId, event.player.uniqueId,
event.player.location, event.player.location.clone(),
event.advancement event.advancement
) )
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
PlayerAdvancementTable.insert { PlayerAdvancementTable.insert {
putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity) putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
@ -39,6 +40,6 @@ class PlayerAdvancement(
} }
companion object : EventCollectorProvider<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)
} }
} }

View File

@ -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.EventBuffer
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import gay.pizza.foundation.heimdall.table.PlayerDeathTable import gay.pizza.foundation.heimdall.table.PlayerDeathTable
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
import org.bukkit.Location import org.bukkit.Location
@ -21,12 +22,12 @@ class PlayerDeath(
) : HeimdallEvent() { ) : HeimdallEvent() {
constructor(event: PlayerDeathEvent, deathMessage: String? = null) : this( constructor(event: PlayerDeathEvent, deathMessage: String? = null) : this(
event.player.uniqueId, event.player.uniqueId,
event.player.location, event.player.location.clone(),
event.player.exp, event.player.exp,
deathMessage deathMessage
) )
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
PlayerDeathTable.insert { PlayerDeathTable.insert {
putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity) putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
@ -52,6 +53,6 @@ class PlayerDeath(
} }
companion object : EventCollectorProvider<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)
} }
} }

View File

@ -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.EventBuffer
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import gay.pizza.foundation.heimdall.table.PlayerPositionTable import gay.pizza.foundation.heimdall.table.PlayerPositionTable
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
@ -18,10 +19,10 @@ class PlayerPosition(
) : HeimdallEvent() { ) : HeimdallEvent() {
constructor(event: PlayerMoveEvent) : this( constructor(event: PlayerMoveEvent) : this(
event.player.uniqueId, event.player.uniqueId,
event.to event.to.clone()
) )
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
PlayerPositionTable.insert { PlayerPositionTable.insert {
putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity) putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
@ -35,6 +36,6 @@ class PlayerPosition(
} }
companion object : EventCollectorProvider<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)
} }
} }

View File

@ -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.EventBuffer
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import gay.pizza.foundation.heimdall.table.PlayerSessionTable import gay.pizza.foundation.heimdall.table.PlayerSessionTable
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
@ -19,7 +20,7 @@ class PlayerSession(
val startTimeInstant: Instant, val startTimeInstant: Instant,
val endTimeInstant: Instant val endTimeInstant: Instant
) : HeimdallEvent() { ) : HeimdallEvent() {
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
PlayerSessionTable.insert { PlayerSessionTable.insert {
it[id] = UUID.randomUUID() it[id] = UUID.randomUUID()
@ -61,6 +62,6 @@ class PlayerSession(
} }
companion object : EventCollectorProvider<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)
} }
} }

View File

@ -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
}
}

View File

@ -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.EventBuffer
import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer import gay.pizza.foundation.heimdall.plugin.buffer.IEventBuffer
import gay.pizza.foundation.heimdall.plugin.model.HeimdallConfig
import gay.pizza.foundation.heimdall.table.WorldChangeTable import gay.pizza.foundation.heimdall.table.WorldChangeTable
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.player.PlayerChangedWorldEvent import org.bukkit.event.player.PlayerChangedWorldEvent
@ -18,7 +19,7 @@ class WorldChange(
val toWorldActualName: String, val toWorldActualName: String,
val timestamp: Instant = Instant.now() val timestamp: Instant = Instant.now()
) : HeimdallEvent() { ) : HeimdallEvent() {
override fun store(transaction: Transaction) { override fun store(transaction: Transaction, index: Int) {
transaction.apply { transaction.apply {
WorldChangeTable.insert { WorldChangeTable.insert {
putTimedEvent(it, timestamp) putTimedEvent(it, timestamp)
@ -45,6 +46,6 @@ class WorldChange(
} }
companion object : EventCollectorProvider<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)
} }
} }

View File

@ -5,7 +5,9 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class HeimdallConfig( data class HeimdallConfig(
val enabled: Boolean = false, val enabled: Boolean = false,
val db: DbConfig val db: DbConfig,
val blockChangePrecise: Boolean = false,
val blockChangePreciseImmediate: Boolean = false
) )
@Serializable @Serializable

View File

@ -1,6 +1,16 @@
# Whether Heimdall should be enabled for tracking events. # Whether Heimdall should be enabled for tracking events.
enabled: false 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. # Database connection information.
db: db:
# JDBC URL # JDBC URL

View File

@ -28,6 +28,7 @@ select add_compression_policy('player_positions', interval '3 days', if_not_exis
-- --
create table if not exists block_changes ( create table if not exists block_changes (
time timestamp not null, time timestamp not null,
inc int not null,
player uuid null, player uuid null,
world uuid not null, world uuid not null,
x double precision not null, x double precision not null,
@ -38,10 +39,10 @@ create table if not exists block_changes (
block text not null, block text not null,
data text not null, data text not null,
cause 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 ( create table if not exists player_sessions (
id uuid not null, id uuid not null,