diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 1e448d0c2..c5a96101e 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -29,6 +29,8 @@ dependencies { api("net.kyori:adventure-text-serializer-legacy") api("net.kyori:adventure-text-serializer-plain") api("net.kyori:adventure-text-minimessage") + api("net.kyori:adventure-text-logger-slf4j") + api("net.kyori:adventure-text-serializer-ansi") api(libs.slf4j) api(libs.guice) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d204ff616..7733baaa7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,13 +29,14 @@ flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = jline = "org.jline:jline-terminal-jansi:3.23.0" jopt = "net.sf.jopt-simple:jopt-simple:5.0.4" junit = "org.junit.jupiter:junit-jupiter:5.9.0" +kyori-ansi = "net.kyori:ansi:1.0.3" guava = "com.google.guava:guava:25.1-jre" gson = "com.google.code.gson:gson:2.10.1" guice = "com.google.inject:guice:6.0.0" lmbda = "org.lanternpowered:lmbda:2.0.0" log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" } log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" } -log4j-slf4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j-impl", version.ref = "log4j" } +log4j-slf4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" } log4j-iostreams = { module = "org.apache.logging.log4j:log4j-iostreams", version.ref = "log4j" } log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" } mockito = "org.mockito:mockito-core:5.2.0" @@ -45,7 +46,7 @@ netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" netty-handler = { module = "io.netty:netty-handler", version.ref = "netty" } netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" } nightconfig = "com.electronwill.night-config:toml:3.6.6" -slf4j = "org.slf4j:slf4j-api:1.7.30" +slf4j = "org.slf4j:slf4j-api:2.0.7" spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3" terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0" diff --git a/proxy/build.gradle.kts b/proxy/build.gradle.kts index 8d6ed5945..643d5eb78 100644 --- a/proxy/build.gradle.kts +++ b/proxy/build.gradle.kts @@ -93,6 +93,7 @@ dependencies { implementation(project(":velocity-native")) implementation(libs.bundles.log4j) + implementation(libs.kyori.ansi) implementation(libs.netty.codec) implementation(libs.netty.codec.haproxy) implementation(libs.netty.codec.http) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index f7e67cc37..aada98cf5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -79,6 +79,7 @@ import com.velocitypowered.proxy.tablist.VelocityTabList; import com.velocitypowered.proxy.tablist.VelocityTabListLegacy; import com.velocitypowered.proxy.util.ClosestLocaleMatcher; import com.velocitypowered.proxy.util.DurationUtils; +import com.velocitypowered.proxy.util.TranslatableMapper; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import java.net.InetSocketAddress; @@ -100,9 +101,6 @@ import net.kyori.adventure.platform.facet.FacetPointers; import net.kyori.adventure.platform.facet.FacetPointers.Type; import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.KeybindComponent; -import net.kyori.adventure.text.TranslatableComponent; -import net.kyori.adventure.text.flattener.ComponentFlattener; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -125,9 +123,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, private static final int MAX_PLUGIN_CHANNELS = 1024; private static final PlainTextComponentSerializer PASS_THRU_TRANSLATE = - PlainTextComponentSerializer.builder().flattener( - ComponentFlattener.basic().toBuilder().mapper(KeybindComponent.class, c -> "") - .mapper(TranslatableComponent.class, TranslatableComponent::key).build()).build(); + PlainTextComponentSerializer.builder().flattener(TranslatableMapper.FLATTENER).build(); static final PermissionProvider DEFAULT_PERMISSIONS = s -> PermissionFunction.ALWAYS_UNDEFINED; private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java index 08bc04c07..fe360d4d6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -35,8 +35,7 @@ import net.kyori.adventure.platform.facet.FacetPointers.Type; import net.kyori.adventure.pointer.Pointers; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import net.kyori.adventure.translation.GlobalTranslator; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -55,6 +54,8 @@ import org.jline.reader.LineReaderBuilder; public final class VelocityConsole extends SimpleTerminalConsole implements ConsoleCommandSource { private static final Logger logger = LogManager.getLogger(VelocityConsole.class); + private static final ComponentLogger componentLogger = ComponentLogger + .logger(VelocityConsole.class); private final VelocityServer server; private PermissionFunction permissionFunction = ALWAYS_TRUE; @@ -72,9 +73,7 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons @Override public void sendMessage(@NonNull Identity identity, @NonNull Component message, @NonNull MessageType messageType) { - Component translated = GlobalTranslator.render(message, ClosestLocaleMatcher.INSTANCE - .lookupClosest(Locale.getDefault())); - logger.info(LegacyComponentSerializer.legacySection().serialize(translated)); + componentLogger.info(message); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java index 8e59816af..a6331dd48 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java @@ -26,6 +26,7 @@ import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; import java.nio.file.Path; import java.util.concurrent.ExecutorService; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +50,7 @@ class VelocityPluginModule implements Module { binder.bind(description.getMainClass()).in(Scopes.SINGLETON); binder.bind(Logger.class).toInstance(LoggerFactory.getLogger(description.getId())); + binder.bind(ComponentLogger.class).toInstance(ComponentLogger.logger(description.getId())); binder.bind(Path.class).annotatedWith(DataDirectory.class) .toInstance(basePluginPath.resolve(description.getId())); binder.bind(PluginDescription.class).toInstance(description); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/provider/ComponentLoggerProviderImpl.java b/proxy/src/main/java/com/velocitypowered/proxy/provider/ComponentLoggerProviderImpl.java new file mode 100644 index 000000000..df29ca56f --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/provider/ComponentLoggerProviderImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.provider; + +import com.velocitypowered.proxy.util.TranslatableMapper; +import net.kyori.adventure.text.logger.slf4j.ComponentLogger; +import net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider; +import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer; +import org.jetbrains.annotations.NotNull; +import org.slf4j.LoggerFactory; + +/** + * Velocity ComponentLogger Provider. + */ +@SuppressWarnings("UnstableApiUsage") +public final class ComponentLoggerProviderImpl implements ComponentLoggerProvider { + private static final ANSIComponentSerializer SERIALIZER = ANSIComponentSerializer.builder() + .flattener(TranslatableMapper.FLATTENER) + .build(); + + @Override + public @NotNull ComponentLogger logger( + final @NotNull LoggerHelper helper, + final @NotNull String name + ) { + return helper.delegating(LoggerFactory.getLogger(name), SERIALIZER::serialize); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java b/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java new file mode 100644 index 000000000..c2ca2366f --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/TranslatableMapper.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.util; + +import java.util.Locale; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.translation.GlobalTranslator; +import net.kyori.adventure.translation.TranslationRegistry; +import net.kyori.adventure.translation.Translator; +import org.jetbrains.annotations.Nullable; + + +/** + * Velocity Translation Mapper. + */ +public enum TranslatableMapper implements BiConsumer> { + INSTANCE; + + public static final ComponentFlattener FLATTENER = ComponentFlattener.basic().toBuilder() + .complexMapper(TranslatableComponent.class, TranslatableMapper.INSTANCE) + .build(); + + @Override + public void accept( + final TranslatableComponent translatableComponent, + final Consumer componentConsumer + ) { + for (final Translator source : GlobalTranslator.translator().sources()) { + if (source instanceof TranslationRegistry + && ((TranslationRegistry) source).contains(translatableComponent.key())) { + componentConsumer.accept(GlobalTranslator.render(translatableComponent, + ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault()))); + return; + } + } + final @Nullable String fallback = translatableComponent.fallback(); + if (fallback == null) { + return; + } + for (final Translator source : GlobalTranslator.translator().sources()) { + if (source instanceof TranslationRegistry + && ((TranslationRegistry) source).contains(fallback)) { + componentConsumer.accept( + GlobalTranslator.render(Component.translatable(fallback), + ClosestLocaleMatcher.INSTANCE.lookupClosest(Locale.getDefault()))); + return; + } + } + } +} diff --git a/proxy/src/main/resources/META-INF/services/net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider b/proxy/src/main/resources/META-INF/services/net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider new file mode 100644 index 000000000..b95209776 --- /dev/null +++ b/proxy/src/main/resources/META-INF/services/net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider @@ -0,0 +1 @@ +com.velocitypowered.proxy.provider.ComponentLoggerProviderImpl \ No newline at end of file