feat: use async on client
ci/woodpecker/push/jfmod Pipeline was successful
Details
ci/woodpecker/push/jfmod Pipeline was successful
Details
This commit is contained in:
parent
21c0cd1000
commit
fa546b3f26
|
@ -1,6 +1,7 @@
|
||||||
package io.gitlab.jfronny.googlechat.client.mixin;
|
package io.gitlab.jfronny.googlechat.client.mixin;
|
||||||
|
|
||||||
import io.gitlab.jfronny.googlechat.GoogleChatCache;
|
import io.gitlab.jfronny.googlechat.GoogleChat;
|
||||||
|
import io.gitlab.jfronny.googlechat.TranslationDirection;
|
||||||
import net.minecraft.client.gui.screen.ChatScreen;
|
import net.minecraft.client.gui.screen.ChatScreen;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
@ -11,6 +12,6 @@ public class ChatScreenMixin {
|
||||||
@ModifyVariable(method = "sendMessage(Ljava/lang/String;Z)Z", at = @At(value = "HEAD"), argsOnly = true, ordinal = 0)
|
@ModifyVariable(method = "sendMessage(Ljava/lang/String;Z)Z", at = @At(value = "HEAD"), argsOnly = true, ordinal = 0)
|
||||||
String googlechat$translateChatText(String chatText) {
|
String googlechat$translateChatText(String chatText) {
|
||||||
if (chatText.startsWith("/")) return chatText; // Bypass for client-side commands (Carpet, ...)
|
if (chatText.startsWith("/")) return chatText; // Bypass for client-side commands (Carpet, ...)
|
||||||
return GoogleChatCache.c2s(chatText);
|
return GoogleChat.translateIfNeeded(chatText, TranslationDirection.C2S, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.gitlab.jfronny.googlechat.client.mixin;
|
package io.gitlab.jfronny.googlechat.client.mixin;
|
||||||
|
|
||||||
import io.gitlab.jfronny.googlechat.GoogleChatCache;
|
import io.gitlab.jfronny.googlechat.GoogleChat;
|
||||||
|
import io.gitlab.jfronny.googlechat.TranslationDirection;
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
@ -11,6 +12,6 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
public class ClientPlayerEntityMixin {
|
public class ClientPlayerEntityMixin {
|
||||||
@ModifyVariable(method = "sendMessage(Lnet/minecraft/text/Text;)V", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
@ModifyVariable(method = "sendMessage(Lnet/minecraft/text/Text;)V", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||||
Text googlechat$translateMessage(Text source) {
|
Text googlechat$translateMessage(Text source) {
|
||||||
return GoogleChatCache.c2s(source);
|
return GoogleChat.translateIfNeeded(source, TranslationDirection.C2S, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,100 @@
|
||||||
package io.gitlab.jfronny.googlechat.client.mixin;
|
package io.gitlab.jfronny.googlechat.client.mixin;
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import io.gitlab.jfronny.googlechat.GoogleChatCache;
|
import io.gitlab.jfronny.googlechat.*;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.hud.*;
|
||||||
import net.minecraft.client.network.message.MessageHandler;
|
import net.minecraft.client.network.message.MessageHandler;
|
||||||
import net.minecraft.network.message.MessageType;
|
import net.minecraft.client.util.NarratorManager;
|
||||||
import net.minecraft.network.message.SignedMessage;
|
import net.minecraft.network.message.*;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import org.spongepowered.asm.mixin.*;
|
import org.spongepowered.asm.mixin.*;
|
||||||
import org.spongepowered.asm.mixin.injection.*;
|
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)
|
@Mixin(MessageHandler.class)
|
||||||
public class MessageHandlerMixin {
|
public abstract class MessageHandlerMixin {
|
||||||
@Shadow @Final private MinecraftClient client;
|
@Shadow @Final private MinecraftClient client;
|
||||||
|
@Shadow protected abstract void narrate(MessageType.Parameters params, Text message);
|
||||||
|
|
||||||
@Redirect(method = "onChatMessage(Lnet/minecraft/network/message/SignedMessage;Lcom/mojang/authlib/GameProfile;Lnet/minecraft/network/message/MessageType$Parameters;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/message/MessageType$Parameters;applyChatDecoration(Lnet/minecraft/text/Text;)Lnet/minecraft/text/Text;"))
|
@Unique CompletableFuture<Void> googlechat$currentFuture = CompletableFuture.completedFuture(null);
|
||||||
Text googlechat$applyDecoration(MessageType.Parameters instance, Text content, SignedMessage args$signed, GameProfile args$sender) {
|
@Unique ThreadLocal<GameProfile> sender = new ThreadLocal<>();
|
||||||
return instance.applyChatDecoration(googlechat$shouldTranslate(args$sender) ? GoogleChatCache.s2c(content) : content);
|
|
||||||
|
@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 = "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/network/message/MessageType$Parameters;applyChatDecoration(Lnet/minecraft/text/Text;)Lnet/minecraft/text/Text;"))
|
@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"))
|
||||||
Text googlechat$applyDecoration2(MessageType.Parameters instance, Text content, MessageType.Parameters args$params, SignedMessage args$message, Text args$decorated, GameProfile args$sender) {
|
private void googlechat$onGameMessage$addMessage(ChatHud instance, Text text) {
|
||||||
return instance.applyChatDecoration(googlechat$shouldTranslate(args$sender) ? GoogleChatCache.s2c(content) : content);
|
if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.addMessage(googlechat$s2c(text)));
|
||||||
|
else instance.addMessage(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModifyVariable(method = "onProfilelessMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageType$Parameters;)V", at = @At(value = "HEAD"), argsOnly = true, ordinal = 0)
|
@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"))
|
||||||
Text googlechat$applyTranslation(Text origin) {
|
private void googlechat$onGameMessage$narrateSystemMessage(NarratorManager instance, Text text) {
|
||||||
return GoogleChatCache.s2c(origin);
|
if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.narrateSystemMessage(googlechat$s2c(text)));
|
||||||
|
else instance.narrateSystemMessage(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModifyVariable(method = "onGameMessage(Lnet/minecraft/text/Text;Z)V", at = @At(value = "HEAD"), argsOnly = true, ordinal = 0)
|
@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"))
|
||||||
Text googlechat$applyTranslation2(Text origin) {
|
private void googlechat$onProfilelessMessage$addMessage(ChatHud instance, Text text) {
|
||||||
return GoogleChatCache.s2c(origin);
|
if (googlechat$shouldTranslate()) googlechat$schedule(() -> instance.addMessage(googlechat$s2c(text)));
|
||||||
|
else instance.addMessage(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean googlechat$shouldTranslate(GameProfile sender) {
|
@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"))
|
||||||
return client != null && client.player != null && sender != null && !sender.getId().equals(client.player.getUuid());
|
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<Boolean> 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,79 @@
|
||||||
package io.gitlab.jfronny.googlechat;
|
package io.gitlab.jfronny.googlechat;
|
||||||
|
|
||||||
|
import io.gitlab.jfronny.commons.cache.FixedSizeMap;
|
||||||
import io.gitlab.jfronny.commons.log.Logger;
|
import io.gitlab.jfronny.commons.log.Logger;
|
||||||
import io.gitlab.jfronny.libjf.translate.api.Language;
|
import io.gitlab.jfronny.libjf.translate.api.Language;
|
||||||
import io.gitlab.jfronny.libjf.translate.api.TranslateService;
|
import io.gitlab.jfronny.libjf.translate.api.TranslateService;
|
||||||
import net.fabricmc.api.EnvType;
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.minecraft.text.*;
|
import net.minecraft.text.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class GoogleChat implements ModInitializer {
|
public class GoogleChat implements ModInitializer {
|
||||||
public static final String MOD_ID = "google-chat";
|
public static final String MOD_ID = "google-chat";
|
||||||
public static final Logger LOGGER = Logger.forName(MOD_ID);
|
public static final Logger LOGGER = Logger.forName(MOD_ID);
|
||||||
public static TranslateService<?> TRANSLATE_SERVICE;
|
public static TranslateService<?> TRANSLATE_SERVICE;
|
||||||
|
|
||||||
|
private static final Map<Text, Text> s2ct = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
||||||
|
private static final Map<Text, Text> c2st = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
||||||
|
private static final Map<TextContent, TextContent> s2cc = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
||||||
|
private static final Map<TextContent, TextContent> c2sc = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
||||||
|
private static final Map<String, String> s2cs = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
||||||
|
private static final Map<String, String> c2ss = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
ForkJoinPool.commonPool().execute(() -> TRANSLATE_SERVICE = TranslateService.getConfigured());
|
ForkJoinPool.commonPool().execute(() -> TRANSLATE_SERVICE = TranslateService.getConfigured());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Text translateIfNeeded(Text source, Direction direction, boolean respectRegex) {
|
public static void onConfigChange() {
|
||||||
if (shouldSkipOutright(direction)) return source;
|
synchronized (s2ct) {
|
||||||
|
s2ct.clear();
|
||||||
|
}
|
||||||
|
synchronized (c2st) {
|
||||||
|
c2st.clear();
|
||||||
|
}
|
||||||
|
synchronized (s2cc) {
|
||||||
|
s2cc.clear();
|
||||||
|
}
|
||||||
|
synchronized (c2sc) {
|
||||||
|
c2sc.clear();
|
||||||
|
}
|
||||||
|
synchronized (s2cs) {
|
||||||
|
s2cs.clear();
|
||||||
|
}
|
||||||
|
synchronized (c2ss) {
|
||||||
|
c2ss.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Text translateIfNeeded(Text source, TranslationDirection direction, boolean respectRegex) {
|
||||||
|
if (source == null) return null;
|
||||||
|
if (direction.shouldSkipOutright()) return source;
|
||||||
String sourceString = toString(source);
|
String sourceString = toString(source);
|
||||||
if (respectRegex && failsRegex(sourceString, direction)) return source;
|
if (respectRegex && direction.failsRegex(sourceString)) return source;
|
||||||
MutableText translated;
|
return computeIfAbsent2(direction == TranslationDirection.C2S ? c2st : s2ct, source, t -> {
|
||||||
if (GoogleChatConfig.Processing.desugar) {
|
MutableText translated;
|
||||||
translated = Text.literal(translateIfNeeded(sourceString, direction, true));
|
if (GoogleChatConfig.Processing.desugar) {
|
||||||
} else {
|
translated = Text.literal(translateIfNeeded(sourceString, direction, true));
|
||||||
translated = MutableText.of(translateIfNeeded(source.getContent(), direction, false))
|
} else {
|
||||||
.setStyle(source.getStyle());
|
translated = MutableText.of(translateIfNeeded(t.getContent(), direction, false))
|
||||||
for (Text sibling : source.getSiblings()) {
|
.setStyle(t.getStyle());
|
||||||
translated.append(translateIfNeeded(sibling, direction, false));
|
for (Text sibling : t.getSiblings()) {
|
||||||
|
translated.append(translateIfNeeded(sibling, direction, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Translated " + sourceString + " to " + toString(translated));
|
||||||
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Translated " + sourceString + " to " + toString(translated));
|
if (GoogleChatConfig.General.translationTooltip) {
|
||||||
if (GoogleChatConfig.General.translationTooltip) {
|
return t.copy().styled(style -> addHover(style, Text.literal("Translated: ").append(translated)));
|
||||||
return source.copy().styled(style -> addHover(style, Text.literal("Translated: ").append(translated)));
|
} else if (translated.getStyle().getHoverEvent() == null) {
|
||||||
} else if (translated.getStyle().getHoverEvent() == null) {
|
return translated.styled(style -> addHover(style, Text.literal("Original: ").append(t)));
|
||||||
return translated.styled(style -> addHover(style, Text.literal("Original: ").append(source)));
|
} else {
|
||||||
} else {
|
return translated;
|
||||||
return translated;
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toString(Text text) {
|
private static String toString(Text text) {
|
||||||
|
@ -55,28 +85,31 @@ public class GoogleChat implements ModInitializer {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TextContent translateIfNeeded(TextContent source, Direction direction, boolean respectRegex) {
|
public static TextContent translateIfNeeded(TextContent source, TranslationDirection direction, boolean respectRegex) {
|
||||||
if (shouldSkipOutright(direction)) return source;
|
if (source == null) return null;
|
||||||
|
if (direction.shouldSkipOutright()) return source;
|
||||||
String sourceString = toString(source);
|
String sourceString = toString(source);
|
||||||
if (respectRegex && failsRegex(sourceString, direction)) return source;
|
if (respectRegex && direction.failsRegex(sourceString)) return source;
|
||||||
//TODO This method (and the check for translatable args) should be converted to a switch pattern when available
|
return computeIfAbsent2(direction == TranslationDirection.C2S ? c2sc : s2cc, source, t -> {
|
||||||
if (source instanceof TranslatableTextContent tx) {
|
//TODO This method (and the check for translatable args) should be converted to a switch pattern when available
|
||||||
Object[] args = tx.getArgs();
|
if (t instanceof TranslatableTextContent tx) {
|
||||||
args = Arrays.copyOf(args, args.length);
|
Object[] args = tx.getArgs();
|
||||||
// We're not translating TranslatableText, but are translating arguments
|
args = Arrays.copyOf(args, args.length);
|
||||||
for (int i = 0; i < args.length; i++) {
|
// We're not translating TranslatableText, but are translating arguments
|
||||||
if (args[i] instanceof Text tx1) args[i] = translateIfNeeded(tx1, direction, false);
|
for (int i = 0; i < args.length; i++) {
|
||||||
else if (args[i] instanceof TextContent tx1) args[i] = translateIfNeeded(tx1, direction, false);
|
if (args[i] instanceof Text tx1) args[i] = translateIfNeeded(tx1, direction, false);
|
||||||
else if (args[i] instanceof String tx1) args[i] = translateIfNeeded(tx1, direction, false);
|
else if (args[i] instanceof TextContent tx1) args[i] = translateIfNeeded(tx1, direction, false);
|
||||||
else args[i] = args[i];
|
else if (args[i] instanceof String tx1) args[i] = translateIfNeeded(tx1, direction, false);
|
||||||
|
else args[i] = args[i];
|
||||||
|
}
|
||||||
|
return new TranslatableTextContent(tx.getKey(), translateIfNeeded(tx.getFallback(), direction, false), args);
|
||||||
|
} else if (t instanceof LiteralTextContent tx) {
|
||||||
|
return new LiteralTextContent(translateIfNeeded(tx.string(), direction, false));
|
||||||
|
} else {
|
||||||
|
// LOGGER.info("Unhandled text type: " + source.getClass() + " (" + source + ")");
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
return new TranslatableTextContent(tx.getKey(), translateIfNeeded(tx.getFallback(), direction, false), args);
|
});
|
||||||
} else if (source instanceof LiteralTextContent tx) {
|
|
||||||
return new LiteralTextContent(translateIfNeeded(tx.string(), direction, false));
|
|
||||||
} else {
|
|
||||||
// LOGGER.info("Unhandled text type: " + source.getClass() + " (" + source + ")");
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toString(TextContent text) {
|
private static String toString(TextContent text) {
|
||||||
|
@ -92,53 +125,33 @@ public class GoogleChat implements ModInitializer {
|
||||||
return style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverText));
|
return style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverText));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String translateIfNeeded(String source, Direction direction, boolean respectRegex) {
|
public static String translateIfNeeded(String source, TranslationDirection direction, boolean respectRegex) {
|
||||||
if (shouldSkipOutright(direction)) return source;
|
if (source == null) return null;
|
||||||
if (respectRegex && failsRegex(source, direction)) return source;
|
if (direction.shouldSkipOutright()) return source;
|
||||||
try {
|
if (respectRegex && direction.failsRegex(source)) return source;
|
||||||
// Ignore generics since this is apparently not something java supports
|
return computeIfAbsent2(direction == TranslationDirection.C2S ? c2ss : s2cs, source, t -> {
|
||||||
@SuppressWarnings("rawtypes") TranslateService svc = GoogleChat.TRANSLATE_SERVICE;
|
try {
|
||||||
if (svc == null) throw new NullPointerException("Translate service uninitialized");
|
// Ignore generics since this is apparently not something java supports
|
||||||
Language sourceLang = svc.parseLang(direction.source());
|
@SuppressWarnings("rawtypes") TranslateService svc = GoogleChat.TRANSLATE_SERVICE;
|
||||||
Language targetLang = svc.parseLang(direction.target());
|
if (svc == null) throw new NullPointerException("Translate service uninitialized");
|
||||||
//noinspection unchecked
|
Language sourceLang = svc.parseLang(direction.source());
|
||||||
return svc.translate(source, sourceLang, targetLang);
|
Language targetLang = svc.parseLang(direction.target());
|
||||||
} catch (Throwable e) {
|
//noinspection unchecked
|
||||||
LOGGER.error("Could not translate text: " + source, e);
|
return svc.translate(source, sourceLang, targetLang);
|
||||||
return source;
|
} catch (Throwable e) {
|
||||||
}
|
LOGGER.error("Could not translate text: " + source, e);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean failsRegex(String text, Direction direction) {
|
private static <K, V> V computeIfAbsent2(Map<K, V> map, K key, Function<K, V> compute) {
|
||||||
boolean isSender = (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) == (direction == Direction.C2S);
|
if (!GoogleChatConfig.Advanced.async) return map.computeIfAbsent(key, compute);
|
||||||
if (isSender) return text.matches(GoogleChatConfig.Processing.sendingRegex) == GoogleChatConfig.Processing.sendingRegexIsBlacklist;
|
synchronized (map) {
|
||||||
else return text.matches(GoogleChatConfig.Processing.receivingRegex) == GoogleChatConfig.Processing.receivingRegexIsBlacklist;
|
if (map.containsKey(key)) return map.get(key);
|
||||||
}
|
V value = compute.apply(key);
|
||||||
|
map.put(key, value);
|
||||||
private static boolean shouldSkipOutright(Direction direction) {
|
return value;
|
||||||
return !GoogleChatConfig.General.enabled || !hasTarget(direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasTarget(Direction direction) {
|
|
||||||
return !direction.target().equals("auto");
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Direction {
|
|
||||||
C2S,
|
|
||||||
S2C;
|
|
||||||
|
|
||||||
public String source() {
|
|
||||||
return switch (this) {
|
|
||||||
case C2S -> GoogleChatConfig.General.clientLanguage;
|
|
||||||
case S2C -> GoogleChatConfig.General.serverLanguage;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public String target() {
|
|
||||||
return switch (this) {
|
|
||||||
case C2S -> GoogleChatConfig.General.serverLanguage;
|
|
||||||
case S2C -> GoogleChatConfig.General.clientLanguage;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
package io.gitlab.jfronny.googlechat;
|
|
||||||
|
|
||||||
import io.gitlab.jfronny.commons.cache.FixedSizeMap;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class GoogleChatCache {
|
|
||||||
private static Map<Text, Text> s2ct = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
|
||||||
private static Map<Text, Text> c2st = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
|
||||||
private static Map<String, String> s2cs = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
|
||||||
private static Map<String, String> c2ss = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
|
|
||||||
|
|
||||||
public static void clear() {
|
|
||||||
s2ct.clear();
|
|
||||||
c2st.clear();
|
|
||||||
s2cs.clear();
|
|
||||||
c2ss.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onConfigChange() {
|
|
||||||
GoogleChat.LOGGER.info("Config Change");
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Text s2c(Text msg) {
|
|
||||||
return s2ct.computeIfAbsent(msg, m -> GoogleChat.translateIfNeeded(m, GoogleChat.Direction.S2C, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Text c2s(Text msg) {
|
|
||||||
return c2st.computeIfAbsent(msg, m -> GoogleChat.translateIfNeeded(m, GoogleChat.Direction.C2S, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String s2c(String msg) {
|
|
||||||
return s2cs.computeIfAbsent(msg, m -> GoogleChat.translateIfNeeded(m, GoogleChat.Direction.S2C, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String c2s(String msg) {
|
|
||||||
return c2ss.computeIfAbsent(msg, m -> GoogleChat.translateIfNeeded(m, GoogleChat.Direction.C2S, true));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -46,6 +46,7 @@ public class GoogleChatConfig {
|
||||||
@Category
|
@Category
|
||||||
public static class Advanced {
|
public static class Advanced {
|
||||||
@Entry(min = 1, max = 1024) public static int cacheSize = 256;
|
@Entry(min = 1, max = 1024) public static int cacheSize = 256;
|
||||||
|
@Entry public static boolean async = true;
|
||||||
@Entry public static boolean debugLogs = FabricLoader.getInstance().isDevelopmentEnvironment();
|
@Entry public static boolean debugLogs = FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ public class GoogleChatConfig {
|
||||||
Your client language is not set to "auto" and you are using a server.
|
Your client language is not set to "auto" and you are using a server.
|
||||||
This setup is not recommended! Please set up GoogleChat according to its documentation!""");
|
This setup is not recommended! Please set up GoogleChat according to its documentation!""");
|
||||||
}
|
}
|
||||||
if (!initial) GoogleChatCache.onConfigChange();
|
if (!initial) GoogleChat.onConfigChange();
|
||||||
initial = false;
|
initial = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package io.gitlab.jfronny.googlechat;
|
||||||
|
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
public enum TranslationDirection {
|
||||||
|
C2S,
|
||||||
|
S2C;
|
||||||
|
|
||||||
|
public String source() {
|
||||||
|
return switch (this) {
|
||||||
|
case C2S -> GoogleChatConfig.General.clientLanguage;
|
||||||
|
case S2C -> GoogleChatConfig.General.serverLanguage;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String target() {
|
||||||
|
return switch (this) {
|
||||||
|
case C2S -> GoogleChatConfig.General.serverLanguage;
|
||||||
|
case S2C -> GoogleChatConfig.General.clientLanguage;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTarget() {
|
||||||
|
return !target().equals("auto");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldSkipOutright() {
|
||||||
|
if (!GoogleChatConfig.General.enabled) return true;
|
||||||
|
return !hasTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean failsRegex(String text) {
|
||||||
|
boolean isSender = (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) == (this == TranslationDirection.C2S);
|
||||||
|
if (isSender) return text.matches(GoogleChatConfig.Processing.sendingRegex) == GoogleChatConfig.Processing.sendingRegexIsBlacklist;
|
||||||
|
else return text.matches(GoogleChatConfig.Processing.receivingRegex) == GoogleChatConfig.Processing.receivingRegexIsBlacklist;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ import net.minecraft.text.Text;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import static io.gitlab.jfronny.googlechat.GoogleChat.hasTarget;
|
|
||||||
import static io.gitlab.jfronny.libjf.LibJf.LOGGER;
|
import static io.gitlab.jfronny.libjf.LibJf.LOGGER;
|
||||||
|
|
||||||
public class GoogleChatServer implements DedicatedServerModInitializer {
|
public class GoogleChatServer implements DedicatedServerModInitializer {
|
||||||
|
@ -19,20 +18,23 @@ public class GoogleChatServer implements DedicatedServerModInitializer {
|
||||||
// If this causes an incompatibility, I'll add my own phase
|
// If this causes an incompatibility, I'll add my own phase
|
||||||
ServerMessageDecoratorEvent.EVENT.register(Event.DEFAULT_PHASE, (sender, originalMessage) -> {
|
ServerMessageDecoratorEvent.EVENT.register(Event.DEFAULT_PHASE, (sender, originalMessage) -> {
|
||||||
CompletableFuture<Text> futureMessage = CompletableFuture.completedFuture(originalMessage);
|
CompletableFuture<Text> futureMessage = CompletableFuture.completedFuture(originalMessage);
|
||||||
|
if (!GoogleChatConfig.General.enabled) return futureMessage; // fast fallthrough
|
||||||
if (sender != null) { // Client messages should first be translated to the server language
|
if (sender != null) { // Client messages should first be translated to the server language
|
||||||
if (hasTarget(GoogleChat.Direction.C2S) && hasTarget(GoogleChat.Direction.S2C)) {
|
if (TranslationDirection.C2S.hasTarget()) {
|
||||||
// Do not translate back and forth
|
if (TranslationDirection.S2C.hasTarget()) {
|
||||||
return futureMessage;
|
// Do not translate back and forth
|
||||||
|
return futureMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
futureMessage = futureMessage.thenApplyAsync(msg -> {
|
futureMessage = futureMessage.thenApplyAsync(msg -> {
|
||||||
var translated = GoogleChatCache.c2s(msg);
|
var translated = GoogleChat.translateIfNeeded(msg, TranslationDirection.C2S, true);
|
||||||
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied C2S translation from " + msg + " to " + translated);
|
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied C2S translation from " + msg + " to " + translated);
|
||||||
return translated;
|
return translated;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// All messages should be translated to the client language before sending
|
// All messages should be translated to the client language before sending
|
||||||
futureMessage = futureMessage.thenApplyAsync(msg -> {
|
futureMessage = futureMessage.thenApplyAsync(msg -> {
|
||||||
var translated = GoogleChatCache.s2c(msg);
|
var translated = GoogleChat.translateIfNeeded(msg, TranslationDirection.S2C, true);
|
||||||
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied S2C translation from " + msg + " to " + translated);
|
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied S2C translation from " + msg + " to " + translated);
|
||||||
return translated;
|
return translated;
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
"google-chat.jfconfig.processing.sendingRegexIsBlacklist.tooltip": "Whether the relevant regex should blacklist messages from translation instead of whitelisting",
|
"google-chat.jfconfig.processing.sendingRegexIsBlacklist.tooltip": "Whether the relevant regex should blacklist messages from translation instead of whitelisting",
|
||||||
"google-chat.jfconfig.advanced.title": "Advanced",
|
"google-chat.jfconfig.advanced.title": "Advanced",
|
||||||
"google-chat.jfconfig.advanced.cacheSize": "Cache Size",
|
"google-chat.jfconfig.advanced.cacheSize": "Cache Size",
|
||||||
"google-chat.jfconfig.advanced.cacheSize.tooltips": "The size of each message cache. Since there are four caches, the actual size will be four times this.",
|
"google-chat.jfconfig.advanced.cacheSize.tooltips": "The size of each message cache. Since there are six caches, the actual size will be six times this.",
|
||||||
|
"google-chat.jfconfig.advanced.async": "Async",
|
||||||
|
"google-chat.jfconfig.advanced.async.tooltips": "Whether to asynchronously process messages. Should prevent some stutters, but might cause other issues. Disable if you no longer receive messages",
|
||||||
"google-chat.jfconfig.advanced.debugLogs": "Debug Logs",
|
"google-chat.jfconfig.advanced.debugLogs": "Debug Logs",
|
||||||
"google-chat.jfconfig.advanced.debugLogs.tooltips": "Log additional information about message processing. Useful for debugging",
|
"google-chat.jfconfig.advanced.debugLogs.tooltips": "Log additional information about message processing. Useful for debugging",
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue