mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +00:00 
			
		
		
		
	Initial Rough Cut of Heimdall Tracking System
This commit is contained in:
		@ -13,18 +13,18 @@ import net.dv8tion.jda.api.entities.TextChannel
 | 
			
		||||
import net.dv8tion.jda.api.events.GenericEvent
 | 
			
		||||
import net.dv8tion.jda.api.events.ReadyEvent
 | 
			
		||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
 | 
			
		||||
import net.dv8tion.jda.api.hooks.EventListener
 | 
			
		||||
import net.dv8tion.jda.api.hooks.EventListener as DiscordEventListener
 | 
			
		||||
import net.kyori.adventure.text.Component
 | 
			
		||||
import net.kyori.adventure.text.TextComponent
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.Listener
 | 
			
		||||
import org.bukkit.event.Listener as BukkitEventListener
 | 
			
		||||
import org.bukkit.event.player.PlayerJoinEvent
 | 
			
		||||
import org.bukkit.event.player.PlayerQuitEvent
 | 
			
		||||
import org.bukkit.plugin.java.JavaPlugin
 | 
			
		||||
import java.awt.Color
 | 
			
		||||
import kotlin.io.path.inputStream
 | 
			
		||||
 | 
			
		||||
class FoundationBifrostPlugin : JavaPlugin(), EventListener, Listener {
 | 
			
		||||
class FoundationBifrostPlugin : JavaPlugin(), DiscordEventListener, BukkitEventListener {
 | 
			
		||||
  private lateinit var config: BifrostConfig
 | 
			
		||||
  private lateinit var jda: JDA
 | 
			
		||||
  private var isDev = false
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								foundation-heimdall/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								foundation-heimdall/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
dependencies {
 | 
			
		||||
  implementation("org.postgresql:postgresql:42.3.1")
 | 
			
		||||
  implementation("org.jetbrains.exposed:exposed-jdbc:0.36.2")
 | 
			
		||||
  implementation("org.jetbrains.exposed:exposed-java-time:0.36.2")
 | 
			
		||||
  implementation("com.zaxxer:HikariCP:5.0.0")
 | 
			
		||||
  compileOnly(project(":foundation-core"))
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall
 | 
			
		||||
 | 
			
		||||
fun String.sqlSplitStatements(): List<String> {
 | 
			
		||||
  val statements = mutableListOf<String>()
 | 
			
		||||
  val buffer = StringBuilder()
 | 
			
		||||
  fun flush() {
 | 
			
		||||
    val trimmed = buffer.toString().trim()
 | 
			
		||||
    if (trimmed.isNotEmpty()) {
 | 
			
		||||
      statements.add(trimmed)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  for (line in lines()) {
 | 
			
		||||
    if (line.trim() == "--") {
 | 
			
		||||
      flush()
 | 
			
		||||
    } else {
 | 
			
		||||
      buffer.append(line).append("\n")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  flush()
 | 
			
		||||
  return statements
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,90 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.Util
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.buffer.BufferFlushThread
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.buffer.EventBuffer
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.event.PlayerPositionEvent
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.model.HeimdallConfig
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.table.PlayerPositionTable
 | 
			
		||||
import com.charleskorn.kaml.Yaml
 | 
			
		||||
import com.zaxxer.hikari.HikariConfig
 | 
			
		||||
import com.zaxxer.hikari.HikariDataSource
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.Listener
 | 
			
		||||
import org.bukkit.event.player.PlayerMoveEvent
 | 
			
		||||
import org.bukkit.plugin.java.JavaPlugin
 | 
			
		||||
import org.jetbrains.exposed.sql.Database
 | 
			
		||||
import org.jetbrains.exposed.sql.insert
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
import org.postgresql.Driver
 | 
			
		||||
import java.lang.Exception
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import kotlin.io.path.inputStream
 | 
			
		||||
 | 
			
		||||
class FoundationHeimdallPlugin : JavaPlugin(), Listener {
 | 
			
		||||
  private lateinit var config: HeimdallConfig
 | 
			
		||||
  private lateinit var pool: HikariDataSource
 | 
			
		||||
  internal lateinit var db: Database
 | 
			
		||||
 | 
			
		||||
  private val buffer = EventBuffer()
 | 
			
		||||
  private val bufferFlushThread = BufferFlushThread(this, buffer)
 | 
			
		||||
 | 
			
		||||
  override fun onEnable() {
 | 
			
		||||
    val foundation = server.pluginManager.getPlugin("Foundation") as FoundationCorePlugin
 | 
			
		||||
 | 
			
		||||
    val configPath = Util.copyDefaultConfig<FoundationHeimdallPlugin>(
 | 
			
		||||
      slF4JLogger,
 | 
			
		||||
      foundation.pluginDataPath,
 | 
			
		||||
      "heimdall.yaml"
 | 
			
		||||
    )
 | 
			
		||||
    config = Yaml.default.decodeFromStream(HeimdallConfig.serializer(), configPath.inputStream())
 | 
			
		||||
    if (!config.enabled) {
 | 
			
		||||
      slF4JLogger.info("Heimdall is not enabled.")
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    slF4JLogger.info("Heimdall is enabled.")
 | 
			
		||||
    if (!Driver.isRegistered()) {
 | 
			
		||||
      Driver.register()
 | 
			
		||||
    }
 | 
			
		||||
    pool = HikariDataSource(HikariConfig().apply {
 | 
			
		||||
      jdbcUrl = config.db.url
 | 
			
		||||
      username = config.db.username
 | 
			
		||||
      password = config.db.password
 | 
			
		||||
      schema = "heimdall"
 | 
			
		||||
    })
 | 
			
		||||
    val initMigrationContent = FoundationHeimdallPlugin::class.java.getResourceAsStream(
 | 
			
		||||
      "/init.sql"
 | 
			
		||||
    )?.readAllBytes()?.decodeToString() ?: throw RuntimeException("Unable to find Heimdall init.sql")
 | 
			
		||||
 | 
			
		||||
    val statements = initMigrationContent.sqlSplitStatements()
 | 
			
		||||
 | 
			
		||||
    pool.connection.use { conn ->
 | 
			
		||||
      conn.autoCommit = false
 | 
			
		||||
      try {
 | 
			
		||||
        for (statementAsString in statements) {
 | 
			
		||||
          conn.prepareStatement(statementAsString).use {
 | 
			
		||||
            it.execute()
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        conn.commit()
 | 
			
		||||
      } catch (e: Exception) {
 | 
			
		||||
        conn.rollback()
 | 
			
		||||
        throw e
 | 
			
		||||
      } finally {
 | 
			
		||||
        conn.autoCommit = true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    db = Database.connect(pool)
 | 
			
		||||
    server.pluginManager.registerEvents(this, this)
 | 
			
		||||
    bufferFlushThread.start()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @EventHandler
 | 
			
		||||
  fun onPlayerMove(event: PlayerMoveEvent) = buffer.push(PlayerPositionEvent(event))
 | 
			
		||||
 | 
			
		||||
  override fun onDisable() {
 | 
			
		||||
    bufferFlushThread.stop()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,37 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall.buffer
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.FoundationHeimdallPlugin
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean
 | 
			
		||||
 | 
			
		||||
class BufferFlushThread(val plugin: FoundationHeimdallPlugin, val buffer: EventBuffer) {
 | 
			
		||||
  private val running = AtomicBoolean(false)
 | 
			
		||||
 | 
			
		||||
  fun start() {
 | 
			
		||||
    running.set(true)
 | 
			
		||||
    val thread = Thread {
 | 
			
		||||
      plugin.slF4JLogger.info("Buffer Flusher Started")
 | 
			
		||||
      while (running.get()) {
 | 
			
		||||
        try {
 | 
			
		||||
          transaction(plugin.db) {
 | 
			
		||||
            val count = buffer.flush(this)
 | 
			
		||||
            if (count > 0) {
 | 
			
		||||
              plugin.slF4JLogger.info("Flushed $count Events")
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
          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)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall.buffer
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.event.HeimdallEvent
 | 
			
		||||
import org.jetbrains.exposed.sql.Transaction
 | 
			
		||||
 | 
			
		||||
class EventBuffer {
 | 
			
		||||
  private var events = mutableListOf<HeimdallEvent>()
 | 
			
		||||
 | 
			
		||||
  fun flush(transaction: Transaction): Long {
 | 
			
		||||
    val referenceOfEvents = events
 | 
			
		||||
    this.events = mutableListOf()
 | 
			
		||||
    var count = 0L
 | 
			
		||||
    while (referenceOfEvents.isNotEmpty()) {
 | 
			
		||||
      val event = referenceOfEvents.removeAt(0)
 | 
			
		||||
      event.store(transaction)
 | 
			
		||||
      count++
 | 
			
		||||
    }
 | 
			
		||||
    return count
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun push(event: HeimdallEvent) {
 | 
			
		||||
    events.add(event)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,7 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall.event
 | 
			
		||||
 | 
			
		||||
import org.jetbrains.exposed.sql.Transaction
 | 
			
		||||
 | 
			
		||||
abstract class HeimdallEvent {
 | 
			
		||||
  abstract fun store(transaction: Transaction)
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,31 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall.event
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.heimdall.table.PlayerPositionTable
 | 
			
		||||
import org.bukkit.Location
 | 
			
		||||
import org.bukkit.event.player.PlayerMoveEvent
 | 
			
		||||
import org.jetbrains.exposed.sql.Transaction
 | 
			
		||||
import org.jetbrains.exposed.sql.insert
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class PlayerPositionEvent(
 | 
			
		||||
  val playerUniqueIdentity: UUID,
 | 
			
		||||
  val location: Location
 | 
			
		||||
) : HeimdallEvent() {
 | 
			
		||||
  constructor(event: PlayerMoveEvent) : this(event.player.uniqueId, event.to)
 | 
			
		||||
 | 
			
		||||
  override fun store(transaction: Transaction) {
 | 
			
		||||
    transaction.apply {
 | 
			
		||||
      PlayerPositionTable.insert {
 | 
			
		||||
        it[time] = Instant.now()
 | 
			
		||||
        it[player] = playerUniqueIdentity
 | 
			
		||||
        it[world] = location.world.uid
 | 
			
		||||
        it[x] = location.x
 | 
			
		||||
        it[y] = location.y
 | 
			
		||||
        it[z] = location.z
 | 
			
		||||
        it[pitch] = location.pitch.toDouble()
 | 
			
		||||
        it[yaw] = location.yaw.toDouble()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall.model
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class HeimdallConfig(
 | 
			
		||||
  val enabled: Boolean = false,
 | 
			
		||||
  val db: DbConfig
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class DbConfig(
 | 
			
		||||
  val url: String,
 | 
			
		||||
  val username: String,
 | 
			
		||||
  val password: String
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,15 @@
 | 
			
		||||
package cloud.kubelet.foundation.heimdall.table
 | 
			
		||||
 | 
			
		||||
import org.jetbrains.exposed.sql.*
 | 
			
		||||
import org.jetbrains.exposed.sql.javatime.timestamp
 | 
			
		||||
 | 
			
		||||
object PlayerPositionTable : Table("player_positions") {
 | 
			
		||||
  val time = timestamp("time")
 | 
			
		||||
  val world = uuid("world")
 | 
			
		||||
  val player = uuid("player")
 | 
			
		||||
  val x = double("x")
 | 
			
		||||
  val y = double("y")
 | 
			
		||||
  val z = double("z")
 | 
			
		||||
  val pitch = double("pitch")
 | 
			
		||||
  val yaw = double("yaw")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								foundation-heimdall/src/main/resources/heimdall.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								foundation-heimdall/src/main/resources/heimdall.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
# Whether Heimdall should be enabled for tracking events.
 | 
			
		||||
enabled: false
 | 
			
		||||
 | 
			
		||||
# Database connection information.
 | 
			
		||||
db:
 | 
			
		||||
  # JDBC URL
 | 
			
		||||
  url: "jdbc:postgresql://localhost/foundation"
 | 
			
		||||
  # JDBC Username
 | 
			
		||||
  username: "foundation"
 | 
			
		||||
  # JDBC Password
 | 
			
		||||
  password: "foundation"
 | 
			
		||||
							
								
								
									
										20
									
								
								foundation-heimdall/src/main/resources/init.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								foundation-heimdall/src/main/resources/init.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
--
 | 
			
		||||
create extension if not exists "uuid-ossp";
 | 
			
		||||
--
 | 
			
		||||
create extension if not exists timescaledb;
 | 
			
		||||
--
 | 
			
		||||
create schema if not exists heimdall;
 | 
			
		||||
--
 | 
			
		||||
create table if not exists heimdall.player_positions (
 | 
			
		||||
    time timestamp not null,
 | 
			
		||||
    player uuid not null,
 | 
			
		||||
    world uuid not null,
 | 
			
		||||
    x double precision not null,
 | 
			
		||||
    y double precision not null,
 | 
			
		||||
    z double precision not null,
 | 
			
		||||
    pitch double precision not null,
 | 
			
		||||
    yaw double precision not null,
 | 
			
		||||
    PRIMARY KEY (time, player, world)
 | 
			
		||||
);
 | 
			
		||||
--
 | 
			
		||||
select create_hypertable('heimdall.player_positions', 'time', 'player', 4,  if_not_exists => TRUE);
 | 
			
		||||
							
								
								
									
										10
									
								
								foundation-heimdall/src/main/resources/plugin.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								foundation-heimdall/src/main/resources/plugin.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
name: Foundation-Heimdall
 | 
			
		||||
version: '${version}'
 | 
			
		||||
main: cloud.kubelet.foundation.heimdall.FoundationHeimdallPlugin
 | 
			
		||||
api-version: 1.18
 | 
			
		||||
prefix: Foundation-Heimdall
 | 
			
		||||
load: STARTUP
 | 
			
		||||
depend:
 | 
			
		||||
  - Foundation
 | 
			
		||||
authors:
 | 
			
		||||
  - kubelet
 | 
			
		||||
@ -3,4 +3,5 @@ rootProject.name = "foundation"
 | 
			
		||||
include(
 | 
			
		||||
  ":foundation-core",
 | 
			
		||||
  ":foundation-bifrost",
 | 
			
		||||
  ":foundation-heimdall",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user