diff options
author | hannibal2 <24389977+hannibal00212@users.noreply.github.com> | 2023-07-21 13:37:55 +0200 |
---|---|---|
committer | hannibal2 <24389977+hannibal00212@users.noreply.github.com> | 2023-07-21 13:37:55 +0200 |
commit | 9a410749c465280342ea176d855d9ca4ece94be2 (patch) | |
tree | 97157b0a88b0a9af27f0290661c50b1a287a0ab7 | |
parent | ac792ec4d55d46512930667402782fb545221a0f (diff) | |
download | skyhanni-9a410749c465280342ea176d855d9ca4ece94be2.tar.gz skyhanni-9a410749c465280342ea176d855d9ca4ece94be2.tar.bz2 skyhanni-9a410749c465280342ea176d855d9ca4ece94be2.zip |
Added /shconfig command
3 files changed, 191 insertions, 3 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt index e5c46cb00..9e5082f37 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -25,6 +25,7 @@ import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager import at.hannibal2.skyhanni.features.misc.ghostcounter.GhostUtil import at.hannibal2.skyhanni.features.slayer.SlayerItemProfitTracker import at.hannibal2.skyhanni.test.PacketTest +import at.hannibal2.skyhanni.test.SkyHanniConfigSearchResetCommand import at.hannibal2.skyhanni.test.SkyHanniTestCommand import at.hannibal2.skyhanni.test.TestBingo import at.hannibal2.skyhanni.test.command.* @@ -156,6 +157,10 @@ object Commands { "shclearminiondata", "Reset data about minion profit and the name display on the private island" ) { MinionFeatures.clearMinionData() } + registerCommand( + "shconfig", + "Search or reset config elements §c(warning, dangerous!)" + ) { SkyHanniConfigSearchResetCommand.command(it) } } private fun developersDebugFeatures() { diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt new file mode 100644 index 000000000..f257fd037 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt @@ -0,0 +1,185 @@ +package at.hannibal2.skyhanni.test + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.Features +import at.hannibal2.skyhanni.config.core.config.Position +import at.hannibal2.skyhanni.test.command.CopyErrorCommand +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.OSUtils +import java.lang.reflect.Field + +object SkyHanniConfigSearchResetCommand { + fun command(args: Array<String>) { + if (args.isEmpty()) { + LorenzUtils.chat("§c[SkyHanni] This is a config-edit command, only use it if you know what you are doing!") + return + } + if (args[0] == "reset") { + if (args.size != 2) { + LorenzUtils.chat("§c/shconfig reset <config element>") + return + } + val term = args[1] + try { + val (field, defaultObject, _) = getComplexField(term, Features()) + val (_, _, parent) = getComplexField(term, SkyHanniMod.feature) + field.set(parent, defaultObject) + LorenzUtils.chat("§eSuccessfully reset config element '$term'") + } catch (e: Exception) { + CopyErrorCommand.logError(e, "Could not reset config element '$term'") + } + return + } else if (args[0] == "search") { + if (args.size == 1) { + LorenzUtils.chat("§c/shconfig search <config name> [class name]") + return + } + Thread { + try { + startSearch(args) + } catch (e: Exception) { + CopyErrorCommand.logError(e, "Error while trying to search config") + } + }.start() + return + } + + LorenzUtils.chat("§c/shconfig <search;reset>") + } + + private fun createFilter(condition: Boolean, searchTerm: () -> String): Pair<(String) -> Boolean, String> { + return if (condition && searchTerm() != "all") { + val term = searchTerm() + Pair({ it.lowercase().contains(term) }, "'$term'") + } else Pair({ true }, "<all>") + } + + private fun startSearch(args: Array<String>) { + val (configFilter, configSearchTerm) = createFilter(true) { args[1].lowercase() } + val (classFilter, classSearchTerm) = createFilter(args.size == 3) { args[2].lowercase() } + + val elements = findConfigElements(configFilter, classFilter) + val builder = StringBuilder() + builder.append("```\n") + builder.append("Search config for SkyHanni ${SkyHanniMod.version}\n") + builder.append("configSearchTerm: $configSearchTerm\n") + builder.append("classSearchTerm: $classSearchTerm\n") + builder.append("\n") + val size = elements.size + builder.append("Found $size config elements:\n") + for (entry in elements) { + builder.append(entry) + builder.append("\n") + } + builder.append("```") + OSUtils.copyToClipboard(builder.toString()) + LorenzUtils.chat("§eCopied search result ($size) to clipboard.") + } + + private fun findConfigElements( + configFilter: (String) -> Boolean, + classFilter: (String) -> Boolean, + ): MutableList<String> { + val list = mutableListOf<String>() + for ((name, obj) in loadAllFields("config", Features())) { + if (name == "config.DISCORD") continue + if (name == "config.GITHUB") continue + val description = if (obj != null) { + val className = obj.getClassName() + if (!classFilter(className)) continue + val objectName = obj.getObjectName() + if (objectName.startsWith(className) && objectName.startsWith("at.hannibal2.skyhanni.config.features.")) { + "<category>" + } else { + "$className = $objectName" + } + } else "null" + + if (configFilter(name)) { + list.add("$name $description") + } + } + return list + } + + private fun getComplexField(term: String, startObject: Any): Triple<Field, Any, Any> { + var parentObject = startObject + var obj = startObject + val line = term.split(".").drop(1) + var field: Field? = null + for (entry in line) { + field = obj.javaClass.getField(entry) + field.isAccessible = true + parentObject = obj + obj = field.get(obj) + } + if (field == null) { + throw Error("Could not find field for '$term'") + } + return Triple(field, obj, parentObject) + } + + private fun loadAllFields(parentName: String, obj: Any, depth: Int = 0): Map<String, Any?> { + if (depth == 10) return emptyMap() // this is only a safety backup, needs increasing maybe someday + val map = mutableMapOf<String, Any?>() + for (field in obj.javaClass.fields) { + val name = field.name + val fieldName = "$parentName.$name" + field.isAccessible = true + val newObj = field.get(obj) + map[fieldName] = newObj + if (newObj != null) { + if (newObj !is Boolean && newObj !is String && newObj !is Long && newObj !is Int) { + if (newObj !is Position) { + map.putAll(loadAllFields(fieldName, newObj, depth + 1)) + } + } + } + } + + return map + } + + private fun Any.getClassName(): String { + val name = javaClass.name + return when (name) { + "at.hannibal2.skyhanni.config.core.config.Position" -> "Position" + "java.lang.Boolean" -> "Boolean" + "java.lang.Integer" -> "Int" + "java.lang.Long" -> "Long" + "java.lang.String" -> "String" + "io.github.moulberry.moulconfig.observer.Property" -> "moulconfig.Property" + "java.util.ArrayList" -> "List" + "java.util.HashMap" -> "Map" + + else -> name + } + } + + private fun Field.makeAccessible() = also { isAccessible = true } + + private fun Any.getObjectName(): String { + if (this is Position) { + val x = javaClass.getDeclaredField("x").makeAccessible().get(this) + val y = javaClass.getDeclaredField("y").makeAccessible().get(this) + return "($x, $y)" + } + if (this is String) { + return if (toString() == "") { + "<empty string>" + } else { + "'$this'" + } + } + if (this is io.github.moulberry.moulconfig.observer.Property<*>) { + val value = javaClass.getDeclaredField("value").makeAccessible().get(this) + val name = value.getClassName() + return "moulconfig.Property<$name> = ${value.getObjectName()}" + } + + if (this is Int || this is Int) return addSeparators() + + return toString() + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt index 8cf55b29c..b34a5ca4b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt @@ -89,9 +89,7 @@ object NumberUtil { return this.toString() + this.ordinal() } - fun Number.addSeparators(): String { - return NumberFormat.getNumberInstance().format(this) - } + fun Number.addSeparators() = NumberFormat.getNumberInstance().format(this) fun String.romanToDecimalIfNeeded() = toIntOrNull() ?: romanToDecimal() |