diff options
| author | Roman / Linnea Gräf <roman.graef@gmail.com> | 2023-03-04 02:54:50 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-04 12:54:50 +1100 |
| commit | 5dd063fbba6bde64806a7620541dc2d9bdf42871 (patch) | |
| tree | 01aee1a743a32a0b2546513c59a43559ce3085fe /src/main/kotlin | |
| parent | db86c98e0c72b18663ef26cd46cef7d53c1d6414 (diff) | |
| download | NotEnoughUpdates-5dd063fbba6bde64806a7620541dc2d9bdf42871.tar.gz NotEnoughUpdates-5dd063fbba6bde64806a7620541dc2d9bdf42871.tar.bz2 NotEnoughUpdates-5dd063fbba6bde64806a7620541dc2d9bdf42871.zip | |
Replace all commands in NEU with a brigadier implementation (#599)
Diffstat (limited to 'src/main/kotlin')
23 files changed, 2487 insertions, 0 deletions
diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt new file mode 100644 index 00000000..805ff114 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.kt @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.commands.dev + +import com.mojang.brigadier.arguments.StringArgumentType +import io.github.moulberry.notenoughupdates.BuildFlags +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.core.config.GuiPositionEditor +import io.github.moulberry.notenoughupdates.core.util.MiscUtils +import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent +import io.github.moulberry.notenoughupdates.miscfeatures.FishingHelper +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent +import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph +import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager +import io.github.moulberry.notenoughupdates.util.PronounDB +import io.github.moulberry.notenoughupdates.util.SBInfo +import io.github.moulberry.notenoughupdates.util.TabListUtils +import io.github.moulberry.notenoughupdates.util.brigadier.* +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiScreen +import net.minecraft.command.ICommandSender +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.launchwrapper.Launch +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting.* +import net.minecraft.util.EnumParticleTypes +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.function.Predicate +import kotlin.math.floor + +@NEUAutoSubscribe +class DevTestCommand { + companion object { + val DEV_TESTERS: List<String> = mutableListOf( + "d0e05de7-6067-454d-beae-c6d19d886191", // moulberry + "66502b40-6ac1-4d33-950d-3df110297aab", // lucycoconut + "a5761ff3-c710-4cab-b4f4-3e7f017a8dbf", // ironm00n + "5d5c548a-790c-4fc8-bd8f-d25b04857f44", // ariyio + "53924f1a-87e6-4709-8e53-f1c7d13dc239", // throwpo + "d3cb85e2-3075-48a1-b213-a9bfb62360c1", // lrg89 + "0b4d470f-f2fb-4874-9334-1eaef8ba4804", // dediamondpro + "ebb28704-ed85-43a6-9e24-2fe9883df9c2", // lulonaut + "698e199d-6bd1-4b10-ab0c-52fedd1460dc", // craftyoldminer + "8a9f1841-48e9-48ed-b14f-76a124e6c9df", // eisengolem + "a7d6b3f1-8425-48e5-8acc-9a38ab9b86f7", // whalker + "0ce87d5a-fa5f-4619-ae78-872d9c5e07fe", // ascynx + "a049a538-4dd8-43f8-87d5-03f09d48b4dc", // egirlefe + "7a9dc802-d401-4d7d-93c0-8dd1bc98c70d", // efefury + "bb855349-dfd8-4125-a750-5fc2cf543ad5" // hannibal2 + ) + val SPECIAL_KICK = "SPECIAL_KICK" + + val DEV_FAIL_STRINGS = arrayOf( + "No.", + "I said no.", + "You aren't allowed to use this.", + "Are you sure you want to use this? Type 'Yes' in chat.", + "Are you sure you want to use this? Type 'Yes' in chat.", + "Lmao you thought", + "Ok please stop", + "What do you want from me?", + "This command almost certainly does nothing useful for you", + "Ok, this is the last message, after this it will repeat", + "No.", + "I said no.", + "Dammit. I thought that would work. Uhh...", + "\u00a7dFrom \u00a7c[ADMIN] Minikloon\u00a77: If you use that command again, I'll have to ban you", + SPECIAL_KICK, + "Ok, this is actually the last message, use the command again and you'll crash I promise" + ) + + fun isDeveloper(commandSender: ICommandSender): Boolean { + return DEV_TESTERS.contains((commandSender as? EntityPlayer)?.uniqueID?.toString()) + || Launch.blackboard.get("fml.deobfuscatedEnvironment") as Boolean + + } + } + + var devFailIndex = 0 + fun canPlayerExecute(commandSender: ICommandSender): Boolean { + return isDeveloper(commandSender) + } + + @SubscribeEvent + fun onCommands(event: RegisterBrigadierCommandEvent) { + val hook = event.command("neudevtest") { + requires { + canPlayerExecute(it) + } + thenLiteralExecute("profileinfo") { + val currentProfile = SBInfo.getInstance().currentProfile + val gamemode = SBInfo.getInstance().getGamemodeForProfile(currentProfile) + reply("${GOLD}You are on Profile $currentProfile with the mode $gamemode") + }.withHelp("Display information about your current profile") + thenLiteralExecute("buildflags") { + reply("BuildFlags: \n" + + BuildFlags.getAllFlags().entries + .joinToString(("\n")) { (key, value) -> " + $key - $value" }) + }.withHelp("List the flags with which NEU was built") + thenLiteral("exteditor") { + thenArgument("editor", StringArgumentType.string()) { newEditor -> + thenExecute { + NotEnoughUpdates.INSTANCE.config.hidden.externalEditor = this[newEditor] + reply("You changed your external editor to: §Z${this[newEditor]}") + } + }.withHelp("Change the editor used to edit repo files") + thenExecute { + reply("Your external editor is: §Z${NotEnoughUpdates.INSTANCE.config.hidden.externalEditor}") + } + }.withHelp("See your current external editor for repo files") + thenLiteral("pricetest") { + thenArgument("item", StringArgumentType.string()) { item -> + thenExecute { + NotEnoughUpdates.INSTANCE.openGui = GuiPriceGraph(this[item]) + } + }.withHelp("Display the price graph for an item by id") + thenExecute { + NotEnoughUpdates.INSTANCE.manager.auctionManager.updateBazaar() + } + }.withHelp("Update the price data from the bazaar") + thenLiteralExecute("zone") { + val target = Minecraft.getMinecraft().objectMouseOver.blockPos + ?: Minecraft.getMinecraft().thePlayer.position + val zone = CustomBiomes.INSTANCE.getSpecialZone(target) + listOf( + ChatComponentText("Showing Zone Info for: $target"), + ChatComponentText("Zone: " + (zone?.name ?: "null")), + ChatComponentText("Location: " + SBInfo.getInstance().getLocation()), + ChatComponentText("Biome: " + CustomBiomes.INSTANCE.getCustomBiome(target)) + ).forEach { component -> + reply(component) + } + MinecraftForge.EVENT_BUS.post( + LocationChangeEvent( + SBInfo.getInstance().getLocation(), SBInfo + .getInstance() + .getLocation() + ) + ) + }.withHelp("Display information about the special block zone at your cursor (Custom Texture Regions)") + thenLiteralExecute("positiontest") { + NotEnoughUpdates.INSTANCE.openGui = GuiPositionEditor() + }.withHelp("Open the gui position editor") + thenLiteral("pt") { + thenArgument("particle", EnumArgumentType.enum<EnumParticleTypes>()) { particle -> + thenExecute { + FishingHelper.type = this[particle] + reply("Fishing particles set to ${FishingHelper.type}") + } + } + } + thenLiteralExecute("dev") { + NotEnoughUpdates.INSTANCE.config.hidden.dev = !NotEnoughUpdates.INSTANCE.config.hidden.dev + reply("§e[NEU] Dev mode " + if (NotEnoughUpdates.INSTANCE.config.hidden.dev) "§aenabled" else "§cdisabled") + }.withHelp("Toggle developer mode") + thenLiteralExecute("saveconfig") { + NotEnoughUpdates.INSTANCE.saveConfig() + reply("Config saved") + }.withHelp("Force sync the config to disk") + thenLiteralExecute("searchmode") { + NotEnoughUpdates.INSTANCE.config.hidden.firstTimeSearchFocus = true + reply(AQUA.toString() + "I would never search") + }.withHelp("Reset your search data to redisplay the search tutorial") + thenLiteralExecute("bluehair") { + PronounDB.test() + }.withHelp("Test the pronoundb integration") + thenLiteral("opengui") { + thenArgumentExecute("class", StringArgumentType.string()) { className -> + try { + NotEnoughUpdates.INSTANCE.openGui = + Class.forName(this[className]).newInstance() as GuiScreen + reply("Opening gui: " + NotEnoughUpdates.INSTANCE.openGui) + } catch (e: Exception) { + e.printStackTrace() + reply("Failed to open this GUI.") + } + }.withHelp("Open a gui by class name") + } + thenLiteralExecute("center") { + val x = floor(Minecraft.getMinecraft().thePlayer.posX) + 0.5f + val z = floor(Minecraft.getMinecraft().thePlayer.posZ) + 0.5f + Minecraft.getMinecraft().thePlayer.setPosition(x, Minecraft.getMinecraft().thePlayer.posY, z) + reply("Literal hacks") + }.withHelp("Center yourself on the block you are currently standing (like using AOTE)") + thenLiteral("minion") { + thenArgumentExecute("args", RestArgumentType) { arg -> + MinionHelperManager.getInstance().handleCommand(arrayOf("minion") + this[arg].split(" ")) + }.withHelp("Minion related commands. Not yet integrated in brigadier") + } + thenLiteralExecute("copytablist") { + val tabList = TabListUtils.getTabList().joinToString("\n", postfix = "\n") + MiscUtils.copyToClipboard(tabList) + reply("Copied tablist to clipboard!") + }.withHelp("Copy the tab list") + thenLiteralExecute("useragent") { + thenArgumentExecute("newuseragent", RestArgumentType) { userAgent -> + reply("Setting your user agent to ${this[userAgent]}") + NotEnoughUpdates.INSTANCE.config.hidden.customUserAgent = this[userAgent] + }.withHelp("Set a custom user agent for all HTTP requests") + thenExecute { + reply("Resetting your user agent.") + NotEnoughUpdates.INSTANCE.config.hidden.customUserAgent = null + } + }.withHelp("Reset the custom user agent") + } + hook.beforeCommand = Predicate { + if (!canPlayerExecute(it.context.source)) { + if (devFailIndex !in DEV_FAIL_STRINGS.indices) { + throw object : Error("L") { + @Override + fun printStackTrace() { + throw Error("L") + } + } + } + val text = DEV_FAIL_STRINGS[devFailIndex++] + if (text == SPECIAL_KICK) { + val component = ChatComponentText("\u00a7cYou are permanently banned from this server!") + component.appendText("\n") + component.appendText("\n\u00a77Reason: \u00a7rI told you not to run the command - Moulberry") + component.appendText("\n\u00a77Find out more: \u00a7b\u00a7nhttps://www.hypixel.net/appeal") + component.appendText("\n") + component.appendText("\n\u00a77Ban ID: \u00a7r#49871982") + component.appendText("\n\u00a77Sharing your Ban ID may affect the processing of your appeal!") + Minecraft.getMinecraft().netHandler.networkManager.closeChannel(component) + } else { + it.context.source.addChatMessage(ChatComponentText("$RED$text")) + } + false + } else { + true + } + } + } + +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.kt new file mode 100644 index 00000000..3e5e7b9b --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/DiagCommand.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.commands.dev + +import com.mojang.brigadier.arguments.BoolArgumentType.bool +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent +import io.github.moulberry.notenoughupdates.miscfeatures.CrystalMetalDetectorSolver +import io.github.moulberry.notenoughupdates.miscfeatures.CrystalWishingCompassSolver +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag +import io.github.moulberry.notenoughupdates.util.brigadier.* +import io.github.moulberry.notenoughupdates.util.brigadier.EnumArgumentType.Companion.enum +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +// Why is this not merged into /neudevtest +@NEUAutoSubscribe +class DiagCommand { + @SubscribeEvent + fun onCommands(event: RegisterBrigadierCommandEvent) { + event.command("neudiag") { + thenLiteral("metal") { + thenLiteral("center") { + thenArgumentExecute("usecenter", bool()) { useCenter -> + CrystalMetalDetectorSolver.setDebugDoNotUseCenter(this[useCenter]) + reply("Center coordinates-based solutions ${if (this[useCenter]) "enabled" else "disabled"}") + }.withHelp("Toggle coordinate based solutions") + } + thenExecute { + CrystalMetalDetectorSolver.logDiagnosticData(true) + reply("Enabled metal detector diagnostic logging.") + } + }.withHelp("Enable metal detector diagnostics") + thenLiteralExecute("wishing") { + CrystalWishingCompassSolver.getInstance().logDiagnosticData(true) + reply("Enabled wishing compass diagnostic logging") + }.withHelp("Enable wishing compass diagnostic logging") + thenLiteral("debug") { + thenLiteralExecute("list") { + reply("Here are all flags:\n${NEUDebugFlag.getFlagList()}") + }.withHelp("List all debug diagnostic logging flags") + thenLiteral("setflag") { + thenArgument("flag", enum<NEUDebugFlag>()) { flag -> + thenArgumentExecute("enable", bool()) { enable -> + val debugFlags = NotEnoughUpdates.INSTANCE.config.hidden.debugFlags + if (this[enable]) { + debugFlags.add(this[flag]) + } else { + debugFlags.remove(this[flag]) + } + reply("${if(this[enable]) "Enabled" else "Disabled"} the flag ${this[flag]}.") + }.withHelp("Enable or disable a diagnostic logging stream") + } + } + thenExecute { + reply("Effective debug flags: \n${NEUDebugFlag.getEnabledFlags()}") + } + }.withHelp("Log diagnostic data.") + } + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/NEUStatsCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/NEUStatsCommand.kt new file mode 100644 index 00000000..7035aaa3 --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/NEUStatsCommand.kt @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.commands.dev + +import com.mojang.brigadier.context.CommandContext +import com.sun.management.OperatingSystemMXBean +import com.sun.management.UnixOperatingSystemMXBean +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe +import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent +import io.github.moulberry.notenoughupdates.util.DiscordMarkdownBuilder +import io.github.moulberry.notenoughupdates.util.HastebinUploader +import io.github.moulberry.notenoughupdates.util.SBInfo +import io.github.moulberry.notenoughupdates.util.brigadier.reply +import io.github.moulberry.notenoughupdates.util.brigadier.thenExecute +import io.github.moulberry.notenoughupdates.util.brigadier.thenLiteralExecute +import io.github.moulberry.notenoughupdates.util.brigadier.withHelp +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.command.ICommandSender +import net.minecraft.util.EnumChatFormatting.DARK_RED +import net.minecraft.util.EnumChatFormatting.GREEN +import net.minecraftforge.common.ForgeVersion +import net.minecraftforge.fml.client.FMLClientHandler +import net.minecraftforge.fml.common.Loader +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.opengl.Display +import org.lwjgl.opengl.GL11 +import java.awt.Toolkit +import java.awt.datatransfer.StringSelection +import java.lang.management.ManagementFactory +import java.util.concurrent.CompletableFuture +import javax.management.JMX +import javax.management.ObjectName + +@NEUAutoSubscribe +class NEUStatsCommand { + @SubscribeEvent + fun onCommands(event: RegisterBrigadierCommandEvent) { + event.command("stats", "neustats") { + thenLiteralExecute("modlist") { + clipboardAndSendMessage( + DiscordMarkdownBuilder() + .also(::appendModList) + .toString() + ) + }.withHelp("Copy the mod list to your clipboard") + thenLiteralExecute("full") { + clipboardAndSendMessage( + DiscordMarkdownBuilder() + .also(::appendStats) + .also(::appendModList) + .toString() + ) + }.withHelp("Copy the full list of all NEU stats and your mod list to your clipboard") + thenLiteralExecute("dump") { + reply("${GREEN}This will upload a dump of the java classes your game has loaded how big they are and how many there are. This can take a few seconds as it is uploading to HasteBin.") + uploadDataUsageDump().thenAccept { + clipboardAndSendMessage(it) + } + }.withHelp("Dump all loaded classes and their memory usage and copy that to your clipboard.") + thenExecute { + clipboardAndSendMessage( + DiscordMarkdownBuilder() + .also(::appendStats) + .also { + if (Loader.instance().activeModList.size <= 15) appendModList(it) + } + .toString() + ) + } + }.withHelp("Copy a list of NEU relevant stats to your clipboard for debugging purposes") + } + interface DiagnosticCommandMXBean { + fun gcClassHistogram(array: Array<String>): String + } + + private fun uploadDataUsageDump(): CompletableFuture<String?> { + return CompletableFuture.supplyAsync { + try { + val server = + ManagementFactory.getPlatformMBeanServer() + val objectName = + ObjectName.getInstance("com.sun.management:type=DiagnosticCommand") + val proxy = JMX.newMXBeanProxy( + server, + objectName, + DiagnosticCommandMXBean::class.java + ) + HastebinUploader.upload( + proxy.gcClassHistogram(emptyArray()).replace("[", "[]"), + HastebinUploader.Mode.NORMAL + ) + } catch (e: Exception) { + null + } + } + + } + + + private fun getMemorySize(): Long { + try { + return (ManagementFactory.getOperatingSystemMXBean() as OperatingSystemMXBean).totalPhysicalMemorySize + } catch (e: java.lang.Exception) { + try { + return (ManagementFactory.getOperatingSystemMXBean() as UnixOperatingSystemMXBean).totalPhysicalMemorySize + } catch (ignored: java.lang.Exception) { /*IGNORE*/ + } + } + return -1 + } + + val ONE_MB = 1024L * 1024L + private fun appendStats(builder: DiscordMarkdownBuilder) { + val maxMemory = Runtime.getRuntime().maxMemory() + val totalMemory = Runtime.getRuntime().totalMemory() + val freeMemory = Runtime.getRuntime().freeMemory() + val currentMemory = totalMemory - freeMemory + builder.category("System Stats") + builder.append("OS", System.getProperty("os.name")) + builder.append("CPU", OpenGlHelper.getCpu()) + builder.append( + "Display", + String.format("%dx%d (%s)", Display.getWidth(), Display.getHeight(), GL11.glGetString(GL11.GL_VENDOR)) + ) + builder.append("GPU", GL11.glGetString(GL11.GL_RENDERER)) + builder.append("GPU Driver", GL11.glGetString(GL11.GL_VERSION)) + if (getMemorySize() > 0) + builder.append( + "Maximum Memory", + "${getMemorySize() / ONE_MB}MB" + ) + builder.append("Shaders", ("" + OpenGlHelper.isFramebufferEnabled()).uppercase()) + builder.category("Java Stats") + builder.append( + "Java", + "${System.getProperty("java.version")} ${if (Minecraft.getMinecraft().isJava64bit) 64 else 32}bit", + ) + builder.append( + "Memory", String.format( + "% 2d%% %03d/%03dMB", + currentMemory * 100L / maxMemory, + currentMemory / ONE_MB, + maxMemory / ONE_MB + ) + ) + builder.append( + "Memory Allocated", + String.format("% 2d%% %03dMB", totalMemory * 100L / maxMemory, totalMemory / ONE_MB) + ) + builder.category("Game Stats") + builder.append("FPS", Minecraft.getDebugFPS().toString()) + builder.append("Loaded Mods", Loader.instance().activeModList.size) + builder.append("Forge", ForgeVersion.getVersion()) + builder.append("Optifine", if (FMLClientHandler.instance().hasOptifine()) "TRUE" else "FALSE") + builder.category("Neu Settings") + builder.append("API Key", if (NotEnoughUpdates.INSTANCE.config.apiData.apiKey.isEmpty()) "FALSE" else "TRUE") + builder.append("On SkyBlock", if (NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) "TRUE" else "FALSE") + builder.append( + "Mod Version", + Loader.instance().indexedModList[NotEnoughUpdates.MODID]!!.displayVersion + ) + builder.append("SB Profile", SBInfo.getInstance().currentProfile) + builder.append("Has Advanced Tab", if (SBInfo.getInstance().hasNewTab) "TRUE" else "FALSE") + builder.category("Repo Stats") + builder.append("Last Commit", NotEnoughUpdates.INSTANCE.manager.latestRepoCommit) + builder.append("Loaded Items", NotEnoughUpdates.INSTANCE.manager.itemInformation.size.toString()) + } + + private fun appendModList(builder: DiscordMarkdownBuilder): DiscordMarkdownBuilder { + builder.category("Mods Loaded") + Loader.instance().activeModList.forEach { + builder.append(it.name, "${it.source} (${it.displayVersion})") + } + return builder + } + + fun CommandContext<ICommandSender>.clipboardAndSendMessage(data: String?) { + if (data == null) { + reply("${DARK_RED}Error occurred trying to perform command.") + return + } + try { + val clipboard = StringSelection(data) + Toolkit.getDefaultToolkit().systemClipboard.setContents(clipboard, null) + reply("${GREEN}Dev info copied to clipboard.") + } catch (ignored: Exception) { + reply("${DARK_RED}Could not copy to clipboard.") + } + } +} diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/PackDevCommand.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/PackDevCommand.kt new file mode 100644 index 00000000..fceacfab --- /dev/null +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/dev/PackDevCommand.kt @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUp |
