Stonks/app/src/main/kotlin/xyz/etztech/stonks/App.kt

158 lines
5.2 KiB
Kotlin

package xyz.etztech.stonks
import com.natpryce.konfig.*
import java.io.FileInputStream
import java.util.*
import kotlinx.coroutines.*
import kotlinx.serialization.*
import org.h2.tools.Server
import org.jetbrains.exposed.exceptions.ExposedSQLException
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import xyz.etztech.stonks.api.initApiServer
import xyz.etztech.stonks.dsl.AggregateStatistics
import xyz.etztech.stonks.dsl.KeyValue
import xyz.etztech.stonks.dsl.LiveStatistics
import xyz.etztech.stonks.dsl.Players
import xyz.etztech.stonks.dsl.Statistics
import xyz.etztech.stonks.statisticsimporter.StatisticsImporter
fun main() = runBlocking {
println("Starting Stonks...")
val fis = FileInputStream("./stonks.config")
val config = Properties()
config.load(fis)
val databaseBaseDir = config.getProperty("databaseBaseDir")
val databaseName = config.getProperty("databaseName")
val h2StartWebServer = config.getProperty("h2StartWebServer").toBoolean()
val h2tWebServerPort = config.getProperty("h2tWebServerPort").toInt()
val h2TcpServerPort = config.getProperty("h2TcpServerPort").toInt()
val apiServerPort = config.getProperty("apiServerPort").toInt()
val statisticsUpdateInterval = config.getProperty("statisticsUpdateInterval").toLong()
val minecraftStatsFolder = config.getProperty("minecraftStatsFolder")
val database =
initH2Server(
databaseBaseDir,
databaseName,
h2TcpServerPort,
h2StartWebServer,
h2tWebServerPort
)
initApiServer(apiServerPort, database)
initPeriodicFetching(statisticsUpdateInterval, minecraftStatsFolder, database)
delay(60 * 1000L)
println("END")
}
fun initH2Server(
databaseBaseDir: String,
databaseName: String,
h2TcpServerPort: Int,
h2StartWebServer: Boolean,
h2WebServerPort: Int
): Database {
val webServer =
Server.createWebServer(
"-baseDir",
databaseBaseDir,
"-webPort",
h2WebServerPort.toString()
)
val tcpServer =
Server.createTcpServer(
"-trace",
"-baseDir",
databaseBaseDir,
"-webPort",
h2TcpServerPort.toString()
)
if (h2StartWebServer) {
webServer.start()
println("H2 web interface started: ${webServer.getURL()}")
}
tcpServer.start()
println("H2 TCP endpoint started: ${tcpServer.getURL()}")
val database = Database.connect("jdbc:h2:$databaseBaseDir/$databaseName", "org.h2.Driver")
transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.create(Statistics)
SchemaUtils.create(LiveStatistics)
SchemaUtils.create(AggregateStatistics)
SchemaUtils.create(Players)
SchemaUtils.create(KeyValue)
// Create indexes with explicit SQL because I can't figure out how to do it with exposed
// Wrap it in a try block because it throws an exception if index already exists
try {
TransactionManager.current()
.exec("CREATE INDEX idx_playerid ON Statistics (\"PlayerId\")")
} catch (e: ExposedSQLException) {}
try {
TransactionManager.current()
.exec("CREATE INDEX idx_type_name ON Statistics (\"Type\", \"Name\")")
} catch (e: ExposedSQLException) {}
try {
TransactionManager.current()
.exec("CREATE INDEX idx_playerid ON LiveStatistics (\"PlayerId\")")
} catch (e: ExposedSQLException) {}
try {
TransactionManager.current()
.exec("CREATE INDEX idx_type_name ON LiveStatistics (\"Type\", \"Name\")")
} catch (e: ExposedSQLException) {}
val schemaVersions =
KeyValue.slice(KeyValue.value).select { KeyValue.key.eq("SchemaVersion") }.map {
it[KeyValue.value]
}
val schemaVersion = if (schemaVersions.count() > 0) schemaVersions.single() else 0
println("Database schemaVersion = ${schemaVersion}")
if (schemaVersion < 1) {
println("Migrating database to schemaVersion 1.")
TransactionManager.current().exec("TRUNCATE TABLE AGGREGATESTATISTICS")
KeyValue.insert {
it[KeyValue.key] = "SchemaVersion"
it[KeyValue.value] = 1
}
}
if (schemaVersion < 2) {
println("Migrating database to schemaVersion 2.")
TransactionManager.current().exec("TRUNCATE TABLE AGGREGATESTATISTICS")
KeyValue.update({ KeyValue.key eq "SchemaVersion" }) { it[KeyValue.value] = 2 }
}
}
return database
}
suspend fun initPeriodicFetching(interval: Long, folder: String, db: Database) = coroutineScope {
launch {
while (true) {
StatisticsImporter.importStatistics(folder, db)
delay(interval)
}
}
}