diff --git a/fabric.gradle.kts b/fabric.gradle.kts index 7346ace..d8d189b 100644 --- a/fabric.gradle.kts +++ b/fabric.gradle.kts @@ -127,6 +127,12 @@ tasks.jar { } } +val serializationProvidedByFlk = setOf( + "org.jetbrains.kotlinx:kotlinx-serialization-core", + "org.jetbrains.kotlinx:kotlinx-serialization-json", + "org.jetbrains.kotlinx:kotlinx-serialization-cbor", +) + dependencies { minecraft("com.mojang:minecraft:${versionCatalog("common$catalogVersion").findVersion("minecraft").get()}") @@ -139,6 +145,7 @@ dependencies { catalogLib("fabric-api")?.let { implementation(it) { isTransitive = true } } catalogLib("fabric-loader")?.let { implementation(it) { isTransitive = true } } + catalogLib("fabric-language-kotlin")?.let { implementation(it) { isTransitive = false } } catalogLib("sodium")?.let { compileOnly(it) { isTransitive = false } } @@ -149,6 +156,7 @@ dependencies { sentryShade(libs.sentry) implementation(files(relocateSentry.flatMap { it.archiveFile })) + implementation(libs.bundles.serialization) implementation(libs.bundles.ktor.client) implementation(libs.bundles.ktor.server) implementation(libs.bundles.ktor.serialization) @@ -160,15 +168,19 @@ dependencies { run { val bundledRoots = libs.bundles.ktor.client.get() + libs.bundles.ktor.server.get() + - libs.bundles.ktor.serialization.get() + libs.bundles.ktor.serialization.get() + + libs.bundles.serialization.get() val closure = configurations.detachedConfiguration( *bundledRoots.map { dependencies.create(it) }.toTypedArray() ) + + fun key(group: String?, name: String) = "$group:${name.removeSuffix("-jvm")}" + closure.resolvedConfiguration.resolvedArtifacts.forEach { art -> val id = art.moduleVersion.id - if (id.group != "org.jetbrains.kotlin") { - dependencies.include("${id.group}:${id.name}:${id.version}") - } + if (id.group == "org.jetbrains.kotlin") return@forEach + if (key(id.group, id.name) in serializationProvidedByFlk) return@forEach + dependencies.include("${id.group}:${id.name}:${id.version}") } } diff --git a/fabric.obf.gradle.kts b/fabric.obf.gradle.kts index 8bf5c2f..ed02a60 100644 --- a/fabric.obf.gradle.kts +++ b/fabric.obf.gradle.kts @@ -128,6 +128,12 @@ tasks.jar { } } +val serializationProvidedByFlk = setOf( + "org.jetbrains.kotlinx:kotlinx-serialization-core", + "org.jetbrains.kotlinx:kotlinx-serialization-json", + "org.jetbrains.kotlinx:kotlinx-serialization-cbor", +) + dependencies { minecraft("com.mojang:minecraft:${versionCatalog("common$catalogVersion").findVersion("minecraft").get()}") @@ -147,6 +153,7 @@ dependencies { catalogLib("fabric-api")?.let { modImplementation(it) { isTransitive = true } } catalogLib("fabric-loader")?.let { modImplementation(it) { isTransitive = true } } + catalogLib("fabric-language-kotlin")?.let { modImplementation(it) { isTransitive = false } } catalogLib("sodium")?.let { modCompileOnly(it) { isTransitive = false } } @@ -157,6 +164,7 @@ dependencies { sentryShade(libs.sentry) implementation(files(relocateSentry.flatMap { it.archiveFile })) + implementation(libs.bundles.serialization) implementation(libs.bundles.ktor.client) implementation(libs.bundles.ktor.server) implementation(libs.bundles.ktor.serialization) @@ -168,15 +176,19 @@ dependencies { run { val bundledRoots = libs.bundles.ktor.client.get() + libs.bundles.ktor.server.get() + - libs.bundles.ktor.serialization.get() + libs.bundles.ktor.serialization.get() + + libs.bundles.serialization.get() val closure = configurations.detachedConfiguration( *bundledRoots.map { dependencies.create(it) }.toTypedArray() ) + + fun key(group: String?, name: String) = "$group:${name.removeSuffix("-jvm")}" + closure.resolvedConfiguration.resolvedArtifacts.forEach { art -> val id = art.moduleVersion.id - if (id.group != "org.jetbrains.kotlin") { - dependencies.include("${id.group}:${id.name}:${id.version}") - } + if (id.group == "org.jetbrains.kotlin") return@forEach + if (key(id.group, id.name) in serializationProvidedByFlk) return@forEach + dependencies.include("${id.group}:${id.name}:${id.version}") } } diff --git a/gradle/fabric.versions.toml b/gradle/fabric.versions.toml index 99ce49f..4b5d045 100644 --- a/gradle/fabric.versions.toml +++ b/gradle/fabric.versions.toml @@ -1,10 +1,12 @@ [versions] loom = "1.16-SNAPSHOT" loader = "0.19.2" +language-kotlin = "1.13.13+kotlin.2.4.10" [libraries] fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "loader" } fabric-loader-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "loader" } +fabric-language-kotlin = { module = "net.fabricmc:fabric-language-kotlin", version.ref = "language-kotlin" } [plugins] loom-remap = {id = "net.fabricmc.fabric-loom-remap", version.ref = "loom"} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2cd10f9..9b1aa9a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] kotlin = "2.3.0" kotlinx-serialization = "2.3.0" +kotlinx-serialization-runtime = "1.11.0" atomicfu = "0.27.0" ktor = "3.3.1" sentry = "7.18.0" @@ -22,6 +23,10 @@ ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-conte ktor-server-websockets = { group = "io.ktor", name = "ktor-server-websockets", version.ref = "ktor" } ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } +kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core-jvm", version.ref = "kotlinx-serialization-runtime" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json-jvm", version.ref = "kotlinx-serialization-runtime" } +kotlinx-serialization-json-io = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json-io-jvm", version.ref = "kotlinx-serialization-runtime" } + mixin-extras = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixin-extras" } mixin-squared = { module = "com.github.bawnorton.mixinsquared:mixinsquared-common", version.ref = "mixin-squared" } polymixin = { module = "org.polyfrost:polymixin", version.ref = "polymixin" } @@ -34,6 +39,11 @@ junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "jun ktor-client = ["ktor-client-core", "ktor-client-cio", "ktor-client-content-negotiation"] ktor-server = ["ktor-server-websockets"] ktor-serialization = ["ktor-serialization-kotlinx-json"] +serialization = [ + "kotlinx-serialization-core", + "kotlinx-serialization-json", + "kotlinx-serialization-json-io", +] [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } diff --git a/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusClient.kt b/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusClient.kt index 9d1718d..ff445cc 100644 --- a/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusClient.kt +++ b/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusClient.kt @@ -136,7 +136,7 @@ object PolyPlusClient { //? if >= 1.21.1 step("pet entities") { PetEntities.register() } //? if >= 1.21.1 - EmoteWheelKeybind.register() + step("emote wheel keybind") { EmoteWheelKeybind.register() } step("websocket") { PolyConnection.initialize { diff --git a/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusSentry.kt b/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusSentry.kt index 4372eea..1143d6e 100644 --- a/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusSentry.kt +++ b/src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusSentry.kt @@ -501,12 +501,26 @@ object PolyPlusSentry { return false } + private val locallyHandled = ThreadLocal.withInitial { 0 } + + fun handlingLocally(block: () -> T): T { + val depth = locallyHandled.get() + locallyHandled.set(depth + 1) + return try { + block() + } finally { + locallyHandled.set(depth) + } + } + @JvmStatic fun captureCrashReport(title: String?, throwable: Throwable) { if (!PrivacyConsent.allowsOnlineServices()) return initialize() val hub = activeHub() ?: return + if (locallyHandled.get() > 0) return + val description = if (title.isNullOrBlank()) throwable.toString() else "$title: $throwable" if (isIgnoredCrashReport(description) || isNeverReported(throwable)) { PolyPlusCrashLogUploader.recordIgnoredCrash(throwable) diff --git a/src/main/kotlin/org/polyfrost/polyplus/client/cosmetics/CosmeticStore.kt b/src/main/kotlin/org/polyfrost/polyplus/client/cosmetics/CosmeticStore.kt index d779d24..e8a3796 100644 --- a/src/main/kotlin/org/polyfrost/polyplus/client/cosmetics/CosmeticStore.kt +++ b/src/main/kotlin/org/polyfrost/polyplus/client/cosmetics/CosmeticStore.kt @@ -36,6 +36,7 @@ object CosmeticStore { types: List = emptyList(), tags: List = emptyList(), collection: Int? = null, + reportFailures: Boolean = true, ): Result = runCatching { PolyPlusClient.HTTP.get("${PolyPlusConfig.apiUrl}/cosmetics/search") { parameter("page", page.coerceAtLeast(1)) @@ -46,7 +47,7 @@ object CosmeticStore { if (tags.isNotEmpty()) parameter("tags", tags.joinToString(",")) if (collection != null) parameter("collection", collection) }.body() - }.onFailure { reportFailure("Failed to search cosmetics", it) } + }.onFailure { if (reportFailures) reportFailure("Failed to search cosmetics", it) } private var cachedStockedTypes: List? = null @@ -57,14 +58,18 @@ object CosmeticStore { val stocked = coroutineScope { types.map { type -> async { - val result = search(page = 1, perPage = 1, types = listOf(type.serializedName)) - if (result.isSuccess) anySucceeded = true - val count = result.getOrNull()?.pagination?.totalItems - type.takeIf { count == null || count > 0 } + val response = search( + page = 1, + perPage = 1, + types = listOf(type.serializedName), + reportFailures = false, + ).getOrNull() ?: return@async null + anySucceeded = true + type.takeIf { response.pagination.totalItems > 0 } } }.awaitAll() }.filterNotNull() - if (!anySucceeded) return stocked + if (!anySucceeded) return types return stocked.also { cachedStockedTypes = it } } diff --git a/src/main/kotlin/org/polyfrost/polyplus/client/emotes/EmoteWheelKeybind.kt b/src/main/kotlin/org/polyfrost/polyplus/client/emotes/EmoteWheelKeybind.kt index c32f307..ce81f71 100644 --- a/src/main/kotlin/org/polyfrost/polyplus/client/emotes/EmoteWheelKeybind.kt +++ b/src/main/kotlin/org/polyfrost/polyplus/client/emotes/EmoteWheelKeybind.kt @@ -15,11 +15,14 @@ import org.polyfrost.oneconfig.api.event.v1.eventHandler import org.polyfrost.oneconfig.api.event.v1.events.TickEvent import org.polyfrost.polyplus.PolyPlusConstants import org.polyfrost.polyplus.client.gui.EmoteWheelScreen +import java.util.concurrent.atomic.AtomicBoolean object EmoteWheelKeybind { private val logger = LogManager.getLogger("polyplus/emote-wheel") private lateinit var keyMapping: KeyMapping + private val registered = AtomicBoolean(false) + fun isHeld(): Boolean { val key = InputConstants.getKey(keyMapping.saveString()) if (key.type == InputConstants.Type.MOUSE) { @@ -35,6 +38,8 @@ object EmoteWheelKeybind { } fun register() { + if (!registered.compareAndSet(false, true)) return + val mapping = KeyMapping( "key.polyplus.emote_wheel", InputConstants.Type.KEYSYM, diff --git a/src/main/kotlin/org/polyfrost/polyplus/client/gui/preview/PlayerPreviewRenderer.kt b/src/main/kotlin/org/polyfrost/polyplus/client/gui/preview/PlayerPreviewRenderer.kt index a509e7f..fef63e0 100644 --- a/src/main/kotlin/org/polyfrost/polyplus/client/gui/preview/PlayerPreviewRenderer.kt +++ b/src/main/kotlin/org/polyfrost/polyplus/client/gui/preview/PlayerPreviewRenderer.kt @@ -368,9 +368,11 @@ object PlayerPreviewRenderer { previewCape = capeOverride(source)?.let { ClientAsset.ResourceTexture(it).texturePath() } renderingPreview = true try { - val level = mc.level - if (level != null && mc.cameraEntity != null) renderEntity(mc, level, source, yawDeg, w, h, modelScale, verticalAnchor) - else renderDirect(mc, source, yawDeg, w, h, modelScale, verticalAnchor) + org.polyfrost.polyplus.client.PolyPlusSentry.handlingLocally { + val level = mc.level + if (level != null && mc.cameraEntity != null) renderEntity(mc, level, source, yawDeg, w, h, modelScale, verticalAnchor) + else renderDirect(mc, source, yawDeg, w, h, modelScale, verticalAnchor) + } } catch (t: Throwable) { LOG.error("[preview] entity submit failed; skipping frame", t) } finally { diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index cdcf6f3..ccb79d4 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -24,7 +24,7 @@ "fabric-api": "*", "fabricloader": ">=0.16.0", "java": ">=21", - "fabric-language-kotlin": ">=1.13.8", + "fabric-language-kotlin": ">=1.13.13", "oneconfigv1": ">=1.1.2" } }