Skip to content

Commit ef1bef2

Browse files
committed
refactor: use Ktor & kotlinx-serialization
1 parent 765b66d commit ef1bef2

File tree

6 files changed

+125
-45
lines changed

6 files changed

+125
-45
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ dependencies {
1414
}
1515
```
1616

17+
## Initialization in project
18+
```kotlin
19+
McUtilsHttpClient.init(CIO) // Or the engine you want to use
20+
```
21+
1722
## Features
1823

1924
### Get Server Status

src/main/kotlin/tech/aliorpse/mcutils/model/server/JavaServerStatusSerializer.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package tech.aliorpse.mcutils.model.server
22

33
import kotlinx.serialization.KSerializer
4-
import kotlinx.serialization.builtins.serializer
54
import kotlinx.serialization.descriptors.SerialDescriptor
65
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
6+
import kotlinx.serialization.descriptors.element
77
import kotlinx.serialization.encoding.Decoder
88
import kotlinx.serialization.encoding.Encoder
99
import kotlinx.serialization.json.JsonDecoder
@@ -16,15 +16,15 @@ import kotlinx.serialization.json.jsonPrimitive
1616
import kotlinx.serialization.json.put
1717

1818
internal object JavaServerStatusSerializer : KSerializer<JavaServerStatus> {
19-
20-
override val descriptor: SerialDescriptor =
19+
override val descriptor: SerialDescriptor by lazy {
2120
buildClassSerialDescriptor("JavaServerStatus") {
22-
element("description", TextComponentSerializer.descriptor)
23-
element("players", Players.serializer().descriptor)
24-
element("version", Version.serializer().descriptor)
25-
element("favicon", String.serializer().descriptor, isOptional = true)
26-
element("enforcesSecureChat", Boolean.serializer().descriptor, isOptional = true)
21+
element<String>("description")
22+
element<Players>("players")
23+
element<Version>("version")
24+
element<String>("favicon", isOptional = true)
25+
element<Boolean>("enforcesSecureChat", isOptional = true)
2726
}
27+
}
2828

2929
override fun serialize(encoder: Encoder, value: JavaServerStatus) {
3030
require(encoder is JsonEncoder)
@@ -57,4 +57,4 @@ internal object JavaServerStatusSerializer : KSerializer<JavaServerStatus> {
5757
enforcesSecureChat = enforcesSecureChat
5858
)
5959
}
60-
}
60+
}

src/main/kotlin/tech/aliorpse/mcutils/model/server/TextComponentSerializer.kt

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,131 @@ package tech.aliorpse.mcutils.model.server
22

33
import kotlinx.serialization.KSerializer
44
import kotlinx.serialization.builtins.ListSerializer
5-
import kotlinx.serialization.builtins.SetSerializer
65
import kotlinx.serialization.descriptors.SerialDescriptor
76
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
8-
import kotlinx.serialization.descriptors.element
97
import kotlinx.serialization.encoding.Decoder
108
import kotlinx.serialization.encoding.Encoder
9+
import kotlinx.serialization.json.JsonArray
1110
import kotlinx.serialization.json.JsonDecoder
1211
import kotlinx.serialization.json.JsonEncoder
12+
import kotlinx.serialization.json.JsonObject
13+
import kotlinx.serialization.json.JsonPrimitive
14+
import kotlinx.serialization.json.booleanOrNull
1315
import kotlinx.serialization.json.buildJsonObject
14-
import kotlinx.serialization.json.jsonObject
15-
import kotlinx.serialization.json.jsonPrimitive
1616
import kotlinx.serialization.json.put
17-
import kotlin.collections.emptyList
18-
import kotlin.let
17+
import tech.aliorpse.mcutils.utils.toTextComponent
18+
import java.util.EnumSet
1919

2020
internal object TextComponentSerializer : KSerializer<TextComponent> {
21-
private val extraSerializer by lazy { ListSerializer(TextComponentSerializer) }
22-
23-
override val descriptor: SerialDescriptor =
24-
buildClassSerialDescriptor("TextComponent") {
25-
element<String>("text")
26-
element<String>("color")
27-
element<Set<TextStyle>>("styles")
28-
element<List<TextComponent>>("extra", isOptional = true)
29-
}
21+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("TextComponent")
3022

3123
override fun serialize(encoder: Encoder, value: TextComponent) {
3224
require(encoder is JsonEncoder)
25+
3326
val obj = buildJsonObject {
3427
put("text", value.text)
35-
put("color", value.color)
36-
put("styles", encoder.json.encodeToJsonElement(SetSerializer(TextStyle.serializer()), value.styles))
28+
if (value.color.isNotEmpty()) put("color", value.color)
29+
30+
styleMap.forEach { (name, style) ->
31+
if (value.styles.contains(style)) put(name, true)
32+
}
33+
3734
if (value.extra.isNotEmpty()) {
38-
put("extra", encoder.json.encodeToJsonElement(extraSerializer, value.extra))
35+
put(
36+
"extra",
37+
encoder.json.encodeToJsonElement(ListSerializer(TextComponentSerializer), value.extra)
38+
)
3939
}
4040
}
41+
4142
encoder.encodeJsonElement(obj)
4243
}
4344

4445
override fun deserialize(decoder: Decoder): TextComponent {
4546
require(decoder is JsonDecoder)
46-
val json = decoder.decodeJsonElement().jsonObject
47-
48-
val text = json["text"]?.jsonPrimitive?.content ?: ""
49-
val color = json["color"]?.jsonPrimitive?.content ?: ""
50-
val styles = json["styles"]?.let {
51-
decoder.json.decodeFromJsonElement(SetSerializer(TextStyle.serializer()), it)
52-
} ?: emptySet()
53-
val extra = json["extra"]?.let {
54-
// 在这里使用 lazy 初始化的 extraSerializer
55-
decoder.json.decodeFromJsonElement(extraSerializer, it)
56-
} ?: emptyList()
57-
58-
return TextComponent(text, color, styles, extra)
47+
return when (val element = decoder.decodeJsonElement()) {
48+
is JsonPrimitive -> { element.content.toTextComponent() }
49+
50+
is JsonObject -> {
51+
var text = ""
52+
var color = ""
53+
val styles: EnumSet<TextStyle> = EnumSet.noneOf(TextStyle::class.java)
54+
var extra: List<TextComponent> = emptyList()
55+
56+
element.forEach { (name, value) ->
57+
when (name) {
58+
"text" -> {
59+
val rawText = when (value) {
60+
is JsonPrimitive -> value.content
61+
is JsonObject -> decoder.json.decodeFromJsonElement(TextComponentSerializer, value).text
62+
else -> value.toString()
63+
}
64+
65+
val parsed = rawText.toTextComponent()
66+
if ("§" !in rawText) {
67+
text = rawText
68+
} else {
69+
text = parsed.text
70+
color = parsed.color
71+
styles += parsed.styles
72+
if (parsed.extra.isNotEmpty()) extra = parsed.extra
73+
}
74+
}
75+
76+
"color" -> {
77+
val colorName = if (value is JsonPrimitive) value.content else value.toString()
78+
color = colors[colorName] ?: colorName
79+
}
80+
81+
"extra" -> {
82+
if (value is JsonArray) {
83+
extra = decoder.json.decodeFromJsonElement(
84+
ListSerializer(TextComponentSerializer), value
85+
)
86+
}
87+
}
88+
89+
in styleMap.keys -> {
90+
if (value is JsonPrimitive && value.booleanOrNull == true) {
91+
styles += styleMap.getValue(name)
92+
}
93+
}
94+
95+
else -> {}
96+
}
97+
}
98+
TextComponent(text, color, styles, extra)
99+
}
100+
else -> {
101+
TextComponent("", "#FFFFFF")
102+
}
103+
}
59104
}
105+
106+
private val styleMap = mapOf(
107+
"bold" to TextStyle.BOLD,
108+
"italic" to TextStyle.ITALIC,
109+
"underlined" to TextStyle.UNDERLINED,
110+
"strikethrough" to TextStyle.STRIKETHROUGH,
111+
"obfuscated" to TextStyle.OBFUSCATED,
112+
)
113+
114+
private val colors = mapOf(
115+
"black" to "#000000",
116+
"dark_blue" to "#0000AA",
117+
"dark_green" to "#00AA00",
118+
"dark_aqua" to "#00AAAA",
119+
"dark_red" to "#AA0000",
120+
"dark_purple" to "#AA00AA",
121+
"gold" to "#FFAA00",
122+
"gray" to "#AAAAAA",
123+
"dark_gray" to "#555555",
124+
"blue" to "#5555FF",
125+
"green" to "#55FF55",
126+
"aqua" to "#55FFFF",
127+
"red" to "#FF5555",
128+
"light_purple" to "#FF55FF",
129+
"yellow" to "#FFFF55",
130+
"white" to "#FFFFFF"
131+
)
60132
}

src/main/kotlin/tech/aliorpse/mcutils/modules/modrinth/Modrinth.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ public object Modrinth {
3232
config: ModrinthSearchConfig.() -> Unit = {}
3333
): ModrinthSearchResponse = withDispatchersIO {
3434
val cfg = ModrinthSearchConfig().apply(config)
35-
val facetsStr = cfg.buildFacets().takeIf { it.isNotEmpty() }
35+
val facetsStr = cfg.buildFacets()
36+
.takeIf { it.isNotEmpty() }
37+
?.joinToString(prefix = "[", postfix = "]") { inner ->
38+
inner.joinToString(prefix = "[", postfix = "]") { "\"$it\"" }
39+
}
3640

3741
McUtilsHttpClient.client.get("$API_BASE/v2/search") {
3842
parameter("query", query)

src/test/kotlin/tech/aliorpse/mcutils/modrinth/Test.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Test {
2525
fun getProjectsTest() {
2626
McUtilsHttpClient.init(CIO)
2727
val result = runBlocking {
28-
Modrinth.getProject("2H1rLgy4")
28+
Modrinth.getProjects(listOf("2H1rLgy4", "fabric-api"))
2929
}
3030

3131
println(result)

src/test/kotlin/tech/aliorpse/mcutils/server/ServerStatusTest.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ import kotlinx.coroutines.runBlocking
55
import tech.aliorpse.mcutils.modules.server.BedrockServer
66
import tech.aliorpse.mcutils.modules.server.JavaServer
77
import tech.aliorpse.mcutils.utils.McUtilsHttpClient
8-
import tech.aliorpse.mcutils.utils.toHtml
98
import kotlin.test.Test
109

1110
class ServerStatusTest {
1211
@Test
1312
fun javaGetStatusTest() {
1413
McUtilsHttpClient.init(CIO)
15-
val result = runBlocking { JavaServer.getStatus("hypixel.net") }
14+
val result = runBlocking { JavaServer.getStatus("wdsj.net") }
1615

17-
println(result.description.toHtml())
16+
println(result)
1817
assert(result.version.protocol > 0)
1918
}
2019

0 commit comments

Comments
 (0)