/* * 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 . */ 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.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.minionhelper.MinionHelperManager import io.github.moulberry.notenoughupdates.miscgui.pricegraph.GuiPriceGraph import io.github.moulberry.notenoughupdates.util.* 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 = 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 "eaa5623c-8413-46b7-a74b-2d74a42b2841", // calmwolfs "e2c6f077-d45c-43ac-8322-857c7f8df3b9", // vixid "503450fc-72c2-4e87-8243-94e264977437" // thatgravyboat ) 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) } thenLiteral("joinServer") { thenArgument("serverId", RestArgumentType) { serverId -> thenExecute { try { MC.sessionService.joinServer( MC.session.profile, MC.session.token, get(serverId) ) reply("Joined server ${get(serverId)}") } catch (e: Exception) { e.printStackTrace() reply("Failed to join server") } } }.withHelp("Send a joinServer request to mojang (to test authentication with the cape server)") } thenLiteral("testsearch") { thenArgument("name", RestArgumentType) { arg -> thenExecute { reply("Resolved ID: ${ItemResolutionQuery.findInternalNameByDisplayName(get(arg), true)}") } }.withHelp("Search for an item id by name") } thenLiteralExecute("garden") { val player = Minecraft.getMinecraft().thePlayer reply("Is in Garden: ${SBInfo.getInstance().getLocation() == "garden"}") val pp = player.position reply("Plot X: ${floor((pp.getX() + 48) / 96F)}") reply("Plot Z: ${floor((pp.getZ() + 48) / 96F)}") }.withHelp("Show diagnostics information about the garden") 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)") thenLiteral("pt") { thenArgument("particle", EnumArgumentType.enum()) { particle -> thenExecute { FishingHelper.type = this[particle] reply("Fishing particles set to ${FishingHelper.type}") } } } thenLiteral("callUrsa") { thenArgument("path", RestArgumentType) { path -> thenExecute { NotEnoughUpdates.INSTANCE.manager.ursaClient.getString(this[path]) .thenAccept { reply(it.toString()) } } }.withHelp("Send an authenticated request to the current ursa server") } thenLiteralExecute("dev") { NotEnoughUpdates.INSTANCE.config.hidden.dev = !NotEnoughUpdates.INSTANCE.config.hidden.dev reply("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("clearapicache") { ApiCache.clear() NotEnoughUpdates.INSTANCE.manager.ursaClient.clearToken() reply("Cleared API cache and reset ursa token") }.withHelp("Clear the API cache") 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(MC.thePlayer.uniqueID) }.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") Utils.copyToClipboard(tabList) reply("Copied tablist to clipboard!") }.withHelp("Copy the tab list") thenLiteral("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") thenLiteral("crash") { thenExecute { throw object : Error("L") { @Override fun printStackTrace() { throw Error("L") } } } }.withHelp("Crash the game") } 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 } } } }