Add more caching and fix some models

This commit is contained in:
nea
2023-06-02 23:01:09 +02:00
parent f249df8f67
commit f2eb8c4dc8
3 changed files with 128 additions and 41 deletions

View File

@@ -21,7 +21,7 @@ import moe.nea.firmament.util.json.InstantAsLongSerializer
@Serializable @Serializable
data class Profiles( data class Profiles(
val success: Boolean, val success: Boolean,
val profiles: List<Profile> val profiles: List<Profile>?
) )
@Serializable @Serializable
@@ -120,12 +120,12 @@ data class PlayerResponse(
data class PlayerData( data class PlayerData(
val uuid: UUID, val uuid: UUID,
val firstLogin: Instant, val firstLogin: Instant,
val lastLogin: Instant, val lastLogin: Instant? = null,
@SerialName("playername") @SerialName("playername")
val playerName: String, val playerName: String,
val achievementsOneTime: List<String> = listOf(), val achievementsOneTime: List<String> = listOf(),
@SerialName("newPackageRank") @SerialName("newPackageRank")
val packageRank: String?, val packageRank: String? = null,
val monthlyPackageRank: String? = null, val monthlyPackageRank: String? = null,
val rankPlusColor: String = "GOLD" val rankPlusColor: String = "GOLD"
) { ) {

View File

@@ -0,0 +1,109 @@
package moe.nea.firmament.apis
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.http.URLProtocol
import io.ktor.http.isSuccess
import io.ktor.http.path
import io.ktor.util.CaseInsensitiveMap
import java.util.UUID
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import moe.nea.firmament.Firmament
import moe.nea.firmament.util.MinecraftDispatcher
object Routes {
val apiKey = "e721a103-96e0-400f-af2a-73b2a91007b1"
private val nameToUUID: MutableMap<String, Deferred<UUID?>> = CaseInsensitiveMap()
private val profiles: MutableMap<UUID, Deferred<Profiles?>> = mutableMapOf()
private val accounts: MutableMap<UUID, Deferred<PlayerData?>> = mutableMapOf()
private val UUIDToName: MutableMap<UUID, Deferred<String?>> = mutableMapOf()
suspend fun getPlayerNameForUUID(uuid: UUID): String? {
return withContext(MinecraftDispatcher) {
UUIDToName.computeIfAbsent(uuid) {
async(Firmament.coroutineScope.coroutineContext) {
val response = Firmament.httpClient.get("https://api.ashcon.app/mojang/v2/user/$uuid")
if (!response.status.isSuccess()) return@async null
val data = response.body<AshconNameLookup>()
launch(MinecraftDispatcher) {
nameToUUID[data.username] = async { data.uuid }
}
data.username
}
}
}.await()
}
suspend fun getUUIDForPlayerName(name: String): UUID? {
return withContext(MinecraftDispatcher) {
nameToUUID.computeIfAbsent(name) {
async(Firmament.coroutineScope.coroutineContext) {
val response = Firmament.httpClient.get("https://api.ashcon.app/mojang/v2/user/$name")
if (!response.status.isSuccess()) return@async null
val data = response.body<AshconNameLookup>()
launch(MinecraftDispatcher) {
UUIDToName[data.uuid] = async { data.username }
}
data.uuid
}
}
}.await()
}
suspend fun getAccountData(uuid: UUID): PlayerData? {
return withContext(MinecraftDispatcher) {
accounts.computeIfAbsent(uuid) {
async(Firmament.coroutineScope.coroutineContext) {
val response = Firmament.httpClient.get {
url {
protocol = URLProtocol.HTTPS
host = "api.hypixel.net"
path("player")
parameter("key", apiKey)
parameter("uuid", uuid)
}
}
if (!response.status.isSuccess()) {
launch(MinecraftDispatcher) {
@Suppress("DeferredResultUnused")
accounts.remove(uuid)
}
return@async null
}
response.body<PlayerResponse>().player
}
}
}.await()
}
suspend fun getProfiles(uuid: UUID): Profiles? {
return withContext(MinecraftDispatcher) {
profiles.computeIfAbsent(uuid) {
async(Firmament.coroutineScope.coroutineContext) {
val response = Firmament.httpClient.get {
url {
protocol = URLProtocol.HTTPS
host = "api.hypixel.net"
path("skyblock", "profiles")
parameter("key", apiKey)
parameter("uuid", uuid)
}
}
if (!response.status.isSuccess()) {
launch(MinecraftDispatcher) {
@Suppress("DeferredResultUnused")
profiles.remove(uuid)
}
return@async null
}
response.body<Profiles>()
}
}
}.await()
}
}

View File

@@ -3,22 +3,15 @@ package moe.nea.firmament.gui.profileviewer
import io.github.cottonmc.cotton.gui.client.CottonClientScreen import io.github.cottonmc.cotton.gui.client.CottonClientScreen
import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription
import io.github.cottonmc.cotton.gui.widget.WTabPanel import io.github.cottonmc.cotton.gui.widget.WTabPanel
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.http.URLProtocol
import io.ktor.http.path
import java.util.UUID import java.util.UUID
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.apis.AshconNameLookup
import moe.nea.firmament.apis.Member import moe.nea.firmament.apis.Member
import moe.nea.firmament.apis.PlayerData import moe.nea.firmament.apis.PlayerData
import moe.nea.firmament.apis.PlayerResponse
import moe.nea.firmament.apis.Profile import moe.nea.firmament.apis.Profile
import moe.nea.firmament.apis.Profiles import moe.nea.firmament.apis.Routes
import moe.nea.firmament.util.ScreenUtil import moe.nea.firmament.util.ScreenUtil
class ProfileViewer( class ProfileViewer(
@@ -48,40 +41,25 @@ class ProfileViewer(
fun onCommand(source: FabricClientCommandSource, name: String) { fun onCommand(source: FabricClientCommandSource, name: String) {
source.sendFeedback(Text.translatable("firmament.pv.lookingup", name)) source.sendFeedback(Text.translatable("firmament.pv.lookingup", name))
Firmament.coroutineScope.launch { Firmament.coroutineScope.launch {
val nameData = Firmament.httpClient.get("https://api.ashcon.app/mojang/v2/user/$name").body<AshconNameLookup>() val uuid = Routes.getUUIDForPlayerName(name)
val names = mapOf(nameData.uuid to nameData.username) if (uuid == null) {
val data = Firmament.httpClient.get { source.sendError(Text.translatable("firmament.pv.noplayer", name))
url { return@launch
protocol = URLProtocol.HTTPS }
host = "api.hypixel.net" val names = mapOf(uuid to (Routes.getPlayerNameForUUID(uuid) ?: name))
path("player") val data = Routes.getAccountData(uuid)
parameter("key", "e721a103-96e0-400f-af2a-73b2a91007b1") if (data == null) {
parameter("uuid", nameData.uuid) source.sendError(Text.translatable("firmament.pv.nohypixel", name))
} return@launch
}.body<PlayerResponse>() }
val accountData = mapOf(data.player.uuid to data.player) val accountData = mapOf(data.uuid to data)
val playerUuid = data.player.uuid val profiles = Routes.getProfiles(uuid)
val profiles = Firmament.httpClient.get { val profile = profiles?.profiles?.find { it.selected }
url {
protocol = URLProtocol.HTTPS
host = "api.hypixel.net"
path("skyblock", "profiles")
parameter("key", "e721a103-96e0-400f-af2a-73b2a91007b1")
parameter("uuid", playerUuid)
}
}.body<Profiles>()
val profile = profiles.profiles.find { it.selected }
if (profile == null) { if (profile == null) {
source.sendFeedback(Text.translatable("firmament.pv.noprofile", name)) source.sendFeedback(Text.translatable("firmament.pv.noprofile", name))
return@launch return@launch
} }
ScreenUtil.setScreenLater( ScreenUtil.setScreenLater(CottonClientScreen(ProfileViewer(uuid, names, accountData, profile)))
CottonClientScreen(
ProfileViewer(
playerUuid, names, accountData, profile
)
)
)
} }
} }
} }