diff --git a/api/build.gradle b/api/build.gradle index edf3dc198..2680d18ce 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -26,6 +26,7 @@ dependencies { compile "org.slf4j:slf4j-api:${slf4jVersion}" compile 'com.google.inject:guice:4.2.2' compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}" + compile 'com.mojang:brigadier:1.0.17' compile "org.spongepowered:configurate-hocon:${configurateVersion}" compile "org.spongepowered:configurate-yaml:${configurateVersion}" @@ -63,6 +64,7 @@ artifacts { javadoc { options.encoding = 'UTF-8' options.charSet = 'UTF-8' + options.source = '8' options.links( 'http://www.slf4j.org/apidocs/', 'https://google.github.io/guava/releases/25.1-jre/api/docs/', diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java index 81a7b8297..402167151 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java @@ -1,5 +1,7 @@ package com.velocitypowered.api.command; +import java.util.concurrent.CompletableFuture; + /** * Represents an interface to register a command executor with the proxy. */ @@ -34,11 +36,52 @@ public interface CommandManager { void unregister(String alias); /** - * Attempts to execute a command from the specified {@code cmdLine}. + * Calls CommandExecuteEvent and attempts to execute a command using the specified {@code cmdLine} + * in a blocking fashion. * * @param source the command's source * @param cmdLine the command to run * @return true if the command was found and executed, false if it was not + * + * @deprecated This method will block current thread during event call and command execution. + * Prefer {@link #executeAsync(CommandSource, String)} instead. */ + @Deprecated boolean execute(CommandSource source, String cmdLine); + + /** + * Attempts to execute a command using the specified {@code cmdLine} in a blocking fashion without + * calling CommandExecuteEvent. + * + * @param source the command's source + * @param cmdLine the command to run + * @return true if the command was found and executed, false if it was not + * + * @deprecated This method will block current thread during event and command execution. + * Prefer {@link #executeImmediatelyAsync(CommandSource, String)} instead. + */ + @Deprecated + boolean executeImmediately(CommandSource source, String cmdLine); + + /** + * Calls CommandExecuteEvent and attempts to execute a command from the specified {@code cmdLine} + * async. + * + * @param source the command's source + * @param cmdLine the command to run + * @return A future that will be completed with the result of the command execution. + * Can be completed exceptionally if exception was thrown during execution. + */ + CompletableFuture executeAsync(CommandSource source, String cmdLine); + + /** + * Attempts to execute a command from the specified {@code cmdLine} async + * without calling CommandExecuteEvent. + * + * @param source the command's source + * @param cmdLine the command to run + * @return A future that will be completed with the result of the command execution. + * Can be completed exceptionally if exception was thrown during execution. + */ + CompletableFuture executeImmediatelyAsync(CommandSource source, String cmdLine); } diff --git a/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java b/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java new file mode 100644 index 000000000..d08a323f6 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/command/CommandExecuteEvent.java @@ -0,0 +1,143 @@ +package com.velocitypowered.api.event.command; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.event.ResultedEvent; +import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult; +import java.util.Optional; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * This event is fired when someone executing command. + */ +public final class CommandExecuteEvent implements ResultedEvent { + + private final CommandSource commandSource; + private final String command; + private CommandResult result; + + /** + * Constructs a CommandExecuteEvent. + * @param commandSource the source executing the command + * @param command the command being executed without first slash + */ + public CommandExecuteEvent(CommandSource commandSource, String command) { + this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource"); + this.command = Preconditions.checkNotNull(command, "command"); + this.result = CommandResult.allowed(); + } + + public CommandSource getCommandSource() { + return commandSource; + } + + /** + * Gets the original command being executed without first slash. + * @return the original command being executed + */ + public String getCommand() { + return command; + } + + @Override + public CommandResult getResult() { + return result; + } + + @Override + public void setResult(CommandResult result) { + this.result = Preconditions.checkNotNull(result, "result"); + } + + @Override + public String toString() { + return "CommandExecuteEvent{" + + "commandSource=" + commandSource + + ", command=" + command + + ", result=" + result + + '}'; + } + + /** + * Represents the result of the {@link CommandExecuteEvent}. + */ + public static final class CommandResult implements Result { + + private static final CommandResult ALLOWED = new CommandResult(true, false,null); + private static final CommandResult DENIED = new CommandResult(false, false,null); + private static final CommandResult FORWARD_TO_SERVER = new CommandResult(false, true, null); + + private @Nullable String command; + private final boolean status; + private final boolean forward; + + private CommandResult(boolean status, boolean forward, @Nullable String command) { + this.status = status; + this.forward = forward; + this.command = command; + } + + public Optional getCommand() { + return Optional.ofNullable(command); + } + + public boolean isForwardToServer() { + return forward; + } + + @Override + public boolean isAllowed() { + return status; + } + + @Override + public String toString() { + return status ? "allowed" : "denied"; + } + + /** + * Allows the command to be sent, without modification. + * @return the allowed result + */ + public static CommandResult allowed() { + return ALLOWED; + } + + /** + * Prevents the command from being executed. + * @return the denied result + */ + public static CommandResult denied() { + return DENIED; + } + + /** + * Prevents the command from being executed, but forward command to server. + * @return the forward result + */ + public static CommandResult forwardToServer() { + return FORWARD_TO_SERVER; + } + + /** + * Prevents the command from being executed on proxy, but forward command to server. + * @param newCommand the command without first slash to use instead + * @return a result with a new command being forwarded to server + */ + public static CommandResult forwardToServer(@NonNull String newCommand) { + Preconditions.checkNotNull(newCommand, "newCommand"); + return new CommandResult(false, true, newCommand); + } + + /** + * Allows the command to be executed, but silently replaced old command with another. + * @param newCommand the command to use instead without first slash + * @return a result with a new command + */ + public static CommandResult command(@NonNull String newCommand) { + Preconditions.checkNotNull(newCommand, "newCommand"); + return new CommandResult(true, false, newCommand); + } + } +} diff --git a/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java b/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java new file mode 100644 index 000000000..ef9939fbc --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java @@ -0,0 +1,37 @@ +package com.velocitypowered.api.event.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.Beta; +import com.mojang.brigadier.tree.RootCommandNode; +import com.velocitypowered.api.proxy.Player; + +/** + * Allows plugins to modify the packet indicating commands available on the server to a + * Minecraft 1.13+ client. + */ +@Beta +public class PlayerAvailableCommandsEvent { + + private final Player player; + private final RootCommandNode rootNode; + + /** + * Constructs an available commands event. + * @param player the targeted player + * @param rootNode the Brigadier root node + */ + public PlayerAvailableCommandsEvent(Player player, + RootCommandNode rootNode) { + this.player = checkNotNull(player, "player"); + this.rootNode = checkNotNull(rootNode, "rootNode"); + } + + public Player getPlayer() { + return player; + } + + public RootCommandNode getRootNode() { + return rootNode; + } +} 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 6574f9d7b..4c209056a 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 @@ -60,13 +60,11 @@ public final class GameProfileRequestEvent { } /** - * Sets the game profile to use for this connection. It is invalid to use this method on an - * online-mode connection. + * Sets the game profile to use for this connection. * * @param gameProfile the profile for this connection, {@code null} uses the original profile */ public void setGameProfile(@Nullable GameProfile gameProfile) { - Preconditions.checkState(!onlineMode, "Profiles can not be faked in online mode!"); this.gameProfile = gameProfile; } diff --git a/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java index 6b8b7bba1..ef77c96a4 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java @@ -70,6 +70,10 @@ public final class PlayerChatEvent implements ResultedEvent getMessage() { + return Optional.ofNullable(message); + } + @Override public boolean isAllowed() { return status; @@ -96,10 +100,6 @@ public final class PlayerChatEvent implements ResultedEvent getMessage() { - return Optional.ofNullable(message); - } - /** * Allows the message to be sent, but silently replaced with another. * @param message the message to use instead @@ -110,6 +110,4 @@ public final class PlayerChatEvent implements ResultedEventvelocity.toml. This method does * not return all the servers currently in memory, although in most cases it diff --git a/build.gradle b/build.gradle index 2e57eb060..8cb7655e4 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ allprojects { junitVersion = '5.3.0-M1' slf4jVersion = '1.7.25' log4jVersion = '2.11.2' - nettyVersion = '4.1.43.Final' + nettyVersion = '4.1.50.Final' guavaVersion = '25.1-jre' checkerFrameworkVersion = '2.7.0' configurateVersion = '3.6' diff --git a/proxy/build.gradle b/proxy/build.gradle index 216e58cc8..5498db10b 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -65,7 +65,7 @@ dependencies { compile 'it.unimi.dsi:fastutil:8.2.3' compile 'net.kyori:event-method-asm:3.0.0' - compile 'com.mojang:brigadier:1.0.15' + compile 'com.mojang:brigadier:1.0.17' compile 'org.asynchttpclient:async-http-client:2.10.4' diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java index 2ac4f983c..955640e86 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java @@ -9,7 +9,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 31ef34609..036956882 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -95,7 +95,7 @@ public class VelocityServer implements ProxyServer { private @MonotonicNonNull VelocityConfiguration configuration; private @MonotonicNonNull KeyPair serverKeyPair; private final ServerMap servers; - private final VelocityCommandManager commandManager = new VelocityCommandManager(); + private final VelocityCommandManager commandManager; private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false); private boolean shutdown = false; private final VelocityPluginManager pluginManager; @@ -111,6 +111,7 @@ public class VelocityServer implements ProxyServer { VelocityServer(final ProxyOptions options) { pluginManager = new VelocityPluginManager(this); eventManager = new VelocityEventManager(pluginManager); + commandManager = new VelocityCommandManager(eventManager); scheduler = new VelocityScheduler(pluginManager); console = new VelocityConsole(this); cm = new ConnectionManager(this); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/GlistCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/GlistCommand.java index 2c7ddf9a6..3208dc837 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/GlistCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/GlistCommand.java @@ -7,10 +7,8 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; -import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; import org.checkerframework.checker.nullness.qual.NonNull; 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 b79d955bf..1e21e0ab7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ServerCommand.java @@ -12,8 +12,6 @@ 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 java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -25,6 +23,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; public class ServerCommand implements Command { + public static final int MAX_SERVERS_TO_LIST = 50; private final ProxyServer server; public ServerCommand(ProxyServer server) { @@ -61,10 +60,16 @@ public class ServerCommand implements Command { executor.sendMessage(of("You are currently connected to " + currentServer + ".", TextColor.YELLOW)); + List servers = BuiltinCommandUtil.sortedServerList(server); + if (servers.size() > MAX_SERVERS_TO_LIST) { + executor.sendMessage(of("Too many servers to list. Tab-complete to show all servers.", + TextColor.RED)); + return; + } + // Assemble the list of servers as components TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ") .color(TextColor.YELLOW); - List servers = BuiltinCommandUtil.sortedServerList(server); for (int i = 0; i < servers.size(); i++) { RegisteredServer rs = servers.get(i); serverListBuilder.append(formatServerComponent(currentServer, rs)); 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 87f53b211..568c4fdf4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/ShutdownCommand.java @@ -3,8 +3,6 @@ package com.velocitypowered.proxy.command; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.proxy.VelocityServer; -import net.kyori.text.TextComponent; -import net.kyori.text.format.TextColor; import net.kyori.text.serializer.legacy.LegacyComponentSerializer; import org.checkerframework.checker.nullness.qual.NonNull; 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 1c3b777c3..f27b773e4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java @@ -11,7 +11,6 @@ import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.proxy.VelocityServer; -import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -20,7 +19,6 @@ import java.util.stream.Collectors; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; -import net.kyori.text.event.HoverEvent.Action; import net.kyori.text.format.TextColor; import net.kyori.text.format.TextDecoration; import org.apache.logging.log4j.LogManager; @@ -166,7 +164,7 @@ public class VelocityCommand implements Command { .append(TextComponent.of(version.getVersion()).decoration(TextDecoration.BOLD, false)) .build(); TextComponent copyright = TextComponent - .of("Copyright 2018-2019 " + version.getVendor() + ". " + version.getName() + .of("Copyright 2018-2020 " + version.getVendor() + ". " + version.getName() + " is freely licensed under the terms of the MIT License."); source.sendMessage(velocity); source.sendMessage(copyright); 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 4b85bdef6..9a90c2841 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -7,16 +7,25 @@ import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.RawCommand; +import com.velocitypowered.api.event.command.CommandExecuteEvent; +import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult; +import com.velocitypowered.proxy.plugin.VelocityEventManager; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; public class VelocityCommandManager implements CommandManager { private final Map commands = new HashMap<>(); + private final VelocityEventManager eventManager; + + public VelocityCommandManager(VelocityEventManager eventManager) { + this.eventManager = eventManager; + } @Override @Deprecated @@ -47,9 +56,36 @@ public class VelocityCommandManager implements CommandManager { this.commands.remove(alias.toLowerCase(Locale.ENGLISH)); } + /** + * Calls CommandExecuteEvent. + * @param source the command's source + * @param cmd the command + * @return CompletableFuture of event + */ + public CompletableFuture callCommandEvent(CommandSource source, String cmd) { + Preconditions.checkNotNull(source, "source"); + Preconditions.checkNotNull(cmd, "cmd"); + return eventManager.fire(new CommandExecuteEvent(source, cmd)); + } + @Override public boolean execute(CommandSource source, String cmdLine) { - Preconditions.checkNotNull(source, "invoker"); + Preconditions.checkNotNull(source, "source"); + Preconditions.checkNotNull(cmdLine, "cmdLine"); + + CommandExecuteEvent event = callCommandEvent(source, cmdLine).join(); + CommandResult commandResult = event.getResult(); + if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { + return false; + } + cmdLine = commandResult.getCommand().orElse(event.getCommand()); + + return executeImmediately(source, cmdLine); + } + + @Override + public boolean executeImmediately(CommandSource source, String cmdLine) { + Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(cmdLine, "cmdLine"); String alias = cmdLine; @@ -75,6 +111,40 @@ public class VelocityCommandManager implements CommandManager { } } + + @Override + public CompletableFuture executeAsync(CommandSource source, String cmdLine) { + CompletableFuture result = new CompletableFuture<>(); + callCommandEvent(source, cmdLine).thenAccept(event -> { + CommandResult commandResult = event.getResult(); + if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { + result.complete(false); + } + String command = commandResult.getCommand().orElse(event.getCommand()); + try { + result.complete(executeImmediately(source, command)); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + return result; + } + + @Override + public CompletableFuture executeImmediatelyAsync(CommandSource source, String cmdLine) { + Preconditions.checkNotNull(source, "source"); + Preconditions.checkNotNull(cmdLine, "cmdLine"); + CompletableFuture result = new CompletableFuture<>(); + eventManager.getService().execute(() -> { + try { + result.complete(executeImmediately(source, cmdLine)); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + return result; + } + public boolean hasCommand(String command) { return commands.containsKey(command); } @@ -167,7 +237,14 @@ public class VelocityCommandManager implements CommandManager { if (line.isEmpty()) { return new String[0]; } - return line.trim().split(" ", -1); + + String[] trimmed = line.trim().split(" ", -1); + if (line.endsWith(" ") && !line.trim().isEmpty()) { + // To work around a 1.13+ specific bug we have to inject a space at the end of the arguments + trimmed = Arrays.copyOf(trimmed, trimmed.length + 1); + trimmed[trimmed.length - 1] = ""; + } + return trimmed; } @Override 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 ffe8ea832..7ce85543e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -54,6 +54,14 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @ConfigKey("online-mode") private boolean onlineMode = true; + @Comment({ + "If client's ISP/AS sent from this proxy is different from the one from Mojang's", + "authentication server, the player is kicked. This disallows some VPN and proxy", + "connections but is a weak form of protection." + }) + @ConfigKey("prevent-client-proxy-connections") + private boolean preventClientProxyConnections = false; + @Comment({ "Should we forward IP addresses and other data to backend servers?", "Available options:", @@ -332,6 +340,11 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return onlineMode; } + @Override + public boolean shouldPreventClientProxyConnections() { + return preventClientProxyConnections; + } + public PlayerInfoForwarding getPlayerInfoForwardingMode() { return playerInfoForwardingMode; } @@ -406,6 +419,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return pingPassthrough; } + public boolean isBungeePluginChannelEnabled() { + return advanced.isBungeePluginMessageChannel(); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -658,6 +675,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @ConfigKey("tcp-fast-open") private boolean tcpFastOpen = false; + @Comment("Enables BungeeCord plugin messaging channel support on Velocity.") + @ConfigKey("bungee-plugin-message-channel") + private boolean bungeePluginMessageChannel = true; + private Advanced() { } @@ -670,6 +691,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi this.readTimeout = toml.getLong("read-timeout", 30000L).intValue(); this.proxyProtocol = toml.getBoolean("proxy-protocol", false); this.tcpFastOpen = toml.getBoolean("tcp-fast-open", false); + this.bungeePluginMessageChannel = toml.getBoolean("bungee-plugin-message-channel", true); } } @@ -701,6 +723,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return tcpFastOpen; } + public boolean isBungeePluginMessageChannel() { + return bungeePluginMessageChannel; + } + @Override public String toString() { return "Advanced{" @@ -711,6 +737,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi + ", readTimeout=" + readTimeout + ", proxyProtocol=" + proxyProtocol + ", tcpFastOpen=" + tcpFastOpen + + ", bungeePluginMessageChannel=" + bungeePluginMessageChannel + '}'; } } 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 747006dce..7bde50a23 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 @@ -7,6 +7,7 @@ import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.tree.LiteralCommandNode; +import com.velocitypowered.api.event.command.PlayerAvailableCommandsEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; @@ -175,7 +176,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { .build(); commands.getRootNode().addChild(root); } - return false; + + server.getEventManager().fire( + new PlayerAvailableCommandsEvent(serverConn.getPlayer(), commands.getRootNode())) + .thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop()); + return true; } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java index 678c77e8f..deb5b2304 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java @@ -15,6 +15,7 @@ import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import java.util.Optional; import java.util.StringJoiner; import net.kyori.text.serializer.legacy.LegacyComponentSerializer; @@ -43,8 +44,11 @@ class BungeeCordMessageResponder { String playerName = in.readUTF(); String serverName = in.readUTF(); - proxy.getPlayer(playerName).flatMap(player -> proxy.getServer(serverName)) - .ifPresent(server -> player.createConnectionRequest(server).fireAndForget()); + Optional referencedPlayer = proxy.getPlayer(playerName); + Optional referencedServer = proxy.getServer(serverName); + if (referencedPlayer.isPresent() && referencedServer.isPresent()) { + referencedPlayer.get().createConnectionRequest(referencedServer.get()).fireAndForget(); + } } private void processIp(ByteBufDataInput in) { @@ -269,6 +273,10 @@ class BungeeCordMessageResponder { } boolean process(PluginMessage message) { + if (!proxy.getConfiguration().isBungeePluginChannelEnabled()) { + return false; + } + if (!MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId() .equals(message.getChannel())) { return false; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java index c5eb9d8d6..119e03785 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/TransitionSessionHandler.java @@ -83,6 +83,13 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { .fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer(), existingConnection != null ? existingConnection.getServer() : null)) .whenCompleteAsync((x, error) -> { + // Make sure we can still transition (player might have disconnected here). + if (!serverConn.isActive()) { + // Connection is obsolete. + serverConn.disconnect(); + return; + } + // Strap on the ClientPlaySessionHandler if required. ClientPlaySessionHandler playHandler; if (serverConn.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 5b87b95b9..4d14e94c0 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 @@ -2,15 +2,7 @@ package com.velocitypowered.proxy.connection.backend; import static com.velocitypowered.proxy.VelocityServer.GSON; import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN; -import static com.velocitypowered.proxy.network.Connections.FLOW_HANDLER; -import static com.velocitypowered.proxy.network.Connections.FLUSH_CONSOLIDATION; -import static com.velocitypowered.proxy.network.Connections.FLUSH_CONSOLIDATION_AMOUNT; -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 com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -26,19 +18,13 @@ import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; -import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; -import com.velocitypowered.proxy.protocol.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.packet.Handshake; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.ServerLogin; import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.handler.flow.FlowControlHandler; 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 f68448d8c..3affdf9ea 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 static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.constructChannelsPacket; +import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.PlayerChatEvent; import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; @@ -41,6 +42,7 @@ import java.util.List; import java.util.Optional; import java.util.Queue; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; import org.apache.logging.log4j.LogManager; @@ -123,17 +125,18 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { String msg = packet.getMessage(); if (msg.startsWith("/")) { - try { - if (!server.getCommandManager().execute(player, msg.substring(1))) { - return false; - } - } catch (Exception e) { - logger.info("Exception occurred while running command for {}", player.getUsername(), - e); - player.sendMessage( - TextComponent.of("An error occurred while running this command.", TextColor.RED)); - return true; - } + String originalCommand = msg.substring(1); + server.getCommandManager().callCommandEvent(player, msg.substring(1)) + .thenComposeAsync(event -> processCommandExecuteResult(originalCommand, + event.getResult())) + .exceptionally(e -> { + logger.info("Exception occurred while running command for {}", + player.getUsername(), e); + player.sendMessage( + TextComponent.of("An error occurred while running this command.", + TextColor.RED)); + return null; + }); } else { PlayerChatEvent event = new PlayerChatEvent(player, msg); server.getEventManager().fire(event) @@ -365,12 +368,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private boolean handleCommandTabComplete(TabCompleteRequest packet) { // In 1.13+, we need to do additional work for the richer suggestions available. String command = packet.getCommand().substring(1); - int spacePos = command.indexOf(' '); - if (spacePos == -1) { - spacePos = command.length(); + int commandEndPosition = command.indexOf(' '); + if (commandEndPosition == -1) { + commandEndPosition = command.length(); } - String commandLabel = command.substring(0, spacePos); + String commandLabel = command.substring(0, commandEndPosition); if (!server.getCommandManager().hasCommand(commandLabel)) { if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) { // Outstanding tab completes are recorded for use with 1.12 clients and below to provide @@ -461,6 +464,27 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { }, player.getConnection().eventLoop()); } + private CompletableFuture processCommandExecuteResult(String originalCommand, + CommandResult result) { + if (result == CommandResult.denied()) { + return CompletableFuture.completedFuture(null); + } + + MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected(); + String commandToRun = result.getCommand().orElse(originalCommand); + if (result.isForwardToServer()) { + return CompletableFuture.runAsync(() -> smc.write(Chat.createServerbound("/" + + commandToRun)), smc.eventLoop()); + } else { + return server.getCommandManager().executeImmediatelyAsync(player, commandToRun) + .thenAcceptAsync(hasRun -> { + if (!hasRun) { + smc.write(Chat.createServerbound("/" + commandToRun)); + } + }, smc.eventLoop()); + } + } + /** * Immediately send any queued messages to the server. */ 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 fd6b9d71c..5f550e5a0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -1,5 +1,8 @@ package com.velocitypowered.proxy.connection.client; +import static com.velocitypowered.proxy.connection.util.ConnectionRequestResults.plainResult; +import static java.util.concurrent.CompletableFuture.completedFuture; + import com.google.common.base.Preconditions; import com.google.gson.JsonObject; import com.velocitypowered.api.event.connection.DisconnectEvent; @@ -33,7 +36,6 @@ import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.util.ConnectionMessages; -import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.Chat; @@ -711,8 +713,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } private Optional checkServer(RegisteredServer server) { - Preconditions - .checkState(server instanceof VelocityRegisteredServer, "Not a valid Velocity server."); + Preconditions.checkArgument(server instanceof VelocityRegisteredServer, + "Not a valid Velocity server."); if (connectionInFlight != null || (connectedServer != null && !connectedServer.hasCompletedJoin())) { return Optional.of(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS); @@ -723,49 +725,58 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return Optional.empty(); } + private CompletableFuture> getInitialStatus() { + return CompletableFuture.supplyAsync(() -> checkServer(toConnect), connection.eventLoop()); + } + private CompletableFuture internalConnect() { - Optional initialCheck = checkServer(toConnect); - if (initialCheck.isPresent()) { - return CompletableFuture - .completedFuture(ConnectionRequestResults.plainResult(initialCheck.get(), toConnect)); - } - - // Otherwise, initiate the connection. - ServerPreConnectEvent event = new ServerPreConnectEvent(ConnectedPlayer.this, toConnect); - return server.getEventManager().fire(event) - .thenCompose(newEvent -> { - Optional connectTo = newEvent.getResult().getServer(); - if (!connectTo.isPresent()) { - return CompletableFuture.completedFuture( - ConnectionRequestResults - .plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED, toConnect) - ); + return this.getInitialStatus() + .thenCompose(initialCheck -> { + if (initialCheck.isPresent()) { + return completedFuture(plainResult(initialCheck.get(), toConnect)); } - RegisteredServer rs = connectTo.get(); - Optional lastCheck = checkServer(rs); - if (lastCheck.isPresent()) { - return CompletableFuture - .completedFuture(ConnectionRequestResults.plainResult(lastCheck.get(), rs)); - } + ServerPreConnectEvent event = new ServerPreConnectEvent(ConnectedPlayer.this, + toConnect); + return server.getEventManager().fire(event) + .thenComposeAsync(newEvent -> { + Optional newDest = newEvent.getResult().getServer(); + if (!newDest.isPresent()) { + return completedFuture( + plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED, toConnect) + ); + } - VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs; - VelocityServerConnection con = new VelocityServerConnection(vrs, ConnectedPlayer.this, - server); - connectionInFlight = con; - return con.connect(); + RegisteredServer realDestination = newDest.get(); + Optional check = checkServer(realDestination); + if (check.isPresent()) { + return completedFuture(plainResult(check.get(), realDestination)); + } + + VelocityRegisteredServer vrs = (VelocityRegisteredServer) realDestination; + VelocityServerConnection con = new VelocityServerConnection(vrs, + ConnectedPlayer.this, server); + connectionInFlight = con; + return con.connect().whenCompleteAsync((result, throwable) -> + this.cleanupIfRequired(con), connection.eventLoop()); + }, connection.eventLoop()); }); } + private void cleanupIfRequired(VelocityServerConnection establishedConnection) { + if (establishedConnection == connectionInFlight) { + resetInFlightConnection(); + } + } + @Override public CompletableFuture connect() { return this.internalConnect() .whenCompleteAsync((status, throwable) -> { - if (status != null && !status.isSafe()) { - // If it's not safe to continue the connection we need to shut it down. - handleConnectionException(status.getAttemptedConnection(), throwable, true); - } else if ((status != null && !status.isSuccessful())) { - resetInFlightConnection(); + if (status != null && !status.isSuccessful()) { + if (!status.isSafe()) { + handleConnectionException(status.getAttemptedConnection(), throwable, false); + } } }, connection.eventLoop()) .thenApply(x -> x); 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 6fb58ffea..380fd9831 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 @@ -6,7 +6,6 @@ import static com.velocitypowered.proxy.VelocityServer.GSON; import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa; import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId; -import static org.asynchttpclient.Dsl.asyncHttpClient; import com.google.common.base.Preconditions; import com.velocitypowered.api.event.connection.LoginEvent; @@ -21,7 +20,6 @@ import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; - import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.Disconnect; import com.velocitypowered.proxy.protocol.packet.EncryptionRequest; @@ -50,7 +48,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class); private static final String MOJANG_HASJOINED_URL = - "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; + "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s"; private final VelocityServer server; private final MinecraftConnection mcConnection; @@ -96,8 +94,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler { String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString(); String url = String.format(MOJANG_HASJOINED_URL, - urlFormParameterEscaper().escape(login.getUsername()), serverId, - urlFormParameterEscaper().escape(playerIp)); + urlFormParameterEscaper().escape(login.getUsername()), serverId); + + if (server.getConfiguration().shouldPreventClientProxyConnections()) { + url += "&ip=" + urlFormParameterEscaper().escape(playerIp); + } ListenableFuture hasJoinedResponse = server.getAsyncHttpClient().prepareGet(url) .execute(); 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 5fbbac0de..44ac9c2a5 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 @@ -22,7 +22,6 @@ import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.buffer.ByteBuf; import java.net.InetSocketAddress; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java index 2c3061ad1..b78728ff3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ConnectionRequestResults.java @@ -1,7 +1,6 @@ package com.velocitypowered.proxy.connection.util; import com.velocitypowered.api.proxy.ConnectionRequestBuilder; -import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Result; import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.proxy.protocol.packet.Disconnect; 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 649830544..0f89109df 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -18,7 +18,6 @@ import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.epoll.EpollChannelOption; import io.netty.resolver.dns.DnsAddressResolverGroup; import io.netty.resolver.dns.DnsNameResolverBuilder; -import io.netty.util.concurrent.EventExecutor; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; 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 a0e124003..33112a089 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java @@ -15,9 +15,6 @@ public class Connections { public static final String MINECRAFT_DECODER = "minecraft-decoder"; public static final String MINECRAFT_ENCODER = "minecraft-encoder"; public static final String READ_TIMEOUT = "read-timeout"; - public static final String FLUSH_CONSOLIDATION = "flush-consolidation"; - - public static final int FLUSH_CONSOLIDATION_AMOUNT = 10; private Connections() { throw new AssertionError(); 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 2ef08dade..87b0097b6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityEventManager.java @@ -185,6 +185,10 @@ public class VelocityEventManager implements EventManager { fireEvent(new ProxyShutdownEvent()); } + public ExecutorService getService() { + return service; + } + private static class VelocityMethodScanner implements MethodScanner { @Override 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 b1a0c3aaa..f3931569a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java @@ -16,7 +16,6 @@ import com.velocitypowered.api.plugin.meta.PluginDependency; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer; -import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription; import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader; import com.velocitypowered.proxy.plugin.util.PluginDependencyUtils; import java.io.IOException; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/PluginLoader.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/PluginLoader.java index 3ffdeb4b4..560f177ec 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/PluginLoader.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/loader/PluginLoader.java @@ -3,7 +3,6 @@ package com.velocitypowered.proxy.plugin.loader; import com.google.inject.Module; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginDescription; - import java.nio.file.Path; /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 555885575..b4e56044a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -1,13 +1,16 @@ package com.velocitypowered.proxy.protocol; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; +import static com.velocitypowered.proxy.protocol.util.NettyPreconditions.checkFrame; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.util.GameProfile; +import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; +import com.velocitypowered.proxy.util.except.QuietException; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; +import io.netty.handler.codec.CorruptedFrameException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -16,6 +19,7 @@ import java.util.UUID; public enum ProtocolUtils { ; private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB + private static final QuietException BAD_VARINT_CACHED = new QuietException("Bad varint decoded"); /** * Reads a Minecraft-style VarInt from the specified {@code buf}. @@ -23,13 +27,32 @@ public enum ProtocolUtils { * @return the decoded VarInt */ public static int readVarInt(ByteBuf buf) { + int read = readVarIntSafely(buf); + if (read == Integer.MIN_VALUE) { + throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad varint decoded") + : BAD_VARINT_CACHED; + } + return read; + } + + /** + * Reads a Minecraft-style VarInt from the specified {@code buf}. The difference between this + * method and {@link #readVarInt(ByteBuf)} is that this function returns a sentinel value if the + * varint is invalid. + * @param buf the buffer to read from + * @return the decoded VarInt, or {@code Integer.MIN_VALUE} if the varint is invalid + */ + public static int readVarIntSafely(ByteBuf buf) { int i = 0; int j = 0; while (true) { + if (!buf.isReadable()) { + return Integer.MIN_VALUE; + } int k = buf.readByte(); i |= (k & 0x7F) << j++ * 7; if (j > 5) { - throw new RuntimeException("VarInt too big"); + return Integer.MIN_VALUE; } if ((k & 0x80) != 128) { break; @@ -68,17 +91,21 @@ public enum ProtocolUtils { */ public static String readString(ByteBuf buf, int cap) { int length = readVarInt(buf); - checkArgument(length >= 0, "Got a negative-length string (%s)", length); + return readString(buf, cap, length); + } + + private static String readString(ByteBuf buf, int cap, int length) { + checkFrame(length >= 0, "Got a negative-length string (%s)", length); // `cap` is interpreted as a UTF-8 character length. To cover the full Unicode plane, we must - // consider the length of a UTF-8 character, which can be up to a 4 bytes. We do an initial + // consider the length of a UTF-8 character, which can be up to 4 bytes. We do an initial // sanity check and then check again to make sure our optimistic guess was good. - checkArgument(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap); - checkState(buf.isReadable(length), + checkFrame(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap); + checkFrame(buf.isReadable(length), "Trying to read a string that is too long (wanted %s, only have %s)", length, buf.readableBytes()); String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8); buf.skipBytes(length); - checkState(str.length() <= cap, "Got a too-long string (got %s, max %s)", + checkFrame(str.length() <= cap, "Got a too-long string (got %s, max %s)", str.length(), cap); return str; } @@ -107,9 +134,9 @@ public enum ProtocolUtils { */ public static byte[] readByteArray(ByteBuf buf, int cap) { int length = readVarInt(buf); - checkArgument(length >= 0, "Got a negative-length array (%s)", length); - checkArgument(length <= cap, "Bad array size (got %s, maximum is %s)", length, cap); - checkState(buf.isReadable(length), + checkFrame(length >= 0, "Got a negative-length array (%s)", length); + checkFrame(length <= cap, "Bad array size (got %s, maximum is %s)", length, cap); + checkFrame(buf.isReadable(length), "Trying to read an array that is too long (wanted %s, only have %s)", length, buf.readableBytes()); byte[] array = new byte[length]; @@ -228,7 +255,7 @@ public enum ProtocolUtils { // No vanilla packet should give a 3 byte packet int len = readExtendedForgeShort(buf); - Preconditions.checkArgument(len <= (FORGE_MAX_ARRAY_LENGTH), + checkFrame(len <= (FORGE_MAX_ARRAY_LENGTH), "Cannot receive array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, len); return buf.readRetainedSlice(len); @@ -243,12 +270,11 @@ public enum ProtocolUtils { */ public static void writeByteArray17(byte[] b, ByteBuf buf, boolean allowExtended) { if (allowExtended) { - Preconditions - .checkArgument(b.length <= (FORGE_MAX_ARRAY_LENGTH), - "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, - b.length); + checkFrame(b.length <= (FORGE_MAX_ARRAY_LENGTH), + "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, + b.length); } else { - Preconditions.checkArgument(b.length <= Short.MAX_VALUE, + checkFrame(b.length <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length); } // Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for @@ -268,12 +294,11 @@ public enum ProtocolUtils { */ public static void writeByteBuf17(ByteBuf b, ByteBuf buf, boolean allowExtended) { if (allowExtended) { - Preconditions - .checkArgument(b.readableBytes() <= (FORGE_MAX_ARRAY_LENGTH), - "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, - b.readableBytes()); + checkFrame(b.readableBytes() <= (FORGE_MAX_ARRAY_LENGTH), + "Cannot send array longer than %s (got %s bytes)", FORGE_MAX_ARRAY_LENGTH, + b.readableBytes()); } else { - Preconditions.checkArgument(b.readableBytes() <= Short.MAX_VALUE, + checkFrame(b.readableBytes() <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.readableBytes()); } // Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for @@ -326,21 +351,7 @@ public enum ProtocolUtils { * @return the decoded string */ public static String readStringWithoutLength(ByteBuf buf) { - int length = buf.readableBytes(); - int cap = DEFAULT_MAX_STRING_SIZE; - checkArgument(length >= 0, "Got a negative-length string (%s)", length); - // `cap` is interpreted as a UTF-8 character length. To cover the full Unicode plane, we must - // consider the length of a UTF-8 character, which can be up to a 4 bytes. We do an initial - // sanity check and then check again to make sure our optimistic guess was good. - checkArgument(length <= cap * 4, "Bad string size (got %s, maximum is %s)", length, cap); - checkState(buf.isReadable(length), - "Trying to read a string that is too long (wanted %s, only have %s)", length, - buf.readableBytes()); - String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8); - buf.skipBytes(length); - checkState(str.length() <= cap, "Got a too-long string (got %s, max %s)", - str.length(), cap); - return str; + return readString(buf, DEFAULT_MAX_STRING_SIZE, buf.readableBytes()); } public enum Direction { 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 b3407c980..9be2eae8f 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 @@ -24,8 +24,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import net.kyori.text.serializer.plain.PlainComponentSerializer; 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 0204aefb3..c37fef224 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 @@ -14,7 +14,7 @@ import java.util.List; public class MinecraftDecoder extends MessageToMessageDecoder { - private static final boolean DEBUG = Boolean.getBoolean("velocity.packet-decode-logging"); + public static final boolean DEBUG = Boolean.getBoolean("velocity.packet-decode-logging"); private static final QuietException DECODE_FAILED = new QuietException("A packet did not decode successfully (invalid data). If you are a " + "developer, launch Velocity with -Dvelocity.packet-decode-logging=true to see more."); @@ -30,8 +30,8 @@ public class MinecraftDecoder extends MessageToMessageDecoder { */ public MinecraftDecoder(ProtocolUtils.Direction direction) { this.direction = Preconditions.checkNotNull(direction, "direction"); - this.registry = direction - .getProtocolRegistry(StateRegistry.HANDSHAKE, ProtocolVersion.MINIMUM_VERSION); + this.registry = direction.getProtocolRegistry(StateRegistry.HANDSHAKE, + ProtocolVersion.MINIMUM_VERSION); this.state = StateRegistry.HANDSHAKE; } 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 e382861fe..5db35433c 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 @@ -78,9 +78,9 @@ public class TabCompleteRequest implements MinecraftPacket { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { if (version.compareTo(MINECRAFT_1_13) >= 0) { this.transactionId = ProtocolUtils.readVarInt(buf); - this.command = ProtocolUtils.readString(buf); + this.command = ProtocolUtils.readString(buf, Chat.MAX_SERVERBOUND_MESSAGE_LENGTH); } else { - this.command = ProtocolUtils.readString(buf); + this.command = ProtocolUtils.readString(buf, Chat.MAX_SERVERBOUND_MESSAGE_LENGTH); if (version.compareTo(MINECRAFT_1_9) >= 0) { this.assumeCommand = buf.readBoolean(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java index 9560a8b29..e7847ec8e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java @@ -4,6 +4,7 @@ import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgument import static com.velocitypowered.proxy.protocol.packet.brigadier.DummyVoidArgumentPropertySerializer.DUMMY; import static com.velocitypowered.proxy.protocol.packet.brigadier.FloatArgumentPropertySerializer.FLOAT; import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.INTEGER; +import static com.velocitypowered.proxy.protocol.packet.brigadier.LongArgumentPropertySerializer.LONG; import static com.velocitypowered.proxy.protocol.packet.brigadier.StringArgumentPropertySerializer.STRING; import com.mojang.brigadier.arguments.ArgumentType; @@ -11,6 +12,7 @@ import com.mojang.brigadier.arguments.BoolArgumentType; import com.mojang.brigadier.arguments.DoubleArgumentType; import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.LongArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; @@ -90,6 +92,7 @@ public class ArgumentPropertyRegistry { register("brigadier:double", DoubleArgumentType.class, DOUBLE); register("brigadier:bool", BoolArgumentType.class, VoidArgumentPropertySerializer.create(BoolArgumentType::bool)); + register("brigadier:long", LongArgumentType.class, LONG); // Minecraft argument types with extra properties dummy("minecraft:entity", ByteArgumentPropertySerializer.BYTE); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/DummyProperty.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/DummyProperty.java index 762b52686..40ac9fd5c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/DummyProperty.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/DummyProperty.java @@ -2,7 +2,6 @@ package com.velocitypowered.proxy.protocol.packet.brigadier; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import org.checkerframework.checker.nullness.qual.Nullable; class DummyProperty implements ArgumentType { @@ -17,11 +16,6 @@ class DummyProperty implements ArgumentType { this.result = result; } - @Override - public T parse(StringReader reader) throws CommandSyntaxException { - throw new UnsupportedOperationException(); - } - public String getIdentifier() { return identifier; } @@ -33,4 +27,9 @@ class DummyProperty implements ArgumentType { public @Nullable T getResult() { return result; } + + @Override + public T parse(StringReader reader) { + throw new UnsupportedOperationException(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/LongArgumentPropertySerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/LongArgumentPropertySerializer.java new file mode 100644 index 000000000..9d3b40b6d --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/LongArgumentPropertySerializer.java @@ -0,0 +1,40 @@ +package com.velocitypowered.proxy.protocol.packet.brigadier; + +import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.HAS_MAXIMUM; +import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.HAS_MINIMUM; +import static com.velocitypowered.proxy.protocol.packet.brigadier.IntegerArgumentPropertySerializer.getFlags; + +import com.mojang.brigadier.arguments.LongArgumentType; +import io.netty.buffer.ByteBuf; + +class LongArgumentPropertySerializer implements ArgumentPropertySerializer { + + static final LongArgumentPropertySerializer LONG = new LongArgumentPropertySerializer(); + + private LongArgumentPropertySerializer() { + + } + + @Override + public LongArgumentType deserialize(ByteBuf buf) { + byte flags = buf.readByte(); + long minimum = (flags & HAS_MINIMUM) != 0 ? buf.readLong() : Long.MIN_VALUE; + long maximum = (flags & HAS_MAXIMUM) != 0 ? buf.readLong() : Long.MAX_VALUE; + return LongArgumentType.longArg(minimum, maximum); + } + + @Override + public void serialize(LongArgumentType object, ByteBuf buf) { + boolean hasMinimum = object.getMinimum() != Long.MIN_VALUE; + boolean hasMaximum = object.getMaximum() != Long.MAX_VALUE; + byte flag = getFlags(hasMinimum, hasMaximum); + + buf.writeByte(flag); + if (hasMinimum) { + buf.writeLong(object.getMinimum()); + } + if (hasMaximum) { + buf.writeLong(object.getMaximum()); + } + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java index 5c5cabde1..cd3d46452 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/NettyPreconditions.java @@ -1,12 +1,18 @@ package com.velocitypowered.proxy.protocol.util; import com.google.common.base.Strings; +import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; +import com.velocitypowered.proxy.util.except.QuietException; import io.netty.handler.codec.CorruptedFrameException; /** * Extends {@link com.google.common.base.Preconditions} for Netty's {@link CorruptedFrameException}. */ public class NettyPreconditions { + private static final QuietException BAD = new QuietException( + "Invalid packet received. Launch Velocity with -Dvelocity.packet-decode-logging=true " + + "to see more."); + private NettyPreconditions() { throw new AssertionError(); } @@ -18,7 +24,7 @@ public class NettyPreconditions { */ public static void checkFrame(boolean b, String message) { if (!b) { - throw new CorruptedFrameException(message); + throw MinecraftDecoder.DEBUG ? new CorruptedFrameException(message) : BAD; } } @@ -32,7 +38,11 @@ public class NettyPreconditions { */ public static void checkFrame(boolean b, String message, Object arg1) { if (!b) { - throw new CorruptedFrameException(Strings.lenientFormat(message, arg1)); + if (MinecraftDecoder.DEBUG) { + throw new CorruptedFrameException(Strings.lenientFormat(message, arg1)); + } else { + throw BAD; + } } } @@ -47,7 +57,11 @@ public class NettyPreconditions { */ public static void checkFrame(boolean b, String message, Object arg1, Object arg2) { if (!b) { - throw new CorruptedFrameException(Strings.lenientFormat(message, arg1, arg2)); + if (MinecraftDecoder.DEBUG) { + throw new CorruptedFrameException(Strings.lenientFormat(message, arg1, arg2)); + } else { + throw BAD; + } } } @@ -61,7 +75,11 @@ public class NettyPreconditions { */ public static void checkFrame(boolean b, String message, Object... args) { if (!b) { - throw new CorruptedFrameException(Strings.lenientFormat(message, args)); + if (MinecraftDecoder.DEBUG) { + throw new CorruptedFrameException(Strings.lenientFormat(message, args)); + } else { + throw BAD; + } } } } 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 dc8f1687d..979dca88f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -11,7 +11,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.velocitypowered.api.network.ProtocolVersion; 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; @@ -25,7 +24,6 @@ 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.packet.PluginMessage; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index 09d8c01d5..6830382f6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -7,7 +7,6 @@ import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter; import com.velocitypowered.proxy.protocol.packet.PlayerListItem; -import com.velocitypowered.proxy.protocol.packet.PlayerListItem.Item; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java index 8e5eecf43..e46fa4820 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -12,8 +12,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import net.kyori.text.Component; -import net.kyori.text.serializer.legacy.LegacyComponentSerializer; -import net.kyori.text.serializer.plain.PlainComponentSerializer; import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityTabListLegacy extends VelocityTabList { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java index f955fa33f..bb7c383bf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityChannelRegistrar.java @@ -6,13 +6,11 @@ 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 com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import org.checkerframework.checker.nullness.qual.Nullable; public class VelocityChannelRegistrar implements ChannelRegistrar { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/collect/CappedSet.java b/proxy/src/main/java/com/velocitypowered/proxy/util/collect/CappedSet.java index a0f775f9e..334670bfd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/collect/CappedSet.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/collect/CappedSet.java @@ -2,7 +2,6 @@ package com.velocitypowered.proxy.util.collect; import com.google.common.base.Preconditions; import com.google.common.collect.ForwardingSet; - import java.util.Collection; import java.util.HashSet; import java.util.Set;