diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index eb1ee2f66..336b4437b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -54,10 +54,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyPair; -import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -73,10 +71,10 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.IntFunction; import java.util.stream.Collectors; import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.text.Component; -import net.kyori.text.TextComponent; -import net.kyori.text.TranslatableComponent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.AsyncHttpClient; @@ -92,14 +90,17 @@ public class VelocityServer implements ProxyServer { .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) .registerTypeHierarchyAdapter(GameProfile.class, new GameProfileSerializer()) .create(); - private static final Gson PRE_1_16_COMPONENT_SERIALIZER = - wrapAndAddTextSerializers(GsonComponentSerializer.colorDownsamplingGson()) - .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) - .create(); - private static final Gson POST_1_16_COMPONENT_SERIALIZER = - wrapAndAddTextSerializers(GsonComponentSerializer.gson()) - .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) - .create(); + private static final Gson PRE_1_16_PING_SERIALIZER = GsonComponentSerializer + .colorDownsamplingGson() + .serializer() + .newBuilder() + .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) + .create(); + private static final Gson POST_1_16_PING_SERIALIZER = GsonComponentSerializer.gson() + .serializer() + .newBuilder() + .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) + .create(); private final ConnectionManager cm; private final ProxyOptions options; @@ -614,13 +615,8 @@ public class VelocityServer implements ProxyServer { getAllPlayers().iterator()); } - public static Gson getGsonInstance(ProtocolVersion version) { - return version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0 ? POST_1_16_COMPONENT_SERIALIZER - : PRE_1_16_COMPONENT_SERIALIZER; - } - - private static GsonBuilder wrapAndAddTextSerializers(GsonComponentSerializer serializer) { - return net.kyori.text.serializer.gson.GsonComponentSerializer - .populate(serializer.serializer().newBuilder()); + public static Gson getPingGsonInstance(ProtocolVersion version) { + return version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0 ? POST_1_16_PING_SERIALIZER + : PRE_1_16_PING_SERIALIZER; } } 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 fcadbbf86..5dee998dc 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 @@ -43,8 +43,8 @@ import java.util.Optional; import java.util.Queue; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import net.kyori.text.TextComponent; -import net.kyori.text.format.TextColor; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -134,7 +134,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { player.getUsername(), e); player.sendMessage( TextComponent.of("An error occurred while running this command.", - TextColor.RED)); + NamedTextColor.RED)); return null; }); } else { @@ -272,7 +272,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { @Override public void exception(Throwable throwable) { player.disconnect(TextComponent.of("Your connection has encountered an error. Try again later.", - TextColor.RED)); + 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 b1212a04b..ff0b71117 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 @@ -4,8 +4,6 @@ import static com.velocitypowered.proxy.connection.util.ConnectionRequestResults import static java.util.concurrent.CompletableFuture.completedFuture; import com.google.common.base.Preconditions; -import com.google.common.base.Verify; -import com.google.gson.Gson; import com.google.gson.JsonObject; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.player.KickedFromServerEvent; @@ -39,6 +37,7 @@ import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; +import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.Chat; import com.velocitypowered.proxy.protocol.packet.ClientSettings; @@ -69,7 +68,6 @@ 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; -import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; @@ -266,7 +264,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { // We can use the title packet instead. TitlePacket pkt = new TitlePacket(); pkt.setAction(TitlePacket.SET_ACTION_BAR); - pkt.setComponent(VelocityServer.getGsonInstance(this.getProtocolVersion()).toJson(message)); + pkt.setComponent(ProtocolUtils.getJsonChatSerializer(this.getProtocolVersion()) + .serialize(message)); connection.write(pkt); } else { // Due to issues with action bar packets, we'll need to convert the text message into a @@ -282,16 +281,16 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public void showTitle(net.kyori.adventure.title.@NonNull Title title) { - Gson gson = VelocityServer.getGsonInstance(this.getProtocolVersion()); + GsonComponentSerializer serializer = ProtocolUtils.getJsonChatSerializer(this.getProtocolVersion()); TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_TITLE); - titlePkt.setComponent(gson.toJson(title.title())); + titlePkt.setComponent(serializer.serialize(title.title())); connection.delayedWrite(titlePkt); TitlePacket subtitlePkt = new TitlePacket(); subtitlePkt.setAction(TitlePacket.SET_SUBTITLE); - subtitlePkt.setComponent(gson.toJson(title.subtitle())); + subtitlePkt.setComponent(serializer.serialize(title.subtitle())); connection.delayedWrite(titlePkt); TitlePacket timesPkt = TitlePacket.timesForProtocolVersion(this.getProtocolVersion()); @@ -394,7 +393,6 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { connection.write(TitlePacket.hideForProtocolVersion(protocolVersion)); } else if (title instanceof TextTitle) { TextTitle tt = (TextTitle) title; - Gson gson = VelocityServer.getGsonInstance(this.getProtocolVersion()); if (tt.isResetBeforeSend()) { connection.delayedWrite(TitlePacket.resetForProtocolVersion(protocolVersion)); @@ -404,7 +402,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (titleText.isPresent()) { TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_TITLE); - titlePkt.setComponent(gson.toJson(titleText.get())); + titlePkt.setComponent(net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE + .serialize(titleText.get())); connection.delayedWrite(titlePkt); } @@ -412,7 +411,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (subtitleText.isPresent()) { TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_SUBTITLE); - titlePkt.setComponent(gson.toJson(subtitleText.get())); + titlePkt.setComponent(net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE + .serialize(subtitleText.get())); connection.delayedWrite(titlePkt); } 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 c419acba0..5cc033224 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 @@ -124,8 +124,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { Response profileResponse = hasJoinedResponse.get(); if (profileResponse.getStatusCode() == 200) { // All went well, initialize the session. - initializePlayer(GENERAL_GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class), - true); + initializePlayer(GENERAL_GSON.fromJson(profileResponse.getResponseBody(), + GameProfile.class), true); } else if (profileResponse.getStatusCode() == 204) { // Apparently an offline-mode user logged onto this online-mode proxy. inbound.disconnect(VelocityMessages.ONLINE_MODE_ONLY); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java index 278fd7c4a..9557b7e81 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java @@ -173,7 +173,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler { .thenAcceptAsync( (event) -> { StringBuilder json = new StringBuilder(); - VelocityServer.getGsonInstance(connection.getProtocolVersion()) + VelocityServer.getPingGsonInstance(connection.getProtocolVersion()) .toJson(event.getPing(), json); connection.write(new StatusResponse(json)); }, diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java index 166d5f0ef..13cb8d0f6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/JavaPluginLoader.java @@ -117,8 +117,8 @@ public class JavaPluginLoader implements PluginLoader { while ((entry = in.getNextJarEntry()) != null) { if (entry.getName().equals("velocity-plugin.json")) { try (Reader pluginInfoReader = new InputStreamReader(in, StandardCharsets.UTF_8)) { - return Optional.of(VelocityServer.GENERAL_GSON - .fromJson(pluginInfoReader, SerializedPluginDescription.class)); + return Optional.of(VelocityServer.GENERAL_GSON.fromJson(pluginInfoReader, + SerializedPluginDescription.class)); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index ebb7b09df..7aa549d09 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.nbt.CompoundTag; import net.kyori.nbt.TagIO; import net.kyori.nbt.TagType; @@ -460,6 +461,20 @@ public enum ProtocolUtils { return readString(buf, DEFAULT_MAX_STRING_SIZE, buf.readableBytes()); } + /** + * Returns the appropriate {@link GsonComponentSerializer} for the given protocol version. This + * is used to constrain messages sent to older clients. + * + * @param version the protocol version used by the client. + * @return the appropriate {@link GsonComponentSerializer} + */ + public static GsonComponentSerializer getJsonChatSerializer(ProtocolVersion version) { + if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) { + return GsonComponentSerializer.gson(); + } + return GsonComponentSerializer.colorDownsamplingGson(); + } + public enum Direction { SERVERBOUND, CLIENTBOUND; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java index 62ab7c14b..fa4db7792 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Chat.java @@ -2,7 +2,6 @@ package com.velocitypowered.proxy.protocol.packet; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; @@ -116,7 +115,8 @@ public class Chat implements MinecraftPacket { public static Chat createClientbound(net.kyori.adventure.text.Component component, byte type, UUID sender, ProtocolVersion version) { Preconditions.checkNotNull(component, "component"); - return new Chat(VelocityServer.getGsonInstance(version).toJson(component), type, sender); + return new Chat(ProtocolUtils.getJsonChatSerializer(version).serialize(component), type, + sender); } public static Chat createServerbound(String message) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Disconnect.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Disconnect.java index 305fb851e..52d63cf5d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Disconnect.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Disconnect.java @@ -2,7 +2,6 @@ package com.velocitypowered.proxy.protocol.packet; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; @@ -67,6 +66,6 @@ public class Disconnect implements MinecraftPacket { public static Disconnect create(net.kyori.adventure.text.Component component, ProtocolVersion version) { Preconditions.checkNotNull(component, "component"); - return new Disconnect(VelocityServer.getGsonInstance(version).toJson(component)); + return new Disconnect(ProtocolUtils.getJsonChatSerializer(version).serialize(component)); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooter.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooter.java index 235dd1c4b..863cddb50 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooter.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HeaderAndFooter.java @@ -3,16 +3,12 @@ package com.velocitypowered.proxy.protocol.packet; import static com.velocitypowered.proxy.protocol.ProtocolUtils.writeString; import com.google.common.base.Preconditions; -import com.google.gson.Gson; import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -import net.kyori.text.Component; -import net.kyori.text.serializer.ComponentSerializer; -import net.kyori.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; public class HeaderAndFooter implements MinecraftPacket { @@ -55,15 +51,17 @@ public class HeaderAndFooter implements MinecraftPacket { return handler.handle(this); } - public static HeaderAndFooter create(Component header, Component footer) { - ComponentSerializer json = GsonComponentSerializer.INSTANCE; - return new HeaderAndFooter(json.serialize(header), json.serialize(footer)); + public static HeaderAndFooter create(net.kyori.text.Component header, + net.kyori.text.Component footer) { + return new HeaderAndFooter( + net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE.serialize(header), + net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE.serialize(footer)); } public static HeaderAndFooter create(net.kyori.adventure.text.Component header, net.kyori.adventure.text.Component footer, ProtocolVersion protocolVersion) { - Gson serializer = VelocityServer.getGsonInstance(protocolVersion); - return new HeaderAndFooter(serializer.toJson(header), serializer.toJson(footer)); + GsonComponentSerializer serializer = ProtocolUtils.getJsonChatSerializer(protocolVersion); + return new HeaderAndFooter(serializer.serialize(header), serializer.serialize(footer)); } public static HeaderAndFooter reset() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java index f7a7181f0..3c58c3196 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PlayerListItem.java @@ -147,7 +147,8 @@ public class PlayerListItem implements MinecraftPacket { ProtocolVersion version) { buf.writeBoolean(displayName != null); if (displayName != null) { - ProtocolUtils.writeString(buf, VelocityServer.getGsonInstance(version).toJson(displayName)); + ProtocolUtils.writeString(buf, ProtocolUtils.getJsonChatSerializer(version) + .serialize(displayName)); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java index 347fcc4b7..fb4110423 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java @@ -50,7 +50,7 @@ public class PingSessionHandler implements MinecraftSessionHandler { completed = true; connection.close(true); - ServerPing ping = VelocityServer.getGsonInstance(version).fromJson(packet.getStatus(), + ServerPing ping = VelocityServer.getPingGsonInstance(version).fromJson(packet.getStatus(), ServerPing.class); result.complete(ping); return true;