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
|
* 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
|
* User Manual available at https://docs.gradle.org/7.1/userguide/building_java_projects.html
|
||||||
*/
|
*/
|
||||||
// compileOptions {
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
// sourceCompatibility(JavaVersion.VERSION_1_8)
|
|
||||||
// targetCompatibility(JavaVersion.VERSION_1_8)
|
|
||||||
// }
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
|
// 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.beust:klaxon:5.5")
|
||||||
|
|
||||||
implementation("com.natpryce:konfig:1.6.10.0")
|
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 {
|
application {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import kotlinx.serialization.json.Json
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.transactions.*
|
import org.jetbrains.exposed.sql.transactions.*
|
||||||
import xyz.etztech.stonks.dsl.LiveStatistics
|
import xyz.etztech.stonks.dsl.LiveStatistics
|
||||||
|
import xyz.etztech.stonks.dsl.Players
|
||||||
import xyz.etztech.stonks.dsl.Statistics
|
import xyz.etztech.stonks.dsl.Statistics
|
||||||
|
|
||||||
private var statisticsCache = ""
|
private var statisticsCache = ""
|
||||||
|
@ -86,10 +87,11 @@ fun initApiServer(apiServerPort: Int, database: Database) {
|
||||||
addLogger(StdOutSqlLogger)
|
addLogger(StdOutSqlLogger)
|
||||||
|
|
||||||
val players =
|
val players =
|
||||||
LiveStatistics.slice(LiveStatistics.playerId)
|
LiveStatistics.leftJoin(Players, { playerId }, { id })
|
||||||
|
.slice(LiveStatistics.playerId, Players.name)
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.groupBy(LiveStatistics.playerId)
|
.groupBy(LiveStatistics.playerId)
|
||||||
.map { it[LiveStatistics.playerId] }
|
.map { Player(it[LiveStatistics.playerId], it[Players.name]) }
|
||||||
playersCache = Json.encodeToString(players)
|
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 Statistic(val type: String, val name: String)
|
||||||
|
|
||||||
|
@Serializable data class Player(val id: String, val name: String?)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class StatisticValue(
|
data class StatisticValue(
|
||||||
val playerId: String,
|
val playerId: String,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import xyz.etztech.stonks.api.initApiServer
|
import xyz.etztech.stonks.api.initApiServer
|
||||||
import xyz.etztech.stonks.dsl.LiveStatistics
|
import xyz.etztech.stonks.dsl.LiveStatistics
|
||||||
|
import xyz.etztech.stonks.dsl.Players
|
||||||
import xyz.etztech.stonks.dsl.Statistics
|
import xyz.etztech.stonks.dsl.Statistics
|
||||||
import xyz.etztech.stonks.statisticsimporter.StatisticsImporter
|
import xyz.etztech.stonks.statisticsimporter.StatisticsImporter
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ fun initH2Server(
|
||||||
addLogger(StdOutSqlLogger)
|
addLogger(StdOutSqlLogger)
|
||||||
SchemaUtils.create(Statistics)
|
SchemaUtils.create(Statistics)
|
||||||
SchemaUtils.create(LiveStatistics)
|
SchemaUtils.create(LiveStatistics)
|
||||||
|
SchemaUtils.create(Players)
|
||||||
|
|
||||||
// Create indexes with explicit SQL because I can't figure out how to do it with exposed
|
// 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
|
// 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 =
|
override val primaryKey =
|
||||||
PrimaryKey(playerId, type, name, timestamp, name = "PK_playerId_type_name_timestamp")
|
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.*
|
||||||
import com.beust.klaxon.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.io.File
|
||||||
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.`java-time`.timestamp
|
import org.jetbrains.exposed.sql.`java-time`.timestamp
|
||||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
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
|
import xyz.etztech.stonks.dsl.Statistics
|
||||||
|
|
||||||
object StatisticsImporter {
|
object StatisticsImporter {
|
||||||
init {}
|
init {}
|
||||||
|
|
||||||
|
private val httpClient = HttpClient(Java)
|
||||||
private val klaxon = Klaxon()
|
private val klaxon = Klaxon()
|
||||||
|
|
||||||
fun importStatistics(folder: String, database: Database) {
|
fun importStatistics(folder: String, database: Database) {
|
||||||
|
@ -20,6 +30,8 @@ object StatisticsImporter {
|
||||||
File(folder).listFiles().forEach { readFile(it, database) }
|
File(folder).listFiles().forEach { readFile(it, database) }
|
||||||
println("Updating live statistics table...")
|
println("Updating live statistics table...")
|
||||||
updateLiveStatistics(database)
|
updateLiveStatistics(database)
|
||||||
|
println("Refreshing player names...")
|
||||||
|
refreshPlayerNames(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readFile(file: File, database: Database) {
|
fun readFile(file: File, database: Database) {
|
||||||
|
@ -27,21 +39,22 @@ object StatisticsImporter {
|
||||||
val playerId = file.nameWithoutExtension
|
val playerId = file.nameWithoutExtension
|
||||||
|
|
||||||
transaction(database) {
|
transaction(database) {
|
||||||
val maxExpr = Statistics.value.max()
|
addLogger(StdOutSqlLogger)
|
||||||
|
|
||||||
val playerStats = emptyMap<String, MutableMap<String, Long>>().toMutableMap()
|
val playerStats = emptyMap<String, MutableMap<String, Long>>().toMutableMap()
|
||||||
|
|
||||||
Statistics.slice(Statistics.type, Statistics.name, maxExpr)
|
LiveStatistics.slice(LiveStatistics.type, LiveStatistics.name, LiveStatistics.value)
|
||||||
.select { Statistics.playerId.eq(playerId) }
|
.select { LiveStatistics.playerId eq playerId }
|
||||||
.groupBy(Statistics.type, Statistics.name)
|
|
||||||
.forEach {
|
.forEach {
|
||||||
if (playerStats.containsKey(it[Statistics.type])) {
|
if (playerStats.containsKey(it[LiveStatistics.type])) {
|
||||||
playerStats[it[Statistics.type]]?.put(
|
playerStats[it[LiveStatistics.type]]?.put(
|
||||||
it[Statistics.name], it[maxExpr]!!)
|
it[LiveStatistics.name], it[LiveStatistics.value])
|
||||||
} else {
|
} else {
|
||||||
playerStats.put(
|
playerStats.put(
|
||||||
it[Statistics.type],
|
it[LiveStatistics.type],
|
||||||
mapOf<String, Long>(it[Statistics.name] to it[maxExpr]!!)
|
mapOf<String, Long>(
|
||||||
|
it[LiveStatistics.name] to
|
||||||
|
it[LiveStatistics.value])
|
||||||
.toMutableMap())
|
.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>>) {
|
data class StatsFile(val stats: Map<String, Map<String, Long>>) {
|
||||||
companion object {
|
companion object {
|
||||||
public fun fromJson(json: String) = klaxon.parse<StatsFile>(json)
|
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