Skip to content

Commit

Permalink
refactor(legacy): optimized JSON loading from web (CCBlueX#5119)
Browse files Browse the repository at this point in the history
  • Loading branch information
MukjepScarlet authored Dec 31, 2024
1 parent f148f09 commit eb352e6
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 144 deletions.
2 changes: 1 addition & 1 deletion src/main/java/net/ccbluex/liquidbounce/cape/CapeService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ object CapeService : Listenable, MinecraftInstance {

/**
* The API URL to get all cape carriers.
* Format: [["8f617b6abea04af58e4bd026d8fa9de8", "marco"], ...]
* Format: [["8f617b6a-bea0-4af5-8e4b-d026d8fa9de8", "marco"], ...]
*/
private const val CAPE_CARRIERS_URL = "$CAPE_API/carriers"

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/net/ccbluex/liquidbounce/chat/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ abstract class Client : ClientListener, MinecraftInstance {
} catch (_: IllegalArgumentException) {
val incomingUUID = UserUtils.getUUID(target)

if (incomingUUID.isBlank()) return ""
if (incomingUUID.isNullOrBlank()) return ""

val uuid = StringBuilder(incomingUUID)
.insert(20, '-')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,11 @@ object StaffDetector : Module("StaffDetector", Category.MISC, gameDetecting = fa

private fun loadStaffList(url: String): Map<String, Set<String>> {
return try {
val (response, code) = HttpUtils.get(url)
val (response, code) = HttpUtils.requestStream(url)

when (code) {
200 -> {
val staffList = response.lineSequence()
val staffList = response.bufferedReader().lineSequence()
.filter { it.isNotBlank() }
.map { it.trim() }
.toSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/
package net.ccbluex.liquidbounce.features.special

import com.google.gson.JsonObject
import com.jagrosh.discordipc.IPCClient
import com.jagrosh.discordipc.IPCListener
import com.jagrosh.discordipc.entities.RichPresence
Expand All @@ -24,9 +23,8 @@ import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.utils.client.ClientUtils.LOGGER
import net.ccbluex.liquidbounce.utils.client.MinecraftInstance
import net.ccbluex.liquidbounce.utils.client.ServerUtils
import net.ccbluex.liquidbounce.utils.io.HttpUtils
import net.ccbluex.liquidbounce.utils.kotlin.SharedScopes
import net.ccbluex.liquidbounce.utils.io.HttpUtils.get
import net.ccbluex.liquidbounce.utils.io.parseJson
import org.json.JSONObject
import java.io.IOException
import java.time.OffsetDateTime
Expand Down Expand Up @@ -159,20 +157,14 @@ object ClientRichPresence : MinecraftInstance, Listenable {
* @throws IOException If reading failed
*/
private fun loadConfiguration() {
val (response, _) = get("$CLIENT_CLOUD/discord.json")

// Read from web and convert to json object
val json = response.parseJson()

if (json !is JsonObject)
return
val discordConf = HttpUtils.getJson<DiscordConfiguration>("$CLIENT_CLOUD/discord.json") ?: return

// Check has app id
if (json.has("appID"))
appID = json["appID"].asLong
discordConf.appID?.let { appID = it }

// Import all asset names
for ((key, value) in json["assets"].asJsonObject.entrySet())
assets[key] = value.asString
assets += discordConf.assets
}
}

private class DiscordConfiguration(val appID: Long?, val assets: Map<String, String>)
30 changes: 13 additions & 17 deletions src/main/java/net/ccbluex/liquidbounce/tabs/HeadsTab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
*/
package net.ccbluex.liquidbounce.tabs

import com.google.gson.JsonObject
import kotlinx.coroutines.*
import net.ccbluex.liquidbounce.LiquidBounce.CLIENT_CLOUD
import net.ccbluex.liquidbounce.utils.client.ClientUtils.LOGGER
import net.ccbluex.liquidbounce.utils.kotlin.SharedScopes
import net.ccbluex.liquidbounce.utils.inventory.ItemUtils
import net.ccbluex.liquidbounce.utils.io.HttpUtils.get
import net.ccbluex.liquidbounce.utils.io.parseJson
import net.ccbluex.liquidbounce.utils.io.HttpUtils
import net.minecraft.creativetab.CreativeTabs
import net.minecraft.init.Items
import net.minecraft.item.Item
Expand All @@ -38,24 +36,17 @@ class HeadsTab : CreativeTabs("Heads") {
LOGGER.info("Loading heads...")

// Asynchronously fetch the heads configuration
val (response, _) = get("$CLIENT_CLOUD/heads.json")
val headsConf = response.parseJson() as? JsonObject ?: return
val headsConf = HttpUtils.getJson<HeadsConfiguration>("$CLIENT_CLOUD/heads.json") ?: return

if (headsConf["enabled"].asBoolean) {
val url = headsConf["url"].asString
if (headsConf.enabled) {
val url = headsConf.url

LOGGER.info("Loading heads from $url...")

val (headsResponse, _) = get(url)
val headsElement = headsResponse.parseJson() as? JsonObject ?: run {
LOGGER.error("Something is wrong, the heads json is not a JsonObject!")
return
}

heads = headsElement.entrySet().map { (_, value) ->
val headElement = value.asJsonObject
val headsMap = HttpUtils.getJson<Map<String, HeadInfo>>(url) ?: return

ItemUtils.createItem("skull 1 3 {display:{Name:\"${headElement["name"].asString}\"},SkullOwner:{Id:\"${headElement["uuid"].asString}\",Properties:{textures:[{Value:\"${headElement["value"].asString}\"}]}}}")!!
heads = headsMap.values.map { head ->
ItemUtils.createItem("skull 1 3 {display:{Name:\"${head.name}\"},SkullOwner:{Id:\"${head.uuid}\",Properties:{textures:[{Value:\"${head.value}\"}]}}}")!!
}

LOGGER.info("Loaded ${heads.size} heads from HeadDB.")
Expand Down Expand Up @@ -94,4 +85,9 @@ class HeadsTab : CreativeTabs("Heads") {
* @return searchbar status
*/
override fun hasSearchBar() = true
}
}

private class HeadsConfiguration(val enabled: Boolean, val url: String)

// Only includes needed fields
private class HeadInfo(val name: String, val uuid: String, val value: String)
113 changes: 51 additions & 62 deletions src/main/java/net/ccbluex/liquidbounce/ui/client/GuiContributors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@
*/
package net.ccbluex.liquidbounce.ui.client

import com.google.gson.JsonParser
import com.google.gson.annotations.SerializedName
import kotlinx.coroutines.launch
import net.ccbluex.liquidbounce.file.FileManager.PRETTY_GSON
import net.ccbluex.liquidbounce.injection.implementations.IMixinGuiSlot
import net.ccbluex.liquidbounce.lang.translationMenu
import net.ccbluex.liquidbounce.ui.font.AWTFontRenderer.Companion.assumeNonVolatile
import net.ccbluex.liquidbounce.ui.font.Fonts
import net.ccbluex.liquidbounce.utils.client.ClientUtils.LOGGER
import net.ccbluex.liquidbounce.utils.io.HttpUtils.get
import net.ccbluex.liquidbounce.utils.io.HttpUtils.requestStream
import net.ccbluex.liquidbounce.utils.io.HttpUtils
import net.ccbluex.liquidbounce.utils.kotlin.SharedScopes
import net.ccbluex.liquidbounce.utils.render.CustomTexture
import net.ccbluex.liquidbounce.utils.render.RenderUtils.drawLoadingCircle
Expand All @@ -25,17 +22,20 @@ import net.minecraft.client.gui.GuiButton
import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.gui.GuiSlot
import net.minecraft.client.renderer.GlStateManager.*
import okhttp3.Request
import org.lwjgl.input.Keyboard
import org.lwjgl.opengl.GL11.*
import java.awt.Color
import java.text.DecimalFormat
import java.text.NumberFormat
import java.util.*
import javax.imageio.ImageIO
import kotlin.collections.ArrayList
import kotlin.math.sin

private val DECIMAL_FORMAT = NumberFormat.getInstance(Locale.US) as DecimalFormat

class GuiContributors(private val prevGui: GuiScreen) : AbstractScreen() {
private val DECIMAL_FORMAT = NumberFormat.getInstance(Locale.US) as DecimalFormat
private lateinit var list: GuiList

private var credits = emptyList<Credit>()
Expand Down Expand Up @@ -174,31 +174,24 @@ class GuiContributors(private val prevGui: GuiScreen) : AbstractScreen() {

private fun loadCredits() {
try {
val jsonParser = JsonParser()

val gitHubContributors = PRETTY_GSON.fromJson(
get("https://api.github.com/repos/CCBlueX/LiquidBounce/stats/contributors").first,
Array<GitHubContributor>::class.java
)

if (gitHubContributors == null) {
val gitHubContributors = HttpUtils.getJson<Array<GitHubContributor>>(
"https://api.github.com/repos/CCBlueX/LiquidBounce/stats/contributors"
) ?: run {
failed = true
return
}

val additionalInformation =
jsonParser.parse(get("https://raw.githubusercontent.com/CCBlueX/LiquidCloud/master/LiquidBounce/contributors.json").first).asJsonObject
// Note: this API is not available in China
val additionalInformation = HttpUtils.getJson<Map<String, ContributorInformation>>(
"https://raw.githubusercontent.com/CCBlueX/LiquidCloud/master/LiquidBounce/contributors.json"
) ?: emptyMap()

val credits = mutableListOf<Credit>()
val credits = ArrayList<Credit>(gitHubContributors.size)

for (gitHubContributor in gitHubContributors) {
var contributorInformation: ContributorInformation? = null
val author = gitHubContributor.author ?: continue // Skip invalid contributors
val jsonElement = additionalInformation[author.id.toString()]

if (jsonElement != null) {
contributorInformation = PRETTY_GSON.fromJson(jsonElement, ContributorInformation::class.java)
}
val contributorInformation = additionalInformation[author.id.toString()]

var additions = 0
var deletions = 0
Expand All @@ -211,7 +204,7 @@ class GuiContributors(private val prevGui: GuiScreen) : AbstractScreen() {
}

credits += Credit(
author.name, author.avatarUrl, null,
author.name, author.avatarUrl,
additions, deletions, commits,
contributorInformation?.teamMember ?: false,
contributorInformation?.contributions ?: emptyList()
Expand All @@ -231,51 +224,12 @@ class GuiContributors(private val prevGui: GuiScreen) : AbstractScreen() {
}

this.credits = credits

for (credit in credits) {
try {
requestStream(
"${credit.avatarUrl}?s=${fontRendererObj.FONT_HEIGHT * 4}",
"GET"
).let { (stream, _) ->
stream.use {
credit.avatar = CustomTexture(ImageIO.read(it))
}
}
} catch (_: Exception) {
}
}
} catch (e: Exception) {
LOGGER.error("Failed to load credits.", e)
failed = true
}
}

internal inner class ContributorInformation(
val name: String, val teamMember: Boolean,
val contributions: List<String>,
)

internal inner class GitHubContributor(
@SerializedName("total") val totalContributions: Int,
val weeks: List<GitHubWeek>, val author: GitHubAuthor?,
)

internal inner class GitHubWeek(
@SerializedName("w") val timestamp: Long, @SerializedName("a") val additions: Int,
@SerializedName("d") val deletions: Int, @SerializedName("c") val commits: Int,
)

internal inner class GitHubAuthor(
@SerializedName("login") val name: String, val id: Int,
@SerializedName("avatar_url") val avatarUrl: String,
)

internal inner class Credit(
val name: String, val avatarUrl: String, var avatar: CustomTexture?, val additions: Int,
val deletions: Int, val commits: Int, val isTeamMember: Boolean, val contributions: List<String>,
)

private inner class GuiList(gui: GuiScreen) : GuiSlot(mc, gui.width / 4, gui.height, 40, gui.height - 40, 15) {

init {
Expand Down Expand Up @@ -309,4 +263,39 @@ class GuiContributors(private val prevGui: GuiScreen) : AbstractScreen() {

override fun drawBackground() {}
}
}
}

private class ContributorInformation(
val name: String, val teamMember: Boolean,
val contributions: List<String>,
)

private class GitHubContributor(
@SerializedName("total") val totalContributions: Int,
val weeks: List<GitHubWeek>, val author: GitHubAuthor?,
)

private class GitHubWeek(
@SerializedName("w") val timestamp: Long, @SerializedName("a") val additions: Int,
@SerializedName("d") val deletions: Int, @SerializedName("c") val commits: Int,
)

private class GitHubAuthor(
@SerializedName("login") val name: String, val id: Int,
@SerializedName("avatar_url") val avatarUrl: String,
)

private class Credit(
val name: String, val avatarUrl: String, val additions: Int,
val deletions: Int, val commits: Int, val isTeamMember: Boolean, val contributions: List<String>,
) {
val avatar by lazy {
runCatching {
HttpUtils.httpClient.newCall(Request.Builder().url(avatarUrl).build()).execute().use { response ->
response.body!!.byteStream().use(ImageIO::read).let(::CustomTexture)
}
}.onFailure {
LOGGER.error("Failed to load avatar.", it)
}.getOrNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import net.ccbluex.liquidbounce.ui.font.Fonts
import net.ccbluex.liquidbounce.utils.client.ClientUtils.LOGGER
import net.ccbluex.liquidbounce.utils.client.MinecraftInstance.Companion.mc
import net.ccbluex.liquidbounce.utils.io.FileFilters
import net.ccbluex.liquidbounce.utils.io.HttpUtils.get
import net.ccbluex.liquidbounce.utils.io.HttpUtils
import net.ccbluex.liquidbounce.utils.io.MiscUtils
import net.ccbluex.liquidbounce.utils.kotlin.RandomUtils.randomAccount
import net.ccbluex.liquidbounce.utils.kotlin.SharedScopes
Expand Down Expand Up @@ -423,16 +423,7 @@ class GuiAltManager(private val prevGui: GuiScreen) : AbstractScreen() {
fun loadActiveGenerators() {
try {
// Read versions json from cloud
val (response, _) = get("$CLIENT_CLOUD/generators.json")
val jsonElement = JsonParser().parse(response)

// Check json is valid object
if (jsonElement.isJsonObject) {
// Get json object of element
jsonElement.asJsonObject.entrySet().forEach { (key, value) ->
activeGenerators[key] = value.asBoolean
}
}
activeGenerators += HttpUtils.getJson<Map<String, Boolean>>("$CLIENT_CLOUD/generators.json")!!
} catch (throwable: Throwable) {
// Print throwable to console
LOGGER.error("Failed to load enabled generators.", throwable)
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/net/ccbluex/liquidbounce/utils/io/HttpUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package net.ccbluex.liquidbounce.utils.io

import net.ccbluex.liquidbounce.utils.client.ClientUtils
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
Expand Down Expand Up @@ -80,7 +81,7 @@ object HttpUtils {

fun requestStream(
url: String,
method: String,
method: String = "GET",
agent: String = DEFAULT_AGENT,
headers: Array<Pair<String, String>> = emptyArray(),
body: RequestBody? = null
Expand All @@ -92,10 +93,10 @@ object HttpUtils {
throw IOException("Unexpected code ${response.code}")
}

return response.body?.byteStream()!! to response.code
return response.body!!.byteStream() to response.code
}

fun request(
private fun request(
url: String,
method: String,
agent: String = DEFAULT_AGENT,
Expand All @@ -113,6 +114,16 @@ object HttpUtils {
return request(url, "GET", agent, headers)
}

inline fun <reified T> getJson(url: String): T? {
return runCatching {
httpClient.newCall(Request.Builder().url(url).build()).execute().use {
it.body?.charStream()?.decodeJson<T>()
}
}.onFailure {
ClientUtils.LOGGER.error("[HTTP] Failed to GET JSON from $url", it)
}.getOrNull()
}

fun post(
url: String,
agent: String = DEFAULT_AGENT,
Expand Down
Loading

0 comments on commit eb352e6

Please sign in to comment.