Get player names from UUID and include in /players

main
Kevin Belisle 2021-07-02 01:04:26 -04:00
parent 6cf621f156
commit 5945276b1d
5 changed files with 122 additions and 15 deletions

View File

@ -5,10 +5,7 @@
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
* User Manual available at https://docs.gradle.org/7.1/userguide/building_java_projects.html
*/
// compileOptions {
// sourceCompatibility(JavaVersion.VERSION_1_8)
// targetCompatibility(JavaVersion.VERSION_1_8)
// }
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
@ -60,6 +57,13 @@ dependencies {
implementation("com.beust:klaxon:5.5")
implementation("com.natpryce:konfig:1.6.10.0")
implementation("io.ktor:ktor-client-core:1.6.1")
implementation("io.ktor:ktor-client-java:1.6.1")
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
application {

View File

@ -9,6 +9,7 @@ import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.*
import xyz.etztech.stonks.dsl.LiveStatistics
import xyz.etztech.stonks.dsl.Players
import xyz.etztech.stonks.dsl.Statistics
private var statisticsCache = ""
@ -86,10 +87,11 @@ fun initApiServer(apiServerPort: Int, database: Database) {
addLogger(StdOutSqlLogger)
val players =
LiveStatistics.slice(LiveStatistics.playerId)
LiveStatistics.leftJoin(Players, { playerId }, { id })
.slice(LiveStatistics.playerId, Players.name)
.selectAll()
.groupBy(LiveStatistics.playerId)
.map { it[LiveStatistics.playerId] }
.map { Player(it[LiveStatistics.playerId], it[Players.name]) }
playersCache = Json.encodeToString(players)
}
}
@ -159,6 +161,8 @@ fun initApiServer(apiServerPort: Int, database: Database) {
@Serializable data class Statistic(val type: String, val name: String)
@Serializable data class Player(val id: String, val name: String?)
@Serializable
data class StatisticValue(
val playerId: String,

View File

@ -11,6 +11,7 @@ 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.LiveStatistics
import xyz.etztech.stonks.dsl.Players
import xyz.etztech.stonks.dsl.Statistics
import xyz.etztech.stonks.statisticsimporter.StatisticsImporter
@ -78,6 +79,7 @@ fun initH2Server(
addLogger(StdOutSqlLogger)
SchemaUtils.create(Statistics)
SchemaUtils.create(LiveStatistics)
SchemaUtils.create(Players)
// 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

View File

@ -24,3 +24,11 @@ object Statistics : Table() {
override val primaryKey =
PrimaryKey(playerId, type, name, timestamp, name = "PK_playerId_type_name_timestamp")
}
object Players : Table() {
val id: Column<String> = varchar("Id", 150)
val name: Column<String> = varchar("Name", 150)
val timestamp: Column<Instant> = timestamp("Timestamp")
override val primaryKey = PrimaryKey(id, name = "PK_id")
}

View File

@ -2,17 +2,27 @@ package xyz.etztech.stonks.statisticsimporter
import com.beust.klaxon.*
import com.beust.klaxon.Klaxon
import io.ktor.client.*
import io.ktor.client.engine.java.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import java.io.File
import java.time.Duration
import java.time.Instant
import kotlinx.coroutines.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.`java-time`.timestamp
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import xyz.etztech.stonks.dsl.LiveStatistics
import xyz.etztech.stonks.dsl.Players
import xyz.etztech.stonks.dsl.Statistics
object StatisticsImporter {
init {}
private val httpClient = HttpClient(Java)
private val klaxon = Klaxon()
fun importStatistics(folder: String, database: Database) {
@ -20,6 +30,8 @@ object StatisticsImporter {
File(folder).listFiles().forEach { readFile(it, database) }
println("Updating live statistics table...")
updateLiveStatistics(database)
println("Refreshing player names...")
refreshPlayerNames(database)
}
fun readFile(file: File, database: Database) {
@ -27,21 +39,22 @@ object StatisticsImporter {
val playerId = file.nameWithoutExtension
transaction(database) {
val maxExpr = Statistics.value.max()
addLogger(StdOutSqlLogger)
val playerStats = emptyMap<String, MutableMap<String, Long>>().toMutableMap()
Statistics.slice(Statistics.type, Statistics.name, maxExpr)
.select { Statistics.playerId.eq(playerId) }
.groupBy(Statistics.type, Statistics.name)
LiveStatistics.slice(LiveStatistics.type, LiveStatistics.name, LiveStatistics.value)
.select { LiveStatistics.playerId eq playerId }
.forEach {
if (playerStats.containsKey(it[Statistics.type])) {
playerStats[it[Statistics.type]]?.put(
it[Statistics.name], it[maxExpr]!!)
if (playerStats.containsKey(it[LiveStatistics.type])) {
playerStats[it[LiveStatistics.type]]?.put(
it[LiveStatistics.name], it[LiveStatistics.value])
} else {
playerStats.put(
it[Statistics.type],
mapOf<String, Long>(it[Statistics.name] to it[maxExpr]!!)
it[LiveStatistics.type],
mapOf<String, Long>(
it[LiveStatistics.name] to
it[LiveStatistics.value])
.toMutableMap())
}
}
@ -104,9 +117,85 @@ object StatisticsImporter {
}
}
fun refreshPlayerNames(database: Database) {
transaction(database) {
addLogger(StdOutSqlLogger)
val savedPlayers =
Players.selectAll().map {
object {
val id = it[Players.id]
val timestamp = it[Players.timestamp]
}
}
val players =
LiveStatistics.slice(LiveStatistics.playerId)
.selectAll()
.groupBy(LiveStatistics.playerId)
.map { it[LiveStatistics.playerId] }
savedPlayers
.filter { Duration.between(Instant.now(), it.timestamp) > Duration.ofDays(1) }
.forEach { player ->
runBlocking {
val name = getName(player.id)
if (name != null) {
println("Updating ${player.id} -> $name")
Players.update({ Players.id eq player.id }) {
it[Players.name] = name
it[Players.timestamp] = Instant.now()
}
} else {
println("Error updating ${player.id}")
}
}
}
players
.filterNot { player ->
savedPlayers.any { savedPlayer -> savedPlayer.id == player }
}
.forEach { playerId ->
runBlocking {
val name = getName(playerId)
if (name != null) {
println("Updating $playerId -> $name")
Players.insert {
it[Players.id] = playerId
it[Players.name] = name
it[Players.timestamp] = Instant.now()
}
} else {
println("Error updating $playerId")
}
}
}
}
}
suspend fun getName(id: String): String? {
val response: HttpResponse =
httpClient.request(
"https://sessionserver.mojang.com/session/minecraft/profile/${id.replace("-", "")}") {
method = HttpMethod.Get
}
if (response.status.isSuccess()) {
try {
return MojangProfileResponse.fromJson(response.readText())?.name
} catch (e: Exception) {}
}
return null
}
data class StatsFile(val stats: Map<String, Map<String, Long>>) {
companion object {
public fun fromJson(json: String) = klaxon.parse<StatsFile>(json)
}
}
data class MojangProfileResponse(val name: String) {
companion object {
public fun fromJson(json: String) = klaxon.parse<MojangProfileResponse>(json)
}
}
}