Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hover only timestamp #27

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/org/polyfrost/chatting/hook/ChatLineHook.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@

import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List;

public interface ChatLineHook {
HashSet<WeakReference<ChatLine>> chatLines = new HashSet<>();
boolean isDetected();
void setDetected(boolean detected);
List<ChatLine> getChildren();
long getTimestamp();
void setTimestamp(long timestamp);
NetworkPlayerInfo getPlayerInfo();
void setPlayerInfo(NetworkPlayerInfo playerInfo);
NetworkPlayerInfo getDetectedPlayerInfo();
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/org/polyfrost/chatting/mixin/ChatLineMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

@Mixin(ChatLine.class)
public class ChatLineMixin implements ChatLineHook {
Expand All @@ -26,6 +28,23 @@ public class ChatLineMixin implements ChatLineHook {
private NetworkPlayerInfo detectedPlayerInfo;
private static long lastUniqueId = 0;
private long uniqueId = 0;
private long timestamp;
private List<ChatLine> children = new ArrayList<>();

@Override
public List<ChatLine> getChildren() {
return children;
}

@Override
public long getTimestamp() {
return timestamp;
}

@Override
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}

@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(int i, IChatComponent iChatComponent, int j, CallbackInfo ci) {
Expand Down
23 changes: 14 additions & 9 deletions src/main/java/org/polyfrost/chatting/mixin/GuiChatMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@

import cc.polyfrost.oneconfig.libs.universal.UDesktop;
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;
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;
Expand All @@ -19,8 +12,13 @@
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.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 org.polyfrost.chatting.utils.ModCompatHooks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
Expand Down Expand Up @@ -72,6 +70,11 @@ private void init(CallbackInfo ci) {
}
}

@Inject(method = "onGuiClosed", at = @At("HEAD"))
private void onGuiClosedHook(CallbackInfo ci) {
ModCompatHooks.setHoveredText(null);
}

@Inject(method = "updateScreen", at = @At("HEAD"))
private void updateScreen(CallbackInfo ci) {
if (ChattingConfig.INSTANCE.getChatSearch() && searchButton.isEnabled()) {
Expand All @@ -95,6 +98,8 @@ private void keyTyped(char typedChar, int keyCode, CallbackInfo ci) {
@Inject(method = "drawScreen", at = @At("HEAD"))
private void onDrawScreen(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) {
GuiNewChatHook hook = ((GuiNewChatHook) Minecraft.getMinecraft().ingameGUI.getChatGUI());
ChatLine hoveredLine = hook.getHoveredLine(Mouse.getY());
ModCompatHooks.setHoveredText(hoveredLine);
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.minecraft.client.gui.ChatLine;
import net.minecraft.client.gui.GuiNewChat;
import net.minecraft.util.IChatComponent;
import org.polyfrost.chatting.hook.ChatLineHook;
import org.polyfrost.chatting.hook.GuiNewChatHook;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
Expand All @@ -12,6 +13,7 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import tv.twitch.chat.Chat;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -50,6 +52,9 @@ private void handleDrawnLineRemoved(IChatComponent chatComponent, int chatLineId
private void handleLineAdded(IChatComponent chatComponent, int chatLineId, int updateCounter, boolean displayOnly, CallbackInfo ci) {
if (!displayOnly) {
ChatLine masterLine = chatLines.get(0);
ChatLineHook masterHook = (ChatLineHook) masterLine;
masterHook.setTimestamp(System.currentTimeMillis());
masterHook.getChildren().addAll(tempDrawnLines);
for (ChatLine tempDrawnLine : tempDrawnLines) drawnToFull.put(tempDrawnLine, masterLine);
}else {
lastTempLine = null;
Expand Down
39 changes: 18 additions & 21 deletions src/main/java/org/polyfrost/chatting/mixin/GuiNewChatMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@
import cc.polyfrost.oneconfig.libs.universal.UMouse;
import cc.polyfrost.oneconfig.libs.universal.UResolution;
import cc.polyfrost.oneconfig.utils.Notifications;
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.IChatComponent;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
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;
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.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;

import java.awt.datatransfer.StringSelection;
Expand Down Expand Up @@ -66,19 +67,6 @@ public abstract class GuiNewChatMixin extends Gui implements GuiNewChatHook {
@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;<init>(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()) {
Expand Down Expand Up @@ -172,10 +160,14 @@ private void checkStuff(int j2, CallbackInfo ci) {
@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) {
@Unique
private ChatLine chatting$currentComponent;

@Inject(method = "getChatComponent", at = @At(value = "INVOKE", target = "Ljava/util/List;get(I)Ljava/lang/Object;"), locals = LocalCapture.CAPTURE_FAILHARD)
private void storeMouseXAndY(int mouseX, int mouseY, CallbackInfoReturnable<IChatComponent> cir, ScaledResolution scaledResolution, int i, float f, int j, int k, int l, int m) {
chatting$lastMouseX = mouseX;
chatting$lastMouseY = mouseY;
chatting$currentComponent = drawnChatLines.get(m);
}

@ModifyVariable(method = "getChatComponent", at = @At("STORE"), ordinal = 0)
Expand Down Expand Up @@ -203,6 +195,11 @@ private void cancelChatComponent(int mouseX, int mouseY, CallbackInfoReturnable<
}
}

@ModifyVariable(method = "getChatComponent", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/ChatLine;getChatComponent()Lnet/minecraft/util/IChatComponent;"), index = 11)
private int fixOffsetForGetChatComponent(int value) {
return ModCompatHooks.getStartOffset(value, chatting$currentComponent);
}

@Override
public int getRight() {
return chatting$right;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private static int modifyChatLineX(net.minecraft.client.gui.FontRenderer fontRen
if (ChattingConfig.INSTANCE.getShowChatHeads() && (ChattingConfig.INSTANCE.getOffsetNonPlayerMessages() || ChatHeadHooks.INSTANCE.detect(text, null))) {
return fontRenderer.getStringWidth(text) + 10;
}
// TODO: time thingy?
return fontRenderer.getStringWidth(text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,20 @@ object ChattingConfig : Config(
)
var hideChatHeadOnConsecutiveMessages = true

/*/
@Property(
type = PropertyType.SWITCH,
@Switch(
name = "Show Timestamp",
description = "Show message timestamp.",
category = "General"
)
var showTimestamp = false

@Property(
type = PropertyType.SWITCH,
@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,
Expand Down
84 changes: 76 additions & 8 deletions src/main/kotlin/org/polyfrost/chatting/utils/ModCompatHooks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ 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.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.showTimestamp
import org.polyfrost.chatting.config.ChattingConfig.textRenderType
import org.polyfrost.chatting.hook.ChatLineHook
import org.polyfrost.chatting.hook.GuiNewChatHook
import org.polyfrost.chatting.mixin.GuiNewChatAccessor
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.pow

// This exists because mixin doesn't like dummy classes
object ModCompatHooks {
Expand Down Expand Up @@ -53,11 +57,44 @@ object ModCompatHooks {
val drawnChatLines: List<ChatLine>
get() = (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatAccessor).drawnChatLines

@JvmStatic
fun getFullMessage(chatLine: ChatLine): ChatLine? {
return (Minecraft.getMinecraft().ingameGUI.chatGUI as GuiNewChatHook).getFullMessage(chatLine)
}

private var hoveredText: ChatLine? = null
private var hoverProgress = 0L
private val formatter = SimpleDateFormat("HH:mm")

fun getTimeStampText(comp: ChatLine): String {
comp as ChatLineHook
return "[${formatter.format(Date(comp.timestamp))}] "
}

@JvmStatic
fun setHoveredText(comp: ChatLine?) {
val newComp = comp?.let(::getFullMessage)
if (hoveredText != newComp) {
hoveredText = newComp
hoverProgress = System.currentTimeMillis()
}
}

@JvmStatic
fun redirectDrawString(text: String, x: Float, y: Float, color: Int, chatLine: ChatLine, screenshot: Boolean): Int {
var actualX = x
val hook = chatLine as ChatLineHook
if (showTimestamp && !screenshot) {
val timeOffsetInfo = getTimeOffset(chatLine)
if (timeOffsetInfo != null) {
if (timeOffsetInfo.shouldRender) {
fontRenderer.drawString(timeOffsetInfo.text, actualX, y, -1, true)
}
actualX += timeOffsetInfo.offset
}
}
if (showChatHeads && !screenshot) {
val hook = chatLine as ChatLineHook
val renderX = actualX
if (hook.isDetected || offsetNonPlayerMessages) {
actualX += 10f
}
Expand All @@ -70,7 +107,7 @@ object ModCompatHooks {
GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0)
GlStateManager.color(1.0f, 1.0f, 1.0f, color.getAlpha() / 255f)
Gui.drawScaledCustomSizeModalRect(
(x).toInt(),
(renderX).toInt(),
(y - 1f).toInt(),
8.0f,
8.0f,
Expand All @@ -82,7 +119,7 @@ object ModCompatHooks {
64.0f
)
Gui.drawScaledCustomSizeModalRect(
(x).toInt(),
(renderX).toInt(),
(y - 1f).toInt(),
40.0f,
8.0f,
Expand All @@ -106,4 +143,35 @@ object ModCompatHooks {
else -> fontRenderer.drawString(text, actualX, y, color, true)
}
}


data class TimeOffsetInfo(
val text: String,
val shouldRender: Boolean,
val offset: Int,
)

private fun ease(x: Double): Double {
return if (x < 0.5) 4 * x * x * x else 1 - (-2.0 * x + 2).pow(3.0) / 2
}

private fun getTimeOffset(comp: ChatLine): TimeOffsetInfo? {
if (!showTimestamp) return null
val root = getFullMessage(comp)
if (root == null || root != hoveredText) return null
if ((root as ChatLineHook).children.firstOrNull() != comp) return null
val text = getTimeStampText(root)
val strWidth = fontRenderer.getStringWidth(text)
val animationTime = ((System.currentTimeMillis() - hoverProgress) / 100.0).coerceIn(0.0, 1.0)
val easedAnimationPercentage = ease(animationTime)
val progress = (easedAnimationPercentage * strWidth).toInt()
return TimeOffsetInfo(text, progress == strWidth, progress)
}

@JvmStatic
fun getStartOffset(value: Int, comp: ChatLine): Int {
var actualX = value
actualX += getTimeOffset(comp)?.offset ?: 0
return actualX
}
}