diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxiedPlayer.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxiedPlayer.java new file mode 100644 index 000000000..3a9b9dcb3 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxiedPlayer.java @@ -0,0 +1,60 @@ +package com.velocitypowered.api.proxy; + +import com.velocitypowered.api.server.ServerInfo; +import com.velocitypowered.api.util.MessagePosition; +import net.kyori.text.Component; + +import javax.annotation.Nonnull; +import java.net.InetSocketAddress; +import java.util.Optional; +import java.util.UUID; + +/** + * Represents a player who is connected to the proxy. + */ +public interface ProxiedPlayer { + /** + * Returns the player's current username. + * @return the username + */ + String getUsername(); + + /** + * Returns the player's UUID. + * @return the UUID + */ + UUID getUniqueId(); + + /** + * Returns the server that the player is currently connected to. + * @return an {@link Optional} the server that the player is connected to, which may be empty + */ + Optional getCurrentServer(); + + /** + * Returns the player's IP address. + * @return the player's IP + */ + InetSocketAddress getRemoteAddress(); + + /** + * Determine whether or not the player remains online. + * @return whether or not the player active + */ + boolean isActive(); + + /** + * Sends a chat message to the player's client. + * @param component the chat message to send + */ + default void sendMessage(@Nonnull Component component) { + sendMessage(component, MessagePosition.CHAT); + } + + /** + * Sends a chat message to the player's client in the specified position. + * @param component the chat message to send + * @param position the position for the message + */ + void sendMessage(@Nonnull Component component, @Nonnull MessagePosition position); +} diff --git a/api/src/main/java/com/velocitypowered/api/util/MessagePosition.java b/api/src/main/java/com/velocitypowered/api/util/MessagePosition.java new file mode 100644 index 000000000..b27d7d4b1 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/MessagePosition.java @@ -0,0 +1,20 @@ +package com.velocitypowered.api.util; + +/** + * Represents where a chat message is going to be sent. + */ +public enum MessagePosition { + /** + * The chat message will appear in the client's HUD. These messages can be filtered out by the client. + */ + CHAT, + /** + * The chat message will appear in the client's HUD and can't be dismissed. + */ + SYSTEM, + /** + * The chat message will appear above the player's main HUD. This text format doesn't support many component features, + * such as hover events. + */ + ACTION_BAR +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 62cd39ad6..1c1ad4687 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -85,6 +85,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { ServerInfo info = new ServerInfo("test", new InetSocketAddress("localhost", 25566)); ServerConnection connection = new ServerConnection(info, player, VelocityServer.getServer()); connection.connect(); + return; } } 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 e276a12b7..2dbb3b215 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 @@ -1,6 +1,9 @@ package com.velocitypowered.proxy.connection.client; import com.google.common.base.Preconditions; +import com.google.gson.JsonObject; +import com.velocitypowered.api.util.MessagePosition; +import com.velocitypowered.api.proxy.ProxiedPlayer; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.data.GameProfile; @@ -20,12 +23,13 @@ import net.kyori.text.serializer.PlainComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.annotation.Nonnull; import java.net.InetSocketAddress; import java.util.List; import java.util.Optional; import java.util.UUID; -public class ConnectedPlayer implements MinecraftConnectionAssociation { +public class ConnectedPlayer implements MinecraftConnectionAssociation, ProxiedPlayer { private static final PlainComponentSerializer PASS_THRU_TRANSLATE = new PlainComponentSerializer((c) -> "", TranslatableComponent::key); private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class); @@ -42,14 +46,21 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation { this.connection = connection; } + @Override public String getUsername() { return profile.getName(); } + @Override public UUID getUniqueId() { return profile.idAsUuid(); } + @Override + public Optional getCurrentServer() { + return Optional.empty(); + } + public GameProfile getProfile() { return profile; } @@ -58,10 +69,39 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation { return connection; } + @Override public InetSocketAddress getRemoteAddress() { return (InetSocketAddress) connection.getChannel().remoteAddress(); } + @Override + public boolean isActive() { + return connection.getChannel().isActive(); + } + + @Override + public void sendMessage(@Nonnull Component component, @Nonnull MessagePosition position) { + Preconditions.checkNotNull(component, "component"); + Preconditions.checkNotNull(position, "position"); + + byte pos = (byte) position.ordinal(); + String json; + if (position == MessagePosition.ACTION_BAR) { + // 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", ComponentSerializers.LEGACY.serialize(component)); + json = VelocityServer.GSON.toJson(object); + } else { + json = ComponentSerializers.JSON.serialize(component); + } + + Chat chat = new Chat(); + chat.setType(pos); + chat.setMessage(json); + connection.write(chat); + } + public ServerConnection getConnectedServer() { return connectedServer; }