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