From 8c5570bfe6ab93855e24e6924fac1105fe8342ff Mon Sep 17 00:00:00 2001 From: nea Date: Tue, 15 Aug 2023 19:34:56 +0200 Subject: Add ursa client --- .../kotlin/moe/nea/firmament/apis/UrsaManager.kt | 73 ++++++++++++++++++++++ src/main/kotlin/moe/nea/firmament/commands/dsl.kt | 9 ++- src/main/kotlin/moe/nea/firmament/commands/rome.kt | 11 ++++ .../moe/nea/firmament/util/MinecraftDispatcher.kt | 20 +----- .../resources/assets/firmament/lang/en_us.json | 2 + 5 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/moe/nea/firmament/apis/UrsaManager.kt diff --git a/src/main/kotlin/moe/nea/firmament/apis/UrsaManager.kt b/src/main/kotlin/moe/nea/firmament/apis/UrsaManager.kt new file mode 100644 index 0000000..a72d633 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/apis/UrsaManager.kt @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.apis + +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import java.time.Duration +import java.time.Instant +import java.util.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.withContext +import net.minecraft.client.MinecraftClient +import moe.nea.firmament.Firmament + +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 var currentToken: Token? = null + private val lock = Mutex() + private fun getToken(host: String) = currentToken?.takeIf { it.isValid(host) } + + suspend fun request(path: List): 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.profile, mc.session.accessToken, serverId) + header("x-ursa-username", mc.session.profile.name) + 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) + } + return response + } finally { + if (didLock) + lock.unlock() + } + } +} diff --git a/src/main/kotlin/moe/nea/firmament/commands/dsl.kt b/src/main/kotlin/moe/nea/firmament/commands/dsl.kt index 01a3458..d6eaf85 100644 --- a/src/main/kotlin/moe/nea/firmament/commands/dsl.kt +++ b/src/main/kotlin/moe/nea/firmament/commands/dsl.kt @@ -16,6 +16,9 @@ import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.lang.reflect.TypeVariable import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource +import kotlinx.coroutines.launch +import moe.nea.firmament.Firmament +import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.iterate @@ -105,9 +108,11 @@ fun > T.thenLiteral( fun > T.then(node: ArgumentBuilder, block: T.() -> Unit): T = then(node).also(block) -fun > T.thenExecute(block: CommandContext.() -> Unit): T = +fun > T.thenExecute(block: suspend CommandContext.() -> Unit): T = executes { - block(it) + Firmament.coroutineScope.launch(MinecraftDispatcher) { + block(it) + } 1 } diff --git a/src/main/kotlin/moe/nea/firmament/commands/rome.kt b/src/main/kotlin/moe/nea/firmament/commands/rome.kt index 4465459..221c7c1 100644 --- a/src/main/kotlin/moe/nea/firmament/commands/rome.kt +++ b/src/main/kotlin/moe/nea/firmament/commands/rome.kt @@ -8,8 +8,10 @@ package moe.nea.firmament.commands import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.arguments.StringArgumentType.string +import io.ktor.client.statement.* import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource import net.minecraft.text.Text +import moe.nea.firmament.apis.UrsaManager import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlayScreen import moe.nea.firmament.features.world.FairySouls import moe.nea.firmament.gui.config.AllConfigsGui @@ -135,6 +137,15 @@ fun firmamentCommand() = literal("firmament") { } } } + thenLiteral("callUrsa") { + thenArgument("path", string()) { path -> + thenExecute { + source.sendFeedback(Text.translatable("firmament.ursa.debugrequest.start")) + val text = UrsaManager.request(this[path].split("/")).bodyAsText() + source.sendFeedback(Text.translatable("firmament.ursa.debugrequest.result", text)) + } + } + } } } diff --git a/src/main/kotlin/moe/nea/firmament/util/MinecraftDispatcher.kt b/src/main/kotlin/moe/nea/firmament/util/MinecraftDispatcher.kt index 29ca6c2..5f94f41 100644 --- a/src/main/kotlin/moe/nea/firmament/util/MinecraftDispatcher.kt +++ b/src/main/kotlin/moe/nea/firmament/util/MinecraftDispatcher.kt @@ -6,23 +6,7 @@ package moe.nea.firmament.util -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.Runnable -import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.asCoroutineDispatcher import net.minecraft.client.MinecraftClient -object MinecraftDispatcher : CoroutineDispatcher() { - @ExperimentalCoroutinesApi - override fun limitedParallelism(parallelism: Int): CoroutineDispatcher { - throw UnsupportedOperationException("limitedParallelism is not supported for MinecraftDispatcher") - } - - override fun isDispatchNeeded(context: CoroutineContext): Boolean = - !MinecraftClient.getInstance().isOnThread - - - override fun dispatch(context: CoroutineContext, block: Runnable) { - MinecraftClient.getInstance().execute(block) - } -} +val MinecraftDispatcher by lazy { MinecraftClient.getInstance().asCoroutineDispatcher() } diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json index ee6a23b..5835699 100644 --- a/src/main/resources/assets/firmament/lang/en_us.json +++ b/src/main/resources/assets/firmament/lang/en_us.json @@ -28,6 +28,8 @@ "firmament.config.repo.branch": "Repo Branch", "firmament.config.repo.branch.hint": "dangerous", "firmament.config.repo.reset": "Reset", + "firmament.ursa.debugrequest.start": "Ursa request launched", + "firmament.ursa.debugrequest.result": "Ursa request succeeded: %s", "firmament.sbinfo.nolocraw": "No locraw data available", "firmament.sbinfo.profile": "Current profile cutename: %s", "firmament.sbinfo.server": "Locraw Server: %s", -- cgit