package io.gitlab.jfronny.googlechat.client.mixin; import com.mojang.authlib.GameProfile; import io.gitlab.jfronny.googlechat.*; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.hud.*; import net.minecraft.client.network.message.MessageHandler; import net.minecraft.client.util.NarratorManager; import net.minecraft.network.message.*; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.time.Instant; import java.util.concurrent.CompletableFuture; @Mixin(MessageHandler.class) public abstract class MessageHandlerMixin { @Shadow @Final private MinecraftClient client; @Shadow protected abstract void narrate(MessageType.Parameters params, Text message); @Unique CompletableFuture googlechat$currentFuture = CompletableFuture.completedFuture(null); @Unique ThreadLocal sender = new ThreadLocal<>(); @Redirect(method = "onGameMessage(Lnet/minecraft/text/Text;Z)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;setOverlayMessage(Lnet/minecraft/text/Text;Z)V")) private void googlechat$onGameMessage$setOverlayMessage(InGameHud instance, Text message, boolean tinted) { if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.setOverlayMessage(googlechat$s2c(message), tinted)); else instance.setOverlayMessage(message, tinted); } @Redirect(method = "onGameMessage(Lnet/minecraft/text/Text;Z)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;)V")) private void googlechat$onGameMessage$addMessage(ChatHud instance, Text text) { if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.addMessage(googlechat$s2c(text))); else instance.addMessage(text); } @Redirect(method = "onGameMessage(Lnet/minecraft/text/Text;Z)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/NarratorManager;narrateSystemMessage(Lnet/minecraft/text/Text;)V")) private void googlechat$onGameMessage$narrateSystemMessage(NarratorManager instance, Text text) { if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.narrateSystemMessage(googlechat$s2c(text))); else instance.narrateSystemMessage(text); } @Redirect(method = "method_45745(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/text/Text;Ljava/time/Instant;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;)V")) private void googlechat$onProfilelessMessage$addMessage(ChatHud instance, Text text) { if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.addMessage(googlechat$s2c(text))); else instance.addMessage(text); } @Redirect(method = "method_45745(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/text/Text;Ljava/time/Instant;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/message/MessageHandler;narrate(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/text/Text;)V")) private void googlechat$onProfilelessMessage$narrate(MessageHandler instance, MessageType.Parameters params, Text message) { if (instance != (Object) this) GoogleChat.LOGGER.warn("Mismatched message handler in onGameMessage"); if (googlechat$shouldTranslate()) googlechat$schedule(() -> narrate(params, googlechat$s2c(message))); else narrate(params, message); } @Inject(method = "onChatMessage(Lnet/minecraft/network/message/SignedMessage;Lcom/mojang/authlib/GameProfile;Lnet/minecraft/network/message/MessageType$Parameters;)V", at = @At("HEAD")) private void googlechat$onChatMessage$extractSender(SignedMessage message, GameProfile sender, MessageType.Parameters params, CallbackInfo ci) { this.sender.set(sender); } @Redirect(method = "processChatMessageInternal(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/network/message/SignedMessage;Lnet/minecraft/text/Text;Lcom/mojang/authlib/GameProfile;ZLjava/time/Instant;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V")) private void googlechat$processChatMessageInternal$addMessage(ChatHud instance, Text message, MessageSignatureData signature, MessageIndicator indicator) { if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.addMessage(googlechat$s2c(message), signature, indicator)); else instance.addMessage(message, signature, indicator); } @Redirect(method = "processChatMessageInternal(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/network/message/SignedMessage;Lnet/minecraft/text/Text;Lcom/mojang/authlib/GameProfile;ZLjava/time/Instant;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/message/MessageHandler;narrate(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/text/Text;)V")) private void googlechat$processChatMessageInternal$narrate(MessageHandler instance, MessageType.Parameters params, Text message) { if (instance != (Object) this) GoogleChat.LOGGER.warn("Mismatched message handler in onGameMessage"); if (googlechat$shouldTranslate()) googlechat$schedule(() -> narrate(params, googlechat$s2c(message))); else narrate(params, message); } @Inject(method = "processChatMessageInternal(Lnet/minecraft/network/message/MessageType$Parameters;Lnet/minecraft/network/message/SignedMessage;Lnet/minecraft/text/Text;Lcom/mojang/authlib/GameProfile;ZLjava/time/Instant;)Z", at = @At("RETURN")) private void googlechat$processChatMessageInternal$clearSender(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable cir) { this.sender.remove(); } private Text googlechat$s2c(Text message) { return GoogleChat.translateIfNeeded(message, TranslationDirection.S2C, true); } private void googlechat$schedule(Runnable runnable) { if (!GoogleChatConfig.Advanced.async) runnable.run(); else googlechat$currentFuture.whenCompleteAsync((_1, _2) -> runnable.run()).exceptionally(throwable -> { GoogleChat.LOGGER.error("Something went wrong while processing a message", throwable); return null; }); } private boolean googlechat$shouldTranslate() { if (!GoogleChatConfig.General.enabled) return false; if (client == null || client.player == null) return false; var sender = this.sender.get(); if (sender == null) return true; return !sender.getId().equals(client.player.getUuid()); } }