diff options
| author | Linnea Gräf <nea@nea.moe> | 2025-10-13 22:10:38 +0200 |
|---|---|---|
| committer | Linnea Gräf <nea@nea.moe> | 2025-10-13 22:10:38 +0200 |
| commit | 733f01be8c2ca986e594816e73cb89ee1c8d105d (patch) | |
| tree | 7709f194f714b0bcfdbab0c65ec5aa7b3fe49c14 /src/main/kotlin/apis | |
| parent | 05160314e6899ece75779dbd2e5b691ed581c2b9 (diff) | |
| download | Firmament-733f01be8c2ca986e594816e73cb89ee1c8d105d.tar.gz Firmament-733f01be8c2ca986e594816e73cb89ee1c8d105d.tar.bz2 Firmament-733f01be8c2ca986e594816e73cb89ee1c8d105d.zip | |
feat: remove ktor (for a smaller binary)
Diffstat (limited to 'src/main/kotlin/apis')
| -rw-r--r-- | src/main/kotlin/apis/Routes.kt | 121 | ||||
| -rw-r--r-- | src/main/kotlin/apis/UrsaManager.kt | 124 |
2 files changed, 107 insertions, 138 deletions
diff --git a/src/main/kotlin/apis/Routes.kt b/src/main/kotlin/apis/Routes.kt index 737763d..839de22 100644 --- a/src/main/kotlin/apis/Routes.kt +++ b/src/main/kotlin/apis/Routes.kt @@ -1,91 +1,54 @@ - - package moe.nea.firmament.apis -import io.ktor.client.call.body -import io.ktor.client.request.get -import io.ktor.http.isSuccess -import io.ktor.util.CaseInsensitiveMap import java.util.UUID import kotlinx.coroutines.Deferred import kotlinx.coroutines.async +import kotlinx.coroutines.future.await import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import moe.nea.firmament.Firmament +import moe.nea.firmament.util.ErrorUtil import moe.nea.firmament.util.MinecraftDispatcher +import moe.nea.firmament.util.net.HttpUtil object Routes { - 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://mowojang.matdoes.dev/$uuid") - if (!response.status.isSuccess()) return@async null - val data = response.body<MowojangNameLookup>() - launch(MinecraftDispatcher) { - nameToUUID[data.name] = async { data.id } - } - data.name - } - } - }.await() - } - - suspend fun getUUIDForPlayerName(name: String): UUID? { - return withContext(MinecraftDispatcher) { - nameToUUID.computeIfAbsent(name) { - async(Firmament.coroutineScope.coroutineContext) { - val response = Firmament.httpClient.get("https://mowojang.matdoes.dev/$name") - if (!response.status.isSuccess()) return@async null - val data = response.body<MowojangNameLookup>() - launch(MinecraftDispatcher) { - UUIDToName[data.id] = async { data.name } - } - data.id - } - } - }.await() - } - - suspend fun getAccountData(uuid: UUID): PlayerData? { - return withContext(MinecraftDispatcher) { - accounts.computeIfAbsent(uuid) { - async(Firmament.coroutineScope.coroutineContext) { - val response = UrsaManager.request(listOf("v1", "hypixel","player", uuid.toString())) - 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 = UrsaManager.request(listOf("v1", "hypixel","profiles", uuid.toString())) - if (!response.status.isSuccess()) { - launch(MinecraftDispatcher) { - @Suppress("DeferredResultUnused") - profiles.remove(uuid) - } - return@async null - } - response.body<Profiles>() - } - } - }.await() - } - + private val nameToUUID: MutableMap<String, Deferred<UUID?>> = 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 data = ErrorUtil.catch("could not get name for uuid $uuid") { + HttpUtil.request("https://mowojang.matdoes.dev/$uuid") + .forJson<MowojangNameLookup>() + .await() + }.orNull() ?: return@async null + launch(MinecraftDispatcher) { + nameToUUID[data.name] = async { data.id } + } + data.name + } + } + }.await() + } + + suspend fun getUUIDForPlayerName(name: String): UUID? { + return withContext(MinecraftDispatcher) { + nameToUUID.computeIfAbsent(name) { + async(Firmament.coroutineScope.coroutineContext) { + val data = + ErrorUtil.catch("could not get uuid for name $name") { + HttpUtil.request("https://mowojang.matdoes.dev/$name") + .forJson<MowojangNameLookup>() + .await() + }.orNull() ?: return@async null + launch(MinecraftDispatcher) { + UUIDToName[data.id] = async { data.name } + } + data.id + } + } + }.await() + } } diff --git a/src/main/kotlin/apis/UrsaManager.kt b/src/main/kotlin/apis/UrsaManager.kt index 19e030c..cee6904 100644 --- a/src/main/kotlin/apis/UrsaManager.kt +++ b/src/main/kotlin/apis/UrsaManager.kt @@ -1,74 +1,80 @@ - - package moe.nea.firmament.apis -import io.ktor.client.request.get -import io.ktor.client.request.header -import io.ktor.client.statement.HttpResponse -import io.ktor.client.statement.bodyAsText -import io.ktor.http.appendPathSegments +import java.net.URI +import java.net.http.HttpResponse import java.time.Duration import java.time.Instant +import java.util.OptionalLong import java.util.UUID import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.future.await import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.withContext +import kotlinx.serialization.DeserializationStrategy +import kotlin.jvm.optionals.getOrNull import net.minecraft.client.MinecraftClient import moe.nea.firmament.Firmament +import moe.nea.firmament.util.net.HttpUtil object UrsaManager { - private data class Token( - val validUntil: Instant, - val token: String, - val obtainedFrom: String, - ) { - fun isValid(host: String) = Instant.now().plusSeconds(60) < validUntil && obtainedFrom == host - } + private data class Token( + val validUntil: Instant, + val token: String, + val obtainedFrom: String, + ) { + fun isValid(host: String) = Instant.now().plusSeconds(60) < validUntil && obtainedFrom == host + } - private var currentToken: Token? = null - private val lock = Mutex() - private fun getToken(host: String) = currentToken?.takeIf { it.isValid(host) } + private var currentToken: Token? = null + private val lock = Mutex() + private fun getToken(host: String) = currentToken?.takeIf { it.isValid(host) } + + suspend fun <T> request(path: List<String>, bodyHandler: HttpResponse.BodyHandler<T>): T { + var didLock = false + try { + val host = "ursa.notenoughupdates.org" + var token = getToken(host) + if (token == null) { + lock.lock() + didLock = true + token = getToken(host) + } + var url = URI.create("https://$host") + for (segment in path) { + url = url.resolve(segment) + } + val request = HttpUtil.request(url) + if (token == null) { + withContext(Dispatchers.IO) { + val mc = MinecraftClient.getInstance() + val serverId = UUID.randomUUID().toString() + mc.sessionService.joinServer(mc.session.uuidOrNull, mc.session.accessToken, serverId) + request.header("x-ursa-username", mc.session.username) + request.header("x-ursa-serverid", serverId) + } + } else { + request.header("x-ursa-token", token.token) + } + val response = request.execute(bodyHandler) + .await() + val savedToken = response.headers().firstValue("x-ursa-token").getOrNull() + if (savedToken != null) { + val validUntil = response.headers().firstValueAsLong("x-ursa-expires").orNull()?.let { Instant.ofEpochMilli(it) } + ?: (Instant.now() + Duration.ofMinutes(55)) + currentToken = Token(validUntil, savedToken, host) + } + if (response.statusCode() != 200) { + Firmament.logger.error("Failed to contact ursa minor: ${response.statusCode()}") + } + return response.body() + } finally { + if (didLock) + lock.unlock() + } + } +} - suspend fun request(path: List<String>): HttpResponse { - var didLock = false - try { - val host = "ursa.notenoughupdates.org" - var token = getToken(host) - if (token == null) { - lock.lock() - didLock = true - token = getToken(host) - } - val response = Firmament.httpClient.get { - url { - this.host = host - appendPathSegments(path, encodeSlash = true) - } - if (token == null) { - withContext(Dispatchers.IO) { - val mc = MinecraftClient.getInstance() - val serverId = UUID.randomUUID().toString() - mc.sessionService.joinServer(mc.session.uuidOrNull, mc.session.accessToken, serverId) - header("x-ursa-username", mc.session.username) - header("x-ursa-serverid", serverId) - } - } else { - header("x-ursa-token", token.token) - } - } - val savedToken = response.headers["x-ursa-token"] - if (savedToken != null) { - val validUntil = response.headers["x-ursa-expires"]?.toLongOrNull()?.let { Instant.ofEpochMilli(it) } - ?: (Instant.now() + Duration.ofMinutes(55)) - currentToken = Token(validUntil, savedToken, host) - } - if (response.status.value != 200) { - Firmament.logger.error("Failed to contact ursa minor: ${response.bodyAsText()}") - } - return response - } finally { - if (didLock) - lock.unlock() - } - } +private fun OptionalLong.orNull(): Long? { + if (this.isPresent)return null + return this.asLong } |
