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 --- .../cc/woverflow/chatting/hook/ChatLineHook.java | 17 - .../cc/woverflow/chatting/hook/GuiNewChatHook.java | 21 -- .../cc/woverflow/chatting/mixin/ChatLineMixin.java | 114 ------- .../chatting/mixin/ClientCommandHandlerMixin.java | 33 -- .../chatting/mixin/EntityPlayerSPMixin.java | 41 --- .../cc/woverflow/chatting/mixin/GuiChatMixin.java | 141 -------- .../chatting/mixin/GuiNewChatAccessor.java | 20 -- .../chatting/mixin/GuiNewChatMapMixin.java | 78 ----- .../woverflow/chatting/mixin/GuiNewChatMixin.java | 259 --------------- .../chatting/mixin/GuiNewChatMixin_ChatHeight.java | 21 -- .../mixin/GuiNewChatMixin_ChatSearching.java | 31 -- .../chatting/mixin/GuiNewChatMixin_ChatTabs.java | 54 ---- .../chatting/mixin/GuiNewChatMixin_Scrolling.java | 71 ----- .../mixin/GuiNewChatMixin_SmoothMessages.java | 103 ------ .../mixin/GuiNewChatMixin_TextRendering.java | 29 -- .../cc/woverflow/chatting/mixin/GuiUtilsMixin.java | 36 --- .../org/polyfrost/chatting/hook/ChatLineHook.java | 17 + .../polyfrost/chatting/hook/GuiNewChatHook.java | 21 ++ .../polyfrost/chatting/mixin/ChatLineMixin.java | 114 +++++++ .../chatting/mixin/ClientCommandHandlerMixin.java | 33 ++ .../chatting/mixin/EntityPlayerSPMixin.java | 41 +++ .../org/polyfrost/chatting/mixin/GuiChatMixin.java | 143 +++++++++ .../chatting/mixin/GuiNewChatAccessor.java | 20 ++ .../chatting/mixin/GuiNewChatMapMixin.java | 78 +++++ .../polyfrost/chatting/mixin/GuiNewChatMixin.java | 257 +++++++++++++++ .../chatting/mixin/GuiNewChatMixin_ChatHeight.java | 21 ++ .../mixin/GuiNewChatMixin_ChatSearching.java | 31 ++ .../chatting/mixin/GuiNewChatMixin_ChatTabs.java | 54 ++++ .../chatting/mixin/GuiNewChatMixin_Scrolling.java | 71 +++++ .../mixin/GuiNewChatMixin_SmoothMessages.java | 103 ++++++ .../mixin/GuiNewChatMixin_TextRendering.java | 29 ++ .../polyfrost/chatting/mixin/GuiUtilsMixin.java | 36 +++ src/main/kotlin/cc/woverflow/chatting/Chatting.kt | 272 ---------------- .../cc/woverflow/chatting/chat/ChatRegexes.kt | 11 - .../woverflow/chatting/chat/ChatScrollingHook.kt | 5 - .../chatting/chat/ChatSearchingManager.kt | 42 --- .../cc/woverflow/chatting/chat/ChatShortcuts.kt | 79 ----- .../cc/woverflow/chatting/chat/ChatSpamBlock.kt | 124 -------- .../kotlin/cc/woverflow/chatting/chat/ChatTab.kt | 112 ------- .../kotlin/cc/woverflow/chatting/chat/ChatTabs.kt | 354 --------------------- .../cc/woverflow/chatting/chat/ChatTabsJson.kt | 15 - .../woverflow/chatting/command/ChattingCommand.kt | 14 - .../cc/woverflow/chatting/config/ChattingConfig.kt | 313 ------------------ .../chatting/gui/components/CleanButton.kt | 103 ------ .../chatting/gui/components/ClearButton.kt | 42 --- .../chatting/gui/components/RenderType.kt | 7 - .../chatting/gui/components/ScreenshotButton.kt | 36 --- .../chatting/gui/components/SearchButton.kt | 70 ---- .../woverflow/chatting/gui/components/TabButton.kt | 46 --- .../cc/woverflow/chatting/utils/EaseOutQuart.kt | 7 - .../cc/woverflow/chatting/utils/ModCompatHooks.kt | 100 ------ .../cc/woverflow/chatting/utils/RenderUtils.kt | 259 --------------- src/main/kotlin/org/polyfrost/chatting/Chatting.kt | 272 ++++++++++++++++ .../org/polyfrost/chatting/chat/ChatRegexes.kt | 11 + .../polyfrost/chatting/chat/ChatScrollingHook.kt | 5 + .../chatting/chat/ChatSearchingManager.kt | 42 +++ .../org/polyfrost/chatting/chat/ChatShortcuts.kt | 79 +++++ .../org/polyfrost/chatting/chat/ChatSpamBlock.kt | 124 ++++++++ .../kotlin/org/polyfrost/chatting/chat/ChatTab.kt | 112 +++++++ .../kotlin/org/polyfrost/chatting/chat/ChatTabs.kt | 354 +++++++++++++++++++++ .../org/polyfrost/chatting/chat/ChatTabsJson.kt | 15 + .../polyfrost/chatting/command/ChattingCommand.kt | 14 + .../polyfrost/chatting/config/ChattingConfig.kt | 313 ++++++++++++++++++ .../chatting/gui/components/CleanButton.kt | 103 ++++++ .../chatting/gui/components/ClearButton.kt | 42 +++ .../chatting/gui/components/RenderType.kt | 7 + .../chatting/gui/components/ScreenshotButton.kt | 36 +++ .../chatting/gui/components/SearchButton.kt | 70 ++++ .../polyfrost/chatting/gui/components/TabButton.kt | 46 +++ .../org/polyfrost/chatting/utils/EaseOutQuart.kt | 7 + .../org/polyfrost/chatting/utils/ModCompatHooks.kt | 100 ++++++ .../org/polyfrost/chatting/utils/RenderUtils.kt | 259 +++++++++++++++ src/main/resources/mixins.chatting.json | 30 +- 73 files changed, 3095 insertions(+), 3095 deletions(-) delete mode 100644 src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java delete mode 100644 src/main/java/cc/woverflow/chatting/hook/GuiNewChatHook.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/ClientCommandHandlerMixin.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/EntityPlayerSPMixin.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiChatMixin.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatAccessor.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMapMixin.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatHeight.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatSearching.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatTabs.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_Scrolling.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_SmoothMessages.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_TextRendering.java delete mode 100644 src/main/java/cc/woverflow/chatting/mixin/GuiUtilsMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java create mode 100644 src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java create mode 100644 src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java delete mode 100644 src/main/kotlin/cc/woverflow/chatting/Chatting.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatRegexes.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatScrollingHook.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatSearchingManager.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatShortcuts.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatTab.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatTabs.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/chat/ChatTabsJson.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/command/ChattingCommand.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/gui/components/CleanButton.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/gui/components/ClearButton.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/gui/components/RenderType.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/gui/components/ScreenshotButton.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/gui/components/SearchButton.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/gui/components/TabButton.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/utils/EaseOutQuart.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/utils/ModCompatHooks.kt delete mode 100644 src/main/kotlin/cc/woverflow/chatting/utils/RenderUtils.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/Chatting.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/command/ChattingCommand.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/config/ChattingConfig.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/gui/components/CleanButton.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/gui/components/ClearButton.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/gui/components/RenderType.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/gui/components/ScreenshotButton.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/gui/components/SearchButton.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/gui/components/TabButton.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/utils/EaseOutQuart.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt create mode 100644 src/main/kotlin/org/polyfrost/chatting/utils/RenderUtils.kt (limited to 'src') diff --git a/src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java b/src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java deleted file mode 100644 index 9460e0c..0000000 --- a/src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java +++ /dev/null @@ -1,17 +0,0 @@ -package cc.woverflow.chatting.hook; - -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.network.NetworkPlayerInfo; - -import java.lang.ref.WeakReference; -import java.util.HashSet; - -public interface ChatLineHook { - HashSet> chatLines = new HashSet<>(); - boolean hasDetected(); - NetworkPlayerInfo getPlayerInfo(); - - void updatePlayerInfo(); - - long getUniqueId(); -} diff --git a/src/main/java/cc/woverflow/chatting/hook/GuiNewChatHook.java b/src/main/java/cc/woverflow/chatting/hook/GuiNewChatHook.java deleted file mode 100644 index 19da778..0000000 --- a/src/main/java/cc/woverflow/chatting/hook/GuiNewChatHook.java +++ /dev/null @@ -1,21 +0,0 @@ -package cc.woverflow.chatting.hook; - -import net.minecraft.client.gui.ChatLine; - -import java.awt.datatransfer.Transferable; - -public interface GuiNewChatHook { - int getRight(); - - boolean isHovering(); - - ChatLine getHoveredLine(int mouseY); - - Transferable getChattingChatComponent(int mouseY); - - default ChatLine getFullMessage(ChatLine line) { - throw new AssertionError("getFullMessage not overridden on GuiNewChat"); - } - - int getTextOpacity(); -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java b/src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java deleted file mode 100644 index beef37b..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is from chat_heads is licensed under MPL-2.0, which can be found at https://www.mozilla.org/en-US/MPL/2.0/ - * See: https://github.com/dzwdz/chat_heads/blob/fabric-1.16.x/LICENSE - */ - -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.config.ChattingConfig; -import cc.woverflow.chatting.hook.ChatLineHook; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.client.network.NetworkPlayerInfo; -import net.minecraft.util.IChatComponent; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Map; - -@Mixin(ChatLine.class) -public class ChatLineMixin implements ChatLineHook { - private boolean detected = false; - private boolean first = true; - private NetworkPlayerInfo playerInfo; - private NetworkPlayerInfo detectedPlayerInfo; - private static NetworkPlayerInfo lastPlayerInfo; - private static long lastUniqueId = 0; - private long uniqueId = 0; - - @Inject(method = "", at = @At("RETURN")) - private void onInit(int i, IChatComponent iChatComponent, int j, CallbackInfo ci) { - lastUniqueId++; - uniqueId = lastUniqueId; - chatLines.add(new WeakReference<>((ChatLine) (Object) this)); - NetHandlerPlayClient netHandler = Minecraft.getMinecraft().getNetHandler(); - if (netHandler == null) return; - Map nicknameCache = new HashMap<>(); - try { - for (String word : iChatComponent.getFormattedText().split("(§.)|\\W")) { - if (word.isEmpty()) continue; - playerInfo = netHandler.getPlayerInfo(word); - if (playerInfo == null) { - playerInfo = getPlayerFromNickname(word, netHandler, nicknameCache); - } - if (playerInfo != null) { - detectedPlayerInfo = playerInfo; - detected = true; - if (playerInfo == lastPlayerInfo) { - first = false; - if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages()) { - playerInfo = null; - } - } else { - lastPlayerInfo = playerInfo; - } - break; - } - } - } catch (Exception ignored) { - } - } - - @Nullable - private static NetworkPlayerInfo getPlayerFromNickname(String word, NetHandlerPlayClient connection, Map nicknameCache) { - if (nicknameCache.isEmpty()) { - for (NetworkPlayerInfo p : connection.getPlayerInfoMap()) { - IChatComponent displayName = p.getDisplayName(); - if (displayName != null) { - String nickname = displayName.getUnformattedTextForChat(); - if (word.equals(nickname)) { - nicknameCache.clear(); - return p; - } - - nicknameCache.put(nickname, p); - } - } - } else { - // use prepared cache - return nicknameCache.get(word); - } - - return null; - } - - @Override - public boolean hasDetected() { - return detected; - } - - @Override - public NetworkPlayerInfo getPlayerInfo() { - return playerInfo; - } - - @Override - public void updatePlayerInfo() { - if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages() && !first) { - playerInfo = null; - } else { - playerInfo = detectedPlayerInfo; - } - } - - @Override - public long getUniqueId() { - return uniqueId; - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/ClientCommandHandlerMixin.java b/src/main/java/cc/woverflow/chatting/mixin/ClientCommandHandlerMixin.java deleted file mode 100644 index f771c87..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/ClientCommandHandlerMixin.java +++ /dev/null @@ -1,33 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.chat.ChatShortcuts; -import cc.woverflow.chatting.config.ChattingConfig; -import kotlin.Pair; -import net.minecraft.client.Minecraft; -import net.minecraft.command.CommandHandler; -import net.minecraft.command.ICommandSender; -import net.minecraft.util.BlockPos; -import net.minecraftforge.client.ClientCommandHandler; -import net.minecraftforge.fml.client.FMLClientHandler; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.List; - -@Mixin(value = ClientCommandHandler.class, remap = false) -public class ClientCommandHandlerMixin extends CommandHandler { - @Redirect(method = "autoComplete", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/ClientCommandHandler;getTabCompletionOptions(Lnet/minecraft/command/ICommandSender;Ljava/lang/String;Lnet/minecraft/util/BlockPos;)Ljava/util/List;")) - private List addChatShortcuts(ClientCommandHandler instance, ICommandSender iCommandSender, String leftOfCursor, BlockPos blockPos) { - Minecraft mc = FMLClientHandler.instance().getClient(); - List autocompleteList = instance.getTabCompletionOptions(mc.thePlayer, leftOfCursor, mc.thePlayer.getPosition()); - if (ChattingConfig.INSTANCE.getChatShortcuts()) { - for (Pair pair : ChatShortcuts.INSTANCE.getShortcuts()) { - if (pair.getFirst().startsWith(leftOfCursor)) { - autocompleteList.add(pair.getFirst()); - } - } - } - return autocompleteList; - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/EntityPlayerSPMixin.java b/src/main/java/cc/woverflow/chatting/mixin/EntityPlayerSPMixin.java deleted file mode 100644 index fa125cb..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/EntityPlayerSPMixin.java +++ /dev/null @@ -1,41 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.chat.ChatTab; -import cc.woverflow.chatting.chat.ChatTabs; -import cc.woverflow.chatting.config.ChattingConfig; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.client.network.NetHandlerPlayClient; -import net.minecraft.network.Packet; -import net.minecraft.network.play.client.C01PacketChatMessage; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(value = EntityPlayerSP.class, priority = 0) -public class EntityPlayerSPMixin { - @Shadow @Final public NetHandlerPlayClient sendQueue; - - @Redirect(method = "sendChatMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/NetHandlerPlayClient;addToSendQueue(Lnet/minecraft/network/Packet;)V")) - private void handleSentMessages(NetHandlerPlayClient instance, Packet packet, String value) { - if (value.startsWith("/")) { - sendQueue.addToSendQueue(packet); - return; - } - if (ChattingConfig.INSTANCE.getChatTabs() && !ChatTabs.INSTANCE.getCurrentTabs().isEmpty()) { - boolean sent = false; - for (ChatTab tab : ChatTabs.INSTANCE.getCurrentTabs()) { - if (tab.getPrefix() != null && !tab.getPrefix().isEmpty()) { - sendQueue.addToSendQueue(new C01PacketChatMessage(tab.getPrefix() + value)); - sent = true; - } - } - if (!sent) { - sendQueue.addToSendQueue(packet); - } - } else { - sendQueue.addToSendQueue(packet); - } - } -} \ No newline at end of file diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiChatMixin.java b/src/main/java/cc/woverflow/chatting/mixin/GuiChatMixin.java deleted file mode 100644 index a12aea1..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiChatMixin.java +++ /dev/null @@ -1,141 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.chat.*; -import cc.woverflow.chatting.config.ChattingConfig; -import cc.woverflow.chatting.gui.components.ClearButton; -import cc.woverflow.chatting.gui.components.ScreenshotButton; -import cc.woverflow.chatting.gui.components.SearchButton; -import cc.woverflow.chatting.hook.ChatLineHook; -import cc.woverflow.chatting.hook.GuiNewChatHook; -import cc.woverflow.chatting.utils.ModCompatHooks; -import com.google.common.collect.Lists; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.GuiChat; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.MathHelper; -import net.minecraftforge.fml.client.config.GuiUtils; -import org.apache.commons.lang3.StringUtils; -import org.lwjgl.input.Mouse; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.awt.*; -import java.awt.datatransfer.Transferable; -import java.util.List; - -@Mixin(GuiChat.class) -public abstract class GuiChatMixin extends GuiScreen { - - @Unique - private static final List COPY_TOOLTIP = Lists.newArrayList( - "\u00A7e\u00A7lCopy To Clipboard", - "\u00A7b\u00A7lNORMAL CLICK\u00A7r \u00A78- \u00A77Full Message", - "\u00A7b\u00A7lCTRL CLICK\u00A7r \u00A78- \u00A77Single Line", - "\u00A7b\u00A7lSHIFT CLICK\u00A7r \u00A78- \u00A77Screenshot Line", - "", - "\u00A7e\u00A7lModifiers", - "\u00A7b\u00A7lALT\u00A7r \u00A78- \u00A77Formatting Codes"); - - private SearchButton searchButton; - - @Inject(method = "initGui", at = @At("TAIL")) - private void init(CallbackInfo ci) { - if (ChattingConfig.INSTANCE.getChatSearch()) { - searchButton = new SearchButton(); - buttonList.add(searchButton); - } - buttonList.add(new ScreenshotButton()); - buttonList.add(new ClearButton()); - if (ChattingConfig.INSTANCE.getChatTabs()) { - for (ChatTab chatTab : ChatTabs.INSTANCE.getTabs()) { - buttonList.add(chatTab.getButton()); - } - } - } - - @Inject(method = "updateScreen", at = @At("HEAD")) - private void updateScreen(CallbackInfo ci) { - if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) { - searchButton.getInputField().updateCursorCounter(); - } - } - - @Inject(method = "keyTyped", at = @At("HEAD"), cancellable = true) - private void keyTyped(char typedChar, int keyCode, CallbackInfo ci) { - if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) { - ci.cancel(); - if (keyCode == 1) { - searchButton.onMousePress(); - return; - } - searchButton.getInputField().textboxKeyTyped(typedChar, keyCode); - ChatSearchingManager.INSTANCE.setLastSearch(searchButton.getInputField().getText()); - } - } - - @Inject(method = "drawScreen", at = @At("HEAD")) - private void onDrawScreen(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { - GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI()); - float f = mc.ingameGUI.getChatGUI().getChatScale(); - int x = MathHelper.floor_float((float) mouseX / f); - if (hook.isHovering() && (hook.getRight() + ModCompatHooks.getXOffset() + 3) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 13 > x) { - GuiUtils.drawHoveringText(COPY_TOOLTIP, mouseX, mouseY, width, height, -1, fontRendererObj); - GlStateManager.disableLighting(); - } - } - - @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;drawRect(IIIII)V"), index = 2) - private int modifyRight(int right) { - return ChattingConfig.INSTANCE.getCompactInputBox() ? (MathHelper.ceiling_float_int((float) mc.ingameGUI.getChatGUI().getChatWidth() / mc.ingameGUI.getChatGUI().getChatScale()) + 6) : right; - } - - @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;drawRect(IIIII)V"), index = 4) - private int modifyInputBoxColor(int color) { - return ChattingConfig.INSTANCE.getInputBoxBackgroundColor().getRGB(); - } - - @Inject(method = "mouseClicked", at = @At("HEAD")) - private void mouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) { - GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI()); - float f = mc.ingameGUI.getChatGUI().getChatScale(); - int x = MathHelper.floor_float((float) mouseX / f); - if (hook.isHovering()) { - if (((hook.getRight() + ModCompatHooks.getXOffset() + 3) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 13 > x) || (mouseButton == 1 && ChattingConfig.INSTANCE.getRightClickCopy())) { - Transferable message = hook.getChattingChatComponent(Mouse.getY()); - if (message == null) return; - try { - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(message, null); - } catch (Exception e) { - e.printStackTrace(); - } - } else if ((hook.getRight() + ModCompatHooks.getXOffset() + 13) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 23 > x) { - ChatLine chatLine = hook.getHoveredLine(Mouse.getY()); - if (chatLine == null) return; - ((GuiNewChatAccessor) Minecraft.getMinecraft().ingameGUI.getChatGUI()).getDrawnChatLines().removeIf(line -> ((ChatLineHook) line).getUniqueId() == ((ChatLineHook) chatLine).getUniqueId()); - ((GuiNewChatAccessor) Minecraft.getMinecraft().ingameGUI.getChatGUI()).getChatLines().removeIf(line -> ((ChatLineHook) line).getUniqueId() == ((ChatLineHook) chatLine).getUniqueId()); - } - } - - } - - @ModifyArg(method = "keyTyped", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;sendChatMessage(Ljava/lang/String;)V"), index = 0) - private String modifySentMessage(String original) { - if (ChattingConfig.INSTANCE.getChatShortcuts()) { - if (original.startsWith("/")) { - return "/" + ChatShortcuts.INSTANCE.handleSentCommand(StringUtils.substringAfter(original, "/")); - } - } - return original; - } - - @Inject(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;scroll(I)V")) - private void handleMouseInput(CallbackInfo ci) { - ChatScrollingHook.INSTANCE.setShouldSmooth(true); - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatAccessor.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatAccessor.java deleted file mode 100644 index d4fd524..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatAccessor.java +++ /dev/null @@ -1,20 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.GuiNewChat; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.List; - -@Mixin(GuiNewChat.class) -public interface GuiNewChatAccessor { - @Accessor - List getDrawnChatLines(); - - @Accessor - List getChatLines(); - - @Accessor - int getScrollPos(); -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMapMixin.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMapMixin.java deleted file mode 100644 index 5b38bda..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMapMixin.java +++ /dev/null @@ -1,78 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.hook.GuiNewChatHook; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.GuiNewChat; -import net.minecraft.util.IChatComponent; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Mixin(value = GuiNewChat.class, priority = Integer.MIN_VALUE) -public abstract class GuiNewChatMapMixin implements GuiNewChatHook { - - @Unique private final Map drawnToFull = new HashMap<>(); - @Unique private final List tempDrawnLines = new ArrayList<>(); - @Unique private ChatLine lastTempLine = null; - - @Shadow @Final private List drawnChatLines; - @Shadow @Final private List chatLines; - - @Inject(method = "setChatLine", at = @At("HEAD")) - private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - tempDrawnLines.clear(); - } - - @Inject(method = "setChatLine", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", ordinal = 0, shift = At.Shift.AFTER, remap = false)) - private void handleDrawnLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - if (!displayOnly) tempDrawnLines.add(drawnChatLines.get(0)); - else if (lastTempLine != null) { - drawnToFull.put(drawnChatLines.get(0), lastTempLine); - } - } - - @Inject(method = "setChatLine", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(I)Ljava/lang/Object;", ordinal = 0, shift = At.Shift.BEFORE, remap = false)) - private void handleDrawnLineRemoved(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - drawnToFull.remove(drawnChatLines.get(drawnChatLines.size() - 1)); - } - - @Inject(method = "setChatLine", at = @At("RETURN")) - private void handleLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - if (!displayOnly) { - ChatLine masterLine = chatLines.get(0); - for (ChatLine tempDrawnLine : tempDrawnLines) drawnToFull.put(tempDrawnLine, masterLine); - }else { - lastTempLine = null; - } - } - - @Inject(method = "refreshChat", at = @At("HEAD")) - private void handleRefreshedChat(CallbackInfo ci) { - drawnToFull.clear(); - } - - @Inject(method = "refreshChat", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;setChatLine(Lnet/minecraft/util/IChatComponent;IIZ)V", ordinal = 0, shift = At.Shift.BEFORE)) - private void handleLineRefresh(CallbackInfo ci, int i, ChatLine chatline) { - lastTempLine = chatline; - } - - @Inject(method = "clearChatMessages", at = @At("HEAD")) - private void handleChatCleared(CallbackInfo ci) { - drawnToFull.clear(); - } - - @Override - public ChatLine getFullMessage(ChatLine line) { - return drawnToFull.getOrDefault(line, null); - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java deleted file mode 100644 index e0a3b56..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java +++ /dev/null @@ -1,259 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.polyfrost.oneconfig.config.core.OneColor; -import cc.polyfrost.oneconfig.libs.universal.UMouse; -import cc.polyfrost.oneconfig.utils.Notifications; -import cc.woverflow.chatting.Chatting; -import cc.woverflow.chatting.chat.ChatSearchingManager; -import cc.woverflow.chatting.config.ChattingConfig; -import cc.woverflow.chatting.gui.components.CleanButton; -import cc.woverflow.chatting.hook.GuiNewChatHook; -import cc.woverflow.chatting.utils.ModCompatHooks; -import cc.woverflow.chatting.utils.RenderUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.*; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.MathHelper; -import net.minecraft.util.ResourceLocation; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.*; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.invoke.arg.Args; - -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.image.BufferedImage; -import java.util.List; - -@Mixin(value = GuiNewChat.class, priority = 990) -public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook { - @Unique - private int chatting$right = 0; - @Unique - private boolean chatting$isHovering; - @Unique - private boolean chatting$chatCheck; - @Unique - private int chatting$textOpacity; - @Shadow - @Final - private Minecraft mc; - @Shadow - @Final - private List drawnChatLines; - @Shadow - public abstract boolean getChatOpen(); - - @Shadow - public abstract float getChatScale(); - - @Shadow - public abstract int getLineCount(); - - @Shadow - private int scrollPos; - - @Shadow public abstract int getChatWidth(); - - @Unique - private static final ResourceLocation COPY = new ResourceLocation("chatting:copy.png"); - @Unique - private static final ResourceLocation DELETE = new ResourceLocation("chatting:delete.png"); - - /*? - @Unique - private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); - @ModifyArg(method = "setChatLine", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;(ILnet/minecraft/util/IChatComponent;I)V")) - private IChatComponent handleAddDrawnLine(IChatComponent iChatComponent) { - if (!ChattingConfig.INSTANCE.getShowTimestamp()) return iChatComponent; - String time = " §7["+ sdf.format(new Date(System.currentTimeMillis())) + "]§r"; - iChatComponent.appendSibling(new ChatComponentText(time)); - return iChatComponent; - } - - */ - - @Inject(method = "drawChat", at = @At("HEAD")) - private void checkScreenshotKeybind(int j2, CallbackInfo ci) { - if (Chatting.INSTANCE.getKeybind().isPressed()) { - Chatting.INSTANCE.setDoTheThing(true); - } - chatting$chatCheck = false; - } - - @ModifyVariable(method = "drawChat", at = @At("HEAD"), argsOnly = true) - private int setUpdateCounterWhenYes(int updateCounter) { - return Chatting.INSTANCE.getDoTheThing() ? 0 : updateCounter; - } - - @ModifyVariable(method = "drawChat", at = @At("STORE"), index = 2) - private int setChatLimitWhenYes(int linesToDraw) { - return Chatting.INSTANCE.getDoTheThing() - ? GuiNewChat.calculateChatboxHeight(mc.gameSettings.chatHeightFocused) / 9 - : linesToDraw; - } - - private boolean lineInBounds = false; - - @ModifyArgs(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V"), slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/util/MathHelper;clamp_double(DDD)D"), to = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableBlend()V"))) - private void captureDrawRect(Args args) { - args.set(4, ChattingConfig.INSTANCE.getChatBackgroundColor().getRGB()); - if (mc.currentScreen instanceof GuiChat) { - int left = args.get(0); - int top = args.get(1); - int right = args.get(2); - int bottom = args.get(3); - if (isInBounds(left, top, right, bottom, getChatScale())) { - chatting$isHovering = true; - lineInBounds = true; - args.set(4, ChattingConfig.INSTANCE.getHoveredChatBackgroundColor().getRGB()); - } - } - } - - @ModifyArgs(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) - private void drawChatBox(Args args) { - if (mc.currentScreen instanceof GuiChat) { - float f = this.getChatScale(); - int left = 0; - int top = (int) ((float) args.get(2) - 1); - int right = MathHelper.ceiling_float_int((float)getChatWidth() / f) + 4; - int bottom = (int) ((float) args.get(2) + 8); - if ((chatting$isHovering && lineInBounds) || isInBounds(left, top, right, bottom, f)) { - chatting$isHovering = true; - drawCopyChatBox(right, top); - } - } - lineInBounds = false; - } - - private boolean isInBounds(int left, int top, int right, int bottom, float chatScale) { - int mouseX = MathHelper.floor_double(UMouse.getScaledX()) - 3; - int mouseY = MathHelper.floor_double(UMouse.getScaledY()) - 27 + ModCompatHooks.getYOffset() - ModCompatHooks.getChatPosition(); - mouseX = MathHelper.floor_float((float) mouseX / chatScale); - mouseY = -(MathHelper.floor_float((float) mouseY / chatScale)); //WHY DO I NEED TO DO THIS - return mouseX >= (left + ModCompatHooks.getXOffset()) && mouseY < bottom && mouseX < (right + 23 + ModCompatHooks.getXOffset()) && mouseY >= top; - } - - @ModifyVariable(method = "drawChat", at = @At("STORE"), ordinal = 7) - private int modifyYeah(int value) { - return chatting$textOpacity = (int) (((float) (getChatOpen() ? 255 : value)) * (mc.gameSettings.chatOpacity * 0.9F + 0.1F)); - } - /*/ - @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;scale(FFF)V")) - private void drawPre(int updateCounter, CallbackInfo ci) { - RenderUtils.timestampPre(); - } - - @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;popMatrix()V")) - private void drawPost(int updateCounter, CallbackInfo ci) { - RenderUtils.timestampPost(); - } - - */ - - @Inject(method = "drawChat", at = @At("RETURN")) - private void checkStuff(int j2, CallbackInfo ci) { - if (!chatting$chatCheck && chatting$isHovering) { - chatting$isHovering = false; - } - } - - @Override - public int getRight() { - return chatting$right; - } - - @Override - public boolean isHovering() { - return chatting$isHovering; - } - - private void drawCopyChatBox(int right, int top) { - chatting$chatCheck = true; - GlStateManager.enableRescaleNormal(); - GlStateManager.enableBlend(); - GlStateManager.enableDepth(); - GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); - GlStateManager.pushMatrix(); - mc.getTextureManager().bindTexture(COPY); - GlStateManager.enableRescaleNormal(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(516, 0.1f); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(770, 771); - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - chatting$right = right; - drawModalRectWithCustomSizedTexture(right + 1, top, 0f, 0f, 9, 9, 9, 9); - drawRect(right + 1, top, right + 10, top + 9, (((right + ModCompatHooks.getXOffset() + 3) <= (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale()) && (right + ModCompatHooks.getXOffset()) + 13 > (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale())) ? ChattingConfig.INSTANCE.getChatButtonHoveredBackgroundColor().getRGB() : ChattingConfig.INSTANCE.getChatButtonBackgroundColor().getRGB())); - GlStateManager.disableAlpha(); - GlStateManager.disableRescaleNormal(); - mc.getTextureManager().bindTexture(DELETE); - GlStateManager.enableRescaleNormal(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(516, 0.1f); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(770, 771); - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); - drawModalRectWithCustomSizedTexture(right + 11, top, 0f, 0f, 9, 9, 9, 9); - drawRect(right + 11, top, right + 20, top + 9, (((right + ModCompatHooks.getXOffset() + 13) <= (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale()) && (right + ModCompatHooks.getXOffset()) + 23 > (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale())) ? ChattingConfig.INSTANCE.getChatButtonHoveredBackgroundColor().getRGB() : ChattingConfig.INSTANCE.getChatButtonBackgroundColor().getRGB())); - GlStateManager.disableLighting(); - GlStateManager.popMatrix(); - } - - @Override - public ChatLine getHoveredLine(int mouseY) { - if (this.getChatOpen()) { - ScaledResolution scaledresolution = new ScaledResolution(this.mc); - int i = scaledresolution.getScaleFactor(); - float f = this.getChatScale(); - int k = mouseY / i - 27 + ModCompatHooks.getYOffset() - ModCompatHooks.getChatPosition(); - k = MathHelper.floor_float((float) k / f); - - if (k >= 0) { - int l = Math.min(this.getLineCount(), this.drawnChatLines.size()); - - if (k < this.mc.fontRendererObj.FONT_HEIGHT * l + l) { - int i1 = k / this.mc.fontRendererObj.FONT_HEIGHT + this.scrollPos; - - if (i1 >= 0 && i1 < this.drawnChatLines.size()) { - List m = ChatSearchingManager.filterMessages(ChatSearchingManager.INSTANCE.getLastSearch(), this.drawnChatLines); - return m != null ? m.get(i1) : null; - } - - } - } - } - return null; - } - - @Override - public Transferable getChattingChatComponent(int mouseY) { - ChatLine subLine = getHoveredLine(mouseY); - if (subLine != null) { - ChatLine fullLine = this.getFullMessage(subLine); - if (GuiScreen.isShiftKeyDown()) { - if (fullLine != null) { - BufferedImage image = Chatting.INSTANCE.screenshotLine(subLine); - if (image != null) RenderUtils.copyToClipboard(image); - } - return null; - } - ChatLine line = GuiScreen.isCtrlKeyDown() ? subLine : fullLine; - String message = line == null ? "Could not find chat message." : line.getChatComponent().getFormattedText(); - String actualMessage = GuiScreen.isAltKeyDown() ? message : EnumChatFormatting.getTextWithoutFormattingCodes(message); - Notifications.INSTANCE.send("Chatting", line == null ? "Could not find chat message." : "Copied following text: " + actualMessage); - return new StringSelection(actualMessage); - } - return null; - } - - @Override - public int getTextOpacity() { - return chatting$textOpacity; - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatHeight.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatHeight.java deleted file mode 100644 index d0b4db1..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatHeight.java +++ /dev/null @@ -1,21 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.Chatting; -import cc.woverflow.chatting.config.ChattingConfig; -import net.minecraft.client.gui.GuiNewChat; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(GuiNewChat.class) -public abstract class GuiNewChatMixin_ChatHeight { - @Shadow public abstract boolean getChatOpen(); - - @Inject(method = "getChatHeight", at = @At("HEAD"), cancellable = true) - private void customHeight_getChatHeight(CallbackInfoReturnable cir) { - if (ChattingConfig.INSTANCE.getCustomChatHeight()) - cir.setReturnValue(Chatting.INSTANCE.getChatHeight(getChatOpen())); - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatSearching.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatSearching.java deleted file mode 100644 index b40ba52..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatSearching.java +++ /dev/null @@ -1,31 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.chat.ChatSearchingManager; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.GuiNewChat; -import net.minecraft.util.IChatComponent; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; - -@Mixin(GuiNewChat.class) -public class GuiNewChatMixin_ChatSearching { - @Shadow @Final private List drawnChatLines; - - @Inject(method = "setChatLine", at = @At("HEAD")) - private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - ChatSearchingManager.getCache().invalidateAll(); - } - - @Redirect(method = "drawChat", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/GuiNewChat;drawnChatLines:Ljava/util/List;", opcode = Opcodes.GETFIELD)) - private List injected(GuiNewChat instance) { - return ChatSearchingManager.filterMessages(ChatSearchingManager.INSTANCE.getLastSearch(), drawnChatLines); - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatTabs.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatTabs.java deleted file mode 100644 index d21097d..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_ChatTabs.java +++ /dev/null @@ -1,54 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.chat.ChatTabs; -import cc.woverflow.chatting.config.ChattingConfig; -import cc.woverflow.chatting.utils.ModCompatHooks; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.GuiNewChat; -import net.minecraft.util.IChatComponent; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; - -@Mixin(value = GuiNewChat.class, priority = 990) -public abstract class GuiNewChatMixin_ChatTabs { - @Shadow @Final private Minecraft mc; - - @Shadow public abstract void deleteChatLine(int id); - - @Shadow @Final private List chatLines; - - @Inject(method = "printChatMessageWithOptionalDeletion", at = @At("HEAD"), cancellable = true) - private void handlePrintChatMessage(IChatComponent chatComponent, int chatLineId, CallbackInfo ci) { - handleChatTabMessage(chatComponent, chatLineId, mc.ingameGUI.getUpdateCounter(), false, ci); - } - - @Inject(method = "setChatLine", at = @At("HEAD"), cancellable = true) - private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - handleChatTabMessage(chatComponent, chatLineId, updateCounter, displayOnly, ci); - } - - private void handleChatTabMessage(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { - if (ChattingConfig.INSTANCE.getChatTabs()) { - if (!ChatTabs.INSTANCE.shouldRender(chatComponent)) { - ChatTabs.INSTANCE.setHasCancelledAnimation(true); - if (chatLineId != 0) { - deleteChatLine(chatLineId); - } - if (!displayOnly) { - chatLines.add(0, new ChatLine(updateCounter, chatComponent, chatLineId)); - while (this.chatLines.size() > (100 + ModCompatHooks.getExtendedChatLength())) { - this.chatLines.remove(this.chatLines.size() - 1); - } - } - ci.cancel(); - } - } - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_Scrolling.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_Scrolling.java deleted file mode 100644 index 448ba6e..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_Scrolling.java +++ /dev/null @@ -1,71 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.polyfrost.oneconfig.gui.animations.EaseOutQuad; -import cc.polyfrost.oneconfig.utils.MathUtils; -import cc.woverflow.chatting.Chatting; -import cc.woverflow.chatting.chat.ChatScrollingHook; -import cc.woverflow.chatting.config.ChattingConfig; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiNewChat; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; - -@Mixin(GuiNewChat.class) -public abstract class GuiNewChatMixin_Scrolling extends Gui { - @Shadow private int scrollPos; - @Shadow @Final private List drawnChatLines; - - @Shadow public abstract int getLineCount(); - - @Shadow private boolean isScrolled; - - @Unique - private EaseOutQuad chatting$scrollingAnimation; - - @Inject(method = "drawChat", at = @At("HEAD")) - private void chatting$scrollingAnimationStart(int updateCounter, CallbackInfo ci) { - if (chatting$scrollingAnimation != null) { - if (chatting$scrollingAnimation.isFinished()) { - if (scrollPos == 0) { - isScrolled = false; - } - chatting$scrollingAnimation = null; - } else { - scrollPos = (int) chatting$scrollingAnimation.get(Chatting.INSTANCE.getDeltaTime()); - } - } - } - - @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V", ordinal = 1)) - private void redirectScrollBar(int left, int top, int right, int bottom, int color) { - if (!ChattingConfig.INSTANCE.getRemoveScrollBar()) { - drawRect(left, top, right, bottom, color); - } - } - - @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V", ordinal = 2)) - private void redirectScrollBar2(int left, int top, int right, int bottom, int color) { - if (!ChattingConfig.INSTANCE.getRemoveScrollBar()) { - drawRect(left, top, right, bottom, color); - } - } - - @Inject(method = "scroll", at = @At("HEAD"), cancellable = true) - private void injectScroll(int amount, CallbackInfo ci) { - if (ChattingConfig.INSTANCE.getSmoothScrolling() && amount != 0 && ChatScrollingHook.INSTANCE.getShouldSmooth()) { - ci.cancel(); - ChatScrollingHook.INSTANCE.setShouldSmooth(false); - int result = (int) MathUtils.clamp(scrollPos + amount, 0, Math.max(drawnChatLines.size() - getLineCount() - 1, 0)); - chatting$scrollingAnimation = new EaseOutQuad((int) (ChattingConfig.INSTANCE.getScrollingSpeed() * 1000), scrollPos, result, false); - } - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_SmoothMessages.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_SmoothMessages.java deleted file mode 100644 index fa88145..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_SmoothMessages.java +++ /dev/null @@ -1,103 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.Chatting; -import cc.woverflow.chatting.chat.ChatSearchingManager; -import cc.woverflow.chatting.chat.ChatTabs; -import cc.woverflow.chatting.config.ChattingConfig; -import cc.woverflow.chatting.utils.EaseOutQuart; -import cc.woverflow.chatting.utils.ModCompatHooks; -import net.minecraft.client.gui.GuiNewChat; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.IChatComponent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; -import java.util.Locale; - -/** - * Taken from BetterChat under LGPL 3.0 - * https://github.com/LlamaLad7/Better-Chat/blob/1.8.9/LICENSE - */ -@Mixin(GuiNewChat.class) -public abstract class GuiNewChatMixin_SmoothMessages { - @Shadow - private boolean isScrolled; - - @Shadow - public abstract float getChatScale(); - @Unique - private int chatting$newLines; - - private EaseOutQuart chatting$easeOutQuart; - private float chatting$animationPercent; - @Unique - private int chatting$lineBeingDrawn; - - @Inject(method = "drawChat", at = @At("HEAD")) - private void modifyChatRendering(CallbackInfo ci) { - if (chatting$easeOutQuart != null) { - if (chatting$easeOutQuart.isFinished()) { - chatting$easeOutQuart = null; - } else { - chatting$animationPercent = chatting$easeOutQuart.get(Chatting.INSTANCE.getDeltaTime()); - } - } else { - chatting$animationPercent = 1; - } - } - - @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;pushMatrix()V", ordinal = 0, shift = At.Shift.AFTER)) - private void translate(CallbackInfo ci) { - float y = 0; - if (ChattingConfig.INSTANCE.getSmoothChat() && !this.isScrolled) { - y += (9 - 9 * chatting$animationPercent) * this.getChatScale(); - } - GlStateManager.translate(0, y, 0); - } - - @ModifyArg(method = "drawChat", at = @At(value = "INVOKE", target = "Ljava/util/List;get(I)Ljava/lang/Object;", ordinal = 0, remap = false), index = 0) - private int getLineBeingDrawn(int line) { - chatting$lineBeingDrawn = line; - return line; - } - - @ModifyArg(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) - private int modifyTextOpacity(int original) { - if (ChattingConfig.INSTANCE.getSmoothChat() && chatting$lineBeingDrawn <= chatting$newLines) { - int opacity = (original >> 24) & 0xFF; - opacity *= chatting$animationPercent; - return (original & ~(0xFF << 24)) | (opacity << 24); - } else { - return original; - } - } - - @Inject(method = "printChatMessageWithOptionalDeletion", at = @At("HEAD")) - private void resetPercentage(IChatComponent chatComponent, int chatLineId, CallbackInfo ci) { - if (!EnumChatFormatting.getTextWithoutFormattingCodes(chatComponent.getUnformattedText()).toLowerCase(Locale.ENGLISH).contains(ChatSearchingManager.INSTANCE.getLastSearch().toLowerCase(Locale.ENGLISH))) { - return; - } - if (ModCompatHooks.getBetterChatSmoothMessages()) { - return; - } - if (ChatTabs.INSTANCE.getHasCancelledAnimation()) { - ChatTabs.INSTANCE.setHasCancelledAnimation(false); - return; - } - chatting$easeOutQuart = new EaseOutQuart((1.0f - ChattingConfig.INSTANCE.getMessageSpeed()) * 1000f, 0f, 1f, false); - } - - @ModifyVariable(method = "setChatLine", at = @At("STORE"), ordinal = 0) - private List setNewLines(List original) { - chatting$newLines = original.size() - 1; - return original; - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_TextRendering.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_TextRendering.java deleted file mode 100644 index f19db6f..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin_TextRendering.java +++ /dev/null @@ -1,29 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.woverflow.chatting.utils.ModCompatHooks; -import net.minecraft.client.gui.ChatLine; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.GuiNewChat; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(value = GuiNewChat.class, priority = 990) -public class GuiNewChatMixin_TextRendering { - @Unique - private ChatLine chatting$drawingLine = null; - - @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;getChatComponent()Lnet/minecraft/util/IChatComponent;"), locals = LocalCapture.CAPTURE_FAILSOFT) - private void captureChatLine(int updateCounter, CallbackInfo ci, int i, boolean bl, int j, int k, float f, float g, int l, int m, ChatLine chatLine, int n, double d, int o, int p, int q) { - chatting$drawingLine = chatLine; - } - - @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) - private int redirectDrawString(FontRenderer instance, String text, float x, float y, int color) { - return ModCompatHooks.redirectDrawString(text, x, y, color, chatting$drawingLine, false); - } -} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiUtilsMixin.java b/src/main/java/cc/woverflow/chatting/mixin/GuiUtilsMixin.java deleted file mode 100644 index d939d6b..0000000 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiUtilsMixin.java +++ /dev/null @@ -1,36 +0,0 @@ -package cc.woverflow.chatting.mixin; - -import cc.polyfrost.oneconfig.renderer.TextRenderer; -import cc.woverflow.chatting.config.ChattingConfig; -import net.minecraft.client.gui.FontRenderer; -import net.minecraftforge.fml.client.config.GuiUtils; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(value = GuiUtils.class, remap = false) -public class GuiUtilsMixin { - @Shadow - public static void drawGradientRect(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) { - } - - @Redirect(method = "drawHoveringText", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/config/GuiUtils;drawGradientRect(IIIIIII)V")) - private static void redirectBackground(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) { - if (!ChattingConfig.INSTANCE.getRemoveTooltipBackground()) { - drawGradientRect(zLevel, left, top, right, bottom, startColor, endColor); - } - } - - @Redirect(method = "drawHoveringText", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) - private static int redirectText(FontRenderer instance, String text, float x, float y, int color) { - switch (ChattingConfig.INSTANCE.getTooltipTextRenderType()) { - case 0: - return instance.drawString(text, x, y, color, false); - case 2: - return TextRenderer.drawBorderedText(text, x, y, color, 255); - default: - return instance.drawStringWithShadow(text, x, y, color); - } - } -} diff --git a/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java b/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java new file mode 100644 index 0000000..50b9ce3 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java @@ -0,0 +1,17 @@ +package org.polyfrost.chatting.hook; + +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.network.NetworkPlayerInfo; + +import java.lang.ref.WeakReference; +import java.util.HashSet; + +public interface ChatLineHook { + HashSet> chatLines = new HashSet<>(); + boolean hasDetected(); + NetworkPlayerInfo getPlayerInfo(); + + void updatePlayerInfo(); + + long getUniqueId(); +} diff --git a/src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java b/src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java new file mode 100644 index 0000000..84097bd --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/hook/GuiNewChatHook.java @@ -0,0 +1,21 @@ +package org.polyfrost.chatting.hook; + +import net.minecraft.client.gui.ChatLine; + +import java.awt.datatransfer.Transferable; + +public interface GuiNewChatHook { + int getRight(); + + boolean isHovering(); + + ChatLine getHoveredLine(int mouseY); + + Transferable getChattingChatComponent(int mouseY); + + default ChatLine getFullMessage(ChatLine line) { + throw new AssertionError("getFullMessage not overridden on GuiNewChat"); + } + + int getTextOpacity(); +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java b/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java new file mode 100644 index 0000000..2e5f21c --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java @@ -0,0 +1,114 @@ +/* + * This file is from chat_heads is licensed under MPL-2.0, which can be found at https://www.mozilla.org/en-US/MPL/2.0/ + * See: https://github.com/dzwdz/chat_heads/blob/fabric-1.16.x/LICENSE + */ + +package org.polyfrost.chatting.mixin; + +import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.hook.ChatLineHook; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.client.network.NetworkPlayerInfo; +import net.minecraft.util.IChatComponent; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; + +@Mixin(ChatLine.class) +public class ChatLineMixin implements ChatLineHook { + private boolean detected = false; + private boolean first = true; + private NetworkPlayerInfo playerInfo; + private NetworkPlayerInfo detectedPlayerInfo; + private static NetworkPlayerInfo lastPlayerInfo; + private static long lastUniqueId = 0; + private long uniqueId = 0; + + @Inject(method = "", at = @At("RETURN")) + private void onInit(int i, IChatComponent iChatComponent, int j, CallbackInfo ci) { + lastUniqueId++; + uniqueId = lastUniqueId; + chatLines.add(new WeakReference<>((ChatLine) (Object) this)); + NetHandlerPlayClient netHandler = Minecraft.getMinecraft().getNetHandler(); + if (netHandler == null) return; + Map nicknameCache = new HashMap<>(); + try { + for (String word : iChatComponent.getFormattedText().split("(§.)|\\W")) { + if (word.isEmpty()) continue; + playerInfo = netHandler.getPlayerInfo(word); + if (playerInfo == null) { + playerInfo = getPlayerFromNickname(word, netHandler, nicknameCache); + } + if (playerInfo != null) { + detectedPlayerInfo = playerInfo; + detected = true; + if (playerInfo == lastPlayerInfo) { + first = false; + if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages()) { + playerInfo = null; + } + } else { + lastPlayerInfo = playerInfo; + } + break; + } + } + } catch (Exception ignored) { + } + } + + @Nullable + private static NetworkPlayerInfo getPlayerFromNickname(String word, NetHandlerPlayClient connection, Map nicknameCache) { + if (nicknameCache.isEmpty()) { + for (NetworkPlayerInfo p : connection.getPlayerInfoMap()) { + IChatComponent displayName = p.getDisplayName(); + if (displayName != null) { + String nickname = displayName.getUnformattedTextForChat(); + if (word.equals(nickname)) { + nicknameCache.clear(); + return p; + } + + nicknameCache.put(nickname, p); + } + } + } else { + // use prepared cache + return nicknameCache.get(word); + } + + return null; + } + + @Override + public boolean hasDetected() { + return detected; + } + + @Override + public NetworkPlayerInfo getPlayerInfo() { + return playerInfo; + } + + @Override + public void updatePlayerInfo() { + if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages() && !first) { + playerInfo = null; + } else { + playerInfo = detectedPlayerInfo; + } + } + + @Override + public long getUniqueId() { + return uniqueId; + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java b/src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java new file mode 100644 index 0000000..ee63ed3 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/ClientCommandHandlerMixin.java @@ -0,0 +1,33 @@ +package org.polyfrost.chatting.mixin; + +import kotlin.Pair; +import net.minecraft.client.Minecraft; +import net.minecraft.command.CommandHandler; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.BlockPos; +import net.minecraftforge.client.ClientCommandHandler; +import net.minecraftforge.fml.client.FMLClientHandler; +import org.polyfrost.chatting.chat.ChatShortcuts; +import org.polyfrost.chatting.config.ChattingConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +@Mixin(value = ClientCommandHandler.class, remap = false) +public class ClientCommandHandlerMixin extends CommandHandler { + @Redirect(method = "autoComplete", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/ClientCommandHandler;getTabCompletionOptions(Lnet/minecraft/command/ICommandSender;Ljava/lang/String;Lnet/minecraft/util/BlockPos;)Ljava/util/List;")) + private List addChatShortcuts(ClientCommandHandler instance, ICommandSender iCommandSender, String leftOfCursor, BlockPos blockPos) { + Minecraft mc = FMLClientHandler.instance().getClient(); + List autocompleteList = instance.getTabCompletionOptions(mc.thePlayer, leftOfCursor, mc.thePlayer.getPosition()); + if (ChattingConfig.INSTANCE.getChatShortcuts()) { + for (Pair pair : ChatShortcuts.INSTANCE.getShortcuts()) { + if (pair.getFirst().startsWith(leftOfCursor)) { + autocompleteList.add(pair.getFirst()); + } + } + } + return autocompleteList; + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java b/src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java new file mode 100644 index 0000000..d59d7f2 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/EntityPlayerSPMixin.java @@ -0,0 +1,41 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.network.NetHandlerPlayClient; +import net.minecraft.network.Packet; +import net.minecraft.network.play.client.C01PacketChatMessage; +import org.polyfrost.chatting.chat.ChatTab; +import org.polyfrost.chatting.chat.ChatTabs; +import org.polyfrost.chatting.config.ChattingConfig; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = EntityPlayerSP.class, priority = 0) +public class EntityPlayerSPMixin { + @Shadow @Final public NetHandlerPlayClient sendQueue; + + @Redirect(method = "sendChatMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/NetHandlerPlayClient;addToSendQueue(Lnet/minecraft/network/Packet;)V")) + private void handleSentMessages(NetHandlerPlayClient instance, Packet packet, String value) { + if (value.startsWith("/")) { + sendQueue.addToSendQueue(packet); + return; + } + if (ChattingConfig.INSTANCE.getChatTabs() && !ChatTabs.INSTANCE.getCurrentTabs().isEmpty()) { + boolean sent = false; + for (ChatTab tab : ChatTabs.INSTANCE.getCurrentTabs()) { + if (tab.getPrefix() != null && !tab.getPrefix().isEmpty()) { + sendQueue.addToSendQueue(new C01PacketChatMessage(tab.getPrefix() + value)); + sent = true; + } + } + if (!sent) { + sendQueue.addToSendQueue(packet); + } + } else { + sendQueue.addToSendQueue(packet); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java new file mode 100644 index 0000000..303326f --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java @@ -0,0 +1,143 @@ +package org.polyfrost.chatting.mixin; + +import org.polyfrost.chatting.chat.*; +import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.gui.components.ClearButton; +import org.polyfrost.chatting.gui.components.ScreenshotButton; +import org.polyfrost.chatting.gui.components.SearchButton; +import org.polyfrost.chatting.hook.ChatLineHook; +import org.polyfrost.chatting.hook.GuiNewChatHook; +import com.google.common.collect.Lists; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.MathHelper; +import net.minecraftforge.fml.client.config.GuiUtils; +import org.apache.commons.lang3.StringUtils; +import org.lwjgl.input.Mouse; +import org.polyfrost.chatting.chat.ChatSearchingManager; +import org.polyfrost.chatting.chat.ChatShortcuts; +import org.polyfrost.chatting.utils.ModCompatHooks; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.awt.*; +import java.awt.datatransfer.Transferable; +import java.util.List; + +@Mixin(GuiChat.class) +public abstract class GuiChatMixin extends GuiScreen { + + @Unique + private static final List COPY_TOOLTIP = Lists.newArrayList( + "\u00A7e\u00A7lCopy To Clipboard", + "\u00A7b\u00A7lNORMAL CLICK\u00A7r \u00A78- \u00A77Full Message", + "\u00A7b\u00A7lCTRL CLICK\u00A7r \u00A78- \u00A77Single Line", + "\u00A7b\u00A7lSHIFT CLICK\u00A7r \u00A78- \u00A77Screenshot Line", + "", + "\u00A7e\u00A7lModifiers", + "\u00A7b\u00A7lALT\u00A7r \u00A78- \u00A77Formatting Codes"); + + private SearchButton searchButton; + + @Inject(method = "initGui", at = @At("TAIL")) + private void init(CallbackInfo ci) { + if (ChattingConfig.INSTANCE.getChatSearch()) { + searchButton = new SearchButton(); + buttonList.add(searchButton); + } + buttonList.add(new ScreenshotButton()); + buttonList.add(new ClearButton()); + if (ChattingConfig.INSTANCE.getChatTabs()) { + for (ChatTab chatTab : ChatTabs.INSTANCE.getTabs()) { + buttonList.add(chatTab.getButton()); + } + } + } + + @Inject(method = "updateScreen", at = @At("HEAD")) + private void updateScreen(CallbackInfo ci) { + if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) { + searchButton.getInputField().updateCursorCounter(); + } + } + + @Inject(method = "keyTyped", at = @At("HEAD"), cancellable = true) + private void keyTyped(char typedChar, int keyCode, CallbackInfo ci) { + if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) { + ci.cancel(); + if (keyCode == 1) { + searchButton.onMousePress(); + return; + } + searchButton.getInputField().textboxKeyTyped(typedChar, keyCode); + ChatSearchingManager.INSTANCE.setLastSearch(searchButton.getInputField().getText()); + } + } + + @Inject(method = "drawScreen", at = @At("HEAD")) + private void onDrawScreen(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI()); + float f = mc.ingameGUI.getChatGUI().getChatScale(); + int x = MathHelper.floor_float((float) mouseX / f); + if (hook.isHovering() && (hook.getRight() + ModCompatHooks.getXOffset() + 3) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 13 > x) { + GuiUtils.drawHoveringText(COPY_TOOLTIP, mouseX, mouseY, width, height, -1, fontRendererObj); + GlStateManager.disableLighting(); + } + } + + @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;drawRect(IIIII)V"), index = 2) + private int modifyRight(int right) { + return ChattingConfig.INSTANCE.getCompactInputBox() ? (MathHelper.ceiling_float_int((float) mc.ingameGUI.getChatGUI().getChatWidth() / mc.ingameGUI.getChatGUI().getChatScale()) + 6) : right; + } + + @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;drawRect(IIIII)V"), index = 4) + private int modifyInputBoxColor(int color) { + return ChattingConfig.INSTANCE.getInputBoxBackgroundColor().getRGB(); + } + + @Inject(method = "mouseClicked", at = @At("HEAD")) + private void mouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) { + GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI()); + float f = mc.ingameGUI.getChatGUI().getChatScale(); + int x = MathHelper.floor_float((float) mouseX / f); + if (hook.isHovering()) { + if (((hook.getRight() + ModCompatHooks.getXOffset() + 3) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 13 > x) || (mouseButton == 1 && ChattingConfig.INSTANCE.getRightClickCopy())) { + Transferable message = hook.getChattingChatComponent(Mouse.getY()); + if (message == null) return; + try { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(message, null); + } catch (Exception e) { + e.printStackTrace(); + } + } else if ((hook.getRight() + ModCompatHooks.getXOffset() + 13) <= x && (hook.getRight() + ModCompatHooks.getXOffset()) + 23 > x) { + ChatLine chatLine = hook.getHoveredLine(Mouse.getY()); + if (chatLine == null) return; + ((GuiNewChatAccessor) Minecraft.getMinecraft().ingameGUI.getChatGUI()).getDrawnChatLines().removeIf(line -> ((ChatLineHook) line).getUniqueId() == ((ChatLineHook) chatLine).getUniqueId()); + ((GuiNewChatAccessor) Minecraft.getMinecraft().ingameGUI.getChatGUI()).getChatLines().removeIf(line -> ((ChatLineHook) line).getUniqueId() == ((ChatLineHook) chatLine).getUniqueId()); + } + } + + } + + @ModifyArg(method = "keyTyped", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiChat;sendChatMessage(Ljava/lang/String;)V"), index = 0) + private String modifySentMessage(String original) { + if (ChattingConfig.INSTANCE.getChatShortcuts()) { + if (original.startsWith("/")) { + return "/" + ChatShortcuts.INSTANCE.handleSentCommand(StringUtils.substringAfter(original, "/")); + } + } + return original; + } + + @Inject(method = "handleMouseInput", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;scroll(I)V")) + private void handleMouseInput(CallbackInfo ci) { + ChatScrollingHook.INSTANCE.setShouldSmooth(true); + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java new file mode 100644 index 0000000..d0630ca --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatAccessor.java @@ -0,0 +1,20 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.GuiNewChat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(GuiNewChat.class) +public interface GuiNewChatAccessor { + @Accessor + List getDrawnChatLines(); + + @Accessor + List getChatLines(); + + @Accessor + int getScrollPos(); +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java new file mode 100644 index 0000000..ca1c0df --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMapMixin.java @@ -0,0 +1,78 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.GuiNewChat; +import net.minecraft.util.IChatComponent; +import org.polyfrost.chatting.hook.GuiNewChatHook; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Mixin(value = GuiNewChat.class, priority = Integer.MIN_VALUE) +public abstract class GuiNewChatMapMixin implements GuiNewChatHook { + + @Unique private final Map drawnToFull = new HashMap<>(); + @Unique private final List tempDrawnLines = new ArrayList<>(); + @Unique private ChatLine lastTempLine = null; + + @Shadow @Final private List drawnChatLines; + @Shadow @Final private List chatLines; + + @Inject(method = "setChatLine", at = @At("HEAD")) + private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + tempDrawnLines.clear(); + } + + @Inject(method = "setChatLine", at = @At(value = "INVOKE", target = "Ljava/util/List;add(ILjava/lang/Object;)V", ordinal = 0, shift = At.Shift.AFTER, remap = false)) + private void handleDrawnLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + if (!displayOnly) tempDrawnLines.add(drawnChatLines.get(0)); + else if (lastTempLine != null) { + drawnToFull.put(drawnChatLines.get(0), lastTempLine); + } + } + + @Inject(method = "setChatLine", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(I)Ljava/lang/Object;", ordinal = 0, shift = At.Shift.BEFORE, remap = false)) + private void handleDrawnLineRemoved(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + drawnToFull.remove(drawnChatLines.get(drawnChatLines.size() - 1)); + } + + @Inject(method = "setChatLine", at = @At("RETURN")) + private void handleLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + if (!displayOnly) { + ChatLine masterLine = chatLines.get(0); + for (ChatLine tempDrawnLine : tempDrawnLines) drawnToFull.put(tempDrawnLine, masterLine); + }else { + lastTempLine = null; + } + } + + @Inject(method = "refreshChat", at = @At("HEAD")) + private void handleRefreshedChat(CallbackInfo ci) { + drawnToFull.clear(); + } + + @Inject(method = "refreshChat", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;setChatLine(Lnet/minecraft/util/IChatComponent;IIZ)V", ordinal = 0, shift = At.Shift.BEFORE)) + private void handleLineRefresh(CallbackInfo ci, int i, ChatLine chatline) { + lastTempLine = chatline; + } + + @Inject(method = "clearChatMessages", at = @At("HEAD")) + private void handleChatCleared(CallbackInfo ci) { + drawnToFull.clear(); + } + + @Override + public ChatLine getFullMessage(ChatLine line) { + return drawnToFull.getOrDefault(line, null); + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java new file mode 100644 index 0000000..bc90730 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java @@ -0,0 +1,257 @@ +package org.polyfrost.chatting.mixin; + +import cc.polyfrost.oneconfig.libs.universal.UMouse; +import cc.polyfrost.oneconfig.utils.Notifications; +import org.polyfrost.chatting.Chatting; +import org.polyfrost.chatting.chat.ChatSearchingManager; +import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.hook.GuiNewChatHook; +import org.polyfrost.chatting.utils.ModCompatHooks; +import org.polyfrost.chatting.utils.RenderUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.*; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.invoke.arg.Args; + +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.image.BufferedImage; +import java.util.List; + +@Mixin(value = GuiNewChat.class, priority = 990) +public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook { + @Unique + private int chatting$right = 0; + @Unique + private boolean chatting$isHovering; + @Unique + private boolean chatting$chatCheck; + @Unique + private int chatting$textOpacity; + @Shadow + @Final + private Minecraft mc; + @Shadow + @Final + private List drawnChatLines; + @Shadow + public abstract boolean getChatOpen(); + + @Shadow + public abstract float getChatScale(); + + @Shadow + public abstract int getLineCount(); + + @Shadow + private int scrollPos; + + @Shadow public abstract int getChatWidth(); + + @Unique + private static final ResourceLocation COPY = new ResourceLocation("chatting:copy.png"); + @Unique + private static final ResourceLocation DELETE = new ResourceLocation("chatting:delete.png"); + + /*? + @Unique + private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + @ModifyArg(method = "setChatLine", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;(ILnet/minecraft/util/IChatComponent;I)V")) + private IChatComponent handleAddDrawnLine(IChatComponent iChatComponent) { + if (!ChattingConfig.INSTANCE.getShowTimestamp()) return iChatComponent; + String time = " §7["+ sdf.format(new Date(System.currentTimeMillis())) + "]§r"; + iChatComponent.appendSibling(new ChatComponentText(time)); + return iChatComponent; + } + + */ + + @Inject(method = "drawChat", at = @At("HEAD")) + private void checkScreenshotKeybind(int j2, CallbackInfo ci) { + if (Chatting.INSTANCE.getKeybind().isPressed()) { + Chatting.INSTANCE.setDoTheThing(true); + } + chatting$chatCheck = false; + } + + @ModifyVariable(method = "drawChat", at = @At("HEAD"), argsOnly = true) + private int setUpdateCounterWhenYes(int updateCounter) { + return Chatting.INSTANCE.getDoTheThing() ? 0 : updateCounter; + } + + @ModifyVariable(method = "drawChat", at = @At("STORE"), index = 2) + private int setChatLimitWhenYes(int linesToDraw) { + return Chatting.INSTANCE.getDoTheThing() + ? GuiNewChat.calculateChatboxHeight(mc.gameSettings.chatHeightFocused) / 9 + : linesToDraw; + } + + private boolean lineInBounds = false; + + @ModifyArgs(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V"), slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/util/MathHelper;clamp_double(DDD)D"), to = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;enableBlend()V"))) + private void captureDrawRect(Args args) { + args.set(4, ChattingConfig.INSTANCE.getChatBackgroundColor().getRGB()); + if (mc.currentScreen instanceof GuiChat) { + int left = args.get(0); + int top = args.get(1); + int right = args.get(2); + int bottom = args.get(3); + if (isInBounds(left, top, right, bottom, getChatScale())) { + chatting$isHovering = true; + lineInBounds = true; + args.set(4, ChattingConfig.INSTANCE.getHoveredChatBackgroundColor().getRGB()); + } + } + } + + @ModifyArgs(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) + private void drawChatBox(Args args) { + if (mc.currentScreen instanceof GuiChat) { + float f = this.getChatScale(); + int left = 0; + int top = (int) ((float) args.get(2) - 1); + int right = MathHelper.ceiling_float_int((float)getChatWidth() / f) + 4; + int bottom = (int) ((float) args.get(2) + 8); + if ((chatting$isHovering && lineInBounds) || isInBounds(left, top, right, bottom, f)) { + chatting$isHovering = true; + drawCopyChatBox(right, top); + } + } + lineInBounds = false; + } + + private boolean isInBounds(int left, int top, int right, int bottom, float chatScale) { + int mouseX = MathHelper.floor_double(UMouse.getScaledX()) - 3; + int mouseY = MathHelper.floor_double(UMouse.getScaledY()) - 27 + ModCompatHooks.getYOffset() - ModCompatHooks.getChatPosition(); + mouseX = MathHelper.floor_float((float) mouseX / chatScale); + mouseY = -(MathHelper.floor_float((float) mouseY / chatScale)); //WHY DO I NEED TO DO THIS + return mouseX >= (left + ModCompatHooks.getXOffset()) && mouseY < bottom && mouseX < (right + 23 + ModCompatHooks.getXOffset()) && mouseY >= top; + } + + @ModifyVariable(method = "drawChat", at = @At("STORE"), ordinal = 7) + private int modifyYeah(int value) { + return chatting$textOpacity = (int) (((float) (getChatOpen() ? 255 : value)) * (mc.gameSettings.chatOpacity * 0.9F + 0.1F)); + } + /*/ + @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;scale(FFF)V")) + private void drawPre(int updateCounter, CallbackInfo ci) { + RenderUtils.timestampPre(); + } + + @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;popMatrix()V")) + private void drawPost(int updateCounter, CallbackInfo ci) { + RenderUtils.timestampPost(); + } + + */ + + @Inject(method = "drawChat", at = @At("RETURN")) + private void checkStuff(int j2, CallbackInfo ci) { + if (!chatting$chatCheck && chatting$isHovering) { + chatting$isHovering = false; + } + } + + @Override + public int getRight() { + return chatting$right; + } + + @Override + public boolean isHovering() { + return chatting$isHovering; + } + + private void drawCopyChatBox(int right, int top) { + chatting$chatCheck = true; + GlStateManager.enableRescaleNormal(); + GlStateManager.enableBlend(); + GlStateManager.enableDepth(); + GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); + GlStateManager.pushMatrix(); + mc.getTextureManager().bindTexture(COPY); + GlStateManager.enableRescaleNormal(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(516, 0.1f); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(770, 771); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + chatting$right = right; + drawModalRectWithCustomSizedTexture(right + 1, top, 0f, 0f, 9, 9, 9, 9); + drawRect(right + 1, top, right + 10, top + 9, (((right + ModCompatHooks.getXOffset() + 3) <= (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale()) && (right + ModCompatHooks.getXOffset()) + 13 > (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale())) ? ChattingConfig.INSTANCE.getChatButtonHoveredBackgroundColor().getRGB() : ChattingConfig.INSTANCE.getChatButtonBackgroundColor().getRGB())); + GlStateManager.disableAlpha(); + GlStateManager.disableRescaleNormal(); + mc.getTextureManager().bindTexture(DELETE); + GlStateManager.enableRescaleNormal(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(516, 0.1f); + GlStateManager.enableBlend(); + GlStateManager.blendFunc(770, 771); + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); + drawModalRectWithCustomSizedTexture(right + 11, top, 0f, 0f, 9, 9, 9, 9); + drawRect(right + 11, top, right + 20, top + 9, (((right + ModCompatHooks.getXOffset() + 13) <= (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale()) && (right + ModCompatHooks.getXOffset()) + 23 > (UMouse.getScaledX() / mc.ingameGUI.getChatGUI().getChatScale())) ? ChattingConfig.INSTANCE.getChatButtonHoveredBackgroundColor().getRGB() : ChattingConfig.INSTANCE.getChatButtonBackgroundColor().getRGB())); + GlStateManager.disableLighting(); + GlStateManager.popMatrix(); + } + + @Override + public ChatLine getHoveredLine(int mouseY) { + if (this.getChatOpen()) { + ScaledResolution scaledresolution = new ScaledResolution(this.mc); + int i = scaledresolution.getScaleFactor(); + float f = this.getChatScale(); + int k = mouseY / i - 27 + ModCompatHooks.getYOffset() - ModCompatHooks.getChatPosition(); + k = MathHelper.floor_float((float) k / f); + + if (k >= 0) { + int l = Math.min(this.getLineCount(), this.drawnChatLines.size()); + + if (k < this.mc.fontRendererObj.FONT_HEIGHT * l + l) { + int i1 = k / this.mc.fontRendererObj.FONT_HEIGHT + this.scrollPos; + + if (i1 >= 0 && i1 < this.drawnChatLines.size()) { + List m = ChatSearchingManager.filterMessages(ChatSearchingManager.INSTANCE.getLastSearch(), this.drawnChatLines); + return m != null ? m.get(i1) : null; + } + + } + } + } + return null; + } + + @Override + public Transferable getChattingChatComponent(int mouseY) { + ChatLine subLine = getHoveredLine(mouseY); + if (subLine != null) { + ChatLine fullLine = this.getFullMessage(subLine); + if (GuiScreen.isShiftKeyDown()) { + if (fullLine != null) { + BufferedImage image = Chatting.INSTANCE.screenshotLine(subLine); + if (image != null) RenderUtils.copyToClipboard(image); + } + return null; + } + ChatLine line = GuiScreen.isCtrlKeyDown() ? subLine : fullLine; + String message = line == null ? "Could not find chat message." : line.getChatComponent().getFormattedText(); + String actualMessage = GuiScreen.isAltKeyDown() ? message : EnumChatFormatting.getTextWithoutFormattingCodes(message); + Notifications.INSTANCE.send("Chatting", line == null ? "Could not find chat message." : "Copied following text: " + actualMessage); + return new StringSelection(actualMessage); + } + return null; + } + + @Override + public int getTextOpacity() { + return chatting$textOpacity; + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java new file mode 100644 index 0000000..3b6d8b6 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatHeight.java @@ -0,0 +1,21 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.gui.GuiNewChat; +import org.polyfrost.chatting.Chatting; +import org.polyfrost.chatting.config.ChattingConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(GuiNewChat.class) +public abstract class GuiNewChatMixin_ChatHeight { + @Shadow public abstract boolean getChatOpen(); + + @Inject(method = "getChatHeight", at = @At("HEAD"), cancellable = true) + private void customHeight_getChatHeight(CallbackInfoReturnable cir) { + if (ChattingConfig.INSTANCE.getCustomChatHeight()) + cir.setReturnValue(Chatting.INSTANCE.getChatHeight(getChatOpen())); + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java new file mode 100644 index 0000000..f1ad3a4 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatSearching.java @@ -0,0 +1,31 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.GuiNewChat; +import net.minecraft.util.IChatComponent; +import org.objectweb.asm.Opcodes; +import org.polyfrost.chatting.chat.ChatSearchingManager; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(GuiNewChat.class) +public class GuiNewChatMixin_ChatSearching { + @Shadow @Final private List drawnChatLines; + + @Inject(method = "setChatLine", at = @At("HEAD")) + private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + ChatSearchingManager.getCache().invalidateAll(); + } + + @Redirect(method = "drawChat", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/GuiNewChat;drawnChatLines:Ljava/util/List;", opcode = Opcodes.GETFIELD)) + private List injected(GuiNewChat instance) { + return ChatSearchingManager.filterMessages(ChatSearchingManager.INSTANCE.getLastSearch(), drawnChatLines); + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java new file mode 100644 index 0000000..c101e6d --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_ChatTabs.java @@ -0,0 +1,54 @@ +package org.polyfrost.chatting.mixin; + +import org.polyfrost.chatting.chat.ChatTabs; +import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.utils.ModCompatHooks; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.GuiNewChat; +import net.minecraft.util.IChatComponent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(value = GuiNewChat.class, priority = 990) +public abstract class GuiNewChatMixin_ChatTabs { + @Shadow @Final private Minecraft mc; + + @Shadow public abstract void deleteChatLine(int id); + + @Shadow @Final private List chatLines; + + @Inject(method = "printChatMessageWithOptionalDeletion", at = @At("HEAD"), cancellable = true) + private void handlePrintChatMessage(IChatComponent chatComponent, int chatLineId, CallbackInfo ci) { + handleChatTabMessage(chatComponent, chatLineId, mc.ingameGUI.getUpdateCounter(), false, ci); + } + + @Inject(method = "setChatLine", at = @At("HEAD"), cancellable = true) + private void handleSetChatLine(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + handleChatTabMessage(chatComponent, chatLineId, updateCounter, displayOnly, ci); + } + + private void handleChatTabMessage(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) { + if (ChattingConfig.INSTANCE.getChatTabs()) { + if (!ChatTabs.INSTANCE.shouldRender(chatComponent)) { + ChatTabs.INSTANCE.setHasCancelledAnimation(true); + if (chatLineId != 0) { + deleteChatLine(chatLineId); + } + if (!displayOnly) { + chatLines.add(0, new ChatLine(updateCounter, chatComponent, chatLineId)); + while (this.chatLines.size() > (100 + ModCompatHooks.getExtendedChatLength())) { + this.chatLines.remove(this.chatLines.size() - 1); + } + } + ci.cancel(); + } + } + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java new file mode 100644 index 0000000..9a5a871 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_Scrolling.java @@ -0,0 +1,71 @@ +package org.polyfrost.chatting.mixin; + +import cc.polyfrost.oneconfig.gui.animations.EaseOutQuad; +import cc.polyfrost.oneconfig.utils.MathUtils; +import org.polyfrost.chatting.chat.ChatScrollingHook; +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.GuiNewChat; +import org.polyfrost.chatting.Chatting; +import org.polyfrost.chatting.config.ChattingConfig; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(GuiNewChat.class) +public abstract class GuiNewChatMixin_Scrolling extends Gui { + @Shadow private int scrollPos; + @Shadow @Final private List drawnChatLines; + + @Shadow public abstract int getLineCount(); + + @Shadow private boolean isScrolled; + + @Unique + private EaseOutQuad chatting$scrollingAnimation; + + @Inject(method = "drawChat", at = @At("HEAD")) + private void chatting$scrollingAnimationStart(int updateCounter, CallbackInfo ci) { + if (chatting$scrollingAnimation != null) { + if (chatting$scrollingAnimation.isFinished()) { + if (scrollPos == 0) { + isScrolled = false; + } + chatting$scrollingAnimation = null; + } else { + scrollPos = (int) chatting$scrollingAnimation.get(Chatting.INSTANCE.getDeltaTime()); + } + } + } + + @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V", ordinal = 1)) + private void redirectScrollBar(int left, int top, int right, int bottom, int color) { + if (!ChattingConfig.INSTANCE.getRemoveScrollBar()) { + drawRect(left, top, right, bottom, color); + } + } + + @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;drawRect(IIIII)V", ordinal = 2)) + private void redirectScrollBar2(int left, int top, int right, int bottom, int color) { + if (!ChattingConfig.INSTANCE.getRemoveScrollBar()) { + drawRect(left, top, right, bottom, color); + } + } + + @Inject(method = "scroll", at = @At("HEAD"), cancellable = true) + private void injectScroll(int amount, CallbackInfo ci) { + if (ChattingConfig.INSTANCE.getSmoothScrolling() && amount != 0 && ChatScrollingHook.INSTANCE.getShouldSmooth()) { + ci.cancel(); + ChatScrollingHook.INSTANCE.setShouldSmooth(false); + int result = (int) MathUtils.clamp(scrollPos + amount, 0, Math.max(drawnChatLines.size() - getLineCount() - 1, 0)); + chatting$scrollingAnimation = new EaseOutQuad((int) (ChattingConfig.INSTANCE.getScrollingSpeed() * 1000), scrollPos, result, false); + } + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java new file mode 100644 index 0000000..28f2aa8 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_SmoothMessages.java @@ -0,0 +1,103 @@ +package org.polyfrost.chatting.mixin; + +import org.polyfrost.chatting.Chatting; +import org.polyfrost.chatting.chat.ChatSearchingManager; +import org.polyfrost.chatting.chat.ChatTabs; +import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.utils.EaseOutQuart; +import org.polyfrost.chatting.utils.ModCompatHooks; +import net.minecraft.client.gui.GuiNewChat; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Locale; + +/** + * Taken from BetterChat under LGPL 3.0 + * https://github.com/LlamaLad7/Better-Chat/blob/1.8.9/LICENSE + */ +@Mixin(GuiNewChat.class) +public abstract class GuiNewChatMixin_SmoothMessages { + @Shadow + private boolean isScrolled; + + @Shadow + public abstract float getChatScale(); + @Unique + private int chatting$newLines; + + private EaseOutQuart chatting$easeOutQuart; + private float chatting$animationPercent; + @Unique + private int chatting$lineBeingDrawn; + + @Inject(method = "drawChat", at = @At("HEAD")) + private void modifyChatRendering(CallbackInfo ci) { + if (chatting$easeOutQuart != null) { + if (chatting$easeOutQuart.isFinished()) { + chatting$easeOutQuart = null; + } else { + chatting$animationPercent = chatting$easeOutQuart.get(Chatting.INSTANCE.getDeltaTime()); + } + } else { + chatting$animationPercent = 1; + } + } + + @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;pushMatrix()V", ordinal = 0, shift = At.Shift.AFTER)) + private void translate(CallbackInfo ci) { + float y = 0; + if (ChattingConfig.INSTANCE.getSmoothChat() && !this.isScrolled) { + y += (9 - 9 * chatting$animationPercent) * this.getChatScale(); + } + GlStateManager.translate(0, y, 0); + } + + @ModifyArg(method = "drawChat", at = @At(value = "INVOKE", target = "Ljava/util/List;get(I)Ljava/lang/Object;", ordinal = 0, remap = false), index = 0) + private int getLineBeingDrawn(int line) { + chatting$lineBeingDrawn = line; + return line; + } + + @ModifyArg(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) + private int modifyTextOpacity(int original) { + if (ChattingConfig.INSTANCE.getSmoothChat() && chatting$lineBeingDrawn <= chatting$newLines) { + int opacity = (original >> 24) & 0xFF; + opacity *= chatting$animationPercent; + return (original & ~(0xFF << 24)) | (opacity << 24); + } else { + return original; + } + } + + @Inject(method = "printChatMessageWithOptionalDeletion", at = @At("HEAD")) + private void resetPercentage(IChatComponent chatComponent, int chatLineId, CallbackInfo ci) { + if (!EnumChatFormatting.getTextWithoutFormattingCodes(chatComponent.getUnformattedText()).toLowerCase(Locale.ENGLISH).contains(ChatSearchingManager.INSTANCE.getLastSearch().toLowerCase(Locale.ENGLISH))) { + return; + } + if (ModCompatHooks.getBetterChatSmoothMessages()) { + return; + } + if (ChatTabs.INSTANCE.getHasCancelledAnimation()) { + ChatTabs.INSTANCE.setHasCancelledAnimation(false); + return; + } + chatting$easeOutQuart = new EaseOutQuart((1.0f - ChattingConfig.INSTANCE.getMessageSpeed()) * 1000f, 0f, 1f, false); + } + + @ModifyVariable(method = "setChatLine", at = @At("STORE"), ordinal = 0) + private List setNewLines(List original) { + chatting$newLines = original.size() - 1; + return original; + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java new file mode 100644 index 0000000..aff102c --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin_TextRendering.java @@ -0,0 +1,29 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.gui.ChatLine; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiNewChat; +import org.polyfrost.chatting.utils.ModCompatHooks; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(value = GuiNewChat.class, priority = 990) +public class GuiNewChatMixin_TextRendering { + @Unique + private ChatLine chatting$drawingLine = null; + + @Inject(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;getChatComponent()Lnet/minecraft/util/IChatComponent;"), locals = LocalCapture.CAPTURE_FAILSOFT) + private void captureChatLine(int updateCounter, CallbackInfo ci, int i, boolean bl, int j, int k, float f, float g, int l, int m, ChatLine chatLine, int n, double d, int o, int p, int q) { + chatting$drawingLine = chatLine; + } + + @Redirect(method = "drawChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) + private int redirectDrawString(FontRenderer instance, String text, float x, float y, int color) { + return ModCompatHooks.redirectDrawString(text, x, y, color, chatting$drawingLine, false); + } +} diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java new file mode 100644 index 0000000..1e2d0e6 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiUtilsMixin.java @@ -0,0 +1,36 @@ +package org.polyfrost.chatting.mixin; + +import cc.polyfrost.oneconfig.renderer.TextRenderer; +import net.minecraft.client.gui.FontRenderer; +import net.minecraftforge.fml.client.config.GuiUtils; +import org.polyfrost.chatting.config.ChattingConfig; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = GuiUtils.class, remap = false) +public class GuiUtilsMixin { + @Shadow + public static void drawGradientRect(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) { + } + + @Redirect(method = "drawHoveringText", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/client/config/GuiUtils;drawGradientRect(IIIIIII)V")) + private static void redirectBackground(int zLevel, int left, int top, int right, int bottom, int startColor, int endColor) { + if (!ChattingConfig.INSTANCE.getRemoveTooltipBackground()) { + drawGradientRect(zLevel, left, top, right, bottom, startColor, endColor); + } + } + + @Redirect(method = "drawHoveringText", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;FFI)I")) + private static int redirectText(FontRenderer instance, String text, float x, float y, int color) { + switch (ChattingConfig.INSTANCE.getTooltipTextRenderType()) { + case 0: + return instance.drawString(text, x, y, color, false); + case 2: + return TextRenderer.drawBorderedText(text, x, y, color, 255); + default: + return instance.drawStringWithShadow(text, x, y, color); + } + } +} diff --git a/src/main/kotlin/cc/woverflow/chatting/Chatting.kt b/src/main/kotlin/cc/woverflow/chatting/Chatting.kt deleted file mode 100644 index 9452dcf..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/Chatting.kt +++ /dev/null @@ -1,272 +0,0 @@ -package cc.woverflow.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 cc.woverflow.chatting.chat.ChatSearchingManager -import cc.woverflow.chatting.chat.ChatShortcuts -import cc.woverflow.chatting.chat.ChatSpamBlock -import cc.woverflow.chatting.chat.ChatTabs -import cc.woverflow.chatting.command.ChattingCommand -import cc.woverflow.chatting.config.ChattingConfig -import cc.woverflow.chatting.hook.ChatLineHook -import cc.woverflow.chatting.mixin.GuiNewChatAccessor -import cc.woverflow.chatting.utils.ModCompatHooks -import cc.woverflow.chatting.utils.copyToClipboard -import cc.woverflow.chatting.utils.createBindFramebuffer -import cc.woverflow.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 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 - } -} diff --git a/src/main/kotlin/cc/woverflow/chatting/chat/ChatRegexes.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatRegexes.kt deleted file mode 100644 index a0a8e86..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatRegexes.kt +++ /dev/null @@ -1,11 +0,0 @@ -package cc.woverflow.chatting.chat - -data class ChatRegexes(val regexList: List?) { - val compiledRegexList: MutableList = arrayListOf() - - init { - regexList?.forEach { - compiledRegexList.add(Regex(it)) - } - } -} diff --git a/src/main/kotlin/cc/woverflow/chatting/chat/ChatScrollingHook.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatScrollingHook.kt deleted file mode 100644 index b81de94..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatScrollingHook.kt +++ /dev/null @@ -1,5 +0,0 @@ -package cc.woverflow.chatting.chat - -object ChatScrollingHook { - var shouldSmooth = false -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/chat/ChatSearchingManager.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatSearchingManager.kt deleted file mode 100644 index 33a2642..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatSearchingManager.kt +++ /dev/null @@ -1,42 +0,0 @@ -package cc.woverflow.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> = Caffeine.newBuilder().executor(POOL).maximumSize(5000).build() - - var lastSearch = "" - - @JvmStatic - fun filterMessages(text: String, list: List): List? { - 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/cc/woverflow/chatting/chat/ChatShortcuts.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatShortcuts.kt deleted file mode 100644 index ef1881d..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatShortcuts.kt +++ /dev/null @@ -1,79 +0,0 @@ -package cc.woverflow.chatting.chat - -import cc.polyfrost.oneconfig.config.core.ConfigUtils -import cc.woverflow.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>() { - private val comparator = Comparator> { o1, o2 -> - return@Comparator o2.first.length.compareTo(o1.first.length) - } - - override fun add(element: Pair): 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/cc/woverflow/chatting/chat/ChatSpamBlock.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt deleted file mode 100644 index 471eec8..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatSpamBlock.kt +++ /dev/null @@ -1,124 +0,0 @@ -package cc.woverflow.chatting.chat - -import cc.woverflow.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 { - 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): Double { - val tokenProbs = mutableMapOf() - 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/cc/woverflow/chatting/chat/ChatTab.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatTab.kt deleted file mode 100644 index 50de1f4..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatTab.kt +++ /dev/null @@ -1,112 +0,0 @@ -package cc.woverflow.chatting.chat - -import cc.woverflow.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?, - val contains: List?, - @SerializedName("ends") val endsWith: List?, - val equals: List?, - @SerializedName("regex") val uncompiledRegex: List?, - @SerializedName("ignore_starts") val ignoreStartsWith: List?, - @SerializedName("ignore_contains") val ignoreContains: List?, - @SerializedName("ignore_ends") val ignoreEndsWith: List?, - @SerializedName("ignore_equals") val ignoreEquals: List?, - @SerializedName("ignore_regex") val uncompiledIgnoreRegex: List?, - 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/cc/woverflow/chatting/chat/ChatTabs.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatTabs.kt deleted file mode 100644 index 08423e5..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatTabs.kt +++ /dev/null @@ -1,354 +0,0 @@ -package cc.woverflow.chatting.chat - -import cc.polyfrost.oneconfig.config.core.ConfigUtils -import cc.woverflow.chatting.Chatting -import cc.woverflow.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() - var currentTabs: ArrayList = object : ArrayList() { - 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.tabs.forEach { - applyVersion4Changes(it.asJsonObject) - applyVersion5Changes(it.asJsonObject) - applyVersion6Changes(it.asJsonObject) - } - chatTabJson.version = ChatTabsJson.VERSION - tabFile.writeText(GSON.toJson(chatTabJson)) - } - 4 -> { - // ver 5 adds lowercase - chatTabJson.tabs.forEach { - applyVersion5Changes(it.asJsonObject) - applyVersion6Changes(it.asJsonObject) - } - chatTabJson.version = ChatTabsJson.VERSION - tabFile.writeText(GSON.toJson(chatTabJson)) - } - 5 -> { - // ver 6 changes pm regex - chatTabJson.tabs.forEach { - applyVersion6Changes(it.asJsonObject) - } - chatTabJson.version = ChatTabsJson.VERSION - tabFile.writeText(GSON.toJson(chatTabJson)) - } - } - chatTabJson.tabs.forEach { - val chatTab = GSON.fromJson(it.toString(), ChatTab::class.java) - if (chatTab.enabled) { - tabs.add(chatTab) - } - } - } catch (e: Throwable) { - e.printStackTrace() - tabFile.delete() - generateNewFile() - } - } - - private fun applyVersion2Changes(json: JsonObject) { - json.addProperty("enabled", true) - } - - private fun applyVersion3Changes(json: JsonObject) { - json.add("ignore_starts", JsonArray()) - json.add("ignore_contains", JsonArray()) - json.add("ignore_ends", JsonArray()) - json.add("ignore_equals", JsonArray()) - json.add("ignore_regex", JsonArray()) - } - - private fun applyVersion4Changes(json: JsonObject) { - json.addProperty("color", TabButton.color) - json.addProperty("hovered_color", TabButton.hoveredColor) - json.addProperty("selected_color", TabButton.selectedColor) - } - - private fun applyVersion5Changes(json: JsonObject) { - json.addProperty("lowercase", false) - } - - private fun applyVersion6Changes(json: JsonObject) { - if (json.has("starts")) { - val starts = json["starts"].asJsonArray - var detected = false - starts.iterator().let { - while (it.hasNext()) { - when (it.next().asString) { - "To " -> { - detected = true - it.remove() - } - "From " -> { - detected = true - it.remove() - } - } - } - } - if (detected) { - json.add("regex", JsonArray().apply { - add(JsonPrimitive("^(?§dTo|§dFrom) (?.+): §r(?.*)(?:§r)?\$")) - }) - json.remove("unformatted") - json.addProperty("unformatted", false) - } - } - if (json.has("ends")) { - val ends = json["ends"].asJsonArray - var detected = false - ends.iterator().let { - while (it.hasNext()) { - when (it.next().asString) { - "§r§ehas invited you to join their party!", -> { - detected = true - it.remove() - } - } - } - } - if (detected) { - json.add("contains", JsonArray().apply { - add(JsonPrimitive("§r§ehas invited you to join their party!")) - }) - } - } - } - - fun shouldRender(message: IChatComponent): Boolean { - if (currentTabs.isEmpty()) return true - for (tab in currentTabs) { - if (tab?.shouldRender(message) == true) { - return true - } - } - return false - } - - private fun generateNewFile() { - tabFile.createNewFile() - val jsonObject = JsonObject() - val defaultTabs = generateDefaultTabs() - jsonObject.add("tabs", defaultTabs) - jsonObject.addProperty("version", ChatTabsJson.VERSION) - tabFile.writeText(GSON.toJson(jsonObject)) - } - - private fun generateDefaultTabs(): JsonArray { - val all = ChatTab( - true, - "ALL", - unformatted = false, - lowercase = false, - startsWith = null, - contains = null, - endsWith = null, - equals = null, - uncompiledRegex = null, - ignoreStartsWith = null, - ignoreContains = null, - ignoreEndsWith = null, - ignoreEquals = null, - uncompiledIgnoreRegex = null, - color = TabButton.color, - hoveredColor = TabButton.hoveredColor, - selectedColor = TabButton.selectedColor, - prefix = "" - ) - val party = ChatTab( - true, - "PARTY", - unformatted = false, - lowercase = false, - startsWith = listOf("§r§9Party §8> ", "§r§9P §8> ", "§eThe party was transferred to §r", "§eKicked §r"), - contains = listOf("§r§ehas invited you to join their party!"), - endsWith = listOf( - "§r§eto the party! They have §r§c60 §r§eseconds to accept.§r", - "§r§ehas disbanded the party!§r", - "§r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before they are removed from the party.§r", - " §r§ejoined the party.§r", - " §r§ehas left the party.§r", - " §r§ehas been removed from the party.§r", - "§r§e because they were offline.§r" - ), - equals = listOf("§cThe party was disbanded because all invites expired and the party was empty§r"), - uncompiledRegex = listOf( //regexes from https://github.com/kwevin/Hychat-Tabs/blob/main/tabs/re-add%20prefixes%20%26%20fix%20shortened%20tags/chat.json cause i cant write regex - "(§r)*(§9Party §8\u003e)+(.*)", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§einvited §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto the party! They have §r§c60 §r§eseconds to accept\\.§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas left the party\\.§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ejoined the party\\.§r", - "§eYou left the party\\.§r", - "§eYou have joined §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)\u0027s §r§eparty!§r", - "§cThe party was disbanded because all invites expired and the party was empty§r", - "§cYou cannot invite that player since they\u0027re not online\\.§r", - "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e, warped you to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e\u0027s house\\.§r", - "§eSkyBlock Party Warp §r§7\\([0-9]+ players?\\)§r", - "§a. §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§f §r§awarped to your server§r", - "§eYou summoned §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§f §r§eto your server\\.§r", - "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e, warped you to their house\\.§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§aenabled Private Game§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§cdisabled Private Game§r", - "§cThe party is now muted\\. §r", - "§aThe party is no longer muted\\.§r", - "§cThere are no offline players to remove\\.§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas been removed from the party\\.§r", - "§eThe party was transferred to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eby §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has promoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Leader§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has promoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Moderator§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eis now a Party Moderator§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has demoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Member§r", - "§cYou can\u0027t demote yourself!§r", - "§6Party Members \\([0-9]+\\)§r", - "§eParty Leader: §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) ?§r(?:§[a-zA-Z0-9]).§r", - "§eParty Members: §r(?:(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r(?:§[a-zA-Z0-9]) . §r)+", - "§eParty Moderators: §r(?:(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r(?:§[a-zA-Z0-9]) . §r)+", - "§eThe party invite to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas expired§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§cdisabled All Invite§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§aenabled All Invite§r", - "§cYou cannot invite that player\\.§r", - "§cYou are not allowed to invite players\\.§r", - "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before the party is disbanded\\.§r", - "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before they are removed from the party.§r", - "§cYou are not in a party right now\\.§r", - "§cThis party is currently muted\\.§r", - "(§r)*(§9P §8\u003e)+(.*)" - ), - ignoreStartsWith = null, - ignoreContains = null, - ignoreEndsWith = null, - ignoreEquals = null, - uncompiledIgnoreRegex = null, - color = TabButton.color, - hoveredColor = TabButton.hoveredColor, - selectedColor = TabButton.selectedColor, - prefix = "/pc " - ) - val guild = ChatTab( - true, - "GUILD", - unformatted = true, - lowercase = false, - startsWith = listOf("Guild >", "G >"), - contains = null, - endsWith = null, - equals = null, - uncompiledRegex = null, - ignoreStartsWith = null, - ignoreContains = null, - ignoreEndsWith = null, - ignoreEquals = null, - uncompiledIgnoreRegex = null, - color = TabButton.color, - hoveredColor = TabButton.hoveredColor, - selectedColor = TabButton.selectedColor, - prefix = "/gc " - ) - val pm = ChatTab( - true, - "PM", - unformatted = false, - lowercase = false, - startsWith = null, - contains = null, - endsWith = null, - equals = null, - uncompiledRegex = listOf("^(?§dTo|§dFrom) (?.+): §r(?.*)(?:§r)?\$"), - ignoreStartsWith = null, - ignoreContains = null, - ignoreEndsWith = null, - ignoreEquals = null, - uncompiledIgnoreRegex = null, - color = TabButton.color, - hoveredColor = TabButton.hoveredColor, - selectedColor = TabButton.selectedColor, - prefix = "/r " - ) - tabs.add(all) - tabs.add(party) - tabs.add(guild) - tabs.add(pm) - val jsonArray = JsonArray() - jsonArray.add(PARSER.parse(GSON.toJson(all)).asJsonObject) - jsonArray.add(PARSER.parse(GSON.toJson(party)).asJsonObject) - jsonArray.add(PARSER.parse(GSON.toJson(guild)).asJsonObject) - jsonArray.add(PARSER.parse(GSON.toJson(pm)).asJsonObject) - return jsonArray - } -} diff --git a/src/main/kotlin/cc/woverflow/chatting/chat/ChatTabsJson.kt b/src/main/kotlin/cc/woverflow/chatting/chat/ChatTabsJson.kt deleted file mode 100644 index c632561..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/chat/ChatTabsJson.kt +++ /dev/null @@ -1,15 +0,0 @@ -package cc.woverflow.chatting.chat - -import com.google.gson.JsonArray -import com.google.gson.annotations.SerializedName - -data class ChatTabsJson(@SerializedName("tabs") val tabs: JsonArray, var version: Int) { - - override fun toString(): String { - return "{\"tabs\": $tabs, \"version\": $version}" - } - - companion object { - const val VERSION = 6 - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/command/ChattingCommand.kt b/src/main/kotlin/cc/woverflow/chatting/command/ChattingCommand.kt deleted file mode 100644 index 7fcd4a8..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/command/ChattingCommand.kt +++ /dev/null @@ -1,14 +0,0 @@ -package cc.woverflow.chatting.command - -import cc.polyfrost.oneconfig.utils.commands.annotations.Command -import cc.polyfrost.oneconfig.utils.commands.annotations.Main -import cc.woverflow.chatting.Chatting -import cc.woverflow.chatting.config.ChattingConfig - -@Command(value = Chatting.ID, description = "Access the " + Chatting.NAME + " GUI.") -class ChattingCommand { - @Main - fun main() { - ChattingConfig.openGui() - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt b/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt deleted file mode 100644 index 711250f..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt +++ /dev/null @@ -1,313 +0,0 @@ -package cc.woverflow.chatting.config - -import cc.polyfrost.oneconfig.config.Config -import cc.polyfrost.oneconfig.config.annotations.* -import cc.polyfrost.oneconfig.config.core.OneColor -import cc.polyfrost.oneconfig.config.data.InfoType -import cc.polyfrost.oneconfig.config.data.Mod -import cc.polyfrost.oneconfig.config.data.ModType -import cc.polyfrost.oneconfig.config.migration.VigilanceMigrator -import cc.polyfrost.oneconfig.utils.hypixel.HypixelUtils -import cc.woverflow.chatting.Chatting -import cc.woverflow.chatting.chat.ChatShortcuts -import cc.woverflow.chatting.chat.ChatTab -import cc.woverflow.chatting.chat.ChatTabs -import cc.woverflow.chatting.gui.components.TabButton -import cc.woverflow.chatting.hook.ChatLineHook -import cc.woverflow.chatting.utils.ModCompatHooks -import java.io.File - -object ChattingConfig : Config( - Mod( - Chatting.NAME, - ModType.UTIL_QOL, - "/chatting_dark.svg", - VigilanceMigrator(File(Chatting.oldModDir, Chatting.ID + ".toml").toPath().toString()) - ), "chatting.json" -) { - - @Dropdown( - name = "Text Render Type", category = "General", options = ["No Shadow", "Shadow", "Full Shadow"], - description = "Specifies how text should be rendered in the chat. Full Shadow displays a shadow on all sides of the text, while Shadow only displays a shadow on the right and bottom sides of the text." - ) - var textRenderType = 1 - - @Color( - name = "Chat Background Color", category = "General", - description = "The color of the chat background." - ) - var chatBackgroundColor = OneColor(0, 0, 0, 128) - - @Color( - name = "Copy Chat Message Background Color", category = "General", - description = "The color of the chat background when hovering over a message." - ) - var hoveredChatBackgroundColor = OneColor(80, 80, 80, 128) - - @Switch( - name = "Right Click to Copy Chat Message", category = "General", - description = "Enable right clicking on a chat message to copy it." - ) - var rightClickCopy = false - - @Switch( - name = "Compact Input Box", category = "General", - description = "Make the chat input box the same width as the chat box." - ) - var compactInputBox = false - - @Color( - name = "Input Box Background Color", category = "General", - description = "The color of the chat input box background." - ) - var inputBoxBackgroundColor = OneColor(0, 0, 0, 128) - - @Color( - name = "Chat Button Background Color", category = "General", - description = "The color of the chat button background." - ) - var chatButtonBackgroundColor = OneColor(0, 0, 0, 128) - - @Color( - name = "Chat Button Hovered Background Color", category = "General", - description = "The color of the chat button background when hovered." - ) - var chatButtonHoveredBackgroundColor = OneColor(255, 255, 255, 128) - - @Switch( - name = "Inform Outdated Mods", category = "General", - description = "Inform the user when a mod can be replaced by Chatting." - ) - var informForAlternatives = true - - @Switch( - name = "Smooth Chat Messages", - category = "Animations", subcategory = "Messages", - description = "Smoothly animate chat messages when they appear." - ) - var smoothChat = true - - @Slider( - name = "Message Animation Speed", - category = "Animations", subcategory = "Messages", - min = 0.0f, max = 1.0f, - description = "The speed at which chat messages animate." - ) - var messageSpeed = 0.5f - - @Switch( - name = "Smooth Chat Scrolling", - category = "Animations", subcategory = "Scrolling", - description = "Smoothly animate scrolling when scrolling through the chat." - ) - var smoothScrolling = true - - @Slider( - name = "Scrolling Animation Speed", - category = "Animations", subcategory = "Scrolling", - min = 0.0f, max = 1.0f, - description = "The speed at which scrolling animates." - ) - var scrollingSpeed = 0.15f - - @Switch( - name = "Remove Scroll Bar", - category = "Animations", subcategory = "Scrolling", - description = "Removes the vanilla scroll bar from the chat." - ) - var removeScrollBar = true - - @Switch( - name = "Show Chat Heads", description = "Show the chat heads of players in chat", category = "Chat Heads", - ) - var showChatHeads = true - - @Switch( - name = "Offset Non-Player Messages", - description = "Offset all messages, even if a player has not been detected.", - category = "Chat Heads" - ) - var offsetNonPlayerMessages = false - - @Switch( - name = "Hide Chat Head on Consecutive Messages", - description = "Hide the chat head if the previous message was from the same player.", - category = "Chat Heads" - ) - var hideChatHeadOnConsecutiveMessages = true - - /*/ - @Property( - type = PropertyType.SWITCH, - name = "Show Timestamp", - description = "Show message timestamp.", - category = "General" - ) - var showTimestamp = false - - @Property( - type = PropertyType.SWITCH, - name = "Timestamp Only On Hover", - description = "Show timestamp only on mouse hover.", - category = "General" - ) - var showTimestampHover = true - - */ - - @Info( - text = "If Chatting detects a public chat message that seems like spam, and the probability is higher than this, it will hide it.\n" + "Made for Hypixel Skyblock. Set to 100% to disable. 95% is a reasonable threshold to use it at.\n" + "Note that this is not and never will be 100% accurate; however, it's pretty much guaranteed to block most spam.", - size = 2, - category = "Player Chats", - type = InfoType.INFO - ) - var ignored = false - - @Slider( - min = 80F, max = 100F, name = "Spam Blocker Threshold", category = "Player Chats" - ) - var spamThreshold = 100 - - @Switch( - name = "Custom SkyBlock Chat Formatting (remove ranks)", category = "Player Chats" - ) - var customChatFormatting = false - - @Switch( - name = "Completely Hide Spam", category = "Player Chats" - ) - var hideSpam = false - - @Switch( - name = "Custom Chat Height", category = "Chat Window", - description = "Set a custom height for the chat window. Allows for more customization than the vanilla chat height options." - ) - var customChatHeight = false - - @Slider( - min = 180F, max = 2160F, name = "Focused Height (px)", category = "Chat Window", - description = "The height of the chat window when focused." - ) - var focusedHeight = 180 - - @Slider( - min = 180F, max = 2160F, name = "Unfocused Height (px)", category = "Chat Window", - description = "The height of the chat window when unfocused." - ) - var unfocusedHeight = 180 - - @Dropdown( - name = "Screenshot Mode", category = "Screenshotting", options = ["Save To System", "Add To Clipboard", "Both"], - description = "What to do when taking a screenshot." - ) - var copyMode = 0 - - @Checkbox( - name = "Chat Searching", category = "Searching", - description = "Enable searching through chat messages." - ) - var chatSearch = true - - @Switch( - name = "Chat Tabs", category = "Tabs", - description = "Allow filtering chat messages by a tab." - ) - var chatTabs = true - get() { - if (!field) return false - return if (hypixelOnlyChatTabs) { - HypixelUtils.INSTANCE.isHypixel - } else { - true - } - } - - @Checkbox( - name = "Enable Tabs Only on Hypixel", category = "Tabs", - description = "Only enable chat tabs on Hypixel" - ) - var hypixelOnlyChatTabs = true - - @Switch( - name = "Chat Shortcuts", category = "Shortcuts" - ) - var chatShortcuts = false - get() { - if (!field) return false - return if (hypixelOnlyChatShortcuts) { - HypixelUtils.INSTANCE.isHypixel - } else { - true - } - } - - @Checkbox( - name = "Enable Shortcuts Only on Hypixel", category = "Shortcuts" - ) - var hypixelOnlyChatShortcuts = true - - @Switch( - name = "Remove Tooltip Background", category = "Tooltips", - description = "Removes the background from tooltips." - ) - var removeTooltipBackground = false - - @Dropdown( - name = "Tooltip Text Render Type", category = "Tooltips", options = ["No Shadow", "Shadow", "Full Shadow"], - description = "The type of shadow to render on tooltips." - ) - var tooltipTextRenderType = 1 - - init { - initialize() - addDependency("offsetNonPlayerMessages", "showChatHeads") - addDependency("hideChatHeadOnConsecutiveMessages", "showChatHeads") - addDependency("hypixelOnlyChatTabs", "chatTabs") - addDependency("hypixelOnlyChatShortcuts", "chatShortcuts") - addDependency("focusedHeight", "customChatHeight") - addDependency("unfocusedHeight", "customChatHeight") - addDependency("scrollingSpeed", "smoothScrolling") - addDependency("messageSpeed", "smoothChat") - addDependency("smoothChat", "BetterChat Smooth Chat") { - return@addDependency !ModCompatHooks.betterChatSmoothMessages - } - addListener("hideChatHeadOnConsecutiveMessages") { - ChatLineHook.chatLines.map { it.get() as ChatLineHook? }.forEach { it?.updatePlayerInfo() } - } - addListener("chatTabs") { - ChatTabs.initialize() - if (!chatTabs) { - val dummy = ChatTab( - true, - "ALL", - unformatted = false, - lowercase = false, - startsWith = null, - contains = null, - endsWith = null, - equals = null, - uncompiledRegex = null, - ignoreStartsWith = null, - ignoreContains = null, - ignoreEndsWith = null, - ignoreEquals = null, - uncompiledIgnoreRegex = null, - color = TabButton.color, - hoveredColor = TabButton.hoveredColor, - selectedColor = TabButton.selectedColor, - prefix = "" - ) - dummy.initialize() - ChatTabs.currentTabs.clear() - ChatTabs.currentTabs.add(dummy) - } else { - ChatTabs.currentTabs.clear() - ChatTabs.currentTabs.add(ChatTabs.tabs[0]) - } - } - addListener("chatShortcuts") { - ChatShortcuts.initialize() - } - // addDependency("showTimestampHover", "showTimestamp") - } -} diff --git a/src/main/kotlin/cc/woverflow/chatting/gui/components/CleanButton.kt b/src/main/kotlin/cc/woverflow/chatting/gui/components/CleanButton.kt deleted file mode 100644 index de590e6..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/gui/components/CleanButton.kt +++ /dev/null @@ -1,103 +0,0 @@ -package cc.woverflow.chatting.gui.components - -import cc.polyfrost.oneconfig.renderer.TextRenderer -import cc.woverflow.chatting.Chatting -import cc.woverflow.chatting.config.ChattingConfig -import cc.woverflow.chatting.hook.GuiNewChatHook -import club.sk1er.patcher.config.PatcherConfig -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.GuiButton -import net.minecraft.client.renderer.GlStateManager - -/** - * Taken from ChatShortcuts under MIT License - * https://github.com/P0keDev/ChatShortcuts/blob/master/LICENSE - * @author P0keDev - */ -open class CleanButton( - buttonId: Int, - private val x: () -> Int, - private val y: () -> Int, - widthIn: Int, - heightIn: Int, - name: String, - private val renderType: () -> RenderType, - private val textColor: (packedFGColour: Int, enabled: Boolean, hovered: Boolean) -> Int = { packedFGColour: Int, enabled: Boolean, hovered: Boolean -> - var j = 14737632 - if (packedFGColour != 0) { - j = packedFGColour - } else if (!enabled) { - j = 10526880 - } else if (hovered) { - j = 16777120 - } - j - }, -) : - GuiButton(buttonId, x.invoke(), 0, widthIn, heightIn, name) { - - open fun isEnabled(): Boolean { - return false - } - - open fun onMousePress() { - - } - - override fun mousePressed(mc: Minecraft, mouseX: Int, mouseY: Int): Boolean { - val isPressed = - visible && mouseX >= xPosition && mouseY >= yPosition && mouseX < xPosition + width && mouseY < yPosition + height - if (isPressed) { - onMousePress() - } - return isPressed - } - - override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { - enabled = isEnabled() - xPosition = x() - yPosition = y() - if (visible) { - val fontrenderer = mc.fontRendererObj - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f) - hovered = - mouseX >= xPosition && mouseY >= yPosition && mouseX < xPosition + width && mouseY < yPosition + height - if (!Chatting.isPatcher || !PatcherConfig.transparentChatInputField) { - drawRect( - xPosition, - yPosition, - xPosition + width, - yPosition + height, - getBackgroundColor(hovered) - ) - } - mouseDragged(mc, mouseX, mouseY) - val j = textColor(packedFGColour, enabled, hovered) - when (renderType()) { - RenderType.NONE, RenderType.SHADOW -> { - drawCenteredString( - fontrenderer, - displayString, - xPosition + width / 2, - yPosition + (height - 8) / 2, - j - ) - } - - RenderType.FULL -> { - TextRenderer.drawBorderedText( - displayString, - ((xPosition + width / 2) - (fontrenderer.getStringWidth(displayString) / 2)).toFloat(), - (yPosition + (height - 8) / 2).toFloat(), - j, - (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatHook).textOpacity - ) - } - } - } - } - - private fun getBackgroundColor(hovered: Boolean) = - if (hovered) ChattingConfig.chatButtonHoveredBackgroundColor.rgb - else ChattingConfig.chatButtonBackgroundColor.rgb -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/gui/components/ClearButton.kt b/src/main/kotlin/cc/woverflow/chatting/gui/components/ClearButton.kt deleted file mode 100644 index 6ac3d34..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/gui/components/ClearButton.kt +++ /dev/null @@ -1,42 +0,0 @@ -package cc.woverflow.chatting.gui.components - -import cc.polyfrost.oneconfig.libs.universal.ChatColor -import cc.polyfrost.oneconfig.libs.universal.UChat -import cc.polyfrost.oneconfig.libs.universal.UResolution -import cc.polyfrost.oneconfig.utils.Multithreading -import cc.woverflow.chatting.Chatting -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Gui -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.util.ResourceLocation - -class ClearButton : - CleanButton(13379014, { UResolution.scaledWidth - 28 }, { UResolution.scaledHeight - 27 }, 12, 12, "", - { RenderType.NONE }) { - - var times = 0 - - override fun onMousePress() { - ++times - if (times > 1) { - times = 0 - Minecraft.getMinecraft().ingameGUI.chatGUI.clearChatMessages() - } else { - UChat.chat(ChatColor.RED + ChatColor.BOLD.toString() + "Click again to clear the chat!") - Multithreading.runAsync { - Thread.sleep(3000) - times = 0 - } - } - } - - override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { - super.drawButton(mc, mouseX, mouseY) - if (visible) { - if (hovered) GlStateManager.color(1f, 1f, 160f / 255f) - else GlStateManager.color(1f, 1f, 1f) - mc.textureManager.bindTexture(ResourceLocation(Chatting.ID, "delete.png")) - Gui.drawModalRectWithCustomSizedTexture(xPosition + 1, yPosition + 1, 0f, 0f, 10, 10, 10f, 10f) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/gui/components/RenderType.kt b/src/main/kotlin/cc/woverflow/chatting/gui/components/RenderType.kt deleted file mode 100644 index 8a56d5b..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/gui/components/RenderType.kt +++ /dev/null @@ -1,7 +0,0 @@ -package cc.woverflow.chatting.gui.components - -enum class RenderType { - NONE, - SHADOW, - FULL -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/gui/components/ScreenshotButton.kt b/src/main/kotlin/cc/woverflow/chatting/gui/components/ScreenshotButton.kt deleted file mode 100644 index 4f65427..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/gui/components/ScreenshotButton.kt +++ /dev/null @@ -1,36 +0,0 @@ -package cc.woverflow.chatting.gui.components - -import cc.polyfrost.oneconfig.libs.universal.UResolution -import cc.polyfrost.oneconfig.libs.universal.UScreen -import cc.woverflow.chatting.Chatting -import cc.woverflow.chatting.mixin.GuiNewChatAccessor -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Gui -import net.minecraft.client.gui.GuiChat -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.util.ResourceLocation - -class ScreenshotButton : - CleanButton(448318, { UResolution.scaledWidth - 42 }, { UResolution.scaledHeight - 27 }, 12, 12, "", - { RenderType.NONE }) { - - override fun onMousePress() { - val chat = Minecraft.getMinecraft().ingameGUI.chatGUI - if (UScreen.currentScreen is GuiChat) { - Chatting.screenshotChat((chat as GuiNewChatAccessor).scrollPos) - } - } - - override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { - super.drawButton(mc, mouseX, mouseY) - if (visible) { - if (hovered) { - GlStateManager.color(1f, 1f, 160f / 255f) - } else { - GlStateManager.color(1f, 1f, 1f) - } - mc.textureManager.bindTexture(ResourceLocation(Chatting.ID, "screenshot.png")) - Gui.drawModalRectWithCustomSizedTexture(xPosition + 1, yPosition + 1, 0f, 0f, 10, 10, 10f, 10f) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/gui/components/SearchButton.kt b/src/main/kotlin/cc/woverflow/chatting/gui/components/SearchButton.kt deleted file mode 100644 index 54e9041..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/gui/components/SearchButton.kt +++ /dev/null @@ -1,70 +0,0 @@ -package cc.woverflow.chatting.gui.components - -import cc.polyfrost.oneconfig.libs.universal.UResolution -import cc.woverflow.chatting.Chatting -import cc.woverflow.chatting.chat.ChatSearchingManager -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.Gui -import net.minecraft.client.gui.GuiTextField -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.util.ResourceLocation - -class SearchButton : - CleanButton(3993935, { UResolution.scaledWidth - 14 }, { UResolution.scaledHeight - 27 }, 12, 12, "", - { RenderType.NONE }) { - val inputField = SearchTextField() - private var chatBox = false - - override fun isEnabled(): Boolean { - return chatBox - } - - override fun onMousePress() { - chatBox = !chatBox - inputField.setEnabled(chatBox) - inputField.isFocused = chatBox - ChatSearchingManager.lastSearch = "" - inputField.text = "" - } - - override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { - inputField.drawTextBox() - super.drawButton(mc, mouseX, mouseY) - if (visible) { - mc.textureManager.bindTexture(ResourceLocation(Chatting.ID, "search.png")) - if (isEnabled()) { - GlStateManager.color(224f / 255f, 224f / 255f, 224f / 255f) - } else if (mouseX >= xPosition && mouseX <= xPosition + 10 && mouseY >= yPosition && mouseY <= yPosition + 10) { - GlStateManager.color(1f, 1f, 160f / 255f) - } else { - GlStateManager.color(1f, 1f, 1f) - } - Gui.drawModalRectWithCustomSizedTexture(xPosition + 1, yPosition + 1, 0f, 0f, 10, 10, 10f, 10f) - } - } - - inner class SearchTextField : GuiTextField( - 69420, - Minecraft.getMinecraft().fontRendererObj, - UResolution.scaledWidth * 4 / 5 - 60, - UResolution.scaledHeight - 26, - UResolution.scaledWidth / 5, - 12 - ) { - - init { - maxStringLength = 100 - enableBackgroundDrawing = true - isFocused = false - text = "" - setCanLoseFocus(true) - } - - override fun drawTextBox() { - if (isEnabled()) { - if (!isFocused) isFocused = true - super.drawTextBox() - } - } - } -} diff --git a/src/main/kotlin/cc/woverflow/chatting/gui/components/TabButton.kt b/src/main/kotlin/cc/woverflow/chatting/gui/components/TabButton.kt deleted file mode 100644 index e3b96f1..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/gui/components/TabButton.kt +++ /dev/null @@ -1,46 +0,0 @@ -package cc.woverflow.chatting.gui.components - -import cc.polyfrost.oneconfig.libs.universal.UKeyboard -import cc.polyfrost.oneconfig.libs.universal.UResolution -import cc.woverflow.chatting.chat.ChatTab -import cc.woverflow.chatting.chat.ChatTabs -import cc.woverflow.chatting.config.ChattingConfig - -class TabButton(buttonId: Int, x: Int, widthIn: Int, heightIn: Int, private val chatTab: ChatTab) : - CleanButton(buttonId, { x }, { - UResolution.scaledHeight - 26 - }, widthIn, heightIn, chatTab.name, { RenderType.values()[ChattingConfig.textRenderType] }, { packedFGColour: Int, enabled: Boolean, hovered: Boolean -> - var j = chatTab.color ?: color - if (packedFGColour != 0) { - j = packedFGColour - } else if (!enabled) { - j = chatTab.selectedColor ?: selectedColor - } else if (hovered) { - j = chatTab.hoveredColor ?: hoveredColor - } - j - }) { - - override fun onMousePress() { - if (UKeyboard.isShiftKeyDown()) { - if (ChatTabs.currentTabs.contains(chatTab)) { - ChatTabs.currentTabs.remove(chatTab) - } else { - ChatTabs.currentTabs.add(chatTab) - } - } else { - ChatTabs.currentTabs.clear() - ChatTabs.currentTabs.add(chatTab) - } - } - - override fun isEnabled(): Boolean { - return ChatTabs.currentTabs.contains(chatTab) - } - - companion object { - const val color: Int = 14737632 - const val hoveredColor: Int = 16777120 - const val selectedColor: Int = 10526880 - } -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/utils/EaseOutQuart.kt b/src/main/kotlin/cc/woverflow/chatting/utils/EaseOutQuart.kt deleted file mode 100644 index 7943b4d..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/utils/EaseOutQuart.kt +++ /dev/null @@ -1,7 +0,0 @@ -package cc.woverflow.chatting.utils - -import cc.polyfrost.oneconfig.gui.animations.Animation - -class EaseOutQuart(duration: Float, start: Float, end: Float, reverse: Boolean) : Animation(duration, start, end, reverse) { - override fun animate(x: Float) = -1 * (x - 1) * (x - 1) * (x - 1) * (x - 1) + 1 -} \ No newline at end of file diff --git a/src/main/kotlin/cc/woverflow/chatting/utils/ModCompatHooks.kt b/src/main/kotlin/cc/woverflow/chatting/utils/ModCompatHooks.kt deleted file mode 100644 index 3c60c72..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/utils/ModCompatHooks.kt +++ /dev/null @@ -1,100 +0,0 @@ -package cc.woverflow.chatting.utils - -import cc.polyfrost.oneconfig.renderer.TextRenderer -import cc.polyfrost.oneconfig.utils.dsl.getAlpha -import cc.polyfrost.oneconfig.utils.dsl.mc -import cc.woverflow.chatting.Chatting.isBetterChat -import cc.woverflow.chatting.Chatting.isPatcher -import cc.woverflow.chatting.config.ChattingConfig.offsetNonPlayerMessages -import cc.woverflow.chatting.config.ChattingConfig.showChatHeads -import cc.woverflow.chatting.config.ChattingConfig.textRenderType -import cc.woverflow.chatting.hook.ChatLineHook -import cc.woverflow.chatting.hook.GuiNewChatHook -import club.sk1er.patcher.config.PatcherConfig -import com.llamalad7.betterchat.BetterChat -import net.minecraft.client.Minecraft -import net.minecraft.client.gui.ChatLine -import net.minecraft.client.gui.FontRenderer -import net.minecraft.client.gui.Gui -import net.minecraft.client.renderer.GlStateManager - -// This exists because mixin doesn't like dummy classes -object ModCompatHooks { - @JvmStatic - val xOffset - get() = if (isBetterChat) BetterChat.getSettings().xOffset else 0 - - @JvmStatic - val yOffset - get() = if (isBetterChat) BetterChat.getSettings().yOffset else 0 - - @JvmStatic - val chatPosition - get() = if (isPatcher && PatcherConfig.chatPosition) 12 else 0 - - @JvmStatic - val betterChatSmoothMessages - get() = if (isBetterChat) BetterChat.getSettings().smooth else false - - @JvmStatic - val extendedChatLength - get() = if (isPatcher) 32667 else 0 - - @JvmStatic - val fontRenderer: FontRenderer - get() = Minecraft.getMinecraft().fontRendererObj - - @JvmStatic - fun redirectDrawString(text: String, x: Float, y: Float, color: Int, chatLine: ChatLine, screenshot: Boolean): Int { - var actualX = x - if (showChatHeads && !screenshot) { - val hook = chatLine as ChatLineHook - if (hook.hasDetected() || offsetNonPlayerMessages) { - actualX += 10f - } - val networkPlayerInfo = hook.playerInfo - if (networkPlayerInfo != null) { - GlStateManager.enableBlend() - GlStateManager.enableAlpha() - GlStateManager.enableTexture2D() - mc.textureManager.bindTexture(networkPlayerInfo.locationSkin) - GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0) - GlStateManager.color(1.0f, 1.0f, 1.0f, color.getAlpha() / 255f) - Gui.drawScaledCustomSizeModalRect( - (x).toInt(), - (y - 1f).toInt(), - 8.0f, - 8.0f, - 8, - 8, - 8, - 8, - 64.0f, - 64.0f - ) - Gui.drawScaledCustomSizeModalRect( - (x).toInt(), - (y - 1f).toInt(), - 40.0f, - 8.0f, - 8, - 8, - 8, - 8, - 64.0f, - 64.0f - ) - GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f) - } - } - return when (textRenderType) { - 0 -> fontRenderer.drawString(text, actualX, y, color, false) - 2 -> TextRenderer.drawBorderedText(text, - actualX, - y, - color, - (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatHook).textOpacity) - else -> fontRenderer.drawString(text, actualX, y, color, true) - } - } -} diff --git a/src/main/kotlin/cc/woverflow/chatting/utils/RenderUtils.kt b/src/main/kotlin/cc/woverflow/chatting/utils/RenderUtils.kt deleted file mode 100644 index 12051cf..0000000 --- a/src/main/kotlin/cc/woverflow/chatting/utils/RenderUtils.kt +++ /dev/null @@ -1,259 +0,0 @@ -@file:JvmName("RenderUtils") - -package cc.woverflow.chatting.utils - -import cc.polyfrost.oneconfig.utils.IOUtils -import cc.woverflow.chatting.config.ChattingConfig -import net.minecraft.client.renderer.GlStateManager -import net.minecraft.client.renderer.texture.TextureUtil -import net.minecraft.client.shader.Framebuffer -import org.apache.commons.lang3.SystemUtils -import org.lwjgl.BufferUtils -import org.lwjgl.opengl.GL11 -import org.lwjgl.opengl.GL12 -import sun.awt.datatransfer.DataTransferer -import sun.awt.datatransfer.SunClipboard -import java.awt.Toolkit -import java.awt.image.BufferedImage -import java.io.File -import java.lang.reflect.Field -import java.lang.reflect.Method -import java.nio.ByteBuffer -import java.nio.ByteOrder -import javax.imageio.ImageIO - -/** - * Taken from https://github.com/Moulberry/HyChat - */ -fun createBindFramebuffer(w: Int, h: Int): Framebuffer { - val framebuffer = Framebuffer(w, h, false) - framebuffer.framebufferColor[0] = 0x36 / 255f - framebuffer.framebufferColor[1] = 0x39 / 255f - framebuffer.framebufferColor[2] = 0x3F / 255f - framebuffer.framebufferClear() - GlStateManager.matrixMode(5889) - GlStateManager.loadIdentity() - GlStateManager.ortho(0.0, w.toDouble(), h.toDouble(), 0.0, 1000.0, 3000.0) - GlStateManager.matrixMode(5888) - GlStateManager.loadIdentity() - GlStateManager.translate(0.0f, 0.0f, -2000.0f) - framebuffer.bindFramebuffer(true) - return framebuffer -} - -/** - * Taken from https://github.com/Moulberry/HyChat - * Modified so if not on Windows just in case it will switch it to RGB and remove the transparent background. - */ -fun BufferedImage.copyToClipboard() { - if (SystemUtils.IS_OS_WINDOWS) { - try { - val width = this.width - val height = this.height - val hdrSize = 0x28 - val buffer: ByteBuffer = ByteBuffer.allocate(hdrSize + width * height * 4) - buffer.order(ByteOrder.LITTLE_ENDIAN) - //Header size - buffer.putInt(hdrSize) - //Width - buffer.putInt(width) - //Int32 biHeight; - buffer.putInt(height) - //Int16 biPlanes; - buffer.put(1.toByte()) - buffer.put(0.toByte()) - //Int16 biBitCount; - buffer.put(32.toByte()) - buffer.put(0.toByte()) - //Compression - buffer.putInt(0) - //Int32 biSizeImage; - buffer.putInt(width * height * 4) - buffer.putInt(0) - buffer.putInt(0) - buffer.putInt(0) - buffer.putInt(0) - - //Image data - for (y in 0 until height) { - for (x in 0 until width) { - val argb: Int = this.getRGB(x, height - y - 1) - if (argb shr 24 and 0xFF == 0) { - buffer.putInt(0x00000000) - } else { - buffer.putInt(argb) - } - } - } - buffer.flip() - val hdrSizev5 = 0x7C - val bufferv5: ByteBuffer = ByteBuffer.allocate(hdrSizev5 + width * height * 4) - bufferv5.order(ByteOrder.LITTLE_ENDIAN) - //Header size - bufferv5.putInt(hdrSizev5) - //Width - bufferv5.putInt(width) - //Int32 biHeight; - bufferv5.putInt(height) - //Int16 biPlanes; - bufferv5.put(1.toByte()) - bufferv5.put(0.toByte()) - //Int16 biBitCount; - bufferv5.put(32.toByte()) - bufferv5.put(0.toByte()) - //Compression - bufferv5.putInt(0) - //Int32 biSizeImage; - bufferv5.putInt(width * height * 4) - bufferv5.putInt(0) - bufferv5.putInt(0) - bufferv5.putInt(0) - bufferv5.putInt(0) - bufferv5.order(ByteOrder.BIG_ENDIAN) - bufferv5.putInt(-0x1000000) - bufferv5.putInt(0x00FF0000) - bufferv5.putInt(0x0000FF00) - bufferv5.putInt(0x000000FF) - bufferv5.order(ByteOrder.LITTLE_ENDIAN) - - //BGRs - bufferv5.put(0x42.toByte()) - bufferv5.put(0x47.toByte()) - bufferv5.put(0x52.toByte()) - bufferv5.put(0x73.toByte()) - for (i in bufferv5.position() until hdrSizev5) { - bufferv5.put(0.toByte()) - } - - //Image data - for (y in 0 until height) { - for (x in 0 until width) { - val argb: Int = this.getRGB(x, height - y - 1) - val a = argb shr 24 and 0xFF - var r = argb shr 16 and 0xFF - var g = argb shr 8 and 0xFF - var b = argb and 0xFF - r = r * a / 0xFF - g = g * a / 0xFF - b = b * a / 0xFF - bufferv5.putInt(a shl 24 or (r shl 16) or (g shl 8) or b) - } - } - bufferv5.flip() - val clip = Toolkit.getDefaultToolkit().systemClipboard - val dt = DataTransferer.getInstance() - val f: Field = dt.javaClass.getDeclaredField("CF_DIB") - f.isAccessible = true - val format: Long = f.getLong(null) - val openClipboard: Method = clip.javaClass.getDeclaredMethod("openClipboard", SunClipboard::class.java) - openClipboard.isAccessible = true - openClipboard.invoke(clip, clip) - val publishClipboardData: Method = clip.javaClass.getDeclaredMethod( - "publishClipboardData", - Long::class.javaPrimitiveType, - ByteArray::class.java - ) - publishClipboardData.isAccessible = true - val arr: ByteArray = buffer.array() - publishClipboardData.invoke(clip, format, arr) - val closeClipboard: Method = clip.javaClass.getDeclaredMethod("closeClipboard") - closeClipboard.isAccessible = true - closeClipboard.invoke(clip) - return - } catch (e: Exception) { - e.printStackTrace() - } - } - val pixels: IntArray = - this.getRGB(0, 0, this.width, this.height, null, 0, this.width) - val newImage = BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB) - newImage.setRGB(0, 0, newImage.width, newImage.height, pixels, 0, newImage.width) - - try { - IOUtils.copyImageToClipboard(this) - } catch (e: Exception) { - e.printStackTrace() - } -} - -/** - * Taken from https://github.com/Moulberry/HyChat - */ -fun Framebuffer.screenshot(file: File): BufferedImage { - val w = this.framebufferWidth - val h = this.framebufferHeight - val i = w * h - val pixelBuffer = BufferUtils.createIntBuffer(i) - val pixelValues = IntArray(i) - GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1) - GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1) - GlStateManager.bindTexture(this.framebufferTexture) - GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer) - pixelBuffer[pixelValues] //Load buffer into array - TextureUtil.processPixelValues(pixelValues, w, h) //Flip vertically - val bufferedimage = BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB) - val j = this.framebufferTextureHeight - this.framebufferHeight - for (k in j until this.framebufferTextureHeight) { - for (l in 0 until this.framebufferWidth) { - bufferedimage.setRGB(l, k - j, pixelValues[k * this.framebufferTextureWidth + l]) - } - } - if (ChattingConfig.copyMode != 1) { - try { - file.parentFile.mkdirs() - ImageIO.write(bufferedimage, "png", file) - } catch (e: Exception) { - e.printStackTrace() - } - } - return bufferedimage -} -/*/ -private val timePattern = Regex("\\[\\d+:\\d+:\\d+]") -private var lastLines = mutableListOf() -fun timestampPre() { - if (!ChattingConfig.showTimestampHover) return - val drawnChatLines = (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatAccessor).drawnChatLines - val chatLine = getChatLineOverMouse(UMouse.getTrueX().roundToInt(), UMouse.getTrueY().roundToInt()) - - lastLines.clear() - for (line in drawnChatLines) { - val chatComponent = line.chatComponent.createCopy() - val newline = ChatLine(line.updatedCounter, chatComponent, line.chatLineID) - lastLines.add(newline) - } - - drawnChatLines.map { - if (it != chatLine) it.chatComponent.siblings.removeAll { itt -> - timePattern.find(ChatColor.stripControlCodes(itt.unformattedText)!!) != null - } - } -} - -fun timestampPost() { - if (!ChattingConfig.showTimestampHover) return - val drawnChatLines = (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatAccessor).drawnChatLines - drawnChatLines.clear() - drawnChatLines.addAll(lastLines) -} - -private fun getChatLineOverMouse(mouseX: Int, mouseY: Int): ChatLine? { - val chat = Minecraft.getMinecraft().ingameGUI.chatGUI - if (!chat.chatOpen) return null - val scaledResolution = ScaledResolution(Minecraft.getMinecraft()) - val i = scaledResolution.scaleFactor - val f = chat.chatScale - val j = MathHelper.floor_float((mouseX / i - 3).toFloat() / f) - val k = MathHelper.floor_float((mouseY / i - 27).toFloat() / f) - if (j < 0 || k < 0) return null - val drawnChatLines = (chat as GuiNewChatAccessor).drawnChatLines - val l = chat.lineCount.coerceAtMost(drawnChatLines.size) - if (j <= MathHelper.floor_float(chat.chatWidth.toFloat() / f) && k < fontRenderer.FONT_HEIGHT * l + l) { - val m = k / Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT + chat.scrollPos - if (m >= 0 && m < drawnChatLines.size) - return drawnChatLines[m] - } - return null -} - - */ \ No newline at end of file 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 + } +} 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?) { + val compiledRegexList: MutableList = 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> = Caffeine.newBuilder().executor(POOL).maximumSize(5000).build() + + var lastSearch = "" + + @JvmStatic + fun filterMessages(text: String, list: List): List? { + 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>() { + private val comparator = Comparator> { o1, o2 -> + return@Comparator o2.first.length.compareTo(o1.first.length) + } + + override fun add(element: Pair): 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 { + 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): Double { + val tokenProbs = mutableMapOf() + 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?, + val contains: List?, + @SerializedName("ends") val endsWith: List?, + val equals: List?, + @SerializedName("regex") val uncompiledRegex: List?, + @SerializedName("ignore_starts") val ignoreStartsWith: List?, + @SerializedName("ignore_contains") val ignoreContains: List?, + @SerializedName("ignore_ends") val ignoreEndsWith: List?, + @SerializedName("ignore_equals") val ignoreEquals: List?, + @SerializedName("ignore_regex") val uncompiledIgnoreRegex: List?, + 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() + var currentTabs: ArrayList = object : ArrayList() { + 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.tabs.forEach { + applyVersion4Changes(it.asJsonObject) + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 4 -> { + // ver 5 adds lowercase + chatTabJson.tabs.forEach { + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 5 -> { + // ver 6 changes pm regex + chatTabJson.tabs.forEach { + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + } + chatTabJson.tabs.forEach { + val chatTab = GSON.fromJson(it.toString(), ChatTab::class.java) + if (chatTab.enabled) { + tabs.add(chatTab) + } + } + } catch (e: Throwable) { + e.printStackTrace() + tabFile.delete() + generateNewFile() + } + } + + private fun applyVersion2Changes(json: JsonObject) { + json.addProperty("enabled", true) + } + + private fun applyVersion3Changes(json: JsonObject) { + json.add("ignore_starts", JsonArray()) + json.add("ignore_contains", JsonArray()) + json.add("ignore_ends", JsonArray()) + json.add("ignore_equals", JsonArray()) + json.add("ignore_regex", JsonArray()) + } + + private fun applyVersion4Changes(json: JsonObject) { + json.addProperty("color", TabButton.color) + json.addProperty("hovered_color", TabButton.hoveredColor) + json.addProperty("selected_color", TabButton.selectedColor) + } + + private fun applyVersion5Changes(json: JsonObject) { + json.addProperty("lowercase", false) + } + + private fun applyVersion6Changes(json: JsonObject) { + if (json.has("starts")) { + val starts = json["starts"].asJsonArray + var detected = false + starts.iterator().let { + while (it.hasNext()) { + when (it.next().asString) { + "To " -> { + detected = true + it.remove() + } + "From " -> { + detected = true + it.remove() + } + } + } + } + if (detected) { + json.add("regex", JsonArray().apply { + add(JsonPrimitive("^(?§dTo|§dFrom) (?.+): §r(?.*)(?:§r)?\$")) + }) + json.remove("unformatted") + json.addProperty("unformatted", false) + } + } + if (json.has("ends")) { + val ends = json["ends"].asJsonArray + var detected = false + ends.iterator().let { + while (it.hasNext()) { + when (it.next().asString) { + "§r§ehas invited you to join their party!", -> { + detected = true + it.remove() + } + } + } + } + if (detected) { + json.add("contains", JsonArray().apply { + add(JsonPrimitive("§r§ehas invited you to join their party!")) + }) + } + } + } + + fun shouldRender(message: IChatComponent): Boolean { + if (currentTabs.isEmpty()) return true + for (tab in currentTabs) { + if (tab?.shouldRender(message) == true) { + return true + } + } + return false + } + + private fun generateNewFile() { + tabFile.createNewFile() + val jsonObject = JsonObject() + val defaultTabs = generateDefaultTabs() + jsonObject.add("tabs", defaultTabs) + jsonObject.addProperty("version", ChatTabsJson.VERSION) + tabFile.writeText(GSON.toJson(jsonObject)) + } + + private fun generateDefaultTabs(): JsonArray { + val all = ChatTab( + true, + "ALL", + unformatted = false, + lowercase = false, + startsWith = null, + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = null, + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "" + ) + val party = ChatTab( + true, + "PARTY", + unformatted = false, + lowercase = false, + startsWith = listOf("§r§9Party §8> ", "§r§9P §8> ", "§eThe party was transferred to §r", "§eKicked §r"), + contains = listOf("§r§ehas invited you to join their party!"), + endsWith = listOf( + "§r§eto the party! They have §r§c60 §r§eseconds to accept.§r", + "§r§ehas disbanded the party!§r", + "§r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before they are removed from the party.§r", + " §r§ejoined the party.§r", + " §r§ehas left the party.§r", + " §r§ehas been removed from the party.§r", + "§r§e because they were offline.§r" + ), + equals = listOf("§cThe party was disbanded because all invites expired and the party was empty§r"), + uncompiledRegex = listOf( //regexes from https://github.com/kwevin/Hychat-Tabs/blob/main/tabs/re-add%20prefixes%20%26%20fix%20shortened%20tags/chat.json cause i cant write regex + "(§r)*(§9Party §8\u003e)+(.*)", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§einvited §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto the party! They have §r§c60 §r§eseconds to accept\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas left the party\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ejoined the party\\.§r", + "§eYou left the party\\.§r", + "§eYou have joined §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)\u0027s §r§eparty!§r", + "§cThe party was disbanded because all invites expired and the party was empty§r", + "§cYou cannot invite that player since they\u0027re not online\\.§r", + "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e, warped you to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e\u0027s house\\.§r", + "§eSkyBlock Party Warp §r§7\\([0-9]+ players?\\)§r", + "§a. §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§f §r§awarped to your server§r", + "§eYou summoned §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§f §r§eto your server\\.§r", + "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e, warped you to their house\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§aenabled Private Game§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§cdisabled Private Game§r", + "§cThe party is now muted\\. §r", + "§aThe party is no longer muted\\.§r", + "§cThere are no offline players to remove\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas been removed from the party\\.§r", + "§eThe party was transferred to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eby §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has promoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Leader§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has promoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Moderator§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eis now a Party Moderator§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has demoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Member§r", + "§cYou can\u0027t demote yourself!§r", + "§6Party Members \\([0-9]+\\)§r", + "§eParty Leader: §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) ?§r(?:§[a-zA-Z0-9]).§r", + "§eParty Members: §r(?:(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r(?:§[a-zA-Z0-9]) . §r)+", + "§eParty Moderators: §r(?:(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r(?:§[a-zA-Z0-9]) . §r)+", + "§eThe party invite to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas expired§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§cdisabled All Invite§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§aenabled All Invite§r", + "§cYou cannot invite that player\\.§r", + "§cYou are not allowed to invite players\\.§r", + "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before the party is disbanded\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before they are removed from the party.§r", + "§cYou are not in a party right now\\.§r", + "§cThis party is currently muted\\.§r", + "(§r)*(§9P §8\u003e)+(.*)" + ), + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "/pc " + ) + val guild = ChatTab( + true, + "GUILD", + unformatted = true, + lowercase = false, + startsWith = listOf("Guild >", "G >"), + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = null, + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "/gc " + ) + val pm = ChatTab( + true, + "PM", + unformatted = false, + lowercase = false, + startsWith = null, + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = listOf("^(?§dTo|§dFrom) (?.+): §r(?.*)(?:§r)?\$"), + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "/r " + ) + tabs.add(all) + tabs.add(party) + tabs.add(guild) + tabs.add(pm) + val jsonArray = JsonArray() + jsonArray.add(PARSER.parse(GSON.toJson(all)).asJsonObject) + jsonArray.add(PARSER.parse(GSON.toJson(party)).asJsonObject) + jsonArray.add(PARSER.parse(GSON.toJson(guild)).asJsonObject) + jsonArray.add(PARSER.parse(GSON.toJson(pm)).asJsonObject) + return jsonArray + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt new file mode 100644 index 0000000..c5939c3 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt @@ -0,0 +1,15 @@ +package org.polyfrost.chatting.chat + +import com.google.gson.JsonArray +import com.google.gson.annotations.SerializedName + +data class ChatTabsJson(@SerializedName("tabs") val tabs: JsonArray, var version: Int) { + + override fun toString(): String { + return "{\"tabs\": $tabs, \"version\": $version}" + } + + companion object { + const val VERSION = 6 + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/command/ChattingCommand.kt b/src/main/kotlin/org/polyfrost/chatting/command/ChattingCommand.kt new file mode 100644 index 0000000..86aff72 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/command/ChattingCommand.kt @@ -0,0 +1,14 @@ +package org.polyfrost.chatting.command + +import cc.polyfrost.oneconfig.utils.commands.annotations.Command +import cc.polyfrost.oneconfig.utils.commands.annotations.Main +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.config.ChattingConfig + +@Command(value = Chatting.ID, description = "Access the " + Chatting.NAME + " GUI.") +class ChattingCommand { + @Main + fun main() { + ChattingConfig.openGui() + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/config/ChattingConfig.kt b/src/main/kotlin/org/polyfrost/chatting/config/ChattingConfig.kt new file mode 100644 index 0000000..0701471 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/config/ChattingConfig.kt @@ -0,0 +1,313 @@ +package org.polyfrost.chatting.config + +import cc.polyfrost.oneconfig.config.Config +import cc.polyfrost.oneconfig.config.annotations.* +import cc.polyfrost.oneconfig.config.core.OneColor +import cc.polyfrost.oneconfig.config.data.InfoType +import cc.polyfrost.oneconfig.config.data.Mod +import cc.polyfrost.oneconfig.config.data.ModType +import cc.polyfrost.oneconfig.config.migration.VigilanceMigrator +import cc.polyfrost.oneconfig.utils.hypixel.HypixelUtils +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.chat.ChatShortcuts +import org.polyfrost.chatting.chat.ChatTab +import org.polyfrost.chatting.chat.ChatTabs +import org.polyfrost.chatting.gui.components.TabButton +import org.polyfrost.chatting.hook.ChatLineHook +import org.polyfrost.chatting.utils.ModCompatHooks +import java.io.File + +object ChattingConfig : Config( + Mod( + Chatting.NAME, + ModType.UTIL_QOL, + "/chatting_dark.svg", + VigilanceMigrator(File(Chatting.oldModDir, Chatting.ID + ".toml").toPath().toString()) + ), "chatting.json" +) { + + @Dropdown( + name = "Text Render Type", category = "General", options = ["No Shadow", "Shadow", "Full Shadow"], + description = "Specifies how text should be rendered in the chat. Full Shadow displays a shadow on all sides of the text, while Shadow only displays a shadow on the right and bottom sides of the text." + ) + var textRenderType = 1 + + @Color( + name = "Chat Background Color", category = "General", + description = "The color of the chat background." + ) + var chatBackgroundColor = OneColor(0, 0, 0, 128) + + @Color( + name = "Copy Chat Message Background Color", category = "General", + description = "The color of the chat background when hovering over a message." + ) + var hoveredChatBackgroundColor = OneColor(80, 80, 80, 128) + + @Switch( + name = "Right Click to Copy Chat Message", category = "General", + description = "Enable right clicking on a chat message to copy it." + ) + var rightClickCopy = false + + @Switch( + name = "Compact Input Box", category = "General", + description = "Make the chat input box the same width as the chat box." + ) + var compactInputBox = false + + @Color( + name = "Input Box Background Color", category = "General", + description = "The color of the chat input box background." + ) + var inputBoxBackgroundColor = OneColor(0, 0, 0, 128) + + @Color( + name = "Chat Button Background Color", category = "General", + description = "The color of the chat button background." + ) + var chatButtonBackgroundColor = OneColor(0, 0, 0, 128) + + @Color( + name = "Chat Button Hovered Background Color", category = "General", + description = "The color of the chat button background when hovered." + ) + var chatButtonHoveredBackgroundColor = OneColor(255, 255, 255, 128) + + @Switch( + name = "Inform Outdated Mods", category = "General", + description = "Inform the user when a mod can be replaced by Chatting." + ) + var informForAlternatives = true + + @Switch( + name = "Smooth Chat Messages", + category = "Animations", subcategory = "Messages", + description = "Smoothly animate chat messages when they appear." + ) + var smoothChat = true + + @Slider( + name = "Message Animation Speed", + category = "Animations", subcategory = "Messages", + min = 0.0f, max = 1.0f, + description = "The speed at which chat messages animate." + ) + var messageSpeed = 0.5f + + @Switch( + name = "Smooth Chat Scrolling", + category = "Animations", subcategory = "Scrolling", + description = "Smoothly animate scrolling when scrolling through the chat." + ) + var smoothScrolling = true + + @Slider( + name = "Scrolling Animation Speed", + category = "Animations", subcategory = "Scrolling", + min = 0.0f, max = 1.0f, + description = "The speed at which scrolling animates." + ) + var scrollingSpeed = 0.15f + + @Switch( + name = "Remove Scroll Bar", + category = "Animations", subcategory = "Scrolling", + description = "Removes the vanilla scroll bar from the chat." + ) + var removeScrollBar = true + + @Switch( + name = "Show Chat Heads", description = "Show the chat heads of players in chat", category = "Chat Heads", + ) + var showChatHeads = true + + @Switch( + name = "Offset Non-Player Messages", + description = "Offset all messages, even if a player has not been detected.", + category = "Chat Heads" + ) + var offsetNonPlayerMessages = false + + @Switch( + name = "Hide Chat Head on Consecutive Messages", + description = "Hide the chat head if the previous message was from the same player.", + category = "Chat Heads" + ) + var hideChatHeadOnConsecutiveMessages = true + + /*/ + @Property( + type = PropertyType.SWITCH, + name = "Show Timestamp", + description = "Show message timestamp.", + category = "General" + ) + var showTimestamp = false + + @Property( + type = PropertyType.SWITCH, + name = "Timestamp Only On Hover", + description = "Show timestamp only on mouse hover.", + category = "General" + ) + var showTimestampHover = true + + */ + + @Info( + text = "If Chatting detects a public chat message that seems like spam, and the probability is higher than this, it will hide it.\n" + "Made for Hypixel Skyblock. Set to 100% to disable. 95% is a reasonable threshold to use it at.\n" + "Note that this is not and never will be 100% accurate; however, it's pretty much guaranteed to block most spam.", + size = 2, + category = "Player Chats", + type = InfoType.INFO + ) + var ignored = false + + @Slider( + min = 80F, max = 100F, name = "Spam Blocker Threshold", category = "Player Chats" + ) + var spamThreshold = 100 + + @Switch( + name = "Custom SkyBlock Chat Formatting (remove ranks)", category = "Player Chats" + ) + var customChatFormatting = false + + @Switch( + name = "Completely Hide Spam", category = "Player Chats" + ) + var hideSpam = false + + @Switch( + name = "Custom Chat Height", category = "Chat Window", + description = "Set a custom height for the chat window. Allows for more customization than the vanilla chat height options." + ) + var customChatHeight = false + + @Slider( + min = 180F, max = 2160F, name = "Focused Height (px)", category = "Chat Window", + description = "The height of the chat window when focused." + ) + var focusedHeight = 180 + + @Slider( + min = 180F, max = 2160F, name = "Unfocused Height (px)", category = "Chat Window", + description = "The height of the chat window when unfocused." + ) + var unfocusedHeight = 180 + + @Dropdown( + name = "Screenshot Mode", category = "Screenshotting", options = ["Save To System", "Add To Clipboard", "Both"], + description = "What to do when taking a screenshot." + ) + var copyMode = 0 + + @Checkbox( + name = "Chat Searching", category = "Searching", + description = "Enable searching through chat messages." + ) + var chatSearch = true + + @Switch( + name = "Chat Tabs", category = "Tabs", + description = "Allow filtering chat messages by a tab." + ) + var chatTabs = true + get() { + if (!field) return false + return if (hypixelOnlyChatTabs) { + HypixelUtils.INSTANCE.isHypixel + } else { + true + } + } + + @Checkbox( + name = "Enable Tabs Only on Hypixel", category = "Tabs", + description = "Only enable chat tabs on Hypixel" + ) + var hypixelOnlyChatTabs = true + + @Switch( + name = "Chat Shortcuts", category = "Shortcuts" + ) + var chatShortcuts = false + get() { + if (!field) return false + return if (hypixelOnlyChatShortcuts) { + HypixelUtils.INSTANCE.isHypixel + } else { + true + } + } + + @Checkbox( + name = "Enable Shortcuts Only on Hypixel", category = "Shortcuts" + ) + var hypixelOnlyChatShortcuts = true + + @Switch( + name = "Remove Tooltip Background", category = "Tooltips", + description = "Removes the background from tooltips." + ) + var removeTooltipBackground = false + + @Dropdown( + name = "Tooltip Text Render Type", category = "Tooltips", options = ["No Shadow", "Shadow", "Full Shadow"], + description = "The type of shadow to render on tooltips." + ) + var tooltipTextRenderType = 1 + + init { + initialize() + addDependency("offsetNonPlayerMessages", "showChatHeads") + addDependency("hideChatHeadOnConsecutiveMessages", "showChatHeads") + addDependency("hypixelOnlyChatTabs", "chatTabs") + addDependency("hypixelOnlyChatShortcuts", "chatShortcuts") + addDependency("focusedHeight", "customChatHeight") + addDependency("unfocusedHeight", "customChatHeight") + addDependency("scrollingSpeed", "smoothScrolling") + addDependency("messageSpeed", "smoothChat") + addDependency("smoothChat", "BetterChat Smooth Chat") { + return@addDependency !ModCompatHooks.betterChatSmoothMessages + } + addListener("hideChatHeadOnConsecutiveMessages") { + ChatLineHook.chatLines.map { it.get() as ChatLineHook? }.forEach { it?.updatePlayerInfo() } + } + addListener("chatTabs") { + ChatTabs.initialize() + if (!chatTabs) { + val dummy = ChatTab( + true, + "ALL", + unformatted = false, + lowercase = false, + startsWith = null, + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = null, + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "" + ) + dummy.initialize() + ChatTabs.currentTabs.clear() + ChatTabs.currentTabs.add(dummy) + } else { + ChatTabs.currentTabs.clear() + ChatTabs.currentTabs.add(ChatTabs.tabs[0]) + } + } + addListener("chatShortcuts") { + ChatShortcuts.initialize() + } + // addDependency("showTimestampHover", "showTimestamp") + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/gui/components/CleanButton.kt b/src/main/kotlin/org/polyfrost/chatting/gui/components/CleanButton.kt new file mode 100644 index 0000000..d4c4acd --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/gui/components/CleanButton.kt @@ -0,0 +1,103 @@ +package org.polyfrost.chatting.gui.components + +import cc.polyfrost.oneconfig.renderer.TextRenderer +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.config.ChattingConfig +import club.sk1er.patcher.config.PatcherConfig +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.GuiButton +import net.minecraft.client.renderer.GlStateManager +import org.polyfrost.chatting.hook.GuiNewChatHook + +/** + * Taken from ChatShortcuts under MIT License + * https://github.com/P0keDev/ChatShortcuts/blob/master/LICENSE + * @author P0keDev + */ +open class CleanButton( + buttonId: Int, + private val x: () -> Int, + private val y: () -> Int, + widthIn: Int, + heightIn: Int, + name: String, + private val renderType: () -> RenderType, + private val textColor: (packedFGColour: Int, enabled: Boolean, hovered: Boolean) -> Int = { packedFGColour: Int, enabled: Boolean, hovered: Boolean -> + var j = 14737632 + if (packedFGColour != 0) { + j = packedFGColour + } else if (!enabled) { + j = 10526880 + } else if (hovered) { + j = 16777120 + } + j + }, +) : + GuiButton(buttonId, x.invoke(), 0, widthIn, heightIn, name) { + + open fun isEnabled(): Boolean { + return false + } + + open fun onMousePress() { + + } + + override fun mousePressed(mc: Minecraft, mouseX: Int, mouseY: Int): Boolean { + val isPressed = + visible && mouseX >= xPosition && mouseY >= yPosition && mouseX < xPosition + width && mouseY < yPosition + height + if (isPressed) { + onMousePress() + } + return isPressed + } + + override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { + enabled = isEnabled() + xPosition = x() + yPosition = y() + if (visible) { + val fontrenderer = mc.fontRendererObj + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f) + hovered = + mouseX >= xPosition && mouseY >= yPosition && mouseX < xPosition + width && mouseY < yPosition + height + if (!Chatting.isPatcher || !PatcherConfig.transparentChatInputField) { + drawRect( + xPosition, + yPosition, + xPosition + width, + yPosition + height, + getBackgroundColor(hovered) + ) + } + mouseDragged(mc, mouseX, mouseY) + val j = textColor(packedFGColour, enabled, hovered) + when (renderType()) { + RenderType.NONE, RenderType.SHADOW -> { + drawCenteredString( + fontrenderer, + displayString, + xPosition + width / 2, + yPosition + (height - 8) / 2, + j + ) + } + + RenderType.FULL -> { + TextRenderer.drawBorderedText( + displayString, + ((xPosition + width / 2) - (fontrenderer.getStringWidth(displayString) / 2)).toFloat(), + (yPosition + (height - 8) / 2).toFloat(), + j, + (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatHook).textOpacity + ) + } + } + } + } + + private fun getBackgroundColor(hovered: Boolean) = + if (hovered) ChattingConfig.chatButtonHoveredBackgroundColor.rgb + else ChattingConfig.chatButtonBackgroundColor.rgb +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/gui/components/ClearButton.kt b/src/main/kotlin/org/polyfrost/chatting/gui/components/ClearButton.kt new file mode 100644 index 0000000..535cfca --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/gui/components/ClearButton.kt @@ -0,0 +1,42 @@ +package org.polyfrost.chatting.gui.components + +import cc.polyfrost.oneconfig.libs.universal.ChatColor +import cc.polyfrost.oneconfig.libs.universal.UChat +import cc.polyfrost.oneconfig.libs.universal.UResolution +import cc.polyfrost.oneconfig.utils.Multithreading +import org.polyfrost.chatting.Chatting +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.ResourceLocation + +class ClearButton : + CleanButton(13379014, { UResolution.scaledWidth - 28 }, { UResolution.scaledHeight - 27 }, 12, 12, "", + { RenderType.NONE }) { + + var times = 0 + + override fun onMousePress() { + ++times + if (times > 1) { + times = 0 + Minecraft.getMinecraft().ingameGUI.chatGUI.clearChatMessages() + } else { + UChat.chat(ChatColor.RED + ChatColor.BOLD.toString() + "Click again to clear the chat!") + Multithreading.runAsync { + Thread.sleep(3000) + times = 0 + } + } + } + + override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { + super.drawButton(mc, mouseX, mouseY) + if (visible) { + if (hovered) GlStateManager.color(1f, 1f, 160f / 255f) + else GlStateManager.color(1f, 1f, 1f) + mc.textureManager.bindTexture(ResourceLocation(Chatting.ID, "delete.png")) + Gui.drawModalRectWithCustomSizedTexture(xPosition + 1, yPosition + 1, 0f, 0f, 10, 10, 10f, 10f) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/gui/components/RenderType.kt b/src/main/kotlin/org/polyfrost/chatting/gui/components/RenderType.kt new file mode 100644 index 0000000..a150d64 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/gui/components/RenderType.kt @@ -0,0 +1,7 @@ +package org.polyfrost.chatting.gui.components + +enum class RenderType { + NONE, + SHADOW, + FULL +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/gui/components/ScreenshotButton.kt b/src/main/kotlin/org/polyfrost/chatting/gui/components/ScreenshotButton.kt new file mode 100644 index 0000000..d8da4ad --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/gui/components/ScreenshotButton.kt @@ -0,0 +1,36 @@ +package org.polyfrost.chatting.gui.components + +import cc.polyfrost.oneconfig.libs.universal.UResolution +import cc.polyfrost.oneconfig.libs.universal.UScreen +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.mixin.GuiNewChatAccessor +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.gui.GuiChat +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.ResourceLocation + +class ScreenshotButton : + CleanButton(448318, { UResolution.scaledWidth - 42 }, { UResolution.scaledHeight - 27 }, 12, 12, "", + { RenderType.NONE }) { + + override fun onMousePress() { + val chat = Minecraft.getMinecraft().ingameGUI.chatGUI + if (UScreen.currentScreen is GuiChat) { + Chatting.screenshotChat((chat as GuiNewChatAccessor).scrollPos) + } + } + + override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { + super.drawButton(mc, mouseX, mouseY) + if (visible) { + if (hovered) { + GlStateManager.color(1f, 1f, 160f / 255f) + } else { + GlStateManager.color(1f, 1f, 1f) + } + mc.textureManager.bindTexture(ResourceLocation(Chatting.ID, "screenshot.png")) + Gui.drawModalRectWithCustomSizedTexture(xPosition + 1, yPosition + 1, 0f, 0f, 10, 10, 10f, 10f) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/gui/components/SearchButton.kt b/src/main/kotlin/org/polyfrost/chatting/gui/components/SearchButton.kt new file mode 100644 index 0000000..7981945 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/gui/components/SearchButton.kt @@ -0,0 +1,70 @@ +package org.polyfrost.chatting.gui.components + +import cc.polyfrost.oneconfig.libs.universal.UResolution +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.chat.ChatSearchingManager +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.Gui +import net.minecraft.client.gui.GuiTextField +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.util.ResourceLocation + +class SearchButton : + CleanButton(3993935, { UResolution.scaledWidth - 14 }, { UResolution.scaledHeight - 27 }, 12, 12, "", + { RenderType.NONE }) { + val inputField = SearchTextField() + private var chatBox = false + + override fun isEnabled(): Boolean { + return chatBox + } + + override fun onMousePress() { + chatBox = !chatBox + inputField.setEnabled(chatBox) + inputField.isFocused = chatBox + ChatSearchingManager.lastSearch = "" + inputField.text = "" + } + + override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) { + inputField.drawTextBox() + super.drawButton(mc, mouseX, mouseY) + if (visible) { + mc.textureManager.bindTexture(ResourceLocation(Chatting.ID, "search.png")) + if (isEnabled()) { + GlStateManager.color(224f / 255f, 224f / 255f, 224f / 255f) + } else if (mouseX >= xPosition && mouseX <= xPosition + 10 && mouseY >= yPosition && mouseY <= yPosition + 10) { + GlStateManager.color(1f, 1f, 160f / 255f) + } else { + GlStateManager.color(1f, 1f, 1f) + } + Gui.drawModalRectWithCustomSizedTexture(xPosition + 1, yPosition + 1, 0f, 0f, 10, 10, 10f, 10f) + } + } + + inner class SearchTextField : GuiTextField( + 69420, + Minecraft.getMinecraft().fontRendererObj, + UResolution.scaledWidth * 4 / 5 - 60, + UResolution.scaledHeight - 26, + UResolution.scaledWidth / 5, + 12 + ) { + + init { + maxStringLength = 100 + enableBackgroundDrawing = true + isFocused = false + text = "" + setCanLoseFocus(true) + } + + override fun drawTextBox() { + if (isEnabled()) { + if (!isFocused) isFocused = true + super.drawTextBox() + } + } + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/gui/components/TabButton.kt b/src/main/kotlin/org/polyfrost/chatting/gui/components/TabButton.kt new file mode 100644 index 0000000..d0743c3 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/gui/components/TabButton.kt @@ -0,0 +1,46 @@ +package org.polyfrost.chatting.gui.components + +import cc.polyfrost.oneconfig.libs.universal.UKeyboard +import cc.polyfrost.oneconfig.libs.universal.UResolution +import org.polyfrost.chatting.chat.ChatTab +import org.polyfrost.chatting.chat.ChatTabs +import org.polyfrost.chatting.config.ChattingConfig + +class TabButton(buttonId: Int, x: Int, widthIn: Int, heightIn: Int, private val chatTab: ChatTab) : + CleanButton(buttonId, { x }, { + UResolution.scaledHeight - 26 + }, widthIn, heightIn, chatTab.name, { RenderType.values()[ChattingConfig.textRenderType] }, { packedFGColour: Int, enabled: Boolean, hovered: Boolean -> + var j = chatTab.color ?: color + if (packedFGColour != 0) { + j = packedFGColour + } else if (!enabled) { + j = chatTab.selectedColor ?: selectedColor + } else if (hovered) { + j = chatTab.hoveredColor ?: hoveredColor + } + j + }) { + + override fun onMousePress() { + if (UKeyboard.isShiftKeyDown()) { + if (ChatTabs.currentTabs.contains(chatTab)) { + ChatTabs.currentTabs.remove(chatTab) + } else { + ChatTabs.currentTabs.add(chatTab) + } + } else { + ChatTabs.currentTabs.clear() + ChatTabs.currentTabs.add(chatTab) + } + } + + override fun isEnabled(): Boolean { + return ChatTabs.currentTabs.contains(chatTab) + } + + companion object { + const val color: Int = 14737632 + const val hoveredColor: Int = 16777120 + const val selectedColor: Int = 10526880 + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/utils/EaseOutQuart.kt b/src/main/kotlin/org/polyfrost/chatting/utils/EaseOutQuart.kt new file mode 100644 index 0000000..4b6b7a5 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/utils/EaseOutQuart.kt @@ -0,0 +1,7 @@ +package org.polyfrost.chatting.utils + +import cc.polyfrost.oneconfig.gui.animations.Animation + +class EaseOutQuart(duration: Float, start: Float, end: Float, reverse: Boolean) : Animation(duration, start, end, reverse) { + override fun animate(x: Float) = -1 * (x - 1) * (x - 1) * (x - 1) * (x - 1) + 1 +} \ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt b/src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt new file mode 100644 index 0000000..ad7d329 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt @@ -0,0 +1,100 @@ +package org.polyfrost.chatting.utils + +import cc.polyfrost.oneconfig.renderer.TextRenderer +import cc.polyfrost.oneconfig.utils.dsl.getAlpha +import cc.polyfrost.oneconfig.utils.dsl.mc +import org.polyfrost.chatting.Chatting.isBetterChat +import org.polyfrost.chatting.Chatting.isPatcher +import org.polyfrost.chatting.config.ChattingConfig.offsetNonPlayerMessages +import org.polyfrost.chatting.config.ChattingConfig.showChatHeads +import org.polyfrost.chatting.config.ChattingConfig.textRenderType +import club.sk1er.patcher.config.PatcherConfig +import com.llamalad7.betterchat.BetterChat +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.ChatLine +import net.minecraft.client.gui.FontRenderer +import net.minecraft.client.gui.Gui +import net.minecraft.client.renderer.GlStateManager +import org.polyfrost.chatting.hook.ChatLineHook +import org.polyfrost.chatting.hook.GuiNewChatHook + +// This exists because mixin doesn't like dummy classes +object ModCompatHooks { + @JvmStatic + val xOffset + get() = if (isBetterChat) BetterChat.getSettings().xOffset else 0 + + @JvmStatic + val yOffset + get() = if (isBetterChat) BetterChat.getSettings().yOffset else 0 + + @JvmStatic + val chatPosition + get() = if (isPatcher && PatcherConfig.chatPosition) 12 else 0 + + @JvmStatic + val betterChatSmoothMessages + get() = if (isBetterChat) BetterChat.getSettings().smooth else false + + @JvmStatic + val extendedChatLength + get() = if (isPatcher) 32667 else 0 + + @JvmStatic + val fontRenderer: FontRenderer + get() = Minecraft.getMinecraft().fontRendererObj + + @JvmStatic + fun redirectDrawString(text: String, x: Float, y: Float, color: Int, chatLine: ChatLine, screenshot: Boolean): Int { + var actualX = x + if (showChatHeads && !screenshot) { + val hook = chatLine as ChatLineHook + if (hook.hasDetected() || offsetNonPlayerMessages) { + actualX += 10f + } + val networkPlayerInfo = hook.playerInfo + if (networkPlayerInfo != null) { + GlStateManager.enableBlend() + GlStateManager.enableAlpha() + GlStateManager.enableTexture2D() + mc.textureManager.bindTexture(networkPlayerInfo.locationSkin) + GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0) + GlStateManager.color(1.0f, 1.0f, 1.0f, color.getAlpha() / 255f) + Gui.drawScaledCustomSizeModalRect( + (x).toInt(), + (y - 1f).toInt(), + 8.0f, + 8.0f, + 8, + 8, + 8, + 8, + 64.0f, + 64.0f + ) + Gui.drawScaledCustomSizeModalRect( + (x).toInt(), + (y - 1f).toInt(), + 40.0f, + 8.0f, + 8, + 8, + 8, + 8, + 64.0f, + 64.0f + ) + GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f) + } + } + return when (textRenderType) { + 0 -> fontRenderer.drawString(text, actualX, y, color, false) + 2 -> TextRenderer.drawBorderedText(text, + actualX, + y, + color, + (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatHook).textOpacity) + else -> fontRenderer.drawString(text, actualX, y, color, true) + } + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/utils/RenderUtils.kt b/src/main/kotlin/org/polyfrost/chatting/utils/RenderUtils.kt new file mode 100644 index 0000000..6eaa78b --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/utils/RenderUtils.kt @@ -0,0 +1,259 @@ +@file:JvmName("RenderUtils") + +package org.polyfrost.chatting.utils + +import cc.polyfrost.oneconfig.utils.IOUtils +import org.polyfrost.chatting.config.ChattingConfig +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.client.renderer.texture.TextureUtil +import net.minecraft.client.shader.Framebuffer +import org.apache.commons.lang3.SystemUtils +import org.lwjgl.BufferUtils +import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL12 +import sun.awt.datatransfer.DataTransferer +import sun.awt.datatransfer.SunClipboard +import java.awt.Toolkit +import java.awt.image.BufferedImage +import java.io.File +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.nio.ByteBuffer +import java.nio.ByteOrder +import javax.imageio.ImageIO + +/** + * Taken from https://github.com/Moulberry/HyChat + */ +fun createBindFramebuffer(w: Int, h: Int): Framebuffer { + val framebuffer = Framebuffer(w, h, false) + framebuffer.framebufferColor[0] = 0x36 / 255f + framebuffer.framebufferColor[1] = 0x39 / 255f + framebuffer.framebufferColor[2] = 0x3F / 255f + framebuffer.framebufferClear() + GlStateManager.matrixMode(5889) + GlStateManager.loadIdentity() + GlStateManager.ortho(0.0, w.toDouble(), h.toDouble(), 0.0, 1000.0, 3000.0) + GlStateManager.matrixMode(5888) + GlStateManager.loadIdentity() + GlStateManager.translate(0.0f, 0.0f, -2000.0f) + framebuffer.bindFramebuffer(true) + return framebuffer +} + +/** + * Taken from https://github.com/Moulberry/HyChat + * Modified so if not on Windows just in case it will switch it to RGB and remove the transparent background. + */ +fun BufferedImage.copyToClipboard() { + if (SystemUtils.IS_OS_WINDOWS) { + try { + val width = this.width + val height = this.height + val hdrSize = 0x28 + val buffer: ByteBuffer = ByteBuffer.allocate(hdrSize + width * height * 4) + buffer.order(ByteOrder.LITTLE_ENDIAN) + //Header size + buffer.putInt(hdrSize) + //Width + buffer.putInt(width) + //Int32 biHeight; + buffer.putInt(height) + //Int16 biPlanes; + buffer.put(1.toByte()) + buffer.put(0.toByte()) + //Int16 biBitCount; + buffer.put(32.toByte()) + buffer.put(0.toByte()) + //Compression + buffer.putInt(0) + //Int32 biSizeImage; + buffer.putInt(width * height * 4) + buffer.putInt(0) + buffer.putInt(0) + buffer.putInt(0) + buffer.putInt(0) + + //Image data + for (y in 0 until height) { + for (x in 0 until width) { + val argb: Int = this.getRGB(x, height - y - 1) + if (argb shr 24 and 0xFF == 0) { + buffer.putInt(0x00000000) + } else { + buffer.putInt(argb) + } + } + } + buffer.flip() + val hdrSizev5 = 0x7C + val bufferv5: ByteBuffer = ByteBuffer.allocate(hdrSizev5 + width * height * 4) + bufferv5.order(ByteOrder.LITTLE_ENDIAN) + //Header size + bufferv5.putInt(hdrSizev5) + //Width + bufferv5.putInt(width) + //Int32 biHeight; + bufferv5.putInt(height) + //Int16 biPlanes; + bufferv5.put(1.toByte()) + bufferv5.put(0.toByte()) + //Int16 biBitCount; + bufferv5.put(32.toByte()) + bufferv5.put(0.toByte()) + //Compression + bufferv5.putInt(0) + //Int32 biSizeImage; + bufferv5.putInt(width * height * 4) + bufferv5.putInt(0) + bufferv5.putInt(0) + bufferv5.putInt(0) + bufferv5.putInt(0) + bufferv5.order(ByteOrder.BIG_ENDIAN) + bufferv5.putInt(-0x1000000) + bufferv5.putInt(0x00FF0000) + bufferv5.putInt(0x0000FF00) + bufferv5.putInt(0x000000FF) + bufferv5.order(ByteOrder.LITTLE_ENDIAN) + + //BGRs + bufferv5.put(0x42.toByte()) + bufferv5.put(0x47.toByte()) + bufferv5.put(0x52.toByte()) + bufferv5.put(0x73.toByte()) + for (i in bufferv5.position() until hdrSizev5) { + bufferv5.put(0.toByte()) + } + + //Image data + for (y in 0 until height) { + for (x in 0 until width) { + val argb: Int = this.getRGB(x, height - y - 1) + val a = argb shr 24 and 0xFF + var r = argb shr 16 and 0xFF + var g = argb shr 8 and 0xFF + var b = argb and 0xFF + r = r * a / 0xFF + g = g * a / 0xFF + b = b * a / 0xFF + bufferv5.putInt(a shl 24 or (r shl 16) or (g shl 8) or b) + } + } + bufferv5.flip() + val clip = Toolkit.getDefaultToolkit().systemClipboard + val dt = DataTransferer.getInstance() + val f: Field = dt.javaClass.getDeclaredField("CF_DIB") + f.isAccessible = true + val format: Long = f.getLong(null) + val openClipboard: Method = clip.javaClass.getDeclaredMethod("openClipboard", SunClipboard::class.java) + openClipboard.isAccessible = true + openClipboard.invoke(clip, clip) + val publishClipboardData: Method = clip.javaClass.getDeclaredMethod( + "publishClipboardData", + Long::class.javaPrimitiveType, + ByteArray::class.java + ) + publishClipboardData.isAccessible = true + val arr: ByteArray = buffer.array() + publishClipboardData.invoke(clip, format, arr) + val closeClipboard: Method = clip.javaClass.getDeclaredMethod("closeClipboard") + closeClipboard.isAccessible = true + closeClipboard.invoke(clip) + return + } catch (e: Exception) { + e.printStackTrace() + } + } + val pixels: IntArray = + this.getRGB(0, 0, this.width, this.height, null, 0, this.width) + val newImage = BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB) + newImage.setRGB(0, 0, newImage.width, newImage.height, pixels, 0, newImage.width) + + try { + IOUtils.copyImageToClipboard(this) + } catch (e: Exception) { + e.printStackTrace() + } +} + +/** + * Taken from https://github.com/Moulberry/HyChat + */ +fun Framebuffer.screenshot(file: File): BufferedImage { + val w = this.framebufferWidth + val h = this.framebufferHeight + val i = w * h + val pixelBuffer = BufferUtils.createIntBuffer(i) + val pixelValues = IntArray(i) + GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1) + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1) + GlStateManager.bindTexture(this.framebufferTexture) + GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer) + pixelBuffer[pixelValues] //Load buffer into array + TextureUtil.processPixelValues(pixelValues, w, h) //Flip vertically + val bufferedimage = BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB) + val j = this.framebufferTextureHeight - this.framebufferHeight + for (k in j until this.framebufferTextureHeight) { + for (l in 0 until this.framebufferWidth) { + bufferedimage.setRGB(l, k - j, pixelValues[k * this.framebufferTextureWidth + l]) + } + } + if (ChattingConfig.copyMode != 1) { + try { + file.parentFile.mkdirs() + ImageIO.write(bufferedimage, "png", file) + } catch (e: Exception) { + e.printStackTrace() + } + } + return bufferedimage +} +/*/ +private val timePattern = Regex("\\[\\d+:\\d+:\\d+]") +private var lastLines = mutableListOf() +fun timestampPre() { + if (!ChattingConfig.showTimestampHover) return + val drawnChatLines = (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatAccessor).drawnChatLines + val chatLine = getChatLineOverMouse(UMouse.getTrueX().roundToInt(), UMouse.getTrueY().roundToInt()) + + lastLines.clear() + for (line in drawnChatLines) { + val chatComponent = line.chatComponent.createCopy() + val newline = ChatLine(line.updatedCounter, chatComponent, line.chatLineID) + lastLines.add(newline) + } + + drawnChatLines.map { + if (it != chatLine) it.chatComponent.siblings.removeAll { itt -> + timePattern.find(ChatColor.stripControlCodes(itt.unformattedText)!!) != null + } + } +} + +fun timestampPost() { + if (!ChattingConfig.showTimestampHover) return + val drawnChatLines = (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatAccessor).drawnChatLines + drawnChatLines.clear() + drawnChatLines.addAll(lastLines) +} + +private fun getChatLineOverMouse(mouseX: Int, mouseY: Int): ChatLine? { + val chat = Minecraft.getMinecraft().ingameGUI.chatGUI + if (!chat.chatOpen) return null + val scaledResolution = ScaledResolution(Minecraft.getMinecraft()) + val i = scaledResolution.scaleFactor + val f = chat.chatScale + val j = MathHelper.floor_float((mouseX / i - 3).toFloat() / f) + val k = MathHelper.floor_float((mouseY / i - 27).toFloat() / f) + if (j < 0 || k < 0) return null + val drawnChatLines = (chat as GuiNewChatAccessor).drawnChatLines + val l = chat.lineCount.coerceAtMost(drawnChatLines.size) + if (j <= MathHelper.floor_float(chat.chatWidth.toFloat() / f) && k < fontRenderer.FONT_HEIGHT * l + l) { + val m = k / Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT + chat.scrollPos + if (m >= 0 && m < drawnChatLines.size) + return drawnChatLines[m] + } + return null +} + + */ \ No newline at end of file diff --git a/src/main/resources/mixins.chatting.json b/src/main/resources/mixins.chatting.json index 0473261..ce96f59 100644 --- a/src/main/resources/mixins.chatting.json +++ b/src/main/resources/mixins.chatting.json @@ -1,23 +1,23 @@ { "compatibilityLevel": "JAVA_8", "minVersion": "0.7", - "package": "cc.woverflow.chatting.mixin", + "package": "org.polyfrost.chatting.mixin", "refmap": "mixins.${id}.refmap.json", "verbose": true, "client": [ - "ChatLineMixin", - "ClientCommandHandlerMixin", - "EntityPlayerSPMixin", - "GuiChatMixin", - "GuiNewChatAccessor", - "GuiNewChatMapMixin", - "GuiNewChatMixin", - "GuiNewChatMixin_ChatHeight", - "GuiNewChatMixin_ChatSearching", - "GuiNewChatMixin_ChatTabs", - "GuiNewChatMixin_Scrolling", - "GuiNewChatMixin_SmoothMessages", - "GuiNewChatMixin_TextRendering", - "GuiUtilsMixin" + "ChatLineMixin", + "ClientCommandHandlerMixin", + "EntityPlayerSPMixin", + "GuiChatMixin", + "GuiNewChatAccessor", + "GuiNewChatMapMixin", + "GuiNewChatMixin", + "GuiNewChatMixin_ChatHeight", + "GuiNewChatMixin_ChatSearching", + "GuiNewChatMixin_ChatTabs", + "GuiNewChatMixin_Scrolling", + "GuiNewChatMixin_SmoothMessages", + "GuiNewChatMixin_TextRendering", + "GuiUtilsMixin" ] } \ No newline at end of file -- cgit