diff options
| author | Wyvest <wyvestbusiness@gmail.com> | 2023-11-22 08:18:19 +0900 |
|---|---|---|
| committer | Wyvest <wyvestbusiness@gmail.com> | 2023-11-22 08:18:19 +0900 |
| commit | 8b373f577d9c6dde26357ef3fc86691f1efef9b4 (patch) | |
| tree | a5328e995d8f4df21a9fe94ac8e384be08833c70 /src/main/kotlin/org | |
| parent | 64230799777473246b5f98efbc596206c5bbf42d (diff) | |
| download | Chatting-8b373f577d9c6dde26357ef3fc86691f1efef9b4.tar.gz Chatting-8b373f577d9c6dde26357ef3fc86691f1efef9b4.tar.bz2 Chatting-8b373f577d9c6dde26357ef3fc86691f1efef9b4.zip | |
update PGT and relocate to org.polyfrost
Diffstat (limited to 'src/main/kotlin/org')
20 files changed, 2011 insertions, 0 deletions
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<ChatLine, String>().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<ChatLine, String>() + 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<ChatLine, String>): 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<ChatLine, String> -> + 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 + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt new file mode 100644 index 0000000..0d6909e --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt @@ -0,0 +1,11 @@ +package org.polyfrost.chatting.chat + +data class ChatRegexes(val regexList: List<String>?) { + val compiledRegexList: MutableList<Regex> = arrayListOf() + + init { + regexList?.forEach { + compiledRegexList.add(Regex(it)) + } + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt new file mode 100644 index 0000000..982329a --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt @@ -0,0 +1,5 @@ +package org.polyfrost.chatting.chat + +object ChatScrollingHook { + var shouldSmooth = false +}
\ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt new file mode 100644 index 0000000..d20a358 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt @@ -0,0 +1,42 @@ +package org.polyfrost.chatting.chat + +import cc.polyfrost.oneconfig.libs.caffeine.cache.Cache +import cc.polyfrost.oneconfig.libs.caffeine.cache.Caffeine +import cc.polyfrost.oneconfig.libs.universal.wrappers.message.UTextComponent +import net.minecraft.client.gui.ChatLine +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +object ChatSearchingManager { + private var counter: AtomicInteger = AtomicInteger(0) + private var POOL: ThreadPoolExecutor = ThreadPoolExecutor( + 50, 50, + 0L, TimeUnit.SECONDS, + LinkedBlockingQueue() + ) { r -> + Thread( + r, + "Chat Filter Cache Thread ${counter.incrementAndGet()}" + ) + } + + @JvmStatic + val cache: Cache<String, List<ChatLine>> = Caffeine.newBuilder().executor(POOL).maximumSize(5000).build() + + var lastSearch = "" + + @JvmStatic + fun filterMessages(text: String, list: List<ChatLine>): List<ChatLine>? { + if (text.isBlank()) return list + val cached = cache.getIfPresent(text) + return cached ?: run { + cache.put(text, list.filter { + UTextComponent.stripFormatting(it.chatComponent.unformattedText).lowercase() + .contains(text.lowercase()) + }) + cache.getIfPresent(text) + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt new file mode 100644 index 0000000..0c85553 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt @@ -0,0 +1,79 @@ +package org.polyfrost.chatting.chat + +import cc.polyfrost.oneconfig.config.core.ConfigUtils +import org.polyfrost.chatting.Chatting +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import java.io.File + +object ChatShortcuts { + private val oldShortcutsFile = File(Chatting.oldModDir, "chatshortcuts.json") + private val shortcutsFile = ConfigUtils.getProfileFile("chatshortcuts.json") + private val PARSER = JsonParser() + + private var initialized = false + + val shortcuts = object : ArrayList<Pair<String, String>>() { + private val comparator = Comparator<Pair<String, String>> { o1, o2 -> + return@Comparator o2.first.length.compareTo(o1.first.length) + } + + override fun add(element: Pair<String, String>): Boolean { + val value = super.add(element) + sortWith(comparator) + return value + } + } + + fun initialize() { + if (initialized) { + return + } else { + initialized = true + } + if (shortcutsFile.exists()) { + try { + val jsonObj = PARSER.parse(shortcutsFile.readText()).asJsonObject + for (shortcut in jsonObj.entrySet()) { + shortcuts.add(shortcut.key to shortcut.value.asString) + } + return + } catch (_: Throwable) { + shortcutsFile.renameTo(File(shortcutsFile.parentFile, "chatshortcuts.json.bak")) + } + } + shortcutsFile.createNewFile() + if (oldShortcutsFile.exists()) { + shortcutsFile.writeText( + oldShortcutsFile.readText() + ) + } else { + shortcutsFile.writeText(JsonObject().toString()) + } + } + + fun removeShortcut(key: String) { + shortcuts.removeIf { it.first == key } + val jsonObj = PARSER.parse(shortcutsFile.readText()).asJsonObject + jsonObj.remove(key) + shortcutsFile.writeText(jsonObj.toString()) + } + + fun writeShortcut(key: String, value: String) { + shortcuts.add(key to value) + val jsonObj = PARSER.parse(shortcutsFile.readText()).asJsonObject + jsonObj.addProperty(key, value) + shortcutsFile.writeText(jsonObj.toString()) + } + + fun handleSentCommand(command: String): String { + shortcuts.forEach { + if (command == it.first || (command.startsWith(it.first) && command.substringAfter(it.first) + .startsWith(" ")) + ) { + return command.replaceFirst(it.first, it.second) + } + } + return command + } +}
\ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt new file mode 100644 index 0000000..da5dde8 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt @@ -0,0 +1,124 @@ +package org.polyfrost.chatting.chat + +import org.polyfrost.chatting.config.ChattingConfig +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import java.text.Normalizer +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object ChatSpamBlock { + /* + Made by @KTibow + Based off of Unspam (also by @KTibow) + Algorithm based off of https://paulgraham.com/spam.html + */ + private val PLAYER_MESSAGE = Regex("^(\\[VIP\\+?\\] |\\[MVP\\+?\\+?\\] |)(\\w{2,16}): (.*)$") + + @SubscribeEvent + fun onChat(event: ClientChatReceivedEvent) { + val message = event.message.unformattedText.replace(Regex("\u00A7."), "") + if (!PLAYER_MESSAGE.matches(message)) return + + val (rank, player, content) = PLAYER_MESSAGE.matchEntire(message)!!.destructured + + if (ChattingConfig.spamThreshold != 100) { + val tokens = tokenize(content) + val spamProb = findSpamProbability(tokens) + if (spamProb * 100 > ChattingConfig.spamThreshold) { + if (ChattingConfig.hideSpam) { + event.isCanceled = true + } else { + var newMessage = + EnumChatFormatting.DARK_GRAY.toString() + + EnumChatFormatting.STRIKETHROUGH.toString() + if (!ChattingConfig.customChatFormatting) { + newMessage += rank + } + newMessage += "$player${EnumChatFormatting.DARK_GRAY}: $content" + event.message = ChatComponentText(newMessage) + } + return + } + } + if (ChattingConfig.customChatFormatting) { + val coloredPlayer = findRankColor(rank) + player + EnumChatFormatting.RESET.toString() + event.message = ChatComponentText("$coloredPlayer: $content") + } + } + + private fun tokenize(message: String): MutableList<String> { + val strippedMessage = + Normalizer.normalize(message, Normalizer.Form.NFKC) + .replace(Regex("[^\\w\\s/]"), " ") + .lowercase() + .trim() + val tokens = strippedMessage.split(Regex("\\s+")).toMutableList() + if (tokens.size <= 2) { + tokens.add("TINY_LENGTH") + } else if (tokens.size <= 4) { + tokens.add("SMALL_LENGTH") + } else if (tokens.size <= 7) { + tokens.add("MEDIUM_LENGTH") + } else { + tokens.add("LONG_LENGTH") + } + if (message.replace(Regex("[\\w\\s]"), "").length > 2) { + tokens.add("SPECIAL_CHARS") + } else if (message.replace(Regex("[\\w\\s]"), "").isNotEmpty()) { + tokens.add("SPECIAL_CHAR") + } else { + tokens.add("LOW_SPECIAL_CHARS") + } + if (message.replace(Regex("[^A-Z]"), "").length >= message.length / 4) { + tokens.add("HIGH_CAPS") + } else { + tokens.add("LOW_CAPS") + } + return tokens + } + + private fun findSpamProbability(tokens: MutableList<String>): Double { + val tokenProbs = mutableMapOf<String, Double>() + for (token in tokens) { + if (!spamInfoJson.has(token)) continue + val spamInToken = spamInfoJson.get(token).asJsonObject.get("spam").asDouble + val fineInToken = spamInfoJson.get(token).asJsonObject.get("fine").asDouble + tokenProbs[token] = + ((spamInToken / messageCountsJson.get("spam").asInt) / + (fineInToken / messageCountsJson.get("fine").asInt + + spamInToken / messageCountsJson.get("spam").asInt)) + } + val spamProbs = tokenProbs.values.toMutableList() + val fineProbs = tokenProbs.values.map { 1 - it }.toMutableList() + val spamProbability = spamProbs.reduce { a, b -> a * b } + val fineProbability = fineProbs.reduce { a, b -> a * b } + return spamProbability / (spamProbability + fineProbability) + } + + private fun findRankColor(rank: String): String { + println(rank) + return when (rank) { + "[VIP] ", + "[VIP+] " -> EnumChatFormatting.GREEN.toString() + "[MVP] ", + "[MVP+] " -> EnumChatFormatting.AQUA.toString() + "[MVP++] " -> EnumChatFormatting.GOLD.toString() + else -> EnumChatFormatting.GRAY.toString() + } + } + + private fun getResourceAsText(path: String): String? = + object {}.javaClass.getResource(path)?.readText() + private val spamInfoJson: JsonObject + private val messageCountsJson: JsonObject + + init { + // Load the file spamInfo.json from resources/ + val spamInfo = getResourceAsText("/spamInfo.json") + spamInfoJson = JsonParser().parse(spamInfo).asJsonObject + messageCountsJson = JsonParser().parse(" { \"fine\": 668, \"spam\": 230 }").asJsonObject + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt new file mode 100644 index 0000000..bd65f11 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt @@ -0,0 +1,112 @@ +package org.polyfrost.chatting.chat + +import org.polyfrost.chatting.gui.components.TabButton +import com.google.gson.annotations.SerializedName +import net.minecraft.client.Minecraft +import net.minecraft.util.EnumChatFormatting +import net.minecraft.util.IChatComponent +import java.util.* + +data class ChatTab( + val enabled: Boolean, + val name: String, + val unformatted: Boolean, + val lowercase: Boolean?, + @SerializedName("starts") val startsWith: List<String>?, + val contains: List<String>?, + @SerializedName("ends") val endsWith: List<String>?, + val equals: List<String>?, + @SerializedName("regex") val uncompiledRegex: List<String>?, + @SerializedName("ignore_starts") val ignoreStartsWith: List<String>?, + @SerializedName("ignore_contains") val ignoreContains: List<String>?, + @SerializedName("ignore_ends") val ignoreEndsWith: List<String>?, + @SerializedName("ignore_equals") val ignoreEquals: List<String>?, + @SerializedName("ignore_regex") val uncompiledIgnoreRegex: List<String>?, + val color: Int?, + @SerializedName("hovered_color") val hoveredColor: Int?, + @SerializedName("selected_color") val selectedColor: Int?, + val prefix: String?, +) { + lateinit var button: TabButton + lateinit var compiledRegex: ChatRegexes + lateinit var compiledIgnoreRegex: ChatRegexes + + //Ugly hack to make GSON not make button / regex null + fun initialize() { + compiledRegex = ChatRegexes(uncompiledRegex) + compiledIgnoreRegex = ChatRegexes(uncompiledIgnoreRegex) + val width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(name) + button = TabButton(653452, run { + val returnValue = x - 2 + x += 6 + width + return@run returnValue + }, width + 4, 12, this) + } + + fun shouldRender(chatComponent: IChatComponent): Boolean { + val message = + (if (unformatted) EnumChatFormatting.getTextWithoutFormattingCodes(chatComponent.unformattedText) else chatComponent.formattedText).let { + if (lowercase == true) it.lowercase( + Locale.ENGLISH + ) else it + } + ignoreStartsWith?.forEach { + if (message.startsWith(it)) { + return false + } + } + ignoreEquals?.forEach { + if (message == it) { + return false + } + } + ignoreEndsWith?.forEach { + if (message.endsWith(it)) { + return false + } + } + ignoreContains?.forEach { + if (message.contains(it)) { + return false + } + } + compiledIgnoreRegex.compiledRegexList.forEach { + if (it.matches(message)) { + return false + } + } + if (startsWith.isNullOrEmpty() && equals.isNullOrEmpty() && endsWith.isNullOrEmpty() && contains.isNullOrEmpty() && uncompiledRegex.isNullOrEmpty()) { + return true + } + equals?.forEach { + if (message == it) { + return true + } + } + startsWith?.forEach { + if (message.startsWith(it)) { + return true + } + } + endsWith?.forEach { + if (message.endsWith(it)) { + return true + } + } + contains?.forEach { + if (message.contains(it)) { + return true + } + } + compiledRegex.compiledRegexList.forEach { + if (it.matches(message)) { + return true + } + } + return false + } + + companion object { + private var x = 4 + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt new file mode 100644 index 0000000..b46f55d --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt @@ -0,0 +1,354 @@ +package org.polyfrost.chatting.chat + +import cc.polyfrost.oneconfig.config.core.ConfigUtils +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.gui.components.TabButton +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.google.gson.JsonPrimitive +import net.minecraft.client.Minecraft +import net.minecraft.util.IChatComponent +import java.io.File + +object ChatTabs { + private val GSON = GsonBuilder().setPrettyPrinting().create() + private val PARSER = JsonParser() + val tabs = arrayListOf<ChatTab>() + var currentTabs: ArrayList<ChatTab?> = object : ArrayList<ChatTab?>() { + override fun add(element: ChatTab?): Boolean { + if (element == null) return false + val returnValue = super.add(element) + if (Minecraft.getMinecraft().theWorld != null && returnValue) { + Minecraft.getMinecraft().ingameGUI.chatGUI.refreshChat() + } + return returnValue + } + } + var hasCancelledAnimation = false + private var initialized = false + + private val tabFile = ConfigUtils.getProfileFile("chattabs.json") + private val oldTabFile = File(Chatting.oldModDir, "chattabs.json") + + fun initialize() { + if (initialized) { + return + } else { + initialized = true + } + if (!tabFile.exists()) { + if (oldTabFile.exists()) { + tabFile.writeText(oldTabFile.readText()) + handleFile() + } else { + generateNewFile() + } + } else { + handleFile() + } + tabs.forEach { + it.initialize() + } + currentTabs.clear() + currentTabs.add(tabs[0]) + } + + private fun handleFile() { + try { + val chatTabJson = GSON.fromJson(tabFile.readText(), ChatTabsJson::class.java) + when (chatTabJson.version) { + 1 -> { + // ver 2 adds `enabled` + chatTabJson.tabs.forEach { + applyVersion2Changes(it.asJsonObject) + applyVersion3Changes(it.asJsonObject) + applyVersion4Changes(it.asJsonObject) + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 2 -> { + // ver 3 adds ignore_ + chatTabJson.tabs.forEach { + applyVersion3Changes(it.asJsonObject) + applyVersion4Changes(it.asJsonObject) + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 3 -> { + // ver 4 adds color options + chatTabJson |
