Skip to content

Commit 3075988

Browse files
committed
moshi instead of gson
1 parent 2c34f6f commit 3075988

File tree

5 files changed

+102
-83
lines changed

5 files changed

+102
-83
lines changed

.idea/kotlinc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.gradle.kts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
plugins {
2-
kotlin("jvm") version "2.1.21"
2+
kotlin("jvm") version "2.2.0"
3+
id("com.google.devtools.ksp") version "2.2.0-2.0.2"
34
id("org.jetbrains.dokka") version "2.0.0"
45
id("com.vanniktech.maven.publish") version "0.33.0"
56
}
67

78
group = "tech.aliorpse.mcutils"
89
version = System.getenv("GITHUB_REF_NAME") ?: "local"
910

11+
kotlin {
12+
jvmToolchain(21)
13+
14+
compilerOptions {
15+
freeCompilerArgs.add("-Xjvm-default=all")
16+
}
17+
}
18+
1019
repositories {
1120
mavenCentral()
1221
mavenLocal()
1322
}
1423

1524
dependencies {
25+
// kotlinx
1626
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
17-
api("com.google.code.gson:gson:2.13.1")
27+
28+
// retrofit
1829
api("com.squareup.retrofit2:retrofit:3.0.0")
30+
api("com.squareup.retrofit2:converter-moshi:3.0.0")
31+
32+
// moshi
33+
api("com.squareup.moshi:moshi:1.15.2")
34+
api("com.squareup.moshi:moshi-kotlin:1.15.2")
35+
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2")
36+
37+
// to be deprecated
38+
api("com.google.code.gson:gson:2.13.1")
1939
api("com.squareup.retrofit2:converter-gson:3.0.0")
2040

2141
testImplementation(kotlin("test"))
Lines changed: 19 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
package tech.aliorpse.mcutils.model.mojang
22

3-
import com.google.gson.Gson
4-
import com.google.gson.JsonDeserializationContext
5-
import com.google.gson.JsonDeserializer
6-
import com.google.gson.JsonElement
7-
import okio.ByteString.Companion.decodeBase64
8-
import java.lang.reflect.Type
3+
import com.squareup.moshi.JsonClass
94

105
/**
116
* Represents a player's profile as retrieved from the Mojang session servers.
@@ -44,75 +39,30 @@ enum class SkinModel {
4439
}
4540
}
4641

47-
private data class RawPlayerProfile(
42+
@JsonClass(generateAdapter = true)
43+
data class RawPlayerProfile(
4844
val id: String,
4945
val name: String,
5046
val legacy: Boolean = false,
5147
val properties: List<Property>
5248
) {
53-
data class Property(
54-
val name: String,
55-
val value: String
56-
)
49+
@JsonClass(generateAdapter = true)
50+
data class Property(val name: String, val value: String)
5751
}
5852

59-
/**
60-
* A custom deserializer for converting JSON data into a `PlayerProfile` object.
61-
*
62-
* This class implements `JsonDeserializer<PlayerProfile>` and provides functionality
63-
* to parse player profile data, including details such as UUID, name, skin URL, cape URL,
64-
* and skin model type, from the Mojang session server JSON response.
65-
*
66-
* The deserialization process includes:
67-
* - Parsing the raw profile data (`RawPlayerProfile`) to extract basic player information.
68-
* - Decoding and parsing Base64-encoded textures from the profile's property list.
69-
* - Extracting texture URLs for skin and cape as well as determining the player's skin model.
70-
*
71-
* In case of errors during the decoding or parsing of textures, default values are used.
72-
*/
73-
class PlayerProfileDeserializer : JsonDeserializer<PlayerProfile> {
74-
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): PlayerProfile {
75-
val rawProfile = Gson().fromJson(json, RawPlayerProfile::class.java)
76-
val texturesBase64 = rawProfile.properties.find { it.name == "textures" }?.value
77-
78-
var skinUrl: String? = null
79-
var capeUrl: String? = null
80-
var model = SkinModel.CLASSIC
81-
82-
if (texturesBase64 != null) {
83-
try {
84-
val jsonStr = String(texturesBase64.decodeBase64()!!.toByteArray())
85-
val texturesObj = Gson().fromJson(jsonStr, DecodedTextures::class.java)
86-
val skin = texturesObj.textures["SKIN"]
87-
val cape = texturesObj.textures["CAPE"]
88-
skinUrl = skin?.url
89-
capeUrl = cape?.url
90-
model = SkinModel.from(skin?.metadata?.model)
91-
} catch (_: Exception) {
92-
}
93-
}
94-
95-
return PlayerProfile(
96-
id = rawProfile.id,
97-
name = rawProfile.name,
98-
legacy = rawProfile.legacy,
99-
skinUrl = skinUrl,
100-
capeUrl = capeUrl,
101-
skinModel = model
102-
)
103-
}
104-
105-
private data class DecodedTextures(
106-
val timestamp: Long,
107-
val profileId: String,
108-
val profileName: String,
109-
val textures: Map<String, Texture>
110-
)
53+
@JsonClass(generateAdapter = true)
54+
data class DecodedTextures(
55+
val timestamp: Long,
56+
val profileId: String,
57+
val profileName: String,
58+
val textures: Map<String, Texture>
59+
)
11160

112-
private data class Texture(
113-
val url: String,
114-
val metadata: Metadata?
115-
)
61+
@JsonClass(generateAdapter = true)
62+
data class Texture(
63+
val url: String,
64+
val metadata: Metadata?
65+
)
11666

117-
private data class Metadata(val model: String?)
118-
}
67+
@JsonClass(generateAdapter = true)
68+
data class Metadata(val model: String?)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package tech.aliorpse.mcutils.model.mojang
2+
3+
import com.squareup.moshi.FromJson
4+
import com.squareup.moshi.Moshi
5+
import okio.ByteString.Companion.decodeBase64
6+
7+
/**
8+
* A custom adapter for converting JSON data into a `PlayerProfile` object.
9+
*
10+
* This adapter implements `@FromJson` to parse player profile data, including UUID, name,
11+
* textures (skin, cape), and model type, from Mojang session server's JSON response.
12+
*/
13+
class PlayerProfileAdapter {
14+
15+
private val moshi = Moshi.Builder().build()
16+
17+
@FromJson
18+
fun fromJson(raw: RawPlayerProfile): PlayerProfile {
19+
val texturesBase64 = raw.properties.find { it.name == "textures" }?.value
20+
21+
var skinUrl: String? = null
22+
var capeUrl: String? = null
23+
var model = SkinModel.CLASSIC
24+
25+
if (texturesBase64 != null) {
26+
try {
27+
val jsonStr = String(texturesBase64.decodeBase64()!!.toByteArray())
28+
val decoded = moshi.adapter(DecodedTextures::class.java).fromJson(jsonStr)
29+
val skin = decoded?.textures?.get("SKIN")
30+
val cape = decoded?.textures?.get("CAPE")
31+
skinUrl = skin?.url
32+
capeUrl = cape?.url
33+
model = SkinModel.from(skin?.metadata?.model)
34+
} catch (_: Exception) {
35+
}
36+
}
37+
38+
return PlayerProfile(
39+
id = raw.id,
40+
name = raw.name,
41+
legacy = raw.legacy,
42+
skinUrl = skinUrl,
43+
capeUrl = capeUrl,
44+
skinModel = model
45+
)
46+
}
47+
}

src/main/kotlin/tech/aliorpse/mcutils/module/mojang/MojangClient.kt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,42 @@
11
package tech.aliorpse.mcutils.module.mojang
22

3-
import com.google.gson.GsonBuilder
3+
import com.squareup.moshi.Moshi
4+
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
45
import retrofit2.Retrofit
5-
import retrofit2.converter.gson.GsonConverterFactory
6-
import tech.aliorpse.mcutils.model.mojang.PlayerProfile
7-
import tech.aliorpse.mcutils.model.mojang.PlayerProfileDeserializer
6+
import retrofit2.converter.moshi.MoshiConverterFactory
7+
import tech.aliorpse.mcutils.model.mojang.PlayerProfileAdapter
88

99
/**
1010
* Internal object responsible for managing the communication with Mojang's APIs.
1111
*
1212
* This object acts as a client for Mojang's API services, providing access to Mojang's profile
13-
* and session endpoints. It sets up and configures the necessary Retrofit instances and Gson
13+
* and session endpoints. It sets up and configures the necessary Retrofit instances and Moshi
1414
* deserialization logic to facilitate communication with these endpoints.
1515
*
1616
* The MojangClient offers two primary services:
1717
* - `profileService`: Used to interact with the base Mojang API endpoints at "https://api.mojang.com/".
1818
* - `sessionService`: Used to interact with Mojang's session server at "https://sessionserver.mojang.com/".
1919
*/
2020
internal object MojangClient {
21-
private val gson = GsonBuilder()
22-
.registerTypeAdapter(PlayerProfile::class.java, PlayerProfileDeserializer())
23-
.create()
24-
private val gsonFactory = GsonConverterFactory.create(gson)
21+
private val moshi = Moshi.Builder()
22+
.add(PlayerProfileAdapter())
23+
.add(KotlinJsonAdapterFactory())
24+
.build()
25+
26+
private val moshiFactory = MoshiConverterFactory.create(moshi)
2527

2628
val profileService: MojangService by lazy {
2729
Retrofit.Builder()
2830
.baseUrl("https://api.mojang.com/")
29-
.addConverterFactory(gsonFactory)
31+
.addConverterFactory(moshiFactory)
3032
.build()
3133
.create(MojangService::class.java)
3234
}
3335

3436
val sessionService: MojangService by lazy {
3537
Retrofit.Builder()
3638
.baseUrl("https://sessionserver.mojang.com/")
37-
.addConverterFactory(gsonFactory)
39+
.addConverterFactory(moshiFactory)
3840
.build()
3941
.create(MojangService::class.java)
4042
}

0 commit comments

Comments
 (0)