Skip to content

Commit 65277ae

Browse files
committed
HostPort utils & improve docs quality
1 parent e78ceb4 commit 65277ae

File tree

9 files changed

+106
-26
lines changed

9 files changed

+106
-26
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A Kotlin-based Minecraft request library provides utility functions related to M
1212
```kotlin
1313
dependencies {
1414
implementation("tech.aliorpse:mcutils:$version")
15-
implementation("io.ktor:ktor-client-cio:$version") //or the client you want
15+
implementation("io.ktor:ktor-client-cio:$version") // or the client you want
1616
}
1717
```
1818

build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ kotlin {
2323

2424
suspendTransformPlugin {
2525
transformers {
26-
addJvmBlocking()
27-
addJvmAsync()
26+
useJvmDefault()
2827
}
2928
}
3029

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import love.forte.plugin.suspendtrans.annotation.JvmBlocking
77
import tech.aliorpse.mcutils.model.modrinth.project.ModrinthProject
88
import tech.aliorpse.mcutils.model.modrinth.search.ModrinthSearchConfig
99
import tech.aliorpse.mcutils.model.modrinth.search.ModrinthSearchResponse
10-
import tech.aliorpse.mcutils.utils.McUtilsHttpClientProvider
10+
import tech.aliorpse.mcutils.utils.McUtilsHttpClientProvider.httpClient
1111
import tech.aliorpse.mcutils.utils.withDispatchersIO
1212

1313
/**
@@ -21,7 +21,6 @@ public object Modrinth {
2121
*
2222
* @param query Search query.
2323
* @param config Optional configuration for search parameters.
24-
* @return [ModrinthSearchResponse] containing the search results.
2524
*/
2625
@JvmStatic
2726
@JvmOverloads
@@ -38,7 +37,7 @@ public object Modrinth {
3837
inner.joinToString(prefix = "[", postfix = "]") { "\"$it\"" }
3938
}
4039

41-
McUtilsHttpClientProvider.client.get("$API_BASE/v2/search") {
40+
httpClient.get("$API_BASE/v2/search") {
4241
parameter("query", query)
4342
parameter("facets", facetsStr)
4443
parameter("limit", cfg.limit)
@@ -55,7 +54,7 @@ public object Modrinth {
5554
@JvmBlocking
5655
public suspend fun getProjects(ids: List<String>): List<ModrinthProject> = withDispatchersIO {
5756
val idsStr = ids.joinToString(",", "[", "]") { "\"$it\"" }
58-
McUtilsHttpClientProvider.client.get("$API_BASE/v2/projects") {
57+
httpClient.get("$API_BASE/v2/projects") {
5958
parameter("ids", idsStr)
6059
}.body()
6160
}
@@ -80,7 +79,7 @@ public object Modrinth {
8079
@JvmBlocking
8180
public suspend fun getProjectsRandom(count: Int = 10): List<ModrinthProject> = withDispatchersIO {
8281
require(count <= 100)
83-
McUtilsHttpClientProvider.client.get("$API_BASE/v2/projects_random") {
82+
httpClient.get("$API_BASE/v2/projects_random") {
8483
parameter("count", count)
8584
}.body()
8685
}

src/main/kotlin/tech/aliorpse/mcutils/modules/player/Player.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import love.forte.plugin.suspendtrans.annotation.JvmAsync
66
import love.forte.plugin.suspendtrans.annotation.JvmBlocking
77
import tech.aliorpse.mcutils.model.player.PlayerProfile
88
import tech.aliorpse.mcutils.model.player.PlayerUUIDProfile
9-
import tech.aliorpse.mcutils.utils.McUtilsHttpClientProvider
9+
import tech.aliorpse.mcutils.utils.McUtilsHttpClientProvider.httpClient
1010
import tech.aliorpse.mcutils.utils.withDispatchersIO
1111

1212
/**
@@ -30,7 +30,6 @@ public object Player {
3030
* - A valid Minecraft username (3–16 characters, letters, digits, and underscores).
3131
*
3232
* @param player The player's UUID or username.
33-
* @return A [PlayerProfile] containing the player's UUID, username, skin, cape, and model type.
3433
* @throws IllegalArgumentException if the input is neither a valid UUID nor a valid username.
3534
*/
3635
@JvmStatic
@@ -41,13 +40,13 @@ public object Player {
4140

4241
when {
4342
pl.length == UUID_LENGTH -> {
44-
McUtilsHttpClientProvider.client.get("$MOJANG_SESSION_BASE/session/minecraft/profile/$pl").body()
43+
httpClient.get("$MOJANG_SESSION_BASE/session/minecraft/profile/$pl").body()
4544
}
4645

4746
nameRegex.matches(pl) -> {
4847
val uuidProfile: PlayerUUIDProfile =
49-
McUtilsHttpClientProvider.client.get("$MOJANG_PROFILE_BASE/users/profiles/minecraft/$pl").body()
50-
McUtilsHttpClientProvider.client.get("$MOJANG_SESSION_BASE/session/minecraft/profile/${uuidProfile.id}").body()
48+
httpClient.get("$MOJANG_PROFILE_BASE/users/profiles/minecraft/$pl").body()
49+
httpClient.get("$MOJANG_SESSION_BASE/session/minecraft/profile/${uuidProfile.id}").body()
5150
}
5251

5352
else -> throw IllegalArgumentException("Invalid identifier: $pl")

src/main/kotlin/tech/aliorpse/mcutils/modules/server/BedrockServer.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import tech.aliorpse.mcutils.model.server.BedrockServerStatus
66
import tech.aliorpse.mcutils.model.server.GameMode
77
import tech.aliorpse.mcutils.model.server.Players
88
import tech.aliorpse.mcutils.model.server.Version
9+
import tech.aliorpse.mcutils.utils.HostPort
910
import tech.aliorpse.mcutils.utils.toTextComponent
1011
import tech.aliorpse.mcutils.utils.withDispatchersIO
1112
import java.io.IOException
@@ -35,13 +36,11 @@ public object BedrockServer {
3536
private const val SERVER_INFO_OFFSET = 33
3637

3738
/**
38-
* Fetches the status of a Bedrock server.
39+
* Fetches the Bedrock server status.
3940
*
40-
* @param host The server hostname or IP.
41+
* @param host The server host.
4142
* @param port The server port (default 19132).
4243
* @param timeout Timeout in milliseconds (default 2000ms).
43-
* @return A [BedrockServerStatus] containing the parsed server information.
44-
* @throws IOException If the server response is invalid or timed out.
4544
*/
4645
@JvmStatic
4746
@JvmOverloads
@@ -108,4 +107,24 @@ public object BedrockServer {
108107
)
109108
}
110109
}
110+
111+
/**
112+
* Fetches the Bedrock server status.
113+
*
114+
* @param hostPort As it named.
115+
* @param timeout Timeout in milliseconds (default 2000ms).
116+
* @throws IllegalArgumentException If host is null.
117+
*/
118+
@JvmStatic
119+
@JvmOverloads
120+
@JvmAsync
121+
@JvmBlocking
122+
public suspend fun getStatus(
123+
hostPort: HostPort,
124+
timeout: Int = 2000
125+
): BedrockServerStatus = getStatus(
126+
hostPort.host ?: throw IllegalArgumentException("The host is null."),
127+
hostPort.port ?: 19132,
128+
timeout
129+
)
111130
}

src/main/kotlin/tech/aliorpse/mcutils/modules/server/JavaServer.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import love.forte.plugin.suspendtrans.annotation.JvmBlocking
88
import org.xbill.DNS.*
99
import tech.aliorpse.mcutils.model.server.JavaServerStatus
1010
import tech.aliorpse.mcutils.model.server.JavaServerStatusSerializer
11+
import tech.aliorpse.mcutils.utils.HostPort
1112
import tech.aliorpse.mcutils.utils.withDispatchersIO
1213
import java.io.ByteArrayOutputStream
1314
import java.io.DataInputStream
1415
import java.io.DataOutputStream
1516
import java.io.OutputStream
17+
import java.lang.IllegalArgumentException
1618
import java.net.IDN
1719
import java.net.InetSocketAddress
1820
import java.net.Socket
@@ -33,13 +35,11 @@ public object JavaServer {
3335
private val json = Json { ignoreUnknownKeys = true }
3436

3537
/**
36-
* Fetch the Java server status.
38+
* Fetches the Java server status.
3739
*
3840
* @param host The server host.
3941
* @param port The server port (default 25565).
4042
* @param timeout Timeout in milliseconds (default 2000ms).
41-
* @return [JavaServerStatus] representing the server's status.
42-
* @throws IOException If network or parsing fails.
4343
*/
4444
@JvmStatic
4545
@JvmOverloads
@@ -85,6 +85,27 @@ public object JavaServer {
8585
}
8686
}
8787

88+
/**
89+
* Fetches the Java server status.
90+
*
91+
* @param hostPort As it named.
92+
* @param timeout Timeout in milliseconds (default 2000ms).
93+
* @return [JavaServerStatus] representing the server's status.
94+
* @throws IllegalArgumentException If host is null.
95+
*/
96+
@JvmStatic
97+
@JvmOverloads
98+
@JvmAsync
99+
@JvmBlocking
100+
public suspend fun getStatus(
101+
hostPort: HostPort,
102+
timeout: Int = 2000
103+
): JavaServerStatus = getStatus(
104+
hostPort.host ?: throw IllegalArgumentException("The host is null."),
105+
hostPort.port ?: 25565,
106+
timeout
107+
)
108+
88109
private fun resolveToIpOrHost(host: String, depth: Int = 5): String? {
89110
if (depth <= 0) return null
90111
try {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package tech.aliorpse.mcutils.utils
2+
3+
import java.net.IDN
4+
5+
public data class HostPort(
6+
val host: String?,
7+
val port: Int?
8+
)
9+
10+
/**
11+
* Parses a host:port? string into [HostPort].
12+
*
13+
* - Empty input → HostPort(null, null)
14+
* - Domains are normalized to punycode (IDN.toASCII)
15+
* - Invalid ports → null
16+
*/
17+
public fun hostPortOf(address: String?): HostPort {
18+
if (address.isNullOrBlank()) return HostPort(null, null)
19+
20+
return if (address.startsWith("[")) {
21+
// IPv6
22+
val closing = address.indexOf(']')
23+
require(closing != -1) { "Invalid IPv6 address: $address" }
24+
val host = address.substring(1, closing)
25+
val port = address.substring(closing + 1)
26+
.takeIf { it.startsWith(":") }
27+
?.drop(1)
28+
?.toIntOrNull()
29+
HostPort(IDN.toASCII(host), port)
30+
} else {
31+
// IPv4 / domain
32+
val parts = address.split(":", limit = 2)
33+
val host = parts[0]
34+
val port = parts.getOrNull(1)?.toIntOrNull()
35+
HostPort(
36+
host = IDN.toASCII(host),
37+
port = port
38+
)
39+
}
40+
}

src/main/kotlin/tech/aliorpse/mcutils/utils/McUtilsHttpClientProvider.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import io.ktor.client.engine.*
55
import io.ktor.client.plugins.contentnegotiation.*
66
import io.ktor.serialization.kotlinx.json.*
77
import kotlinx.serialization.json.Json
8-
import tech.aliorpse.mcutils.utils.McUtilsHttpClientProvider.client
8+
import tech.aliorpse.mcutils.utils.McUtilsHttpClientProvider.httpClient
99
import kotlin.reflect.KProperty
1010

1111
/**
@@ -31,7 +31,7 @@ import kotlin.reflect.KProperty
3131
public object McUtilsHttpClientProvider {
3232
private val delegate = HttpClientDelegate()
3333

34-
internal val client: HttpClient by delegate
34+
internal val httpClient: HttpClient by delegate
3535

3636
private fun makeClient(engine: HttpClientEngineFactory<*>): HttpClient =
3737
HttpClient(engine) {
@@ -84,8 +84,10 @@ public object McUtilsHttpClientProvider {
8484
/**
8585
* Overrides the default [HttpClient] with a custom engine.
8686
*
87-
* Must be called **before** the first access of [client], or you will get a [IllegalStateException],
88-
* and it should be only called **once**.
87+
* Must be called **before** the first access of [httpClient] (calling of http-related functions).
88+
* It should be only called **once**.
89+
*
90+
* @throws IllegalStateException If called after the first access of [httpClient].
8991
*/
9092
public fun use(engine: HttpClientEngineFactory<*>): Unit = delegate.setClient(makeClient(engine))
9193
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ package tech.aliorpse.mcutils.server
33
import kotlinx.coroutines.runBlocking
44
import tech.aliorpse.mcutils.modules.server.BedrockServer
55
import tech.aliorpse.mcutils.modules.server.JavaServer
6+
import tech.aliorpse.mcutils.utils.hostPortOf
67
import kotlin.test.Test
78

89
class ServerStatusTest {
910
@Test
1011
fun javaGetStatusTest() {
11-
val result = runBlocking { JavaServer.getStatus("wdsj.net") }
12+
val result = runBlocking { JavaServer.getStatus(hostPortOf("wdsj.net")) }
1213

1314
println(result)
1415
assert(result.version.protocol > 0)
1516
}
1617

1718
@Test
1819
fun bedrockGetStatusTest() {
19-
val result = runBlocking { BedrockServer.getStatus("asia.easecation.net") }
20+
val result = runBlocking { BedrockServer.getStatus(hostPortOf("asia.easecation.net:19132")) }
2021

2122
println(result)
2223
assert(result.ping > 0)

0 commit comments

Comments
 (0)