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 08bf0c440..92871b30e 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -1,6 +1,7 @@ package com.velocitypowered.api.proxy; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.messages.ChannelMessageSource; import com.velocitypowered.api.server.ServerInfo; import com.velocitypowered.api.util.MessagePosition; import net.kyori.text.Component; @@ -12,7 +13,7 @@ import java.util.UUID; /** * Represents a player who is connected to the proxy. */ -public interface Player extends CommandSource, InboundConnection { +public interface Player extends CommandSource, InboundConnection, ChannelMessageSource { /** * Returns the player's current username. * @return the username 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 b35fe127c..433c61097 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -4,6 +4,7 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.plugin.PluginManager; +import com.velocitypowered.api.proxy.messages.ChannelRegistrar; import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.server.ServerInfo; @@ -101,4 +102,10 @@ public interface ProxyServer { * @return the scheduler instance */ Scheduler getScheduler(); + + /** + * Gets the {@link ChannelRegistrar} instance. + * @return the channel registrar + */ + ChannelRegistrar getChannelRegistrar(); } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ServerConnection.java b/api/src/main/java/com/velocitypowered/api/proxy/ServerConnection.java index 8753019d4..4e18fd949 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ServerConnection.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ServerConnection.java @@ -1,11 +1,12 @@ package com.velocitypowered.api.proxy; +import com.velocitypowered.api.proxy.messages.ChannelMessageSource; import com.velocitypowered.api.server.ServerInfo; /** * Represents a connection to a backend server from the proxy for a client. */ -public interface ServerConnection { +public interface ServerConnection extends ChannelMessageSource { ServerInfo getServerInfo(); Player getPlayer(); diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelIdentifier.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelIdentifier.java index 66d8b65c9..0af1eeae2 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelIdentifier.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelIdentifier.java @@ -4,4 +4,5 @@ package com.velocitypowered.api.proxy.messages; * Represents a kind of channel identifier. */ public interface ChannelIdentifier { + String getId(); } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSource.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSource.java new file mode 100644 index 000000000..1aaeae943 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSource.java @@ -0,0 +1,4 @@ +package com.velocitypowered.api.proxy.messages; + +public interface ChannelMessageSource { +} diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelRegistrar.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelRegistrar.java index 71fa1bb1d..412e172df 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelRegistrar.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelRegistrar.java @@ -1,4 +1,11 @@ package com.velocitypowered.api.proxy.messages; +/** + * Represents an interface to register and unregister {@link MessageHandler} instances for handling plugin messages from + * the client or the server. + */ public interface ChannelRegistrar { + void register(MessageHandler handler, ChannelIdentifier... identifiers); + + void unregister(ChannelIdentifier... identifiers); } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelSide.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelSide.java new file mode 100644 index 000000000..f27faf71b --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelSide.java @@ -0,0 +1,6 @@ +package com.velocitypowered.api.proxy.messages; + +public enum ChannelSide { + FROM_SERVER, + FROM_CLIENT +} diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/LegacyChannelIdentifier.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/LegacyChannelIdentifier.java index cf660ab5e..a5a345cfd 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/LegacyChannelIdentifier.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/LegacyChannelIdentifier.java @@ -40,4 +40,9 @@ public final class LegacyChannelIdentifier implements ChannelIdentifier { public int hashCode() { return Objects.hash(name); } + + @Override + public String getId() { + return name; + } } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/MessageHandler.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/MessageHandler.java new file mode 100644 index 000000000..74fe81f82 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/MessageHandler.java @@ -0,0 +1,10 @@ +package com.velocitypowered.api.proxy.messages; + +public interface MessageHandler { + ForwardStatus handle(ChannelMessageSource source, ChannelSide side, byte[] data); + + enum ForwardStatus { + FORWARD, + HANDLED + } +} diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/MinecraftChannelIdentifier.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/MinecraftChannelIdentifier.java index 09b5397d0..c171fee66 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/MinecraftChannelIdentifier.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/MinecraftChannelIdentifier.java @@ -61,4 +61,9 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier { public int hashCode() { return Objects.hash(namespace, name); } + + @Override + public String getId() { + return namespace + ":" + name; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index c474019e6..5a9702c5f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -21,6 +21,7 @@ import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.http.NettyHttpClient; import com.velocitypowered.proxy.command.VelocityCommandManager; +import com.velocitypowered.proxy.messages.VelocityChannelRegistrar; import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.plugin.VelocityPluginManager; @@ -84,6 +85,7 @@ public class VelocityServer implements ProxyServer { private Ratelimiter ipAttemptLimiter; private VelocityEventManager eventManager; private VelocityScheduler scheduler; + private VelocityChannelRegistrar channelRegistrar; private VelocityServer() { commandManager.register(new VelocityCommand(), "velocity"); @@ -137,6 +139,7 @@ public class VelocityServer implements ProxyServer { httpClient = new NettyHttpClient(this); eventManager = new VelocityEventManager(pluginManager); scheduler = new VelocityScheduler(pluginManager, Sleeper.SYSTEM); + channelRegistrar = new VelocityChannelRegistrar(); loadPlugins(); try { @@ -304,4 +307,9 @@ public class VelocityServer implements ProxyServer { public VelocityScheduler getScheduler() { return scheduler; } + + @Override + public VelocityChannelRegistrar getChannelRegistrar() { + return channelRegistrar; + } } 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 b48aa2dcd..29e24b2f0 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 @@ -1,6 +1,8 @@ package com.velocitypowered.proxy.connection.backend; import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.proxy.messages.ChannelSide; +import com.velocitypowered.api.proxy.messages.MessageHandler; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; @@ -65,7 +67,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { return; } - connection.getPlayer().getConnection().write(pm); + MessageHandler.ForwardStatus status = VelocityServer.getServer().getChannelRegistrar().handlePluginMessage( + connection, ChannelSide.FROM_SERVER, pm); + if (status == MessageHandler.ForwardStatus.FORWARD) { + connection.getPlayer().getConnection().write(pm); + } } else { // Just forward the packet on. We don't have anything to handle at this time. connection.getPlayer().getConnection().write(packet); 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 2f185f328..0cce4bfc4 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 @@ -1,6 +1,8 @@ package com.velocitypowered.proxy.connection.client; import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.proxy.messages.ChannelSide; +import com.velocitypowered.api.proxy.messages.MessageHandler; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; @@ -220,8 +222,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return; } - // We're going to forward on the original packet. - player.getConnectedServer().getMinecraftConnection().write(packet); + MessageHandler.ForwardStatus status = VelocityServer.getServer().getChannelRegistrar().handlePluginMessage( + player, ChannelSide.FROM_CLIENT, packet); + if (status == MessageHandler.ForwardStatus.FORWARD) { + // We're going to forward on the original packet. + player.getConnectedServer().getMinecraftConnection().write(packet); + } } public Set getClientPluginMsgChannels() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java new file mode 100644 index 000000000..8b74c4917 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java @@ -0,0 +1,48 @@ +package com.velocitypowered.proxy.messages; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.messages.*; +import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class VelocityChannelRegistrar implements ChannelRegistrar { + private static final Logger logger = LogManager.getLogger(VelocityChannelRegistrar.class); + private final Map handlers = new ConcurrentHashMap<>(); + + @Override + public void register(MessageHandler handler, ChannelIdentifier... identifiers) { + for (ChannelIdentifier identifier : identifiers) { + Preconditions.checkArgument(identifier instanceof LegacyChannelIdentifier || identifier instanceof MinecraftChannelIdentifier, + "identifier is unknown"); + } + + for (ChannelIdentifier identifier : identifiers) { + handlers.put(identifier.getId(), handler); + } + } + + public MessageHandler.ForwardStatus handlePluginMessage(ChannelMessageSource source, ChannelSide side, PluginMessage message) { + MessageHandler handler = handlers.get(message.getChannel()); + if (handler == null) { + // Nothing we can do. + return MessageHandler.ForwardStatus.FORWARD; + } + + try { + return handler.handle(source, side, message.getData()); + } catch (Exception e) { + logger.info("Unable to handle plugin message on channel {} for {}", message.getChannel(), source); + // In case of doubt, do not forward the message on. + return MessageHandler.ForwardStatus.HANDLED; + } + } + + @Override + public void unregister(ChannelIdentifier... identifiers) { + + } +}