/*
* Skytils - Hypixel Skyblock Quality of Life Mod
* Copyright (C) 2021 Skytils
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package skytils.skytilsmod
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import gg.essential.vigilance.gui.SettingsGui
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiButton
import net.minecraft.client.gui.GuiChat
import net.minecraft.client.gui.GuiIngameMenu
import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.settings.KeyBinding
import net.minecraft.launchwrapper.Launch
import net.minecraft.network.play.client.C01PacketChatMessage
import net.minecraft.network.play.server.S1CPacketEntityMetadata
import net.minecraft.util.IChatComponent
import net.minecraftforge.client.ClientCommandHandler
import net.minecraftforge.client.event.GuiOpenEvent
import net.minecraftforge.client.event.GuiScreenEvent
import net.minecraftforge.client.event.RenderGameOverlayEvent
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.common.Loader
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.common.event.FMLInitializationEvent
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
import net.minecraftforge.fml.common.network.FMLNetworkEvent
import org.lwjgl.input.Mouse
import skytils.hylin.HylinAPI.Companion.createHylinAPI
import skytils.skytilsmod.commands.impl.*
import skytils.skytilsmod.commands.stats.impl.CataCommand
import skytils.skytilsmod.commands.stats.impl.SlayerCommand
import skytils.skytilsmod.core.*
import skytils.skytilsmod.events.impl.PacketEvent
import skytils.skytilsmod.features.impl.dungeons.*
import skytils.skytilsmod.features.impl.dungeons.solvers.*
import skytils.skytilsmod.features.impl.dungeons.solvers.terminals.*
import skytils.skytilsmod.features.impl.events.GriffinBurrows
import skytils.skytilsmod.features.impl.events.MayorDiana
import skytils.skytilsmod.features.impl.events.MayorJerry
import skytils.skytilsmod.features.impl.events.TechnoMayor
import skytils.skytilsmod.features.impl.farming.FarmingFeatures
import skytils.skytilsmod.features.impl.farming.TreasureHunter
import skytils.skytilsmod.features.impl.handlers.*
import skytils.skytilsmod.features.impl.mining.DarkModeMist
import skytils.skytilsmod.features.impl.mining.MiningFeatures
import skytils.skytilsmod.features.impl.mining.StupidTreasureChestOpeningThing
import skytils.skytilsmod.features.impl.misc.*
import skytils.skytilsmod.features.impl.overlays.AuctionPriceOverlay
import skytils.skytilsmod.features.impl.protectitems.ProtectItems
import skytils.skytilsmod.features.impl.spidersden.RainTimer
import skytils.skytilsmod.features.impl.spidersden.RelicWaypoints
import skytils.skytilsmod.features.impl.spidersden.SpidersDenFeatures
import skytils.skytilsmod.features.impl.trackers.impl.MayorJerryTracker
import skytils.skytilsmod.features.impl.trackers.impl.MythologicalTracker
import skytils.skytilsmod.gui.OptionsGui
import skytils.skytilsmod.gui.ReopenableGUI
import skytils.skytilsmod.listeners.ChatListener
import skytils.skytilsmod.listeners.DungeonListener
import skytils.skytilsmod.mixins.extensions.ExtensionEntityLivingBase
import skytils.skytilsmod.mixins.transformers.accessors.AccessorCommandHandler
import skytils.skytilsmod.mixins.transformers.accessors.AccessorGuiNewChat
import skytils.skytilsmod.mixins.transformers.accessors.AccessorSettingsGui
import skytils.skytilsmod.utils.*
import skytils.skytilsmod.utils.graphics.ScreenRenderer
import java.io.File
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.concurrent.Executors
import java.util.concurrent.ThreadPoolExecutor
@Mod(
modid = Skytils.MODID,
name = Skytils.MOD_NAME,
version = Skytils.VERSION,
acceptedMinecraftVersions = "[1.8.9]",
clientSideOnly = true
)
class Skytils {
companion object {
const val MODID = "skytils"
const val MOD_NAME = "Skytils"
const val VERSION = "1.0.8-RC1"
@JvmField
val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Enchant::class.java, Enchant.Serializer())
.create()
@JvmStatic
val mc: Minecraft
get() = Minecraft.getMinecraft()
val config = Config
@JvmField
val modDir = File(File(mc.mcDataDir, "config"), "skytils")
@JvmStatic
lateinit var guiManager: GuiManager
var ticks = 0
@JvmField
val sendMessageQueue = ArrayDeque()
@JvmField
var usingDungeonRooms = false
@JvmField
var usingLabymod = false
@JvmField
var usingNEU = false
@JvmField
var jarFile: File? = null
private var lastChatMessage = 0L
@JvmField
var displayScreen: GuiScreen? = null
@JvmField
val threadPool = Executors.newFixedThreadPool(10) as ThreadPoolExecutor
val hylinAPI = createHylinAPI("", false)
}
@Mod.EventHandler
fun preInit(event: FMLPreInitializationEvent) {
if (!modDir.exists()) modDir.mkdirs()
File(modDir, "trackers").mkdirs()
guiManager = GuiManager()
jarFile = event.sourceFile
Minecraft.getMinecraft().framebuffer.enableStencil();
}
@Mod.EventHandler
fun init(event: FMLInitializationEvent) {
config.init()
hylinAPI.key = config.apiKey
UpdateChecker.downloadDeleteTask()
MinecraftForge.EVENT_BUS.register(this)
MinecraftForge.EVENT_BUS.register(ChatListener())
MinecraftForge.EVENT_BUS.register(DungeonListener)
MinecraftForge.EVENT_BUS.register(guiManager)
MinecraftForge.EVENT_BUS.register(MayorInfo)
MinecraftForge.EVENT_BUS.register(SBInfo)
MinecraftForge.EVENT_BUS.register(SoundQueue)
MinecraftForge.EVENT_BUS.register(TickTaskManager)
MinecraftForge.EVENT_BUS.register(UpdateChecker)
MinecraftForge.EVENT_BUS.register(AlignmentTaskSolver())
MinecraftForge.EVENT_BUS.register(ArmorColor())
MinecraftForge.EVENT_BUS.register(AuctionData())
MinecraftForge.EVENT_BUS.register(AuctionPriceOverlay())
MinecraftForge.EVENT_BUS.register(BlazeSolver())
MinecraftForge.EVENT_BUS.register(BossHPDisplays())
MinecraftForge.EVENT_BUS.register(BoulderSolver())
MinecraftForge.EVENT_BUS.register(ChatTabs)
MinecraftForge.EVENT_BUS.register(ChestProfit())
MinecraftForge.EVENT_BUS.register(ClickInOrderSolver())
MinecraftForge.EVENT_BUS.register(CreeperSolver())
MinecraftForge.EVENT_BUS.register(CommandAliases())
MinecraftForge.EVENT_BUS.register(CooldownTracker())
MinecraftForge.EVENT_BUS.register(DamageSplash())
MinecraftForge.EVENT_BUS.register(DarkModeMist())
MinecraftForge.EVENT_BUS.register(DungeonFeatures())
MinecraftForge.EVENT_BUS.register(DungeonMap())
MinecraftForge.EVENT_BUS.register(DungeonTimer())
MinecraftForge.EVENT_BUS.register(EnchantNames())
MinecraftForge.EVENT_BUS.register(FarmingFeatures())
MinecraftForge.EVENT_BUS.register(FavoritePets())
MinecraftForge.EVENT_BUS.register(GlintCustomizer())
MinecraftForge.EVENT_BUS.register(GriffinBurrows())
MinecraftForge.EVENT_BUS.register(IceFillSolver())
MinecraftForge.EVENT_BUS.register(IcePathSolver())
MinecraftForge.EVENT_BUS.register(ItemFeatures())
MinecraftForge.EVENT_BUS.register(KeyShortcuts())
MinecraftForge.EVENT_BUS.register(LockOrb())
MinecraftForge.EVENT_BUS.register(MayorDiana())
MinecraftForge.EVENT_BUS.register(MayorJerry())
MinecraftForge.EVENT_BUS.register(MayorJerryTracker)
MinecraftForge.EVENT_BUS.register(MiningFeatures())
MinecraftForge.EVENT_BUS.register(MinionFeatures())
MinecraftForge.EVENT_BUS.register(MiscFeatures())
MinecraftForge.EVENT_BUS.register(MythologicalTracker())
MinecraftForge.EVENT_BUS.register(PetFeatures())
MinecraftForge.EVENT_BUS.register(Ping)
MinecraftForge.EVENT_BUS.register(ProtectItems())
MinecraftForge.EVENT_BUS.register(RainTimer())
MinecraftForge.EVENT_BUS.register(RelicWaypoints())
MinecraftForge.EVENT_BUS.register(ScoreCalculation)
MinecraftForge.EVENT_BUS.register(SelectAllColorSolver())
MinecraftForge.EVENT_BUS.register(ShootTheTargetSolver())
MinecraftForge.EVENT_BUS.register(SimonSaysSolver())
MinecraftForge.EVENT_BUS.register(SlayerFeatures())
MinecraftForge.EVENT_BUS.register(SpidersDenFeatures())
MinecraftForge.EVENT_BUS.register(SpiritLeap())
MinecraftForge.EVENT_BUS.register(StartsWithSequenceSolver())
MinecraftForge.EVENT_BUS.register(StupidTreasureChestOpeningThing)
MinecraftForge.EVENT_BUS.register(TankDisplayStuff())
MinecraftForge.EVENT_BUS.register(TechnoMayor())
MinecraftForge.EVENT_BUS.register(TeleportMazeSolver())
MinecraftForge.EVENT_BUS.register(TerminalFeatures())
MinecraftForge.EVENT_BUS.register(ThreeWeirdosSolver())
MinecraftForge.EVENT_BUS.register(TicTacToeSolver())
MinecraftForge.EVENT_BUS.register(TreasureHunter())
MinecraftForge.EVENT_BUS.register(TriviaSolver())
MinecraftForge.EVENT_BUS.register(WaterBoardSolver())
MinecraftForge.EVENT_BUS.register(Waypoints())
}
@Mod.EventHandler
fun postInit(event: FMLPostInitializationEvent) {
usingDungeonRooms = Loader.isModLoaded("dungeonrooms")
usingLabymod = Loader.isModLoaded("labymod")
usingNEU = Loader.isModLoaded("notenoughupdates")
if (usingDungeonRooms) {
if (Loader.instance().indexedModList["dungeonrooms"]!!.version.startsWith("2.0")) {
runCatching {
Class.forName("io.github.quantizr.utils.Utils").also {
ScoreCalculation.drmRoomScanMethod = MethodHandles.publicLookup().findStatic(
it, "roomList", MethodType.methodType(
List::class.java
)
)
}
}
} else {
config.scoreCalculationMethod = 0
}
}
val cch = ClientCommandHandler.instance
if (cch !is AccessorCommandHandler) throw RuntimeException("Skytils was unable to mixin to the CommandHandler. Please report this on our Discord at discord.gg/skytils.")
cch.registerCommand(SkytilsCommand)
cch.registerCommand(CataCommand)
cch.registerCommand(CalcXPCommand)
cch.registerCommand(HollowWaypointCommand)
cch.registerCommand(SlayerCommand)
if (!cch.commands.containsKey("armorcolor")) {
cch.registerCommand(ArmorColorCommand)
}
if (!cch.commands.containsKey("glintcustomize")) {
cch.registerCommand(GlintCustomizeCommand)
}
if (!cch.commands.containsKey("trackcooldown")) {
cch.registerCommand(TrackCooldownCommand)
}
cch.commandSet.add(RepartyCommand)
cch.commandMap["skytilsreparty"] = RepartyCommand
if (config.overrideReparty || !cch.commands.containsKey("reparty")) {
cch.commandMap["reparty"] = RepartyCommand
}
if (config.overrideReparty || !cch.commands.containsKey("rp")) {
cch.commandMap["rp"] = RepartyCommand
}
DataFetcher.preload()
MayorInfo.fetchMayorData()
MinecraftForge.EVENT_BUS.register(SpamHider())
Launch.classLoader.findClass("net.minecraft.client.gui.ServerListEntryNormal")
}
@SubscribeEvent
fun onTick(event: TickEvent.ClientTickEvent) {
if (event.phase != TickEvent.Phase.START) return
ScreenRenderer.refresh()
if (displayScreen != null) {
mc.displayGuiScreen(displayScreen)
displayScreen = null
}
if (mc.thePlayer != null && sendMessageQueue.isNotEmpty() && System.currentTimeMillis() - lastChatMessage > 250) {
val msg = sendMessageQueue.removeFirstOrNull()
if (!msg.isNullOrBlank()) mc.thePlayer.sendChatMessage(msg)
}
if (ticks % 20 == 0) {
if (mc.thePlayer != null) {
Utils.isOnHypixel = mc.runCatching {
theWorld != null && !isSingleplayer && (thePlayer?.clientBrand?.lowercase()?.contains("hypixel")
?: currentServerData?.serverIP?.lowercase()?.contains("hypixel") ?: false)
}.onFailure { it.printStackTrace() }.getOrDefault(false)
Utils.inSkyblock = Utils.isOnHypixel && mc.theWorld.scoreboard.getObjectiveInDisplaySlot(1)
?.let { ScoreboardUtil.cleanSB(it.displayName).contains("SKYBLOCK") } ?: false
Utils.inDungeons = Utils.inSkyblock && ScoreboardUtil.sidebarLines.any {
ScoreboardUtil.cleanSB(it).run {
(contains("The Catacombs") && !contains("Queue")) || contains("Dungeon Cleared:")
}
}
if (DevTools.getToggle("sprint"))
KeyBinding.setKeyBindState(mc.gameSettings.keyBindSprint.keyCode, true)
}
ticks = 0
}
ticks++
}
@SubscribeEvent
fun onDisconnect(event: FMLNetworkEvent.ClientDisconnectionFromServerEvent) {
Utils.isOnHypixel = false
Utils.inSkyblock = false
Utils.inDungeons = false
}
@SubscribeEvent
fun onSendPacket(event: PacketEvent.SendEvent) {
if (event.packet is C01PacketChatMessage) {
lastChatMessage = System.currentTimeMillis()
}
}
@SubscribeEvent
fun onRenderGameOverlay(event: RenderGameOverlayEvent) {
if (mc.currentScreen is OptionsGui && event.type == RenderGameOverlayEvent.ElementType.CROSSHAIRS) {
event.isCanceled = true
}
}
@SubscribeEvent
fun onGuiInitPost(event: GuiScreenEvent.InitGuiEvent.Post) {
if (config.configButtonOnPause && event.gui is GuiIngameMenu) {
val x = event.gui.width - 105
val x2 = x + 100
var y = event.gui.height - 22
var y2 = y + 20
val sorted = event.buttonList.sortedWith { a, b -> b.yPosition + b.height - a.yPosition + a.height }
for (button in sorted) {
val otherX = button.xPosition
val otherX2 = button.xPosition + button.width
val otherY = button.yPosition
val otherY2 = button.yPosition + button.height
if (otherX2 > x && otherX < x2 && otherY2 > y && otherY < y2) {
y = otherY - 20 - 2
y2 = y + 20
}
}
event.buttonList.add(GuiButton(6969420, x, 0.coerceAtLeast(y), 100, 20, "Skytils"))
}
}
@SubscribeEvent
fun onGuiAction(event: GuiScreenEvent.ActionPerformedEvent.Post) {
if (config.configButtonOnPause && event.gui is GuiIngameMenu && event.button.id == 6969420) {
displayScreen = OptionsGui()
}
}
@SubscribeEvent
fun onGuiChange(event: GuiOpenEvent) {
val old = mc.currentScreen
if (event.gui == null && config.reopenOptionsMenu) {
if (old is ReopenableGUI || (old is SettingsGui && (old as AccessorSettingsGui).config is Config)) {
TickTask(1) {
displayScreen = OptionsGui()
}
}
}
}
@SubscribeEvent
fun onMouseClick(event: GuiScreenEvent.MouseInputEvent.Pre) {
if (event.gui is GuiChat && DevTools.getToggle("chat") && GuiScreen.isCtrlKeyDown() && Mouse.getEventButtonState()) {
val button = Mouse.getEventButton()
if (button != 0 && button != 1) return
val chatLine = mc.ingameGUI.chatGUI.getChatLine(Mouse.getX(), Mouse.getY()) ?: return
if (button == 0) {
val component =
(mc.ingameGUI.chatGUI as AccessorGuiNewChat).chatLines.find { it.chatLineID == chatLine.chatLineID }?.chatComponent
?: chatLine.chatComponent
val realText = buildString {
append(component.unformattedTextForChat)
append("§r")
component.siblings.forEach {
append(it.unformattedTextForChat)
append("§r")
}
}
GuiScreen.setClipboardString(realText)
printDevMessage("Copied formatted message to clipboard!", "chat")
} else {
val component =
(mc.ingameGUI.chatGUI as AccessorGuiNewChat).chatLines.find { it.chatLineID == chatLine.chatLineID }?.chatComponent
?: chatLine.chatComponent
printDevMessage("Copied serialized message to clipboard!", "chat")
GuiScreen.setClipboardString(
IChatComponent.Serializer.componentToJson(
component
)
)
}
}
}
@SubscribeEvent
fun onReceivePacket(event: PacketEvent.ReceiveEvent) {
if (event.packet is S1CPacketEntityMetadata) {
val nameObj = event.packet.func_149376_c()?.find { it.dataValueId == 2 }
if (nameObj != null) {
val entity = mc.theWorld.getEntityByID(event.packet.entityId)
if (entity is ExtensionEntityLivingBase) {
entity.skytilsHook.onNewDisplayName(nameObj.`object` as String)
}
}
}
}
}