fix: support disabling Advanced.async on server
ci/woodpecker/push/jfmod Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-07-28 14:57:14 +02:00
parent fa546b3f26
commit e04cfaaf93
Signed by: Johannes
GPG Key ID: E76429612C2929F4
3 changed files with 66 additions and 27 deletions

View File

@ -4,7 +4,9 @@ import io.gitlab.jfronny.commons.cache.FixedSizeMap;
import io.gitlab.jfronny.commons.log.Logger;
import io.gitlab.jfronny.libjf.translate.api.Language;
import io.gitlab.jfronny.libjf.translate.api.TranslateService;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.text.*;
import java.util.*;
@ -15,6 +17,7 @@ public class GoogleChat implements ModInitializer {
public static final String MOD_ID = "google-chat";
public static final Logger LOGGER = Logger.forName(MOD_ID);
public static TranslateService<?> TRANSLATE_SERVICE;
private static final boolean IS_SERVER = FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER;
private static final Map<Text, Text> s2ct = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
private static final Map<Text, Text> c2st = new FixedSizeMap<>(GoogleChatConfig.Advanced.cacheSize);
@ -146,7 +149,7 @@ public class GoogleChat implements ModInitializer {
}
private static <K, V> V computeIfAbsent2(Map<K, V> map, K key, Function<K, V> compute) {
if (!GoogleChatConfig.Advanced.async) return map.computeIfAbsent(key, compute);
if (!GoogleChatConfig.Advanced.async && !IS_SERVER) return map.computeIfAbsent(key, compute);
synchronized (map) {
if (map.containsKey(key)) return map.get(key);
V value = compute.apply(key);

View File

@ -1,44 +1,48 @@
package io.gitlab.jfronny.googlechat.server;
import io.gitlab.jfronny.googlechat.*;
import io.gitlab.jfronny.googlechat.GoogleChatConfig;
import io.gitlab.jfronny.googlechat.TranslationDirection;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
import net.minecraft.network.message.MessageDecorator;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import static io.gitlab.jfronny.libjf.LibJf.LOGGER;
public class GoogleChatServer implements DedicatedServerModInitializer {
public class GoogleChatServer implements DedicatedServerModInitializer, MessageDecorator {
@Override
public void onInitializeServer() {
// Default phase is executed between CONTENT and STYLING
// Perform translation there instead of during CONTENT to better support other mods (such as chat-transform)
// If this causes an incompatibility, I'll add my own phase
ServerMessageDecoratorEvent.EVENT.register(Event.DEFAULT_PHASE, (sender, 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 (TranslationDirection.C2S.hasTarget()) {
if (TranslationDirection.S2C.hasTarget()) {
// Do not translate back and forth
return futureMessage;
}
ServerMessageDecoratorEvent.EVENT.register(Event.DEFAULT_PHASE, this);
}
@Override
public CompletableFuture<Text> decorate(@Nullable ServerPlayerEntity sender, Text original) {
final CompletableFuture<Text> future = CompletableFuture.completedFuture(original);
if (!GoogleChatConfig.General.enabled) return future;
return GoogleChatConfig.Advanced.async
? decorate(sender, new TranslatableContainer.Async(future)).text()
: CompletableFuture.completedFuture(decorate(sender, new TranslatableContainer.Sync(original)).text());
}
private <K, T extends TranslatableContainer<K, T>> T decorate(@Nullable ServerPlayerEntity sender, T original) {
T message = original;
if (sender != null) { // Client messages should first be translated to the server language
if (TranslationDirection.C2S.hasTarget()) {
if (TranslationDirection.S2C.hasTarget()) {
// Do not translate back and forth
return message;
}
futureMessage = futureMessage.thenApplyAsync(msg -> {
var translated = GoogleChat.translateIfNeeded(msg, TranslationDirection.C2S, true);
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied C2S translation from " + msg + " to " + translated);
return translated;
});
}
// All messages should be translated to the client language before sending
futureMessage = futureMessage.thenApplyAsync(msg -> {
var translated = GoogleChat.translateIfNeeded(msg, TranslationDirection.S2C, true);
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied S2C translation from " + msg + " to " + translated);
return translated;
});
return futureMessage;
});
message = message.translate(TranslationDirection.C2S);
}
// All messages should be translated to the client language before sending
message = message.translate(TranslationDirection.S2C);
return message;
}
}

View File

@ -0,0 +1,32 @@
package io.gitlab.jfronny.googlechat.server;
import io.gitlab.jfronny.googlechat.*;
import net.minecraft.text.Text;
import java.util.concurrent.CompletableFuture;
import static io.gitlab.jfronny.libjf.LibJf.LOGGER;
public sealed interface TranslatableContainer<T, S extends TranslatableContainer<T, S>> {
S translate(TranslationDirection direction);
record Sync(Text text) implements TranslatableContainer<Text, Sync> {
@Override
public Sync translate(TranslationDirection direction) {
return new Sync(translateAndLog(text, direction));
}
}
record Async(CompletableFuture<Text> text) implements TranslatableContainer<CompletableFuture<Text>, Async> {
@Override
public Async translate(TranslationDirection direction) {
return new Async(text.thenApplyAsync(msg -> translateAndLog(msg, direction)));
}
}
static Text translateAndLog(final Text source, final TranslationDirection direction) {
var translated = GoogleChat.translateIfNeeded(source, direction, true);
if (GoogleChatConfig.Advanced.debugLogs) LOGGER.info("Applied C2S translation from " + source + " to " + translated);
return translated;
}
}