From 7ea5b9ab24c78f01bdbbd4357fc87b75bbe4d950 Mon Sep 17 00:00:00 2001 From: Wyvest <45589059+Wyvest@users.noreply.github.com> Date: Sun, 11 Dec 2022 17:06:21 -0500 Subject: new: chat heads --- build.gradle | 1 + gradle.properties | 2 +- .../cc/woverflow/chatting/hook/ChatLineHook.java | 15 +++ .../cc/woverflow/chatting/mixin/ChatLineMixin.java | 101 +++++++++++++++++++++ .../woverflow/chatting/mixin/GuiNewChatMixin.java | 29 ++++++ .../cc/woverflow/chatting/config/ChattingConfig.kt | 27 ++++++ src/main/resources/mixins.chatting.json | 31 ++++--- 7 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java create mode 100644 src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java diff --git a/build.gradle b/build.gradle index 6720b2e..780ccd4 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ loom { } runConfigs { client { + vmArgs.remove("-XstartOnFirstThread") ideConfigGenerated = true } } diff --git a/gradle.properties b/gradle.properties index 2177dae..76dac55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx2G mod_name = Chatting mod_id = chatting -mod_version = 1.4.2-alpha3 +mod_version = 1.4.2-beta2 loom.platform = forge minecraft.version = "1.8.9-forge" diff --git a/src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java b/src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java new file mode 100644 index 0000000..fb10225 --- /dev/null +++ b/src/main/java/cc/woverflow/chatting/hook/ChatLineHook.java @@ -0,0 +1,15 @@ +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(); +} diff --git a/src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java b/src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java new file mode 100644 index 0000000..b739284 --- /dev/null +++ b/src/main/java/cc/woverflow/chatting/mixin/ChatLineMixin.java @@ -0,0 +1,101 @@ +/* + * 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; + + @Inject(method = "", at = @At("RETURN")) + private void onInit(int i, IChatComponent iChatComponent, int j, CallbackInfo ci) { + chatLines.add(new WeakReference<>((ChatLine) (Object) this)); + NetHandlerPlayClient netHandler = Minecraft.getMinecraft().getNetHandler(); + Map nicknameCache = new HashMap<>(); + 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; + } + } + } + + @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; + } + } +} diff --git a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java index 13c91ef..a430f8e 100644 --- a/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java +++ b/src/main/java/cc/woverflow/chatting/mixin/GuiNewChatMixin.java @@ -1,17 +1,20 @@ package cc.woverflow.chatting.mixin; import cc.polyfrost.oneconfig.config.core.OneColor; +import cc.polyfrost.oneconfig.utils.color.ColorUtils; 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.gui.components.CleanButton; +import cc.woverflow.chatting.hook.ChatLineHook; import cc.woverflow.chatting.hook.GuiNewChatHook; import cc.woverflow.chatting.utils.ModCompatHooks; import cc.woverflow.chatting.utils.RenderUtils; import cc.polyfrost.oneconfig.libs.universal.UMouse; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.*; +import net.minecraft.client.network.NetworkPlayerInfo; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; @@ -25,6 +28,7 @@ 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.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.invoke.arg.Args; import java.awt.datatransfer.StringSelection; @@ -141,6 +145,14 @@ public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook { } } + @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; + } + @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) { @@ -155,6 +167,23 @@ public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook { } } lineInBounds = false; + if (ChattingConfig.INSTANCE.getShowChatHeads()) { + ChatLineHook hook = ((ChatLineHook) chatting$drawingLine); + if (hook.hasDetected() || ChattingConfig.INSTANCE.getOffsetNonPlayerMessages()) { + args.set(1, ((float) args.get(1)) + 10f); + } + NetworkPlayerInfo networkPlayerInfo = hook.getPlayerInfo(); + if (networkPlayerInfo != null) { + GlStateManager.enableBlend(); + GlStateManager.enableAlpha(); + mc.getTextureManager().bindTexture(networkPlayerInfo.getLocationSkin()); + GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0); + GlStateManager.color(1.0F, 1.0F, 1.0F, ColorUtils.getAlpha(args.get(3)) / 255f); + Gui.drawScaledCustomSizeModalRect((int) ((float) args.get(1) - 10f), (int) ((float) args.get(2) - 1f), 8.0F, 8.0F, 8, 8, 8, 8, 64.0F, 64.0F); + Gui.drawScaledCustomSizeModalRect((int) ((float) args.get(1) - 10f), (int) ((float) args.get(2) - 1f), 40.0F, 8.0F, 8, 8, 8, 8, 64.0F, 64.0F); + GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + } + } } private boolean isInBounds(int left, int top, int right, int bottom, float chatScale) { diff --git a/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt b/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt index 40ebd9a..a7f6ffe 100644 --- a/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt +++ b/src/main/kotlin/cc/woverflow/chatting/config/ChattingConfig.kt @@ -13,6 +13,7 @@ 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 java.io.File object ChattingConfig : @@ -66,6 +67,27 @@ object ChattingConfig : ) var informForAlternatives = 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, @@ -191,6 +213,11 @@ object ChattingConfig : init { initialize() + addDependency("offsetNonPlayerMessages", "showChatHeads") + addDependency("hideChatHeadOnConsecutiveMessages", "showChatHeads") + addListener("hideChatHeadOnConsecutiveMessages") { + ChatLineHook.chatLines.map { it.get() as ChatLineHook? }.forEach { it?.updatePlayerInfo() } + } addListener("chatTabs") { ChatTabs.initialize() if (!chatTabs) { diff --git a/src/main/resources/mixins.chatting.json b/src/main/resources/mixins.chatting.json index 5a7c5bf..78b43c5 100644 --- a/src/main/resources/mixins.chatting.json +++ b/src/main/resources/mixins.chatting.json @@ -1,16 +1,19 @@ { - "compatibilityLevel": "JAVA_8", - "minVersion": "0.7", - "package": "cc.woverflow.chatting.mixin", - "refmap": "mixins.${id}.refmap.json", - "mixins": [ - "ClientCommandHandlerMixin", - "EntityPlayerSPMixin", - "GuiChatMixin", - "GuiNewChatAccessor", - "GuiNewChatMapMixin", - "GuiNewChatMixin", - "GuiUtilsMixin" - ], - "verbose": true + "compatibilityLevel": "JAVA_8", + "minVersion": "0.7", + "package": "cc.woverflow.chatting.mixin", + "refmap": "mixins.${id}.refmap.json", + "mixins": [ + "ClientCommandHandlerMixin", + "EntityPlayerSPMixin", + "GuiChatMixin", + "GuiNewChatAccessor", + "GuiNewChatMapMixin", + "GuiNewChatMixin", + "GuiUtilsMixin" + ], + "verbose": true, + "client": [ + "ChatLineMixin" + ] } \ No newline at end of file -- cgit