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

feat(Nextgen/BetterChat): Copy chat line #4958

Open
wants to merge 3 commits into
base: nextgen
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,31 @@

import com.llamalad7.mixinextras.sugar.Local;
import net.ccbluex.liquidbounce.features.module.modules.misc.betterchat.ModuleBetterChat;
import net.ccbluex.liquidbounce.interfaces.ChatHudAddition;
import net.ccbluex.liquidbounce.interfaces.ChatMessageAddition;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.text.OrderedText;
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.ModifyArgs;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;

import java.util.List;

@Mixin(ChatHud.class)
public abstract class MixinChatHud {
public abstract class MixinChatHud implements ChatHudAddition {

@Shadow
@Final
private List<ChatHudLine.Visible> visibleMessages;
public List<ChatHudLine.Visible> visibleMessages;

@Shadow
public abstract boolean isChatFocused();
Expand All @@ -57,6 +62,12 @@ public abstract class MixinChatHud {
@Final
public List<ChatHudLine> messages;

@Shadow
public abstract int getWidth();

@Unique
private int chatY = -1;

/**
* Spoofs the message size to be empty to avoid deletion.
*/
Expand Down Expand Up @@ -85,7 +96,6 @@ public void hookClear(boolean clearHistory, CallbackInfo ci) {
* Modifies {@link ChatHud#addVisibleMessage(ChatHudLine)} so, that the id is
* forwarded and if {@link ModuleBetterChat} is enabled, older lines won't be removed.
*/
@SuppressWarnings("JavadocReference")
@Inject(method = "addVisibleMessage", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;isChatFocused()Z", shift = At.Shift.BEFORE), cancellable = true)
public void hookAddVisibleMessage(ChatHudLine message, CallbackInfo ci, @Local List<OrderedText> list) {
var focused = isChatFocused();
Expand Down Expand Up @@ -117,4 +127,32 @@ public void hookAddVisibleMessage(ChatHudLine message, CallbackInfo ci, @Local L
ci.cancel();
}

@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;getLineHeight()I", ordinal = 0))
public void hookStoreChatY(DrawContext context, int currentTick, int mouseX, int mouseY, boolean focused, CallbackInfo ci, @Local(ordinal = 7) int m) {
this.chatY = m;
}

@ModifyArgs(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;fill(IIIII)V", ordinal = 0))
private void modifyArgs(
Args args,
@Local(ordinal = 1, argsOnly = true) int mouseX,
@Local(ordinal = 2, argsOnly = true) int mouseY
) {
if(!(ModuleBetterChat.INSTANCE.getRunning() && ModuleBetterChat.Copy.INSTANCE.getRunning() && ModuleBetterChat.Copy.INSTANCE.getHighlight())) {
return;
}

var hovering = mouseX >= 0 && mouseX <= ((int) args.get(2)) -4 &&
mouseY >= ((int)args.get(1)+1) && mouseY <= ((int)args.get(3));

if (hovering) {
args.set(4, 140 << 24);
}
}

@Override
public int liquidbounce_getChatY() {
return chatY;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package net.ccbluex.liquidbounce.injection.mixins.minecraft.gui;

import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;

import java.util.List;

/**
* @author 00101110001100010111000101111
* @since 12/18/2024
**/
@Mixin(ChatHud.class)
public interface MixinChatHudAccessor {
@Invoker("toChatLineY")
double invokeToChatLineY(double y);

@Invoker("getMessageIndex")
int invokeGetMessageIndex(double chatLineX, double chatLineY);

@Invoker("getLineHeight")
int invokeGetLineHeight();

@Accessor
List<ChatHudLine.Visible> getVisibleMessages();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,26 @@

import net.ccbluex.liquidbounce.event.EventManager;
import net.ccbluex.liquidbounce.event.events.ChatSendEvent;
import net.ccbluex.liquidbounce.event.events.NotificationEvent;
import net.ccbluex.liquidbounce.features.module.modules.misc.betterchat.ModuleBetterChat;
import net.ccbluex.liquidbounce.interfaces.ChatHudAddition;
import net.ccbluex.liquidbounce.utils.client.ClientUtilsKt;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.text.CharacterVisitor;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
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.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.ArrayList;
import java.util.List;

@Mixin(ChatScreen.class)
public abstract class MixinChatScreen extends MixinScreen {

Expand Down Expand Up @@ -56,4 +69,111 @@ private void handleChatMessage(String chatText, boolean addToHistory, CallbackIn
}
}

@Inject(method = "mouseClicked", at = @At("HEAD"))
private void hookMouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
if (!(ModuleBetterChat.INSTANCE.getRunning() && ModuleBetterChat.Copy.INSTANCE.getRunning())) {
return;
}

int[] activeMessage = getActiveMessage((int)mouseX, (int)mouseY);

if (activeMessage == null) {
return;
}

ChatHud chatHud = this.client.inGameHud.getChatHud();
MixinChatHudAccessor accessor = (MixinChatHudAccessor) chatHud;

List<ChatHudLine.Visible> visibleMessages = accessor.getVisibleMessages();
List<ChatHudLine.Visible> messageParts = new ArrayList<>();
messageParts.add(visibleMessages.get(activeMessage[3]));

for (int index = activeMessage[3] + 1; index < visibleMessages.size(); index++) {
if (visibleMessages.get(index).endOfEntry())
break;

messageParts.addFirst(visibleMessages.get(index));
}

if (messageParts.isEmpty())
return;

copyMessage(messageParts, button);
}

@Unique
private void copyMessage(List<ChatHudLine.Visible> messageParts, int button) {
final StringBuilder builder = new StringBuilder();

CharacterVisitor visitor = (index, style, codePoint) -> {
builder.append((char) codePoint);
return true;
};

for (ChatHudLine.Visible line : messageParts) {
line.content().accept(visitor);
}

if (isPressed(GLFW.GLFW_KEY_LEFT_SHIFT, GLFW.GLFW_KEY_RIGHT_SHIFT) && button == GLFW.GLFW_MOUSE_BUTTON_1) {
client.keyboard.setClipboard(builder.toString());

if (ModuleBetterChat.Copy.INSTANCE.getNotification()) {
ClientUtilsKt.notification(
"ChatCopy",
"The line is copied",
NotificationEvent.Severity.SUCCESS
);
}
} else if (button == GLFW.GLFW_MOUSE_BUTTON_2) {
if (client.currentScreen instanceof ChatScreen chat) {
((MixinChatScreenAccessor) chat).getChatField().setText(builder.toString());
}
}
}

@Unique
private boolean isPressed(int... keys) {
for (int key : keys) {
if (GLFW.glfwGetKey(client.getWindow().getHandle(), key) == GLFW.GLFW_PRESS) {
return true;
}
}

return false;
}

// [0] - y,
// [1] - width,
// [2] - height,
// [3] - (message) index
@Unique
private int @Nullable [] getActiveMessage(int mouseX, int mouseY) {
ChatHud chatHud = this.client.inGameHud.getChatHud();
MixinChatHudAccessor accessor = (MixinChatHudAccessor) chatHud;
ChatHudAddition addition = (ChatHudAddition) chatHud;

float chatScale = (float) chatHud.getChatScale();
int chatLineY = (int) accessor.invokeToChatLineY(mouseY);
int messageIndex = accessor.invokeGetMessageIndex(0, chatLineY);
int buttonX = (int) (chatHud.getWidth() + 14 * chatScale);

if (messageIndex == -1 || mouseX > buttonX + 14 * chatScale)
return null;

int chatY = addition.liquidbounce_getChatY();

int buttonSize = (int) (9 * chatScale);
int lineHeight = accessor.invokeGetLineHeight();
int scaledButtonY = chatY - (chatLineY + 1) * lineHeight + (int) Math.ceil((lineHeight - 9) / 2.0);
float buttonY = scaledButtonY * chatScale;

boolean hovering = mouseX >= 0 && mouseX <= buttonX && mouseY >= buttonY && mouseY <= buttonY + buttonSize;

if (hovering) {
return new int[]{(int) buttonY, buttonX, buttonSize, messageIndex};
} else {
return null;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.ccbluex.liquidbounce.injection.mixins.minecraft.gui;

import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(ChatScreen.class)
public interface MixinChatScreenAccessor {
@Accessor("chatField")
TextFieldWidget getChatField();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package net.ccbluex.liquidbounce.interfaces;

public interface ChatHudAddition {
int liquidbounce_getChatY();
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,16 @@ object ModuleBetterChat : ClientModule("BetterChat", Category.MISC, aliases = ar
private val forceUnicodeChat by boolean("ForceUnicodeChat", false)

init {
tree(AntiSpam)
treeAll(
AntiSpam,
Copy
)
}


object Copy : ToggleableConfigurable(this, "Copy", true) {
val notification by boolean("Notificate", true)
val highlight by boolean("Highlight", true)
}

var antiChatClearPaused = false
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/liquidbounce.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
"minecraft.fluid.MixinFlowableFluid",
"minecraft.gui.MixinBossBarHud",
"minecraft.gui.MixinChatHud",
"minecraft.gui.MixinChatHudAccessor",
"minecraft.gui.MixinChatInputSuggestor",
"minecraft.gui.MixinChatScreen",
"minecraft.gui.MixinChatScreenAccessor",
"minecraft.gui.MixinHandledScreen",
"minecraft.gui.MixinInGameHud",
"minecraft.gui.MixinPlayerListHud",
Expand Down
Loading