diff options
9 files changed, 219 insertions, 81 deletions
diff --git a/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java b/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java index 50b9ce3..22f180c 100644 --- a/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java +++ b/src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java @@ -8,8 +8,14 @@ import java.util.HashSet; public interface ChatLineHook { HashSet<WeakReference<ChatLine>> chatLines = new HashSet<>(); - boolean hasDetected(); + boolean isDetected(); + void setDetected(boolean detected); NetworkPlayerInfo getPlayerInfo(); + void setPlayerInfo(NetworkPlayerInfo playerInfo); + NetworkPlayerInfo getDetectedPlayerInfo(); + void setDetectedPlayerInfo(NetworkPlayerInfo detectedPlayerInfo); + boolean isFirstDetection(); + void setFirstDetection(boolean firstDetection); void updatePlayerInfo(); diff --git a/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java b/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java index 2e5f21c..cde09ea 100644 --- a/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java +++ b/src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java @@ -7,28 +7,23 @@ 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.polyfrost.chatting.utils.ChatHeadHooks; 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 boolean firstDetection = true; private NetworkPlayerInfo playerInfo; private NetworkPlayerInfo detectedPlayerInfo; - private static NetworkPlayerInfo lastPlayerInfo; private static long lastUniqueId = 0; private long uniqueId = 0; @@ -37,60 +32,17 @@ public class ChatLineMixin implements ChatLineHook { lastUniqueId++; uniqueId = lastUniqueId; chatLines.add(new WeakReference<>((ChatLine) (Object) this)); - NetHandlerPlayClient netHandler = Minecraft.getMinecraft().getNetHandler(); - if (netHandler == null) return; - Map<String, NetworkPlayerInfo> 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) { - } + ChatHeadHooks.INSTANCE.detect(iChatComponent.getFormattedText(), (ChatLine) (Object) this); } - @Nullable - private static NetworkPlayerInfo getPlayerFromNickname(String word, NetHandlerPlayClient connection, Map<String, NetworkPlayerInfo> 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 isDetected() { + return detected; } @Override - public boolean hasDetected() { - return detected; + public void setDetected(boolean detected) { + this.detected = detected; } @Override @@ -99,8 +51,33 @@ public class ChatLineMixin implements ChatLineHook { } @Override + public void setPlayerInfo(NetworkPlayerInfo playerInfo) { + this.playerInfo = playerInfo; + } + + @Override + public NetworkPlayerInfo getDetectedPlayerInfo() { + return detectedPlayerInfo; + } + + @Override + public void setDetectedPlayerInfo(NetworkPlayerInfo detectedPlayerInfo) { + this.detectedPlayerInfo = detectedPlayerInfo; + } + + @Override + public boolean isFirstDetection() { + return firstDetection; + } + + @Override + public void setFirstDetection(boolean firstDetection) { + this.firstDetection = firstDetection; + } + + @Override public void updatePlayerInfo() { - if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages() && !first) { + if (ChattingConfig.INSTANCE.getHideChatHeadOnConsecutiveMessages() && !firstDetection) { playerInfo = null; } else { playerInfo = detectedPlayerInfo; diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java index 167bbd4..d434958 100644 --- a/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java @@ -1,5 +1,6 @@ package org.polyfrost.chatting.mixin; +import cc.polyfrost.oneconfig.libs.universal.UResolution; import org.polyfrost.chatting.chat.*; import org.polyfrost.chatting.config.ChattingConfig; import org.polyfrost.chatting.gui.components.ClearButton; @@ -102,6 +103,15 @@ public abstract class GuiChatMixin extends GuiScreen { return ChattingConfig.INSTANCE.getInputBoxBackgroundColor().getRGB(); } + @ModifyArg(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;getChatComponent(II)Lnet/minecraft/util/IChatComponent;"), index = 0) + private int modifyChatComponentX(int x) { + if (ChattingConfig.INSTANCE.getShowChatHeads()) { + return x - (10 * (int) UResolution.getScaleFactor()); + } else { + return x; + } + } + @Inject(method = "mouseClicked", at = @At("HEAD")) private void mouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) { GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI()); @@ -126,6 +136,15 @@ public abstract class GuiChatMixin extends GuiScreen { } + @ModifyArg(method = "mouseClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiNewChat;getChatComponent(II)Lnet/minecraft/util/IChatComponent;"), index = 0) + private int modifyChatComponentX2(int x) { + if (ChattingConfig.INSTANCE.getShowChatHeads()) { + return x - (10 * (int) UResolution.getScaleFactor()); + } else { + return x; + } + } + @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()) { diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java index bc90730..4733ab6 100644 --- a/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java @@ -1,10 +1,13 @@ package org.polyfrost.chatting.mixin; import cc.polyfrost.oneconfig.libs.universal.UMouse; +import cc.polyfrost.oneconfig.libs.universal.UResolution; import cc.polyfrost.oneconfig.utils.Notifications; +import net.minecraft.util.IChatComponent; import org.polyfrost.chatting.Chatting; import org.polyfrost.chatting.chat.ChatSearchingManager; import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.hook.ChatLineHook; import org.polyfrost.chatting.hook.GuiNewChatHook; import org.polyfrost.chatting.utils.ModCompatHooks; import org.polyfrost.chatting.utils.RenderUtils; @@ -20,6 +23,7 @@ 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.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.invoke.arg.Args; import java.awt.datatransfer.StringSelection; @@ -161,6 +165,44 @@ public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook { } } + @Unique + private boolean chatting$cancelChatComponent = false; + @Unique + private int chatting$lastMouseX = 0; + @Unique + private int chatting$lastMouseY = 0; + + @Inject(method = "getChatComponent", at = @At(value = "INVOKE", target = "Ljava/util/List;get(I)Ljava/lang/Object;")) + private void storeMouseXAndY(int mouseX, int mouseY, CallbackInfoReturnable<IChatComponent> cir) { + chatting$lastMouseX = mouseX; + chatting$lastMouseY = mouseY; + } + + @ModifyVariable(method = "getChatComponent", at = @At("STORE"), ordinal = 0) + private ChatLine storeChatLine(ChatLine line) { + if (ChattingConfig.INSTANCE.getShowChatHeads() && !((ChatLineHook) line).isDetected() && !ChattingConfig.INSTANCE.getOffsetNonPlayerMessages()) { + int i = (int) UResolution.getScaleFactor(); + float f = this.getChatScale(); + int j = chatting$lastMouseX / i - 3; + int k = chatting$lastMouseY / i - 27; + j = MathHelper.floor_float((float)j / f); + k = MathHelper.floor_float((float)k / f); + int l = Math.min(this.getLineCount(), this.drawnChatLines.size()); + if (j > MathHelper.floor_float((float)this.getChatWidth() / this.getChatScale()) && k < this.mc.fontRendererObj.FONT_HEIGHT * l + l) { + chatting$cancelChatComponent = true; + } + } + return line; + } + + @Inject(method = "getChatComponent", at = @At(value = "INVOKE", target = "Ljava/util/List;get(I)Ljava/lang/Object;", shift = At.Shift.AFTER), cancellable = true) + private void cancelChatComponent(int mouseX, int mouseY, CallbackInfoReturnable<IChatComponent> cir) { + if (chatting$cancelChatComponent) { + cir.setReturnValue(null); + chatting$cancelChatComponent = false; + } + } + @Override public int getRight() { return chatting$right; diff --git a/src/main/java/org/polyfrost/chatting/mixin/GuiUtilRenderComponentsMixin.java b/src/main/java/org/polyfrost/chatting/mixin/GuiUtilRenderComponentsMixin.java new file mode 100644 index 0000000..fd497a6 --- /dev/null +++ b/src/main/java/org/polyfrost/chatting/mixin/GuiUtilRenderComponentsMixin.java @@ -0,0 +1,19 @@ +package org.polyfrost.chatting.mixin; + +import net.minecraft.client.gui.GuiUtilRenderComponents; +import org.polyfrost.chatting.config.ChattingConfig; +import org.polyfrost.chatting.utils.ChatHeadHooks; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(GuiUtilRenderComponents.class) +public class GuiUtilRenderComponentsMixin { + @Redirect(method = "splitText", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/FontRenderer;getStringWidth(Ljava/lang/String;)I")) + private static int modifyChatLineX(net.minecraft.client.gui.FontRenderer fontRenderer, String text) { + if (ChattingConfig.INSTANCE.getShowChatHeads() && (ChattingConfig.INSTANCE.getOffsetNonPlayerMessages() || ChatHeadHooks.INSTANCE.detect(text, null))) { + return fontRenderer.getStringWidth(text) + 10; + } + return fontRenderer.getStringWidth(text); + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/Chatting.kt b/src/main/kotlin/org/polyfrost/chatting/Chatting.kt index 0e8745c..cf439ce 100644 --- a/src/main/kotlin/org/polyfrost/chatting/Chatting.kt +++ b/src/main/kotlin/org/polyfrost/chatting/Chatting.kt @@ -245,7 +245,7 @@ object Chatting { } 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 width = messages.maxOf { fr.getStringWidth(it.value) + (if (ChattingConfig.showChatHeads && ((it.key as ChatLineHook).isDetected || 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())) diff --git a/src/main/kotlin/org/polyfrost/chatting/utils/ChatHeadHooks.kt b/src/main/kotlin/org/polyfrost/chatting/utils/ChatHeadHooks.kt new file mode 100644 index 0000000..aa777c1 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/utils/ChatHeadHooks.kt @@ -0,0 +1,74 @@ +package org.polyfrost.chatting.utils + +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.ChatLine +import net.minecraft.client.network.NetHandlerPlayClient +import net.minecraft.client.network.NetworkPlayerInfo +import org.polyfrost.chatting.config.ChattingConfig.hideChatHeadOnConsecutiveMessages +import org.polyfrost.chatting.hook.ChatLineHook + +object ChatHeadHooks { + private var lastPlayerInfo: NetworkPlayerInfo? = null + fun detect(formattedText: String, chatLine: ChatLine?): Boolean { + if (chatLine !is ChatLineHook) { + return false + } + val netHandler = Minecraft.getMinecraft().netHandler ?: return false + val nicknameCache: MutableMap<String, NetworkPlayerInfo> = HashMap() + var detected = false + try { + formattedText.split("(§.)|\\W".toRegex()).dropLastWhile { it.isEmpty() } + .forEach { word -> + if (word.isNotEmpty()) { + var maybePlayerInfo = netHandler.getPlayerInfo(word) + if (maybePlayerInfo == null) { + maybePlayerInfo = getPlayerFromNickname(word, netHandler, nicknameCache) + } + if (maybePlayerInfo != null) { + detected = true + chatLine.run { + playerInfo = maybePlayerInfo + detectedPlayerInfo = playerInfo + isDetected = true + if (playerInfo == lastPlayerInfo) { + isFirstDetection = false + if (hideChatHeadOnConsecutiveMessages) { + playerInfo = null + } + } else { + lastPlayerInfo = playerInfo + } + return@forEach + } + } + } + } + } catch (ignored: Exception) { + } + return detected + } + + private fun getPlayerFromNickname( + word: String, + connection: NetHandlerPlayClient, + nicknameCache: MutableMap<String, NetworkPlayerInfo> + ): NetworkPlayerInfo? { + if (nicknameCache.isEmpty()) { + for (p in connection.playerInfoMap) { + val displayName = p.displayName + if (displayName != null) { + val nickname = displayName.unformattedTextForChat + if (word == nickname) { + nicknameCache.clear() + return p + } + nicknameCache[nickname] = p + } + } + } else { + // use prepared cache + return nicknameCache[word] + } + return null + } +}
\ 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 index b6198fa..591461d 100644 --- a/src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt +++ b/src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt @@ -58,7 +58,7 @@ object ModCompatHooks { var actualX = x if (showChatHeads && !screenshot) { val hook = chatLine as ChatLineHook - if (hook.hasDetected() || offsetNonPlayerMessages) { + if (hook.isDetected || offsetNonPlayerMessages) { actualX += 10f } val networkPlayerInfo = hook.playerInfo diff --git a/src/main/resources/mixins.chatting.json b/src/main/resources/mixins.chatting.json index ce96f59..429b9f5 100644 --- a/src/main/resources/mixins.chatting.json +++ b/src/main/resources/mixins.chatting.json @@ -1,23 +1,24 @@ { - "compatibilityLevel": "JAVA_8", - "minVersion": "0.7", - "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" - ] + "compatibilityLevel": "JAVA_8", + "minVersion": "0.7", + "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", + "GuiUtilRenderComponentsMixin", + "GuiUtilsMixin" + ] }
\ No newline at end of file |