mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 03:39:37 +00:00 
			
		
		
		
	Rewrite Heimdall block handling to support more event types and non-player block changes.
This commit is contained in:
		@ -1,6 +0,0 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.table
 | 
			
		||||
 | 
			
		||||
object BlockBreakTable : PlayerTimedLocalEventTable("block_breaks") {
 | 
			
		||||
  val block = text("block")
 | 
			
		||||
  val blockData = text("block_data").nullable()
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.table
 | 
			
		||||
 | 
			
		||||
object BlockChangeTable : PlayerTimedLocalEventTable("block_changes") {
 | 
			
		||||
  val block = text("block")
 | 
			
		||||
  val data = text("data")
 | 
			
		||||
  val cause = text("cause")
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +0,0 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.table
 | 
			
		||||
 | 
			
		||||
object BlockPlaceTable : PlayerTimedLocalEventTable("block_places") {
 | 
			
		||||
  val block = text("block")
 | 
			
		||||
  val blockData = text("block_data").nullable()
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.table
 | 
			
		||||
 | 
			
		||||
abstract class PlayerTimedLocalEventTable(name: String) : TimedLocalEventTable(name) {
 | 
			
		||||
  val player = uuid("player")
 | 
			
		||||
  val player = uuid("player").nullable()
 | 
			
		||||
  val pitch = double("pitch")
 | 
			
		||||
  val yaw = double("yaw")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.view
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.PlayerTimedLocalEventTable
 | 
			
		||||
 | 
			
		||||
object BlockChangeView : PlayerTimedLocalEventTable("block_changes") {
 | 
			
		||||
  val isBreak = bool("break")
 | 
			
		||||
  val block = text("block")
 | 
			
		||||
  val blockData = text("block_data").nullable()
 | 
			
		||||
}
 | 
			
		||||
@ -1,47 +0,0 @@
 | 
			
		||||
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.table.BlockBreakTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.Material
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
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.*
 | 
			
		||||
 | 
			
		||||
class BlockBreak(
 | 
			
		||||
  val playerUniqueIdentity: UUID,
 | 
			
		||||
  val location: Location,
 | 
			
		||||
  val material: Material,
 | 
			
		||||
  val blockData: String? = null,
 | 
			
		||||
  val timestamp: Instant = Instant.now()
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(event: BlockBreakEvent) : this(
 | 
			
		||||
    event.player.uniqueId,
 | 
			
		||||
    event.block.location,
 | 
			
		||||
    event.block.type,
 | 
			
		||||
    event.block.blockData.asString
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      BlockBreakTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
        it[block] = material.key.toString()
 | 
			
		||||
        it[blockData] = this@BlockBreak.blockData
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  class Collector(val buffer: IEventBuffer) : EventCollector<BlockBreak> {
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onBlockBroken(event: BlockBreakEvent) = buffer.push(BlockBreak(event))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<BlockBreak> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<BlockBreak> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,182 @@
 | 
			
		||||
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.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.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
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class BlockChange(
 | 
			
		||||
  val playerUniqueIdentity: UUID? = null,
 | 
			
		||||
  val cause: String = "place",
 | 
			
		||||
  val location: Location,
 | 
			
		||||
  val material: Material,
 | 
			
		||||
  val blockData: String,
 | 
			
		||||
  val timestamp: Instant = Instant.now()
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(
 | 
			
		||||
    playerUniqueIdentity: UUID? = null,
 | 
			
		||||
    isBreak: Boolean = false,
 | 
			
		||||
    cause: String,
 | 
			
		||||
    event: BlockEvent,
 | 
			
		||||
    block: Block = event.block
 | 
			
		||||
  ) : 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
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      BlockChangeTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
        it[block] = material.key.toString()
 | 
			
		||||
        it[data] = this@BlockChange.blockData
 | 
			
		||||
        it[cause] = this@BlockChange.cause
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,47 +0,0 @@
 | 
			
		||||
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.table.BlockPlaceTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.Material
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
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.*
 | 
			
		||||
 | 
			
		||||
class BlockPlace(
 | 
			
		||||
  val playerUniqueIdentity: UUID,
 | 
			
		||||
  val location: Location,
 | 
			
		||||
  val material: Material,
 | 
			
		||||
  val blockData: String? = null,
 | 
			
		||||
  val timestamp: Instant = Instant.now()
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(event: BlockPlaceEvent) : this(
 | 
			
		||||
    event.player.uniqueId,
 | 
			
		||||
    event.block.location,
 | 
			
		||||
    event.block.type,
 | 
			
		||||
    event.block.blockData.asString
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      BlockPlaceTable.insert {
 | 
			
		||||
        putPlayerTimedLocalEvent(it, timestamp, location, playerUniqueIdentity)
 | 
			
		||||
        it[block] = material.key.toString()
 | 
			
		||||
        it[blockData] = this@BlockPlace.blockData
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  class Collector(val buffer: IEventBuffer) : EventCollector<BlockPlace> {
 | 
			
		||||
    @EventHandler
 | 
			
		||||
    fun onBlockPlaced(event: BlockPlaceEvent) = buffer.push(BlockPlace(event))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object : EventCollectorProvider<BlockPlace> {
 | 
			
		||||
    override fun collector(buffer: EventBuffer): EventCollector<BlockPlace> = Collector(buffer)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -2,8 +2,7 @@ package gay.pizza.foundation.heimdall.plugin.event
 | 
			
		||||
 | 
			
		||||
object EventCollectorProviders {
 | 
			
		||||
  val all = listOf<EventCollectorProvider<*>>(
 | 
			
		||||
    BlockBreak,
 | 
			
		||||
    BlockPlace,
 | 
			
		||||
    BlockChange,
 | 
			
		||||
    EntityKill,
 | 
			
		||||
    PlayerAdvancement,
 | 
			
		||||
    PlayerDeath,
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ fun <T : PlayerTimedLocalEventTable, K : Any> T.putPlayerTimedLocalEvent(
 | 
			
		||||
  statement: InsertStatement<K>,
 | 
			
		||||
  time: Instant,
 | 
			
		||||
  location: Location,
 | 
			
		||||
  player: UUID
 | 
			
		||||
  player: UUID?
 | 
			
		||||
) {
 | 
			
		||||
  putTimedLocalEvent(statement, time, location)
 | 
			
		||||
  statement[this.player] = player
 | 
			
		||||
 | 
			
		||||
@ -26,9 +26,9 @@ alter table player_positions set (
 | 
			
		||||
--
 | 
			
		||||
select add_compression_policy('player_positions', interval '3 days', if_not_exists => true);
 | 
			
		||||
--
 | 
			
		||||
create table if not exists block_breaks (
 | 
			
		||||
create table if not exists block_changes (
 | 
			
		||||
    time timestamp not null,
 | 
			
		||||
    player uuid not null,
 | 
			
		||||
    player uuid null,
 | 
			
		||||
    world uuid not null,
 | 
			
		||||
    x double precision not null,
 | 
			
		||||
    y double precision not null,
 | 
			
		||||
@ -36,25 +36,12 @@ create table if not exists block_breaks (
 | 
			
		||||
    pitch double precision not null,
 | 
			
		||||
    yaw double precision not null,
 | 
			
		||||
    block text not null,
 | 
			
		||||
    PRIMARY KEY (time, player, world)
 | 
			
		||||
    data text not null,
 | 
			
		||||
    cause text not null,
 | 
			
		||||
    PRIMARY KEY (time, world, x, y, z)
 | 
			
		||||
);
 | 
			
		||||
--
 | 
			
		||||
select create_hypertable('block_breaks', 'time', 'player', 4,  if_not_exists => TRUE);
 | 
			
		||||
--
 | 
			
		||||
create table if not exists 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('block_places', 'time', 'player', 4,  if_not_exists => TRUE);
 | 
			
		||||
select create_hypertable('block_changes', 'time', 'x', 4,  if_not_exists => TRUE);
 | 
			
		||||
--
 | 
			
		||||
create table if not exists player_sessions (
 | 
			
		||||
    id uuid not null,
 | 
			
		||||
@ -140,13 +127,3 @@ create or replace view player_names as
 | 
			
		||||
    ) as name
 | 
			
		||||
    from unique_player_ids;
 | 
			
		||||
--
 | 
			
		||||
alter table block_places add column if not exists block_data text null;
 | 
			
		||||
--
 | 
			
		||||
alter table block_breaks add column if not exists block_data text null;
 | 
			
		||||
--
 | 
			
		||||
create or replace view block_changes as
 | 
			
		||||
    select true as break, *
 | 
			
		||||
    from block_breaks
 | 
			
		||||
    union all
 | 
			
		||||
    select false as break, * from block_places;
 | 
			
		||||
--
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,11 @@ import com.github.ajalt.clikt.parameters.options.option
 | 
			
		||||
import com.github.ajalt.clikt.parameters.options.required
 | 
			
		||||
import com.github.ajalt.clikt.parameters.types.enum
 | 
			
		||||
import com.github.ajalt.clikt.parameters.types.int
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.BlockChangeTable
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.WorldChangeTable
 | 
			
		||||
import gay.pizza.foundation.heimdall.tool.render.*
 | 
			
		||||
import gay.pizza.foundation.heimdall.tool.state.*
 | 
			
		||||
import gay.pizza.foundation.heimdall.tool.util.compose
 | 
			
		||||
import gay.pizza.foundation.heimdall.view.BlockChangeView
 | 
			
		||||
import org.jetbrains.exposed.sql.Database
 | 
			
		||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 | 
			
		||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.greaterEq
 | 
			
		||||
@ -49,8 +49,6 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name
 | 
			
		||||
    .enum<ImageFormatType> { it.id }
 | 
			
		||||
    .default(ImageFormatType.Png)
 | 
			
		||||
 | 
			
		||||
  private val considerAirBlocks by option("--consider-air-blocks", help = "Enable Air Block Consideration").flag()
 | 
			
		||||
 | 
			
		||||
  private val fromCoordinate by option("--trim-from", help = "Trim From Coordinate")
 | 
			
		||||
  private val toCoordinate by option("--trim-to", help = "Trim To Coordinate")
 | 
			
		||||
 | 
			
		||||
@ -100,11 +98,11 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name
 | 
			
		||||
 | 
			
		||||
    val filter = compose(
 | 
			
		||||
      combine = { a, b -> a and b },
 | 
			
		||||
      { trim?.first?.x != null } to { BlockChangeView.x greaterEq trim!!.first.x.toDouble() },
 | 
			
		||||
      { trim?.first?.z != null } to { BlockChangeView.z greaterEq trim!!.first.z.toDouble() },
 | 
			
		||||
      { trim?.second?.x != null } to { BlockChangeView.x lessEq trim!!.second.x.toDouble() },
 | 
			
		||||
      { trim?.second?.z != null } to { BlockChangeView.z lessEq trim!!.second.z.toDouble() },
 | 
			
		||||
      { true } to { BlockChangeView.world eq world }
 | 
			
		||||
      { trim?.first?.x != null } to { BlockChangeTable.x greaterEq trim!!.first.x.toDouble() },
 | 
			
		||||
      { trim?.first?.z != null } to { BlockChangeTable.z greaterEq trim!!.first.z.toDouble() },
 | 
			
		||||
      { trim?.second?.x != null } to { BlockChangeTable.x lessEq trim!!.second.x.toDouble() },
 | 
			
		||||
      { trim?.second?.z != null } to { BlockChangeTable.z lessEq trim!!.second.z.toDouble() },
 | 
			
		||||
      { true } to { BlockChangeTable.world eq world }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    val changelog = BlockChangelog.query(db, filter)
 | 
			
		||||
@ -131,7 +129,6 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name
 | 
			
		||||
 | 
			
		||||
    val pool = BlockMapRenderPool(
 | 
			
		||||
      changelog = changelog,
 | 
			
		||||
      blockTrackMode = if (considerAirBlocks) BlockTrackMode.AirOnDelete else BlockTrackMode.RemoveOnDelete,
 | 
			
		||||
      delegate = timelapse,
 | 
			
		||||
      createRendererFunction = { expanse -> render.createNewRenderer(expanse, db) },
 | 
			
		||||
      threadPoolExecutor = threadPoolExecutor
 | 
			
		||||
 | 
			
		||||
@ -36,9 +36,9 @@ class PlayerLocationShareRenderer(
 | 
			
		||||
        val player = it[PlayerPositionTable.player]
 | 
			
		||||
        playerSparseMap.createOrModify(
 | 
			
		||||
          coordinate,
 | 
			
		||||
          create = { mutableListOf(player) },
 | 
			
		||||
          modify = { players -> players.add(player) })
 | 
			
		||||
        allPlayerIds.add(player)
 | 
			
		||||
          create = { mutableListOf(player!!) },
 | 
			
		||||
          modify = { players -> players.add(player!!) })
 | 
			
		||||
        allPlayerIds.add(player!!)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.tool.state
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
enum class BlockChangeType {
 | 
			
		||||
  Place,
 | 
			
		||||
  Break
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
package gay.pizza.foundation.heimdall.tool.state
 | 
			
		||||
 | 
			
		||||
import gay.pizza.foundation.heimdall.view.BlockChangeView
 | 
			
		||||
import gay.pizza.foundation.heimdall.table.BlockChangeTable
 | 
			
		||||
import org.jetbrains.exposed.sql.Database
 | 
			
		||||
import org.jetbrains.exposed.sql.Op
 | 
			
		||||
import org.jetbrains.exposed.sql.select
 | 
			
		||||
@ -10,7 +10,7 @@ import java.time.Instant
 | 
			
		||||
import java.util.stream.Stream
 | 
			
		||||
 | 
			
		||||
class BlockChangelog(
 | 
			
		||||
  val changes: List<BlockChange>
 | 
			
		||||
  val changes: List<RecordedBlockChange>
 | 
			
		||||
) {
 | 
			
		||||
  fun slice(slice: ChangelogSlice): BlockChangelog = BlockChangelog(changes.filter {
 | 
			
		||||
    slice.isTimeWithinFullRange(it.time)
 | 
			
		||||
@ -60,43 +60,30 @@ class BlockChangelog(
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    fun query(db: Database, filter: Op<Boolean> = Op.TRUE): BlockChangelog = transaction(db) {
 | 
			
		||||
      BlockChangelog(BlockChangeView.select(filter).orderBy(BlockChangeView.time).map { row ->
 | 
			
		||||
        val time = row[BlockChangeView.time]
 | 
			
		||||
        val changeIsBreak = row[BlockChangeView.isBreak]
 | 
			
		||||
        val world = row[BlockChangeView.world]
 | 
			
		||||
        val x = row[BlockChangeView.x]
 | 
			
		||||
        val y = row[BlockChangeView.y]
 | 
			
		||||
        val z = row[BlockChangeView.z]
 | 
			
		||||
        val block = row[BlockChangeView.block]
 | 
			
		||||
        val blockData = row[BlockChangeView.blockData]
 | 
			
		||||
      BlockChangelog(BlockChangeTable.select(filter).orderBy(BlockChangeTable.time).map { row ->
 | 
			
		||||
        val time = row[BlockChangeTable.time]
 | 
			
		||||
        val world = row[BlockChangeTable.world]
 | 
			
		||||
        val x = row[BlockChangeTable.x]
 | 
			
		||||
        val y = row[BlockChangeTable.y]
 | 
			
		||||
        val z = row[BlockChangeTable.z]
 | 
			
		||||
        val blockMaterial = row[BlockChangeTable.block]
 | 
			
		||||
        val blockData = row[BlockChangeTable.data]
 | 
			
		||||
        val location = BlockCoordinate(x.toLong(), y.toLong(), z.toLong())
 | 
			
		||||
 | 
			
		||||
        val fromBlock = if (changeIsBreak) {
 | 
			
		||||
          BlockState(block, blockData)
 | 
			
		||||
        } else {
 | 
			
		||||
          BlockState.AirBlock
 | 
			
		||||
        }
 | 
			
		||||
        val block = BlockState(blockMaterial, blockData)
 | 
			
		||||
 | 
			
		||||
        val toBlock = if (changeIsBreak) {
 | 
			
		||||
          BlockState.AirBlock
 | 
			
		||||
        } else {
 | 
			
		||||
          BlockState(block, blockData)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        BlockChange(
 | 
			
		||||
        RecordedBlockChange(
 | 
			
		||||
          time,
 | 
			
		||||
          world,
 | 
			
		||||
          if (changeIsBreak) BlockChangeType.Break else BlockChangeType.Place,
 | 
			
		||||
          location,
 | 
			
		||||
          fromBlock,
 | 
			
		||||
          toBlock
 | 
			
		||||
          block
 | 
			
		||||
        )
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun <T> splitBy(key: (BlockChange) -> T): Map<T, BlockChangelog> {
 | 
			
		||||
    val logs = mutableMapOf<T, MutableList<BlockChange>>()
 | 
			
		||||
  fun <T> splitBy(key: (RecordedBlockChange) -> T): Map<T, BlockChangelog> {
 | 
			
		||||
    val logs = mutableMapOf<T, MutableList<RecordedBlockChange>>()
 | 
			
		||||
    for (change in changes) {
 | 
			
		||||
      val k = key(change)
 | 
			
		||||
      var log = logs[k]
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import gay.pizza.foundation.heimdall.tool.util.minOfAll
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap
 | 
			
		||||
import kotlin.math.absoluteValue
 | 
			
		||||
 | 
			
		||||
class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOnDelete, isConcurrent: Boolean = false) {
 | 
			
		||||
class BlockLogTracker(isConcurrent: Boolean = false) {
 | 
			
		||||
  internal val blocks: MutableMap<BlockCoordinate, BlockState> = if (isConcurrent) ConcurrentHashMap() else mutableMapOf()
 | 
			
		||||
 | 
			
		||||
  fun place(position: BlockCoordinate, state: BlockState) {
 | 
			
		||||
@ -16,14 +16,6 @@ class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOn
 | 
			
		||||
    blocks.putAll(map)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun delete(position: BlockCoordinate) {
 | 
			
		||||
    if (mode == BlockTrackMode.AirOnDelete) {
 | 
			
		||||
      blocks[position] = BlockState.AirBlock
 | 
			
		||||
    } else {
 | 
			
		||||
      blocks.remove(position)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun calculateZeroBlockOffset(): BlockCoordinate {
 | 
			
		||||
    val (x, y, z) = blocks.keys.minOfAll(3) { listOf(it.x, it.y, it.z) }
 | 
			
		||||
    val xOffset = if (x < 0) x.absoluteValue else 0
 | 
			
		||||
@ -60,11 +52,7 @@ class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOn
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun replay(changelog: BlockChangelog) = changelog.changes.forEach { change ->
 | 
			
		||||
    if (change.type == BlockChangeType.Break) {
 | 
			
		||||
      delete(change.location)
 | 
			
		||||
    } else {
 | 
			
		||||
      place(change.location, change.to)
 | 
			
		||||
    }
 | 
			
		||||
    place(change.location, change.state)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun get(position: BlockCoordinate): BlockState? = blocks[position]
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ import java.util.concurrent.ThreadPoolExecutor
 | 
			
		||||
 | 
			
		||||
class BlockMapRenderPool<T>(
 | 
			
		||||
  val changelog: BlockChangelog,
 | 
			
		||||
  val blockTrackMode: BlockTrackMode,
 | 
			
		||||
  val createRendererFunction: (BlockExpanse) -> BlockMapRenderer<T>,
 | 
			
		||||
  val delegate: BlockMapRenderPoolDelegate<T>,
 | 
			
		||||
  val threadPoolExecutor: ThreadPoolExecutor,
 | 
			
		||||
@ -64,7 +63,7 @@ class BlockMapRenderPool<T>(
 | 
			
		||||
  private fun runPlaybackSlice(id: String, slice: ChangelogSlice) {
 | 
			
		||||
    val start = System.currentTimeMillis()
 | 
			
		||||
    val sliced = changelog.slice(slice)
 | 
			
		||||
    val tracker = BlockLogTracker(blockTrackMode)
 | 
			
		||||
    val tracker = BlockLogTracker()
 | 
			
		||||
    tracker.replay(sliced)
 | 
			
		||||
    if (tracker.isNotEmpty()) {
 | 
			
		||||
      trackers[slice] = tracker
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ class PlayerPositionChangelog(
 | 
			
		||||
        val pitch = row[PlayerPositionTable.z]
 | 
			
		||||
        val yaw = row[PlayerPositionTable.z]
 | 
			
		||||
 | 
			
		||||
        PlayerPositionChange(time, player, world, x, y, z, pitch, yaw)
 | 
			
		||||
        PlayerPositionChange(time, player!!, world, x, y, z, pitch, yaw)
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -3,11 +3,9 @@ package gay.pizza.foundation.heimdall.tool.state
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import java.util.UUID
 | 
			
		||||
 | 
			
		||||
data class BlockChange(
 | 
			
		||||
data class RecordedBlockChange(
 | 
			
		||||
  val time: Instant,
 | 
			
		||||
  val world: UUID,
 | 
			
		||||
  val type: BlockChangeType,
 | 
			
		||||
  val location: BlockCoordinate,
 | 
			
		||||
  val from: BlockState,
 | 
			
		||||
  val to: BlockState
 | 
			
		||||
  val state: BlockState
 | 
			
		||||
)
 | 
			
		||||
		Reference in New Issue
	
	Block a user