From 8b373f577d9c6dde26357ef3fc86691f1efef9b4 Mon Sep 17 00:00:00 2001 From: Wyvest Date: Wed, 22 Nov 2023 08:18:19 +0900 Subject: update PGT and relocate to org.polyfrost --- src/main/kotlin/org/polyfrost/chatting/Chatting.kt | 272 +++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 src/main/kotlin/org/polyfrost/chatting/Chatting.kt (limited to 'src/main/kotlin/org/polyfrost/chatting/Chatting.kt') diff --git a/src/main/kotlin/org/polyfrost/chatting/Chatting.kt b/src/main/kotlin/org/polyfrost/chatting/Chatting.kt new file mode 100644 index 0000000..0e8745c --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/Chatting.kt @@ -0,0 +1,272 @@ +package org.polyfrost.chatting + +import cc.polyfrost.oneconfig.libs.universal.UDesktop +import cc.polyfrost.oneconfig.libs.universal.UMinecraft +import cc.polyfrost.oneconfig.libs.universal.UResolution +import cc.polyfrost.oneconfig.utils.Notifications +import cc.polyfrost.oneconfig.utils.commands.CommandManager +import cc.polyfrost.oneconfig.utils.dsl.browseLink +import org.polyfrost.chatting.chat.ChatSearchingManager +import org.polyfrost.chatting.chat.ChatShortcuts +import org.polyfrost.chatting.chat.ChatSpamBlock +import org.polyfrost.chatting.chat.ChatTabs +import org.polyfrost.chatting.command.ChattingCommand +import org.polyfrost.chatting.config.ChattingConfig +import org.polyfrost.chatting.utils.ModCompatHooks +import org.polyfrost.chatting.utils.copyToClipboard +import org.polyfrost.chatting.utils.createBindFramebuffer +import org.polyfrost.chatting.utils.screenshot +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.* +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.OpenGlHelper +import net.minecraft.client.settings.KeyBinding +import net.minecraft.client.shader.Framebuffer +import net.minecraft.util.MathHelper +import net.minecraftforge.client.event.RenderGameOverlayEvent +import net.minecraftforge.common.MinecraftForge.EVENT_BUS +import net.minecraftforge.fml.client.registry.ClientRegistry +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.FMLLoadCompleteEvent +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import org.lwjgl.input.Keyboard +import org.polyfrost.chatting.hook.ChatLineHook +import org.polyfrost.chatting.mixin.GuiNewChatAccessor +import java.awt.image.BufferedImage +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + + +@Mod( + modid = Chatting.ID, + name = Chatting.NAME, + version = Chatting.VER, + modLanguageAdapter = "cc.polyfrost.oneconfig.utils.KotlinLanguageAdapter" +) +object Chatting { + + val keybind = KeyBinding("Screenshot Chat", Keyboard.KEY_NONE, "Chatting") + const val NAME = "@NAME@" + const val VER = "@VER@" + const val ID = "@ID@" + var doTheThing = false + var isPatcher = false + private set + var isBetterChat = false + private set + var isSkytils = false + private set + var isHychat = false + private set + + private var time = -1L + var deltaTime = 17L + + private val fileFormatter: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd_HH.mm.ss'.png'") + + val oldModDir = File(File(Minecraft.getMinecraft().mcDataDir, "W-OVERFLOW"), NAME) + + @Mod.EventHandler + fun onInitialization(event: FMLInitializationEvent) { + ChattingConfig + CommandManager.INSTANCE.registerCommand(ChattingCommand()) + ClientRegistry.registerKeyBinding(keybind) + EVENT_BUS.register(this) + EVENT_BUS.register(ChatSpamBlock) + ChatTabs.initialize() + ChatShortcuts.initialize() + } + + @Mod.EventHandler + fun onPostInitialization(event: FMLPostInitializationEvent) { + isPatcher = Loader.isModLoaded("patcher") + isBetterChat = Loader.isModLoaded("betterchat") + isSkytils = Loader.isModLoaded("skytils") + isHychat = Loader.isModLoaded("hychat") + } + + @Mod.EventHandler + fun onForgeLoad(event: FMLLoadCompleteEvent) { + if (ChattingConfig.informForAlternatives) { + if (isHychat) { + Notifications.INSTANCE.send( + NAME, + "Hychat can be removed as it is replaced by Chatting. Click here for more information.", + Runnable { + UDesktop.browseLink("https://microcontrollersdev.github.io/Alternatives/1.8.9/hychat") + }) + } + if (isSkytils) { + try { + skytilsCompat(Class.forName("gg.skytils.skytilsmod.core.Config")) + } catch (e: Exception) { + e.printStackTrace() + try { + skytilsCompat(Class.forName("skytils.skytilsmod.core.Config")) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + if (isBetterChat) { + Notifications.INSTANCE.send( + NAME, + "BetterChat can be removed as it is replaced by Chatting. Click here to open your mods folder to delete the BetterChat file.", + Runnable { + UDesktop.open(File("./mods")) + }) + } + } + } + + private fun skytilsCompat(skytilsClass: Class<*>) { + val instance = skytilsClass.getDeclaredField("INSTANCE") + val chatTabs = skytilsClass.getDeclaredField("chatTabs") + chatTabs.isAccessible = true + if (chatTabs.getBoolean(instance)) { + Notifications.INSTANCE.send( + NAME, + "Skytils' chat tabs can be disabled as it is replace by Chatting.\nClick here to automatically do this.", + Runnable { + chatTabs.setBoolean(instance, false) + ChattingConfig.chatTabs = true + ChattingConfig.hypixelOnlyChatTabs = true + ChattingConfig.save() + skytilsClass.getMethod("markDirty").invoke(instance) + skytilsClass.getMethod("writeData").invoke(instance) + }) + } + val copyChat = skytilsClass.getDeclaredField("copyChat") + copyChat.isAccessible = true + if (copyChat.getBoolean(instance)) { + Notifications.INSTANCE.send( + NAME, + "Skytils' copy chat messages can be disabled as it is replace by Chatting.\nClick here to automatically do this.", + Runnable { + copyChat.setBoolean(instance, false) + skytilsClass.getMethod("markDirty").invoke(instance) + skytilsClass.getMethod("writeData").invoke(instance) + }) + } + } + + @SubscribeEvent + fun onRenderTick(event: RenderGameOverlayEvent.Pre) { + if (event.type == RenderGameOverlayEvent.ElementType.ALL) { + if (time == -1L) { + time = UMinecraft.getTime() + } else { + val currentTime = UMinecraft.getTime() + deltaTime = currentTime - time + time = currentTime + } + } + } + + @SubscribeEvent + fun onTickEvent(event: TickEvent.ClientTickEvent) { + if (event.phase == TickEvent.Phase.START && Minecraft.getMinecraft().theWorld != null && Minecraft.getMinecraft().thePlayer != null && (Minecraft.getMinecraft().currentScreen == null || Minecraft.getMinecraft().currentScreen is GuiChat)) { + if (doTheThing) { + screenshotChat() + doTheThing = false + } + } + } + + fun getChatHeight(opened: Boolean): Int { + var height = if (opened) ChattingConfig.focusedHeight else ChattingConfig.unfocusedHeight + height = (height * Minecraft.getMinecraft().gameSettings.chatScale).toInt() + val chatY = ModCompatHooks.yOffset + ModCompatHooks.chatPosition + if (height + chatY + 27 > (UResolution.scaledHeight / Minecraft.getMinecraft().gameSettings.chatScale).toInt() - 27 - chatY) { + height = (UResolution.scaledHeight / Minecraft.getMinecraft().gameSettings.chatScale).toInt() - 27 - chatY + } + return height + } + + fun screenshotLine(line: ChatLine): BufferedImage? { + val hud = Minecraft.getMinecraft().ingameGUI + val chat = hud.chatGUI + val i = MathHelper.floor_float(chat.chatWidth / chat.chatScale) + return screenshot( + hashMapOf().also { + GuiUtilRenderComponents.splitText( + line.chatComponent, + i, + Minecraft.getMinecraft().fontRendererObj, + false, + false + ).map { it.formattedText }.reversed().forEach { string -> + it[line] = string + } + } + ) + } + + private fun screenshotChat() { + screenshotChat(0) + } + + fun screenshotChat(scrollPos: Int) { + val hud = Minecraft.getMinecraft().ingameGUI + val chat = hud.chatGUI + val chatLines = LinkedHashMap() + ChatSearchingManager.filterMessages( + ChatSearchingManager.lastSearch, + (chat as GuiNewChatAccessor).drawnChatLines + )?.let { drawnLines -> + val chatHeight = + if (ChattingConfig.customChatHeight) getChatHeight(true) / 9 else GuiNewChat.calculateChatboxHeight( + Minecraft.getMinecraft().gameSettings.chatHeightFocused / 9 + ) + for (i in scrollPos until drawnLines.size.coerceAtMost(scrollPos + chatHeight)) { + chatLines[drawnLines[i]] = drawnLines[i].chatComponent.formattedText + } + + screenshot(chatLines)?.copyToClipboard() + } + } + + private fun screenshot(messages: HashMap): BufferedImage? { + if (messages.isEmpty()) { + Notifications.INSTANCE.send("Chatting", "Chat window is empty.") + return null + } + if (!OpenGlHelper.isFramebufferEnabled()) { + Notifications.INSTANCE.send( + "Chatting", + "Screenshot failed, please disable “Fast Render” in OptiFine’s “Performance” tab." + ) + return null + } + + val fr: FontRenderer = ModCompatHooks.fontRenderer + val width = messages.maxOf { fr.getStringWidth(it.value) + (if (ChattingConfig.showChatHeads && ((it.key as ChatLineHook).hasDetected() || ChattingConfig.offsetNonPlayerMessages)) 10 else 0) } + 4 + val fb: Framebuffer = createBindFramebuffer(width * 2, (messages.size * 9) * 2) + val file = File(Minecraft.getMinecraft().mcDataDir, "screenshots/chat/" + fileFormatter.format(Date())) + + GlStateManager.scale(2f, 2f, 1f) + val scale = Minecraft.getMinecraft().gameSettings.chatScale + GlStateManager.scale(scale, scale, 1f) + messages.entries.forEachIndexed { i: Int, entry: MutableMap.MutableEntry -> + ModCompatHooks.redirectDrawString(entry.value, 0f, (messages.size - 1 - i) * 9f, 0xffffff, entry.key, true) + } + + val image = fb.screenshot(file) + Minecraft.getMinecraft().entityRenderer.setupOverlayRendering() + Minecraft.getMinecraft().framebuffer.bindFramebuffer(true) + Notifications.INSTANCE.send( + "Chatting", + "Chat screenshotted successfully." + (if (ChattingConfig.copyMode != 1) "\nClick to open." else ""), + Runnable { + if (!UDesktop.open(file)) { + Notifications.INSTANCE.send("Chatting", "Could not browse!") + } + }) + return image + } +} -- cgit