mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +00:00 
			
		
		
		
	Heimdall: Player Session Tracking
This commit is contained in:
		@ -7,20 +7,25 @@ import cloud.kubelet.foundation.heimdall.buffer.EventBuffer
 | 
				
			|||||||
import cloud.kubelet.foundation.heimdall.event.BlockBreak
 | 
					import cloud.kubelet.foundation.heimdall.event.BlockBreak
 | 
				
			||||||
import cloud.kubelet.foundation.heimdall.event.BlockPlace
 | 
					import cloud.kubelet.foundation.heimdall.event.BlockPlace
 | 
				
			||||||
import cloud.kubelet.foundation.heimdall.event.PlayerPosition
 | 
					import cloud.kubelet.foundation.heimdall.event.PlayerPosition
 | 
				
			||||||
 | 
					import cloud.kubelet.foundation.heimdall.event.PlayerSession
 | 
				
			||||||
import cloud.kubelet.foundation.heimdall.model.HeimdallConfig
 | 
					import cloud.kubelet.foundation.heimdall.model.HeimdallConfig
 | 
				
			||||||
import com.charleskorn.kaml.Yaml
 | 
					import com.charleskorn.kaml.Yaml
 | 
				
			||||||
import com.zaxxer.hikari.HikariConfig
 | 
					import com.zaxxer.hikari.HikariConfig
 | 
				
			||||||
import com.zaxxer.hikari.HikariDataSource
 | 
					import com.zaxxer.hikari.HikariDataSource
 | 
				
			||||||
import org.bukkit.event.EventHandler
 | 
					import org.bukkit.event.EventHandler
 | 
				
			||||||
 | 
					import org.bukkit.event.EventPriority
 | 
				
			||||||
import org.bukkit.event.Listener
 | 
					import org.bukkit.event.Listener
 | 
				
			||||||
import org.bukkit.event.block.BlockBreakEvent
 | 
					import org.bukkit.event.block.BlockBreakEvent
 | 
				
			||||||
import org.bukkit.event.block.BlockPlaceEvent
 | 
					import org.bukkit.event.block.BlockPlaceEvent
 | 
				
			||||||
import org.bukkit.event.entity.EntityDeathEvent
 | 
					import org.bukkit.event.player.PlayerJoinEvent
 | 
				
			||||||
import org.bukkit.event.player.PlayerMoveEvent
 | 
					import org.bukkit.event.player.PlayerMoveEvent
 | 
				
			||||||
 | 
					import org.bukkit.event.player.PlayerQuitEvent
 | 
				
			||||||
import org.bukkit.plugin.java.JavaPlugin
 | 
					import org.bukkit.plugin.java.JavaPlugin
 | 
				
			||||||
import org.jetbrains.exposed.sql.Database
 | 
					import org.jetbrains.exposed.sql.Database
 | 
				
			||||||
import org.postgresql.Driver
 | 
					import org.postgresql.Driver
 | 
				
			||||||
import java.lang.Exception
 | 
					import java.time.Instant
 | 
				
			||||||
 | 
					import java.util.*
 | 
				
			||||||
 | 
					import java.util.concurrent.ConcurrentHashMap
 | 
				
			||||||
import kotlin.io.path.inputStream
 | 
					import kotlin.io.path.inputStream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
					class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
				
			||||||
@ -31,6 +36,8 @@ 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 playerJoinTimes = ConcurrentHashMap<UUID, Instant>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun onEnable() {
 | 
					  override fun onEnable() {
 | 
				
			||||||
    val foundation = server.pluginManager.getPlugin("Foundation") as FoundationCorePlugin
 | 
					    val foundation = server.pluginManager.getPlugin("Foundation") as FoundationCorePlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -91,7 +98,25 @@ class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
				
			|||||||
  @EventHandler
 | 
					  @EventHandler
 | 
				
			||||||
  fun onBlockBroken(event: BlockBreakEvent) = buffer.push(BlockBreak(event))
 | 
					  fun onBlockBroken(event: BlockBreakEvent) = buffer.push(BlockBreak(event))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @EventHandler
 | 
				
			||||||
 | 
					  fun onPlayerJoin(event: PlayerJoinEvent) {
 | 
				
			||||||
 | 
					    playerJoinTimes[event.player.uniqueId] = Instant.now()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @EventHandler(priority = EventPriority.HIGHEST)
 | 
				
			||||||
 | 
					  fun onPlayerQuit(event: PlayerQuitEvent) {
 | 
				
			||||||
 | 
					    val startTime = playerJoinTimes.remove(event.player.uniqueId) ?: return
 | 
				
			||||||
 | 
					    val endTime = Instant.now()
 | 
				
			||||||
 | 
					    buffer.push(PlayerSession(event.player.uniqueId, event.player.name, startTime, endTime))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun onDisable() {
 | 
					  override fun onDisable() {
 | 
				
			||||||
    bufferFlushThread.stop()
 | 
					    bufferFlushThread.stop()
 | 
				
			||||||
 | 
					    val endTime = Instant.now()
 | 
				
			||||||
 | 
					    for (playerId in playerJoinTimes.keys().toList()) {
 | 
				
			||||||
 | 
					      val startTime = playerJoinTimes.remove(playerId) ?: continue
 | 
				
			||||||
 | 
					      buffer.push(PlayerSession(playerId, server.getPlayer(playerId)?.name ?: "__unknown__", startTime, endTime))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bufferFlushThread.flush()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,12 +6,30 @@ import java.util.concurrent.atomic.AtomicBoolean
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventBuffer) {
 | 
					class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventBuffer) {
 | 
				
			||||||
  private val running = AtomicBoolean(false)
 | 
					  private val running = AtomicBoolean(false)
 | 
				
			||||||
 | 
					  private var thread: Thread? = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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()
 | 
				
			||||||
 | 
					        Thread.sleep(5000)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      plugin.slF4JLogger.info("Buffer Flusher Stopped")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    thread.name = "Heimdall Buffer Flush"
 | 
				
			||||||
 | 
					    thread.isDaemon = false
 | 
				
			||||||
 | 
					    thread.start()
 | 
				
			||||||
 | 
					    this.thread = thread
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fun stop() {
 | 
				
			||||||
 | 
					    running.set(false)
 | 
				
			||||||
 | 
					    thread?.join()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fun flush() {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      transaction(plugin.db) {
 | 
					      transaction(plugin.db) {
 | 
				
			||||||
        val count = buffer.flush(this)
 | 
					        val count = buffer.flush(this)
 | 
				
			||||||
@ -22,16 +40,5 @@ class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventB
 | 
				
			|||||||
    } catch (e: Exception) {
 | 
					    } catch (e: Exception) {
 | 
				
			||||||
      plugin.slF4JLogger.warn("Failed to flush buffer.", e)
 | 
					      plugin.slF4JLogger.warn("Failed to flush buffer.", e)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        Thread.sleep(5000)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      plugin.slF4JLogger.info("Buffer Flusher Stopped")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    thread.name = "Heimdall Buffer Flush"
 | 
					 | 
				
			||||||
    thread.isDaemon = false
 | 
					 | 
				
			||||||
    thread.start()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  fun stop() {
 | 
					 | 
				
			||||||
    running.set(false)
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ class BlockPlace(
 | 
				
			|||||||
  val location: Location,
 | 
					  val location: Location,
 | 
				
			||||||
  val material: Material,
 | 
					  val material: Material,
 | 
				
			||||||
  val timestamp: Instant = Instant.now()
 | 
					  val timestamp: Instant = Instant.now()
 | 
				
			||||||
  ) : HeimdallEvent() {
 | 
					) : HeimdallEvent() {
 | 
				
			||||||
  constructor(event: BlockPlaceEvent) : this(event.player.uniqueId, event.block.location, event.block.type)
 | 
					  constructor(event: BlockPlaceEvent) : this(event.player.uniqueId, event.block.location, event.block.type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun store(transaction: Transaction) {
 | 
					  override fun store(transaction: Transaction) {
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					package cloud.kubelet.foundation.heimdall.event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cloud.kubelet.foundation.heimdall.table.PlayerSessionTable
 | 
				
			||||||
 | 
					import org.jetbrains.exposed.sql.Transaction
 | 
				
			||||||
 | 
					import org.jetbrains.exposed.sql.insert
 | 
				
			||||||
 | 
					import java.time.Instant
 | 
				
			||||||
 | 
					import java.util.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PlayerSession(
 | 
				
			||||||
 | 
					  val playerUniqueIdentity: UUID,
 | 
				
			||||||
 | 
					  val playerName: String,
 | 
				
			||||||
 | 
					  val startTimeInstant: Instant,
 | 
				
			||||||
 | 
					  val endTimeInstant: Instant
 | 
				
			||||||
 | 
					) : HeimdallEvent() {
 | 
				
			||||||
 | 
					  override fun store(transaction: Transaction) {
 | 
				
			||||||
 | 
					    transaction.apply {
 | 
				
			||||||
 | 
					      PlayerSessionTable.insert {
 | 
				
			||||||
 | 
					        it[player] = playerUniqueIdentity
 | 
				
			||||||
 | 
					        it[name] = playerName
 | 
				
			||||||
 | 
					        it[startTime] = startTimeInstant
 | 
				
			||||||
 | 
					        it[endTime] = endTimeInstant
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					package cloud.kubelet.foundation.heimdall.table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.jetbrains.exposed.sql.*
 | 
				
			||||||
 | 
					import org.jetbrains.exposed.sql.javatime.timestamp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					object PlayerSessionTable : Table("player_sessions") {
 | 
				
			||||||
 | 
					  val player = uuid("player")
 | 
				
			||||||
 | 
					  val name = text("name")
 | 
				
			||||||
 | 
					  val startTime = timestamp("start")
 | 
				
			||||||
 | 
					  val endTime = timestamp("end")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -44,3 +44,13 @@ create table if not exists heimdall.block_places (
 | 
				
			|||||||
);
 | 
					);
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
select create_hypertable('heimdall.block_places', 'time', 'player', 4,  if_not_exists => TRUE);
 | 
					select create_hypertable('heimdall.block_places', 'time', 'player', 4,  if_not_exists => TRUE);
 | 
				
			||||||
 | 
					--
 | 
				
			||||||
 | 
					create table if not exists heimdall.player_sessions (
 | 
				
			||||||
 | 
					    player uuid not null,
 | 
				
			||||||
 | 
					    name text not null,
 | 
				
			||||||
 | 
					    "start" timestamp not null,
 | 
				
			||||||
 | 
					    "end" timestamp not null,
 | 
				
			||||||
 | 
					    PRIMARY KEY (player, start)
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					--
 | 
				
			||||||
 | 
					select create_hypertable('heimdall.player_sessions', 'start', 'player', 4,  if_not_exists => TRUE);
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user