diff --git a/api/build.gradle b/api/build.gradle index 245165348..e96762a13 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -18,10 +18,18 @@ sourceSets { dependencies { compile 'com.google.code.gson:gson:2.8.5' compile "com.google.guava:guava:${guavaVersion}" + + // DEPRECATED: Will be removed in Velocity 2.0.0 compile "net.kyori:text-api:${textVersion}" compile "net.kyori:text-serializer-gson:${textVersion}" compile "net.kyori:text-serializer-legacy:${textVersion}" compile "net.kyori:text-serializer-plain:${textVersion}" + + compile "net.kyori:adventure-api:${adventureVersion}" + compile "net.kyori:adventure-text-serializer-gson:${adventureVersion}" + compile "net.kyori:adventure-text-serializer-legacy:${adventureVersion}" + compile "net.kyori:adventure-text-serializer-plain:${adventureVersion}" + compile 'com.moandjiezana.toml:toml4j:0.7.2' compile "org.slf4j:slf4j-api:${slf4jVersion}" compile 'com.google.inject:guice:4.2.3' diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java index f74a7fc2b..82f7beb0d 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java @@ -1,17 +1,20 @@ package com.velocitypowered.api.command; import com.velocitypowered.api.permission.PermissionSubject; -import net.kyori.text.Component; +import com.velocitypowered.api.proxy.ProxyAudience; +import net.kyori.adventure.text.Component; /** * Represents something that can be used to run a {@link Command}. */ -public interface CommandSource extends PermissionSubject { +public interface CommandSource extends PermissionSubject, ProxyAudience { /** * Sends the specified {@code component} to the invoker. * * @param component the text component to send + * @deprecated Use {@link #sendMessage(Component)} instead */ - void sendMessage(Component component); + @Deprecated + void sendMessage(net.kyori.text.Component component); } diff --git a/api/src/main/java/com/velocitypowered/api/event/ResultedEvent.java b/api/src/main/java/com/velocitypowered/api/event/ResultedEvent.java index 3b25dcfc9..22e6fc230 100644 --- a/api/src/main/java/com/velocitypowered/api/event/ResultedEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/ResultedEvent.java @@ -1,9 +1,10 @@ package com.velocitypowered.api.event; import com.google.common.base.Preconditions; +import com.velocitypowered.api.util.AdventureCompat; import java.util.Optional; -import net.kyori.text.Component; -import net.kyori.text.serializer.plain.PlainComponentSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -92,7 +93,11 @@ public interface ResultedEvent { return status; } - public Optional getReason() { + public Optional getReason() { + return Optional.ofNullable(reason).map(AdventureCompat::asOriginalTextComponent); + } + + public Optional getReasonComponent() { return Optional.ofNullable(reason); } @@ -115,5 +120,11 @@ public interface ResultedEvent { Preconditions.checkNotNull(reason, "reason"); return new ComponentResult(false, reason); } + + @Deprecated + public static ComponentResult denied(net.kyori.text.Component reason) { + Preconditions.checkNotNull(reason, "reason"); + return new ComponentResult(false, AdventureCompat.asAdventureComponent(reason)); + } } } diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/PreLoginEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/PreLoginEvent.java index f9773d09a..aa714bfdb 100644 --- a/api/src/main/java/com/velocitypowered/api/event/connection/PreLoginEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/connection/PreLoginEvent.java @@ -3,8 +3,8 @@ package com.velocitypowered.api.event.connection; import com.google.common.base.Preconditions; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.util.AdventureCompat; import java.util.Optional; -import net.kyori.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -71,9 +71,10 @@ public final class PreLoginEvent implements ResultedEvent getReason() { + @Deprecated + public Optional getReason() { + return Optional.ofNullable(reason).map(AdventureCompat::asOriginalTextComponent); + } + + public Optional getReasonComponent() { return Optional.ofNullable(reason); } @@ -143,9 +149,23 @@ public final class PreLoginEvent implements ResultedEvent getOriginalReason() { + return Optional.ofNullable(originalReason).map(AdventureCompat::asOriginalTextComponent); + } + + public Optional getServerKickReason() { return Optional.ofNullable(originalReason); } @@ -115,9 +137,9 @@ public final class KickedFromServerEvent implements */ public static final class DisconnectPlayer implements ServerKickResult { - private final Component component; + private final net.kyori.adventure.text.Component component; - private DisconnectPlayer(Component component) { + private DisconnectPlayer(net.kyori.adventure.text.Component component) { this.component = Preconditions.checkNotNull(component, "component"); } @@ -126,7 +148,12 @@ public final class KickedFromServerEvent implements return true; } + @Deprecated public Component getReason() { + return AdventureCompat.asOriginalTextComponent(component); + } + + public net.kyori.adventure.text.Component getReasonComponent() { return component; } @@ -135,8 +162,20 @@ public final class KickedFromServerEvent implements * * @param reason the reason to use when disconnecting the player * @return the disconnect result + * @deprecated Use {@link #create(net.kyori.adventure.text.Component)} instead */ + @Deprecated public static DisconnectPlayer create(Component reason) { + return new DisconnectPlayer(AdventureCompat.asAdventureComponent(reason)); + } + + /** + * Creates a new {@link DisconnectPlayer} with the specified reason. + * + * @param reason the reason to use when disconnecting the player + * @return the disconnect result + */ + public static DisconnectPlayer create(net.kyori.adventure.text.Component reason) { return new DisconnectPlayer(reason); } } @@ -147,10 +186,10 @@ public final class KickedFromServerEvent implements */ public static final class RedirectPlayer implements ServerKickResult { - private final Component message; + private final net.kyori.adventure.text.Component message; private final RegisteredServer server; - private RedirectPlayer(RegisteredServer server, @Nullable Component message) { + private RedirectPlayer(RegisteredServer server, net.kyori.adventure.text.Component message) { this.server = Preconditions.checkNotNull(server, "server"); this.message = message; } @@ -164,8 +203,12 @@ public final class KickedFromServerEvent implements return server; } - @Nullable + @Deprecated public Component getMessage() { + return AdventureCompat.asOriginalTextComponent(message); + } + + public net.kyori.adventure.text.Component getMessageComponent() { return message; } @@ -174,13 +217,29 @@ public final class KickedFromServerEvent implements * * @param server the server to send the player to * @return the redirect result + * @deprecated Use {@link #create(RegisteredServer, net.kyori.adventure.text.Component)} */ - public static RedirectPlayer create(RegisteredServer server, @Nullable Component message) { + @Deprecated + public static RedirectPlayer create(RegisteredServer server, net.kyori.text.Component message) { + if (message == null) { + return new RedirectPlayer(server, null); + } + return new RedirectPlayer(server, AdventureCompat.asAdventureComponent(message)); + } + + /** + * Creates a new redirect result to forward the player to the specified {@code server}. + * + * @param server the server to send the player to + * @return the redirect result + */ + public static RedirectPlayer create(RegisteredServer server, + net.kyori.adventure.text.Component message) { return new RedirectPlayer(server, message); } public static ServerKickResult create(RegisteredServer server) { - return create(server, null); + return new RedirectPlayer(server, null); } } @@ -191,9 +250,10 @@ public final class KickedFromServerEvent implements */ public static final class Notify implements ServerKickResult { - private final Component message; + private final net.kyori.adventure.text.Component message; - private Notify(Component message) { + @Deprecated + private Notify(net.kyori.adventure.text.Component message) { this.message = Preconditions.checkNotNull(message, "message"); } @@ -202,7 +262,13 @@ public final class KickedFromServerEvent implements return false; } + @Deprecated public Component getMessage() { + return AdventureCompat.asOriginalTextComponent(message); + } + + @Deprecated + public net.kyori.adventure.text.Component getMessageComponent() { return message; } @@ -211,8 +277,20 @@ public final class KickedFromServerEvent implements * * @param message the server to send the player to * @return the redirect result + * @deprecated Use {@link #create(net.kyori.adventure.text.Component)} instead */ + @Deprecated public static Notify create(Component message) { + return new Notify(AdventureCompat.asAdventureComponent(message)); + } + + /** + * Notifies the player with the specified message but does nothing else. + * + * @param message the server to send the player to + * @return the redirect result + */ + public static Notify create(net.kyori.adventure.text.Component message) { return new Notify(message); } } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ConnectionRequestBuilder.java b/api/src/main/java/com/velocitypowered/api/proxy/ConnectionRequestBuilder.java index 40728d67f..b163b2646 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ConnectionRequestBuilder.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ConnectionRequestBuilder.java @@ -67,9 +67,18 @@ public interface ConnectionRequestBuilder { * Returns an (optional) textual reason for the failure to connect to the server. * * @return the reason why the user could not connect to the server + * @deprecated Use {@link #getReasonComponent()} instead */ + @Deprecated Optional getReason(); + /** + * Returns an (optional) textual reason for the failure to connect to the server. + * + * @return the reason why the user could not connect to the server + */ + Optional getReasonComponent(); + /** * Returns the server we actually tried to connect to. * diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index b2ef61409..dce8a5c4b 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -14,7 +14,7 @@ import com.velocitypowered.api.util.title.Title; import java.util.List; import java.util.Optional; import java.util.UUID; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; /** * Represents a player who is connected to the proxy. @@ -76,9 +76,11 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage * Sends a chat message to the player's client. * * @param component the chat message to send + * @deprecated Use {@link #sendMessage(net.kyori.adventure.text.Component)} */ + @Deprecated @Override - default void sendMessage(Component component) { + default void sendMessage(net.kyori.text.Component component) { sendMessage(component, MessagePosition.CHAT); } @@ -87,8 +89,11 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage * * @param component the chat message to send * @param position the position for the message + * @deprecated Use @deprecated Use {@link #sendMessage(net.kyori.adventure.text.Component)} or + * {@link #sendActionBar(net.kyori.adventure.text.Component)} */ - void sendMessage(Component component, MessagePosition position); + @Deprecated + void sendMessage(net.kyori.text.Component component, MessagePosition position); /** * Creates a new connection request so that the player can connect to another server. @@ -127,7 +132,7 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage * @deprecated Use {@link TabList#setHeaderAndFooter(Component, Component)}. */ @Deprecated - void setHeaderAndFooter(Component header, Component footer); + void setHeaderAndFooter(net.kyori.text.Component header, net.kyori.text.Component footer); /** * Clears the tab list header and footer for the player. @@ -149,14 +154,26 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage * other {@link Player} methods will become undefined. * * @param reason component with the reason + * @deprecated Use {@link #disconnect(Component)} instead */ - void disconnect(Component reason); + @Deprecated + void disconnect(net.kyori.text.Component reason); + + /** + * Disconnects the player with the specified reason. Once this method is called, further calls to + * other {@link Player} methods will become undefined. + * + * @param reason component with the reason + */ + void disconnect(net.kyori.adventure.text.Component reason); /** * Sends the specified title to the client. * * @param title the title to send + * @deprecated Use {@link #showTitle(net.kyori.adventure.title.Title)} and {@link #resetTitle()} */ + @Deprecated void sendTitle(Title title); /** diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java new file mode 100644 index 000000000..6a64da885 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyAudience.java @@ -0,0 +1,71 @@ +package com.velocitypowered.api.proxy; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.sound.SoundStop; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Indicates an {@link Audience} that is on the proxy. This interface contains no-op default methods + * that are used to bridge compatibility issues with the new adventure API. This interface will go + * away in Velocity 2.0.0. + */ +public interface ProxyAudience extends Audience { + + @Override + void sendMessage(@NonNull Component message); + + @Override + default void sendActionBar(@NonNull Component message) { + + } + + @Override + default void showTitle(@NonNull Title title) { + + } + + @Override + default void clearTitle() { + + } + + @Override + default void resetTitle() { + + } + + @Override + default void showBossBar(@NonNull BossBar bar) { + + } + + @Override + default void hideBossBar(@NonNull BossBar bar) { + + } + + @Override + default void playSound(@NonNull Sound sound) { + + } + + @Override + default void playSound(@NonNull Sound sound, double x, double y, double z) { + + } + + @Override + default void stopSound(@NonNull SoundStop stop) { + + } + + @Override + default void openBook(@NonNull Book book) { + + } +} diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java index 3bb4f42b2..0c76500b8 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -17,13 +17,13 @@ import java.net.InetSocketAddress; import java.util.Collection; import java.util.Optional; import java.util.UUID; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; /** * Provides an interface to a Minecraft server proxy. */ -public interface ProxyServer { +public interface ProxyServer extends ProxyAudience { /** * Retrieves the player currently connected to this proxy by their Minecraft username. The search @@ -46,8 +46,10 @@ public interface ProxyServer { * Broadcasts a message to all players currently online. * * @param component the message to send + * @deprecated Use {@link #sendMessage(Component)} instead */ - void broadcast(Component component); + @Deprecated + void broadcast(net.kyori.text.Component component); /** * Retrieves all players currently connected to this proxy. This call may or may not be a snapshot @@ -186,8 +188,10 @@ public interface ProxyServer { * @param overlay boss bar overlay * @param progress boss bar progress * @return a completely new and fresh boss bar + * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar} instead */ + @Deprecated @NonNull - BossBar createBossBar(@NonNull Component title, @NonNull BossBarColor color, + BossBar createBossBar(net.kyori.text.Component title, @NonNull BossBarColor color, @NonNull BossBarOverlay overlay, float progress); } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java b/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java index 6e5de2371..3efa22c78 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/config/ProxyConfig.java @@ -5,7 +5,6 @@ import com.velocitypowered.api.util.Favicon; import java.util.List; import java.util.Map; import java.util.Optional; -import net.kyori.text.Component; /** * Exposes certain proxy configuration information that plugins may use. @@ -44,8 +43,17 @@ public interface ProxyConfig { * Get the MOTD component shown in the tab list. * * @return the motd component + * @deprecated Use {@link #getMotd()} instead */ - Component getMotdComponent(); + @Deprecated + net.kyori.text.Component getMotdComponent(); + + /** + * Get the MOTD component shown in the tab list. + * + * @return the motd component + */ + net.kyori.adventure.text.Component getMotd(); /** * Get the maximum players shown in the tab list. diff --git a/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java b/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java index 71bb47ba7..0b862147c 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java @@ -5,7 +5,7 @@ import com.velocitypowered.api.util.GameProfile; import java.util.Collection; import java.util.Optional; import java.util.UUID; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -13,6 +13,16 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ public interface TabList { + /** + * Sets the tab list header and footer for the player. + * + * @param header the header component + * @param footer the footer component + * @deprecated Use {@link #setHeaderAndFooter(Component, Component)} instead + */ + @Deprecated + void setHeaderAndFooter(net.kyori.text.Component header, net.kyori.text.Component footer); + /** * Sets the tab list header and footer for the player. * diff --git a/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java b/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java index 41af4e496..ea0de73f8 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java @@ -1,8 +1,9 @@ package com.velocitypowered.api.proxy.player; +import com.velocitypowered.api.util.AdventureCompat; import com.velocitypowered.api.util.GameProfile; import java.util.Optional; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -27,12 +28,35 @@ public interface TabListEntry { GameProfile getProfile(); /** - * Returns {@link Optional} text {@link Component}, which if present is the text displayed for - * {@code this} entry in the {@link TabList}, otherwise {@link GameProfile#getName()} is shown. + * Returns {@link Optional} text {@link net.kyori.text.Component}, which if present is the text + * displayed for {@code this} entry in the {@link TabList}, otherwise + * {@link GameProfile#getName()} is shown. + * + * @return {@link Optional} text {@link net.kyori.text.Component} of name displayed in the tab + * list * - * @return {@link Optional} text {@link Component} of name displayed in the tab list */ - Optional getDisplayName(); + Optional getDisplayName(); + + /** + * Returns {@link Optional} text {@link net.kyori.text.Component}, which if present is the text + * displayed for {@code this} entry in the {@link TabList}, otherwise + * {@link GameProfile#getName()} is shown. + * + * @return {@link Optional} text {@link net.kyori.text.Component} of name displayed in the tab + * list + */ + Optional getDisplayNameComponent(); + + /** + * Sets the text {@link net.kyori.text.Component} to be displayed for {@code this} + * {@link TabListEntry}. If {@code null}, {@link GameProfile#getName()} will be shown. + * + * @param displayName to show in the {@link TabList} for {@code this} entry + * @return {@code this}, for chaining + * @deprecated Use {@link #setDisplayName(Component)} instead + */ + TabListEntry setDisplayName(net.kyori.text.Component displayName); /** * Sets the text {@link Component} to be displayed for {@code this} {@link TabListEntry}. If @@ -149,6 +173,19 @@ public interface TabListEntry { * @param displayName to set * @return {@code this}, for chaining * @see TabListEntry#getDisplayName() + * @deprecated Use {@link #displayName(Component)} instead + */ + @Deprecated + public Builder displayName(net.kyori.text.Component displayName) { + return displayName(AdventureCompat.asAdventureComponent(displayName)); + } + + /** + * Sets the displayed name of the {@link TabListEntry}. + * + * @param displayName to set + * @return {@code this}, for chaining + * @see TabListEntry#getDisplayNameComponent() () */ public Builder displayName(@Nullable Component displayName) { this.displayName = displayName; diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/QueryResponse.java b/api/src/main/java/com/velocitypowered/api/proxy/server/QueryResponse.java index fab5f2fb2..566dd0659 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/server/QueryResponse.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/QueryResponse.java @@ -49,7 +49,7 @@ public final class QueryResponse { /** * Get hostname which will be used to reply to the query. By default it is {@link - * ProxyConfig#getMotdComponent()} in plain text without colour codes. + * ProxyConfig#getMotd()} in plain text without colour codes. * * @return hostname */ diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/RegisteredServer.java b/api/src/main/java/com/velocitypowered/api/proxy/server/RegisteredServer.java index 1ed7066b3..6451a47b6 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/server/RegisteredServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/RegisteredServer.java @@ -1,14 +1,17 @@ package com.velocitypowered.api.proxy.server; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyAudience; import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import java.util.Collection; import java.util.concurrent.CompletableFuture; /** - * Represents a server that has been registered with the proxy. + * Represents a server that has been registered with the proxy. The {@code Audience} associated with + * a {@code RegisteredServer} represent all players on the server connected to this proxy and do not + * interact with the server in any way. */ -public interface RegisteredServer extends ChannelMessageSink { +public interface RegisteredServer extends ChannelMessageSink, ProxyAudience { /** * Returns the {@link ServerInfo} for this server. diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java index 7049c3c30..9b78f3554 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java @@ -3,6 +3,7 @@ package com.velocitypowered.api.proxy.server; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.velocitypowered.api.util.AdventureCompat; import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.ModInfo; import java.util.ArrayList; @@ -12,7 +13,6 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; -import net.kyori.text.Component; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -22,12 +22,19 @@ public final class ServerPing { private final Version version; private final @Nullable Players players; - private final Component description; + private final net.kyori.adventure.text.Component description; private final @Nullable Favicon favicon; private final @Nullable ModInfo modinfo; - public ServerPing(Version version, @Nullable Players players, Component description, - @Nullable Favicon favicon) { + @Deprecated + public ServerPing(Version version, @Nullable Players players, + net.kyori.text.Component description, @Nullable Favicon favicon) { + this(version, players, AdventureCompat.asAdventureComponent(description), favicon, + ModInfo.DEFAULT); + } + + public ServerPing(Version version, @Nullable Players players, + net.kyori.adventure.text.Component description, @Nullable Favicon favicon) { this(version, players, description, favicon, ModInfo.DEFAULT); } @@ -40,8 +47,25 @@ public final class ServerPing { * @param favicon the server's favicon * @param modinfo the mods this server runs */ - public ServerPing(Version version, @Nullable Players players, Component description, - @Nullable Favicon favicon, @Nullable ModInfo modinfo) { + @Deprecated + public ServerPing(Version version, @Nullable Players players, + net.kyori.text.Component description, @Nullable Favicon favicon, + @Nullable ModInfo modinfo) { + this(version, players, AdventureCompat.asAdventureComponent(description), favicon, modinfo); + } + + /** + * Constructs a ServerPing instance. + * + * @param version the version of the server + * @param players the players on the server + * @param description the MOTD for the server + * @param favicon the server's favicon + * @param modinfo the mods this server runs + */ + public ServerPing(Version version, @Nullable Players players, + net.kyori.adventure.text.Component description, @Nullable Favicon favicon, + @Nullable ModInfo modinfo) { this.version = Preconditions.checkNotNull(version, "version"); this.players = players; this.description = Preconditions.checkNotNull(description, "description"); @@ -57,7 +81,12 @@ public final class ServerPing { return Optional.ofNullable(players); } - public Component getDescription() { + @Deprecated + public net.kyori.text.Component getDescription() { + return AdventureCompat.asOriginalTextComponent(description); + } + + public net.kyori.adventure.text.Component getDescriptionComponent() { return description; } @@ -143,7 +172,7 @@ public final class ServerPing { private final List samplePlayers = new ArrayList<>(); private String modType = "FML"; private final List mods = new ArrayList<>(); - private @Nullable Component description; + private net.kyori.adventure.text.Component description; private @Nullable Favicon favicon; private boolean nullOutPlayers; private boolean nullOutModinfo; @@ -215,7 +244,13 @@ public final class ServerPing { return this; } - public Builder description(Component description) { + @Deprecated + public Builder description(net.kyori.text.Component description) { + this.description(AdventureCompat.asAdventureComponent(description)); + return this; + } + + public Builder description(net.kyori.adventure.text.Component description) { this.description = Preconditions.checkNotNull(description, "description"); return this; } @@ -258,7 +293,11 @@ public final class ServerPing { return samplePlayers; } - public Optional getDescription() { + public Optional getDescription() { + return Optional.ofNullable(description).map(AdventureCompat::asOriginalTextComponent); + } + + public Optional getDescriptionComponent() { return Optional.ofNullable(description); } diff --git a/api/src/main/java/com/velocitypowered/api/util/AdventureCompat.java b/api/src/main/java/com/velocitypowered/api/util/AdventureCompat.java new file mode 100644 index 000000000..ef775ded1 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/AdventureCompat.java @@ -0,0 +1,29 @@ +package com.velocitypowered.api.util; + +/** + * Utilities to convert from adventure {@link net.kyori.adventure.text.Component}s to text + * {@link net.kyori.text.Component}s and vice versa. + * + * @deprecated Provided only as a transitional aid, will be removed in Velocity 2.0.0 + */ +@Deprecated +public class AdventureCompat { + private AdventureCompat() { + throw new AssertionError("Do not create instances of this class."); + } + + public static net.kyori.adventure.text.Component asAdventureComponent( + net.kyori.text.Component component) { + String json = net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE + .serialize(component); + return net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson() + .deserialize(json); + } + + public static net.kyori.text.Component asOriginalTextComponent( + net.kyori.adventure.text.Component component) { + String json = net.kyori.adventure.text.serializer.gson.GsonComponentSerializer + .colorDownsamplingGson().serialize(component); + return net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE.deserialize(json); + } +} diff --git a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBar.java b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBar.java index 65ebe3623..115652d4d 100644 --- a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBar.java +++ b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBar.java @@ -7,7 +7,10 @@ import net.kyori.text.Component; /** * Represents a boss bar, which can be send to a (group of) player(s). * Boss bars only work on 1.9 and above. + * + * @deprecated Replaced with {@link net.kyori.adventure.bossbar.BossBar} */ +@Deprecated public interface BossBar { /** diff --git a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarColor.java b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarColor.java index e31fdaadd..b5b38c8bb 100644 --- a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarColor.java +++ b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarColor.java @@ -2,7 +2,10 @@ package com.velocitypowered.api.util.bossbar; /** * Represents a color of a {@link BossBar}. + * + * @deprecated Replaced with {@link net.kyori.adventure.bossbar.BossBar.Color} */ +@Deprecated public enum BossBarColor { PINK, BLUE, diff --git a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarFlag.java b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarFlag.java index 1e5d46ac6..3a8fde2a3 100644 --- a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarFlag.java +++ b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarFlag.java @@ -2,7 +2,10 @@ package com.velocitypowered.api.util.bossbar; /** * Represents any {@link BossBar}'s flags. + * + * @deprecated Replaced with {@link net.kyori.adventure.bossbar.BossBar.Flag} */ +@Deprecated public enum BossBarFlag { DARKEN_SCREEN, PLAY_BOSS_MUSIC, diff --git a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarOverlay.java b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarOverlay.java index 8d52ffbe0..61d92d9d7 100644 --- a/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarOverlay.java +++ b/api/src/main/java/com/velocitypowered/api/util/bossbar/BossBarOverlay.java @@ -2,7 +2,10 @@ package com.velocitypowered.api.util.bossbar; /** * Represents a overlay of a {@link BossBar}. + * + * @deprecated Replaced with {@link net.kyori.adventure.bossbar.BossBar.Overlay} */ +@Deprecated public enum BossBarOverlay { PROGRESS, NOTCHED_6, diff --git a/api/src/main/java/com/velocitypowered/api/util/title/TextTitle.java b/api/src/main/java/com/velocitypowered/api/util/title/TextTitle.java index d0265f4f4..9580e9a84 100644 --- a/api/src/main/java/com/velocitypowered/api/util/title/TextTitle.java +++ b/api/src/main/java/com/velocitypowered/api/util/title/TextTitle.java @@ -9,7 +9,10 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** * Represents a "full" title, including all components. This class is immutable. + * + * @deprecated Replaced with {@link net.kyori.adventure.title.Title} */ +@Deprecated public final class TextTitle implements Title { private final @Nullable Component title; diff --git a/api/src/main/java/com/velocitypowered/api/util/title/Title.java b/api/src/main/java/com/velocitypowered/api/util/title/Title.java index bf70ba029..e95c6f1a3 100644 --- a/api/src/main/java/com/velocitypowered/api/util/title/Title.java +++ b/api/src/main/java/com/velocitypowered/api/util/title/Title.java @@ -2,7 +2,10 @@ package com.velocitypowered.api.util.title; /** * Represents a title that can be sent to a Minecraft client. + * + * @deprecated Replaced with {@link net.kyori.adventure.title.Title} */ +@Deprecated public interface Title { } diff --git a/api/src/main/java/com/velocitypowered/api/util/title/Titles.java b/api/src/main/java/com/velocitypowered/api/util/title/Titles.java index 31b1959bc..444e41018 100644 --- a/api/src/main/java/com/velocitypowered/api/util/title/Titles.java +++ b/api/src/main/java/com/velocitypowered/api/util/title/Titles.java @@ -2,7 +2,10 @@ package com.velocitypowered.api.util.title; /** * Provides special-purpose titles. + * + * @deprecated Replaced with {@link net.kyori.adventure.title.Title} */ +@Deprecated public final class Titles { private Titles() { diff --git a/api/src/main/java/com/velocitypowered/api/util/title/package-info.java b/api/src/main/java/com/velocitypowered/api/util/title/package-info.java index 467f57ab6..bedf52abb 100644 --- a/api/src/main/java/com/velocitypowered/api/util/title/package-info.java +++ b/api/src/main/java/com/velocitypowered/api/util/title/package-info.java @@ -1,4 +1,6 @@ /** * Provides data structures for creating and manipulating titles. + * + * @deprecated Replaced with {@link net.kyori.adventure.title} */ package com.velocitypowered.api.util.title; \ No newline at end of file diff --git a/build.gradle b/build.gradle index 959a24739..611ed740b 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,7 @@ allprojects { ext { // dependency versions textVersion = '3.0.3' + adventureVersion = '4.0.0-SNAPSHOT' junitVersion = '5.3.0-M1' slf4jVersion = '1.7.25' log4jVersion = '2.11.2' diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java index 955640e86..2dd7f48e4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java @@ -224,7 +224,7 @@ public class Metrics { ) ) ) { - VelocityServer.GSON.toJson(object, writer); + VelocityServer.GENERAL_GSON.toJson(object, writer); } catch (IOException e) { throw e; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index e011085a5..5d3377ac6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -8,6 +8,7 @@ import com.google.gson.GsonBuilder; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyReloadEvent; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.Player; @@ -68,10 +69,10 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.IntFunction; import java.util.stream.Collectors; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.TranslatableComponent; -import net.kyori.text.serializer.gson.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.AsyncHttpClient; @@ -83,10 +84,18 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityServer implements ProxyServer { private static final Logger logger = LogManager.getLogger(VelocityServer.class); - public static final Gson GSON = GsonComponentSerializer.populate(new GsonBuilder()) + public static final Gson GENERAL_GSON = new GsonBuilder() .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 final ConnectionManager cm; private final ProxyOptions options; @@ -594,4 +603,22 @@ public class VelocityServer implements ProxyServer { } return configuration.getBind(); } + + @Override + public void sendMessage(net.kyori.adventure.text.@NonNull Component message) { + Preconditions.checkNotNull(message, "message"); + for (ConnectedPlayer player : connectionsByUuid.values()) { + player.sendMessage(message); + } + } + + 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()); + } } 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 a6d25c897..076eecb47 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -1,19 +1,16 @@ package com.velocitypowered.proxy.config; import com.electronwill.nightconfig.core.CommentedConfig; -import com.electronwill.nightconfig.core.Config; import com.electronwill.nightconfig.core.UnmodifiableConfig; import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.moandjiezana.toml.Toml; import com.velocitypowered.api.proxy.config.ProxyConfig; +import com.velocitypowered.api.util.AdventureCompat; import com.velocitypowered.api.util.Favicon; -import com.velocitypowered.proxy.Velocity; import com.velocitypowered.proxy.util.AddressUtil; import java.io.IOException; -import java.io.Reader; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -22,14 +19,12 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Random; import java.util.UUID; -import net.kyori.text.Component; -import net.kyori.text.serializer.gson.GsonComponentSerializer; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -54,7 +49,7 @@ public class VelocityConfiguration implements ProxyConfig { private final Advanced advanced; private final Query query; private final Metrics metrics; - private @MonotonicNonNull Component motdAsComponent; + private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent; private @Nullable Favicon favicon; private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced, @@ -161,7 +156,7 @@ public class VelocityConfiguration implements ProxyConfig { } try { - getMotdComponent(); + getMotd(); } catch (Exception e) { logger.error("Can't parse your MOTD", e); valid = false; @@ -228,22 +223,23 @@ public class VelocityConfiguration implements ProxyConfig { return query.shouldQueryShowPlugins(); } - public String getMotd() { - return motd; - } - /** * Returns the proxy's MOTD. * * @return the MOTD */ @Override - public Component getMotdComponent() { + public net.kyori.text.Component getMotdComponent() { + return AdventureCompat.asOriginalTextComponent(this.getMotd()); + } + + @Override + public net.kyori.adventure.text.Component getMotd() { if (motdAsComponent == null) { if (motd.startsWith("{")) { - motdAsComponent = GsonComponentSerializer.INSTANCE.deserialize(motd); + motdAsComponent = GsonComponentSerializer.gson().deserialize(motd); } else { - motdAsComponent = LegacyComponentSerializer.legacy().deserialize(motd, '&'); + motdAsComponent = LegacyComponentSerializer.legacy('&').deserialize(motd); } } return motdAsComponent; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 5c43eb020..2926c52c3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -9,6 +9,7 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.tree.LiteralCommandNode; import com.velocitypowered.api.event.command.PlayerAvailableCommandsEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; @@ -216,7 +217,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { serverConn.getServer().removePlayer(serverConn.getPlayer()); if (!serverConn.isGracefulDisconnect() && !exceptionTriggered) { serverConn.getPlayer().handleConnectionException(serverConn.getServer(), - Disconnect.create(ConnectionMessages.UNEXPECTED_DISCONNECT), true); + Disconnect.create(ConnectionMessages.UNEXPECTED_DISCONNECT, + ProtocolVersion.MINECRAFT_1_16), true); } } } 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 5627d9ba5..87c75ff6e 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 @@ -26,7 +26,7 @@ import java.util.concurrent.CompletableFuture; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; -import net.kyori.text.TextComponent; +import net.kyori.adventure.text.TextComponent; public class LoginSessionHandler implements MinecraftSessionHandler { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index 0db532f62..fc2902865 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -1,6 +1,6 @@ package com.velocitypowered.proxy.connection.backend; -import static com.velocitypowered.proxy.VelocityServer.GSON; +import static com.velocitypowered.proxy.VelocityServer.GENERAL_GSON; import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN; import static com.velocitypowered.proxy.network.Connections.HANDLER; @@ -105,7 +105,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, .append('\0') .append(proxyPlayer.getGameProfile().getUndashedId()) .append('\0'); - GSON.toJson(propertiesTransform.apply(proxyPlayer.getGameProfile().getProperties()), data); + GENERAL_GSON + .toJson(propertiesTransform.apply(proxyPlayer.getGameProfile().getProperties()), data); return data.toString(); } 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 27ba08d0c..919f93c64 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,6 +4,8 @@ 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; @@ -61,18 +63,19 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; -import net.kyori.text.Component; -import net.kyori.text.TextComponent; -import net.kyori.text.TranslatableComponent; -import net.kyori.text.format.TextColor; -import net.kyori.text.serializer.gson.GsonComponentSerializer; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; -import net.kyori.text.serializer.plain.PlainComponentSerializer; +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; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @@ -218,7 +221,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public void sendMessage(Component component, MessagePosition position) { + public void sendMessage(net.kyori.text.Component component, MessagePosition position) { Preconditions.checkNotNull(component, "component"); Preconditions.checkNotNull(position, "position"); @@ -229,18 +232,20 @@ 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(GsonComponentSerializer.INSTANCE.serialize(component)); + pkt.setComponent(net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE + .serialize(component)); connection.write(pkt); return; } else { // Due to issues with action bar packets, we'll need to convert the text message into a // legacy message and then inject the legacy text into a component... yuck! JsonObject object = new JsonObject(); - object.addProperty("text", LegacyComponentSerializer.legacy().serialize(component)); + object.addProperty("text", net.kyori.text.serializer.legacy + .LegacyComponentSerializer.legacy().serialize(component)); json = object.toString(); } } else { - json = GsonComponentSerializer.INSTANCE.serialize(component); + json = net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE.serialize(component); } Chat chat = new Chat(); @@ -249,6 +254,31 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { connection.write(chat); } + @Override + public void sendMessage(net.kyori.adventure.text.@NonNull Component message) { + connection.write(Chat.createClientbound(message, this.getProtocolVersion())); + } + + @Override + public void sendActionBar(net.kyori.adventure.text.@NonNull Component message) { + if (getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) { + // 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)); + connection.write(pkt); + } else { + // Due to issues with action bar packets, we'll need to convert the text message into a + // legacy message and then inject the legacy text into a component... yuck! + JsonObject object = new JsonObject(); + object.addProperty("text", LegacyComponentSerializer.legacy().serialize(message)); + Chat chat = new Chat(); + chat.setMessage(object.toString()); + chat.setType((byte) 1); + connection.write(chat); + } + } + @Override public ConnectionRequestBuilder createConnectionRequest(RegisteredServer server) { return new ConnectionRequestBuilderImpl(server); @@ -265,7 +295,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public void setHeaderAndFooter(Component header, Component footer) { + public void setHeaderAndFooter(net.kyori.text.Component header, net.kyori.text.Component footer) { tabList.setHeaderAndFooter(header, footer); } @@ -288,6 +318,26 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } } + @Override + public void disconnect(net.kyori.text.Component reason) { + if (connection.eventLoop().inEventLoop()) { + disconnect0(reason, false); + } else { + connection.eventLoop().execute(() -> disconnect0(reason, false)); + } + } + + /** + * Disconnects the player from the proxy. + * @param reason the reason for disconnecting the player + * @param duringLogin whether the disconnect happened during login + */ + public void disconnect0(net.kyori.text.Component reason, boolean duringLogin) { + logger.info("{} has disconnected: {}", this, + net.kyori.text.serializer.legacy.LegacyComponentSerializer.legacy().serialize(reason)); + connection.closeWith(Disconnect.create(reason)); + } + /** * Disconnects the player from the proxy. * @param reason the reason for disconnecting the player @@ -296,7 +346,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { public void disconnect0(Component reason, boolean duringLogin) { logger.info("{} has disconnected: {}", this, LegacyComponentSerializer.legacy().serialize(reason)); - connection.closeWith(Disconnect.create(reason)); + connection.closeWith(Disconnect.create(reason, this.getProtocolVersion())); } @Override @@ -310,24 +360,25 @@ 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)); } - Optional titleText = tt.getTitle(); + Optional titleText = tt.getTitle(); if (titleText.isPresent()) { TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_TITLE); - titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(titleText.get())); + titlePkt.setComponent(gson.toJson(titleText.get())); connection.delayedWrite(titlePkt); } - Optional subtitleText = tt.getSubtitle(); + Optional subtitleText = tt.getSubtitle(); if (subtitleText.isPresent()) { TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_SUBTITLE); - titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(subtitleText.get())); + titlePkt.setComponent(gson.toJson(subtitleText.get())); connection.delayedWrite(titlePkt); } @@ -391,7 +442,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { userMessage = "Unable to connect to " + server.getServerInfo().getName() + ". Try again " + "later."; } - handleConnectionException(server, null, TextComponent.of(userMessage, TextColor.RED), safe); + handleConnectionException(server, null, TextComponent.of(userMessage, + NamedTextColor.RED), safe); } /** @@ -407,15 +459,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return; } - Component disconnectReason = GsonComponentSerializer.INSTANCE.deserialize( - disconnect.getReason()); + Component disconnectReason = GsonComponentSerializer.gson().deserialize(disconnect.getReason()); String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason); if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { logger.error("{}: kicked from server {}: {}", this, server.getServerInfo().getName(), plainTextReason); handleConnectionException(server, disconnectReason, TextComponent.builder() .content("Kicked from " + server.getServerInfo().getName() + ": ") - .color(TextColor.RED) + .color(NamedTextColor.RED) .append(disconnectReason) .build(), safe); } else { @@ -423,7 +474,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { server.getServerInfo().getName(), plainTextReason); handleConnectionException(server, disconnectReason, TextComponent.builder() .content("Can't connect to server " + server.getServerInfo().getName() + ": ") - .color(TextColor.RED) + .color(NamedTextColor.RED) .append(disconnectReason) .build(), safe); } @@ -448,7 +499,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { ServerKickResult result; if (kickedFromCurrent) { Optional next = getNextServerToTry(rs); - result = next.map(RedirectPlayer::create) + result = next.map(RedirectPlayer::create) .orElseGet(() -> DisconnectPlayer.create(friendlyReason)); } else { // If we were kicked by going to another server, the connection should not be in flight @@ -479,10 +530,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (newResult == null || !newResult) { disconnect(friendlyReason); } else { - if (res.getMessage() == null) { + if (res.getMessageComponent() == null) { sendMessage(VelocityMessages.MOVED_TO_NEW_SERVER.append(friendlyReason)); } else { - sendMessage(res.getMessage()); + sendMessage(res.getMessageComponent()); } } }, connection.eventLoop()); @@ -816,8 +867,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { // Ignored; the plugin probably already handled this. break; case SERVER_DISCONNECTED: - handleConnectionException(toConnect, Disconnect.create(status.getReason() - .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR)), status.isSafe()); + Component reason = status.getReasonComponent() + .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR); + handleConnectionException(toConnect, Disconnect.create(reason, + getProtocolVersion()), status.isSafe()); 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 64a234dff..87ea57a8c 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 @@ -23,9 +23,10 @@ import io.netty.buffer.ByteBuf; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Optional; -import net.kyori.text.TextComponent; -import net.kyori.text.TranslatableComponent; -import net.kyori.text.format.TextColor; +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 org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -55,7 +56,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(LegacyHandshake packet) { connection.closeWith(LegacyDisconnect - .from(TextComponent.of("Your client is old, please upgrade!", TextColor.RED))); + .from(TextComponent.of("Your client is old, please upgrade!", NamedTextColor.RED))); return true; } @@ -101,15 +102,13 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { private void handleLogin(Handshake handshake, InitialInboundConnection ic) { if (!ProtocolVersion.isSupported(handshake.getProtocolVersion())) { - connection.closeWith(Disconnect - .create(TranslatableComponent.of("multiplayer.disconnect.outdated_client"))); + ic.disconnectQuietly(TranslatableComponent.of("multiplayer.disconnect.outdated_client")); return; } InetAddress address = ((InetSocketAddress) connection.getRemoteAddress()).getAddress(); if (!server.getIpAttemptLimiter().attempt(address)) { - connection.closeWith( - Disconnect.create(TextComponent.of("You are logging in too fast, try again later."))); + ic.disconnectQuietly(TextComponent.of("You are logging in too fast, try again later.")); return; } @@ -119,8 +118,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { // and lower, otherwise IP information will never get forwarded. if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && handshake.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_13) < 0) { - connection.closeWith(Disconnect - .create(TextComponent.of("This server is only compatible with 1.13 and above."))); + ic.disconnectQuietly(TextComponent.of("This server is only compatible with 1.13 and above.")); return; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java index bab4bd8de..44a6829dc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java @@ -8,8 +8,8 @@ import com.velocitypowered.proxy.protocol.packet.Disconnect; import com.velocitypowered.proxy.protocol.packet.Handshake; import java.net.InetSocketAddress; import java.util.Optional; -import net.kyori.text.Component; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -61,6 +61,14 @@ public final class InitialInboundConnection implements InboundConnection, public void disconnect(Component reason) { logger.info("{} has disconnected: {}", this, LegacyComponentSerializer.legacy().serialize(reason)); - connection.closeWith(Disconnect.create(reason)); + connection.closeWith(Disconnect.create(reason, getProtocolVersion())); + } + + /** + * Disconnects the connection from the server silently. + * @param reason the reason for disconnecting + */ + public void disconnectQuietly(Component reason) { + connection.closeWith(Disconnect.create(reason, getProtocolVersion())); } } 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 2f185652b..c419acba0 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 @@ -2,7 +2,7 @@ package com.velocitypowered.proxy.connection.client; import static com.google.common.net.UrlEscapers.urlFormParameterEscaper; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; -import static com.velocitypowered.proxy.VelocityServer.GSON; +import static com.velocitypowered.proxy.VelocityServer.GENERAL_GSON; import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa; import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId; @@ -124,7 +124,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { Response profileResponse = hasJoinedResponse.get(); if (profileResponse.getStatusCode() == 200) { // All went well, initialize the session. - initializePlayer(GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class), + 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. 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 04a96e0ef..675f02b7a 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 @@ -1,12 +1,14 @@ package com.velocitypowered.proxy.connection.client; import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; import com.spotify.futures.CompletableFutures; import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.PingPassthroughMode; @@ -18,6 +20,7 @@ import com.velocitypowered.proxy.protocol.packet.LegacyPing; import com.velocitypowered.proxy.protocol.packet.StatusPing; import com.velocitypowered.proxy.protocol.packet.StatusRequest; import com.velocitypowered.proxy.protocol.packet.StatusResponse; +import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.buffer.ByteBuf; import java.net.InetSocketAddress; @@ -25,11 +28,23 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class StatusSessionHandler implements MinecraftSessionHandler { + 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 static final Logger logger = LogManager.getLogger(StatusSessionHandler.class); private final VelocityServer server; @@ -58,7 +73,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler { "Velocity " + ProtocolVersion.SUPPORTED_VERSION_STRING), new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(), ImmutableList.of()), - configuration.getMotdComponent(), + configuration.getMotd(), configuration.getFavicon().orElse(null), configuration.isAnnounceForge() ? ModInfo.DEFAULT : null ); @@ -119,7 +134,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler { return new ServerPing( fallback.getVersion(), fallback.getPlayers().orElse(null), - response.getDescription(), + response.getDescriptionComponent(), fallback.getFavicon().orElse(null), response.getModinfo().orElse(null) ); @@ -173,7 +188,8 @@ public class StatusSessionHandler implements MinecraftSessionHandler { .thenAcceptAsync( (event) -> { StringBuilder json = new StringBuilder(); - VelocityServer.GSON.toJson(event.getPing(), json); + VelocityServer.getGsonInstance(connection.getProtocolVersion()) + .toJson(event.getPing(), json); connection.write(new StatusResponse(json)); }, connection.eventLoop()); 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 c35dd9f71..99bb6350f 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 @@ -1,14 +1,14 @@ package com.velocitypowered.proxy.connection.util; -import net.kyori.text.TextComponent; -import net.kyori.text.format.TextColor; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; public class ConnectionMessages { public static final TextComponent ALREADY_CONNECTED = TextComponent - .of("You are already connected to this server!", TextColor.RED); + .of("You are already connected to this server!", NamedTextColor.RED); public static final TextComponent IN_PROGRESS = TextComponent - .of("You are already connecting to a server!", TextColor.RED); + .of("You are already connecting to a server!", NamedTextColor.RED); public static final TextComponent INTERNAL_SERVER_CONNECTION_ERROR = TextComponent .of("Internal server connection error"); public static final TextComponent UNEXPECTED_DISCONNECT = TextComponent diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java index b78728ff3..307f97fa7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java @@ -3,11 +3,12 @@ package com.velocitypowered.proxy.connection.util; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status; import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.AdventureCompat; import com.velocitypowered.proxy.protocol.packet.Disconnect; import java.util.Optional; import javax.annotation.Nullable; -import net.kyori.text.Component; -import net.kyori.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; public class ConnectionRequestResults { @@ -42,19 +43,19 @@ public class ConnectionRequestResults { } public static Impl forDisconnect(Disconnect disconnect, RegisteredServer server) { - Component deserialized = GsonComponentSerializer.INSTANCE.deserialize(disconnect.getReason()); + Component deserialized = GsonComponentSerializer.gson().deserialize(disconnect.getReason()); return forDisconnect(deserialized, server); } public static Impl forUnsafeDisconnect(Disconnect disconnect, RegisteredServer server) { - Component deserialized = GsonComponentSerializer.INSTANCE.deserialize(disconnect.getReason()); + Component deserialized = GsonComponentSerializer.gson().deserialize(disconnect.getReason()); return new Impl(Status.SERVER_DISCONNECTED, deserialized, server, false); } public static class Impl implements ConnectionRequestBuilder.Result { private final Status status; - private final @Nullable Component component; + private final @Nullable net.kyori.adventure.text.Component component; private final RegisteredServer attemptedConnection; private final boolean safe; @@ -72,7 +73,12 @@ public class ConnectionRequestResults { } @Override - public Optional getReason() { + public Optional getReason() { + return Optional.ofNullable(component).map(AdventureCompat::asOriginalTextComponent); + } + + @Override + public Optional getReasonComponent() { return Optional.ofNullable(component); } 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 4b9d38aee..49e1afc73 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -8,10 +8,8 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.proxy.VelocityServer; import java.util.List; -import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; import net.minecrell.terminalconsole.SimpleTerminalConsole; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -34,8 +32,15 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons } @Override - public void sendMessage(Component component) { - logger.info(LegacyComponentSerializer.legacy().serialize(component)); + public void sendMessage(net.kyori.text.Component component) { + logger.info(net.kyori.text.serializer.legacy.LegacyComponentSerializer.legacy() + .serialize(component)); + } + + @Override + public void sendMessage(net.kyori.adventure.text.@NonNull Component message) { + logger.info(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacy() + .serialize(message)); } @Override @@ -103,5 +108,4 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons protected void shutdown() { this.server.shutdown(true); } - } 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 17a075c23..166d5f0ef 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,7 +117,7 @@ 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.GSON + return Optional.of(VelocityServer.GENERAL_GSON .fromJson(pluginInfoReader, SerializedPluginDescription.class)); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java index 9be2eae8f..8487785ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import net.kyori.text.serializer.plain.PlainComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -72,8 +72,7 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler private QueryResponse createInitialResponse() { return QueryResponse.builder() - .hostname(PlainComponentSerializer.INSTANCE - .serialize(server.getConfiguration().getMotdComponent())) + .hostname(PlainComponentSerializer.INSTANCE.serialize(server.getConfiguration().getMotd())) .gameVersion(ProtocolVersion.SUPPORTED_VERSION_STRING) .map(server.getConfiguration().getQueryMap()) .currentPlayers(server.getPlayerCount()) 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 028f1ccad..62ab7c14b 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,12 +2,11 @@ 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; import io.netty.buffer.ByteBuf; -import net.kyori.text.Component; -import net.kyori.text.serializer.gson.GsonComponentSerializer; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.UUID; @@ -97,13 +96,27 @@ public class Chat implements MinecraftPacket { return handler.handle(this); } - public static Chat createClientbound(Component component) { + @Deprecated + public static Chat createClientbound(net.kyori.text.Component component) { return createClientbound(component, CHAT_TYPE, EMPTY_SENDER); } - public static Chat createClientbound(Component component, byte type, UUID sender) { + @Deprecated + public static Chat createClientbound(net.kyori.text.Component component, byte type, UUID sender) { Preconditions.checkNotNull(component, "component"); - return new Chat(GsonComponentSerializer.INSTANCE.serialize(component), type, sender); + return new Chat(net.kyori.text.serializer.gson.GsonComponentSerializer.INSTANCE + .serialize(component), type, sender); + } + + public static Chat createClientbound(net.kyori.adventure.text.Component component, + ProtocolVersion version) { + return createClientbound(component, CHAT_TYPE, EMPTY_SENDER, version); + } + + 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); } 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 c3ece521e..305fb851e 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,6 +2,7 @@ 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; @@ -57,8 +58,15 @@ public class Disconnect implements MinecraftPacket { return handler.handle(this); } + @Deprecated public static Disconnect create(Component component) { Preconditions.checkNotNull(component, "component"); return new Disconnect(GsonComponentSerializer.INSTANCE.serialize(component)); } + + public static Disconnect create(net.kyori.adventure.text.Component component, + ProtocolVersion version) { + Preconditions.checkNotNull(component, "component"); + return new Disconnect(VelocityServer.getGsonInstance(version).toJson(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 177d3a19d..235dd1c4b 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,7 +3,9 @@ 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; @@ -58,6 +60,12 @@ public class HeaderAndFooter implements MinecraftPacket { return new HeaderAndFooter(json.serialize(header), json.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)); + } + public static HeaderAndFooter reset() { return RESET; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyDisconnect.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyDisconnect.java index 0b28ba17c..5d6a4a2bf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyDisconnect.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LegacyDisconnect.java @@ -4,9 +4,9 @@ import com.google.common.collect.ImmutableList; import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.proxy.server.ServerPing.Players; import com.velocitypowered.proxy.protocol.packet.legacyping.LegacyMinecraftPingVersion; -import net.kyori.text.TextComponent; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; -import net.kyori.text.serializer.plain.PlainComponentSerializer; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; public class LegacyDisconnect { @@ -36,7 +36,8 @@ public class LegacyDisconnect { // remove all section symbols, along with fetching just the first line of an (unformatted) // MOTD. return new LegacyDisconnect(String.join(LEGACY_COLOR_CODE, - cleanSectionSymbol(getFirstLine(PlainComponentSerializer.INSTANCE.serialize(response.getDescription()))), + cleanSectionSymbol(getFirstLine(PlainComponentSerializer.INSTANCE.serialize( + response.getDescriptionComponent()))), Integer.toString(players.getOnline()), Integer.toString(players.getMax()))); case MINECRAFT_1_4: @@ -46,7 +47,8 @@ public class LegacyDisconnect { LEGACY_COLOR_CODE + "1", Integer.toString(response.getVersion().getProtocol()), response.getVersion().getName(), - getFirstLine(LegacyComponentSerializer.legacy().serialize(response.getDescription())), + getFirstLine(LegacyComponentSerializer.legacy().serialize(response + .getDescriptionComponent())), Integer.toString(players.getOnline()), Integer.toString(players.getMax()) )); 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 1037ac765..f7a7181f0 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 @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; @@ -11,9 +12,9 @@ import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import net.kyori.text.Component; -import net.kyori.text.serializer.gson.GsonComponentSerializer; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.checkerframework.checker.nullness.qual.Nullable; public class PlayerListItem implements MinecraftPacket { @@ -86,7 +87,7 @@ public class PlayerListItem implements MinecraftPacket { private static @Nullable Component readOptionalComponent(ByteBuf buf) { if (buf.readBoolean()) { - return GsonComponentSerializer.INSTANCE.deserialize(ProtocolUtils.readString(buf)); + return GsonComponentSerializer.gson().deserialize(ProtocolUtils.readString(buf)); } return null; } @@ -105,7 +106,7 @@ public class PlayerListItem implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, item.getGameMode()); ProtocolUtils.writeVarInt(buf, item.getLatency()); - writeDisplayName(buf, item.getDisplayName()); + writeDisplayName(buf, item.getDisplayName(), version); break; case UPDATE_GAMEMODE: ProtocolUtils.writeVarInt(buf, item.getGameMode()); @@ -114,10 +115,10 @@ public class PlayerListItem implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, item.getLatency()); break; case UPDATE_DISPLAY_NAME: - writeDisplayName(buf, item.getDisplayName()); + writeDisplayName(buf, item.getDisplayName(), version); break; case REMOVE_PLAYER: - //Do nothing, all that is needed is the uuid + // Do nothing, all that is needed is the uuid break; default: throw new UnsupportedOperationException("Unknown action " + action); @@ -142,10 +143,11 @@ public class PlayerListItem implements MinecraftPacket { return handler.handle(this); } - private void writeDisplayName(ByteBuf buf, @Nullable Component displayName) { + private void writeDisplayName(ByteBuf buf, @Nullable Component displayName, + ProtocolVersion version) { buf.writeBoolean(displayName != null); if (displayName != null) { - ProtocolUtils.writeString(buf, GsonComponentSerializer.INSTANCE.serialize(displayName)); + ProtocolUtils.writeString(buf, VelocityServer.getGsonInstance(version).toJson(displayName)); } } @@ -172,7 +174,7 @@ public class PlayerListItem implements MinecraftPacket { .setProperties(entry.getProfile().getProperties()) .setLatency(entry.getLatency()) .setGameMode(entry.getGameMode()) - .setDisplayName(entry.getDisplayName().orElse(null)); + .setDisplayName(entry.getDisplayNameComponent().orElse(null)); } public @Nullable UUID getUuid() { 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 bf2d2e286..347fcc4b7 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,8 @@ public class PingSessionHandler implements MinecraftSessionHandler { completed = true; connection.close(true); - ServerPing ping = VelocityServer.GSON.fromJson(packet.getStatus(), ServerPing.class); + ServerPing ping = VelocityServer.getGsonInstance(version).fromJson(packet.getStatus(), + ServerPing.class); result.complete(ping); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java index 979dca88f..7aedd4c98 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -37,6 +37,8 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityRegisteredServer implements RegisteredServer { @@ -145,4 +147,11 @@ public class VelocityRegisteredServer implements RegisteredServer { public String toString() { return "registered server: " + serverInfo; } + + @Override + public void sendMessage(@NonNull Component message) { + for (ConnectedPlayer player : players) { + player.sendMessage(message); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index 6830382f6..58103eb07 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -34,6 +34,14 @@ public class VelocityTabList implements TabList { connection.write(HeaderAndFooter.create(header, footer)); } + @Override + public void setHeaderAndFooter(net.kyori.adventure.text.Component header, + net.kyori.adventure.text.Component footer) { + Preconditions.checkNotNull(header, "header"); + Preconditions.checkNotNull(footer, "footer"); + connection.write(HeaderAndFooter.create(header, footer, connection.getProtocolVersion())); + } + @Override public void clearHeaderAndFooter() { connection.write(HeaderAndFooter.reset()); @@ -97,8 +105,8 @@ public class VelocityTabList implements TabList { } @Override - public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, - int gameMode) { + public TabListEntry buildEntry(GameProfile profile, + net.kyori.adventure.text.@Nullable Component displayName, int latency, int gameMode) { return new VelocityTabListEntry(this, profile, displayName, latency, gameMode); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java index 647ce3d7a..86ceddfff 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java @@ -2,6 +2,7 @@ package com.velocitypowered.proxy.tablist; import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabListEntry; +import com.velocitypowered.api.util.AdventureCompat; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.protocol.packet.PlayerListItem; import java.util.Optional; @@ -12,12 +13,12 @@ public class VelocityTabListEntry implements TabListEntry { private final VelocityTabList tabList; private final GameProfile profile; - private @Nullable Component displayName; + private net.kyori.adventure.text.Component displayName; private int latency; private int gameMode; VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, - @Nullable Component displayName, int latency, int gameMode) { + net.kyori.adventure.text.@Nullable Component displayName, int latency, int gameMode) { this.tabList = tabList; this.profile = profile; this.displayName = displayName; @@ -37,17 +38,27 @@ public class VelocityTabListEntry implements TabListEntry { @Override public Optional getDisplayName() { + return Optional.ofNullable(displayName).map(AdventureCompat::asOriginalTextComponent); + } + + @Override + public Optional getDisplayNameComponent() { return Optional.ofNullable(displayName); } @Override public TabListEntry setDisplayName(@Nullable Component displayName) { + return this.setDisplayName(AdventureCompat.asAdventureComponent(displayName)); + } + + @Override + public TabListEntry setDisplayName(net.kyori.adventure.text.@Nullable Component displayName) { this.displayName = displayName; tabList.updateEntry(PlayerListItem.UPDATE_DISPLAY_NAME, this); return this; } - void setDisplayNameInternal(@Nullable Component displayName) { + void setDisplayNameInternal(net.kyori.adventure.text.@Nullable Component displayName) { this.displayName = displayName; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java index 7ef7b5dd7..0b7016fd5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntryLegacy.java @@ -2,7 +2,7 @@ package com.velocitypowered.proxy.tablist; import com.velocitypowered.api.proxy.player.TabListEntry; import com.velocitypowered.api.util.GameProfile; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityTabListEntryLegacy extends VelocityTabListEntry { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java index e46fa4820..305564ea7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -11,7 +11,7 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import net.kyori.text.Component; +import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityTabListLegacy extends VelocityTabList { @@ -22,6 +22,10 @@ public class VelocityTabListLegacy extends VelocityTabList { super(connection); } + @Override + public void setHeaderAndFooter(net.kyori.text.Component header, net.kyori.text.Component footer) { + } + @Override public void setHeaderAndFooter(Component header, Component footer) { } @@ -55,7 +59,6 @@ public class VelocityTabListLegacy extends VelocityTabList { @Override public void processBackendPacket(PlayerListItem packet) { - Item item = packet.getItems().get(0); // Only one item per packet in 1.7 switch (packet.getAction()) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java index 4acf5889f..d8ea66789 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java @@ -1,27 +1,25 @@ package com.velocitypowered.proxy.util; -import net.kyori.text.Component; -import net.kyori.text.TextComponent; -import net.kyori.text.format.TextColor; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; public class VelocityMessages { public static final Component ONLINE_MODE_ONLY = TextComponent .builder("This server only accepts connections from online-mode clients.") - .color(TextColor.RED) + .color(NamedTextColor.RED) .append( TextComponent.of("\n\nDid you change your username? Sign out of Minecraft, sign back in, " - + "and try again.", TextColor.GRAY) + + "and try again.", NamedTextColor.GRAY) ) .build(); - public static final Component NO_PROXY_BEHIND_PROXY = TextComponent - .of("Running Velocity behind Velocity isn't supported.", TextColor.RED); public static final Component NO_AVAILABLE_SERVERS = TextComponent - .of("No available servers", TextColor.RED); + .of("No available servers", NamedTextColor.RED); public static final Component ALREADY_CONNECTED = TextComponent - .of("You are already connected to this proxy!", TextColor.RED); + .of("You are already connected to this proxy!", NamedTextColor.RED); public static final Component MOVED_TO_NEW_SERVER = TextComponent - .of("The server you were on kicked you: ", TextColor.RED); + .of("The server you were on kicked you: ", NamedTextColor.RED); private VelocityMessages() { throw new AssertionError();