From c520e04ac890c0b17972b03f5b0e92a40b7f7976 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 2 Sep 2018 20:50:24 -0400 Subject: [PATCH 01/62] [BREAKING] PluginMessageEvent instead --- .../event/connection/PluginMessageEvent.java | 94 +++++++++++++++++++ .../api/proxy/messages/ChannelRegistrar.java | 8 +- .../api/proxy/messages/ChannelSide.java | 15 --- .../api/proxy/messages/MessageHandler.java | 28 ------ .../backend/BackendPlaySessionHandler.java | 16 ++-- .../client/ClientPlaySessionHandler.java | 17 ++-- .../messages/VelocityChannelRegistrar.java | 29 +----- 7 files changed, 120 insertions(+), 87 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java delete mode 100644 api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelSide.java delete mode 100644 api/src/main/java/com/velocitypowered/api/proxy/messages/MessageHandler.java diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java new file mode 100644 index 000000000..4caf3280c --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java @@ -0,0 +1,94 @@ +package com.velocitypowered.api.event.connection; + +import com.google.common.base.Preconditions; +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import com.velocitypowered.api.event.ResultedEvent; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.messages.ChannelMessageSink; +import com.velocitypowered.api.proxy.messages.ChannelMessageSource; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.Arrays; + +/** + * This event is fired when a plugin message is sent to the proxy, either from a client ({@link com.velocitypowered.api.proxy.Player}) + * or a server ({@link com.velocitypowered.api.proxy.ServerConnection}). + */ +public class PluginMessageEvent implements ResultedEvent { + private final ChannelMessageSource source; + private final ChannelMessageSink target; + private final ChannelIdentifier identifier; + private final byte[] data; + private ForwardResult result; + + public PluginMessageEvent(ChannelMessageSource source, ChannelMessageSink target, ChannelIdentifier identifier, byte[] data) { + this.source = Preconditions.checkNotNull(source, "source"); + this.target = Preconditions.checkNotNull(target, "target"); + this.identifier = Preconditions.checkNotNull(identifier, "identifier"); + this.data = Preconditions.checkNotNull(data, "data"); + this.result = ForwardResult.forward(); + } + + @Override + public ForwardResult getResult() { + return result; + } + + @Override + public void setResult(@NonNull ForwardResult result) { + this.result = Preconditions.checkNotNull(result, "result"); + } + + public ChannelMessageSource getSource() { + return source; + } + + public ChannelMessageSink getTarget() { + return target; + } + + public ChannelIdentifier getIdentifier() { + return identifier; + } + + public byte[] getData() { + return Arrays.copyOf(data, data.length); + } + + public ByteArrayDataInput dataAsDataStream() { + return ByteStreams.newDataInput(data); + } + + /** + * A result determining whether or not to forward this message on. + */ + public static class ForwardResult implements ResultedEvent.Result { + private static final ForwardResult ALLOWED = new ForwardResult(true); + private static final ForwardResult DENIED = new ForwardResult(false); + + private final boolean allowed; + + private ForwardResult(boolean b) { + this.allowed = b; + } + + @Override + public boolean isAllowed() { + return allowed; + } + + @Override + public String toString() { + return allowed ? "forward to sink" : "handled message at proxy"; + } + + public static ForwardResult forward() { + return ALLOWED; + } + + public static ForwardResult handled() { + return DENIED; + } + } +} 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 2d77988b5..84db8799d 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,16 +1,14 @@ 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. + * Represents an interface to register and unregister {@link ChannelIdentifier}s for the proxy to listen on. */ public interface ChannelRegistrar { /** - * Registers the specified message handler to listen for plugin messages on the specified channels. - * @param handler the handler to register + * Registers the specified message identifiers to listen on for the * @param identifiers the channel identifiers to register */ - void register(MessageHandler handler, ChannelIdentifier... identifiers); + void register(ChannelIdentifier... identifiers); /** * Unregisters the handler for the specified channel. 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 deleted file mode 100644 index 12256f432..000000000 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelSide.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.velocitypowered.api.proxy.messages; - -/** - * Represents from "which side" of the proxy the plugin message came from. - */ -public enum ChannelSide { - /** - * The plugin message came from a server that a client was connected to. - */ - FROM_SERVER, - /** - * The plugin message came from the client. - */ - FROM_CLIENT -} 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 deleted file mode 100644 index 70a7e5fa7..000000000 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/MessageHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.velocitypowered.api.proxy.messages; - -/** - * Represents a handler for handling plugin messages. - */ -public interface MessageHandler { - /** - * Handles an incoming plugin message. - * @param source the source of the plugin message - * @param side from where the plugin message originated - * @param identifier the channel on which the message was sent - * @param data the data inside the plugin message - * @return a {@link ForwardStatus} indicating whether or not to forward this plugin message on - */ - ForwardStatus handle(ChannelMessageSource source, ChannelSide side, ChannelIdentifier identifier, byte[] data); - - enum ForwardStatus { - /** - * Forwards this plugin message on to the client or server, depending on the {@link ChannelSide} it originated - * from. - */ - FORWARD, - /** - * Discard the plugin message and do not forward it on. - */ - HANDLED - } -} 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 60ed678c0..8fbee6e6d 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,8 +1,7 @@ package com.velocitypowered.proxy.connection.backend; +import com.velocitypowered.api.event.connection.PluginMessageEvent; 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; @@ -68,11 +67,14 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { return; } - MessageHandler.ForwardStatus status = server.getChannelRegistrar().handlePluginMessage(connection, - ChannelSide.FROM_SERVER, pm); - if (status == MessageHandler.ForwardStatus.FORWARD) { - connection.getPlayer().getConnection().write(pm); - } + PluginMessageEvent event = new PluginMessageEvent(connection, connection.getPlayer(), server.getChannelRegistrar().getFromId(pm.getChannel()), + pm.getData()); + server.getEventManager().fire(event) + .thenAcceptAsync(pme -> { + if (pme.getResult().isAllowed()) { + connection.getPlayer().getConnection().write(pm); + } + }, connection.getMinecraftConnection().getChannel().eventLoop()); } 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 9c36766ef..aa296599d 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,8 +1,7 @@ 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.api.event.connection.PluginMessageEvent; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; @@ -235,12 +234,14 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return; } - MessageHandler.ForwardStatus status = server.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); - } + PluginMessageEvent event = new PluginMessageEvent(player, player.getConnectedServer(), + server.getChannelRegistrar().getFromId(packet.getChannel()), packet.getData()); + server.getEventManager().fire(event) + .thenAcceptAsync(pme -> { + if (pme.getResult().isAllowed()) { + player.getConnectedServer().getMinecraftConnection().write(packet); + } + }, player.getConnectedServer().getMinecraftConnection().getChannel().eventLoop()); } 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 index 1fb47cce1..1536716cc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java @@ -3,9 +3,6 @@ package com.velocitypowered.proxy.messages; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; 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.Collection; import java.util.Map; @@ -13,39 +10,20 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; public class VelocityChannelRegistrar implements ChannelRegistrar { - private static final Logger logger = LogManager.getLogger(VelocityChannelRegistrar.class); - private final Map handlers = new ConcurrentHashMap<>(); private final Map identifierMap = new ConcurrentHashMap<>(); @Override - public void register(MessageHandler handler, ChannelIdentifier... identifiers) { + public void register(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); identifierMap.put(identifier.getId(), identifier); } } - public MessageHandler.ForwardStatus handlePluginMessage(ChannelMessageSource source, ChannelSide side, PluginMessage message) { - MessageHandler handler = handlers.get(message.getChannel()); - ChannelIdentifier identifier = identifierMap.get(message.getChannel()); - if (handler == null || identifier == null) { - return MessageHandler.ForwardStatus.FORWARD; - } - - try { - return handler.handle(source, side, identifier, 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) { for (ChannelIdentifier identifier : identifiers) { @@ -54,7 +32,6 @@ public class VelocityChannelRegistrar implements ChannelRegistrar { } for (ChannelIdentifier identifier : identifiers) { - handlers.remove(identifier.getId()); identifierMap.remove(identifier.getId()); } } @@ -73,4 +50,8 @@ public class VelocityChannelRegistrar implements ChannelRegistrar { public boolean registered(String id) { return identifierMap.containsKey(id); } + + public ChannelIdentifier getFromId(String id) { + return identifierMap.get(id); + } } From 74bf246c3958e640a183aea653d839d636ae2554 Mon Sep 17 00:00:00 2001 From: Leymooo Date: Tue, 11 Sep 2018 16:15:54 +0300 Subject: [PATCH 02/62] Add PostLoginEvent. Resolve #72 --- .../api/event/connection/PostLoginEvent.java | 29 +++++++++++ .../client/LoginSessionHandler.java | 48 ++++++++++--------- 2 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java new file mode 100644 index 000000000..8cd3dea50 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java @@ -0,0 +1,29 @@ +package com.velocitypowered.api.event.connection; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.Player; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * This event is fired once the player has been successfully authenticated and + * fully initialized and player will be connected to server after this event + */ +public class PostLoginEvent { + + private final Player player; + + public PostLoginEvent(@NonNull Player player) { + this.player = Preconditions.checkNotNull(player, "player"); + } + + public Player getPlayer() { + return player; + } + + @Override + public String toString() { + return "PostLoginEvent{" + + "player=" + player + + '}'; + } +} 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 ff728178f..0ae515ffb 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,6 +2,7 @@ package com.velocitypowered.proxy.connection.client; import com.google.common.base.Preconditions; import com.velocitypowered.api.event.connection.LoginEvent; +import com.velocitypowered.api.event.connection.PostLoginEvent; import com.velocitypowered.api.event.connection.PreLoginEvent; import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult; import com.velocitypowered.api.event.permission.PermissionsSetupEvent; @@ -39,9 +40,10 @@ import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; public class LoginSessionHandler implements MinecraftSessionHandler { + private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class); - private static final String MOJANG_SERVER_AUTH_URL = - "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; + private static final String MOJANG_SERVER_AUTH_URL + = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; private final VelocityServer server; private final MinecraftConnection inbound; @@ -193,26 +195,26 @@ public class LoginSessionHandler implements MinecraftSessionHandler { apiInbound.getVirtualHost().orElse(null)); return server.getEventManager().fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS)) - .thenCompose(event -> { - // wait for permissions to load, then set the players permission function - player.setPermissionFunction(event.createFunction(player)); - // then call & wait for the login event - return server.getEventManager().fire(new LoginEvent(player)); - }) - // then complete the connection - .thenAcceptAsync(event -> { - if (inbound.isClosed()) { - // The player was disconnected - return; - } - if (!event.getResult().isAllowed()) { - // The component is guaranteed to be provided if the connection was denied. - inbound.closeWith(Disconnect.create(event.getResult().getReason().get())); - return; - } + .thenCompose(event -> { + // wait for permissions to load, then set the players permission function + player.setPermissionFunction(event.createFunction(player)); + // then call & wait for the login event + return server.getEventManager().fire(new LoginEvent(player)); + }) + // then complete the connection + .thenAcceptAsync(event -> { + if (inbound.isClosed()) { + // The player was disconnected + return; + } + if (!event.getResult().isAllowed()) { + // The component is guaranteed to be provided if the connection was denied. + inbound.closeWith(Disconnect.create(event.getResult().getReason().get())); + return; + } - handleProxyLogin(player); - }, inbound.getChannel().eventLoop()); + handleProxyLogin(player); + }, inbound.getChannel().eventLoop()); }); } @@ -244,7 +246,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler { logger.info("{} has connected", player); inbound.setSessionHandler(new InitialConnectSessionHandler(player)); - player.createConnectionRequest(toTry.get()).fireAndForget(); + server.getEventManager().fire(new PostLoginEvent(player)).thenRun(() -> { + player.createConnectionRequest(toTry.get()).fireAndForget(); + }); } @Override From 79bb43468f97399663902bb6eb64d9e380ee2547 Mon Sep 17 00:00:00 2001 From: Leymooo Date: Tue, 11 Sep 2018 17:42:24 +0300 Subject: [PATCH 03/62] remove @NonNull, revert reindentation --- .../api/event/connection/PostLoginEvent.java | 3 +- .../client/LoginSessionHandler.java | 42 +++++++++---------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java index 8cd3dea50..7ecc23bbc 100644 --- a/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/connection/PostLoginEvent.java @@ -2,7 +2,6 @@ package com.velocitypowered.api.event.connection; import com.google.common.base.Preconditions; import com.velocitypowered.api.proxy.Player; -import org.checkerframework.checker.nullness.qual.NonNull; /** * This event is fired once the player has been successfully authenticated and @@ -12,7 +11,7 @@ public class PostLoginEvent { private final Player player; - public PostLoginEvent(@NonNull Player player) { + public PostLoginEvent(Player player) { this.player = Preconditions.checkNotNull(player, "player"); } 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 0ae515ffb..fed5cb580 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 @@ -42,8 +42,8 @@ import java.util.concurrent.ThreadLocalRandom; public class LoginSessionHandler implements MinecraftSessionHandler { private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class); - private static final String MOJANG_SERVER_AUTH_URL - = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; + private static final String MOJANG_SERVER_AUTH_URL = + "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; private final VelocityServer server; private final MinecraftConnection inbound; @@ -195,26 +195,26 @@ public class LoginSessionHandler implements MinecraftSessionHandler { apiInbound.getVirtualHost().orElse(null)); return server.getEventManager().fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS)) - .thenCompose(event -> { - // wait for permissions to load, then set the players permission function - player.setPermissionFunction(event.createFunction(player)); - // then call & wait for the login event - return server.getEventManager().fire(new LoginEvent(player)); - }) - // then complete the connection - .thenAcceptAsync(event -> { - if (inbound.isClosed()) { - // The player was disconnected - return; - } - if (!event.getResult().isAllowed()) { - // The component is guaranteed to be provided if the connection was denied. - inbound.closeWith(Disconnect.create(event.getResult().getReason().get())); - return; - } + .thenCompose(event -> { + // wait for permissions to load, then set the players permission function + player.setPermissionFunction(event.createFunction(player)); + // then call & wait for the login event + return server.getEventManager().fire(new LoginEvent(player)); + }) + // then complete the connection + .thenAcceptAsync(event -> { + if (inbound.isClosed()) { + // The player was disconnected + return; + } + if (!event.getResult().isAllowed()) { + // The component is guaranteed to be provided if the connection was denied. + inbound.closeWith(Disconnect.create(event.getResult().getReason().get())); + return; + } - handleProxyLogin(player); - }, inbound.getChannel().eventLoop()); + handleProxyLogin(player); + }, inbound.getChannel().eventLoop()); }); } From bc86a12c57e8e36952f1927cd63f35b7024e4972 Mon Sep 17 00:00:00 2001 From: Leymooo Date: Wed, 12 Sep 2018 11:43:33 +0300 Subject: [PATCH 04/62] expand PreLoginComponentResult with force offline mode --- .../api/event/connection/PreLoginEvent.java | 25 ++++++++++++++++--- .../client/LoginSessionHandler.java | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) 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 ef8edfb9f..9d0620f8b 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 @@ -56,31 +56,41 @@ public class PreLoginEvent implements ResultedEvent Date: Wed, 12 Sep 2018 10:21:28 +0100 Subject: [PATCH 05/62] Don't fire a FML reset packet on first login, set it as required for the second join after the first. Fixes #69 --- .../connection/backend/LoginSessionHandler.java | 6 ------ .../connection/client/ClientPlaySessionHandler.java | 12 +++++++++++- 2 files changed, 11 insertions(+), 7 deletions(-) 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 c65f1745e..1a7f5e2df 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 @@ -82,12 +82,6 @@ public class LoginSessionHandler implements MinecraftSessionHandler { if (existingConnection == null) { // Strap on the play session handler connection.getPlayer().getConnection().setSessionHandler(new ClientPlaySessionHandler(server, connection.getPlayer())); - - // This is for legacy Forge servers - during first connection the FML handshake will transition to complete regardless - // Thus, we need to ensure that a reset packet is ALWAYS sent on first switch. - // - // The call will handle if the player is not a Forge player appropriately. - connection.getPlayer().getConnection().setCanSendLegacyFMLResetPacket(true); } else { // The previous server connection should become obsolete. // Before we remove it, if the server we are departing is modded, we must always reset the client state. 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 b7cb0f91e..d178fe7aa 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 @@ -164,9 +164,19 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { public void handleBackendJoinGame(JoinGame joinGame) { resetPingData(); // reset ping data; if (!spawned) { - // nothing special to do here + // Nothing special to do with regards to spawning the player spawned = true; player.getConnection().delayedWrite(joinGame); + + // We have something special to do for legacy Forge servers - during first connection the FML handshake + // will transition to complete regardless. Thus, we need to ensure that a reset packet is ALWAYS sent on + // first switch. + // + // As we know that calling this branch only happens on first join, we set that if we are a Forge + // client that we must reset on the next switch. + // + // The call will handle if the player is not a Forge player appropriately. + player.getConnection().setCanSendLegacyFMLResetPacket(true); } else { // Ah, this is the meat and potatoes of the whole venture! // From 6196f94adf803eab442d9e51129c1236cf55f293 Mon Sep 17 00:00:00 2001 From: Leymooo Date: Wed, 12 Sep 2018 16:28:34 +0300 Subject: [PATCH 06/62] I think we dont need this comment --- .../api/event/connection/PreLoginEvent.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 9d0620f8b..36d7b98c0 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 @@ -52,7 +52,7 @@ public class PreLoginEvent implements ResultedEvent Date: Wed, 12 Sep 2018 22:41:51 -0700 Subject: [PATCH 07/62] ServerPreConnectEvent#getInfo -> getServer --- .../api/event/player/ServerPreConnectEvent.java | 12 ++++++------ .../proxy/connection/client/ConnectedPlayer.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java index 2d86c06f8..a91f819cd 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java @@ -50,11 +50,11 @@ public class ServerPreConnectEvent implements ResultedEvent getInfo() { - return Optional.ofNullable(info); + public Optional getServer() { + return Optional.ofNullable(server); } @Override @@ -71,7 +71,7 @@ public class ServerPreConnectEvent implements ResultedEvent Date: Thu, 13 Sep 2018 10:16:10 +0300 Subject: [PATCH 08/62] Refactor PreLoginComponentResult --- .../api/event/connection/PreLoginEvent.java | 73 ++++++++++++------- 1 file changed, 46 insertions(+), 27 deletions(-) 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 36d7b98c0..e4b4b1460 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,10 @@ 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 java.util.Optional; import net.kyori.text.Component; +import net.kyori.text.serializer.ComponentSerializers; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -54,33 +56,35 @@ public class PreLoginEvent implements ResultedEvent reason; + + private PreLoginComponentResult(Result result, @Nullable Component reason) { + this.result = result; + this.reason = Optional.ofNullable(reason); } - private PreLoginComponentResult(@Nullable Component reason) { - super(reason == null, reason); - // Don't care about this - this.onlineMode = false; - this.forceOfflineMode = false; + @Override + public boolean isAllowed() { + return result != Result.DISALLOWED; + } + + public Optional getReason() { + return reason; } public boolean isOnlineModeAllowed() { - return this.onlineMode; + return result == Result.FORCE_ONLINE; } public boolean isForceOfflineMode() { - return forceOfflineMode; + return result == Result.FORCE_OFFLINE; } @Override @@ -91,12 +95,18 @@ public class PreLoginEvent implements ResultedEvent Date: Thu, 13 Sep 2018 10:33:27 +0100 Subject: [PATCH 09/62] Only send FML/FML|MP plugin messages if the player has joined the server. See #78 --- .../proxy/connection/VelocityConstants.java | 2 ++ .../backend/VelocityServerConnection.java | 6 ++++++ .../connection/client/ClientPlaySessionHandler.java | 13 ++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java index b6956336b..b524b31e1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java @@ -8,6 +8,8 @@ public class VelocityConstants { public static final String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info"; public static final String FORGE_LEGACY_HANDSHAKE_CHANNEL = "FML|HS"; + public static final String FORGE_LEGACY_CHANNEL = "FML"; + public static final String FORGE_MULTIPART_LEGACY_CHANNEL = "FML|MP"; public static final byte[] FORGE_LEGACY_HANDSHAKE_RESET_DATA = new byte[] { -2, 0 }; } 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 ceb53b9e6..59b97be24 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 @@ -127,6 +127,12 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, minecraftConnection.write(login); } + public void writeIfJoined(PluginMessage message) { + if (hasCompletedJoin) { + minecraftConnection.write(message); + } + } + public MinecraftConnection getMinecraftConnection() { return minecraftConnection; } 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 b7cb0f91e..137b97f71 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 @@ -4,6 +4,7 @@ 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.connection.VelocityConstants; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; @@ -275,8 +276,18 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { MessageHandler.ForwardStatus status = server.getChannelRegistrar().handlePluginMessage(player, ChannelSide.FROM_CLIENT, packet); if (status == MessageHandler.ForwardStatus.FORWARD) { + String channel = packet.getChannel(); + // We're going to forward on the original packet. - player.getConnectedServer().getMinecraftConnection().write(packet); + // + // If we have Forge messages, we may need to drop them if the server switch has + // not completed yet. + if (channel.equals(VelocityConstants.FORGE_LEGACY_CHANNEL) + || channel.equals(VelocityConstants.FORGE_MULTIPART_LEGACY_CHANNEL)) { + player.getConnectedServer().writeIfJoined(packet); + } else { + player.getConnectedServer().getMinecraftConnection().write(packet); + } } } From 961757b481107285655a30daf114d8827ffa4fcf Mon Sep 17 00:00:00 2001 From: Leymooo Date: Thu, 13 Sep 2018 23:00:12 +0300 Subject: [PATCH 10/62] fix compile --- .../com/velocitypowered/api/event/connection/PreLoginEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e4b4b1460..04fdc7622 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 @@ -56,7 +56,7 @@ public class PreLoginEvent implements ResultedEvent Date: Thu, 13 Sep 2018 22:41:21 -0600 Subject: [PATCH 11/62] Add CONNECT_TIMEOUT_MILLIS to fix timeout errors. --- .../proxy/connection/backend/VelocityServerConnection.java | 1 + .../com/velocitypowered/proxy/network/ConnectionManager.java | 1 + 2 files changed, 2 insertions(+) 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 59b97be24..7a52248fc 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 @@ -56,6 +56,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, CompletableFuture result = new CompletableFuture<>(); server.initializeGenericBootstrap() .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, SERVER_READ_TIMEOUT_SECONDS * 1000) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index cc37d62b2..ff98ae029 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -89,6 +89,7 @@ public final class ConnectionManager { ch.pipeline().addLast(Connections.HANDLER, connection); } }) + .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, CLIENT_READ_TIMEOUT_SECONDS * 1000) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); From cf46659d9e6ebd2431f8574e50571f9ede2bf176 Mon Sep 17 00:00:00 2001 From: PurpleIsEverything Date: Thu, 13 Sep 2018 22:53:23 -0600 Subject: [PATCH 12/62] Change to a 5 second connection timeout. --- .../proxy/connection/backend/VelocityServerConnection.java | 3 ++- .../com/velocitypowered/proxy/network/ConnectionManager.java | 2 +- .../java/com/velocitypowered/proxy/network/Connections.java | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) 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 7a52248fc..ed6b3b6a0 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 @@ -33,6 +33,7 @@ import static com.velocitypowered.proxy.network.Connections.HANDLER; import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER; import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT; +import static com.velocitypowered.proxy.network.Connections.CONNECTION_TIMEOUT_SECONDS; import static com.velocitypowered.proxy.network.Connections.SERVER_READ_TIMEOUT_SECONDS; public class VelocityServerConnection implements MinecraftConnectionAssociation, ServerConnection { @@ -56,7 +57,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, CompletableFuture result = new CompletableFuture<>(); server.initializeGenericBootstrap() .option(ChannelOption.TCP_NODELAY, true) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, SERVER_READ_TIMEOUT_SECONDS * 1000) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECTION_TIMEOUT_SECONDS * 1000) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index ff98ae029..e1ffe129e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -89,7 +89,7 @@ public final class ConnectionManager { ch.pipeline().addLast(Connections.HANDLER, connection); } }) - .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, CLIENT_READ_TIMEOUT_SECONDS * 1000) + .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECTION_TIMEOUT_SECONDS * 1000) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java index 518a3acbe..59869eede 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java @@ -16,4 +16,5 @@ public interface Connections { int CLIENT_READ_TIMEOUT_SECONDS = 30; // client -> proxy int SERVER_READ_TIMEOUT_SECONDS = 30; // proxy -> server + int CONNECTION_TIMEOUT_SECONDS = 5; } From 0469aaa03a9c2016e9f8570726089e78924fef30 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 14 Sep 2018 01:00:56 -0400 Subject: [PATCH 13/62] Fix several invalid connection closure issues. --- .../proxy/connection/backend/BackendPlaySessionHandler.java | 4 ++-- .../proxy/connection/backend/VelocityServerConnection.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) 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 1a011a2e8..0d0236927 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 @@ -32,7 +32,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { if (!connection.getPlayer().isActive()) { // Connection was left open accidentally. Close it so as to avoid "You logged in from another location" // errors. - connection.getMinecraftConnection().close(); + connection.disconnect(); return; } @@ -99,7 +99,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { if (!connection.getPlayer().isActive()) { // Connection was left open accidentally. Close it so as to avoid "You logged in from another location" // errors. - connection.getMinecraftConnection().close(); + connection.disconnect(); return; } 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 ed6b3b6a0..0d0a4be0a 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 @@ -149,8 +149,10 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, } public void disconnect() { - minecraftConnection.close(); - minecraftConnection = null; + if (minecraftConnection != null) { + minecraftConnection.close(); + minecraftConnection = null; + } } @Override From 496c579e469e5ba13e26c98bf8ae8bc7ee2a5113 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 14 Sep 2018 13:56:38 -0400 Subject: [PATCH 14/62] Drop non-FML handshake packets if the game start process is not done. --- .../proxy/connection/client/ClientPlaySessionHandler.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 6bc44a2b0..353c2d0c4 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 @@ -278,8 +278,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) { - // Ensure that the messages are forwarded - player.getConnectedServer().getMinecraftConnection().write(packet); + // Ensure that the FML handshake is forwarded. Do not try to forward other client-side plugin messages, as + // some mods are poorly coded and will crash if mod packets are sent during the handshake/join game process. + if (packet.getChannel().equals(VelocityConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) { + player.getConnectedServer().getMinecraftConnection().write(packet); + } return; } From 9776675b7067307a925e3ce401d33933d5b65db5 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 14 Sep 2018 14:16:32 -0400 Subject: [PATCH 15/62] Queue mod plugin messages instead. --- .../client/ClientPlaySessionHandler.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) 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 353c2d0c4..295b4b131 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 @@ -34,6 +34,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private boolean spawned = false; private final List serverBossBars = new ArrayList<>(); private final Set clientPluginMsgChannels = new HashSet<>(); + private final Queue loginPluginMessages = new ArrayDeque<>(); private final VelocityServer server; public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) { @@ -163,7 +164,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } public void handleBackendJoinGame(JoinGame joinGame) { - resetPingData(); // reset ping data; + resetPingData(); // reset ping data if (!spawned) { // Nothing special to do with regards to spawning the player spawned = true; @@ -221,6 +222,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { channel, toRegister)); } + // If we had plugin messages queued during login/FML handshake, send them now. + PluginMessage pm; + while ((pm = loginPluginMessages.poll()) != null) { + player.getConnectedServer().getMinecraftConnection().delayedWrite(pm); + } + // Flush everything player.getConnection().flush(); player.getConnectedServer().getMinecraftConnection().flush(); @@ -278,10 +285,14 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) { - // Ensure that the FML handshake is forwarded. Do not try to forward other client-side plugin messages, as - // some mods are poorly coded and will crash if mod packets are sent during the handshake/join game process. if (packet.getChannel().equals(VelocityConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) { + // Always forward the FML handshake to the remote server. player.getConnectedServer().getMinecraftConnection().write(packet); + } else { + // The client is trying to send messages too early. This is primarily caused by mods, but it's further + // aggravated by Velocity. To work around these issues, we will queue any non-FML handshake messages to + // be sent once the JoinGame packet has been received by the proxy. + loginPluginMessages.add(packet); } return; } From e9568e1b6c27764a320bd6d2d708e4a017251503 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 14 Sep 2018 15:26:51 -0400 Subject: [PATCH 16/62] Do not write plugin messages from the server if the player hasn't joined --- .../connection/client/ClientPlaySessionHandler.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) 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 295b4b131..f550865d2 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 @@ -300,18 +300,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { MessageHandler.ForwardStatus status = server.getChannelRegistrar().handlePluginMessage(player, ChannelSide.FROM_CLIENT, packet); if (status == MessageHandler.ForwardStatus.FORWARD) { - String channel = packet.getChannel(); - - // We're going to forward on the original packet. - // - // If we have Forge messages, we may need to drop them if the server switch has - // not completed yet. - if (channel.equals(VelocityConstants.FORGE_LEGACY_CHANNEL) - || channel.equals(VelocityConstants.FORGE_MULTIPART_LEGACY_CHANNEL)) { - player.getConnectedServer().writeIfJoined(packet); - } else { - player.getConnectedServer().getMinecraftConnection().write(packet); - } + player.getConnectedServer().writeIfJoined(packet); } } From 2b1d55a0fcfae68661277f0311fe1f2d0990d716 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 15 Sep 2018 01:16:26 -0400 Subject: [PATCH 17/62] Expose original server in ServerPreConnectEvent --- .../api/event/player/ServerPreConnectEvent.java | 11 +++++++++-- .../proxy/connection/client/ConnectedPlayer.java | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java index a91f819cd..c0ad88736 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java @@ -14,11 +14,13 @@ import java.util.Optional; */ public class ServerPreConnectEvent implements ResultedEvent { private final Player player; + private final ServerInfo originalServer; private ServerResult result; - public ServerPreConnectEvent(Player player, ServerResult result) { + public ServerPreConnectEvent(Player player, ServerInfo originalServer) { this.player = Preconditions.checkNotNull(player, "player"); - this.result = Preconditions.checkNotNull(result, "result"); + this.originalServer = Preconditions.checkNotNull(originalServer, "originalServer"); + this.result = ServerResult.allowed(originalServer); } public Player getPlayer() { @@ -35,10 +37,15 @@ public class ServerPreConnectEvent implements ResultedEvent> CONNECTION_NOTIFIER = @@ -56,13 +54,11 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, public CompletableFuture connect() { CompletableFuture result = new CompletableFuture<>(); server.initializeGenericBootstrap() - .option(ChannelOption.TCP_NODELAY, true) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECTION_TIMEOUT_SECONDS * 1000) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline() - .addLast(READ_TIMEOUT, new ReadTimeoutHandler(SERVER_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)) + .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.SECONDS)) .addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder()) .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolConstants.Direction.CLIENTBOUND)) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index e1ffe129e..9a115a608 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -75,7 +75,7 @@ public final class ConnectionManager { @Override protected void initChannel(final Channel ch) { ch.pipeline() - .addLast(READ_TIMEOUT, new ReadTimeoutHandler(CLIENT_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)) + .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.SECONDS)) .addLast(LEGACY_PING_DECODER, new LegacyPingDecoder()) .addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder()) .addLast(LEGACY_PING_ENCODER, LegacyPingEncoder.INSTANCE) @@ -89,7 +89,6 @@ public final class ConnectionManager { ch.pipeline().addLast(Connections.HANDLER, connection); } }) - .childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECTION_TIMEOUT_SECONDS * 1000) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); @@ -126,7 +125,9 @@ public final class ConnectionManager { public Bootstrap createWorker() { return new Bootstrap() .channel(this.transportType.socketChannelClass) - .group(this.workerGroup); + .group(this.workerGroup) + .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, server.getConfiguration().getConnectTimeout()); } public void shutdown() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java index 59869eede..fb248dc6a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java @@ -13,8 +13,4 @@ public interface Connections { String MINECRAFT_DECODER = "minecraft-decoder"; String MINECRAFT_ENCODER = "minecraft-encoder"; String READ_TIMEOUT = "read-timeout"; - - int CLIENT_READ_TIMEOUT_SECONDS = 30; // client -> proxy - int SERVER_READ_TIMEOUT_SECONDS = 30; // proxy -> server - int CONNECTION_TIMEOUT_SECONDS = 5; } From 84947564e432b6b3089df810c24abc54152aa39b Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 15 Sep 2018 01:46:28 -0400 Subject: [PATCH 19/62] Handle unexpected disconnects without a reason. --- .../backend/BackendPlaySessionHandler.java | 14 ++++++++++++++ .../backend/VelocityServerConnection.java | 6 ++++++ .../proxy/connection/client/ConnectedPlayer.java | 15 ++++++++++----- .../proxy/connection/util/ConnectionMessages.java | 1 + 4 files changed, 31 insertions(+), 5 deletions(-) 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 0d0236927..e62eb4615 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 @@ -6,6 +6,7 @@ import com.velocitypowered.api.proxy.messages.MessageHandler; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; +import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.packet.*; @@ -44,6 +45,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { connection.getPlayer().getConnection().write(packet); } else if (packet instanceof Disconnect) { Disconnect original = (Disconnect) packet; + connection.disconnect(); connection.getPlayer().handleConnectionException(connection.getServerInfo(), original); } else if (packet instanceof JoinGame) { playerHandler.handleBackendJoinGame((JoinGame) packet); @@ -113,6 +115,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { connection.getPlayer().handleConnectionException(connection.getServerInfo(), throwable); } + public VelocityServer getServer() { + return server; + } + + @Override + public void disconnected() { + if (connection.isGracefulDisconnect()) { + return; + } + connection.getPlayer().handleConnectionException(connection.getServerInfo(), Disconnect.create(ConnectionMessages.UNEXPECTED_DISCONNECT)); + } + private boolean canForwardPluginMessage(PluginMessage message) { ClientPlaySessionHandler playerHandler = (ClientPlaySessionHandler) connection.getPlayer().getConnection().getSessionHandler(); 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 2362a27d5..496a4a686 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 @@ -44,6 +44,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, private MinecraftConnection minecraftConnection; private boolean legacyForge = false; private boolean hasCompletedJoin = false; + private boolean gracefulDisconnect = false; public VelocityServerConnection(ServerInfo target, ConnectedPlayer proxyPlayer, VelocityServer server) { this.serverInfo = target; @@ -148,6 +149,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, if (minecraftConnection != null) { minecraftConnection.close(); minecraftConnection = null; + gracefulDisconnect = true; } } @@ -181,4 +183,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, public void setHasCompletedJoin(boolean hasCompletedJoin) { this.hasCompletedJoin = hasCompletedJoin; } + + public boolean isGracefulDisconnect() { + return gracefulDisconnect; + } } 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 2d3bd80d9..b56b74753 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 @@ -205,14 +205,19 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason); if (connectedServer != null && connectedServer.getServerInfo().equals(info)) { logger.error("{}: kicked from server {}: {}", this, info.getName(), plainTextReason); + handleConnectionException(info, disconnectReason, TextComponent.builder() + .content("Kicked from " + info.getName() + ": ") + .color(TextColor.RED) + .append(disconnectReason) + .build()); } else { logger.error("{}: disconnected while connecting to {}: {}", this, info.getName(), plainTextReason); + handleConnectionException(info, disconnectReason, TextComponent.builder() + .content("Unable to connect to " + info.getName() + ": ") + .color(TextColor.RED) + .append(disconnectReason) + .build()); } - handleConnectionException(info, disconnectReason, TextComponent.builder() - .content("Unable to connect to " + info.getName() + ": ") - .color(TextColor.RED) - .append(disconnectReason) - .build()); } private void handleConnectionException(ServerInfo info, @Nullable Component kickReason, Component friendlyReason) { 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 b2ff70a41..727750d62 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 @@ -7,6 +7,7 @@ public class ConnectionMessages { public static final TextComponent ALREADY_CONNECTED = TextComponent.of("You are already connected to this server!", TextColor.RED); public static final TextComponent IN_PROGRESS = TextComponent.of("You are already connecting to a server!", TextColor.RED); public static final TextComponent INTERNAL_SERVER_CONNECTION_ERROR = TextComponent.of("Internal server connection error"); + public static final TextComponent UNEXPECTED_DISCONNECT = TextComponent.of("Unexpectedly disconnected from server - crash?"); private ConnectionMessages() { throw new AssertionError(); From ab568405dd1394045605005baf3960209bb42a77 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 15 Sep 2018 02:22:52 -0400 Subject: [PATCH 20/62] Cleaned up client plugin message logic. --- .../client/ClientPlaySessionHandler.java | 33 +++++++------------ .../protocol/util/PluginMessageUtil.java | 17 +++++++--- 2 files changed, 24 insertions(+), 26 deletions(-) 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 f550865d2..55b023af3 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 @@ -252,8 +252,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return serverBossBars; } - public void handleClientPluginMessage(PluginMessage packet) { - if (packet.getChannel().equals("REGISTER") || packet.getChannel().equals("minecraft:register")) { + private void handleClientPluginMessage(PluginMessage packet) { + if (PluginMessageUtil.isMCRegister(packet)) { List actuallyRegistered = new ArrayList<>(); List channels = PluginMessageUtil.getChannels(packet); for (String channel : channels) { @@ -270,21 +270,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(packet.getChannel(), actuallyRegistered); player.getConnectedServer().getMinecraftConnection().write(newRegisterPacket); } - - return; - } - - if (packet.getChannel().equals("UNREGISTER") || packet.getChannel().equals("minecraft:unregister")) { + } else if (PluginMessageUtil.isMCUnregister(packet)) { List channels = PluginMessageUtil.getChannels(packet); clientPluginMsgChannels.removeAll(channels); - } - - if (PluginMessageUtil.isMCBrand(packet)) { + player.getConnectedServer().getMinecraftConnection().write(packet); + } else if (PluginMessageUtil.isMCBrand(packet)) { player.getConnectedServer().getMinecraftConnection().write(PluginMessageUtil.rewriteMCBrand(packet)); - return; - } - - if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) { + } else if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) { if (packet.getChannel().equals(VelocityConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) { // Always forward the FML handshake to the remote server. player.getConnectedServer().getMinecraftConnection().write(packet); @@ -294,13 +286,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // be sent once the JoinGame packet has been received by the proxy. loginPluginMessages.add(packet); } - return; - } - - MessageHandler.ForwardStatus status = server.getChannelRegistrar().handlePluginMessage(player, - ChannelSide.FROM_CLIENT, packet); - if (status == MessageHandler.ForwardStatus.FORWARD) { - player.getConnectedServer().writeIfJoined(packet); + } else { + MessageHandler.ForwardStatus status = server.getChannelRegistrar().handlePluginMessage(player, + ChannelSide.FROM_CLIENT, packet); + if (status == MessageHandler.ForwardStatus.FORWARD) { + player.getConnectedServer().writeIfJoined(packet); + } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index 075058486..e3fdeab31 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -20,13 +20,20 @@ public enum PluginMessageUtil { return message.getChannel().equals("MC|Brand") || message.getChannel().equals("minecraft:brand"); } + public static boolean isMCRegister(PluginMessage message) { + Preconditions.checkNotNull(message, "message"); + return message.getChannel().equals("REGISTER") || message.getChannel().equals("minecraft:register"); + } + + public static boolean isMCUnregister(PluginMessage message) { + Preconditions.checkNotNull(message, "message"); + return message.getChannel().equals("UNREGISTER") || message.getChannel().equals("minecraft:unregister"); + } + public static List getChannels(PluginMessage message) { Preconditions.checkNotNull(message, "message"); - Preconditions.checkArgument(message.getChannel().equals("REGISTER") || - message.getChannel().equals("UNREGISTER") || - message.getChannel().equals("minecraft:register") || - message.getChannel().equals("minecraft:unregister"), - "Unknown channel type " + message.getChannel()); + Preconditions.checkArgument(isMCRegister(message) || isMCUnregister(message),"Unknown channel type %s", + message.getChannel()); String channels = new String(message.getData(), StandardCharsets.UTF_8); return ImmutableList.copyOf(channels.split("\0")); } From 88b7407aaf0dabdf2a7c6bb82908b4290989dd6e Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 15 Sep 2018 13:37:30 -0400 Subject: [PATCH 21/62] Fix rare NPE during server transition. Fixes #87 --- .../client/ClientPlaySessionHandler.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) 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 55b023af3..c3bf38456 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 @@ -55,6 +55,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { @Override public void handle(MinecraftPacket packet) { + VelocityServerConnection serverConnection = player.getConnectedServer(); + if (serverConnection == null) { + // No server connection yet, probably transitioning. + return; + } + if (packet instanceof KeepAlive) { KeepAlive keepAlive = (KeepAlive) packet; if (keepAlive.getRandomId() != lastPingID) { @@ -64,7 +70,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } player.setPing(System.currentTimeMillis() - lastPingSent); resetPingData(); - player.getConnectedServer().getMinecraftConnection().write(packet); + serverConnection.getMinecraftConnection().write(packet); return; } @@ -112,7 +118,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { player.getConnection().write(response); } else { - player.getConnectedServer().getMinecraftConnection().write(packet); + serverConnection.getMinecraftConnection().write(packet); } } catch (Exception e) { logger.error("Unable to provide tab list completions for " + player.getUsername() + " for command '" + req.getCommand() + "'", e); @@ -127,15 +133,21 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } // If we don't want to handle this packet, just forward it on. - if (player.getConnectedServer().hasCompletedJoin()) { - player.getConnectedServer().getMinecraftConnection().write(packet); + if (serverConnection.hasCompletedJoin()) { + serverConnection.getMinecraftConnection().write(packet); } } @Override public void handleUnknown(ByteBuf buf) { - if (player.getConnectedServer().hasCompletedJoin()) { - player.getConnectedServer().getMinecraftConnection().write(buf.retain()); + VelocityServerConnection serverConnection = player.getConnectedServer(); + if (serverConnection == null) { + // No server connection yet, probably transitioning. + return; + } + + if (serverConnection.hasCompletedJoin()) { + serverConnection.getMinecraftConnection().write(buf.retain()); } } From be0f666f4d479a341775d09dadf483966106f515 Mon Sep 17 00:00:00 2001 From: PurpleIsEverything Date: Sat, 15 Sep 2018 18:36:10 -0600 Subject: [PATCH 22/62] Allow modifying the ModInfo type. --- .../api/proxy/server/ServerPing.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) 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 252ad77c3..069a5f7e8 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 @@ -74,6 +74,7 @@ public class ServerPing { builder.favicon = favicon; builder.nullOutModinfo = modinfo == null; if (modinfo != null) { + builder.modType = modinfo.type; builder.mods.addAll(modinfo.modList); } return builder; @@ -91,6 +92,7 @@ public class ServerPing { private int onlinePlayers; private int maximumPlayers; private final List samplePlayers = new ArrayList<>(); + private String modType; private final List mods = new ArrayList<>(); private Component description; private Favicon favicon; @@ -121,6 +123,11 @@ public class ServerPing { return this; } + public Builder modType(String modType) { + this.modType = Preconditions.checkNotNull(modType, "modType"); + return this; + } + public Builder mods(Mod... mods) { this.mods.addAll(Arrays.asList(mods)); return this; @@ -158,7 +165,7 @@ public class ServerPing { public ServerPing build() { return new ServerPing(version, nullOutPlayers ? null : new Players(onlinePlayers, maximumPlayers, samplePlayers), description, favicon, - nullOutModinfo ? null : new Modinfo(mods)); + nullOutModinfo ? null : new Modinfo(modType, mods)); } public Version getVersion() { @@ -185,6 +192,10 @@ public class ServerPing { return favicon; } + public String getModType() { + return modType; + } + public List getMods() { return mods; } @@ -196,6 +207,7 @@ public class ServerPing { ", onlinePlayers=" + onlinePlayers + ", maximumPlayers=" + maximumPlayers + ", samplePlayers=" + samplePlayers + + ", modType=" + modType + ", mods=" + mods + ", description=" + description + ", favicon=" + favicon + @@ -291,12 +303,13 @@ public class ServerPing { } public static class Modinfo { - public static final Modinfo DEFAULT = new Modinfo(ImmutableList.of()); + public static final Modinfo DEFAULT = new Modinfo("FML", ImmutableList.of()); - private final String type = "FML"; + private final String type; private final List modList; - public Modinfo(List modList) { + public Modinfo(String type, List modList) { + this.type = Preconditions.checkNotNull(type, "type"); this.modList = ImmutableList.copyOf(modList); } } From e1b2dc0d435b35aaa70941eb44ec49fb647a7a2f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 02:26:29 -0400 Subject: [PATCH 23/62] Introduce RegisteredServer API This interface roughly maps the BungeeCord ServerInfo API. Accordingly, this is a breaking API change, as many of the server-related events and methods working with server info instances now provide/expect the RegisteredServer interface instead. --- .../event/player/KickedFromServerEvent.java | 15 +-- .../event/player/ServerConnectedEvent.java | 8 +- .../event/player/ServerPreConnectEvent.java | 17 ++-- .../api/proxy/ConnectionRequestBuilder.java | 7 +- .../com/velocitypowered/api/proxy/Player.java | 5 +- .../api/proxy/ProxyServer.java | 12 ++- .../api/proxy/ServerConnection.java | 7 ++ .../proxy/messages/ChannelMessageSink.java | 3 +- .../api/proxy/server/RegisteredServer.java | 30 ++++++ .../velocitypowered/proxy/VelocityServer.java | 13 +-- .../proxy/command/ServerCommand.java | 23 +++-- .../backend/BackendPlaySessionHandler.java | 14 +-- .../backend/VelocityServerConnection.java | 29 ++++-- .../connection/client/ConnectedPlayer.java | 67 +++++++------ .../client/LoginSessionHandler.java | 7 +- .../proxy/server/ServerMap.java | 68 +++++++++++++ .../server/VelocityRegisteredServer.java | 95 +++++++++++++++++++ .../velocitypowered/proxy/util/ServerMap.java | 56 ----------- .../proxy/util/ServerMapTest.java | 14 +-- 19 files changed, 331 insertions(+), 159 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/proxy/server/RegisteredServer.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/util/ServerMap.java diff --git a/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java index 04359eea8..86bce5e66 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java @@ -3,6 +3,7 @@ package com.velocitypowered.api.event.player; import com.google.common.base.Preconditions; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import net.kyori.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; @@ -13,12 +14,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; */ public class KickedFromServerEvent implements ResultedEvent { private final Player player; - private final ServerInfo server; + private final RegisteredServer server; private final Component originalReason; private final boolean duringLogin; private ServerKickResult result; - public KickedFromServerEvent(Player player, ServerInfo server, Component originalReason, boolean duringLogin, Component fancyReason) { + public KickedFromServerEvent(Player player, RegisteredServer server, Component originalReason, boolean duringLogin, Component fancyReason) { this.player = Preconditions.checkNotNull(player, "player"); this.server = Preconditions.checkNotNull(server, "server"); this.originalReason = Preconditions.checkNotNull(originalReason, "originalReason"); @@ -40,7 +41,7 @@ public class KickedFromServerEvent implements ResultedEvent { private final Player player; - private final ServerInfo originalServer; + private final RegisteredServer originalServer; private ServerResult result; - public ServerPreConnectEvent(Player player, ServerInfo originalServer) { + public ServerPreConnectEvent(Player player, RegisteredServer originalServer) { this.player = Preconditions.checkNotNull(player, "player"); this.originalServer = Preconditions.checkNotNull(originalServer, "originalServer"); this.result = ServerResult.allowed(originalServer); @@ -37,7 +38,7 @@ public class ServerPreConnectEvent implements ResultedEvent getServer() { + public Optional getServer() { return Optional.ofNullable(server); } @@ -78,14 +79,14 @@ public class ServerPreConnectEvent implements ResultedEvent connect(); + CompletableFuture connect(); /** * Initiates the connection to the remote server without waiting for a result. Velocity will use generic error 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 0ebb1fdfa..83c05a4ae 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -4,6 +4,7 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import com.velocitypowered.api.proxy.messages.ChannelMessageSource; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.util.MessagePosition; import net.kyori.text.Component; @@ -59,10 +60,10 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage /** * Creates a new connection request so that the player can connect to another server. - * @param info the server to connect to + * @param server the server to connect to * @return a new connection request */ - ConnectionRequestBuilder createConnectionRequest(@NonNull ServerInfo info); + ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server); /** * Sets the tab list header and footer for the player. 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 b4c851c58..eb2edcbe1 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -5,6 +5,7 @@ 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.proxy.server.RegisteredServer; import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.proxy.server.ServerInfo; @@ -45,23 +46,24 @@ public interface ProxyServer { int getPlayerCount(); /** - * Retrieves a registered {@link ServerInfo} instance by its name. The search is case-insensitive. + * Retrieves a registered {@link RegisteredServer} instance by its name. The search is case-insensitive. * @param name the name of the server * @return the registered server, which may be empty */ - Optional getServerInfo(String name); + Optional getServerInfo(String name); /** - * Retrieves all {@link ServerInfo}s registered with this proxy. + * Retrieves all {@link RegisteredServer}s registered with this proxy. * @return the servers registered with this proxy */ - Collection getAllServers(); + Collection getAllServers(); /** * Registers a server with this proxy. A server with this name should not already exist. * @param server the server to register + * @return the newly registered server */ - void registerServer(ServerInfo server); + RegisteredServer registerServer(ServerInfo server); /** * Unregisters this server from the proxy. 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 5f12fb326..b800131c0 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ServerConnection.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ServerConnection.java @@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy; import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import com.velocitypowered.api.proxy.messages.ChannelMessageSource; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; /** @@ -12,6 +13,12 @@ public interface ServerConnection extends ChannelMessageSource, ChannelMessageSi * Returns the server that this connection is connected to. * @return the server this connection is connected to */ + RegisteredServer getServer(); + + /** + * Returns the server info for this connection. + * @return the server info for this connection + */ ServerInfo getServerInfo(); /** diff --git a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSink.java b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSink.java index 57ebe4ea6..37f02e539 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSink.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/messages/ChannelMessageSink.java @@ -8,6 +8,7 @@ public interface ChannelMessageSink { * Sends a plugin message to this target. * @param identifier the channel identifier to send the message on * @param data the data to send + * @return whether or not the message could be sent */ - void sendPluginMessage(ChannelIdentifier identifier, byte[] data); + boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data); } 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 new file mode 100644 index 000000000..b7bf9acb5 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/RegisteredServer.java @@ -0,0 +1,30 @@ +package com.velocitypowered.api.proxy.server; + +import com.velocitypowered.api.proxy.Player; +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. + */ +public interface RegisteredServer extends ChannelMessageSink { + /** + * Returns the {@link ServerInfo} for this server. + * @return the server info + */ + ServerInfo getServerInfo(); + + /** + * Returns a list of all the players currently connected to this server on this proxy. + * @return the players on this proxy + */ + Collection getPlayersConnected(); + + /** + * Attempts to ping the remote server and return the server list ping result. + * @return the server ping result from the server + */ + CompletableFuture ping(); +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index d23709111..55a2ba4c7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -10,6 +10,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.server.ServerInfo; @@ -30,7 +31,7 @@ import com.velocitypowered.proxy.scheduler.VelocityScheduler; import com.velocitypowered.proxy.util.AddressUtil; import com.velocitypowered.proxy.util.EncryptionUtils; import com.velocitypowered.proxy.util.Ratelimiter; -import com.velocitypowered.proxy.util.ServerMap; +import com.velocitypowered.proxy.server.ServerMap; import io.netty.bootstrap.Bootstrap; import net.kyori.text.Component; import net.kyori.text.TextComponent; @@ -61,7 +62,7 @@ public class VelocityServer implements ProxyServer { private VelocityConfiguration configuration; private NettyHttpClient httpClient; private KeyPair serverKeyPair; - private final ServerMap servers = new ServerMap(); + private final ServerMap servers = new ServerMap(this); private final VelocityCommandManager commandManager = new VelocityCommandManager(); private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false); private boolean shutdown = false; @@ -263,19 +264,19 @@ public class VelocityServer implements ProxyServer { } @Override - public Optional getServerInfo(String name) { + public Optional getServerInfo(String name) { Preconditions.checkNotNull(name, "name"); return servers.getServer(name); } @Override - public Collection getAllServers() { + public Collection getAllServers() { return servers.getAllServers(); } @Override - public void registerServer(ServerInfo server) { - servers.register(server); + public RegisteredServer registerServer(ServerInfo server) { + return servers.register(server); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java index b42a9ed26..3707a800c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java @@ -6,6 +6,7 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; @@ -34,7 +35,7 @@ public class ServerCommand implements Command { if (args.length == 1) { // Trying to connect to a server. String serverName = args[0]; - Optional toConnect = server.getServerInfo(serverName); + Optional toConnect = server.getServerInfo(serverName); if (!toConnect.isPresent()) { player.sendMessage(TextComponent.of("Server " + serverName + " doesn't exist.", TextColor.RED)); return; @@ -48,17 +49,19 @@ public class ServerCommand implements Command { // Assemble the list of servers as components TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ").color(TextColor.YELLOW); - List infos = ImmutableList.copyOf(server.getAllServers()); + List infos = ImmutableList.copyOf(server.getAllServers()); for (int i = 0; i < infos.size(); i++) { - ServerInfo serverInfo = infos.get(i); - TextComponent infoComponent = TextComponent.of(serverInfo.getName()); - if (serverInfo.getName().equals(currentServer)) { + RegisteredServer rs = infos.get(i); + TextComponent infoComponent = TextComponent.of(rs.getServerInfo().getName()); + String playersText = rs.getPlayersConnected().size() + " player(s) online"; + if (rs.getServerInfo().getName().equals(currentServer)) { infoComponent = infoComponent.color(TextColor.GREEN) - .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Currently connected to this server"))); + .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, + TextComponent.of("Currently connected to this server\n" + playersText))); } else { infoComponent = infoComponent.color(TextColor.GRAY) - .clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + serverInfo.getName())) - .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to connect to this server"))); + .clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + rs.getServerInfo().getName())) + .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to connect to this server\n" + playersText))); } serverListBuilder.append(infoComponent); if (i != infos.size() - 1) { @@ -74,11 +77,11 @@ public class ServerCommand implements Command { public List suggest(CommandSource source, String[] currentArgs) { if (currentArgs.length == 0) { return server.getAllServers().stream() - .map(ServerInfo::getName) + .map(rs -> rs.getServerInfo().getName()) .collect(Collectors.toList()); } else if (currentArgs.length == 1) { return server.getAllServers().stream() - .map(ServerInfo::getName) + .map(rs -> rs.getServerInfo().getName()) .filter(name -> name.regionMatches(true, 0, currentArgs[0], 0, currentArgs[0].length())) .collect(Collectors.toList()); } else { 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 e62eb4615..fb7e1138d 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 @@ -25,7 +25,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void activated() { - server.getEventManager().fireAndForget(new ServerConnectedEvent(connection.getPlayer(), connection.getServerInfo())); + server.getEventManager().fireAndForget(new ServerConnectedEvent(connection.getPlayer(), connection.getServer())); + connection.getServer().addPlayer(connection.getPlayer()); } @Override @@ -46,7 +47,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { } else if (packet instanceof Disconnect) { Disconnect original = (Disconnect) packet; connection.disconnect(); - connection.getPlayer().handleConnectionException(connection.getServerInfo(), original); + connection.getPlayer().handleConnectionException(connection.getServer(), original); } else if (packet instanceof JoinGame) { playerHandler.handleBackendJoinGame((JoinGame) packet); } else if (packet instanceof BossBar) { @@ -112,7 +113,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void exception(Throwable throwable) { - connection.getPlayer().handleConnectionException(connection.getServerInfo(), throwable); + connection.getPlayer().handleConnectionException(connection.getServer(), throwable); } public VelocityServer getServer() { @@ -121,10 +122,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void disconnected() { - if (connection.isGracefulDisconnect()) { - return; + connection.getServer().removePlayer(connection.getPlayer()); + if (!connection.isGracefulDisconnect()) { + connection.getPlayer().handleConnectionException(connection.getServer(), Disconnect.create( + ConnectionMessages.UNEXPECTED_DISCONNECT)); } - connection.getPlayer().handleConnectionException(connection.getServerInfo(), Disconnect.create(ConnectionMessages.UNEXPECTED_DISCONNECT)); } private boolean canForwardPluginMessage(PluginMessage message) { 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 496a4a686..87f55b6e9 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 @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.protocol.ProtocolConstants; @@ -19,6 +20,7 @@ import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.channel.*; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.AttributeKey; @@ -38,7 +40,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, static final AttributeKey> CONNECTION_NOTIFIER = AttributeKey.newInstance("connection-notification-result"); - private final ServerInfo serverInfo; + private final VelocityRegisteredServer registeredServer; private final ConnectedPlayer proxyPlayer; private final VelocityServer server; private MinecraftConnection minecraftConnection; @@ -46,8 +48,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, private boolean hasCompletedJoin = false; private boolean gracefulDisconnect = false; - public VelocityServerConnection(ServerInfo target, ConnectedPlayer proxyPlayer, VelocityServer server) { - this.serverInfo = target; + public VelocityServerConnection(VelocityRegisteredServer registeredServer, ConnectedPlayer proxyPlayer, VelocityServer server) { + this.registeredServer = registeredServer; this.proxyPlayer = proxyPlayer; this.server = server; } @@ -72,7 +74,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, ch.pipeline().addLast(HANDLER, connection); } }) - .connect(serverInfo.getAddress()) + .connect(registeredServer.getServerInfo().getAddress()) .addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { @@ -94,7 +96,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, // BungeeCord IP forwarding is simply a special injection after the "address" in the handshake, // separated by \0 (the null byte). In order, you send the original host, the player's IP, their // UUID (undashed), and if you are in online-mode, their login properties (retrieved from Mojang). - return serverInfo.getAddress().getHostString() + "\0" + + return registeredServer.getServerInfo().getAddress().getHostString() + "\0" + proxyPlayer.getRemoteAddress().getHostString() + "\0" + proxyPlayer.getProfile().getId() + "\0" + GSON.toJson(proxyPlayer.getProfile().getProperties()); @@ -112,9 +114,9 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, } else if (proxyPlayer.getConnection().isLegacyForge()) { handshake.setServerAddress(handshake.getServerAddress() + "\0FML\0"); } else { - handshake.setServerAddress(serverInfo.getAddress().getHostString()); + handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString()); } - handshake.setPort(serverInfo.getAddress().getPort()); + handshake.setPort(registeredServer.getServerInfo().getAddress().getPort()); minecraftConnection.write(handshake); int protocolVersion = proxyPlayer.getConnection().getProtocolVersion(); @@ -136,8 +138,14 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, return minecraftConnection; } + @Override + public VelocityRegisteredServer getServer() { + return registeredServer; + } + + @Override public ServerInfo getServerInfo() { - return serverInfo; + return registeredServer.getServerInfo(); } @Override @@ -155,17 +163,18 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, @Override public String toString() { - return "[server connection] " + proxyPlayer.getProfile().getName() + " -> " + serverInfo.getName(); + return "[server connection] " + proxyPlayer.getProfile().getName() + " -> " + registeredServer.getServerInfo().getName(); } @Override - public void sendPluginMessage(ChannelIdentifier identifier, byte[] data) { + public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(data, "data"); PluginMessage message = new PluginMessage(); message.setChannel(identifier.getId()); message.setData(data); minecraftConnection.write(message); + return true; } public boolean isLegacyForge() { 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 b56b74753..5cfe49ac4 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 @@ -11,6 +11,7 @@ import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.MessagePosition; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.proxy.VelocityServer; @@ -24,6 +25,7 @@ import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.protocol.packet.ClientSettings; import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.util.ThrowableUtils; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.proxy.protocol.packet.Disconnect; @@ -159,8 +161,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public ConnectionRequestBuilder createConnectionRequest(@NonNull ServerInfo info) { - return new ConnectionRequestBuilderImpl(info); + public ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server) { + return new ConnectionRequestBuilderImpl(server); } @Override @@ -184,57 +186,57 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return connectedServer; } - public void handleConnectionException(ServerInfo info, Throwable throwable) { + public void handleConnectionException(RegisteredServer server, Throwable throwable) { String error = ThrowableUtils.briefDescription(throwable); String userMessage; - if (connectedServer != null && connectedServer.getServerInfo().equals(info)) { - userMessage = "Exception in server " + info.getName(); + if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { + userMessage = "Exception in server " + server.getServerInfo().getName(); } else { - logger.error("{}: unable to connect to server {}", this, info.getName(), throwable); - userMessage = "Exception connecting to server " + info.getName(); + logger.error("{}: unable to connect to server {}", this, server.getServerInfo().getName(), throwable); + userMessage = "Exception connecting to server " + server.getServerInfo().getName(); } - handleConnectionException(info, null, TextComponent.builder() + handleConnectionException(server, null, TextComponent.builder() .content(userMessage + ": ") .color(TextColor.RED) .append(TextComponent.of(error, TextColor.WHITE)) .build()); } - public void handleConnectionException(ServerInfo info, Disconnect disconnect) { + public void handleConnectionException(RegisteredServer server, Disconnect disconnect) { Component disconnectReason = ComponentSerializers.JSON.deserialize(disconnect.getReason()); String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason); - if (connectedServer != null && connectedServer.getServerInfo().equals(info)) { - logger.error("{}: kicked from server {}: {}", this, info.getName(), plainTextReason); - handleConnectionException(info, disconnectReason, TextComponent.builder() - .content("Kicked from " + info.getName() + ": ") + 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) .append(disconnectReason) .build()); } else { - logger.error("{}: disconnected while connecting to {}: {}", this, info.getName(), plainTextReason); - handleConnectionException(info, disconnectReason, TextComponent.builder() - .content("Unable to connect to " + info.getName() + ": ") + logger.error("{}: disconnected while connecting to {}: {}", this, server.getServerInfo().getName(), plainTextReason); + handleConnectionException(server, disconnectReason, TextComponent.builder() + .content("Unable to connect to " + server.getServerInfo().getName() + ": ") .color(TextColor.RED) .append(disconnectReason) .build()); } } - private void handleConnectionException(ServerInfo info, @Nullable Component kickReason, Component friendlyReason) { - boolean alreadyConnected = connectedServer != null && connectedServer.getServerInfo().equals(info);; + private void handleConnectionException(RegisteredServer rs, @Nullable Component kickReason, Component friendlyReason) { + boolean alreadyConnected = connectedServer != null && connectedServer.getServerInfo().equals(rs.getServerInfo()); connectionInFlight = null; if (connectedServer == null) { // The player isn't yet connected to a server. - Optional nextServer = getNextServerToTry(); + Optional nextServer = getNextServerToTry(); if (nextServer.isPresent()) { createConnectionRequest(nextServer.get()).fireAndForget(); } else { connection.closeWith(Disconnect.create(friendlyReason)); } - } else if (connectedServer.getServerInfo().equals(info)) { + } else if (connectedServer.getServerInfo().equals(rs.getServerInfo())) { // Already connected to the server being disconnected from. if (kickReason != null) { - server.getEventManager().fire(new KickedFromServerEvent(this, info, kickReason, !alreadyConnected, friendlyReason)) + server.getEventManager().fire(new KickedFromServerEvent(this, rs, kickReason, !alreadyConnected, friendlyReason)) .thenAcceptAsync(event -> { if (event.getResult() instanceof KickedFromServerEvent.DisconnectPlayer) { KickedFromServerEvent.DisconnectPlayer res = (KickedFromServerEvent.DisconnectPlayer) event.getResult(); @@ -255,7 +257,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } } - Optional getNextServerToTry() { + Optional getNextServerToTry() { List serversToTry = server.getConfiguration().getAttemptConnectionOrder(); if (tryIndex >= serversToTry.size()) { return Optional.empty(); @@ -289,7 +291,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { ); } - return new VelocityServerConnection(newEvent.getResult().getServer().get(), this, server).connect(); + RegisteredServer rs = newEvent.getResult().getServer().get(); + Preconditions.checkState(rs instanceof VelocityRegisteredServer, "Not a valid Velocity server."); + return new VelocityServerConnection((VelocityRegisteredServer) rs, this, server).connect(); }); } @@ -335,25 +339,26 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public void sendPluginMessage(ChannelIdentifier identifier, byte[] data) { + public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(data, "data"); PluginMessage message = new PluginMessage(); message.setChannel(identifier.getId()); message.setData(data); connection.write(message); + return true; } private class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder { - private final ServerInfo info; + private final RegisteredServer server; - ConnectionRequestBuilderImpl(ServerInfo info) { - this.info = Preconditions.checkNotNull(info, "info"); + ConnectionRequestBuilderImpl(RegisteredServer server) { + this.server = Preconditions.checkNotNull(server, "info"); } @Override - public ServerInfo getServer() { - return info; + public RegisteredServer getServer() { + return server; } @Override @@ -366,7 +371,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { connect() .whenCompleteAsync((status, throwable) -> { if (throwable != null) { - handleConnectionException(info, throwable); + handleConnectionException(server, throwable); return; } @@ -381,7 +386,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { // Ignored; the plugin probably already handled this. break; case SERVER_DISCONNECTED: - handleConnectionException(info, Disconnect.create(status.getReason().orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR))); + handleConnectionException(server, Disconnect.create(status.getReason().orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR))); break; } }, connection.getChannel().eventLoop()); 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 63890c3f0..bf63dc63d 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 @@ -8,6 +8,7 @@ import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentR import com.velocitypowered.api.event.permission.PermissionsSetupEvent; import com.velocitypowered.api.event.player.GameProfileRequestEvent; import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.connection.VelocityConstants; @@ -220,7 +221,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } private void handleProxyLogin(ConnectedPlayer player) { - Optional toTry = player.getNextServerToTry(); + Optional toTry = player.getNextServerToTry(); if (!toTry.isPresent()) { player.close(TextComponent.of("No available servers", TextColor.RED)); return; @@ -246,9 +247,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { logger.info("{} has connected", player); inbound.setSessionHandler(new InitialConnectSessionHandler(player)); - server.getEventManager().fire(new PostLoginEvent(player)).thenRun(() -> { - player.createConnectionRequest(toTry.get()).fireAndForget(); - }); + server.getEventManager().fire(new PostLoginEvent(player)).thenRun(() -> player.createConnectionRequest(toTry.get()).fireAndForget()); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java new file mode 100644 index 000000000..30835df3e --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java @@ -0,0 +1,68 @@ +package com.velocitypowered.proxy.server; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.proxy.VelocityServer; + +import java.util.*; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class ServerMap { + private final VelocityServer server; + private final Map servers = new HashMap<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + public ServerMap(VelocityServer server) { + this.server = server; + } + + public Optional getServer(String name) { + Preconditions.checkNotNull(name, "server"); + String lowerName = name.toLowerCase(Locale.US); + lock.readLock().lock(); + try { + return Optional.ofNullable(servers.get(lowerName)); + } finally { + lock.readLock().unlock(); + } + } + + public Collection getAllServers() { + lock.readLock().lock(); + try { + return ImmutableList.copyOf(servers.values()); + } finally { + lock.readLock().unlock(); + } + } + + public RegisteredServer register(ServerInfo serverInfo) { + Preconditions.checkNotNull(serverInfo, "serverInfo"); + String lowerName = serverInfo.getName().toLowerCase(Locale.US); + lock.writeLock().lock(); + try { + VelocityRegisteredServer rs = new VelocityRegisteredServer(server, serverInfo); + Preconditions.checkArgument(servers.putIfAbsent(lowerName, rs) == null, "Server with name %s already registered", serverInfo.getName()); + return rs; + } finally { + lock.writeLock().unlock(); + } + } + + public void unregister(ServerInfo serverInfo) { + Preconditions.checkNotNull(serverInfo, "serverInfo"); + String lowerName = serverInfo.getName().toLowerCase(Locale.US); + lock.writeLock().lock(); + try { + RegisteredServer rs = servers.get(lowerName); + Preconditions.checkArgument(rs != null, "Server with name %s is not registered!", serverInfo.getName()); + Preconditions.checkArgument(rs.getServerInfo().equals(serverInfo), "Trying to remove server %s with differing information", serverInfo.getName()); + servers.remove(lowerName); + } finally { + lock.writeLock().unlock(); + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java new file mode 100644 index 000000000..21c654957 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -0,0 +1,95 @@ +package com.velocitypowered.proxy.server; + +import com.google.common.collect.ImmutableList; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class VelocityRegisteredServer implements RegisteredServer { + private final VelocityServer server; + private final ServerInfo serverInfo; + private final Set players = new HashSet<>(); + private final ReadWriteLock playersLock = new ReentrantReadWriteLock(); + + public VelocityRegisteredServer(VelocityServer server, ServerInfo serverInfo) { + this.server = server; + this.serverInfo = serverInfo; + } + + @Override + public ServerInfo getServerInfo() { + return serverInfo; + } + + @Override + public Collection getPlayersConnected() { + playersLock.readLock().lock(); + try { + return ImmutableList.copyOf(players); + } finally { + playersLock.readLock().unlock(); + } + } + + @Override + public CompletableFuture ping() { + CompletableFuture p = new CompletableFuture<>(); + p.completeExceptionally(new UnsupportedOperationException("Not currently implemented.")); + return p; + } + + public void addPlayer(ConnectedPlayer player) { + playersLock.writeLock().lock(); + try { + players.add(player); + } finally { + playersLock.writeLock().unlock(); + } + } + + public void removePlayer(ConnectedPlayer player) { + playersLock.writeLock().lock(); + try { + players.remove(player); + } finally { + playersLock.writeLock().unlock(); + } + } + + @Override + public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { + ServerConnection backendConnection = null; + playersLock.readLock().lock(); + try { + for (ConnectedPlayer player : players) { + if (player.getConnectedServer() != null && player.getConnectedServer().getServerInfo().equals(serverInfo)) { + backendConnection = player.getConnectedServer(); + break; + } + } + + if (backendConnection == null) { + return false; + } + } finally { + playersLock.readLock().unlock(); + } + + return backendConnection.sendPluginMessage(identifier, data); + } + + @Override + public String toString() { + return "registered server: " + serverInfo; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/ServerMap.java b/proxy/src/main/java/com/velocitypowered/proxy/util/ServerMap.java deleted file mode 100644 index 75784ef0b..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/ServerMap.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.velocitypowered.proxy.util; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.velocitypowered.api.proxy.server.ServerInfo; - -import java.util.*; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class ServerMap { - private final Map servers = new HashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - - public Optional getServer(String server) { - Preconditions.checkNotNull(server, "server"); - String lowerName = server.toLowerCase(Locale.US); - lock.readLock().lock(); - try { - return Optional.ofNullable(servers.get(lowerName)); - } finally { - lock.readLock().unlock(); - } - } - - public Collection getAllServers() { - lock.readLock().lock(); - try { - return ImmutableList.copyOf(servers.values()); - } finally { - lock.readLock().unlock(); - } - } - - public void register(ServerInfo server) { - Preconditions.checkNotNull(server, "server"); - String lowerName = server.getName().toLowerCase(Locale.US); - lock.writeLock().lock(); - try { - Preconditions.checkArgument(servers.putIfAbsent(lowerName, server) == null, "Server with name %s already registered", server.getName()); - } finally { - lock.writeLock().unlock(); - } - } - - public void unregister(ServerInfo server) { - Preconditions.checkNotNull(server, "server"); - String lowerName = server.getName().toLowerCase(Locale.US); - lock.writeLock().lock(); - try { - Preconditions.checkArgument(servers.remove(lowerName, server), "Server with this name is not registered!"); - } finally { - lock.writeLock().unlock(); - } - } -} diff --git a/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java b/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java index e332ed85f..ad8d3d52b 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java @@ -1,6 +1,8 @@ package com.velocitypowered.proxy.util; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.proxy.server.ServerMap; import org.junit.jupiter.api.Test; import java.net.InetAddress; @@ -14,18 +16,18 @@ class ServerMapTest { @Test void respectsCaseInsensitivity() { - ServerMap map = new ServerMap(); + ServerMap map = new ServerMap(null); ServerInfo info = new ServerInfo("TestServer", TEST_ADDRESS); - map.register(info); + RegisteredServer connection = map.register(info); - assertEquals(Optional.of(info), map.getServer("TestServer")); - assertEquals(Optional.of(info), map.getServer("testserver")); - assertEquals(Optional.of(info), map.getServer("TESTSERVER")); + assertEquals(Optional.of(connection), map.getServer("TestServer")); + assertEquals(Optional.of(connection), map.getServer("testserver")); + assertEquals(Optional.of(connection), map.getServer("TESTSERVER")); } @Test void rejectsRepeatedRegisterAttempts() { - ServerMap map = new ServerMap(); + ServerMap map = new ServerMap(null); ServerInfo info = new ServerInfo("TestServer", TEST_ADDRESS); map.register(info); From 8b94fe6ed2343bc3eaf06b01ff487e3edd9a552a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 02:41:35 -0400 Subject: [PATCH 24/62] Readd missing check --- .../proxy/connection/backend/BackendPlaySessionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0f82b12eb..e0dc386af 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 @@ -92,7 +92,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { connection.getPlayer().getConnection().write(pm); } }, connection.getMinecraftConnection().getChannel().eventLoop()); - } else { + } else if (connection.hasCompletedJoin()) { // Just forward the packet on. We don't have anything to handle at this time. connection.getPlayer().getConnection().write(packet); } From 1f8152c3a52e482a01fa84eef45b75eae05c99d1 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 13:16:16 -0400 Subject: [PATCH 25/62] ProxyServer#getServerInfo -> ProxyServer#getServer --- .../main/java/com/velocitypowered/api/proxy/ProxyServer.java | 2 +- .../src/main/java/com/velocitypowered/proxy/VelocityServer.java | 2 +- .../java/com/velocitypowered/proxy/command/ServerCommand.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 eb2edcbe1..553e88c10 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -50,7 +50,7 @@ public interface ProxyServer { * @param name the name of the server * @return the registered server, which may be empty */ - Optional getServerInfo(String name); + Optional getServer(String name); /** * Retrieves all {@link RegisteredServer}s registered with this proxy. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 55a2ba4c7..b9711f915 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -264,7 +264,7 @@ public class VelocityServer implements ProxyServer { } @Override - public Optional getServerInfo(String name) { + public Optional getServer(String name) { Preconditions.checkNotNull(name, "name"); return servers.getServer(name); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java index 3707a800c..2926bb038 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java @@ -35,7 +35,7 @@ public class ServerCommand implements Command { if (args.length == 1) { // Trying to connect to a server. String serverName = args[0]; - Optional toConnect = server.getServerInfo(serverName); + Optional toConnect = server.getServer(serverName); if (!toConnect.isPresent()) { player.sendMessage(TextComponent.of("Server " + serverName + " doesn't exist.", TextColor.RED)); return; From 291069af807b5a20c09a3a4d68d0710ae37f7f27 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 13:59:44 -0400 Subject: [PATCH 26/62] Implement RegisteredServer#ping() --- .../proxy/connection/MinecraftConnection.java | 2 - .../server/VelocityRegisteredServer.java | 51 +++++++++++++- .../proxy/server/ping/PingSessionHandler.java | 67 +++++++++++++++++++ 3 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index b4a68dfbf..0f6844304 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -99,8 +99,6 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (association != null) { logger.error("{}: exception encountered", association, cause); - } else { - logger.error("{} encountered an exception", ctx.channel().remoteAddress(), cause); } ctx.close(); 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 21c654957..18dfef0d0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -8,13 +8,31 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; +import com.velocitypowered.proxy.server.ping.PingSessionHandler; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelInitializer; +import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import static com.velocitypowered.proxy.network.Connections.*; +import static com.velocitypowered.proxy.network.Connections.HANDLER; +import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; + public class VelocityRegisteredServer implements RegisteredServer { private final VelocityServer server; private final ServerInfo serverInfo; @@ -43,9 +61,36 @@ public class VelocityRegisteredServer implements RegisteredServer { @Override public CompletableFuture ping() { - CompletableFuture p = new CompletableFuture<>(); - p.completeExceptionally(new UnsupportedOperationException("Not currently implemented.")); - return p; + CompletableFuture pingFuture = new CompletableFuture<>(); + server.initializeGenericBootstrap() + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ch.pipeline() + .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.SECONDS)) + .addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder()) + .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) + .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolConstants.Direction.CLIENTBOUND)) + .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolConstants.Direction.SERVERBOUND)); + + MinecraftConnection connection = new MinecraftConnection(ch, server); + connection.setState(StateRegistry.HANDSHAKE); + ch.pipeline().addLast(HANDLER, connection); + } + }) + .connect(serverInfo.getAddress()) + .addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class); + conn.setSessionHandler(new PingSessionHandler(pingFuture, VelocityRegisteredServer.this, conn)); + } else { + pingFuture.completeExceptionally(future.cause()); + } + } + }); + return pingFuture; } public void addPlayer(ConnectedPlayer player) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java new file mode 100644 index 000000000..4ed4dae44 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java @@ -0,0 +1,67 @@ +package com.velocitypowered.proxy.server.ping; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerPing; +import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.StateRegistry; +import com.velocitypowered.proxy.protocol.packet.Handshake; +import com.velocitypowered.proxy.protocol.packet.StatusRequest; +import com.velocitypowered.proxy.protocol.packet.StatusResponse; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +public class PingSessionHandler implements MinecraftSessionHandler { + private final CompletableFuture result; + private final RegisteredServer server; + private final MinecraftConnection connection; + private boolean completed = false; + + public PingSessionHandler(CompletableFuture result, RegisteredServer server, MinecraftConnection connection) { + this.result = result; + this.server = server; + this.connection = connection; + } + + @Override + public void activated() { + Handshake handshake = new Handshake(); + handshake.setNextStatus(StateRegistry.STATUS_ID); + handshake.setServerAddress(server.getServerInfo().getAddress().getHostString()); + handshake.setPort(server.getServerInfo().getAddress().getPort()); + handshake.setProtocolVersion(ProtocolConstants.MINIMUM_GENERIC_VERSION); + connection.write(handshake); + + connection.setState(StateRegistry.STATUS); + connection.write(new StatusRequest()); + } + + @Override + public void handle(MinecraftPacket packet) { + Preconditions.checkState(packet instanceof StatusResponse, "Did not get status response back from connection"); + + // All good! + completed = true; + connection.close(); + + ServerPing ping = VelocityServer.GSON.fromJson(((StatusResponse) packet).getStatus(), ServerPing.class); + result.complete(ping); + } + + @Override + public void disconnected() { + if (!completed) { + result.completeExceptionally(new IOException("Unexpectedly disconnected from remote server")); + } + } + + @Override + public void exception(Throwable throwable) { + result.completeExceptionally(throwable); + } +} From 2a842bffbe13d0db88d14c5ea184eefb7615a1a5 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 15:32:51 -0400 Subject: [PATCH 27/62] Add missing null check --- .../backend/BackendPlaySessionHandler.java | 21 ++++++++++++------- .../client/ClientPlaySessionHandler.java | 21 ++++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) 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 e0dc386af..2785a38b9 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 @@ -2,6 +2,7 @@ package com.velocitypowered.proxy.connection.backend; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; @@ -84,14 +85,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { return; } - PluginMessageEvent event = new PluginMessageEvent(connection, connection.getPlayer(), server.getChannelRegistrar().getFromId(pm.getChannel()), - pm.getData()); - server.getEventManager().fire(event) - .thenAcceptAsync(pme -> { - if (pme.getResult().isAllowed()) { - connection.getPlayer().getConnection().write(pm); - } - }, connection.getMinecraftConnection().getChannel().eventLoop()); + ChannelIdentifier id = server.getChannelRegistrar().getFromId(pm.getChannel()); + if (id == null) { + connection.getPlayer().getConnection().write(pm); + } else { + PluginMessageEvent event = new PluginMessageEvent(connection, connection.getPlayer(), id, pm.getData()); + server.getEventManager().fire(event) + .thenAcceptAsync(pme -> { + if (pme.getResult().isAllowed()) { + connection.getPlayer().getConnection().write(pm); + } + }, connection.getMinecraftConnection().getChannel().eventLoop()); + } } else if (connection.hasCompletedJoin()) { // 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 b5a88ee55..836481fba 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 @@ -2,6 +2,7 @@ package com.velocitypowered.proxy.connection.client; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; @@ -298,14 +299,18 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { loginPluginMessages.add(packet); } } else { - PluginMessageEvent event = new PluginMessageEvent(player, player.getConnectedServer(), - server.getChannelRegistrar().getFromId(packet.getChannel()), packet.getData()); - server.getEventManager().fire(event) - .thenAcceptAsync(pme -> { - if (pme.getResult().isAllowed()) { - player.getConnectedServer().getMinecraftConnection().write(packet); - } - }, player.getConnectedServer().getMinecraftConnection().getChannel().eventLoop()); + ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel()); + if (id == null) { + player.getConnectedServer().getMinecraftConnection().write(packet); + } else { + PluginMessageEvent event = new PluginMessageEvent(player, player.getConnectedServer(), id, packet.getData()); + server.getEventManager().fire(event) + .thenAcceptAsync(pme -> { + if (pme.getResult().isAllowed()) { + player.getConnectedServer().getMinecraftConnection().write(packet); + } + }, player.getConnectedServer().getMinecraftConnection().getChannel().eventLoop()); + } } } From 037dceb599d57dd9fbf1c8bb9097d7d9f7bbdbbe Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 16 Sep 2018 15:35:25 -0400 Subject: [PATCH 28/62] Fix potential race --- .../velocitypowered/proxy/server/ping/PingSessionHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java index 4ed4dae44..60f2ea158 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java @@ -62,6 +62,7 @@ public class PingSessionHandler implements MinecraftSessionHandler { @Override public void exception(Throwable throwable) { + completed = true; result.completeExceptionally(throwable); } } From e75ec71b32d061b44fceff76549f7158276c2847 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 17 Sep 2018 01:03:07 -0400 Subject: [PATCH 29/62] Improve native initialization logic This uses the double-check pattern as recommended in Effective Java. --- .../natives/util/NativeCodeLoader.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java b/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java index 2f07378c8..692df5375 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java +++ b/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java @@ -8,7 +8,7 @@ import java.util.function.Supplier; public class NativeCodeLoader implements Supplier { private final List> variants; - private Variant selected; + private volatile Variant selected; public NativeCodeLoader(List> variants) { this.variants = ImmutableList.copyOf(variants); @@ -17,25 +17,32 @@ public class NativeCodeLoader implements Supplier { @Override public T get() { if (selected == null) { - selected = select(); + selected = tryLoad(); } return selected.object; } - private Variant select() { - for (Variant variant : variants) { - T got = variant.get(); - if (got == null) { - continue; + private Variant tryLoad() { + synchronized (this) { + if (selected != null) { + return selected; } - return variant; + + for (Variant variant : variants) { + T got = variant.get(); + if (got == null) { + continue; + } + selected = variant; + return selected; + } + throw new IllegalArgumentException("Can't find any suitable variants"); } - throw new IllegalArgumentException("Can't find any suitable variants"); } public String getLoadedVariant() { if (selected == null) { - selected = select(); + selected = tryLoad(); } return selected.name; } From fc48db62b564e2011a9a51ff3179ea0379907d56 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 17 Sep 2018 15:02:49 -0400 Subject: [PATCH 30/62] Improve native initialization logic, again --- .../natives/util/NativeCodeLoader.java | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java b/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java index 692df5375..4b84516da 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java +++ b/native/src/main/java/com/velocitypowered/natives/util/NativeCodeLoader.java @@ -16,13 +16,14 @@ public class NativeCodeLoader implements Supplier { @Override public T get() { - if (selected == null) { - selected = tryLoad(); - } - return selected.object; + return tryLoad().object; } private Variant tryLoad() { + if (selected != null) { + return selected; + } + synchronized (this) { if (selected != null) { return selected; @@ -41,18 +42,15 @@ public class NativeCodeLoader implements Supplier { } public String getLoadedVariant() { - if (selected == null) { - selected = tryLoad(); - } - return selected.name; + return tryLoad().name; } static class Variant { - private boolean available; + private volatile boolean available; private final Runnable setup; private final String name; private final T object; - private boolean hasBeenSetup = false; + private volatile boolean hasBeenSetup = false; Variant(BooleanSupplier available, Runnable setup, String name, T object) { this.available = available.getAsBoolean(); @@ -61,27 +59,34 @@ public class NativeCodeLoader implements Supplier { this.object = object; } - private void setup() { - if (available && !hasBeenSetup) { - try { - setup.run(); - hasBeenSetup = true; - } catch (Exception e) { - available = false; + public T get() { + if (!available) { + return null; + } + + // Make sure setup happens only once + if (!hasBeenSetup) { + synchronized (this) { + // We change availability if need be below, may as well check it again here for sanity. + if (!available) { + return null; + } + + // Okay, now try the setup if we haven't done so yet. + if (!hasBeenSetup) { + try { + setup.run(); + hasBeenSetup = true; + return object; + } catch (Exception e) { + available = false; + return null; + } + } } } - } - public T get() { - if (!hasBeenSetup) { - setup(); - } - - if (available) { - return object; - } - - return null; + return object; } } From a60c55007a89a7cd7c0efa9592919e189ca555dd Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 17 Sep 2018 16:11:23 -0400 Subject: [PATCH 31/62] Remove LegacyChatColorUtils In most cases, you should only work with legacy text to deserialize or serialize it for backwards compatibility or user configurations only. --- .../api/util/LegacyChatColorUtils.java | 66 ------------------- .../api/util/LegacyChatColorUtilsTest.java | 62 ----------------- .../proxy/config/VelocityConfiguration.java | 2 - 3 files changed, 130 deletions(-) delete mode 100644 api/src/main/java/com/velocitypowered/api/util/LegacyChatColorUtils.java delete mode 100644 api/src/test/java/com/velocitypowered/api/util/LegacyChatColorUtilsTest.java diff --git a/api/src/main/java/com/velocitypowered/api/util/LegacyChatColorUtils.java b/api/src/main/java/com/velocitypowered/api/util/LegacyChatColorUtils.java deleted file mode 100644 index a21b97ffd..000000000 --- a/api/src/main/java/com/velocitypowered/api/util/LegacyChatColorUtils.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.velocitypowered.api.util; - -import com.google.common.base.Preconditions; -import org.checkerframework.checker.nullness.qual.NonNull; - -import java.util.regex.Pattern; - -/** - * LegacyChatColorUtils contains utilities for handling legacy Minecraft color codes. Generally, you should prefer - * JSON-based components, but for convenience Velocity provides a limited set of tools to handle Minecraft color codes. - */ -public class LegacyChatColorUtils { - private LegacyChatColorUtils() { - throw new AssertionError(); - } - - /** - * Represents the legacy Minecraft format character, the section symbol. - */ - public static final char FORMAT_CHAR = '\u00a7'; - - /** - * Translates a string with Minecraft color codes prefixed with a different character than the section symbol into - * a string that uses the section symbol. - * @param originalChar the char the color codes are prefixed by - * @param text the text to translate - * @return the translated text - */ - public static String translate(char originalChar, @NonNull String text) { - Preconditions.checkNotNull(text, "text"); - char[] textChars = text.toCharArray(); - int foundSectionIdx = -1; - for (int i = 0; i < textChars.length; i++) { - char textChar = textChars[i]; - if (textChar == originalChar) { - foundSectionIdx = i; - continue; - } - - if (foundSectionIdx >= 0) { - textChar = Character.toLowerCase(textChar); - if ((textChar >= 'a' && textChar <= 'f') || (textChar >= '0' && textChar <= '9') || - (textChar >= 'l' && textChar <= 'o' || textChar == 'r')) { - textChars[foundSectionIdx] = FORMAT_CHAR; - } - foundSectionIdx = -1; - } - } - return new String(textChars); - } - - /** - * A regex that matches all Minecraft color codes and removes them. - */ - private static final Pattern CHAT_COLOR_MATCHER = Pattern.compile("(?i)" + Character.toString(FORMAT_CHAR) + "[0-9A-FL-OR]"); - - /** - * Removes all Minecraft color codes from the string. - * @param text the text to remove color codes from - * @return a new String without Minecraft color codes - */ - public static String removeFormatting(@NonNull String text) { - Preconditions.checkNotNull(text, "text"); - return CHAT_COLOR_MATCHER.matcher(text).replaceAll(""); - } -} diff --git a/api/src/test/java/com/velocitypowered/api/util/LegacyChatColorUtilsTest.java b/api/src/test/java/com/velocitypowered/api/util/LegacyChatColorUtilsTest.java deleted file mode 100644 index b98b7027b..000000000 --- a/api/src/test/java/com/velocitypowered/api/util/LegacyChatColorUtilsTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.velocitypowered.api.util; - -import com.velocitypowered.api.util.LegacyChatColorUtils; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class LegacyChatColorUtilsTest { - private static final String NON_FORMATTED = "Velocity"; - private static final String FORMATTED = "\u00a7cVelocity"; - private static final String FORMATTED_MULTIPLE = "\u00a7c\u00a7lVelocity"; - private static final String FORMATTED_MULTIPLE_VARIED = "\u00a7c\u00a7lVelo\u00a7a\u00a7mcity"; - private static final String INVALID = "\u00a7gVelocity"; - private static final String RAW_SECTION = "\u00a7"; - - @Test - void removeFormattingNonFormatted() { - assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(NON_FORMATTED)); - } - - @Test - void removeFormattingFormatted() { - assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(FORMATTED)); - } - - @Test - void removeFormattingFormattedMultiple() { - assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(FORMATTED_MULTIPLE)); - } - - @Test - void removeFormattingFormattedMultipleVaried() { - assertEquals(NON_FORMATTED, LegacyChatColorUtils.removeFormatting(FORMATTED_MULTIPLE_VARIED)); - } - - @Test - void removeFormattingInvalidFormat() { - assertEquals(INVALID, LegacyChatColorUtils.removeFormatting(INVALID)); - } - - @Test - void removeFormattingRawSection() { - assertEquals(RAW_SECTION, LegacyChatColorUtils.removeFormatting(RAW_SECTION)); - } - - @Test - void translate() { - assertEquals(FORMATTED, LegacyChatColorUtils.translate('&', "&cVelocity")); - } - - @Test - void translateMultiple() { - assertEquals(FORMATTED_MULTIPLE, LegacyChatColorUtils.translate('&', "&c&lVelocity")); - assertEquals(FORMATTED_MULTIPLE_VARIED, LegacyChatColorUtils.translate('&', "&c&lVelo&a&mcity")); - } - - @Test - void translateDifferentChar() { - assertEquals(FORMATTED, LegacyChatColorUtils.translate('$', "$cVelocity")); - assertEquals(FORMATTED_MULTIPLE_VARIED, LegacyChatColorUtils.translate('$', "$c$lVelo$a$mcity")); - } -} \ No newline at end of file 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 feb076dbb..c2ada433c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -5,8 +5,6 @@ import com.google.common.collect.ImmutableMap; import com.moandjiezana.toml.Toml; import com.velocitypowered.api.util.Favicon; import com.velocitypowered.proxy.util.AddressUtil; -import com.velocitypowered.api.util.LegacyChatColorUtils; -import io.netty.buffer.ByteBufUtil; import net.kyori.text.Component; import net.kyori.text.serializer.ComponentSerializers; From f973ded4eeb867e6a125c13d332943b1eeea31bb Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 17 Sep 2018 16:18:47 -0400 Subject: [PATCH 32/62] Tighten identifier checks since NuVotifier fixed this --- .../api/proxy/messages/MinecraftChannelIdentifier.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b8f689324..1355ed239 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 @@ -10,7 +10,7 @@ import java.util.regex.Pattern; * Represents a Minecraft 1.13+ channel identifier. This class is immutable and safe for multi-threaded use. */ public final class MinecraftChannelIdentifier implements ChannelIdentifier { - private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9\\-_]+", Pattern.CASE_INSENSITIVE); + private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9\\-_]+"); private final String namespace; private final String name; From 44b1b82b09ae7b0fa3622d445f56e313be365e1e Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 16:40:51 -0400 Subject: [PATCH 33/62] Try to avoid locking, use an actual concurrent data structure --- .../proxy/server/ServerMap.java | 41 ++++------------- .../server/VelocityRegisteredServer.java | 45 +++++-------------- 2 files changed, 19 insertions(+), 67 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java index 30835df3e..357c97e95 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java @@ -7,13 +7,10 @@ import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.proxy.VelocityServer; import java.util.*; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; public class ServerMap { private final VelocityServer server; private final Map servers = new HashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(); public ServerMap(VelocityServer server) { this.server = server; @@ -22,47 +19,27 @@ public class ServerMap { public Optional getServer(String name) { Preconditions.checkNotNull(name, "server"); String lowerName = name.toLowerCase(Locale.US); - lock.readLock().lock(); - try { - return Optional.ofNullable(servers.get(lowerName)); - } finally { - lock.readLock().unlock(); - } + return Optional.ofNullable(servers.get(lowerName)); } public Collection getAllServers() { - lock.readLock().lock(); - try { - return ImmutableList.copyOf(servers.values()); - } finally { - lock.readLock().unlock(); - } + return ImmutableList.copyOf(servers.values()); } public RegisteredServer register(ServerInfo serverInfo) { Preconditions.checkNotNull(serverInfo, "serverInfo"); String lowerName = serverInfo.getName().toLowerCase(Locale.US); - lock.writeLock().lock(); - try { - VelocityRegisteredServer rs = new VelocityRegisteredServer(server, serverInfo); - Preconditions.checkArgument(servers.putIfAbsent(lowerName, rs) == null, "Server with name %s already registered", serverInfo.getName()); - return rs; - } finally { - lock.writeLock().unlock(); - } + VelocityRegisteredServer rs = new VelocityRegisteredServer(server, serverInfo); + Preconditions.checkArgument(servers.putIfAbsent(lowerName, rs) == null, "Server with name %s already registered", serverInfo.getName()); + return rs; } public void unregister(ServerInfo serverInfo) { Preconditions.checkNotNull(serverInfo, "serverInfo"); String lowerName = serverInfo.getName().toLowerCase(Locale.US); - lock.writeLock().lock(); - try { - RegisteredServer rs = servers.get(lowerName); - Preconditions.checkArgument(rs != null, "Server with name %s is not registered!", serverInfo.getName()); - Preconditions.checkArgument(rs.getServerInfo().equals(serverInfo), "Trying to remove server %s with differing information", serverInfo.getName()); - servers.remove(lowerName); - } finally { - lock.writeLock().unlock(); - } + RegisteredServer rs = servers.get(lowerName); + Preconditions.checkArgument(rs != null, "Server with name %s is not registered!", serverInfo.getName()); + Preconditions.checkArgument(rs.getServerInfo().equals(serverInfo), "Trying to remove server %s with differing information", serverInfo.getName()); + Preconditions.checkState(servers.remove(lowerName, rs), "Server with name %s replaced whilst unregistering", serverInfo.getName()); } } 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 18dfef0d0..b937fc926 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -25,6 +25,7 @@ import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -36,8 +37,7 @@ import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; public class VelocityRegisteredServer implements RegisteredServer { private final VelocityServer server; private final ServerInfo serverInfo; - private final Set players = new HashSet<>(); - private final ReadWriteLock playersLock = new ReentrantReadWriteLock(); + private final Set players = ConcurrentHashMap.newKeySet(); public VelocityRegisteredServer(VelocityServer server, ServerInfo serverInfo) { this.server = server; @@ -51,12 +51,7 @@ public class VelocityRegisteredServer implements RegisteredServer { @Override public Collection getPlayersConnected() { - playersLock.readLock().lock(); - try { - return ImmutableList.copyOf(players); - } finally { - playersLock.readLock().unlock(); - } + return ImmutableList.copyOf(players); } @Override @@ -94,43 +89,23 @@ public class VelocityRegisteredServer implements RegisteredServer { } public void addPlayer(ConnectedPlayer player) { - playersLock.writeLock().lock(); - try { - players.add(player); - } finally { - playersLock.writeLock().unlock(); - } + players.add(player); } public void removePlayer(ConnectedPlayer player) { - playersLock.writeLock().lock(); - try { - players.remove(player); - } finally { - playersLock.writeLock().unlock(); - } + players.remove(player); } @Override public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { - ServerConnection backendConnection = null; - playersLock.readLock().lock(); - try { - for (ConnectedPlayer player : players) { - if (player.getConnectedServer() != null && player.getConnectedServer().getServerInfo().equals(serverInfo)) { - backendConnection = player.getConnectedServer(); - break; - } + for (ConnectedPlayer player : players) { + if (player.getConnectedServer() != null && player.getConnectedServer().getServerInfo().equals(serverInfo)) { + ServerConnection connection = player.getConnectedServer(); + return connection.sendPluginMessage(identifier, data); } - - if (backendConnection == null) { - return false; - } - } finally { - playersLock.readLock().unlock(); } - return backendConnection.sendPluginMessage(identifier, data); + return false; } @Override From 2bfb7061ee746a85c1f824721732079902fd9270 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 16:48:21 -0400 Subject: [PATCH 34/62] Turn StatusRequest into a singleton. --- .../velocitypowered/proxy/protocol/StateRegistry.java | 2 +- .../proxy/protocol/packet/StatusRequest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 91fab4eb4..f343adae4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -23,7 +23,7 @@ public enum StateRegistry { }, STATUS { { - SERVERBOUND.register(StatusRequest.class, StatusRequest::new, + SERVERBOUND.register(StatusRequest.class, () -> StatusRequest.INSTANCE, genericMappings(0x00)); SERVERBOUND.register(StatusPing.class, StatusPing::new, genericMappings(0x01)); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java index c53276e5a..ff9897c69 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java @@ -5,6 +5,12 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket; import io.netty.buffer.ByteBuf; public class StatusRequest implements MinecraftPacket { + public static final StatusRequest INSTANCE = new StatusRequest(); + + private StatusRequest() { + + } + @Override public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -14,4 +20,9 @@ public class StatusRequest implements MinecraftPacket { public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { } + + @Override + public String toString() { + return "StatusRequest"; + } } From 508ba3569731e86bca30bc0af608ef2d8d6c7404 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 16:48:52 -0400 Subject: [PATCH 35/62] Exclude plugins from Git. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fd8f6bff5..aa52285df 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,4 @@ logs/ server-icon.png /bin/ run/ +plugins/ \ No newline at end of file From 717c2afeb33da124f644b6afeb6744cc2e2c8726 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 16:50:33 -0400 Subject: [PATCH 36/62] Update for changes in master. --- .../velocitypowered/proxy/server/ping/PingSessionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java index 60f2ea158..0bce28b74 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ping/PingSessionHandler.java @@ -38,7 +38,7 @@ public class PingSessionHandler implements MinecraftSessionHandler { connection.write(handshake); connection.setState(StateRegistry.STATUS); - connection.write(new StatusRequest()); + connection.write(StatusRequest.INSTANCE); } @Override From c38b73863a1a5306454bc7b7a67f5801079a792f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 16:59:13 -0400 Subject: [PATCH 37/62] Fix more scheduler issues --- .../proxy/scheduler/VelocityScheduler.java | 68 ++++--------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java index 6e10807e3..6f61cea06 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java @@ -91,62 +91,9 @@ public class VelocityScheduler implements Scheduler { @Override public ScheduledTask schedule() { - if (delay == 0 && repeat == 0) { - // A special purpose, simplified implementation - VelocityImmediatelyScheduledTask task = new VelocityImmediatelyScheduledTask(plugin, runnable); - tasksByPlugin.put(plugin, task); - taskService.execute(task); - return task; - } else { - VelocityTask task = new VelocityTask(plugin, runnable, delay, repeat); - tasksByPlugin.put(plugin, task); - return task; - } - } - } - - private class VelocityImmediatelyScheduledTask implements ScheduledTask, Runnable { - private final Object plugin; - private final Runnable runnable; - private final AtomicReference status; - private Thread taskThread; - - private VelocityImmediatelyScheduledTask(Object plugin, Runnable runnable) { - this.plugin = plugin; - this.runnable = runnable; - this.status = new AtomicReference<>(TaskStatus.SCHEDULED); - } - - @Override - public Object plugin() { - return plugin; - } - - @Override - public TaskStatus status() { - return status.get(); - } - - @Override - public void cancel() { - if (status.compareAndSet(TaskStatus.SCHEDULED, TaskStatus.CANCELLED)) { - if (taskThread != null) { - taskThread.interrupt(); - } - } - } - - @Override - public void run() { - taskThread = Thread.currentThread(); - try { - runnable.run(); - } catch (Exception e) { - Log.logger.error("Exception in task {} by plugin {}", runnable, plugin); - } - status.compareAndSet(TaskStatus.SCHEDULED, TaskStatus.FINISHED); - taskThread = null; - tasksByPlugin.remove(plugin, this); + VelocityTask task = new VelocityTask(plugin, runnable, delay, repeat); + tasksByPlugin.put(plugin, task); + return task; } } @@ -154,6 +101,7 @@ public class VelocityScheduler implements Scheduler { private final Object plugin; private final Runnable runnable; private ScheduledFuture future; + private volatile Thread currentTaskThread; private VelocityTask(Object plugin, Runnable runnable, long delay, long repeat) { this.plugin = plugin; @@ -191,6 +139,12 @@ public class VelocityScheduler implements Scheduler { public void cancel() { if (future != null) { future.cancel(false); + + Thread cur = currentTaskThread; + if (cur != null) { + cur.interrupt(); + } + onFinish(); } } @@ -198,6 +152,7 @@ public class VelocityScheduler implements Scheduler { @Override public void run() { taskService.execute(() -> { + currentTaskThread = Thread.currentThread(); try { runnable.run(); } catch (Exception e) { @@ -208,6 +163,7 @@ public class VelocityScheduler implements Scheduler { Log.logger.error("Exception in task {} by plugin {}", runnable, plugin); } } + currentTaskThread = null; }); } From d796f079f075567b41506ab079c3dbe7820c3b8e Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 17:01:26 -0400 Subject: [PATCH 38/62] I forgot this. --- .../main/java/com/velocitypowered/proxy/server/ServerMap.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java index 357c97e95..247b5ebfe 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java @@ -7,10 +7,11 @@ import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.proxy.VelocityServer; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class ServerMap { private final VelocityServer server; - private final Map servers = new HashMap<>(); + private final Map servers = new ConcurrentHashMap<>(); public ServerMap(VelocityServer server) { this.server = server; From 9016162a49ee266f045bd01425e0fb5db050aae2 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 18 Sep 2018 17:24:28 -0400 Subject: [PATCH 39/62] Optimize and clarify GS4 query handler. --- .../proxy/protocol/netty/GS4QueryHandler.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 df093d78d..2628bc445 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 @@ -101,9 +101,6 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler queryResponse.writeByte(QUERY_TYPE_STAT); queryResponse.writeInt(sessionId); - // Fetch information - Collection players = server.getAllPlayers(); - // Start writing the response ResponseWriter responseWriter = new ResponseWriter(queryResponse, queryMessage.readableBytes() == 0); responseWriter.write("hostname", ComponentSerializers.PLAIN.serialize(server.getConfiguration().getMotdComponent())); @@ -114,12 +111,14 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler responseWriter.write("plugins", ""); responseWriter.write("map", "Velocity"); - responseWriter.write("numplayers", players.size()); + responseWriter.write("numplayers", server.getPlayerCount()); responseWriter.write("maxplayers", server.getConfiguration().getShowMaxPlayers()); responseWriter.write("hostport", server.getConfiguration().getBind().getPort()); responseWriter.write("hostip", server.getConfiguration().getBind().getHostString()); - responseWriter.writePlayers(players); + if (!responseWriter.isBasic) { + responseWriter.writePlayers(server.getAllPlayers()); + } break; } @@ -132,6 +131,8 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler ctx.writeAndFlush(responsePacket); } catch (Exception e) { logger.warn("Error while trying to handle a query packet from {}", msg.sender(), e); + // NB: Only need to explicitly release upon exception, writing the response out will decrement the reference + // count. responsePacket.release(); } } From 50c27066e2d823460e6d32fa83b47d91206d64e2 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 19 Sep 2018 14:56:49 -0400 Subject: [PATCH 40/62] Import cleanup --- .../plugin/ap/PluginAnnotationProcessor.java | 3 ++- .../api/event/connection/PreLoginEvent.java | 5 ++-- .../event/player/GameProfileRequestEvent.java | 5 ++-- .../event/player/KickedFromServerEvent.java | 1 - .../player/PlayerSettingsChangedEvent.java | 2 +- .../event/player/ServerPreConnectEvent.java | 1 - .../api/plugin/meta/PluginDependency.java | 1 - .../api/proxy/ConnectionRequestBuilder.java | 2 -- .../com/velocitypowered/api/proxy/Player.java | 3 +-- .../api/proxy/ProxyServer.java | 4 ++-- .../velocitypowered/natives/util/Natives.java | 2 -- .../compression/VelocityCompressorTest.java | 1 - .../velocitypowered/proxy/VelocityServer.java | 16 ++++++------- .../proxy/command/VelocityCommandManager.java | 3 +-- .../proxy/config/AnnotatedConfig.java | 11 ++++----- .../proxy/config/VelocityConfiguration.java | 8 ++----- .../proxy/connection/MinecraftConnection.java | 12 ++-------- .../backend/BackendPlaySessionHandler.java | 2 +- .../backend/LoginSessionHandler.java | 4 ++-- .../backend/VelocityServerConnection.java | 23 ++++++++----------- .../client/ClientPlaySessionHandler.java | 2 +- .../client/ClientSettingsWrapper.java | 1 + .../connection/client/ConnectedPlayer.java | 18 +++++---------- .../client/HandshakeSessionHandler.java | 2 +- .../client/InitialConnectSessionHandler.java | 2 +- .../client/LoginSessionHandler.java | 11 ++++----- .../client/StatusSessionHandler.java | 6 ++--- .../proxy/console/VelocityConsole.java | 4 +++- .../messages/VelocityChannelRegistrar.java | 5 +++- .../proxy/network/ConnectionManager.java | 14 ++--------- .../proxy/network/http/NettyHttpClient.java | 2 +- .../proxy/plugin/VelocityEventManager.java | 14 ++++++++--- .../proxy/plugin/VelocityPluginManager.java | 2 +- .../proxy/plugin/loader/JavaPluginLoader.java | 4 +++- .../loader/java/VelocityPluginModule.java | 1 - .../proxy/protocol/StateRegistry.java | 2 +- .../netty/MinecraftCompressDecoder.java | 2 +- .../netty/MinecraftCompressEncoder.java | 2 +- .../protocol/netty/MinecraftDecoder.java | 5 +++- .../netty/MinecraftVarintLengthEncoder.java | 1 - .../proxy/protocol/packet/Chat.java | 2 +- .../proxy/protocol/packet/Disconnect.java | 2 +- .../proxy/protocol/packet/Handshake.java | 2 +- .../protocol/packet/HeaderAndFooter.java | 4 ++-- .../proxy/protocol/packet/KeepAlive.java | 2 +- .../proxy/protocol/packet/ServerLogin.java | 2 +- .../proxy/protocol/packet/StatusRequest.java | 2 +- .../proxy/protocol/packet/StatusResponse.java | 2 +- .../protocol/util/PluginMessageUtil.java | 1 - .../proxy/scheduler/VelocityScheduler.java | 1 - .../proxy/server/ServerMap.java | 5 +++- .../server/VelocityRegisteredServer.java | 7 ++---- .../concurrency/RecordingThreadFactory.java | 1 - .../proxy/protocol/PacketRegistryTest.java | 4 +--- .../scheduler/VelocitySchedulerTest.java | 2 +- .../proxy/util/RatelimiterTest.java | 3 ++- .../proxy/util/ServerMapTest.java | 3 ++- 57 files changed, 111 insertions(+), 143 deletions(-) diff --git a/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java b/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java index a00f9c7d3..6a8310c72 100644 --- a/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java +++ b/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java @@ -19,7 +19,8 @@ import javax.tools.StandardLocation; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; -import java.util.*; +import java.util.Objects; +import java.util.Set; @SupportedAnnotationTypes({"com.velocitypowered.api.plugin.Plugin"}) public class PluginAnnotationProcessor extends AbstractProcessor { 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 04fdc7622..d5e817f49 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,14 +3,13 @@ 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 java.util.Optional; - import net.kyori.text.Component; import net.kyori.text.serializer.ComponentSerializers; - import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Optional; + /** * This event is fired when a player has initiated a connection with the proxy but before the proxy authenticates the * player with Mojang or before the player's proxy connection is fully established (for offline mode). diff --git a/api/src/main/java/com/velocitypowered/api/event/player/GameProfileRequestEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/GameProfileRequestEvent.java index 2a02d0a18..ab46c37c1 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/GameProfileRequestEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/GameProfileRequestEvent.java @@ -1,10 +1,9 @@ package com.velocitypowered.api.event.player; -import com.velocitypowered.api.proxy.InboundConnection; -import org.checkerframework.checker.nullness.qual.Nullable; - import com.google.common.base.Preconditions; +import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.util.GameProfile; +import org.checkerframework.checker.nullness.qual.Nullable; /** * This event is fired after the {@link com.velocitypowered.api.event.connection.PreLoginEvent} in order to set up the diff --git a/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java index 86bce5e66..bfdf296e6 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/KickedFromServerEvent.java @@ -4,7 +4,6 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; import net.kyori.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; diff --git a/api/src/main/java/com/velocitypowered/api/event/player/PlayerSettingsChangedEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/PlayerSettingsChangedEvent.java index a657543ad..b59c643ff 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/PlayerSettingsChangedEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/PlayerSettingsChangedEvent.java @@ -1,8 +1,8 @@ package com.velocitypowered.api.event.player; import com.google.common.base.Preconditions; -import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.player.PlayerSettings; public class PlayerSettingsChangedEvent { private final Player player; diff --git a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java index 8aa142ddd..f80d3cb33 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/ServerPreConnectEvent.java @@ -4,7 +4,6 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; diff --git a/api/src/main/java/com/velocitypowered/api/plugin/meta/PluginDependency.java b/api/src/main/java/com/velocitypowered/api/plugin/meta/PluginDependency.java index f63b87c5c..2229d6ed3 100644 --- a/api/src/main/java/com/velocitypowered/api/plugin/meta/PluginDependency.java +++ b/api/src/main/java/com/velocitypowered/api/plugin/meta/PluginDependency.java @@ -1,7 +1,6 @@ package com.velocitypowered.api.plugin.meta; import javax.annotation.Nullable; - import java.util.Objects; import java.util.Optional; 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 17c9173f3..d7369a15c 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ConnectionRequestBuilder.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ConnectionRequestBuilder.java @@ -1,9 +1,7 @@ package com.velocitypowered.api.proxy; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; import net.kyori.text.Component; -import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Optional; import java.util.concurrent.CompletableFuture; 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 83c05a4ae..0bdc9b607 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -1,11 +1,10 @@ package com.velocitypowered.api.proxy; import com.velocitypowered.api.command.CommandSource; -import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import com.velocitypowered.api.proxy.messages.ChannelMessageSource; +import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.util.MessagePosition; import net.kyori.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; 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 553e88c10..c6c75df8a 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -1,13 +1,13 @@ package com.velocitypowered.api.proxy; -import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandManager; +import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.messages.ChannelRegistrar; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.api.scheduler.Scheduler; import java.net.InetSocketAddress; import java.util.Collection; diff --git a/native/src/main/java/com/velocitypowered/natives/util/Natives.java b/native/src/main/java/com/velocitypowered/natives/util/Natives.java index 9c135843d..5c22ae752 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/Natives.java +++ b/native/src/main/java/com/velocitypowered/natives/util/Natives.java @@ -3,10 +3,8 @@ package com.velocitypowered.natives.util; import com.google.common.collect.ImmutableList; import com.velocitypowered.natives.compression.JavaVelocityCompressor; import com.velocitypowered.natives.compression.NativeVelocityCompressor; -import com.velocitypowered.natives.compression.VelocityCompressor; import com.velocitypowered.natives.compression.VelocityCompressorFactory; import com.velocitypowered.natives.encryption.JavaVelocityCipher; -import com.velocitypowered.natives.encryption.NativeVelocityCipher; import com.velocitypowered.natives.encryption.VelocityCipherFactory; import java.io.IOException; diff --git a/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java b/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java index 93136b8df..363c80442 100644 --- a/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java +++ b/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import java.util.Random; -import java.util.function.Supplier; import java.util.zip.DataFormatException; import java.util.zip.Deflater; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index b9711f915..7eaf8eaf3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -8,30 +8,30 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.util.Favicon; -import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.server.ServerInfo; -import com.velocitypowered.proxy.network.ConnectionManager; +import com.velocitypowered.api.util.Favicon; import com.velocitypowered.proxy.command.ServerCommand; import com.velocitypowered.proxy.command.ShutdownCommand; import com.velocitypowered.proxy.command.VelocityCommand; -import com.velocitypowered.proxy.config.VelocityConfiguration; -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import com.velocitypowered.proxy.network.http.NettyHttpClient; import com.velocitypowered.proxy.command.VelocityCommandManager; import com.velocitypowered.proxy.config.AnnotatedConfig; +import com.velocitypowered.proxy.config.VelocityConfiguration; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.messages.VelocityChannelRegistrar; +import com.velocitypowered.proxy.network.ConnectionManager; +import com.velocitypowered.proxy.network.http.NettyHttpClient; import com.velocitypowered.proxy.plugin.VelocityEventManager; -import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.plugin.VelocityPluginManager; +import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.scheduler.VelocityScheduler; +import com.velocitypowered.proxy.server.ServerMap; import com.velocitypowered.proxy.util.AddressUtil; import com.velocitypowered.proxy.util.EncryptionUtils; import com.velocitypowered.proxy.util.Ratelimiter; -import com.velocitypowered.proxy.server.ServerMap; import io.netty.bootstrap.Bootstrap; import net.kyori.text.Component; import net.kyori.text.TextComponent; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index 38c3759ea..7793bf075 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -1,10 +1,9 @@ package com.velocitypowered.proxy.command; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; import com.velocitypowered.api.command.Command; -import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandManager; +import com.velocitypowered.api.command.CommandSource; import java.util.*; import java.util.stream.Collectors; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/AnnotatedConfig.java b/proxy/src/main/java/com/velocitypowered/proxy/config/AnnotatedConfig.java index 32f9a4880..99a417846 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/AnnotatedConfig.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/AnnotatedConfig.java @@ -1,5 +1,8 @@ package com.velocitypowered.proxy.config; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.io.File; import java.io.IOException; import java.lang.annotation.ElementType; @@ -8,18 +11,12 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; -import java.nio.file.AtomicMoveNotSupportedException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; +import java.nio.file.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; /** * Only for simple configs 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 c2ada433c..303dbcd3f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -7,6 +7,7 @@ import com.velocitypowered.api.util.Favicon; import com.velocitypowered.proxy.util.AddressUtil; import net.kyori.text.Component; import net.kyori.text.serializer.ComponentSerializers; +import org.apache.logging.log4j.Logger; import java.io.IOException; import java.io.Reader; @@ -15,12 +16,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import org.apache.logging.log4j.Logger; +import java.util.*; public class VelocityConfiguration extends AnnotatedConfig { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index 0f6844304..6f57fd2ba 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -2,13 +2,13 @@ package com.velocitypowered.proxy.connection; import com.google.common.base.Preconditions; import com.velocitypowered.natives.compression.VelocityCompressor; +import com.velocitypowered.natives.encryption.VelocityCipher; import com.velocitypowered.natives.encryption.VelocityCipherFactory; import com.velocitypowered.natives.util.Natives; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.StateRegistry; -import com.velocitypowered.natives.encryption.VelocityCipher; import com.velocitypowered.proxy.protocol.netty.*; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; @@ -21,17 +21,9 @@ import org.apache.logging.log4j.Logger; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; - import java.security.GeneralSecurityException; -import static com.velocitypowered.proxy.network.Connections.CIPHER_DECODER; -import static com.velocitypowered.proxy.network.Connections.CIPHER_ENCODER; -import static com.velocitypowered.proxy.network.Connections.COMPRESSION_DECODER; -import static com.velocitypowered.proxy.network.Connections.COMPRESSION_ENCODER; -import static com.velocitypowered.proxy.network.Connections.FRAME_DECODER; -import static com.velocitypowered.proxy.network.Connections.FRAME_ENCODER; -import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER; -import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; +import static com.velocitypowered.proxy.network.Connections.*; /** * A utility class to make working with the pipeline a little less painful and transparently handles certain Minecraft 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 f0d23d362..fea65c6a9 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 @@ -4,13 +4,13 @@ import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.packet.*; -import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; 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 1a7f5e2df..5fb5341b2 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 @@ -1,18 +1,18 @@ package com.velocitypowered.proxy.connection.backend; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; +import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.config.VelocityConfiguration; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; -import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.*; -import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.kyori.text.TextComponent; 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 87f55b6e9..80210e33a 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 @@ -4,10 +4,14 @@ import com.google.common.base.Preconditions; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; -import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.PlayerInfoForwarding; +import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; @@ -15,13 +19,11 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; import com.velocitypowered.proxy.protocol.packet.Handshake; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.ServerLogin; -import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.protocol.StateRegistry; -import com.velocitypowered.api.proxy.server.ServerInfo; -import com.velocitypowered.proxy.VelocityServer; -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.server.VelocityRegisteredServer; -import io.netty.channel.*; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelInitializer; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.AttributeKey; @@ -29,12 +31,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import static com.velocitypowered.proxy.VelocityServer.GSON; -import static com.velocitypowered.proxy.network.Connections.FRAME_DECODER; -import static com.velocitypowered.proxy.network.Connections.FRAME_ENCODER; -import static com.velocitypowered.proxy.network.Connections.HANDLER; -import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER; -import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; -import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT; +import static com.velocitypowered.proxy.network.Connections.*; public class VelocityServerConnection implements MinecraftConnectionAssociation, ServerConnection { static final AttributeKey> CONNECTION_NOTIFIER = 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 836481fba..91d2d64ac 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 @@ -4,12 +4,12 @@ import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.packet.*; -import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.util.ThrowableUtils; import io.netty.buffer.ByteBuf; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java index 4977c5df9..235915f24 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java @@ -3,6 +3,7 @@ package com.velocitypowered.proxy.connection.client; import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.SkinParts; import com.velocitypowered.proxy.protocol.packet.ClientSettings; + import java.util.Locale; public class ClientSettingsWrapper implements PlayerSettings { 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 5cfe49ac4..032f7c8fa 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 @@ -7,30 +7,24 @@ import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent; import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.api.permission.PermissionFunction; import com.velocitypowered.api.permission.PermissionProvider; -import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; +import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.MessagePosition; -import com.velocitypowered.api.proxy.Player; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.VelocityConstants; +import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; -import com.velocitypowered.api.util.GameProfile; -import com.velocitypowered.proxy.protocol.packet.Chat; -import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; -import com.velocitypowered.proxy.protocol.packet.ClientSettings; -import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import com.velocitypowered.proxy.protocol.packet.*; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.util.ThrowableUtils; -import com.velocitypowered.api.proxy.server.ServerInfo; -import com.velocitypowered.proxy.protocol.packet.Disconnect; -import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter; - import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.TranslatableComponent; 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 54899d2a1..6ed3c805c 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 @@ -5,12 +5,12 @@ import com.google.common.collect.ImmutableList; import com.velocitypowered.api.event.connection.ConnectionHandshakeEvent; import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; -import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.StateRegistry; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialConnectSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialConnectSessionHandler.java index 64273473a..c25298bef 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialConnectSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialConnectSessionHandler.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.connection.client; -import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; public class InitialConnectSessionHandler implements MinecraftSessionHandler { private final ConnectedPlayer player; 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 bf63dc63d..116351ebb 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 @@ -9,17 +9,16 @@ import com.velocitypowered.api.event.permission.PermissionsSetupEvent; import com.velocitypowered.api.event.player.GameProfileRequestEvent; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.proxy.server.ServerInfo; -import com.velocitypowered.proxy.config.PlayerInfoForwarding; -import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.config.PlayerInfoForwarding; +import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.*; -import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.connection.MinecraftSessionHandler; -import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.util.EncryptionUtils; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; 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 ccc53a8ac..7c4e84376 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 @@ -4,16 +4,16 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.VelocityConfiguration; +import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; 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.connection.MinecraftConnection; -import com.velocitypowered.api.proxy.server.ServerPing; -import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; 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 fcb1d34f9..4e233ddce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -4,7 +4,9 @@ import com.velocitypowered.proxy.VelocityServer; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; import net.minecrell.terminalconsole.SimpleTerminalConsole; -import org.jline.reader.*; +import org.jline.reader.Candidate; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; import java.util.List; import java.util.Optional; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java index 1536716cc..c6feff212 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/messages/VelocityChannelRegistrar.java @@ -2,7 +2,10 @@ package com.velocitypowered.proxy.messages; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import com.velocitypowered.api.proxy.messages.*; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; +import com.velocitypowered.api.proxy.messages.ChannelRegistrar; +import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import java.util.Collection; import java.util.Map; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index 9a115a608..65301cd77 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -7,21 +7,11 @@ import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.StateRegistry; -import com.velocitypowered.proxy.protocol.netty.GS4QueryHandler; -import com.velocitypowered.proxy.protocol.netty.LegacyPingDecoder; -import com.velocitypowered.proxy.protocol.netty.LegacyPingEncoder; -import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; -import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; -import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; -import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; +import com.velocitypowered.proxy.protocol.netty.*; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; -import io.netty.channel.epoll.Epoll; -import io.netty.channel.epoll.EpollDatagramChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.epoll.EpollServerSocketChannel; -import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.epoll.*; import io.netty.channel.kqueue.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/http/NettyHttpClient.java b/proxy/src/main/java/com/velocitypowered/proxy/network/http/NettyHttpClient.java index 24ab295da..f14f10c40 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/http/NettyHttpClient.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/http/NettyHttpClient.java @@ -2,7 +2,7 @@ package com.velocitypowered.proxy.network.http; import com.velocitypowered.proxy.VelocityServer; import io.netty.bootstrap.Bootstrap; -import io.netty.channel.*; +import io.netty.channel.Channel; import io.netty.channel.pool.*; import io.netty.handler.codec.http.*; import io.netty.handler.ssl.SslContext; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java index 3f84e6279..b93afb3fd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java @@ -13,7 +13,10 @@ import com.velocitypowered.proxy.util.concurrency.RecordingThreadFactory; import net.kyori.event.EventSubscriber; import net.kyori.event.PostResult; import net.kyori.event.SimpleEventBus; -import net.kyori.event.method.*; +import net.kyori.event.method.EventExecutor; +import net.kyori.event.method.MethodScanner; +import net.kyori.event.method.MethodSubscriptionAdapter; +import net.kyori.event.method.SimpleMethodSubscriptionAdapter; import net.kyori.event.method.asm.ASMEventExecutorFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -21,8 +24,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; import java.lang.reflect.Method; import java.net.URL; -import java.util.*; -import java.util.concurrent.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.IdentityHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; public class VelocityEventManager implements EventManager { private static final Logger logger = LogManager.getLogger(VelocityEventManager.class); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java index f4625a900..3c1d5b2c5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.plugin; -import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginContainer; +import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.meta.PluginDependency; import com.velocitypowered.proxy.VelocityServer; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/JavaPluginLoader.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/JavaPluginLoader.java index dc26dec3e..09d1df949 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/JavaPluginLoader.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/JavaPluginLoader.java @@ -2,7 +2,9 @@ package com.velocitypowered.proxy.plugin.loader; import com.google.inject.Guice; import com.google.inject.Injector; -import com.velocitypowered.api.plugin.*; +import com.velocitypowered.api.plugin.InvalidPluginException; +import com.velocitypowered.api.plugin.PluginContainer; +import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.meta.PluginDependency; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.proxy.VelocityServer; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java index cc3ddadc2..66e116eeb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/java/VelocityPluginModule.java @@ -8,7 +8,6 @@ import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; -import com.velocitypowered.proxy.VelocityServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index f343adae4..8056c3823 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -8,7 +8,7 @@ import io.netty.util.collection.IntObjectMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import java.util.*; +import java.util.Objects; import java.util.function.Supplier; import static com.velocitypowered.proxy.protocol.ProtocolConstants.*; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java index 61893ff44..95b218056 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java @@ -1,8 +1,8 @@ package com.velocitypowered.proxy.protocol.netty; import com.google.common.base.Preconditions; -import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.natives.compression.VelocityCompressor; +import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java index 802f45121..48ded65a5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressEncoder.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.protocol.netty; -import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.natives.compression.VelocityCompressor; +import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java index 07ddb287f..4b7e00232 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java @@ -1,7 +1,10 @@ package com.velocitypowered.proxy.protocol.netty; import com.google.common.base.Preconditions; -import com.velocitypowered.proxy.protocol.*; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.StateRegistry; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.CorruptedFrameException; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java index 7a0d61c92..3116c6a69 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java @@ -4,7 +4,6 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToMessageEncoder; import java.util.List; 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 c3de9c6b9..f289b7c54 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 @@ -1,8 +1,8 @@ package com.velocitypowered.proxy.protocol.packet; import com.google.common.base.Preconditions; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import net.kyori.text.Component; 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 1420b7981..f6dfd877a 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 @@ -1,8 +1,8 @@ package com.velocitypowered.proxy.protocol.packet; import com.google.common.base.Preconditions; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import net.kyori.text.Component; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Handshake.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Handshake.java index dec9ecb4a..aef59ec2b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Handshake.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/Handshake.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; 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 35c75af30..6ee851d65 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 @@ -2,13 +2,13 @@ package com.velocitypowered.proxy.protocol.packet; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants.Direction; -import static com.velocitypowered.proxy.protocol.ProtocolUtils.writeString; - import io.netty.buffer.ByteBuf; import net.kyori.text.Component; import net.kyori.text.serializer.ComponentSerializer; import net.kyori.text.serializer.ComponentSerializers; +import static com.velocitypowered.proxy.protocol.ProtocolUtils.writeString; + public class HeaderAndFooter implements MinecraftPacket { private static final HeaderAndFooter RESET = new HeaderAndFooter("{\"translate\":\"\"}", "{\"translate\":\"\"}"); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java index 638b15d52..1e491b61b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/KeepAlive.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLogin.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLogin.java index 459afe338..a9cb0ac68 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLogin.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLogin.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java index ff9897c69..33e1e1411 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequest.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import io.netty.buffer.ByteBuf; public class StatusRequest implements MinecraftPacket { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponse.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponse.java index 75a052258..7c397fa83 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponse.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponse.java @@ -1,7 +1,7 @@ package com.velocitypowered.proxy.protocol.packet; -import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index e3fdeab31..1ffb37dc9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableList; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import java.nio.charset.StandardCharsets; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java index 6f61cea06..0b0d110d9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java @@ -16,7 +16,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; public class VelocityScheduler implements Scheduler { private final PluginManager pluginManager; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java index 247b5ebfe..2216387e7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/ServerMap.java @@ -6,7 +6,10 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.proxy.VelocityServer; -import java.util.*; +import java.util.Collection; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; public class ServerMap { 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 b937fc926..7abd379f0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -23,16 +23,13 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.handler.timeout.ReadTimeoutHandler; -import java.util.*; +import java.util.Collection; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.velocitypowered.proxy.network.Connections.*; -import static com.velocitypowered.proxy.network.Connections.HANDLER; -import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; public class VelocityRegisteredServer implements RegisteredServer { private final VelocityServer server; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/concurrency/RecordingThreadFactory.java b/proxy/src/main/java/com/velocitypowered/proxy/util/concurrency/RecordingThreadFactory.java index ae289bb1c..1de4b165d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/concurrency/RecordingThreadFactory.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/concurrency/RecordingThreadFactory.java @@ -6,7 +6,6 @@ import com.google.common.collect.MapMaker; import java.util.Collections; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadFactory; /** diff --git a/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java b/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java index dac88e563..cc227f471 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/protocol/PacketRegistryTest.java @@ -3,9 +3,7 @@ package com.velocitypowered.proxy.protocol; import com.velocitypowered.proxy.protocol.packet.Handshake; import org.junit.jupiter.api.Test; -import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12; -import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12_1; -import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_12_2; +import static com.velocitypowered.proxy.protocol.ProtocolConstants.*; import static org.junit.jupiter.api.Assertions.*; class PacketRegistryTest { diff --git a/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java b/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java index d6d6896ee..7be0008c8 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/scheduler/VelocitySchedulerTest.java @@ -9,7 +9,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; class VelocitySchedulerTest { // TODO: The timings here will be inaccurate on slow systems. Need to find a testing-friendly replacement for Thread.sleep() diff --git a/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java b/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java index b9f9ff204..ba97fc493 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java @@ -7,7 +7,8 @@ import java.net.InetAddress; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class RatelimiterTest { diff --git a/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java b/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java index ad8d3d52b..4105fdac7 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/util/ServerMapTest.java @@ -9,7 +9,8 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class ServerMapTest { private static final InetSocketAddress TEST_ADDRESS = new InetSocketAddress(InetAddress.getLoopbackAddress(), 25565); From 5c3e1adee2deb717b67706589ba401cba84b3cb4 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 19 Sep 2018 14:57:53 -0400 Subject: [PATCH 41/62] Fix this annoying wart --- .../java/com/velocitypowered/api/scheduler/Scheduler.java | 4 ++-- .../velocitypowered/proxy/scheduler/VelocityScheduler.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/scheduler/Scheduler.java b/api/src/main/java/com/velocitypowered/api/scheduler/Scheduler.java index fd652d1c6..2854a664c 100644 --- a/api/src/main/java/com/velocitypowered/api/scheduler/Scheduler.java +++ b/api/src/main/java/com/velocitypowered/api/scheduler/Scheduler.java @@ -24,7 +24,7 @@ public interface Scheduler { * @param unit the unit of time for {@code time} * @return this builder, for chaining */ - TaskBuilder delay(int time, TimeUnit unit); + TaskBuilder delay(long time, TimeUnit unit); /** * Specifies that the task should continue running after waiting for the specified amount, until it is cancelled. @@ -32,7 +32,7 @@ public interface Scheduler { * @param unit the unit of time for {@code time} * @return this builder, for chaining */ - TaskBuilder repeat(int time, TimeUnit unit); + TaskBuilder repeat(long time, TimeUnit unit); /** * Clears the delay on this task. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java index 0b0d110d9..d37abc0e3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java @@ -65,13 +65,13 @@ public class VelocityScheduler implements Scheduler { } @Override - public TaskBuilder delay(int time, TimeUnit unit) { + public TaskBuilder delay(long time, TimeUnit unit) { this.delay = unit.toMillis(time); return this; } @Override - public TaskBuilder repeat(int time, TimeUnit unit) { + public TaskBuilder repeat(long time, TimeUnit unit) { this.repeat = unit.toMillis(time); return this; } From b7e6334324112418888b827f4a9c68f895abdc5b Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 19 Sep 2018 15:20:18 -0400 Subject: [PATCH 42/62] Add toString() to PluginMessageEvent --- .../api/event/connection/PluginMessageEvent.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java index 4caf3280c..b16f933e4 100644 --- a/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/connection/PluginMessageEvent.java @@ -60,6 +60,17 @@ public class PluginMessageEvent implements ResultedEvent getModList() { + return modList; + } } public static class Mod { From 1bdba0075f8753ed5b41401eac56c8b6e170be0c Mon Sep 17 00:00:00 2001 From: PurpleIsEverything Date: Wed, 19 Sep 2018 17:13:29 -0600 Subject: [PATCH 44/62] Rename getModList to getMods --- .../java/com/velocitypowered/api/proxy/server/ServerPing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0c9feffb3..bf9a3f2ee 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 @@ -317,7 +317,7 @@ public class ServerPing { return type; } - public List getModList() { + public List getMods() { return modList; } } From 95c761479f870dae3bb0a1dfafca33126faa4ed0 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 19 Sep 2018 21:29:07 -0400 Subject: [PATCH 45/62] Make connections more robust --- .../connection/client/ConnectedPlayer.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) 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 032f7c8fa..8917fec7e 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 @@ -262,17 +262,21 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return server.getServers().getServer(toTryName); } - private CompletableFuture connect(ConnectionRequestBuilderImpl request) { + private Optional checkServer(RegisteredServer server) { + Preconditions.checkState(server instanceof VelocityRegisteredServer, "Not a valid Velocity server."); if (connectionInFlight != null) { - return CompletableFuture.completedFuture( - ConnectionRequestResults.plainResult(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS) - ); + return Optional.of(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS); } + if (connectedServer != null && connectedServer.getServer().equals(server)) { + return Optional.of(ConnectionRequestBuilder.Status.ALREADY_CONNECTED); + } + return Optional.empty(); + } - if (connectedServer != null && connectedServer.getServerInfo().equals(request.getServer())) { - return CompletableFuture.completedFuture( - ConnectionRequestResults.plainResult(ConnectionRequestBuilder.Status.ALREADY_CONNECTED) - ); + private CompletableFuture connect(ConnectionRequestBuilderImpl request) { + Optional initialCheck = checkServer(request.getServer()); + if (initialCheck.isPresent()) { + return CompletableFuture.completedFuture(ConnectionRequestResults.plainResult(initialCheck.get())); } // Otherwise, initiate the connection. @@ -286,7 +290,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } RegisteredServer rs = newEvent.getResult().getServer().get(); - Preconditions.checkState(rs instanceof VelocityRegisteredServer, "Not a valid Velocity server."); + Optional lastCheck = checkServer(rs); + if (lastCheck.isPresent()) { + return CompletableFuture.completedFuture(ConnectionRequestResults.plainResult(lastCheck.get())); + } return new VelocityServerConnection((VelocityRegisteredServer) rs, this, server).connect(); }); } From 921bfbae87df2b6e765e5abe182fbeb4b63aba79 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 20 Sep 2018 14:59:34 +0100 Subject: [PATCH 46/62] Add missing getters to ServerPing and rename Modinfo --> ModInfo --- .../api/proxy/server/ServerPing.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) 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 bf9a3f2ee..f4fb7c1ae 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 @@ -16,13 +16,13 @@ public class ServerPing { private final Players players; private final Component description; private final @Nullable Favicon favicon; - private final Modinfo modinfo; + private final ModInfo modinfo; public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon) { - this(version, players, description, favicon, Modinfo.DEFAULT); + this(version, players, description, favicon, ModInfo.DEFAULT); } - public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon, @Nullable Modinfo modinfo) { + public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon, ServerPing.@Nullable ModInfo modinfo) { this.version = Preconditions.checkNotNull(version, "version"); this.players = players; this.description = Preconditions.checkNotNull(description, "description"); @@ -46,7 +46,7 @@ public class ServerPing { return Optional.ofNullable(favicon); } - public Optional getModinfo() { + public Optional getModinfo() { return Optional.ofNullable(modinfo); } @@ -165,7 +165,7 @@ public class ServerPing { public ServerPing build() { return new ServerPing(version, nullOutPlayers ? null : new Players(onlinePlayers, maximumPlayers, samplePlayers), description, favicon, - nullOutModinfo ? null : new Modinfo(modType, mods)); + nullOutModinfo ? null : new ModInfo(modType, mods)); } public Version getVersion() { @@ -302,13 +302,13 @@ public class ServerPing { } } - public static class Modinfo { - public static final Modinfo DEFAULT = new Modinfo("FML", ImmutableList.of()); + public static class ModInfo { + public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of()); private final String type; private final List modList; - public Modinfo(String type, List modList) { + public ModInfo(String type, List modList) { this.type = Preconditions.checkNotNull(type, "type"); this.modList = ImmutableList.copyOf(modList); } @@ -330,5 +330,13 @@ public class ServerPing { this.id = Preconditions.checkNotNull(id, "id"); this.version = Preconditions.checkNotNull(version, "version"); } + + public String getId() { + return id; + } + + public String getVersion() { + return version; + } } } From 86c1d42fa513be3994845c3962e9019abfa9775a Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 20 Sep 2018 15:05:24 +0100 Subject: [PATCH 47/62] Fix/improve gitignore file to fully cover all IntelliJ IDEA files Although the gitignore.io template does a decent job, it isn't broad enough to cover all of the files generated by IDEA. :( --- .gitignore | 179 +++++++++++++++++++---------------------------------- 1 file changed, 64 insertions(+), 115 deletions(-) diff --git a/.gitignore b/.gitignore index aa52285df..8df1bfa3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,135 +1,84 @@ - -# Created by https://www.gitignore.io/api/java,gradle,intellij - ### Intellij ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf -.idea/**/compiler.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/modules.xml -# .idea/*.iml -# .idea/modules - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format +.idea/ *.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin +/out/ +*.iml .idea_modules/ - -# JIRA plugin atlassian-ide-plugin.xml -# Cursive Clojure plugin -.idea/replstate.xml +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders +.externalToolBuilders/ +*.launch +.factorypath +.recommenders/ +.apt_generated/ +.project +.classpath -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties +### Linux ### +*~ +.fuse_hidden* +.directory +.Trash-* +.nfs* -# Editor-based Rest Client -.idea/httpRequests +### macOS ### +.DS_Store +.AppleDouble +.LSOverride +Icon +._* +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk -### Intellij Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -*.iml -modules.xml -.idea/misc.xml -*.ipr -.idea/vcs.xml - -# Sonarlint plugin -.idea/sonarlint - -### Java ### -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Eclipse # -**/.classpath -**/.project -**/.settings/ -**/bin/ - -# NetBeans Gradle# +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ .nb-gradle/ -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db +*.stackdump +[Dd]esktop.ini +$RECYCLE.BIN/ +*.lnk ### Gradle ### .gradle -build/ - -# Ignore Gradle GUI config +/build/ +/out/ gradle-app.setting - -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar - -# Cache of project .gradletasknamecache -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties - - -# End of https://www.gitignore.io/api/java,gradle,intellij - -# Other trash +### Other trash ### logs/ /velocity.toml server-icon.png From e577d6b6f54eae437f11f4e4d8c683ade194e1e4 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 20 Sep 2018 16:09:46 -0400 Subject: [PATCH 48/62] Fix typo from PR. --- .../proxy/connection/client/StatusSessionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7c4e84376..a8b0f3bd0 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 @@ -49,7 +49,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler { new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(), ImmutableList.of()), configuration.getMotdComponent(), configuration.getFavicon(), - configuration.isAnnounceForge() ? ServerPing.Modinfo.DEFAULT : null + configuration.isAnnounceForge() ? ServerPing.ModInfo.DEFAULT : null ); ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing); From 8763573ae61c6b10d0b05b458cb07af11cf6fbeb Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 20 Sep 2018 16:39:50 -0400 Subject: [PATCH 49/62] Major improvements to tab complete support. - Removed half-baked 1.13 support that didn't even work anyway. Perhaps in the future we could restore it by rewriting the available commands packet. - Proxy commands now show up in tab complete responses where possible. --- .../proxy/command/VelocityCommandManager.java | 1 + .../backend/BackendPlaySessionHandler.java | 2 + .../client/ClientPlaySessionHandler.java | 46 ++++---- .../proxy/protocol/StateRegistry.java | 10 +- .../protocol/packet/TabCompleteRequest.java | 50 +++------ .../protocol/packet/TabCompleteResponse.java | 106 ++---------------- 6 files changed, 52 insertions(+), 163 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index 7793bf075..a6ec96c69 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -66,6 +66,7 @@ public class VelocityCommandManager implements CommandManager { if (split.length == 1) { return Optional.of(commands.keySet().stream() .filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length())) + .map(cmd -> "/" + cmd) .collect(Collectors.toList())); } 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 fea65c6a9..88cba258d 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 @@ -98,6 +98,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { } }, connection.getMinecraftConnection().getChannel().eventLoop()); } + } else if (packet instanceof TabCompleteResponse) { + playerHandler.handleTabCompleteResponse((TabCompleteResponse) packet); } else if (connection.hasCompletedJoin()) { // 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 91d2d64ac..0672839dc 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 @@ -36,6 +36,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private final Set clientPluginMsgChannels = new HashSet<>(); private final Queue loginPluginMessages = new ArrayDeque<>(); private final VelocityServer server; + private TabCompleteRequest outstandingTabComplete; public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) { this.player = player; @@ -100,31 +101,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } if (packet instanceof TabCompleteRequest) { - TabCompleteRequest req = (TabCompleteRequest) packet; - int lastSpace = req.getCommand().indexOf(' '); - if (!req.isAssumeCommand() && lastSpace != -1) { - String command = req.getCommand().substring(1); - try { - Optional> offers = server.getCommandManager().offerSuggestions(player, command); - if (offers.isPresent()) { - TabCompleteResponse response = new TabCompleteResponse(); - response.setTransactionId(req.getTransactionId()); - response.setStart(lastSpace); - response.setLength(req.getCommand().length() - lastSpace); - - for (String s : offers.get()) { - response.getOffers().add(new TabCompleteResponse.Offer(s, null)); - } - - player.getConnection().write(response); - } else { - serverConnection.getMinecraftConnection().write(packet); - } - } catch (Exception e) { - logger.error("Unable to provide tab list completions for " + player.getUsername() + " for command '" + req.getCommand() + "'", e); - } - return; - } + // Record the request so that the outstanding request can be augmented later. + outstandingTabComplete = (TabCompleteRequest) packet; + serverConnection.getMinecraftConnection().write(packet); } if (packet instanceof PluginMessage) { @@ -327,4 +306,21 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { this.lastPingID = -1; this.lastPingSent = -1; } + + public void handleTabCompleteResponse(TabCompleteResponse response) { + if (outstandingTabComplete != null) { + if (!outstandingTabComplete.isAssumeCommand()) { + String command = outstandingTabComplete.getCommand().substring(1); + try { + Optional> offers = server.getCommandManager().offerSuggestions(player, command); + offers.ifPresent(strings -> response.getOffers().addAll(strings)); + } catch (Exception e) { + logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(), + command, e); + } + outstandingTabComplete = null; + } + player.getConnection().write(response); + } + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 8056c3823..8f702ee6b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -40,8 +40,7 @@ public enum StateRegistry { map(0x14, MINECRAFT_1_8, false), map(0x01, MINECRAFT_1_9, false), map(0x02, MINECRAFT_1_12, false), - map(0x01, MINECRAFT_1_12_1, false), - map(0x05, MINECRAFT_1_13, false)); + map(0x01, MINECRAFT_1_12_1, false)); SERVERBOUND.register(Chat.class, Chat::new, map(0x01, MINECRAFT_1_8, false), map(0x02, MINECRAFT_1_9, false), @@ -77,10 +76,9 @@ public enum StateRegistry { map(0x0F, MINECRAFT_1_12, true), map(0x0E, MINECRAFT_1_13, true)); CLIENTBOUND.register(TabCompleteResponse.class, TabCompleteResponse::new, - map(0x3A, MINECRAFT_1_8, true), - map(0x0E, MINECRAFT_1_9, true), - map(0x0E, MINECRAFT_1_12, true), - map(0x10, MINECRAFT_1_13, true)); + map(0x3A, MINECRAFT_1_8, false), + map(0x0E, MINECRAFT_1_9, false), + map(0x0E, MINECRAFT_1_12, false)); CLIENTBOUND.register(PluginMessage.class, PluginMessage::new, map(0x3F, MINECRAFT_1_8, false), map(0x18, MINECRAFT_1_9, false), diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java index 10c14b400..a3636f85c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteRequest.java @@ -9,20 +9,11 @@ import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_1 import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_9; public class TabCompleteRequest implements MinecraftPacket { - private int transactionId; private String command; private boolean assumeCommand; private boolean hasPosition; private long position; - public int getTransactionId() { - return transactionId; - } - - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; - } - public String getCommand() { return command; } @@ -58,8 +49,7 @@ public class TabCompleteRequest implements MinecraftPacket { @Override public String toString() { return "TabCompleteRequest{" + - "transactionId=" + transactionId + - ", command='" + command + '\'' + + "command='" + command + '\'' + ", assumeCommand=" + assumeCommand + ", hasPosition=" + hasPosition + ", position=" + position + @@ -68,35 +58,25 @@ public class TabCompleteRequest implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - this.transactionId = ProtocolUtils.readVarInt(buf); - this.command = ProtocolUtils.readString(buf); - } else { - this.command = ProtocolUtils.readString(buf); - if (protocolVersion >= MINECRAFT_1_9) { - this.assumeCommand = buf.readBoolean(); - } - this.hasPosition = buf.readBoolean(); - if (hasPosition) { - this.position = buf.readLong(); - } + this.command = ProtocolUtils.readString(buf); + if (protocolVersion >= MINECRAFT_1_9) { + this.assumeCommand = buf.readBoolean(); + } + this.hasPosition = buf.readBoolean(); + if (hasPosition) { + this.position = buf.readLong(); } } @Override public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - ProtocolUtils.writeVarInt(buf, transactionId); - ProtocolUtils.writeString(buf, command); - } else { - ProtocolUtils.writeString(buf, command); - if (protocolVersion >= MINECRAFT_1_9) { - buf.writeBoolean(assumeCommand); - } - buf.writeBoolean(hasPosition); - if (hasPosition) { - buf.writeLong(position); - } + ProtocolUtils.writeString(buf, command); + if (protocolVersion >= MINECRAFT_1_9) { + buf.writeBoolean(assumeCommand); + } + buf.writeBoolean(hasPosition); + if (hasPosition) { + buf.writeLong(position); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java index f5464360c..0a528acd8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TabCompleteResponse.java @@ -4,125 +4,37 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; -import net.kyori.text.Component; -import net.kyori.text.serializer.ComponentSerializers; -import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.List; -import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_13; - public class TabCompleteResponse implements MinecraftPacket { - private int transactionId; - private int start; - private int length; - private final List offers = new ArrayList<>(); + private final List offers = new ArrayList<>(); - public int getTransactionId() { - return transactionId; - } - - public void setTransactionId(int transactionId) { - this.transactionId = transactionId; - } - - public int getStart() { - return start; - } - - public void setStart(int start) { - this.start = start; - } - - public int getLength() { - return length; - } - - public void setLength(int length) { - this.length = length; - } - - public List getOffers() { + public List getOffers() { return offers; } @Override public String toString() { return "TabCompleteResponse{" + - "transactionId=" + transactionId + - ", start=" + start + - ", length=" + length + - ", offers=" + offers + + "offers=" + offers + '}'; } @Override public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - this.transactionId = ProtocolUtils.readVarInt(buf); - this.start = ProtocolUtils.readVarInt(buf); - this.length = ProtocolUtils.readVarInt(buf); - int offersAvailable = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < offersAvailable; i++) { - String entry = ProtocolUtils.readString(buf); - Component component = buf.readBoolean() ? ComponentSerializers.JSON.deserialize(ProtocolUtils.readString(buf)) : - null; - offers.add(new Offer(entry, component)); - } - } else { - int offersAvailable = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < offersAvailable; i++) { - offers.add(new Offer(ProtocolUtils.readString(buf), null)); - } + int offersAvailable = ProtocolUtils.readVarInt(buf); + for (int i = 0; i < offersAvailable; i++) { + offers.add(ProtocolUtils.readString(buf)); } } @Override public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - if (protocolVersion >= MINECRAFT_1_13) { - ProtocolUtils.writeVarInt(buf, transactionId); - ProtocolUtils.writeVarInt(buf, start); - ProtocolUtils.writeVarInt(buf, length); - ProtocolUtils.writeVarInt(buf, offers.size()); - for (Offer offer : offers) { - ProtocolUtils.writeString(buf, offer.entry); - buf.writeBoolean(offer.tooltip != null); - if (offer.tooltip != null) { - ProtocolUtils.writeString(buf, ComponentSerializers.JSON.serialize(offer.tooltip)); - } - } - } else { - ProtocolUtils.writeVarInt(buf, offers.size()); - for (Offer offer : offers) { - ProtocolUtils.writeString(buf, offer.entry); - } - } - } - - public static class Offer { - private final String entry; - private final Component tooltip; - - public Offer(String entry, @Nullable Component tooltip) { - this.entry = entry; - this.tooltip = tooltip; - } - - public String getEntry() { - return entry; - } - - public @Nullable Component getTooltip() { - return tooltip; - } - - @Override - public String toString() { - return "Offer{" + - "entry='" + entry + '\'' + - ", tooltip=" + tooltip + - '}'; + ProtocolUtils.writeVarInt(buf, offers.size()); + for (String offer : offers) { + ProtocolUtils.writeString(buf, offer); } } } From 97e4ff91e73ac67a515fa21d548db3b1051072d0 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 20 Sep 2018 19:22:23 +0100 Subject: [PATCH 50/62] Implement optional permission check method for commands This allows plugins to customize which players can use their commands. For players without permission, the command is effectively invisible, and the handling is passed through to the backend server. --- .../velocitypowered/api/command/Command.java | 15 +++++++++++ .../api/permission/PermissionFunction.java | 2 +- .../api/permission/PermissionSubject.java | 12 ++++++++- .../velocitypowered/proxy/VelocityServer.java | 6 +++-- .../proxy/command/ServerCommand.java | 7 ++++++ .../proxy/command/ShutdownCommand.java | 6 +++++ .../proxy/command/VelocityCommand.java | 7 ++++++ .../proxy/command/VelocityCommandManager.java | 25 +++++++++++++------ .../connection/client/ConnectedPlayer.java | 5 ++-- 9 files changed, 71 insertions(+), 14 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/command/Command.java b/api/src/main/java/com/velocitypowered/api/command/Command.java index d1cfd9f49..ecbbcb379 100644 --- a/api/src/main/java/com/velocitypowered/api/command/Command.java +++ b/api/src/main/java/com/velocitypowered/api/command/Command.java @@ -26,4 +26,19 @@ public interface Command { default List suggest(@NonNull CommandSource source, @NonNull String[] currentArgs) { return ImmutableList.of(); } + + /** + * Tests to check if the {@code source} has permission to use this command + * with the provided {@code args}. + * + *

If this method returns false, the handling will be forwarded onto + * the players current server.

+ * + * @param source the source of the command + * @param args the arguments for this command + * @return whether the source has permission + */ + default boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return true; + } } diff --git a/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java b/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java index 102e36eba..f9b70e366 100644 --- a/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java +++ b/api/src/main/java/com/velocitypowered/api/permission/PermissionFunction.java @@ -29,5 +29,5 @@ public interface PermissionFunction { * @param permission the permission * @return the value the permission is set to */ - @NonNull Tristate getPermissionSetting(@NonNull String permission); + @NonNull Tristate getPermissionValue(@NonNull String permission); } diff --git a/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java b/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java index 8377f0729..67bb18a10 100644 --- a/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java +++ b/api/src/main/java/com/velocitypowered/api/permission/PermissionSubject.java @@ -12,5 +12,15 @@ public interface PermissionSubject { * @param permission the permission to check for * @return whether or not the subject has the permission */ - boolean hasPermission(@NonNull String permission); + default boolean hasPermission(@NonNull String permission) { + return getPermissionValue(permission).asBoolean(); + } + + /** + * Gets the subjects setting for a particular permission. + * + * @param permission the permission + * @return the value the permission is set to + */ + @NonNull Tristate getPermissionValue(@NonNull String permission); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 7eaf8eaf3..6b7b56a58 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.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; @@ -39,6 +40,7 @@ import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; import java.net.InetSocketAddress; import java.nio.file.Files; @@ -77,8 +79,8 @@ public class VelocityServer implements ProxyServer { } @Override - public boolean hasPermission(String permission) { - return true; + public @NonNull Tristate getPermissionValue(@NonNull String permission) { + return Tristate.TRUE; } }; private Ratelimiter ipAttemptLimiter; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java index 2926bb038..b466ae5f7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java @@ -3,6 +3,7 @@ package com.velocitypowered.proxy.command; import com.google.common.collect.ImmutableList; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ServerConnection; @@ -12,6 +13,7 @@ import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; import net.kyori.text.format.TextColor; +import org.checkerframework.checker.nullness.qual.NonNull; import java.util.List; import java.util.Optional; @@ -88,4 +90,9 @@ public class ServerCommand implements Command { return ImmutableList.of(); } } + + @Override + public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return source.getPermissionValue("velocity.command.server") != Tristate.FALSE; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java index 7f60c81ff..210a9a026 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java @@ -5,6 +5,7 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.proxy.VelocityServer; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; +import org.checkerframework.checker.nullness.qual.NonNull; public class ShutdownCommand implements Command { private final VelocityServer server; @@ -21,4 +22,9 @@ public class ShutdownCommand implements Command { } server.shutdown(); } + + @Override + public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return source == server.getConsoleCommandSource(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java index f3acb9caf..c8b72dbcd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java @@ -2,10 +2,12 @@ package com.velocitypowered.proxy.command; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.proxy.VelocityServer; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.format.TextColor; +import org.checkerframework.checker.nullness.qual.NonNull; public class VelocityCommand implements Command { @Override @@ -37,4 +39,9 @@ public class VelocityCommand implements Command { source.sendMessage(velocityInfo); source.sendMessage(velocityWebsite); } + + @Override + public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) { + return source.getPermissionValue("velocity.command.info") != Tristate.FALSE; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index a6ec96c69..1b952e1dd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -46,6 +46,10 @@ public class VelocityCommandManager implements CommandManager { } try { + if (!command.hasPermission(source, actualArgs)) { + return false; + } + command.execute(source, actualArgs); return true; } catch (Exception e) { @@ -62,24 +66,29 @@ public class VelocityCommandManager implements CommandManager { return Optional.empty(); } - String command = split[0]; + String alias = split[0]; if (split.length == 1) { - return Optional.of(commands.keySet().stream() - .filter(cmd -> cmd.regionMatches(true, 0, command, 0, command.length())) - .map(cmd -> "/" + cmd) + return Optional.of(commands.entrySet().stream() + .filter(ent -> ent.getKey().regionMatches(true, 0, alias, 0, alias.length())) + .filter(ent -> ent.getValue().hasPermission(source, new String[0])) + .map(ent -> "/" + ent.getKey()) .collect(Collectors.toList())); } String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); - Command executor = commands.get(command); - if (executor == null) { + Command command = commands.get(alias.toLowerCase(Locale.ENGLISH)); + if (command == null) { return Optional.empty(); } try { - return Optional.of(executor.suggest(source, actualArgs)); + if (!command.hasPermission(source, actualArgs)) { + return Optional.empty(); + } + + return Optional.of(command.suggest(source, actualArgs)); } catch (Exception e) { - throw new RuntimeException("Unable to invoke suggestions for command " + command + " for " + source, e); + throw new RuntimeException("Unable to invoke suggestions for command " + alias + " for " + source, e); } } } 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 8917fec7e..c5fb0c776 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 @@ -7,6 +7,7 @@ import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent; import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.api.permission.PermissionFunction; import com.velocitypowered.api.permission.PermissionProvider; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ServerConnection; @@ -335,8 +336,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public boolean hasPermission(String permission) { - return permissionFunction.getPermissionSetting(permission).asBoolean(); + public @NonNull Tristate getPermissionValue(@NonNull String permission) { + return permissionFunction.getPermissionValue(permission); } @Override From b805891d1fa7005bd522d80a941e863fa723e719 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 20 Sep 2018 19:42:27 +0100 Subject: [PATCH 51/62] Implement Player#spoofChatInput method --- .../java/com/velocitypowered/api/proxy/Player.java | 7 +++++++ .../proxy/connection/client/ConnectedPlayer.java | 8 +++++++- .../velocitypowered/proxy/protocol/packet/Chat.java | 12 +++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) 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 0bdc9b607..6d1fa94df 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -82,4 +82,11 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage * @param reason component with the reason */ void disconnect(Component reason); + + /** + * Sends chat input onto the players current server as if they typed it + * into the client chat box. + * @param input the chat input to send + */ + void spoofChatInput(String input); } 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 8917fec7e..77f8722a8 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 @@ -247,7 +247,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { connection.closeWith(Disconnect.create(friendlyReason)); } } else { - connection.write(Chat.create(friendlyReason)); + connection.write(Chat.createClientbound(friendlyReason)); } } @@ -350,6 +350,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return true; } + @Override + public void spoofChatInput(String input) { + Preconditions.checkArgument(input.length() <= Chat.MAX_SERVERBOUND_MESSAGE_LENGTH, "input cannot be greater than " + Chat.MAX_SERVERBOUND_MESSAGE_LENGTH + " characters in length"); + connectedServer.getMinecraftConnection().write(Chat.createServerbound(input)); + } + private class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder { private final RegisteredServer server; 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 f289b7c54..749854506 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 @@ -10,6 +10,8 @@ import net.kyori.text.serializer.ComponentSerializers; public class Chat implements MinecraftPacket { public static final byte CHAT = (byte) 0; + public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256; + private String message; private byte type; @@ -61,12 +63,16 @@ public class Chat implements MinecraftPacket { } } - public static Chat create(Component component) { - return create(component, CHAT); + public static Chat createClientbound(Component component) { + return createClientbound(component, CHAT); } - public static Chat create(Component component, byte type) { + public static Chat createClientbound(Component component, byte type) { Preconditions.checkNotNull(component, "component"); return new Chat(ComponentSerializers.JSON.serialize(component), type); } + + public static Chat createServerbound(String message) { + return new Chat(message, CHAT); + } } From 0e901e2843491899a22c25593731319284d65e89 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 20 Sep 2018 16:58:38 -0400 Subject: [PATCH 52/62] Update README --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 493fdb342..d585b15ab 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,8 @@ [![Build Status](https://img.shields.io/jenkins/s/https/ci.velocitypowered.com/job/velocity/job/master.svg)](https://ci.velocitypowered.com/job/velocity/job/master/) [![Join our Discord](https://img.shields.io/discord/472484458856185878.svg?logo=discord&label=)](https://discord.gg/8cB9Bgf) -Velocity is a next-generation Minecraft: Java Edition proxy suite. It is -designed specifically for enhanced server support and scalability whilst -not compromising flexibility. +A Minecraft server proxy with unparalleled server support, scalability, +and flexibility. Velocity is licensed under the MIT license for ultimate permissiveness and expanding the pool of potential contributors and users. @@ -40,8 +39,7 @@ page. ## Status Velocity is currently in an alpha state: it is prone to change at any time and -is not currently suitable for production usage. For development and testing -purposes, however, Velocity is fully-fledged and ready to go. +is currently only suitable for small servers and development/testing. Velocity supports Minecraft 1.8-1.13.1, and has full support for Paper and Sponge. Forge is fully supported but mod compatibility may vary. \ No newline at end of file From d06028e0f855faf41967891964efa83aa6d68602 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 20 Sep 2018 22:43:58 -0400 Subject: [PATCH 53/62] ProxyServer#broadcast() --- .../java/com/velocitypowered/api/proxy/ProxyServer.java | 7 +++++++ .../java/com/velocitypowered/proxy/VelocityServer.java | 9 +++++++++ 2 files changed, 16 insertions(+) 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 c6c75df8a..11bf095ff 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -8,6 +8,7 @@ import com.velocitypowered.api.proxy.messages.ChannelRegistrar; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.scheduler.Scheduler; +import net.kyori.text.Component; import java.net.InetSocketAddress; import java.util.Collection; @@ -32,6 +33,12 @@ public interface ProxyServer { */ Optional getPlayer(UUID uuid); + /** + * Broadcasts a message to all players currently online. + * @param component the message to send + */ + void broadcast(Component component); + /** * Retrieves all players currently connected to this proxy. This call may or may not be a snapshot of all players * online. diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 7eaf8eaf3..3c66d1c2d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -26,6 +26,7 @@ import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.network.http.NettyHttpClient; import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager; +import com.velocitypowered.proxy.protocol.packet.Chat; import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.scheduler.VelocityScheduler; import com.velocitypowered.proxy.server.ServerMap; @@ -253,6 +254,14 @@ public class VelocityServer implements ProxyServer { return Optional.ofNullable(connectionsByUuid.get(uuid)); } + @Override + public void broadcast(Component component) { + Chat chat = Chat.createClientbound(component); + for (ConnectedPlayer player : connectionsByUuid.values()) { + player.getConnection().write(chat); + } + } + @Override public Collection getAllPlayers() { return ImmutableList.copyOf(connectionsByUuid.values()); From 284f90183f25536c16fb2f2c9d56a71a0c142c22 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 20 Sep 2018 22:44:26 -0400 Subject: [PATCH 54/62] Add missing null check here --- .../src/main/java/com/velocitypowered/proxy/VelocityServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 3c66d1c2d..d4d67a6ad 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -256,6 +256,7 @@ public class VelocityServer implements ProxyServer { @Override public void broadcast(Component component) { + Preconditions.checkNotNull(component, "component"); Chat chat = Chat.createClientbound(component); for (ConnectedPlayer player : connectionsByUuid.values()) { player.getConnection().write(chat); From df06d85d62b4eed185dbb2d3ed61653bd2e95512 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 21 Sep 2018 12:40:34 -0400 Subject: [PATCH 55/62] Deploy separate agents per stage --- Jenkinsfile | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index bbf4ab4ab..14533ac21 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,12 +1,13 @@ pipeline { - agent { - docker { - image 'velocitypowered/openjdk8-plus-git:slim' - args '-v gradle-cache:/root/.gradle:rw -v maven-repo:/maven-repo:rw -v javadoc:/javadoc' - } - } + agent none stages { stage('Build') { + agent { + docker { + image 'velocitypowered/openjdk8-plus-git:slim' + args '-v gradle-cache:/root/.gradle:rw' + } + } steps { sh './gradlew build' archiveArtifacts 'proxy/build/libs/*-all.jar,api/build/libs/*-all.jar' @@ -19,6 +20,12 @@ pipeline { return GIT_BRANCH == 'master' } } + agent { + docker { + image 'velocitypowered/openjdk8-plus-git:slim' + args '-v maven-repo:/maven-repo:rw' + } + } steps { sh 'export MAVEN_DEPLOYMENT=true; ./gradlew publish' } @@ -30,6 +37,12 @@ pipeline { return GIT_BRANCH == 'master' } } + agent { + docker { + image 'velocitypowered/openjdk8-plus-git:slim' + args '-v javadoc:/javadoc:rw' + } + } steps { sh 'rsync -av --delete ./api/build/docs/javadoc/ /javadoc' } From b894dfbdd470ebdfc17a422ee5a6f10dca2113b0 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 21 Sep 2018 12:44:17 -0400 Subject: [PATCH 56/62] Combine the javadoc and publish stages together In other words, the security-sensitive stuff now runs on its own agent. --- Jenkinsfile | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 14533ac21..5c3715bb3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,7 +13,7 @@ pipeline { archiveArtifacts 'proxy/build/libs/*-all.jar,api/build/libs/*-all.jar' } } - stage('Deploy Artifacts') { + stage('Deploy') { when { expression { GIT_BRANCH = sh(returnStdout: true, script: 'git rev-parse --abbrev-ref HEAD').trim() @@ -23,27 +23,11 @@ pipeline { agent { docker { image 'velocitypowered/openjdk8-plus-git:slim' - args '-v maven-repo:/maven-repo:rw' + args '-v gradle-cache:/root/.gradle:rw -v maven-repo:/maven-repo:rw -v javadoc:/javadoc:rw' } } steps { sh 'export MAVEN_DEPLOYMENT=true; ./gradlew publish' - } - } - stage('Deploy Javadoc') { - when { - expression { - GIT_BRANCH = sh(returnStdout: true, script: 'git rev-parse --abbrev-ref HEAD').trim() - return GIT_BRANCH == 'master' - } - } - agent { - docker { - image 'velocitypowered/openjdk8-plus-git:slim' - args '-v javadoc:/javadoc:rw' - } - } - steps { sh 'rsync -av --delete ./api/build/docs/javadoc/ /javadoc' } } From 1e04d27bb701fc7ce30f85cf2871de7fcab91a51 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 21 Sep 2018 12:49:02 -0400 Subject: [PATCH 57/62] Never launch the Gradle daemon Since we are using separate containers for each pipeline stage, the daemons get nicked once the stage is done. Why bother having them? --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5c3715bb3..19ae32e52 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { } } steps { - sh './gradlew build' + sh './gradlew build --no-daemon' archiveArtifacts 'proxy/build/libs/*-all.jar,api/build/libs/*-all.jar' } } @@ -27,7 +27,7 @@ pipeline { } } steps { - sh 'export MAVEN_DEPLOYMENT=true; ./gradlew publish' + sh 'export MAVEN_DEPLOYMENT=true; ./gradlew publish --no-daemon' sh 'rsync -av --delete ./api/build/docs/javadoc/ /javadoc' } } From ee917682e0c443e4e31b5ad19ef4299d5d9125bf Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 21 Sep 2018 15:58:47 -0400 Subject: [PATCH 58/62] Title API (#95) --- .../com/velocitypowered/api/proxy/Player.java | 7 + .../api/util/title/TextTitle.java | 236 ++++++++++++++++++ .../velocitypowered/api/util/title/Title.java | 7 + .../api/util/title/Titles.java | 50 ++++ .../api/util/title/package-info.java | 4 + .../client/ClientPlaySessionHandler.java | 3 + .../connection/client/ConnectedPlayer.java | 65 ++++- .../proxy/protocol/StateRegistry.java | 6 + .../proxy/protocol/packet/TitlePacket.java | 137 ++++++++++ 9 files changed, 510 insertions(+), 5 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/util/title/TextTitle.java create mode 100644 api/src/main/java/com/velocitypowered/api/util/title/Title.java create mode 100644 api/src/main/java/com/velocitypowered/api/util/title/Titles.java create mode 100644 api/src/main/java/com/velocitypowered/api/util/title/package-info.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TitlePacket.java 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 6d1fa94df..50b3ae7e4 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -6,6 +6,7 @@ import com.velocitypowered.api.proxy.messages.ChannelMessageSource; import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.MessagePosition; +import com.velocitypowered.api.util.title.Title; import net.kyori.text.Component; import org.checkerframework.checker.nullness.qual.NonNull; @@ -83,6 +84,12 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage */ void disconnect(Component reason); + /** + * Sends the specified title to the client. + * @param title the title to send + */ + void sendTitle(Title title); + /** * Sends chat input onto the players current server as if they typed it * into the client chat box. 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 new file mode 100644 index 000000000..53a84fe45 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/title/TextTitle.java @@ -0,0 +1,236 @@ +package com.velocitypowered.api.util.title; + +import com.google.common.base.Preconditions; +import net.kyori.text.Component; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Objects; +import java.util.Optional; + +/** + * Represents a "full" title, including all components. This class is immutable. + */ +public class TextTitle implements Title { + private final Component title; + private final Component subtitle; + private final int stay; + private final int fadeIn; + private final int fadeOut; + private final boolean resetBeforeSend; + + private TextTitle(Builder builder) { + this.title = builder.title; + this.subtitle = builder.subtitle; + this.stay = builder.stay; + this.fadeIn = builder.fadeIn; + this.fadeOut = builder.fadeOut; + this.resetBeforeSend = builder.resetBeforeSend; + } + + /** + * Returns the main title this title has, if any. + * @return the main title of this title + */ + public Optional getTitle() { + return Optional.ofNullable(title); + } + + /** + * Returns the subtitle this title has, if any. + * @return the subtitle + */ + public Optional getSubtitle() { + return Optional.ofNullable(subtitle); + } + + /** + * Returns the number of ticks this title will stay up. + * @return how long the title will stay, in ticks + */ + public int getStay() { + return stay; + } + + /** + * Returns the number of ticks over which this title will fade in. + * @return how long the title will fade in, in ticks + */ + public int getFadeIn() { + return fadeIn; + } + + /** + * Returns the number of ticks over which this title will fade out. + * @return how long the title will fade out, in ticks + */ + public int getFadeOut() { + return fadeOut; + } + + /** + * Returns whether or not a reset packet will be sent before this title is sent. By default, unless explicitly + * disabled, this is enabled by default. + * @return whether or not a reset packet will be sent before this title is sent + */ + public boolean isResetBeforeSend() { + return resetBeforeSend; + } + + /** + * Determines whether or not this title has times set on it. If none are set, it will update the previous title + * set on the client. + * @return whether or not this title has times set on it + */ + public boolean areTimesSet() { + return stay != 0 || fadeIn != 0 || fadeOut != 0; + } + + /** + * Creates a new builder from the contents of this title so that it may be changed. + * @return a builder instance with the contents of this title + */ + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TextTitle textTitle = (TextTitle) o; + return stay == textTitle.stay && + fadeIn == textTitle.fadeIn && + fadeOut == textTitle.fadeOut && + resetBeforeSend == textTitle.resetBeforeSend && + Objects.equals(title, textTitle.title) && + Objects.equals(subtitle, textTitle.subtitle); + } + + @Override + public String toString() { + return "TextTitle{" + + "title=" + title + + ", subtitle=" + subtitle + + ", stay=" + stay + + ", fadeIn=" + fadeIn + + ", fadeOut=" + fadeOut + + ", resetBeforeSend=" + resetBeforeSend + + '}'; + } + + @Override + public int hashCode() { + return Objects.hash(title, subtitle, stay, fadeIn, fadeOut, resetBeforeSend); + } + + /** + * Creates a new builder for constructing titles. + * @return a builder for constructing titles + */ + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private @Nullable Component title; + private @Nullable Component subtitle; + private int stay; + private int fadeIn; + private int fadeOut; + private boolean resetBeforeSend = true; + + private Builder() {} + + private Builder(TextTitle copy) { + this.title = copy.title; + this.subtitle = copy.subtitle; + this.stay = copy.stay; + this.fadeIn = copy.fadeIn; + this.fadeOut = copy.fadeOut; + this.resetBeforeSend = copy.resetBeforeSend; + } + + public Builder title(Component title) { + this.title = Preconditions.checkNotNull(title, "title"); + return this; + } + + public Builder clearTitle() { + this.title = null; + return this; + } + + public Builder subtitle(Component subtitle) { + this.subtitle = Preconditions.checkNotNull(subtitle, "subtitle"); + return this; + } + + public Builder clearSubtitle() { + this.subtitle = null; + return this; + } + + public Builder stay(int ticks) { + Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks); + this.stay = ticks; + return this; + } + + public Builder fadeIn(int ticks) { + Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks); + this.fadeIn = ticks; + return this; + } + + public Builder fadeOut(int ticks) { + Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks); + this.fadeOut = ticks; + return this; + } + + public Builder resetBeforeSend(boolean b) { + this.resetBeforeSend = b; + return this; + } + + public Component getTitle() { + return title; + } + + public Component getSubtitle() { + return subtitle; + } + + public int getStay() { + return stay; + } + + public int getFadeIn() { + return fadeIn; + } + + public int getFadeOut() { + return fadeOut; + } + + public boolean isResetBeforeSend() { + return resetBeforeSend; + } + + public TextTitle build() { + return new TextTitle(this); + } + + @Override + public String toString() { + return "Builder{" + + "title=" + title + + ", subtitle=" + subtitle + + ", stay=" + stay + + ", fadeIn=" + fadeIn + + ", fadeOut=" + fadeOut + + ", resetBeforeSend=" + resetBeforeSend + + '}'; + } + } +} 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 new file mode 100644 index 000000000..6849e38a4 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/title/Title.java @@ -0,0 +1,7 @@ +package com.velocitypowered.api.util.title; + +/** + * Represents a title that can be sent to a Minecraft client. + */ +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 new file mode 100644 index 000000000..242a04411 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/title/Titles.java @@ -0,0 +1,50 @@ +package com.velocitypowered.api.util.title; + +/** + * Provides special-purpose titles. + */ +public class Titles { + private Titles() { + throw new AssertionError(); + } + + private static final Title RESET = new Title() { + @Override + public String toString() { + return "reset title"; + } + }; + + private static final Title HIDE = new Title() { + @Override + public String toString() { + return "hide title"; + } + }; + + /** + * Returns a title that, when sent to the client, will cause all title data to be reset and any existing title to be + * hidden. + * @return the reset title + */ + public static Title reset() { + return RESET; + } + + /** + * Returns a title that, when sent to the client, will cause any existing title to be hidden. The title may be + * restored by a {@link TextTitle} with no title or subtitle (only a time). + * @return the hide title + */ + public static Title hide() { + return HIDE; + } + + /** + * Returns a builder for {@link TextTitle}s. + * @return a builder for text titles + */ + public static TextTitle.Builder text() { + return TextTitle.builder(); + } +} 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 new file mode 100644 index 000000000..467f57ab6 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/title/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides data structures for creating and manipulating titles. + */ +package com.velocitypowered.api.util.title; \ No newline at end of file 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 0672839dc..cc50e7877 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 @@ -219,6 +219,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { player.getConnectedServer().getMinecraftConnection().delayedWrite(pm); } + // Clear any title from the previous server. + player.getConnection().delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion())); + // Flush everything player.getConnection().flush(); player.getConnectedServer().getMinecraftConnection().flush(); 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 77f8722a8..9cbe478aa 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 @@ -15,6 +15,9 @@ import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.MessagePosition; +import com.velocitypowered.api.util.title.TextTitle; +import com.velocitypowered.api.util.title.Title; +import com.velocitypowered.api.util.title.Titles; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; @@ -22,6 +25,7 @@ import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; +import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.packet.*; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.util.ThrowableUtils; @@ -139,11 +143,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { 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); + if (getProtocolVersion() >= ProtocolConstants.MINECRAFT_1_11) { + // We can use the title packet instead. + TitlePacket pkt = new TitlePacket(); + pkt.setAction(TitlePacket.SET_ACTION_BAR); + pkt.setComponent(ComponentSerializers.JSON.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", ComponentSerializers.LEGACY.serialize(component)); + json = VelocityServer.GSON.toJson(object); + } } else { json = ComponentSerializers.JSON.serialize(component); } @@ -176,6 +189,48 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { connection.closeWith(Disconnect.create(reason)); } + @Override + public void sendTitle(Title title) { + Preconditions.checkNotNull(title, "title"); + + if (title.equals(Titles.reset())) { + connection.write(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion())); + } else if (title.equals(Titles.hide())) { + connection.write(TitlePacket.hideForProtocolVersion(connection.getProtocolVersion())); + } else if (title instanceof TextTitle) { + TextTitle tt = (TextTitle) title; + + if (tt.isResetBeforeSend()) { + connection.delayedWrite(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion())); + } + + if (tt.getTitle().isPresent()) { + TitlePacket titlePkt = new TitlePacket(); + titlePkt.setAction(TitlePacket.SET_TITLE); + titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getTitle().get())); + connection.delayedWrite(titlePkt); + } + if (tt.getSubtitle().isPresent()) { + TitlePacket titlePkt = new TitlePacket(); + titlePkt.setAction(TitlePacket.SET_SUBTITLE); + titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getSubtitle().get())); + connection.delayedWrite(titlePkt); + } + + if (tt.areTimesSet()) { + TitlePacket timesPkt = TitlePacket.timesForProtocolVersion(connection.getProtocolVersion()); + timesPkt.setFadeIn(tt.getFadeIn()); + timesPkt.setStay(tt.getStay()); + timesPkt.setFadeOut(tt.getFadeOut()); + connection.delayedWrite(timesPkt); + } + connection.flush(); + } else { + throw new IllegalArgumentException("Unknown title class " + title.getClass().getName()); + } + + } + public VelocityServerConnection getConnectedServer() { return connectedServer; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 8f702ee6b..3c92cfe4c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -136,6 +136,12 @@ public enum StateRegistry { map(0x44, MINECRAFT_1_12, true), map(0x45, MINECRAFT_1_12_1, true), map(0x48, MINECRAFT_1_13, true)); + CLIENTBOUND.register(TitlePacket.class, TitlePacket::new, + map(0x45, MINECRAFT_1_8, true), + map(0x45, MINECRAFT_1_9, true), + map(0x47, MINECRAFT_1_12, true), + map(0x48, MINECRAFT_1_12_1, true), + map(0x4B, MINECRAFT_1_13, true)); } }, LOGIN { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TitlePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TitlePacket.java new file mode 100644 index 000000000..de1398060 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/TitlePacket.java @@ -0,0 +1,137 @@ +package com.velocitypowered.proxy.protocol.packet; + +import com.google.common.base.Preconditions; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolConstants; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; + +public class TitlePacket implements MinecraftPacket { + public static final int SET_TITLE = 0; + public static final int SET_SUBTITLE = 1; + public static final int SET_ACTION_BAR = 2; + public static final int SET_TIMES = 3; + public static final int SET_TIMES_OLD = 2; + public static final int HIDE = 4; + public static final int HIDE_OLD = 3; + public static final int RESET = 5; + public static final int RESET_OLD = 4; + + private int action; + private String component; + private int fadeIn; + private int stay; + private int fadeOut; + + @Override + public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + throw new UnsupportedOperationException(); // encode only + } + + @Override + public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + ProtocolUtils.writeVarInt(buf, action); + if (protocolVersion >= ProtocolConstants.MINECRAFT_1_11) { + // 1.11+ shifted the action enum by 1 to handle the action bar + switch (action) { + case SET_TITLE: + case SET_SUBTITLE: + case SET_ACTION_BAR: + ProtocolUtils.writeString(buf, component); + break; + case SET_TIMES: + buf.writeInt(fadeIn); + buf.writeInt(stay); + buf.writeInt(fadeOut); + break; + case HIDE: + case RESET: + break; + } + } else { + switch (action) { + case SET_TITLE: + case SET_SUBTITLE: + ProtocolUtils.writeString(buf, component); + break; + case SET_TIMES_OLD: + buf.writeInt(fadeIn); + buf.writeInt(stay); + buf.writeInt(fadeOut); + break; + case HIDE_OLD: + case RESET_OLD: + break; + } + } + } + + public int getAction() { + return action; + } + + public void setAction(int action) { + this.action = action; + } + + public String getComponent() { + return component; + } + + public void setComponent(String component) { + this.component = component; + } + + public int getFadeIn() { + return fadeIn; + } + + public void setFadeIn(int fadeIn) { + this.fadeIn = fadeIn; + } + + public int getStay() { + return stay; + } + + public void setStay(int stay) { + this.stay = stay; + } + + public int getFadeOut() { + return fadeOut; + } + + public void setFadeOut(int fadeOut) { + this.fadeOut = fadeOut; + } + + public static TitlePacket hideForProtocolVersion(int protocolVersion) { + TitlePacket packet = new TitlePacket(); + packet.setAction(protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? TitlePacket.HIDE : TitlePacket.HIDE_OLD); + return packet; + } + + public static TitlePacket resetForProtocolVersion(int protocolVersion) { + TitlePacket packet = new TitlePacket(); + packet.setAction(protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? TitlePacket.RESET : TitlePacket.RESET_OLD); + return packet; + } + + public static TitlePacket timesForProtocolVersion(int protocolVersion) { + TitlePacket packet = new TitlePacket(); + packet.setAction(protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? TitlePacket.SET_TIMES : TitlePacket.SET_TIMES_OLD); + return packet; + } + + @Override + public String toString() { + return "TitlePacket{" + + "action=" + action + + ", component='" + component + '\'' + + ", fadeIn=" + fadeIn + + ", stay=" + stay + + ", fadeOut=" + fadeOut + + '}'; + } +} From ea42f47c7b5e358e800b6ffbf6a0df19e9a45a6d Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 20 Sep 2018 19:00:42 +0100 Subject: [PATCH 59/62] Properly initialise console permissions using the PermissionsSetupEvent --- .../com/velocitypowered/proxy/Velocity.java | 3 +- .../velocitypowered/proxy/VelocityServer.java | 22 ++++-------- .../proxy/console/VelocityConsole.java | 35 ++++++++++++++++--- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java index c628775c4..f2f05f49f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java @@ -1,6 +1,5 @@ package com.velocitypowered.proxy; -import com.velocitypowered.proxy.console.VelocityConsole; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -28,6 +27,6 @@ public class Velocity { double bootTime = (System.currentTimeMillis() - startTime) / 1000d; logger.info("Done ({}s)!", new DecimalFormat("#.##").format(bootTime)); - new VelocityConsole(server).start(); + server.getConsoleCommandSource().start(); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 2cd0df2d9..4fa54cdfe 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -4,7 +4,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; @@ -22,6 +21,7 @@ import com.velocitypowered.proxy.command.VelocityCommandManager; import com.velocitypowered.proxy.config.AnnotatedConfig; import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.console.VelocityConsole; import com.velocitypowered.proxy.messages.VelocityChannelRegistrar; import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.network.http.NettyHttpClient; @@ -37,7 +37,6 @@ import com.velocitypowered.proxy.util.Ratelimiter; import io.netty.bootstrap.Bootstrap; import net.kyori.text.Component; import net.kyori.text.TextComponent; -import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -73,17 +72,7 @@ public class VelocityServer implements ProxyServer { private final Map connectionsByUuid = new ConcurrentHashMap<>(); private final Map connectionsByName = new ConcurrentHashMap<>(); - private final CommandSource consoleCommandSource = new CommandSource() { - @Override - public void sendMessage(Component component) { - logger.info(ComponentSerializers.LEGACY.serialize(component)); - } - - @Override - public @NonNull Tristate getPermissionValue(@NonNull String permission) { - return Tristate.TRUE; - } - }; + private final VelocityConsole console = new VelocityConsole(this); private Ratelimiter ipAttemptLimiter; private VelocityEventManager eventManager; private VelocityScheduler scheduler; @@ -148,6 +137,9 @@ public class VelocityServer implements ProxyServer { // issues) and there is almost no chance ExecutionException will be thrown. } + // init console permissions after plugins are loaded + console.setupPermissions(); + this.cm.bind(configuration.getBind()); if (configuration.isQueryEnabled()) { @@ -297,8 +289,8 @@ public class VelocityServer implements ProxyServer { } @Override - public CommandSource getConsoleCommandSource() { - return consoleCommandSource; + public VelocityConsole getConsoleCommandSource() { + return console; } @Override 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 4e233ddce..c654a2388 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -1,9 +1,18 @@ package com.velocitypowered.proxy.console; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.permission.PermissionsSetupEvent; +import com.velocitypowered.api.permission.PermissionFunction; +import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.proxy.VelocityServer; +import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; +import net.kyori.text.serializer.ComponentSerializers; import net.minecrell.terminalconsole.SimpleTerminalConsole; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; import org.jline.reader.Candidate; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; @@ -11,20 +20,38 @@ import org.jline.reader.LineReaderBuilder; import java.util.List; import java.util.Optional; -public final class VelocityConsole extends SimpleTerminalConsole { +public final class VelocityConsole extends SimpleTerminalConsole implements CommandSource { + private static final Logger logger = LogManager.getLogger(VelocityConsole.class); private final VelocityServer server; + private PermissionFunction permissionFunction = PermissionFunction.ALWAYS_TRUE; public VelocityConsole(VelocityServer server) { this.server = server; } + @Override + public void sendMessage(Component component) { + logger.info(ComponentSerializers.LEGACY.serialize(component)); + } + + @Override + public @NonNull Tristate getPermissionValue(@NonNull String permission) { + return this.permissionFunction.getPermissionValue(permission); + } + + public void setupPermissions() { + PermissionsSetupEvent event = new PermissionsSetupEvent(this, s -> PermissionFunction.ALWAYS_TRUE); + this.server.getEventManager().fire(event).join(); // this is called on startup, we can safely #join + this.permissionFunction = event.createFunction(this); + } + @Override protected LineReader buildReader(LineReaderBuilder builder) { return super.buildReader(builder .appName("Velocity") .completer((reader, parsedLine, list) -> { - Optional> o = server.getCommandManager().offerSuggestions(server.getConsoleCommandSource(), parsedLine.line()); + Optional> o = this.server.getCommandManager().offerSuggestions(this, parsedLine.line()); o.ifPresent(offers -> { for (String offer : offers) { list.add(new Candidate(offer)); @@ -41,8 +68,8 @@ public final class VelocityConsole extends SimpleTerminalConsole { @Override protected void runCommand(String command) { - if (!this.server.getCommandManager().execute(this.server.getConsoleCommandSource(), command)) { - server.getConsoleCommandSource().sendMessage(TextComponent.of("Command not found.", TextColor.RED)); + if (!this.server.getCommandManager().execute(this, command)) { + sendMessage(TextComponent.of("Command not found.", TextColor.RED)); } } From 9e999e1e5a3f709f57d0a747aef86ae31f8ca212 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 21 Sep 2018 23:18:58 -0400 Subject: [PATCH 60/62] Maintain keep alives on the server connection, not on the client. --- .../backend/BackendPlaySessionHandler.java | 2 +- .../backend/VelocityServerConnection.java | 15 +++++++++++++++ .../client/ClientPlaySessionHandler.java | 18 ++---------------- 3 files changed, 18 insertions(+), 17 deletions(-) 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 88cba258d..35ef63d92 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 @@ -42,7 +42,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { (ClientPlaySessionHandler) connection.getPlayer().getConnection().getSessionHandler(); if (packet instanceof KeepAlive) { // Forward onto the player - playerHandler.setLastPing(((KeepAlive) packet).getRandomId()); + connection.setLastPingId(((KeepAlive) packet).getRandomId()); connection.getPlayer().getConnection().write(packet); } else if (packet instanceof Disconnect) { Disconnect original = (Disconnect) packet; 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 80210e33a..5b0a8cf3d 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 @@ -44,6 +44,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, private boolean legacyForge = false; private boolean hasCompletedJoin = false; private boolean gracefulDisconnect = false; + private long lastPingId; + private long lastPingSent; public VelocityServerConnection(VelocityRegisteredServer registeredServer, ConnectedPlayer proxyPlayer, VelocityServer server) { this.registeredServer = registeredServer; @@ -193,4 +195,17 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, public boolean isGracefulDisconnect() { return gracefulDisconnect; } + + public long getLastPingId() { + return lastPingId; + } + + public long getLastPingSent() { + return lastPingSent; + } + + public void setLastPingId(long lastPingId) { + this.lastPingId = lastPingId; + this.lastPingSent = System.currentTimeMillis(); + } } 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 cc50e7877..81471ffa3 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 @@ -29,8 +29,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private static final int MAX_PLUGIN_CHANNELS = 1024; private final ConnectedPlayer player; - private long lastPingID = -1; - private long lastPingSent = -1; private boolean spawned = false; private final List serverBossBars = new ArrayList<>(); private final Set clientPluginMsgChannels = new HashSet<>(); @@ -64,13 +62,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { if (packet instanceof KeepAlive) { KeepAlive keepAlive = (KeepAlive) packet; - if (keepAlive.getRandomId() != lastPingID) { + if (keepAlive.getRandomId() != serverConnection.getLastPingId()) { // The last keep alive we got was probably from a different server. Let's ignore it, and hope the next // ping is alright. return; } - player.setPing(System.currentTimeMillis() - lastPingSent); - resetPingData(); + player.setPing(System.currentTimeMillis() - serverConnection.getLastPingSent()); serverConnection.getMinecraftConnection().write(packet); return; } @@ -155,7 +152,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } public void handleBackendJoinGame(JoinGame joinGame) { - resetPingData(); // reset ping data if (!spawned) { // Nothing special to do with regards to spawning the player spawned = true; @@ -300,16 +296,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return clientPluginMsgChannels; } - public void setLastPing(long lastPing) { - this.lastPingID = lastPing; - this.lastPingSent = System.currentTimeMillis(); - } - - private void resetPingData() { - this.lastPingID = -1; - this.lastPingSent = -1; - } - public void handleTabCompleteResponse(TabCompleteResponse response) { if (outstandingTabComplete != null) { if (!outstandingTabComplete.isAssumeCommand()) { From 6c61aa49f325e84704cf444693aa8cbe04df0a7d Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 22 Sep 2018 20:38:59 -0400 Subject: [PATCH 61/62] Sanity: reset ping ID after getting the right KeepAlive --- .../proxy/connection/backend/VelocityServerConnection.java | 4 ++++ .../proxy/connection/client/ClientPlaySessionHandler.java | 1 + 2 files changed, 5 insertions(+) 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 5b0a8cf3d..36450f1ea 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 @@ -208,4 +208,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, this.lastPingId = lastPingId; this.lastPingSent = System.currentTimeMillis(); } + + public void resetLastPingId() { + this.lastPingId = -1; + } } 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 81471ffa3..aa27bc1cf 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 @@ -69,6 +69,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } player.setPing(System.currentTimeMillis() - serverConnection.getLastPingSent()); serverConnection.getMinecraftConnection().write(packet); + serverConnection.resetLastPingId(); return; } From c89f3ea4687a2e42460fef866a1d16df0e088602 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 24 Sep 2018 02:04:12 -0400 Subject: [PATCH 62/62] Remove scoreboard packets We don't use these any more, and to be frank, it's kind of scary to update anyway. --- .../proxy/protocol/StateRegistry.java | 24 -- .../protocol/packet/ScoreboardDisplay.java | 47 ---- .../protocol/packet/ScoreboardObjective.java | 93 -------- .../protocol/packet/ScoreboardSetScore.java | 77 ------- .../proxy/protocol/packet/ScoreboardTeam.java | 215 ------------------ .../protocol/util/ScoreboardProtocolUtil.java | 24 -- 6 files changed, 480 deletions(-) delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardDisplay.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardObjective.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardSetScore.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardTeam.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ScoreboardProtocolUtil.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 3c92cfe4c..8e6d1fe78 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -112,30 +112,6 @@ public enum StateRegistry { map(0x49, MINECRAFT_1_12, true), map(0x4A, MINECRAFT_1_12_1, true), map(0x4E, MINECRAFT_1_13, true)); - CLIENTBOUND.register(ScoreboardDisplay.class, ScoreboardDisplay::new, - map(0x3D, MINECRAFT_1_8, true), - map(0x38, MINECRAFT_1_9, true), - map(0x3A, MINECRAFT_1_12, true), - map(0x3B, MINECRAFT_1_12_1, true), - map(0x3E, MINECRAFT_1_13, true)); - CLIENTBOUND.register(ScoreboardObjective.class, ScoreboardObjective::new, - map(0x3B, MINECRAFT_1_8, true), - map(0x3F, MINECRAFT_1_9, true), - map(0x41, MINECRAFT_1_12, true), - map(0x42, MINECRAFT_1_12_1, true), - map(0x45, MINECRAFT_1_13, true)); - CLIENTBOUND.register(ScoreboardTeam.class, ScoreboardTeam::new, - map(0x3E, MINECRAFT_1_8, true), - map(0x41, MINECRAFT_1_9, true), - map(0x43, MINECRAFT_1_12, true), - map(0x44, MINECRAFT_1_12_1, true), - map(0x47, MINECRAFT_1_13, true)); - CLIENTBOUND.register(ScoreboardSetScore.class, ScoreboardSetScore::new, - map(0x3C, MINECRAFT_1_8, true), - map(0x42, MINECRAFT_1_9, true), - map(0x44, MINECRAFT_1_12, true), - map(0x45, MINECRAFT_1_12_1, true), - map(0x48, MINECRAFT_1_13, true)); CLIENTBOUND.register(TitlePacket.class, TitlePacket::new, map(0x45, MINECRAFT_1_8, true), map(0x45, MINECRAFT_1_9, true), diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardDisplay.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardDisplay.java deleted file mode 100644 index 8f22abeb5..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardDisplay.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.velocitypowered.proxy.protocol.packet; - -import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolConstants; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import io.netty.buffer.ByteBuf; - -public class ScoreboardDisplay implements MinecraftPacket { - private byte position; - private String displayName; - - public byte getPosition() { - return position; - } - - public void setPosition(byte position) { - this.position = position; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - @Override - public String toString() { - return "ScoreboardDisplay{" + - "position=" + position + - ", displayName='" + displayName + '\'' + - '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - this.position = buf.readByte(); - this.displayName = ProtocolUtils.readString(buf, 16); - } - - @Override - public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - buf.writeByte(position); - ProtocolUtils.writeString(buf, displayName); - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardObjective.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardObjective.java deleted file mode 100644 index 1234e0831..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardObjective.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.velocitypowered.proxy.protocol.packet; - -import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolConstants; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.util.ScoreboardProtocolUtil; -import io.netty.buffer.ByteBuf; -import net.kyori.text.Component; - -public class ScoreboardObjective implements MinecraftPacket { - public static final byte ADD = (byte) 0; - public static final byte REMOVE = (byte) 1; - public static final byte CHANGE = (byte) 2; - private String id; - private byte mode; - private Component displayName; - private ObjectiveMode type; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public byte getMode() { - return mode; - } - - public void setMode(byte mode) { - this.mode = mode; - } - - public Component getDisplayName() { - return displayName; - } - - public void setDisplayName(Component displayName) { - this.displayName = displayName; - } - - public ObjectiveMode getType() { - return type; - } - - public void setType(ObjectiveMode type) { - this.type = type; - } - - @Override - public String toString() { - return "ScoreboardObjective{" + - "id='" + id + '\'' + - ", mode=" + mode + - ", displayName='" + displayName + '\'' + - ", type='" + type + '\'' + - '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - this.id = ProtocolUtils.readString(buf, 16); - this.mode = buf.readByte(); - if (this.mode != REMOVE) { - this.displayName = ProtocolUtils.readScoreboardTextComponent(buf, protocolVersion); - if (protocolVersion >= ProtocolConstants.MINECRAFT_1_13) { - this.type = ScoreboardProtocolUtil.getMode(ProtocolUtils.readVarInt(buf)); - } else { - this.type = ScoreboardProtocolUtil.getMode(ProtocolUtils.readString(buf)); - } - } - } - - @Override - public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - ProtocolUtils.writeString(buf, id); - buf.writeByte(mode); - if (this.mode != REMOVE) { - ProtocolUtils.writeScoreboardTextComponent(buf, protocolVersion, displayName); - if (protocolVersion >= ProtocolConstants.MINECRAFT_1_13) { - ProtocolUtils.writeVarInt(buf, type.ordinal()); - } else { - ProtocolUtils.writeString(buf, type.name().toLowerCase()); - } - } - } - - public enum ObjectiveMode { - INTEGER, - HEARTS - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardSetScore.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardSetScore.java deleted file mode 100644 index 264d9eec2..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardSetScore.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.velocitypowered.proxy.protocol.packet; - -import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolConstants; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import io.netty.buffer.ByteBuf; - -public class ScoreboardSetScore implements MinecraftPacket { - public static final byte CHANGE = (byte) 0; - public static final byte REMOVE = (byte) 1; - private String entity; - private byte action; - private String objective; - private int value; - - public String getEntity() { - return entity; - } - - public void setEntity(String entity) { - this.entity = entity; - } - - public byte getAction() { - return action; - } - - public void setAction(byte action) { - this.action = action; - } - - public String getObjective() { - return objective; - } - - public void setObjective(String objective) { - this.objective = objective; - } - - public int getValue() { - return value; - } - - public void setValue(int value) { - this.value = value; - } - - @Override - public String toString() { - return "ScoreboardSetScore{" + - "entity='" + entity + '\'' + - ", action=" + action + - ", objective='" + objective + '\'' + - ", value=" + value + - '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - this.entity = ProtocolUtils.readString(buf, 40); - this.action = buf.readByte(); - this.objective = ProtocolUtils.readString(buf, 16); - if (this.action != REMOVE) { - value = ProtocolUtils.readVarInt(buf); - } - } - - @Override - public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - ProtocolUtils.writeString(buf, entity); - buf.writeByte(action); - ProtocolUtils.writeString(buf, objective); - if (this.action != REMOVE) { - ProtocolUtils.writeVarInt(buf, value); - } - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardTeam.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardTeam.java deleted file mode 100644 index e6a3d7421..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ScoreboardTeam.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.velocitypowered.proxy.protocol.packet; - -import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.ProtocolConstants; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import io.netty.buffer.ByteBuf; -import net.kyori.text.Component; - -import java.util.ArrayList; -import java.util.List; - -public class ScoreboardTeam implements MinecraftPacket { - public static final byte ADD = (byte) 0; - public static final byte REMOVE = (byte) 1; - public static final byte UPDATE = (byte) 2; - public static final byte ADD_PLAYER = (byte) 3; - public static final byte REMOVE_PLAYER = (byte) 4; - private String id; - private byte mode; - - private Component displayName; - private Component prefix; - private Component suffix; - private byte flags; - private String nameTagVisibility; - private String collisionRule; - private int color; - private List entities; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public byte getMode() { - return mode; - } - - public void setMode(byte mode) { - this.mode = mode; - } - - public Component getDisplayName() { - return displayName; - } - - public void setDisplayName(Component displayName) { - this.displayName = displayName; - } - - public Component getPrefix() { - return prefix; - } - - public void setPrefix(Component prefix) { - this.prefix = prefix; - } - - public Component getSuffix() { - return suffix; - } - - public void setSuffix(Component suffix) { - this.suffix = suffix; - } - - public byte getFlags() { - return flags; - } - - public void setFlags(byte flags) { - this.flags = flags; - } - - public String getNameTagVisibility() { - return nameTagVisibility; - } - - public void setNameTagVisibility(String nameTagVisibility) { - this.nameTagVisibility = nameTagVisibility; - } - - public String getCollisionRule() { - return collisionRule; - } - - public void setCollisionRule(String collisionRule) { - this.collisionRule = collisionRule; - } - - public int getColor() { - return color; - } - - public void setColor(int color) { - this.color = color; - } - - public List getEntities() { - return entities; - } - - public void setEntities(List entities) { - this.entities = entities; - } - - @Override - public String toString() { - return "ScoreboardTeam{" + - "id='" + id + '\'' + - ", mode=" + mode + - ", displayName='" + displayName + '\'' + - ", prefix='" + prefix + '\'' + - ", suffix='" + suffix + '\'' + - ", flags=" + flags + - ", nameTagVisibility='" + nameTagVisibility + '\'' + - ", collisionRule='" + collisionRule + '\'' + - ", color=" + color + - ", entities=" + entities + - '}'; - } - - @Override - public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - this.id = ProtocolUtils.readString(buf, 16); - this.mode = buf.readByte(); - - switch (mode) { - case ADD: - case UPDATE: - this.displayName = ProtocolUtils.readScoreboardTextComponent(buf, protocolVersion); - if (protocolVersion <= ProtocolConstants.MINECRAFT_1_12_2) { - this.prefix = ProtocolUtils.readScoreboardTextComponent(buf, protocolVersion); - this.suffix = ProtocolUtils.readScoreboardTextComponent(buf, protocolVersion); - } - this.flags = buf.readByte(); - this.nameTagVisibility = ProtocolUtils.readString(buf, 32); - if (protocolVersion >= ProtocolConstants.MINECRAFT_1_9) { - this.collisionRule = ProtocolUtils.readString(buf, 32); - } - this.color = protocolVersion <= ProtocolConstants.MINECRAFT_1_12_2 ? buf.readByte() : - ProtocolUtils.readVarInt(buf); - if (protocolVersion >= ProtocolConstants.MINECRAFT_1_13) { - this.prefix = ProtocolUtils.readScoreboardTextComponent(buf, protocolVersion); - this.suffix = ProtocolUtils.readScoreboardTextComponent(buf, protocolVersion); - } - if (mode == ADD) { - this.entities = readEntities(buf); - } - break; - case REMOVE: // remove - break; - case ADD_PLAYER: // add player - case REMOVE_PLAYER: // remove player - this.entities = readEntities(buf); - break; - } - } - - @Override - public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { - ProtocolUtils.writeString(buf, id); - buf.writeByte(mode); - switch (mode) { - case ADD: - case UPDATE: - ProtocolUtils.writeScoreboardTextComponent(buf, protocolVersion, displayName); - if (protocolVersion <= ProtocolConstants.MINECRAFT_1_12_2) { - ProtocolUtils.writeScoreboardTextComponent(buf, protocolVersion, prefix); - ProtocolUtils.writeScoreboardTextComponent(buf, protocolVersion, suffix); - } - buf.writeByte(flags); - ProtocolUtils.writeString(buf, nameTagVisibility); - if (protocolVersion >= ProtocolConstants.MINECRAFT_1_9) { - ProtocolUtils.writeString(buf, collisionRule); - } - if (protocolVersion >= ProtocolConstants.MINECRAFT_1_13) { - ProtocolUtils.writeVarInt(buf, color); - ProtocolUtils.writeScoreboardTextComponent(buf, protocolVersion, prefix); - ProtocolUtils.writeScoreboardTextComponent(buf, protocolVersion, suffix); - } else { - buf.writeByte(color); - } - if (mode == ADD) { - writeEntities(buf, entities); - } - break; - case REMOVE: - break; - case ADD_PLAYER: - case REMOVE_PLAYER: - writeEntities(buf, entities); - break; - } - } - - private static List readEntities(ByteBuf buf) { - List entities = new ArrayList<>(); - int size = ProtocolUtils.readVarInt(buf); - for (int i = 0; i < size; i++) { - entities.add(ProtocolUtils.readString(buf, 40)); - } - return entities; - } - - private static void writeEntities(ByteBuf buf, List entities) { - ProtocolUtils.writeVarInt(buf, entities.size()); - for (String entity : entities) { - ProtocolUtils.writeString(buf, entity); - } - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ScoreboardProtocolUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ScoreboardProtocolUtil.java deleted file mode 100644 index 0cc15a61b..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/ScoreboardProtocolUtil.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.velocitypowered.proxy.protocol.util; - -import com.velocitypowered.proxy.protocol.packet.ScoreboardObjective; - -public class ScoreboardProtocolUtil { - private ScoreboardProtocolUtil() { - throw new AssertionError(); - } - - public static ScoreboardObjective.ObjectiveMode getMode(String mode) { - return ScoreboardObjective.ObjectiveMode.valueOf(mode.toUpperCase()); - } - - public static ScoreboardObjective.ObjectiveMode getMode(int enumVal) { - switch (enumVal) { - case 0: - return ScoreboardObjective.ObjectiveMode.INTEGER; - case 1: - return ScoreboardObjective.ObjectiveMode.HEARTS; - default: - throw new IllegalStateException("Unknown mode " + enumVal); - } - } -}