forked from Minecraft/Stonks
Get player names from UUID and include in /players
parent
6cf621f156
commit
5945276b1d
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue