From 921f2e81f227284cad3105bac14ce9f841f8210a Mon Sep 17 00:00:00 2001 From: MukjepScarlet <93977077+MukjepScarlet@users.noreply.github.com> Date: Sun, 22 Dec 2024 16:57:00 +0800 Subject: [PATCH] refactor/fix(legacy): HUD config & memory leak in writeJson/readJson (#5007) --- .../liquidbounce/file/configs/HudConfig.kt | 103 +++++++++--------- .../ccbluex/liquidbounce/ui/client/hud/HUD.kt | 8 +- .../ui/client/hud/designer/EditorPanel.kt | 4 +- .../liquidbounce/ui/font/GameFontRenderer.kt | 46 ++++---- .../liquidbounce/utils/io/FileExtensions.kt | 8 +- .../liquidbounce/utils/io/GsonExtensions.kt | 16 ++- 6 files changed, 99 insertions(+), 86 deletions(-) diff --git a/src/main/java/net/ccbluex/liquidbounce/file/configs/HudConfig.kt b/src/main/java/net/ccbluex/liquidbounce/file/configs/HudConfig.kt index c41ba2e1cab..741f656c3e8 100644 --- a/src/main/java/net/ccbluex/liquidbounce/file/configs/HudConfig.kt +++ b/src/main/java/net/ccbluex/liquidbounce/file/configs/HudConfig.kt @@ -8,12 +8,14 @@ package net.ccbluex.liquidbounce.file.configs import com.google.gson.JsonArray import com.google.gson.JsonObject import net.ccbluex.liquidbounce.file.FileConfig -import net.ccbluex.liquidbounce.file.FileManager.PRETTY_GSON import net.ccbluex.liquidbounce.ui.client.hud.HUD -import net.ccbluex.liquidbounce.ui.client.hud.element.ElementInfo import net.ccbluex.liquidbounce.ui.client.hud.element.Side import net.ccbluex.liquidbounce.utils.client.ClientUtils import net.ccbluex.liquidbounce.config.FontValue +import net.ccbluex.liquidbounce.utils.io.json +import net.ccbluex.liquidbounce.utils.io.jsonArray +import net.ccbluex.liquidbounce.utils.io.readJson +import net.ccbluex.liquidbounce.utils.io.writeJson import java.io.File import java.io.IOException @@ -28,57 +30,56 @@ class HudConfig(file: File) : FileConfig(file) { */ @Throws(IOException::class) override fun loadConfig() { - val jsonArray = PRETTY_GSON.fromJson(file.bufferedReader(), JsonArray::class.java) + val jsonArray = file.readJson().takeIf { it is JsonArray } as? JsonArray ?: return HUD.clearElements() try { for (jsonObject in jsonArray) { - try { - if (jsonObject !is JsonObject) - continue - - if (!jsonObject.has("Type")) - continue + if (jsonObject !is JsonObject) + continue - val type = jsonObject["Type"].asString + if (!jsonObject.has("Type")) + continue - for (elementClass in HUD.ELEMENTS) { - val classType = elementClass.getAnnotation(ElementInfo::class.java).name + val type = jsonObject["Type"].asString - if (classType == type) { - val element = elementClass.newInstance() + try { + val elementClass = HUD.ELEMENTS.entries.find { it.value.name == type }?.key - element.x = jsonObject["X"].asDouble - element.y = jsonObject["Y"].asDouble - element.scale = jsonObject["Scale"].asFloat - element.side = Side( - Side.Horizontal.getByName(jsonObject["HorizontalFacing"].asString) ?: Side.Horizontal.RIGHT, - Side.Vertical.getByName(jsonObject["VerticalFacing"].asString) ?: Side.Vertical.UP - ) + if (elementClass == null) { + ClientUtils.LOGGER.warn("Unrecognized HUD element: '$type'") + continue + } - for (value in element.values) { - if (jsonObject.has(value.name)) - value.fromJson(jsonObject[value.name]) - } + val element = elementClass.newInstance() - // Support for old HUD files - if (jsonObject.has("font")) - element.values.find { it is FontValue }?.fromJson(jsonObject["font"]) + element.x = jsonObject["X"].asDouble + element.y = jsonObject["Y"].asDouble + element.scale = jsonObject["Scale"].asFloat + element.side = Side( + Side.Horizontal.getByName(jsonObject["HorizontalFacing"].asString) ?: Side.Horizontal.RIGHT, + Side.Vertical.getByName(jsonObject["VerticalFacing"].asString) ?: Side.Vertical.UP + ) - HUD.addElement(element) - break - } + for (value in element.values) { + if (jsonObject.has(value.name)) + value.fromJson(jsonObject[value.name]) } + + // Support for old HUD files + if (jsonObject.has("font")) + element.values.find { it is FontValue }?.fromJson(jsonObject["font"]) + + HUD.addElement(element) } catch (e: Exception) { - ClientUtils.LOGGER.error("Error while loading custom hud element from config.", e) + ClientUtils.LOGGER.error("Error while loading custom HUD element '$type' from config.", e) } } // Add forced elements when missing - for (elementClass in HUD.ELEMENTS) { - if (elementClass.getAnnotation(ElementInfo::class.java).force - && HUD.elements.none { it.javaClass == elementClass }) { + for ((elementClass, info) in HUD.ELEMENTS) { + if (info.force && HUD.elements.none { it.javaClass == elementClass }) { HUD.addElement(elementClass.newInstance()) } } @@ -95,25 +96,23 @@ class HudConfig(file: File) : FileConfig(file) { */ @Throws(IOException::class) override fun saveConfig() { - val jsonArray = JsonArray() - - for (element in HUD.elements) { - val elementObject = JsonObject() - elementObject.run { - addProperty("Type", element.name) - addProperty("X", element.x) - addProperty("Y", element.y) - addProperty("Scale", element.scale) - addProperty("HorizontalFacing", element.side.horizontal.sideName) - addProperty("VerticalFacing", element.side.vertical.sideName) + val jsonArray = jsonArray { + for (element in HUD.elements) { + +json { + "Type" to element.name + "X" to element.x + "Y" to element.y + "Scale" to element.scale + "HorizontalFacing" to element.side.horizontal.sideName + "VerticalFacing" to element.side.vertical.sideName + + element.values.forEach { + it.name to it.toJson() + } + } } - - for (value in element.values) - elementObject.add(value.name, value.toJson()) - - jsonArray.add(elementObject) } - file.writeText(PRETTY_GSON.toJson(jsonArray)) + file.writeJson(jsonArray) } } \ No newline at end of file diff --git a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/HUD.kt b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/HUD.kt index ad13b6d51eb..4eeb52d8222 100644 --- a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/HUD.kt +++ b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/HUD.kt @@ -7,6 +7,7 @@ package net.ccbluex.liquidbounce.ui.client.hud import net.ccbluex.liquidbounce.ui.client.hud.designer.GuiHudDesigner import net.ccbluex.liquidbounce.ui.client.hud.element.Element +import net.ccbluex.liquidbounce.ui.client.hud.element.ElementInfo import net.ccbluex.liquidbounce.ui.client.hud.element.elements.* import net.ccbluex.liquidbounce.ui.client.hud.element.elements.Target import net.ccbluex.liquidbounce.utils.client.ClientUtils.LOGGER @@ -15,6 +16,7 @@ import net.ccbluex.liquidbounce.utils.extensions.component1 import net.ccbluex.liquidbounce.utils.extensions.component2 import net.minecraft.client.gui.ScaledResolution import org.lwjgl.opengl.GL11.* +import java.util.* import kotlin.math.max import kotlin.math.min @@ -23,7 +25,7 @@ object HUD : MinecraftInstance { val elements = mutableListOf() val notifications = mutableListOf() - val ELEMENTS = arrayOf( + private val ALL_ELEMENT_CLASSES = arrayOf( Armor::class.java, Arraylist::class.java, Effects::class.java, @@ -43,6 +45,10 @@ object HUD : MinecraftInstance { Keystrokes::class.java ) + val ELEMENTS = ALL_ELEMENT_CLASSES.associateWithTo(IdentityHashMap(ALL_ELEMENT_CLASSES.size)) { + it.getAnnotation(ElementInfo::class.java) + } + /** Create default HUD */ fun setDefault() { elements.clear() diff --git a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/designer/EditorPanel.kt b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/designer/EditorPanel.kt index 877f7fb040f..359615ca17d 100644 --- a/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/designer/EditorPanel.kt +++ b/src/main/java/net/ccbluex/liquidbounce/ui/client/hud/designer/EditorPanel.kt @@ -118,9 +118,7 @@ class EditorPanel(private val hudDesigner: GuiHudDesigner, var x: Int, var y: In realHeight = 15 width = 90 - for (element in ELEMENTS) { - val info = element.getAnnotation(ElementInfo::class.java) ?: continue - + for ((element, info) in ELEMENTS) { if (info.single && HUD.elements.any { it.javaClass == element }) continue val name = info.name diff --git a/src/main/java/net/ccbluex/liquidbounce/ui/font/GameFontRenderer.kt b/src/main/java/net/ccbluex/liquidbounce/ui/font/GameFontRenderer.kt index 82edd648983..80bef87c42b 100644 --- a/src/main/java/net/ccbluex/liquidbounce/ui/font/GameFontRenderer.kt +++ b/src/main/java/net/ccbluex/liquidbounce/ui/font/GameFontRenderer.kt @@ -49,7 +49,7 @@ class GameFontRenderer(font: Font) : FontRenderer( fun drawCenteredString(s: String, x: Float, y: Float, color: Int, shadow: Boolean) = drawString(s, x - getStringWidth(s) / 2F, y, color, shadow) fun drawCenteredString(s: String, x: Float, y: Float, color: Int) = - drawStringWithShadow(s, x - getStringWidth(s) / 2F, y, color) + drawStringWithShadow(s, x - getStringWidth(s) / 2F, y, color) override fun drawString(text: String, x: Float, y: Float, color: Int, shadow: Boolean): Int { glEnable(GL_BLEND) @@ -100,8 +100,8 @@ class GameFontRenderer(font: Font) : FontRenderer( val alpha = (currentColor shr 24 and 0xff) - if ("§" in text) { - val parts = text.split("§") + if ('§' in text) { + val parts = text.split('§') var currentFont = defaultFont @@ -168,26 +168,24 @@ class GameFontRenderer(font: Font) : FontRenderer( } } - currentFont = if (bold && italic) - boldItalicFont - else if (bold) - boldFont - else if (italic) - italicFont - else - defaultFont + currentFont = when { + bold && italic -> boldItalicFont + bold -> boldFont + italic -> italicFont + else -> defaultFont + } currentFont.drawString(if (randomCase) ColorUtils.randomMagicText(words) else words, width, 0.0, currentColor) if (strikeThrough) drawLine(width / 2.0 + 1, currentFont.height / 3.0, - (width + currentFont.getStringWidth(words)) / 2.0 + 1, currentFont.height / 3.0, - fontHeight / 16F) + (width + currentFont.getStringWidth(words)) / 2.0 + 1, currentFont.height / 3.0, + fontHeight / 16F) if (underline) drawLine(width / 2.0 + 1, currentFont.height / 2.0, - (width + currentFont.getStringWidth(words)) / 2.0 + 1, currentFont.height / 2.0, - fontHeight / 16F) + (width + currentFont.getStringWidth(words)) / 2.0 + 1, currentFont.height / 2.0, + fontHeight / 16F) width += currentFont.getStringWidth(words) } @@ -209,8 +207,8 @@ class GameFontRenderer(font: Font) : FontRenderer( override fun getStringWidth(text: String): Int { val currentText = NameProtect.handleTextMessage(text) - return if ("§" in currentText) { - val parts = currentText.split("§") + return if ('§' in currentText) { + val parts = currentText.split('§') var currentFont = defaultFont var width = 0 @@ -241,14 +239,12 @@ class GameFontRenderer(font: Font) : FontRenderer( } } - currentFont = if (bold && italic) - boldItalicFont - else if (bold) - boldFont - else if (italic) - italicFont - else - defaultFont + currentFont = when { + bold && italic -> boldItalicFont + bold -> boldFont + italic -> italicFont + else -> defaultFont + } width += currentFont.getStringWidth(words) } diff --git a/src/main/java/net/ccbluex/liquidbounce/utils/io/FileExtensions.kt b/src/main/java/net/ccbluex/liquidbounce/utils/io/FileExtensions.kt index 8cae66c4431..0c579dd97f5 100644 --- a/src/main/java/net/ccbluex/liquidbounce/utils/io/FileExtensions.kt +++ b/src/main/java/net/ccbluex/liquidbounce/utils/io/FileExtensions.kt @@ -9,12 +9,12 @@ import java.io.File private val parser = JsonParser() -fun File.writeJson(content: JsonElement, gson: Gson = PRETTY_GSON) = gson.toJson(content, bufferedWriter()) +fun File.writeJson(content: JsonElement, gson: Gson = PRETTY_GSON) = bufferedWriter().use { gson.toJson(content, it) } -fun File.writeJson(content: Any?, gson: Gson = PRETTY_GSON) = gson.toJson(content, bufferedWriter()) +fun File.writeJson(content: Any?, gson: Gson = PRETTY_GSON) = bufferedWriter().use { gson.toJson(content, it) } -fun File.readJson(): JsonElement = parser.parse(bufferedReader()) +fun File.readJson(): JsonElement = bufferedReader().use { parser.parse(it) } -fun File.sha256(): String = DigestUtils.sha256Hex(inputStream()) +fun File.sha256(): String = inputStream().use { DigestUtils.sha256Hex(it) } val File.isEmpty: Boolean get() = length() == 0L \ No newline at end of file diff --git a/src/main/java/net/ccbluex/liquidbounce/utils/io/GsonExtensions.kt b/src/main/java/net/ccbluex/liquidbounce/utils/io/GsonExtensions.kt index f4553a8f572..fb07e8acc88 100644 --- a/src/main/java/net/ccbluex/liquidbounce/utils/io/GsonExtensions.kt +++ b/src/main/java/net/ccbluex/liquidbounce/utils/io/GsonExtensions.kt @@ -3,7 +3,6 @@ package net.ccbluex.liquidbounce.utils.io import com.google.gson.* import com.google.gson.reflect.TypeToken import net.ccbluex.liquidbounce.file.FileManager.PRETTY_GSON -import java.io.File private val EMPTY_JSON_ARRAY = JsonArray() @@ -32,6 +31,21 @@ class JsonObjectBuilder { backend.addProperty(this, value) } + /** + * Fallback + */ + infix fun String.to(value: Any?) { + when (value) { + null -> backend.add(this, JsonNull.INSTANCE) + is String -> backend.addProperty(this, value) + is Number -> backend.addProperty(this, value) + is Boolean -> backend.addProperty(this, value) + is JsonElement -> backend.add(this, value) + is JsonObjectBuilder -> backend.add(this, value.build()) + else -> throw IllegalArgumentException("Unsupported type: ${value::class.java}") + } + } + fun build() = backend }