diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 5855fdf8e..bbe8a23aa 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -78,6 +78,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.ResourceBundle; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -90,7 +91,11 @@ import java.util.function.IntFunction; import java.util.stream.Collectors; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; +import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.translation.GlobalTranslator; +import net.kyori.adventure.translation.TranslationRegistry; +import net.kyori.adventure.util.UTF8ResourceBundleControl; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.AsyncHttpClient; @@ -193,6 +198,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { logger.info("Booting up {} {}...", version().getName(), version().getVersion()); console.setupStreams(); + registerTranslations(); + serverKeyPair = EncryptionUtils.createRsaKeyPair(1024); cm.logChannelInformation(); @@ -236,6 +243,15 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics()); } + private void registerTranslations() { + final TranslationRegistry translationRegistry = TranslationRegistry + .create(Key.key("velocity", "translations")); + translationRegistry.registerAll(Locale.US, + ResourceBundle.getBundle("com/velocitypowered/proxy/messages", Locale.US, + UTF8ResourceBundleControl.get()), false); + GlobalTranslator.get().addSource(translationRegistry); + } + @SuppressFBWarnings("DM_EXIT") private void doStartupConfigLoad() { try { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/CommandMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/CommandMessages.java new file mode 100644 index 000000000..b4a5a2130 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/CommandMessages.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 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.command.builtin; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.format.NamedTextColor; + +public class CommandMessages { + + public static final TranslatableComponent PLAYERS_ONLY = Component.translatable( + "velocity.command.players-only", NamedTextColor.RED); + public static final TranslatableComponent SERVER_DOES_NOT_EXIST = Component.translatable( + "velocity.command.server-does-not-exist", NamedTextColor.RED); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java index d12f33cf2..6d0f8f947 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java @@ -78,11 +78,7 @@ public class GlistCommand { final CommandSource source = context.getSource(); sendTotalProxyCount(source); source.sendMessage(Identity.nil(), - Component.text().content("To view all players on servers, use ") - .color(NamedTextColor.YELLOW) - .append(Component.text("/glist all", NamedTextColor.DARK_AQUA)) - .append(Component.text(".", NamedTextColor.YELLOW)) - .build()); + Component.translatable("velocity.command.glist-view-all", NamedTextColor.YELLOW)); return 1; } @@ -98,7 +94,7 @@ public class GlistCommand { Optional registeredServer = server.server(serverName); if (!registeredServer.isPresent()) { source.sendMessage(Identity.nil(), - Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); + CommandMessages.SERVER_DOES_NOT_EXIST.args(Component.text(serverName))); return -1; } sendServerPlayers(source, registeredServer.get(), false); @@ -107,10 +103,19 @@ public class GlistCommand { } private void sendTotalProxyCount(CommandSource target) { + int online = server.countConnectedPlayers(); target.sendMessage(Identity.nil(), Component.text() - .content("There are ").color(NamedTextColor.YELLOW) - .append(Component.text(server.connectedPlayers().size(), NamedTextColor.GREEN)) - .append(Component.text(" player(s) online.", NamedTextColor.YELLOW)) + .append(Component.text(Integer.toString(online), NamedTextColor.GREEN)) + .append(Component.space()) + .append(Component.translatable( + online == 1 + ? "velocity.command.glist-player-singular" + : "velocity.command.glist-player-plural", + NamedTextColor.YELLOW + )) + .append(Component.space()) + .append(Component.translatable("velocity.command.glist-total-suffix", + NamedTextColor.YELLOW)) .build()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java index 760c6b6e0..87ec2d6a6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ServerCommand.java @@ -35,6 +35,7 @@ import java.util.stream.Stream; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -53,8 +54,7 @@ public class ServerCommand implements SimpleCommand { final String[] args = invocation.arguments(); if (!(source instanceof Player)) { - source.sendMessage(Identity.nil(), Component.text("Only players may run this command.", - NamedTextColor.RED)); + source.sendMessage(Identity.nil(), CommandMessages.PLAYERS_ONLY); return; } @@ -64,8 +64,8 @@ public class ServerCommand implements SimpleCommand { String serverName = args[0]; Optional toConnect = server.server(serverName); if (!toConnect.isPresent()) { - player.sendMessage(Identity.nil(), - Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED)); + player.sendMessage(Identity.nil(), CommandMessages.SERVER_DOES_NOT_EXIST + .args(Component.text(serverName))); return; } @@ -78,19 +78,23 @@ public class ServerCommand implements SimpleCommand { private void outputServerInformation(Player executor) { String currentServer = executor.connectedServer().map(ServerConnection::serverInfo) .map(ServerInfo::name).orElse(""); - executor.sendMessage(Identity.nil(), Component.text( - "You are currently connected to " + currentServer + ".", NamedTextColor.YELLOW)); + executor.sendMessage(Identity.nil(), Component.translatable( + "velocity.command.server-current-server", + NamedTextColor.YELLOW, + Component.text(currentServer))); List servers = BuiltinCommandUtil.sortedServerList(server); if (servers.size() > MAX_SERVERS_TO_LIST) { - executor.sendMessage(Identity.nil(), Component.text( - "Too many servers to list. Tab-complete to show all servers.", NamedTextColor.RED)); + executor.sendMessage(Identity.nil(), Component.translatable( + "velocity.command.server-too-many", NamedTextColor.RED)); return; } // Assemble the list of servers as components - TextComponent.Builder serverListBuilder = Component.text().content("Available servers: ") - .color(NamedTextColor.YELLOW); + TextComponent.Builder serverListBuilder = Component.text() + .append(Component.translatable("velocity.command.server-available", + NamedTextColor.YELLOW)) + .append(Component.space()); for (int i = 0; i < servers.size(); i++) { RegisteredServer rs = servers.get(i); serverListBuilder.append(formatServerComponent(currentServer, rs)); @@ -106,17 +110,31 @@ public class ServerCommand implements SimpleCommand { ServerInfo serverInfo = server.serverInfo(); TextComponent serverTextComponent = Component.text(serverInfo.name()); - String playersText = server.connectedPlayers().size() + " player(s) online"; + int connectedPlayers = server.connectedPlayers().size(); + TranslatableComponent playersTextComponent; + if (connectedPlayers == 1) { + playersTextComponent = Component.translatable("velocity.command.server-tooltip-player-online"); + } else { + playersTextComponent = Component.translatable("velocity.command.server-tooltip-players-online"); + } + playersTextComponent = playersTextComponent.args(Component.text(connectedPlayers)); + if (serverInfo.name().equals(currentPlayerServer)) { serverTextComponent = serverTextComponent.color(NamedTextColor.GREEN) .hoverEvent( - showText(Component.text("Currently connected to this server\n" + playersText)) + showText( + Component.translatable("velocity.command.server-tooltip-current-server") + .append(Component.newline()) + .append(playersTextComponent)) ); } else { serverTextComponent = serverTextComponent.color(NamedTextColor.GRAY) .clickEvent(ClickEvent.runCommand("/server " + serverInfo.name())) .hoverEvent( - showText(Component.text("Click to connect to this server\n" + playersText)) + showText( + Component.translatable("velocity.command.server-tooltip-offer-connect-server") + .append(Component.newline()) + .append(playersTextComponent)) ); } return serverTextComponent; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java index e7a579727..c2fee2570 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java @@ -17,7 +17,6 @@ package com.velocitypowered.proxy.command.builtin; -import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -49,6 +48,7 @@ import java.util.stream.Collectors; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -180,17 +180,15 @@ public class VelocityCommand implements SimpleCommand { public void execute(CommandSource source, String @NonNull [] args) { try { if (server.reloadConfiguration()) { - source.sendMessage(Identity.nil(), Component.text( - "Configuration reloaded.", NamedTextColor.GREEN)); + source.sendMessage(Component.translatable("velocity.command.reload-success", + NamedTextColor.GREEN)); } else { - source.sendMessage(Identity.nil(), Component.text( - "Unable to reload your configuration. Check the console for more details.", + source.sendMessage(Component.translatable("velocity.command.reload-failure", NamedTextColor.RED)); } } catch (Exception e) { logger.error("Unable to reload configuration", e); - source.sendMessage(Identity.nil(), Component.text( - "Unable to reload your configuration. Check the console for more details.", + source.sendMessage(Component.translatable("velocity.command.reload-failure", NamedTextColor.RED)); } } @@ -219,33 +217,35 @@ public class VelocityCommand implements SimpleCommand { ProxyVersion version = server.version(); - TextComponent velocity = Component.text().content(version.getName() + " ") + Component velocity = Component.text().content(version.getName() + " ") .decoration(TextDecoration.BOLD, true) .color(VELOCITY_COLOR) .append(Component.text(version.getVersion()).decoration(TextDecoration.BOLD, false)) .build(); - TextComponent copyright = Component - .text("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName() - + " is licensed under the terms of the GNU General Public License v3."); + Component copyright = Component + .translatable("velocity.command.version-copyright", + Component.text(version.getVendor()), + Component.text(version.getName())); source.sendMessage(Identity.nil(), velocity); source.sendMessage(Identity.nil(), copyright); if (version.getName().equals("Velocity")) { - TextComponent velocityWebsite = Component.text() - .content("Visit the ") - .append(Component.text().content("Velocity website") + TextComponent embellishment = Component.text() + .append(Component.text().content("velocitypowered.com") .color(NamedTextColor.GREEN) + .decoration(TextDecoration.UNDERLINED, true) .clickEvent( ClickEvent.openUrl("https://www.velocitypowered.com")) .build()) - .append(Component.text(" or the ")) - .append(Component.text().content("Velocity GitHub") + .append(Component.text(" - ")) + .append(Component.text().content("GitHub") .color(NamedTextColor.GREEN) + .decoration(TextDecoration.UNDERLINED, true) .clickEvent(ClickEvent.openUrl( "https://github.com/VelocityPowered/Velocity")) .build()) .build(); - source.sendMessage(Identity.nil(), velocityWebsite); + source.sendMessage(Identity.nil(), embellishment); } } @@ -274,12 +274,13 @@ public class VelocityCommand implements SimpleCommand { int pluginCount = plugins.size(); if (pluginCount == 0) { - source.sendMessage(Identity.nil(), Component.text( - "No plugins installed.", NamedTextColor.YELLOW)); + source.sendMessage(Component.translatable("velocity.command.no-plugins", + NamedTextColor.YELLOW)); return; } - TextComponent.Builder output = Component.text().content("Plugins: ") + TranslatableComponent.Builder output = Component.translatable() + .key("velocity.command.plugins-list") .color(NamedTextColor.YELLOW); for (int i = 0; i < pluginCount; i++) { PluginContainer plugin = plugins.get(i); @@ -300,15 +301,21 @@ public class VelocityCommand implements SimpleCommand { description.url().ifPresent(url -> { hoverText.append(Component.newline()); - hoverText.append(Component.text("Website: " + url)); + hoverText.append(Component.translatable( + "velocity.command.plugin-tooltip-website", + Component.text(url))); }); if (!description.authors().isEmpty()) { hoverText.append(Component.newline()); if (description.authors().size() == 1) { - hoverText.append(Component.text("Author: " + description.authors().get(0))); + hoverText.append(Component.translatable("velocity.command.plugin-tooltip-author", + Component.text(description.authors().get(0)))); } else { - hoverText.append(Component.text("Authors: " + Joiner.on(", ") - .join(description.authors()))); + hoverText.append( + Component.translatable("velocity.command.plugin-tooltip-author", + Component.text(String.join(", ", description.authors())) + ) + ); } } description.description().ifPresent(pdesc -> { @@ -352,8 +359,8 @@ public class VelocityCommand implements SimpleCommand { JsonArray connectOrder = new JsonArray(); List attemptedConnectionOrder = ImmutableList.copyOf( server.configuration().getAttemptConnectionOrder()); - for (int i = 0; i < attemptedConnectionOrder.size(); i++) { - connectOrder.add(attemptedConnectionOrder.get(i)); + for (String s : attemptedConnectionOrder) { + connectOrder.add(s); } JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.configuration()); @@ -446,7 +453,7 @@ public class VelocityCommand implements SimpleCommand { e.getCause().printStackTrace(); } catch (JsonParseException e) { source.sendMessage(Component.text() - .content("An error occurred on the Velocity-servers and the dump could not " + .content("An error occurred on the Velocity servers and the dump could not " + "be completed. Please contact the Velocity staff about this problem. " + "If you do, provide the details about this error from the Velocity " + "console or server log.") diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index 033dd188f..deefcd99b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -45,7 +45,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Random; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.apache.logging.log4j.LogManager; @@ -72,25 +71,23 @@ public class VelocityConfiguration implements ProxyConfig { @Expose private final Advanced advanced; @Expose private final Query query; private final Metrics metrics; - private final Messages messages; private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent; private @Nullable Favicon favicon; private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced, - Query query, Metrics metrics, Messages messages) { + Query query, Metrics metrics) { this.servers = servers; this.forcedHosts = forcedHosts; this.advanced = advanced; this.query = query; this.metrics = metrics; - this.messages = messages; } private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode, boolean preventClientProxyConnections, boolean announceForge, PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret, boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough, Servers servers, - ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics, Messages messages) { + ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics) { this.bind = bind; this.motd = motd; this.showMaxPlayers = showMaxPlayers; @@ -106,7 +103,6 @@ public class VelocityConfiguration implements ProxyConfig { this.advanced = advanced; this.query = query; this.metrics = metrics; - this.messages = messages; } /** @@ -373,10 +369,6 @@ public class VelocityConfiguration implements ProxyConfig { return advanced.isLogCommandExecutions(); } - public Messages getMessages() { - return messages; - } - @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -450,7 +442,6 @@ public class VelocityConfiguration implements ProxyConfig { CommentedConfig advancedConfig = config.get("advanced"); CommentedConfig queryConfig = config.get("query"); CommentedConfig metricsConfig = config.get("metrics"); - CommentedConfig messagesConfig = config.get("messages"); PlayerInfoForwarding forwardingMode = config.getEnumOrElse("player-info-forwarding-mode", PlayerInfoForwarding.NONE); PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough", @@ -480,8 +471,7 @@ public class VelocityConfiguration implements ProxyConfig { new ForcedHosts(forcedHostsConfig), new Advanced(advancedConfig), new Query(queryConfig), - new Metrics(metricsConfig), - new Messages(messagesConfig, defaultConfig.get("messages")) + new Metrics(metricsConfig) ); } @@ -793,73 +783,4 @@ public class VelocityConfiguration implements ProxyConfig { return enabled; } } - - public static class Messages { - - private final CommentedConfig toml; - private final CommentedConfig defaultToml; - - private final String kickPrefix; - private final String disconnectPrefix; - private final String onlineModeOnly; - private final String noAvailableServers; - private final String alreadyConnected; - private final String movedToNewServerPrefix; - private final String genericConnectionError; - - private Messages(CommentedConfig toml, CommentedConfig defaultToml) { - this.toml = toml; - this.defaultToml = defaultToml; - this.kickPrefix = getString("kick-prefix"); - this.disconnectPrefix = getString("disconnect-prefix"); - this.onlineModeOnly = getString("online-mode-only"); - this.noAvailableServers = getString("no-available-servers"); - this.alreadyConnected = getString("already-connected"); - this.movedToNewServerPrefix = getString("moved-to-new-server-prefix"); - this.genericConnectionError = getString("generic-connection-error"); - } - - private String getString(String path) { - String def = defaultToml.getOrElse(path, ""); - if (toml == null) { - return def; - } - return toml.getOrElse(path, def); - } - - public Component getKickPrefix(String server) { - return deserialize(String.format(kickPrefix, server)); - } - - public Component getDisconnectPrefix(String server) { - return deserialize(String.format(disconnectPrefix, server)); - } - - public Component getOnlineModeOnly() { - return deserialize(onlineModeOnly); - } - - public Component getNoAvailableServers() { - return deserialize(noAvailableServers); - } - - public Component getAlreadyConnected() { - return deserialize(alreadyConnected); - } - - public Component getMovedToNewServerPrefix() { - return deserialize(movedToNewServerPrefix); - } - - public Component getGenericConnectionError() { - return deserialize(genericConnectionError); - } - - private Component deserialize(String str) { - if (str.startsWith("{")) { - return GsonComponentSerializer.gson().deserialize(str); - } - return LegacyComponentSerializer.legacyAmpersand().deserialize(str); - } - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index 6cdac9475..a06fa6600 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -50,8 +50,8 @@ import net.kyori.adventure.text.TextComponent; public class LoginSessionHandler implements MinecraftSessionHandler { - private static final TextComponent MODERN_IP_FORWARDING_FAILURE = Component - .text("Your server did not send a forwarding request to the proxy. Is it set up correctly?"); + private static final Component MODERN_IP_FORWARDING_FAILURE = Component + .translatable("velocity.error.modern-forwarding-failed"); private final VelocityServer server; private final VelocityServerConnection serverConn; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index ae6e48d1a..3840f0b3c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -168,9 +168,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { .exceptionally(e -> { logger.info("Exception occurred while running command for {}", player.username(), e); - player.sendMessage(Identity.nil(), - Component.text("An error occurred while running this command.", - NamedTextColor.RED)); + player.sendMessage(Component.translatable("velocity.command.generic-error", + NamedTextColor.RED)); return null; }); } else { @@ -335,7 +334,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { @Override public void exception(Throwable throwable) { - player.disconnect(server.configuration().getMessages().getGenericConnectionError()); + player.disconnect(Component.translatable("velocity.error.player-connection-error", + NamedTextColor.RED)); } @Override 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 8de648b6a..5e432c164 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 @@ -446,18 +446,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { wrapped = cause; } } - String userMessage; + + Component friendlyError; if (connectedServer != null && connectedServer.serverInfo().equals(server.serverInfo())) { - userMessage = "Your connection to " + server.serverInfo().name() + " encountered an " - + "error."; + friendlyError = Component.translatable("velocity.error.connected-server-error", + Component.text(server.serverInfo().name())); } else { logger.error("{}: unable to connect to server {}", this, server.serverInfo().name(), wrapped); - userMessage = "Unable to connect to " + server.serverInfo().name() + ". Try again " - + "later."; + friendlyError = Component.translatable("velocity.error.connecting-server-error", + Component.text(server.serverInfo().name())); } - handleConnectionException(server, null, Component.text(userMessage, - NamedTextColor.RED), safe); + handleConnectionException(server, null, friendlyError.color(NamedTextColor.RED), safe); } /** @@ -473,26 +473,22 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return; } - VelocityConfiguration.Messages messages = this.server.configuration().getMessages(); Component disconnectReason = GsonComponentSerializer.gson().deserialize(disconnect.getReason()); String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason); if (connectedServer != null && connectedServer.serverInfo().equals(server.serverInfo())) { logger.error("{}: kicked from server {}: {}", this, server.serverInfo().name(), plainTextReason); - handleConnectionException(server, disconnectReason, Component.text() - .append(messages.getKickPrefix(server.serverInfo().name()) - .colorIfAbsent(NamedTextColor.RED)) - .color(NamedTextColor.RED) - .append(disconnectReason) - .build(), safe); + handleConnectionException(server, disconnectReason, + Component.translatable("velocity.error.moved-to-new-server", NamedTextColor.RED, + Component.text(server.serverInfo().name()), + disconnectReason), safe); } else { logger.error("{}: disconnected while connecting to {}: {}", this, server.serverInfo().name(), plainTextReason); - handleConnectionException(server, disconnectReason, Component.text() - .append(messages.getDisconnectPrefix(server.serverInfo().name()) - .colorIfAbsent(NamedTextColor.RED)) - .append(disconnectReason) - .build(), safe); + handleConnectionException(server, disconnectReason, + Component.translatable("velocity.error.cant-connect", NamedTextColor.RED, + Component.text(server.serverInfo().name()), + disconnectReason), safe); } } @@ -576,8 +572,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { protocolVersion()), ((Impl) status).isSafe()); break; case SUCCESS: - sendMessage(Identity.nil(), server.configuration().getMessages() - .getMovedToNewServerPrefix().append(friendlyReason)); + sendMessage(Component.translatable("velocity.error.moved-to-new-server", + NamedTextColor.RED, + Component.text(originalEvent.server().serverInfo().name()), + friendlyReason)); break; default: // The only remaining value is successful (no need to do anything!) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java index bfe109eaf..8d9fbf669 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java @@ -70,7 +70,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(LegacyHandshakePacket packet) { connection.closeWith(LegacyDisconnectPacket - .from(Component.text("Your client is old, please upgrade!", NamedTextColor.RED))); + .from(Component.text( + "Your client is extremely old. Please update to a newer version of Minecraft.", + NamedTextColor.RED) + )); return true; } @@ -122,7 +125,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { InetAddress address = ((InetSocketAddress) connection.getRemoteAddress()).getAddress(); if (!server.getIpAttemptLimiter().attempt(address)) { - ic.disconnectQuietly(Component.text("You are logging in too fast, try again later.")); + ic.disconnectQuietly(Component.translatable("velocity.error.logging-in-too-fast")); return; } @@ -132,7 +135,8 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { // and lower, otherwise IP information will never get forwarded. if (server.configuration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && handshake.getProtocolVersion().lt(ProtocolVersion.MINECRAFT_1_13)) { - ic.disconnectQuietly(Component.text("This server is only compatible with 1.13 and above.")); + ic.disconnectQuietly(Component.translatable( + "velocity.error.modern-forwarding-needs-new-client")); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 008644ff3..4acde610d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -65,6 +65,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.translation.GlobalTranslator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -152,7 +153,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { GameProfile.class), true); } else if (profileResponse.getStatusCode() == 204) { // Apparently an offline-mode user logged onto this online-mode proxy. - inbound.disconnect(server.configuration().getMessages().getOnlineModeOnly()); + inbound.disconnect(Component.translatable("velocity.error.online-mode-only", + NamedTextColor.RED)); } else { // Something else went wrong logger.error( @@ -192,9 +194,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { Optional disconnectReason = result.denialReason(); if (disconnectReason.isPresent()) { // The component is guaranteed to be provided if the connection was denied. - Component disconnectReasonTranslated = GlobalTranslator.render(disconnectReason.get(), - Locale.getDefault()); - inbound.disconnect(disconnectReasonTranslated); + inbound.disconnect(disconnectReason.get()); return; } @@ -243,7 +243,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { mcConnection, inbound.connectedHost().orElse(null), onlineMode); this.connectedPlayer = player; if (!server.canRegisterConnection(player)) { - player.disconnect0(server.configuration().getMessages().getAlreadyConnected(), true); + player.disconnect0(Component.translatable("velocity.error.already-connected-proxy", + NamedTextColor.RED), true); return CompletableFuture.completedFuture(null); } @@ -304,8 +305,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { player.disconnect0(reason.get(), true); } else { if (!server.registerConnection(player)) { - player.disconnect0(server.configuration().getMessages() - .getAlreadyConnected(), true); + player.disconnect0(Component.translatable("velocity.error.already-connected-proxy"), + true); return; } @@ -333,8 +334,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { .thenRunAsync(() -> { Optional toTry = event.initialServer(); if (!toTry.isPresent()) { - player.disconnect0(server.configuration().getMessages() - .getNoAvailableServers(), true); + player.disconnect0(Component.translatable("velocity.error.no-available-servers", + NamedTextColor.RED), true); return; } player.createConnectionRequest(toTry.get()).fireAndForget(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java index bbb6c2daf..5e29672ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionMessages.java @@ -19,16 +19,17 @@ package com.velocitypowered.proxy.connection.util; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.format.NamedTextColor; public class ConnectionMessages { - public static final TextComponent ALREADY_CONNECTED = Component - .text("You are already connected to this server!", NamedTextColor.RED); - public static final TextComponent IN_PROGRESS = Component - .text("You are already connecting to a server!", NamedTextColor.RED); - public static final TextComponent INTERNAL_SERVER_CONNECTION_ERROR = Component - .text("An internal server connection error occurred.", NamedTextColor.RED); + public static final TranslatableComponent ALREADY_CONNECTED = Component + .translatable("velocity.error.already-connected", NamedTextColor.RED); + public static final TranslatableComponent IN_PROGRESS = Component + .translatable("velocity.error.already-connecting", NamedTextColor.RED); + public static final TranslatableComponent INTERNAL_SERVER_CONNECTION_ERROR = Component + .translatable("velocity.error.internal-server-connection-error", NamedTextColor.RED); private ConnectionMessages() { throw new AssertionError(); 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 80e4e3ea6..d3138a60e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -26,9 +26,12 @@ import com.velocitypowered.api.permission.PermissionFunction; import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.proxy.VelocityServer; import java.util.List; +import java.util.Locale; import net.kyori.adventure.identity.Identity; 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.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -52,8 +55,8 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons @Override public void sendMessage(@NonNull Identity identity, @NonNull Component message) { - logger.info(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection() - .serialize(message)); + Component translated = GlobalTranslator.render(message, Locale.getDefault()); + logger.info(LegacyComponentSerializer.legacySection().serialize(translated)); } @Override @@ -114,7 +117,8 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons protected void runCommand(String command) { try { if (!this.server.commandManager().execute(this, command).join()) { - sendMessage(Component.text("Command not found.", NamedTextColor.RED)); + sendMessage(Component.translatable("velocity.command.command-does-not-exist", + NamedTextColor.RED)); } } catch (Exception e) { logger.error("An error occurred while running this command.", e); diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/messages.properties b/proxy/src/main/resources/com/velocitypowered/proxy/messages.properties new file mode 100644 index 000000000..df291dc77 --- /dev/null +++ b/proxy/src/main/resources/com/velocitypowered/proxy/messages.properties @@ -0,0 +1,62 @@ +# +# Copyright (C) 2018 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 . +# + +velocity.error.already-connected=You are already connected to this server! +velocity.error.already-connected-proxy=You are already connected to this proxy! +velocity.error.already-connecting=You are already trying to connect to a server! +velocity.error.cant-connect=Unable to connect to {0}: {1} +velocity.error.connecting-server-error=Unable to connect you to {0}. Please try again later. +velocity.error.connected-server-error=Your connection to {0} encountered a problem. +velocity.error.internal-server-connection-error=An internal server connection error occurred. +velocity.error.logging-in-too-fast=You are logging in too fast, try again later. +velocity.error.online-mode-only=You are not logged into your Minecraft account. If you are logged into your Minecraft account, try restarting your Minecraft client. +velocity.error.player-connection-error=An internal error occurred in your connection. +velocity.error.modern-forwarding-needs-new-client=This server is only compatible with Minecraft 1.13 and above. +velocity.error.modern-forwarding-failed=Your server did not send a forwarding request to the proxy. Make sure the server is configured for Velocity forwarding. +velocity.error.moved-to-new-server=YOu were kicked from {0}: {1} +velocity.error.no-available-servers=There are no available servers to connect you to. Try again later or contact an admin. + +# Commands +velocity.command.generic-error=An error occurred while running this command. +velocity.command.command-does-not-exist=This command does not exist. + +velocity.command.players-only=Only players can run this command. +velocity.command.server-does-not-exist=The specified server {0} does not exist. + +velocity.command.server-current-server=You are currently connected to {0}. +velocity.command.server-too-many=There are too many servers set up. Use tab completion to get individual server counts. +velocity.command.server-available=Available servers: +velocity.command.server-tooltip-player-online={0} player online +velocity.command.server-tooltip-players-online={0} players online +velocity.command.server-tooltip-current-server=Currently connected to this server +velocity.command.server-tooltip-offer-connect-server=Click to connect to this server + +velocity.command.glist-player-singular=player +velocity.command.glist-player-plural=players +velocity.command.glist-total-suffix= are currently connected to the proxy. +velocity.command.glist-view-all=To view all players on servers, use /glist all. + +velocity.command.reload-success=Velocity configuration successfully reloaded. +velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details. + +velocity.command.version-copyright=Copyright 2018-2021 {0}. {1} is licensed under the terms of the GNU General Public License v3. + +velocity.command.no-plugins=There are no plugins currently installed. +velocity.command.plugins-list=Plugins: {0} +velocity.command.plugin-tooltip-website=Website: {0} +velocity.command.plugin-tooltip-author=Author: {0} +velocity.command.plugin-tooltip-authors=Authors: {0} \ No newline at end of file diff --git a/proxy/src/main/resources/default-velocity.toml b/proxy/src/main/resources/default-velocity.toml index fbad6626c..4a8447336 100644 --- a/proxy/src/main/resources/default-velocity.toml +++ b/proxy/src/main/resources/default-velocity.toml @@ -141,20 +141,4 @@ port = 25577 map = "Velocity" # Whether plugins should be shown in query response by default or not -show-plugins = false - -# Legacy color codes and JSON are accepted in all messages. -[messages] -# Prefix when the player gets kicked from a server. -# First argument '%s': the server name -kick-prefix = "&cKicked from %s: " - -# Prefix when the player is disconnected from a server. -# First argument '%s': the server name -disconnect-prefix = "&cCan't connect to %s: " - -online-mode-only = "&cThis server only accepts connections from online-mode clients.\n\n&7Did you change your username? Sign out of Minecraft, sign back in, and try again." -no-available-servers = "&cThere are no available servers." -already-connected = "&cYou are already connected to this proxy!" -moved-to-new-server-prefix = "&cThe server you were on kicked you: " -generic-connection-error = "&cAn internal error occurred in your connection." +show-plugins = false \ No newline at end of file