From 40c8343494eb48ad8f32ec936d8a08d2850a000b Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 8 Jul 2019 21:28:18 -0400 Subject: [PATCH 01/37] Help smoke out some internal concurrency issues --- .../proxy/connection/MinecraftConnection.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 f4c56ae74..c1f15cc19 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -155,6 +155,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { } } + private void ensureInEventLoop() { + Preconditions.checkState(this.channel.eventLoop().inEventLoop(), "Not in event loop"); + } + public EventLoop eventLoop() { return channel.eventLoop(); } @@ -233,6 +237,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { * @param autoReading whether or not we should read data automatically */ public void setAutoReading(boolean autoReading) { + ensureInEventLoop(); + channel.config().setAutoRead(autoReading); if (autoReading) { // For some reason, the channel may not completely read its queued contents once autoread @@ -249,6 +255,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { * @param state the new state */ public void setState(StateRegistry state) { + ensureInEventLoop(); + this.state = state; this.channel.pipeline().get(MinecraftEncoder.class).setState(state); this.channel.pipeline().get(MinecraftDecoder.class).setState(state); @@ -263,6 +271,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { * @param protocolVersion the protocol version to use */ public void setProtocolVersion(ProtocolVersion protocolVersion) { + ensureInEventLoop(); + this.protocolVersion = protocolVersion; this.nextProtocolVersion = protocolVersion; if (protocolVersion != ProtocolVersion.LEGACY) { @@ -284,6 +294,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { * @param sessionHandler the handler to use */ public void setSessionHandler(MinecraftSessionHandler sessionHandler) { + ensureInEventLoop(); + if (this.sessionHandler != null) { this.sessionHandler.deactivated(); } @@ -302,6 +314,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { */ public void setCompressionThreshold(int threshold) { ensureOpen(); + ensureInEventLoop(); if (threshold == -1) { channel.pipeline().remove(COMPRESSION_DECODER); @@ -325,6 +338,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { */ public void enableEncryption(byte[] secret) throws GeneralSecurityException { ensureOpen(); + ensureInEventLoop(); SecretKey key = new SecretKeySpec(secret, "AES"); @@ -342,6 +356,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { } public void setAssociation(MinecraftConnectionAssociation association) { + ensureInEventLoop(); this.association = association; } From 85e5fb48271ffd323fe1bc22ed5cde11b2fdb004 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 19 Jul 2019 13:25:04 -0400 Subject: [PATCH 02/37] Allow existing players to be kicked from server if in online-mode Fixes #226 --- .../velocitypowered/proxy/VelocityServer.java | 28 +++++++++++++++---- .../proxy/config/VelocityConfiguration.java | 14 +++++++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 095fd5e86..3d2d4a40e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -70,6 +70,7 @@ import java.util.function.IntFunction; import java.util.stream.Collectors; import net.kyori.text.Component; import net.kyori.text.TextComponent; +import net.kyori.text.TranslatableComponent; import net.kyori.text.serializer.gson.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -454,6 +455,9 @@ public class VelocityServer implements ProxyServer { * @return {@code true} if we can register the connection, {@code false} if not */ public boolean canRegisterConnection(ConnectedPlayer connection) { + if (configuration.isOnlineMode() && configuration.isOnlineModeKickExistingPlayers()) { + return true; + } String lowerName = connection.getUsername().toLowerCase(Locale.US); return !(connectionsByName.containsKey(lowerName) || connectionsByUuid.containsKey(connection.getUniqueId())); @@ -466,12 +470,24 @@ public class VelocityServer implements ProxyServer { */ public boolean registerConnection(ConnectedPlayer connection) { String lowerName = connection.getUsername().toLowerCase(Locale.US); - if (connectionsByName.putIfAbsent(lowerName, connection) != null) { - return false; - } - if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) { - connectionsByName.remove(lowerName, connection); - return false; + + if (!this.configuration.isOnlineModeKickExistingPlayers()) { + if (connectionsByName.putIfAbsent(lowerName, connection) != null) { + return false; + } + if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) { + connectionsByName.remove(lowerName, connection); + return false; + } + } else { + ConnectedPlayer existing = connectionsByUuid.get(connection.getUniqueId()); + if (existing != null) { + existing.disconnect(TranslatableComponent.of("multiplayer.disconnect.duplicate_login")); + } + + // We can now replace the entries as needed. + connectionsByName.put(lowerName, connection); + connectionsByUuid.put(connection.getUniqueId(), connection); } return true; } 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 6fd13239e..48449e079 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -77,6 +77,11 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @ConfigKey("announce-forge") private boolean announceForge = false; + @Comment({"If enabled (default is false) and the proxy is in online mode, Velocity will kick", + "any existing player who is online if a duplicate connection attempt is made."}) + @ConfigKey("kick-existing-players") + private boolean onlineModeKickExistingPlayers = false; + @Table("[servers]") private final Servers servers; @@ -109,7 +114,8 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode, boolean announceForge, PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret, - Servers servers, ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics) { + boolean onlineModeKickExistingPlayers, Servers servers, ForcedHosts forcedHosts, + Advanced advanced, Query query, Metrics metrics) { this.bind = bind; this.motd = motd; this.showMaxPlayers = showMaxPlayers; @@ -117,6 +123,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi this.announceForge = announceForge; this.playerInfoForwardingMode = playerInfoForwardingMode; this.forwardingSecret = forwardingSecret; + this.onlineModeKickExistingPlayers = onlineModeKickExistingPlayers; this.servers = servers; this.forcedHosts = forcedHosts; this.advanced = advanced; @@ -425,6 +432,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi toml.getBoolean("announce-forge", false), PlayerInfoForwarding.valueOf(forwardingModeName), forwardingSecret, + toml.getBoolean("kick-existing-players", false), servers, forcedHosts, advanced, @@ -443,6 +451,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return builder.toString(); } + public boolean isOnlineModeKickExistingPlayers() { + return onlineModeKickExistingPlayers; + } + private static class Servers { @IsMap From 6d5bacb26280ac0811359807e2dee5207edd9869 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 20 Jul 2019 00:30:44 -0400 Subject: [PATCH 03/37] Minor Netty cleanup --- .../com/velocitypowered/proxy/VelocityServer.java | 8 ++------ .../backend/VelocityServerConnection.java | 3 +-- .../proxy/network/ConnectionManager.java | 13 +++++-------- .../proxy/network/http/NettyHttpClient.java | 14 +++++++------- .../proxy/server/VelocityRegisteredServer.java | 3 +-- 5 files changed, 16 insertions(+), 25 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 3d2d4a40e..6e590b3c4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -265,15 +265,11 @@ public class VelocityServer implements ProxyServer { logger.info("Loaded {} plugins", pluginManager.getPlugins().size()); } - public EventLoopGroup getWorkerGroup() { - return this.cm.getWorkerGroup(); - } - - public Bootstrap initializeGenericBootstrap() { + public Bootstrap createBootstrap() { return this.cm.createWorker(); } - public Bootstrap initializeGenericBootstrap(EventLoopGroup group) { + public Bootstrap createBootstrap(EventLoopGroup group) { return this.cm.createWorker(group); } 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 bb4142904..39e0bbd0c 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 @@ -12,7 +12,6 @@ import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; -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.ServerInfo; @@ -77,7 +76,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, CompletableFuture result = new CompletableFuture<>(); // Note: we use the event loop for the connection the player is on. This reduces context // switches. - server.initializeGenericBootstrap(proxyPlayer.getMinecraftConnection().eventLoop()) + server.createBootstrap(proxyPlayer.getMinecraftConnection().eventLoop()) .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 97b0341c6..c3b1d195f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; public final class ConnectionManager { @@ -113,23 +114,23 @@ public final class ConnectionManager { } public Bootstrap createWorker() { - return this.createWorker(this.workerGroup); + return this.createWorker(null); } /** * Creates a TCP {@link Bootstrap} using Velocity's event loops. * - * @param group the event loop group to use + * @param group the event loop group to use. Use {@code null} for the default worker group. * * @return a new {@link Bootstrap} */ - public Bootstrap createWorker(EventLoopGroup group) { + public Bootstrap createWorker(@Nullable EventLoopGroup group) { return new Bootstrap() .channel(this.transportType.socketChannelClass) - .group(group) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.server.getConfiguration().getConnectTimeout()) + .group(group == null ? this.workerGroup : group) .resolver(this.resolverGroup); } @@ -164,10 +165,6 @@ public final class ConnectionManager { return bossGroup; } - public EventLoopGroup getWorkerGroup() { - return workerGroup; - } - public ServerChannelInitializerHolder getServerChannelInitializer() { return this.serverChannelInitializer; } 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 ec2436b72..5d0211a09 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 @@ -9,7 +9,6 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoop; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContentCompressor; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; @@ -22,6 +21,7 @@ import java.net.URL; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import javax.net.ssl.SSLEngine; +import org.checkerframework.checker.nullness.qual.Nullable; public class NettyHttpClient { @@ -38,7 +38,7 @@ public class NettyHttpClient { this.server = server; } - private ChannelFuture establishConnection(URL url, EventLoop loop) { + private ChannelFuture establishConnection(URL url, @Nullable EventLoop loop) { String host = url.getHost(); int port = url.getPort(); boolean ssl = url.getProtocol().equals("https"); @@ -47,7 +47,7 @@ public class NettyHttpClient { } InetSocketAddress address = InetSocketAddress.createUnresolved(host, port); - return server.initializeGenericBootstrap(loop) + return server.createBootstrap(loop) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { @@ -74,7 +74,7 @@ public class NettyHttpClient { * @param loop the event loop to use * @return a future representing the response */ - public CompletableFuture get(URL url, EventLoop loop) { + public CompletableFuture get(URL url, @Nullable EventLoop loop) { CompletableFuture reply = new CompletableFuture<>(); establishConnection(url, loop) .addListener((ChannelFutureListener) future -> { @@ -109,18 +109,18 @@ public class NettyHttpClient { */ public CompletableFuture post(URL url, ByteBuf body, Consumer decorator) { - return post(url, server.getWorkerGroup().next(), body, decorator); + return post(url, null, body, decorator); } /** * Attempts an HTTP POST request to the specified URL. * @param url the URL to fetch * @param loop the event loop to use - * @param body the body to post + * @param body the body to post - the HTTP client takes ownership of the buffer * @param decorator a consumer that can modify the request as required * @return a future representing the response */ - public CompletableFuture post(URL url, EventLoop loop, ByteBuf body, + public CompletableFuture post(URL url, @Nullable EventLoop loop, ByteBuf body, Consumer decorator) { CompletableFuture reply = new CompletableFuture<>(); establishConnection(url, loop) 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 c0109467e..6261d5614 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -19,7 +19,6 @@ import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; 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; @@ -63,7 +62,7 @@ public class VelocityRegisteredServer implements RegisteredServer { throw new IllegalStateException("No Velocity proxy instance available"); } CompletableFuture pingFuture = new CompletableFuture<>(); - server.initializeGenericBootstrap() + server.createBootstrap() .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { From 2b5b14e4ecdf7b4222ccf687718387a6546e6f16 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 20 Jul 2019 14:25:47 -0400 Subject: [PATCH 04/37] Switch to new TCA version and jansi --- proxy/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/build.gradle b/proxy/build.gradle index 54ddc7b86..02aa8fa9c 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -57,11 +57,11 @@ dependencies { compile "org.apache.logging.log4j:log4j-iostreams:${log4jVersion}" compile 'net.sf.jopt-simple:jopt-simple:5.0.4' // command-line options - compile 'net.minecrell:terminalconsoleappender:1.1.1' - runtime 'net.java.dev.jna:jna:4.5.2' // Needed for JLine + compile 'net.minecrell:terminalconsoleappender:1.2.0-SNAPSHOT' + runtime 'org.jline:jline-terminal-jansi:3.12.1' // Needed for JLine runtime 'com.lmax:disruptor:3.4.2' // Async loggers - compile 'it.unimi.dsi:fastutil:8.2.2' + compile 'it.unimi.dsi:fastutil:8.2.3' compile 'net.kyori:event-method-asm:3.0.0' compile 'com.mojang:brigadier:1.0.15' From fde5ec36e752994233fa7b9237af933c2a981606 Mon Sep 17 00:00:00 2001 From: Minecrell Date: Sun, 21 Jul 2019 16:51:08 +0200 Subject: [PATCH 05/37] Update TerminalConsoleAppender to 1.2.0 Add log4j.skipJansi to prevent Log4j from initializing a Jansi terminal. It is redundant because this is handled by TCA and it will be only active for a few seconds until TCA is loaded. --- proxy/build.gradle | 2 +- proxy/src/main/resources/log4j2.component.properties | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/build.gradle b/proxy/build.gradle index 02aa8fa9c..675dd0363 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -57,7 +57,7 @@ dependencies { compile "org.apache.logging.log4j:log4j-iostreams:${log4jVersion}" compile 'net.sf.jopt-simple:jopt-simple:5.0.4' // command-line options - compile 'net.minecrell:terminalconsoleappender:1.2.0-SNAPSHOT' + compile 'net.minecrell:terminalconsoleappender:1.2.0' runtime 'org.jline:jline-terminal-jansi:3.12.1' // Needed for JLine runtime 'com.lmax:disruptor:3.4.2' // Async loggers diff --git a/proxy/src/main/resources/log4j2.component.properties b/proxy/src/main/resources/log4j2.component.properties index 6ed08f31f..e43f5dacc 100644 --- a/proxy/src/main/resources/log4j2.component.properties +++ b/proxy/src/main/resources/log4j2.component.properties @@ -1 +1,2 @@ log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector +log4j.skipJansi=true From d1736bf94cd6dddd42954276a2fc4f777c073f81 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 26 Jul 2019 01:29:43 -0400 Subject: [PATCH 06/37] Move from custom HTTP client to Async HTTP Client The most significant advantage is that Velocity gets a well-tested HTTP client implementation which also includes a connection pool, allowing us to avoid the overhead of TCP and TLS handshakes upon each login. Unfortunately, Async HTTP Client does not work with the kqueue transport. Since almost nobody runs a production Velocity server on macOS, we have decided to remove kqueue support. The benefits that Async HTTP Client provides outweigh the disadvantages of not having a macOS native transport. macOS is adequately supported by the normal NIO transport. --- proxy/build.gradle | 3 +- .../com/velocitypowered/proxy/Metrics.java | 54 ++++--- .../velocitypowered/proxy/VelocityServer.java | 8 +- .../client/LoginSessionHandler.java | 83 ++++++---- .../proxy/network/ConnectionManager.java | 14 ++ .../proxy/network/TransportType.java | 12 +- .../proxy/network/http/NettyHttpClient.java | 153 ------------------ .../network/http/SimpleHttpResponse.java | 28 ---- .../http/SimpleHttpResponseCollector.java | 51 ------ 9 files changed, 97 insertions(+), 309 deletions(-) delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/network/http/NettyHttpClient.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponse.java delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponseCollector.java diff --git a/proxy/build.gradle b/proxy/build.gradle index 675dd0363..9c3f151b1 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -48,7 +48,6 @@ dependencies { compile "io.netty:netty-handler:${nettyVersion}" compile "io.netty:netty-transport-native-epoll:${nettyVersion}" compile "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-x86_64" - compile "io.netty:netty-transport-native-kqueue:${nettyVersion}:osx-x86_64" compile "io.netty:netty-resolver-dns:${nettyVersion}" compile "org.apache.logging.log4j:log4j-api:${log4jVersion}" @@ -65,6 +64,8 @@ dependencies { compile 'net.kyori:event-method-asm:3.0.0' compile 'com.mojang:brigadier:1.0.15' + + compile 'org.asynchttpclient:async-http-client:2.10.1' testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}" testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java index 6f2dfa535..402746171 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java @@ -3,12 +3,9 @@ package com.velocitypowered.proxy; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.velocitypowered.proxy.config.VelocityConfiguration; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufOutputStream; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.HttpHeaderNames; import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; @@ -21,11 +18,14 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPOutputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.asynchttpclient.ListenableFuture; +import org.asynchttpclient.Response; /** * bStats collects some data for plugin authors. @@ -185,40 +185,44 @@ public class Metrics { } // Compress the data to save bandwidth - ByteBuf reqBody = createResponseBody(data); - - server.getHttpClient().post(new URL(URL), reqBody, request -> { - request.headers().add(HttpHeaderNames.CONTENT_ENCODING, "gzip"); - request.headers().add(HttpHeaderNames.ACCEPT, "application/json"); - request.headers().add(HttpHeaderNames.CONTENT_TYPE, "application/json"); - }) - .whenCompleteAsync((resp, exc) -> { - if (logFailedRequests) { - if (exc != null) { - logger.error("Unable to send metrics to bStats", exc); - } else if (resp.getCode() != 429) { - logger.error("Got HTTP status code {} when sending metrics to bStats", - resp.getCode()); - } + ListenableFuture future = server.getAsyncHttpClient() + .preparePost(URL) + .addHeader(HttpHeaderNames.CONTENT_ENCODING, "gzip") + .addHeader(HttpHeaderNames.ACCEPT, "application/json") + .addHeader(HttpHeaderNames.CONTENT_TYPE, "application/json") + .setBody(createResponseBody(data)) + .execute(); + future.addListener(() -> { + if (logFailedRequests) { + try { + Response r = future.get(); + if (r.getStatusCode() != 429) { + logger.error("Got HTTP status code {} when sending metrics to bStats", + r.getStatusCode()); } - }); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + logger.error("Unable to send metrics to bStats", e); + } + } + }, null); } - private static ByteBuf createResponseBody(JsonObject object) throws IOException { - ByteBuf buf = Unpooled.buffer(); + private static byte[] createResponseBody(JsonObject object) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); try (Writer writer = new BufferedWriter( new OutputStreamWriter( - new GZIPOutputStream(new ByteBufOutputStream(buf)), StandardCharsets.UTF_8 + new GZIPOutputStream(os), StandardCharsets.UTF_8 ) ) ) { VelocityServer.GSON.toJson(object, writer); } catch (IOException e) { - buf.release(); throw e; } - return buf; + return os.toByteArray(); } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 6e590b3c4..c078ecfb9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -30,7 +30,6 @@ import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.console.VelocityConsole; 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; @@ -74,6 +73,7 @@ import net.kyori.text.TranslatableComponent; import net.kyori.text.serializer.gson.GsonComponentSerializer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.asynchttpclient.AsyncHttpClient; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; @@ -90,7 +90,6 @@ public class VelocityServer implements ProxyServer { private final ConnectionManager cm; private final ProxyOptions options; private @MonotonicNonNull VelocityConfiguration configuration; - private @MonotonicNonNull NettyHttpClient httpClient; private @MonotonicNonNull KeyPair serverKeyPair; private final ServerMap servers; private final VelocityCommandManager commandManager = new VelocityCommandManager(); @@ -202,7 +201,6 @@ public class VelocityServer implements ProxyServer { } ipAttemptLimiter = Ratelimiters.createWithMilliseconds(configuration.getLoginRatelimit()); - httpClient = new NettyHttpClient(this); loadPlugins(); // Go ahead and fire the proxy initialization event. We block since plugins should have a chance @@ -430,8 +428,8 @@ public class VelocityServer implements ProxyServer { thread.start(); } - public NettyHttpClient getHttpClient() { - return ensureInitialized(httpClient); + public AsyncHttpClient getAsyncHttpClient() { + return ensureInitialized(cm).getHttpClient(); } public Ratelimiter getIpAttemptLimiter() { 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 d0707588c..f30b3d218 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 @@ -7,6 +7,8 @@ import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ import static com.velocitypowered.proxy.connection.VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL; import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa; import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId; +import static org.asynchttpclient.Dsl.asyncHttpClient; +import static org.asynchttpclient.Dsl.config; import com.google.common.base.Preconditions; import com.google.common.net.UrlEscapers; @@ -42,14 +44,19 @@ import java.security.KeyPair; import java.util.Arrays; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import net.kyori.text.Component; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.asynchttpclient.Dsl; +import org.asynchttpclient.ListenableFuture; +import org.asynchttpclient.Response; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 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"; @@ -61,6 +68,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { private byte[] verify = EMPTY_BYTE_ARRAY; private int playerInfoId; private @MonotonicNonNull ConnectedPlayer connectedPlayer; + private final long loginProcessBegan = System.currentTimeMillis(); LoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection, InitialInboundConnection inbound) { @@ -123,46 +131,50 @@ public class LoginSessionHandler implements MinecraftSessionHandler { String url = String.format(MOJANG_HASJOINED_URL, urlFormParameterEscaper().escape(login.getUsername()), serverId, urlFormParameterEscaper().escape(playerIp)); - server.getHttpClient() - .get(new URL(url), mcConnection.eventLoop()) - .thenAcceptAsync(profileResponse -> { - if (mcConnection.isClosed()) { - // The player disconnected after we authenticated them. - return; - } - // Go ahead and enable encryption. Once the client sends EncryptionResponse, encryption - // is enabled. - try { - mcConnection.enableEncryption(decryptedSharedSecret); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } + ListenableFuture hasJoinedResponse = server.getAsyncHttpClient().prepareGet(url) + .execute(); + hasJoinedResponse.addListener(() -> { + if (mcConnection.isClosed()) { + // The player disconnected after we authenticated them. + return; + } - if (profileResponse.getCode() == 200) { - // All went well, initialize the session. - initializePlayer(GSON.fromJson(profileResponse.getBody(), GameProfile.class), true); - } else if (profileResponse.getCode() == 204) { - // Apparently an offline-mode user logged onto this online-mode proxy. - inbound.disconnect(VelocityMessages.ONLINE_MODE_ONLY); - } else { - // Something else went wrong - logger.error( - "Got an unexpected error code {} whilst contacting Mojang to log in {} ({})", - profileResponse.getCode(), login.getUsername(), playerIp); - mcConnection.close(); - } - }, mcConnection.eventLoop()) - .exceptionally(exception -> { - logger.error("Unable to enable encryption", exception); + // Go ahead and enable encryption. Once the client sends EncryptionResponse, encryption + // is enabled. + try { + mcConnection.enableEncryption(decryptedSharedSecret); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + + try { + Response profileResponse = hasJoinedResponse.get(); + if (profileResponse.getStatusCode() == 200) { + // All went well, initialize the session. + initializePlayer(GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class), + true); + } else if (profileResponse.getStatusCode() == 204) { + // Apparently an offline-mode user logged onto this online-mode proxy. + inbound.disconnect(VelocityMessages.ONLINE_MODE_ONLY); + } else { + // Something else went wrong + logger.error( + "Got an unexpected error code {} whilst contacting Mojang to log in {} ({})", + profileResponse.getStatusCode(), login.getUsername(), playerIp); mcConnection.close(); - return null; - }); + } + } catch (ExecutionException e) { + logger.error("Unable to authenticate with Mojang", e); + mcConnection.close(); + } catch (InterruptedException e) { + // not much we can do usefully + Thread.currentThread().interrupt(); + } + }, mcConnection.eventLoop()); } catch (GeneralSecurityException e) { logger.error("Unable to enable encryption", e); mcConnection.close(); - } catch (MalformedURLException e) { - throw new AssertionError(e); } return true; } @@ -179,6 +191,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { // The player was disconnected return; } + PreLoginComponentResult result = event.getResult(); Optional disconnectReason = result.getReason(); if (disconnectReason.isPresent()) { @@ -277,7 +290,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { player.disconnect(VelocityMessages.ALREADY_CONNECTED); return; } - + mcConnection.setSessionHandler(new InitialConnectSessionHandler(player)); server.getEventManager().fire(new PostLoginEvent(player)) .thenRun(() -> player.createConnectionRequest(toTry.get()).fireAndForget()); 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 c3b1d195f..9fa8b6b0c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -1,5 +1,8 @@ package com.velocitypowered.proxy.network; +import static org.asynchttpclient.Dsl.asyncHttpClient; +import static org.asynchttpclient.Dsl.config; + import com.google.common.base.Preconditions; import com.velocitypowered.natives.util.Natives; import com.velocitypowered.proxy.VelocityServer; @@ -18,6 +21,8 @@ import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.Dsl; import org.checkerframework.checker.nullness.qual.Nullable; public final class ConnectionManager { @@ -36,6 +41,7 @@ public final class ConnectionManager { public final ServerChannelInitializerHolder serverChannelInitializer; private final DnsAddressResolverGroup resolverGroup; + private final AsyncHttpClient httpClient; /** * Initalizes the {@code ConnectionManager}. @@ -55,6 +61,10 @@ public final class ConnectionManager { .negativeTtl(15) .ndots(1) ); + this.httpClient = asyncHttpClient(config() + .setEventLoopGroup(this.workerGroup) + .setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion()) + .build()); } public void logChannelInformation() { @@ -168,4 +178,8 @@ public final class ConnectionManager { public ServerChannelInitializerHolder getServerChannelInitializer() { return this.serverChannelInitializer; } + + public AsyncHttpClient getHttpClient() { + return httpClient; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java index e382a4856..a1b799494 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java @@ -7,11 +7,6 @@ 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.kqueue.KQueue; -import io.netty.channel.kqueue.KQueueDatagramChannel; -import io.netty.channel.kqueue.KQueueEventLoopGroup; -import io.netty.channel.kqueue.KQueueServerSocketChannel; -import io.netty.channel.kqueue.KQueueSocketChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.ServerSocketChannel; @@ -27,10 +22,7 @@ enum TransportType { (name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))), EPOLL("epoll", EpollServerSocketChannel.class, EpollSocketChannel.class, EpollDatagramChannel.class, - (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))), - KQUEUE("Kqueue", KQueueServerSocketChannel.class, KQueueSocketChannel.class, - KQueueDatagramChannel.class, - (name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type))); + (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))); final String name; final Class serverSocketChannelClass; @@ -66,8 +58,6 @@ enum TransportType { public static TransportType bestType() { if (Epoll.isAvailable()) { return EPOLL; - } else if (KQueue.isAvailable()) { - return KQUEUE; } else { return NIO; } 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 deleted file mode 100644 index 5d0211a09..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/http/NettyHttpClient.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.velocitypowered.proxy.network.http; - -import com.velocitypowered.proxy.VelocityServer; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.EventLoop; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SslHandler; -import java.net.InetSocketAddress; -import java.net.URL; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import javax.net.ssl.SSLEngine; -import org.checkerframework.checker.nullness.qual.Nullable; - -public class NettyHttpClient { - - private final String userAgent; - private final VelocityServer server; - - /** - * Initializes the HTTP client. - * - * @param server the Velocity server - */ - public NettyHttpClient(VelocityServer server) { - this.userAgent = server.getVersion().getName() + "/" + server.getVersion().getVersion(); - this.server = server; - } - - private ChannelFuture establishConnection(URL url, @Nullable EventLoop loop) { - String host = url.getHost(); - int port = url.getPort(); - boolean ssl = url.getProtocol().equals("https"); - if (port == -1) { - port = ssl ? 443 : 80; - } - - InetSocketAddress address = InetSocketAddress.createUnresolved(host, port); - return server.createBootstrap(loop) - .handler(new ChannelInitializer() { - @Override - protected void initChannel(Channel ch) throws Exception { - if (ssl) { - SslContext context = SslContextBuilder.forClient().protocols("TLSv1.2").build(); - // Unbelievably, Java doesn't automatically check the CN to make sure we're talking - // to the right host! Therefore, we provide the intended host name and port, along - // with asking Java very nicely if it could check the hostname in the certificate - // for us. - SSLEngine engine = context.newEngine(ch.alloc(), address.getHostString(), - address.getPort()); - engine.getSSLParameters().setEndpointIdentificationAlgorithm("HTTPS"); - ch.pipeline().addLast("ssl", new SslHandler(engine)); - } - ch.pipeline().addLast("http", new HttpClientCodec()); - } - }) - .connect(address); - } - - /** - * Attempts an HTTP GET request to the specified URL. - * @param url the URL to fetch - * @param loop the event loop to use - * @return a future representing the response - */ - public CompletableFuture get(URL url, @Nullable EventLoop loop) { - CompletableFuture reply = new CompletableFuture<>(); - establishConnection(url, loop) - .addListener((ChannelFutureListener) future -> { - if (future.isSuccess()) { - Channel channel = future.channel(); - - channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply)); - - String pathAndQuery = url.getPath(); - if (url.getQuery() != null && url.getQuery().length() > 0) { - pathAndQuery += "?" + url.getQuery(); - } - - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.GET, pathAndQuery); - request.headers().add(HttpHeaderNames.HOST, url.getHost()); - request.headers().add(HttpHeaderNames.USER_AGENT, userAgent); - channel.writeAndFlush(request, channel.voidPromise()); - } else { - reply.completeExceptionally(future.cause()); - } - }); - return reply; - } - - /** - * Attempts an HTTP POST request to the specified URL. - * @param url the URL to fetch - * @param body the body to post - * @param decorator a consumer that can modify the request as required - * @return a future representing the response - */ - public CompletableFuture post(URL url, ByteBuf body, - Consumer decorator) { - return post(url, null, body, decorator); - } - - /** - * Attempts an HTTP POST request to the specified URL. - * @param url the URL to fetch - * @param loop the event loop to use - * @param body the body to post - the HTTP client takes ownership of the buffer - * @param decorator a consumer that can modify the request as required - * @return a future representing the response - */ - public CompletableFuture post(URL url, @Nullable EventLoop loop, ByteBuf body, - Consumer decorator) { - CompletableFuture reply = new CompletableFuture<>(); - establishConnection(url, loop) - .addListener((ChannelFutureListener) future -> { - if (future.isSuccess()) { - Channel channel = future.channel(); - - channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply)); - - String pathAndQuery = url.getPath(); - if (url.getQuery() != null && url.getQuery().length() > 0) { - pathAndQuery += "?" + url.getQuery(); - } - - DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, - HttpMethod.POST, pathAndQuery, body); - request.headers().add(HttpHeaderNames.HOST, url.getHost()); - request.headers().add(HttpHeaderNames.USER_AGENT, userAgent); - request.headers().add(HttpHeaderNames.CONTENT_LENGTH, body.readableBytes()); - decorator.accept(request); - - channel.writeAndFlush(request, channel.voidPromise()); - } else { - body.release(); - reply.completeExceptionally(future.cause()); - } - }); - return reply; - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponse.java b/proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponse.java deleted file mode 100644 index 3bcfdc40a..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.velocitypowered.proxy.network.http; - -public class SimpleHttpResponse { - - private final int code; - private final String body; - - SimpleHttpResponse(int code, String body) { - this.code = code; - this.body = body; - } - - public int getCode() { - return code; - } - - public String getBody() { - return body; - } - - @Override - public String toString() { - return "SimpleHttpResponse{" - + "code=" + code - + ", body='" + body + '\'' - + '}'; - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponseCollector.java b/proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponseCollector.java deleted file mode 100644 index 533e8170f..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/http/SimpleHttpResponseCollector.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.velocitypowered.proxy.network.http; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.util.ReferenceCountUtil; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.CompletableFuture; - -class SimpleHttpResponseCollector extends ChannelInboundHandlerAdapter { - - private final StringBuilder buffer = new StringBuilder(); - private final CompletableFuture reply; - private int httpCode; - - SimpleHttpResponseCollector(CompletableFuture reply) { - this.reply = reply; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - try { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; - HttpResponseStatus status = response.status(); - this.httpCode = status.code(); - } - - if (msg instanceof HttpContent) { - buffer.append(((HttpContent) msg).content().toString(StandardCharsets.UTF_8)); - - if (msg instanceof LastHttpContent) { - ctx.close(); - reply.complete(new SimpleHttpResponse(httpCode, buffer.toString())); - } - } - } finally { - ReferenceCountUtil.release(msg); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ctx.close(); - reply.completeExceptionally(cause); - } -} From 5d37a7f96e9022ede9620cdc839fc49948a13bb3 Mon Sep 17 00:00:00 2001 From: Jamie Campbell Date: Fri, 19 Jul 2019 17:47:05 +0100 Subject: [PATCH 07/37] 1.14.4 support --- README.md | 2 +- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c518f6537..c83e79669 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Velocity is currently in beta. Production networks are successfully running Velocity with many hundreds of concurrent players online, but your mileage may vary. -Velocity supports Minecraft 1.8-1.14.3. Velocity is best supported with Paper +Velocity supports Minecraft 1.8-1.14.4. Velocity is best supported with Paper and SpongeVanilla. Minecraft Forge is fully supported but mod compatibility may vary. Generally, Velocity will support many mods better than BungeeCord or Waterfall do but compatibility can not always be ensured. diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 7b096f194..d1764a5c6 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -30,7 +30,8 @@ public enum ProtocolVersion { MINECRAFT_1_14(477, "1.14"), MINECRAFT_1_14_1(480, "1.14.1"), MINECRAFT_1_14_2(485, "1.14.2"), - MINECRAFT_1_14_3(490, "1.14.3"); + MINECRAFT_1_14_3(490, "1.14.3"), + MINECRAFT_1_14_4(498, "1.14.4"); private final int protocol; private final String name; From 454b3c9bee47eff552a55dd722d0c7af228bfa3f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 12 Jul 2019 14:32:37 -0400 Subject: [PATCH 08/37] Add flag to disable native transports --- .../java/com/velocitypowered/proxy/network/TransportType.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java index a1b799494..b5829f375 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java @@ -56,6 +56,10 @@ enum TransportType { } public static TransportType bestType() { + if (Boolean.getBoolean("velocity.disable-native-transport")) { + return NIO; + } + if (Epoll.isAvailable()) { return EPOLL; } else { From 8d5f5e48f29255ae12bacc6f79127bc6c4a7e2ef Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 26 Jul 2019 01:36:28 -0400 Subject: [PATCH 09/37] Fix Travis build matrix --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 62f109821..ff0d0f19d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,5 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ jdk: - - oraclejdk8 \ No newline at end of file + - openjdk8 + - openjdk11 \ No newline at end of file From bf035bce73637636003b8a77d883bacf5e7398a1 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 28 Jul 2019 23:40:23 -0400 Subject: [PATCH 10/37] Properly use non-blocking DNS in AsyncHttpClient --- .../proxy/network/ConnectionManager.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) 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 9fa8b6b0c..85f5ca1af 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -22,7 +22,11 @@ import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.Dsl; +import org.asynchttpclient.RequestBuilder; +import org.asynchttpclient.filter.FilterContext; +import org.asynchttpclient.filter.FilterContext.FilterContextBuilder; +import org.asynchttpclient.filter.FilterException; +import org.asynchttpclient.filter.RequestFilter; import org.checkerframework.checker.nullness.qual.Nullable; public final class ConnectionManager { @@ -55,15 +59,26 @@ public final class ConnectionManager { this.workerGroup = this.transportType.createEventLoopGroup(TransportType.Type.WORKER); this.serverChannelInitializer = new ServerChannelInitializerHolder( new ServerChannelInitializer(this.server)); - this.resolverGroup = new DnsAddressResolverGroup( - new DnsNameResolverBuilder() - .channelType(this.transportType.datagramChannelClass) - .negativeTtl(15) - .ndots(1) - ); + + DnsNameResolverBuilder builder = new DnsNameResolverBuilder() + .channelType(this.transportType.datagramChannelClass) + .negativeTtl(15) + .ndots(1); + + this.resolverGroup = new DnsAddressResolverGroup(builder); this.httpClient = asyncHttpClient(config() .setEventLoopGroup(this.workerGroup) .setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion()) + .addRequestFilter(new RequestFilter() { + @Override + public FilterContext filter(FilterContext ctx) throws FilterException { + return new FilterContextBuilder<>(ctx) + .request(new RequestBuilder(ctx.getRequest()) + .setNameResolver(builder.eventLoop(workerGroup.next()).build()) + .build()) + .build(); + } + }) .build()); } From ead8a213625603234347b99fa936f2c0a56fb557 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 00:19:15 -0400 Subject: [PATCH 11/37] Most proper clean way of using a DnsAddressResolverGroup with AHC Let us never speak of this again. --- .../proxy/network/ConnectionManager.java | 13 ++-- ...dressResolverGroupNameResolverAdapter.java | 68 +++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java 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 85f5ca1af..d1c3dba9b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -6,6 +6,7 @@ import static org.asynchttpclient.Dsl.config; import com.google.common.base.Preconditions; import com.velocitypowered.natives.util.Natives; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.network.netty.DnsAddressResolverGroupNameResolverAdapter; import com.velocitypowered.proxy.protocol.netty.GS4QueryHandler; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; @@ -16,6 +17,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.WriteBufferWaterMark; 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; @@ -59,13 +61,10 @@ public final class ConnectionManager { this.workerGroup = this.transportType.createEventLoopGroup(TransportType.Type.WORKER); this.serverChannelInitializer = new ServerChannelInitializerHolder( new ServerChannelInitializer(this.server)); - - DnsNameResolverBuilder builder = new DnsNameResolverBuilder() + this.resolverGroup = new DnsAddressResolverGroup(new DnsNameResolverBuilder() .channelType(this.transportType.datagramChannelClass) .negativeTtl(15) - .ndots(1); - - this.resolverGroup = new DnsAddressResolverGroup(builder); + .ndots(1)); this.httpClient = asyncHttpClient(config() .setEventLoopGroup(this.workerGroup) .setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion()) @@ -74,7 +73,9 @@ public final class ConnectionManager { public FilterContext filter(FilterContext ctx) throws FilterException { return new FilterContextBuilder<>(ctx) .request(new RequestBuilder(ctx.getRequest()) - .setNameResolver(builder.eventLoop(workerGroup.next()).build()) + .setNameResolver( + new DnsAddressResolverGroupNameResolverAdapter(resolverGroup, workerGroup) + ) .build()) .build(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java new file mode 100644 index 000000000..9de466f84 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java @@ -0,0 +1,68 @@ +package com.velocitypowered.proxy.network.netty; + +import io.netty.channel.EventLoopGroup; +import io.netty.resolver.InetNameResolver; +import io.netty.resolver.dns.DnsAddressResolverGroup; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.ImmediateEventExecutor; +import io.netty.util.concurrent.Promise; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; + +public class DnsAddressResolverGroupNameResolverAdapter extends InetNameResolver { + + private final DnsAddressResolverGroup resolverGroup; + private final EventLoopGroup group; + + public DnsAddressResolverGroupNameResolverAdapter( + DnsAddressResolverGroup resolverGroup, EventLoopGroup group) { + super(ImmediateEventExecutor.INSTANCE); + this.resolverGroup = resolverGroup; + this.group = group; + } + + @Override + protected void doResolve(String inetHost, Promise promise) throws Exception { + EventExecutor executor = this.findExecutor(); + resolverGroup.getResolver(executor).resolve(InetSocketAddress.createUnresolved(inetHost, 17)) + .addListener((FutureListener) future -> { + if (future.isSuccess()) { + promise.trySuccess(future.getNow().getAddress()); + } else { + promise.tryFailure(future.cause()); + } + }); + } + + @Override + protected void doResolveAll(String inetHost, Promise> promise) + throws Exception { + EventExecutor executor = this.findExecutor(); + resolverGroup.getResolver(executor).resolveAll(InetSocketAddress.createUnresolved(inetHost, 17)) + .addListener((FutureListener>) future -> { + if (future.isSuccess()) { + List addresses = new ArrayList<>(future.getNow().size()); + for (InetSocketAddress address : future.getNow()) { + addresses.add(address.getAddress()); + } + promise.trySuccess(addresses); + } else { + promise.tryFailure(future.cause()); + } + }); + } + + private EventExecutor findExecutor() { + for (EventExecutor executor : group) { + if (executor.inEventLoop()) { + return executor; + } + } + + // otherwise, pick one + return group.next(); + } +} From d65e3710383881734fbfe08859a0a4f3b83c925d Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 00:24:57 -0400 Subject: [PATCH 12/37] Fix up the Travis build... --- .travis.yml | 3 +-- .../netty/DnsAddressResolverGroupNameResolverAdapter.java | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff0d0f19d..3d0c36340 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,4 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ jdk: - - openjdk8 - - openjdk11 \ No newline at end of file + - openjdk8 \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java index 9de466f84..f76a160c9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/netty/DnsAddressResolverGroupNameResolverAdapter.java @@ -17,6 +17,11 @@ public class DnsAddressResolverGroupNameResolverAdapter extends InetNameResolver private final DnsAddressResolverGroup resolverGroup; private final EventLoopGroup group; + /** + * Creates a DnsAddressResolverGroupNameResolverAdapter. + * @param resolverGroup the resolver group to use + * @param group the event loop group + */ public DnsAddressResolverGroupNameResolverAdapter( DnsAddressResolverGroup resolverGroup, EventLoopGroup group) { super(ImmediateEventExecutor.INSTANCE); From 1da51b8ffb3bf6cec7faef648becf2fe5781a351 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 05:25:29 -0400 Subject: [PATCH 13/37] Batch handshake and server login/ping packets --- .../proxy/connection/backend/VelocityServerConnection.java | 5 +++-- .../velocitypowered/proxy/server/PingSessionHandler.java | 6 ++++-- 2 files changed, 7 insertions(+), 4 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 39e0bbd0c..5922238a1 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 @@ -150,11 +150,12 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString()); } handshake.setPort(registeredServer.getServerInfo().getAddress().getPort()); - mc.write(handshake); + mc.delayedWrite(handshake); mc.setProtocolVersion(protocolVersion); mc.setState(StateRegistry.LOGIN); - mc.write(new ServerLogin(proxyPlayer.getUsername())); + mc.delayedWrite(new ServerLogin(proxyPlayer.getUsername())); + mc.flush(); } public @Nullable MinecraftConnection getConnection() { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java index 63e7630c8..48875b988 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java @@ -34,10 +34,12 @@ public class PingSessionHandler implements MinecraftSessionHandler { handshake.setServerAddress(server.getServerInfo().getAddress().getHostString()); handshake.setPort(server.getServerInfo().getAddress().getPort()); handshake.setProtocolVersion(ProtocolVersion.MINIMUM_VERSION); - connection.write(handshake); + connection.delayedWrite(handshake); connection.setState(StateRegistry.STATUS); - connection.write(StatusRequest.INSTANCE); + connection.delayedWrite(StatusRequest.INSTANCE); + + connection.flush(); } @Override From 04efd16a838a065c8b7c405e8d2e290f1f49bbc3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 06:04:37 -0400 Subject: [PATCH 14/37] Avoid a memory copy in creating modern-style forwarding data --- .../backend/LoginSessionHandler.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 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 b2535931e..d18951369 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 @@ -117,32 +117,28 @@ public class LoginSessionHandler implements MinecraftSessionHandler { private static ByteBuf createForwardingData(byte[] hmacSecret, String address, GameProfile profile) { - ByteBuf dataToForward = Unpooled.buffer(); - ByteBuf finalData = Unpooled.buffer(); + ByteBuf forwarded = Unpooled.buffer(2048); try { - ProtocolUtils.writeVarInt(dataToForward, VelocityConstants.FORWARDING_VERSION); - ProtocolUtils.writeString(dataToForward, address); - ProtocolUtils.writeUuid(dataToForward, profile.getId()); - ProtocolUtils.writeString(dataToForward, profile.getName()); - ProtocolUtils.writeProperties(dataToForward, profile.getProperties()); + ProtocolUtils.writeVarInt(forwarded, VelocityConstants.FORWARDING_VERSION); + ProtocolUtils.writeString(forwarded, address); + ProtocolUtils.writeUuid(forwarded, profile.getId()); + ProtocolUtils.writeString(forwarded, profile.getName()); + ProtocolUtils.writeProperties(forwarded, profile.getProperties()); SecretKey key = new SecretKeySpec(hmacSecret, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(key); - mac.update(dataToForward.array(), dataToForward.arrayOffset(), dataToForward.readableBytes()); + mac.update(forwarded.array(), forwarded.arrayOffset(), forwarded.readableBytes()); byte[] sig = mac.doFinal(); - finalData.writeBytes(sig); - finalData.writeBytes(dataToForward); - return finalData; + + return Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(sig), forwarded); } catch (InvalidKeyException e) { - finalData.release(); + forwarded.release(); throw new RuntimeException("Unable to authenticate data", e); } catch (NoSuchAlgorithmException e) { // Should never happen - finalData.release(); + forwarded.release(); throw new AssertionError(e); - } finally { - dataToForward.release(); } } } From 03c9729e638e3ddb31eff088fe209053e64f3540 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 14:19:21 -0400 Subject: [PATCH 15/37] Don't disconnect the player if the connection unexpectedly closes --- .../proxy/connection/backend/BackendPlaySessionHandler.java | 3 ++- 1 file changed, 2 insertions(+), 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 64c72042a..571a7323c 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 @@ -193,7 +193,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { public void disconnected() { serverConn.getServer().removePlayer(serverConn.getPlayer()); if (!serverConn.isGracefulDisconnect() && !exceptionTriggered) { - serverConn.getPlayer().disconnect(ConnectionMessages.UNEXPECTED_DISCONNECT); + serverConn.getPlayer().handleConnectionException(serverConn.getServer(), + Disconnect.create(ConnectionMessages.UNEXPECTED_DISCONNECT), true); } } } From 0acb1ed7d4e21a793a0ef583e6102c6f3a36d88c Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 14:27:40 -0400 Subject: [PATCH 16/37] Send disconnect message on getting kicked from the server --- .../proxy/connection/client/ConnectedPlayer.java | 2 +- .../java/com/velocitypowered/proxy/util/VelocityMessages.java | 2 +- 2 files changed, 2 insertions(+), 2 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 4d37cd324..97140e06e 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 @@ -452,7 +452,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (newResult == null || !newResult) { disconnect(friendlyReason); } else { - sendMessage(VelocityMessages.MOVED_TO_NEW_SERVER); + sendMessage(VelocityMessages.MOVED_TO_NEW_SERVER.append(friendlyReason)); } }, minecraftConnection.eventLoop()); } else if (event.getResult() instanceof Notify) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java index 1a4d29337..4acf5889f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java @@ -21,7 +21,7 @@ public class VelocityMessages { public static final Component ALREADY_CONNECTED = TextComponent .of("You are already connected to this proxy!", TextColor.RED); public static final Component MOVED_TO_NEW_SERVER = TextComponent - .of("You were moved from the server you were on because you were kicked", TextColor.RED); + .of("The server you were on kicked you: ", TextColor.RED); private VelocityMessages() { throw new AssertionError(); From 121ab41d96736cf324351f4f9b597986525fc029 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 29 Jul 2019 17:32:56 -0400 Subject: [PATCH 17/37] Introduce TCP_FASTOPEN support For best results, use Linux 4.11+ and a server modified to set the TCP_FASTOPEN flag. This should in particular benefit users of legacy forwarding. --- .../proxy/config/VelocityConfiguration.java | 14 ++++++++++++++ .../proxy/network/ConnectionManager.java | 12 +++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) 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 48449e079..812d66154 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -372,6 +372,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return advanced.isProxyProtocol(); } + public boolean useTcpFastOpen() { + return advanced.tcpFastOpen; + } + public Metrics getMetrics() { return metrics; } @@ -608,6 +612,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @ConfigKey("proxy-protocol") private boolean proxyProtocol = false; + @Comment("Enables TCP fast open support on the proxy. Requires the proxy to run on Linux.") + @ConfigKey("tcp-fast-open") + private boolean tcpFastOpen = false; + private Advanced() { } @@ -619,6 +627,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi this.connectionTimeout = toml.getLong("connection-timeout", 5000L).intValue(); this.readTimeout = toml.getLong("read-timeout", 30000L).intValue(); this.proxyProtocol = toml.getBoolean("proxy-protocol", false); + this.tcpFastOpen = toml.getBoolean("tcp-fast-open", false); } } @@ -646,6 +655,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return proxyProtocol; } + public boolean isTcpFastOpen() { + return tcpFastOpen; + } + @Override public String toString() { return "Advanced{" @@ -655,6 +668,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi + ", connectionTimeout=" + connectionTimeout + ", readTimeout=" + readTimeout + ", proxyProtocol=" + proxyProtocol + + ", tcpFastOpen=" + tcpFastOpen + '}'; } } 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 d1c3dba9b..208ce766e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -15,6 +15,7 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; 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; @@ -102,6 +103,11 @@ public final class ConnectionManager { .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); + + if (server.getConfiguration().useTcpFastOpen()) { + bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3); + } + bootstrap.bind() .addListener((ChannelFutureListener) future -> { final Channel channel = future.channel(); @@ -151,13 +157,17 @@ public final class ConnectionManager { * @return a new {@link Bootstrap} */ public Bootstrap createWorker(@Nullable EventLoopGroup group) { - return new Bootstrap() + Bootstrap bootstrap = new Bootstrap() .channel(this.transportType.socketChannelClass) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.server.getConfiguration().getConnectTimeout()) .group(group == null ? this.workerGroup : group) .resolver(this.resolverGroup); + if (server.getConfiguration().useTcpFastOpen()) { + bootstrap.option(EpollChannelOption.TCP_FASTOPEN_CONNECT, true); + } + return bootstrap; } /** From 162bb8e191495ba8ac7625ab6b375f270144e19f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 30 Jul 2019 13:05:26 -0400 Subject: [PATCH 18/37] Update to Netty 4.1.38.Final --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3341ce3ad..5add3e122 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.37.Final' + nettyVersion = '4.1.38.Final' guavaVersion = '25.1-jre' checkerFrameworkVersion = '2.7.0' configurateVersion = '3.6' From 7db757cd04eddbb0cd9ea9865b66c41a2097e2c3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 30 Jul 2019 17:10:10 -0400 Subject: [PATCH 19/37] Allow certain mangled large packets to pass through --- .../proxy/protocol/netty/MinecraftCompressDecoder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 56af37e65..a57485f03 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 @@ -35,10 +35,9 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder { checkFrame(expectedSize >= threshold, "Uncompressed size %s is greater than threshold %s", expectedSize, threshold); - checkFrame(expectedSize <= MAXIMUM_UNCOMPRESSED_SIZE, "Expected uncompressed size" - + "%s is larger than protocol maximum of %s", expectedSize, MAXIMUM_UNCOMPRESSED_SIZE); + int initialCapacity = Math.min(expectedSize, MAXIMUM_UNCOMPRESSED_SIZE); ByteBuf compatibleIn = ensureCompatible(ctx.alloc(), compressor, in); - ByteBuf uncompressed = preferredBuffer(ctx.alloc(), compressor, expectedSize); + ByteBuf uncompressed = preferredBuffer(ctx.alloc(), compressor, initialCapacity); try { compressor.inflate(compatibleIn, uncompressed, expectedSize); out.add(uncompressed); From 19cec571d04fca4c59f7bef90f9f02bedeeb5640 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 5 Aug 2019 09:53:18 -0400 Subject: [PATCH 20/37] Clean up some junk --- .../proxy/connection/client/LoginSessionHandler.java | 2 -- 1 file changed, 2 deletions(-) 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 f30b3d218..6e38f0518 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 @@ -56,7 +56,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 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"; @@ -68,7 +67,6 @@ public class LoginSessionHandler implements MinecraftSessionHandler { private byte[] verify = EMPTY_BYTE_ARRAY; private int playerInfoId; private @MonotonicNonNull ConnectedPlayer connectedPlayer; - private final long loginProcessBegan = System.currentTimeMillis(); LoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection, InitialInboundConnection inbound) { From b4e62443c99faaba599bfdc285dbe2fbadf303bf Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 5 Aug 2019 10:30:55 -0400 Subject: [PATCH 21/37] Add arbitrary chat tab complete event. Fixes #236 --- .../api/event/player/TabCompleteEvent.java | 52 +++++++++++ .../client/ClientPlaySessionHandler.java | 86 +++++++++++++------ .../protocol/packet/TabCompleteResponse.java | 4 + 3 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java diff --git a/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java new file mode 100644 index 000000000..ecfbded01 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java @@ -0,0 +1,52 @@ +package com.velocitypowered.api.event.player; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.velocitypowered.api.proxy.Player; +import java.util.ArrayList; +import java.util.List; + +/** + * This event is fired after a tab complete response is sent by the remote server, for clients on + * 1.12.2 and below. You have the opportunity to modify the response sent to the remote player. + */ +public class TabCompleteEvent { + private final Player player; + private final String partialMessage; + private final List suggestions; + + public TabCompleteEvent(Player player, String partialMessage, List suggestions) { + this.player = checkNotNull(player, "player"); + this.partialMessage = checkNotNull(partialMessage, "partialMessage"); + this.suggestions = new ArrayList<>(checkNotNull(suggestions, "suggestions")); + } + + /** + * Returns the player requesting the tab completion. + * @return the requesting player + */ + public Player getPlayer() { + return player; + } + + /** + * Returns the message being partially completed. + * @return + */ + public String getPartialMessage() { + return partialMessage; + } + + public List getSuggestions() { + return suggestions; + } + + @Override + public String toString() { + return "TabCompleteEvent{" + + "player=" + player + + ", partialMessage='" + partialMessage + '\'' + + ", suggestions=" + suggestions + + '}'; + } +} 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 69d5ec3a3..103389abb 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 @@ -6,6 +6,7 @@ import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.construc import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.PlayerChatEvent; import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; +import com.velocitypowered.api.event.player.TabCompleteEvent; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; @@ -35,12 +36,11 @@ import io.netty.util.ReferenceCountUtil; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Queue; -import java.util.Set; import java.util.UUID; +import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; import org.apache.logging.log4j.LogManager; @@ -60,7 +60,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { private final List serverBossBars = new ArrayList<>(); private final Queue loginPluginMessages = new ArrayDeque<>(); private final VelocityServer server; - private @Nullable TabCompleteRequest legacyCommandTabComplete; + private @Nullable TabCompleteRequest outstandingTabComplete; /** * Constructs a client play session handler. @@ -156,25 +156,27 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { public boolean handle(TabCompleteRequest packet) { boolean isCommand = !packet.isAssumeCommand() && packet.getCommand().startsWith("/"); - if (!isCommand) { - // We can't deal with anything else. - return false; + if (isCommand) { + return this.handleTabCompleteForCommand(packet); + } else { + return this.handleRegularTabComplete(packet); } + } + private boolean handleTabCompleteForCommand(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) { - return false; + spacePos = command.length(); } String commandLabel = command.substring(0, spacePos); 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 - // tab list completion support for command names. In 1.13, Brigadier handles everything for - // us. - legacyCommandTabComplete = packet; + // additional tab completion support. + outstandingTabComplete = packet; } return false; } @@ -213,6 +215,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return true; } + private boolean handleRegularTabComplete(TabCompleteRequest packet) { + if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) { + // Outstanding tab completes are recorded for use with 1.12 clients and below to provide + // additional tab completion support. + outstandingTabComplete = packet; + } + return false; + } + @Override public boolean handle(PluginMessage packet) { VelocityServerConnection serverConn = player.getConnectedServer(); @@ -412,28 +423,53 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } /** - * Handles additional tab complete for 1.12 and lower clients. + * Handles additional tab complete. * * @param response the tab complete response from the backend */ public void handleTabCompleteResponse(TabCompleteResponse response) { - if (legacyCommandTabComplete != null) { - String command = legacyCommandTabComplete.getCommand().substring(1); - try { - List offers = server.getCommandManager().offerSuggestions(player, command); - for (String offer : offers) { - response.getOffers().add(new Offer(offer, null)); - } - response.getOffers().sort(null); - } catch (Exception e) { - logger.error("Unable to provide tab list completions for {} for command '{}'", - player.getUsername(), - command, e); + if (outstandingTabComplete != null) { + if (outstandingTabComplete.isAssumeCommand()) { + return; // used for command blocks which can't run Velocity commands anyway } - legacyCommandTabComplete = null; + if (outstandingTabComplete.getCommand().startsWith("/")) { + this.finishCommandTabComplete(outstandingTabComplete, response); + } else { + this.finishRegularTabComplete(outstandingTabComplete, response); + } + outstandingTabComplete = null; } + } - player.getMinecraftConnection().write(response); + private void finishCommandTabComplete(TabCompleteRequest request, TabCompleteResponse response) { + String command = request.getCommand().substring(1); + try { + List offers = server.getCommandManager().offerSuggestions(player, command); + for (String offer : offers) { + response.getOffers().add(new Offer(offer, null)); + } + response.getOffers().sort(null); + player.getMinecraftConnection().write(response); + } catch (Exception e) { + logger.error("Unable to provide tab list completions for {} for command '{}'", + player.getUsername(), + command, e); + } + } + + private void finishRegularTabComplete(TabCompleteRequest request, TabCompleteResponse response) { + List offers = new ArrayList<>(); + for (Offer offer : response.getOffers()) { + offers.add(offer.getText()); + } + server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), offers)) + .thenAcceptAsync(e -> { + response.getOffers().clear(); + for (String s : e.getSuggestions()) { + response.getOffers().add(new Offer(s)); + } + player.getMinecraftConnection().write(response); + }, player.getMinecraftConnection().eventLoop()); } /** 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 c6db498d2..3975b3235 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 @@ -133,5 +133,9 @@ public class TabCompleteResponse implements MinecraftPacket { public int compareTo(Offer o) { return this.text.compareTo(o.text); } + + public String getText() { + return text; + } } } From 2d9227ccf06588f8e464b053448a30acea4d1be0 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 5 Aug 2019 10:51:45 -0400 Subject: [PATCH 22/37] Fix Checkstyle --- .../api/event/player/TabCompleteEvent.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java index ecfbded01..f033d954e 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/TabCompleteEvent.java @@ -15,6 +15,12 @@ public class TabCompleteEvent { private final String partialMessage; private final List suggestions; + /** + * Constructs a new TabCompleteEvent instance. + * @param player the player + * @param partialMessage the partial message + * @param suggestions the initial list of suggestions + */ public TabCompleteEvent(Player player, String partialMessage, List suggestions) { this.player = checkNotNull(player, "player"); this.partialMessage = checkNotNull(partialMessage, "partialMessage"); @@ -31,12 +37,16 @@ public class TabCompleteEvent { /** * Returns the message being partially completed. - * @return + * @return the partial message */ public String getPartialMessage() { return partialMessage; } + /** + * Returns all the suggestions provided to the user, as a mutable list. + * @return the suggestions + */ public List getSuggestions() { return suggestions; } From 9f3d1a23907d0596a31ab479a5029566bceff6f3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 5 Aug 2019 11:01:18 -0400 Subject: [PATCH 23/37] Fix Checkstyle, again --- .../client/ClientPlaySessionHandler.java | 125 +++++++++--------- 1 file changed, 63 insertions(+), 62 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 103389abb..32a67b755 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 @@ -157,73 +157,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { boolean isCommand = !packet.isAssumeCommand() && packet.getCommand().startsWith("/"); if (isCommand) { - return this.handleTabCompleteForCommand(packet); + return this.handleCommandTabComplete(packet); } else { return this.handleRegularTabComplete(packet); } } - private boolean handleTabCompleteForCommand(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(); - } - - String commandLabel = command.substring(0, spacePos); - 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 - // additional tab completion support. - outstandingTabComplete = packet; - } - return false; - } - - List suggestions = server.getCommandManager().offerSuggestions(player, command); - if (suggestions.isEmpty()) { - return false; - } - - List offers = new ArrayList<>(); - int longestLength = 0; - for (String suggestion : suggestions) { - offers.add(new Offer(suggestion)); - if (suggestion.length() > longestLength) { - longestLength = suggestion.length(); - } - } - - TabCompleteResponse resp = new TabCompleteResponse(); - resp.setTransactionId(packet.getTransactionId()); - - int startPos = packet.getCommand().lastIndexOf(' ') + 1; - int length; - if (startPos == 0) { - startPos = packet.getCommand().length() + 1; - length = longestLength; - } else { - length = packet.getCommand().length() - startPos; - } - - resp.setStart(startPos); - resp.setLength(length); - resp.getOffers().addAll(offers); - - player.getMinecraftConnection().write(resp); - return true; - } - - private boolean handleRegularTabComplete(TabCompleteRequest packet) { - if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) { - // Outstanding tab completes are recorded for use with 1.12 clients and below to provide - // additional tab completion support. - outstandingTabComplete = packet; - } - return false; - } - @Override public boolean handle(PluginMessage packet) { VelocityServerConnection serverConn = player.getConnectedServer(); @@ -422,6 +361,68 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return serverBossBars; } + + 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(); + } + + String commandLabel = command.substring(0, spacePos); + 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 + // additional tab completion support. + outstandingTabComplete = packet; + } + return false; + } + + List suggestions = server.getCommandManager().offerSuggestions(player, command); + if (suggestions.isEmpty()) { + return false; + } + + List offers = new ArrayList<>(); + int longestLength = 0; + for (String suggestion : suggestions) { + offers.add(new Offer(suggestion)); + if (suggestion.length() > longestLength) { + longestLength = suggestion.length(); + } + } + + TabCompleteResponse resp = new TabCompleteResponse(); + resp.setTransactionId(packet.getTransactionId()); + + int startPos = packet.getCommand().lastIndexOf(' ') + 1; + int length; + if (startPos == 0) { + startPos = packet.getCommand().length() + 1; + length = longestLength; + } else { + length = packet.getCommand().length() - startPos; + } + + resp.setStart(startPos); + resp.setLength(length); + resp.getOffers().addAll(offers); + + player.getMinecraftConnection().write(resp); + return true; + } + + private boolean handleRegularTabComplete(TabCompleteRequest packet) { + if (player.getProtocolVersion().compareTo(MINECRAFT_1_13) < 0) { + // Outstanding tab completes are recorded for use with 1.12 clients and below to provide + // additional tab completion support. + outstandingTabComplete = packet; + } + return false; + } + /** * Handles additional tab complete. * From ca9a4492c423af521d8acaa536b200c9ad6140bf Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 6 Aug 2019 02:06:53 -0400 Subject: [PATCH 24/37] Add support for ping pass-through By default, ping pass-through is not enabled. However, you can use ping passthrough to pass through just mods (great for modded servers) or everything. --- .../api/proxy/server/ServerPing.java | 13 +++ proxy/build.gradle | 2 + .../velocitypowered/proxy/VelocityServer.java | 7 +- .../proxy/config/PingPassthroughMode.java | 7 ++ .../proxy/config/VelocityConfiguration.java | 35 +++++++- .../client/StatusSessionHandler.java | 90 ++++++++++++++++--- .../proxy/network/ConnectionManager.java | 4 - .../proxy/server/PingSessionHandler.java | 6 +- .../server/VelocityRegisteredServer.java | 19 +++- 9 files changed, 152 insertions(+), 31 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java 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 f571c4517..db3ef1ed5 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 @@ -182,6 +182,19 @@ public final class ServerPing { return this; } + /** + * Uses the modified {@code mods} list in the response. + * @param mods the mods list to use + * @return this build, for chaining + */ + public Builder mods(ModInfo mods) { + Preconditions.checkNotNull(mods, "mods"); + this.modType = mods.getType(); + this.mods.clear(); + this.mods.addAll(mods.getMods()); + return this; + } + public Builder clearMods() { this.mods.clear(); return this; diff --git a/proxy/build.gradle b/proxy/build.gradle index 9c3f151b1..9c5bf26fb 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -66,6 +66,8 @@ dependencies { compile 'com.mojang:brigadier:1.0.15' compile 'org.asynchttpclient:async-http-client:2.10.1' + + compile 'com.spotify:completable-futures:0.3.2' testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}" testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index c078ecfb9..5b032e180 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -77,6 +77,7 @@ import org.asynchttpclient.AsyncHttpClient; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.RequiresNonNull; public class VelocityServer implements ProxyServer { @@ -263,11 +264,7 @@ public class VelocityServer implements ProxyServer { logger.info("Loaded {} plugins", pluginManager.getPlugins().size()); } - public Bootstrap createBootstrap() { - return this.cm.createWorker(); - } - - public Bootstrap createBootstrap(EventLoopGroup group) { + public Bootstrap createBootstrap(@Nullable EventLoopGroup group) { return this.cm.createWorker(group); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java b/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java new file mode 100644 index 000000000..89ed3ad8e --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java @@ -0,0 +1,7 @@ +package com.velocitypowered.proxy.config; + +public enum PingPassthroughMode { + DISABLED, + MODS, + ALL +} 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 812d66154..7891d778c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -72,8 +72,13 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @ConfigKey("forwarding-secret") private byte[] forwardingSecret = generateRandomString(12).getBytes(StandardCharsets.UTF_8); - @Comment({"Announce whether or not your server supports Forge. If you run a modded server, we", - "suggest turning this on."}) + @Comment({ + "Announce whether or not your server supports Forge. If you run a modded server, we", + "suggest turning this on.", + "", + "If your network runs one modpack consistently, consider using ping-passthrough = \"mods\"", + "instead for a nicer display in the server list." + }) @ConfigKey("announce-forge") private boolean announceForge = false; @@ -82,6 +87,20 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @ConfigKey("kick-existing-players") private boolean onlineModeKickExistingPlayers = false; + @Comment({ + "Should Velocity pass server list ping requests to a backend server?", + "Available options:", + "- \"disabled\": No pass-through will be done. The velocity.toml and server-icon.png", + " will determine the initial server list ping response.", + "- \"mods\": Passes only the mod list from your backend server into the response.", + " This is the recommended replacement for announce-forge = true. If no backend", + " servers can be contacted, Velocity will not display any mod information.", + "- \"all\": Passes everything from the backend server into the response. The Velocity", + " configuration is used if no servers could be contacted." + }) + @ConfigKey("ping-passthrough") + private PingPassthroughMode pingPassthrough; + @Table("[servers]") private final Servers servers; @@ -114,8 +133,8 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode, boolean announceForge, PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret, - boolean onlineModeKickExistingPlayers, Servers servers, ForcedHosts forcedHosts, - Advanced advanced, Query query, Metrics metrics) { + boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough, Servers servers, + ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics) { this.bind = bind; this.motd = motd; this.showMaxPlayers = showMaxPlayers; @@ -124,6 +143,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi this.playerInfoForwardingMode = playerInfoForwardingMode; this.forwardingSecret = forwardingSecret; this.onlineModeKickExistingPlayers = onlineModeKickExistingPlayers; + this.pingPassthrough = pingPassthrough; this.servers = servers; this.forcedHosts = forcedHosts; this.advanced = advanced; @@ -380,6 +400,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return metrics; } + public PingPassthroughMode getPingPassthrough() { + return pingPassthrough; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -427,6 +451,8 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi String forwardingModeName = toml.getString("player-info-forwarding-mode", "MODERN") .toUpperCase(Locale.US); + String passThroughName = toml.getString("ping-passthrough", "DISABLED") + .toUpperCase(Locale.US); return new VelocityConfiguration( toml.getString("bind", "0.0.0.0:25577"), @@ -437,6 +463,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi PlayerInfoForwarding.valueOf(forwardingModeName), forwardingSecret, toml.getBoolean("kick-existing-players", false), + PingPassthroughMode.valueOf(passThroughName), servers, forcedHosts, advanced, 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 0916d4aea..68403374c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java @@ -1,12 +1,15 @@ package com.velocitypowered.proxy.connection.client; import com.google.common.collect.ImmutableList; +import com.spotify.futures.CompletableFutures; import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.InboundConnection; +import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.config.PingPassthroughMode; import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; @@ -15,7 +18,13 @@ import com.velocitypowered.proxy.protocol.packet.LegacyPing; import com.velocitypowered.proxy.protocol.packet.StatusPing; import com.velocitypowered.proxy.protocol.packet.StatusRequest; import com.velocitypowered.proxy.protocol.packet.StatusResponse; +import com.velocitypowered.proxy.server.VelocityRegisteredServer; import io.netty.buffer.ByteBuf; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; public class StatusSessionHandler implements MinecraftSessionHandler { @@ -30,12 +39,10 @@ public class StatusSessionHandler implements MinecraftSessionHandler { this.inboundWrapper = inboundWrapper; } - private ServerPing createInitialPing() { + private ServerPing constructLocalPing(ProtocolVersion version) { VelocityConfiguration configuration = server.getConfiguration(); - ProtocolVersion shownVersion = ProtocolVersion.isSupported(connection.getProtocolVersion()) - ? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION; return new ServerPing( - new ServerPing.Version(shownVersion.getProtocol(), + new ServerPing.Version(version.getProtocol(), "Velocity " + ProtocolVersion.SUPPORTED_VERSION_STRING), new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(), ImmutableList.of()), @@ -45,12 +52,69 @@ public class StatusSessionHandler implements MinecraftSessionHandler { ); } + private CompletableFuture createInitialPing() { + VelocityConfiguration configuration = server.getConfiguration(); + ProtocolVersion shownVersion = ProtocolVersion.isSupported(connection.getProtocolVersion()) + ? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION; + + PingPassthroughMode passthrough = configuration.getPingPassthrough(); + if (passthrough == PingPassthroughMode.DISABLED) { + return CompletableFuture.completedFuture(constructLocalPing(shownVersion)); + } else { + return attemptPingPassthrough(configuration.getPingPassthrough(), + configuration.getAttemptConnectionOrder(), shownVersion); + } + } + + private CompletableFuture attemptPingPassthrough(PingPassthroughMode mode, + List servers, ProtocolVersion pingingVersion) { + ServerPing fallback = constructLocalPing(pingingVersion); + List> pings = new ArrayList<>(); + for (String s : servers) { + Optional rs = server.getServer(s); + if (!rs.isPresent()) { + continue; + } + VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs.get(); + pings.add(vrs.ping(connection.eventLoop(), pingingVersion)); + } + if (pings.isEmpty()) { + return CompletableFuture.completedFuture(fallback); + } + + CompletableFuture> pingResponses = CompletableFutures.successfulAsList(pings, + (ex) -> fallback); + switch (mode) { + case ALL: + return pingResponses.thenApply(responses -> { + // Find the first non-fallback + return responses.stream() + .filter(ping -> ping != fallback) + .findFirst() + .orElse(fallback); + }); + case MODS: + return pingResponses.thenApply(responses -> { + // Find the first non-fallback that contains a non-empty mod list + Optional modInfo = responses.stream() + .filter(ping -> ping != fallback) + .map(ServerPing::getModinfo) + .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) + .findFirst(); + return modInfo.map(mi -> fallback.asBuilder().mods(mi).build()).orElse(fallback); + }); + default: + // Not possible, but covered for completeness. + return CompletableFuture.completedFuture(fallback); + } + } + @Override public boolean handle(LegacyPing packet) { - ServerPing initialPing = createInitialPing(); - ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing); - server.getEventManager().fire(event) - .thenRunAsync(() -> { + createInitialPing() + .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inboundWrapper, + ping))) + .thenAcceptAsync(event -> { connection.closeWith(LegacyDisconnect.fromServerPing(event.getPing(), packet.getVersion())); }, connection.eventLoop()); @@ -65,11 +129,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(StatusRequest packet) { - ServerPing initialPing = createInitialPing(); - ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing); - server.getEventManager().fire(event) - .thenRunAsync( - () -> { + createInitialPing() + .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inboundWrapper, + ping))) + .thenAcceptAsync( + (event) -> { StringBuilder json = new StringBuilder(); VelocityServer.GSON.toJson(event.getPing(), json); connection.write(new StatusResponse(json)); 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 208ce766e..9fba8e657 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -145,10 +145,6 @@ public final class ConnectionManager { }); } - public Bootstrap createWorker() { - return this.createWorker(null); - } - /** * Creates a TCP {@link Bootstrap} using Velocity's event loops. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java index 48875b988..93094cc36 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java @@ -18,13 +18,15 @@ public class PingSessionHandler implements MinecraftSessionHandler { private final CompletableFuture result; private final RegisteredServer server; private final MinecraftConnection connection; + private final ProtocolVersion version; private boolean completed = false; PingSessionHandler(CompletableFuture result, RegisteredServer server, - MinecraftConnection connection) { + MinecraftConnection connection, ProtocolVersion version) { this.result = result; this.server = server; this.connection = connection; + this.version = version; } @Override @@ -33,7 +35,7 @@ public class PingSessionHandler implements MinecraftSessionHandler { handshake.setNextStatus(StateRegistry.STATUS_ID); handshake.setServerAddress(server.getServerInfo().getAddress().getHostString()); handshake.setPort(server.getServerInfo().getAddress().getPort()); - handshake.setProtocolVersion(ProtocolVersion.MINIMUM_VERSION); + handshake.setProtocolVersion(version); connection.delayedWrite(handshake); connection.setState(StateRegistry.STATUS); 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 6261d5614..cf6694cb4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -9,6 +9,7 @@ import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT; 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; @@ -27,6 +28,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoop; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.Collection; import java.util.Set; @@ -58,11 +60,22 @@ public class VelocityRegisteredServer implements RegisteredServer { @Override public CompletableFuture ping() { + return ping(null, ProtocolVersion.UNKNOWN); + } + + /** + * Pings the specified server using the specified event {@code loop}, claiming to be + * {@code version}. + * @param loop the event loop to use + * @param version the version to report + * @return the server list ping response + */ + public CompletableFuture ping(@Nullable EventLoop loop, ProtocolVersion version) { if (server == null) { throw new IllegalStateException("No Velocity proxy instance available"); } CompletableFuture pingFuture = new CompletableFuture<>(); - server.createBootstrap() + server.createBootstrap(loop) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { @@ -86,8 +99,8 @@ public class VelocityRegisteredServer implements RegisteredServer { 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)); + conn.setSessionHandler(new PingSessionHandler( + pingFuture, VelocityRegisteredServer.this, conn, version)); } else { pingFuture.completeExceptionally(future.cause()); } From d8fb7c11e77c1fdf06aeb40da6fbd63194f455ce Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 6 Aug 2019 03:16:24 -0400 Subject: [PATCH 25/37] Clean up slightly, don't use streams --- .../client/StatusSessionHandler.java | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) 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 68403374c..12d54e06a 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 @@ -24,19 +24,18 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; public class StatusSessionHandler implements MinecraftSessionHandler { private final VelocityServer server; private final MinecraftConnection connection; - private final InboundConnection inboundWrapper; + private final InboundConnection inbound; StatusSessionHandler(VelocityServer server, MinecraftConnection connection, - InboundConnection inboundWrapper) { + InboundConnection inbound) { this.server = server; this.connection = connection; - this.inboundWrapper = inboundWrapper; + this.inbound = inbound; } private ServerPing constructLocalPing(ProtocolVersion version) { @@ -52,20 +51,6 @@ public class StatusSessionHandler implements MinecraftSessionHandler { ); } - private CompletableFuture createInitialPing() { - VelocityConfiguration configuration = server.getConfiguration(); - ProtocolVersion shownVersion = ProtocolVersion.isSupported(connection.getProtocolVersion()) - ? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION; - - PingPassthroughMode passthrough = configuration.getPingPassthrough(); - if (passthrough == PingPassthroughMode.DISABLED) { - return CompletableFuture.completedFuture(constructLocalPing(shownVersion)); - } else { - return attemptPingPassthrough(configuration.getPingPassthrough(), - configuration.getAttemptConnectionOrder(), shownVersion); - } - } - private CompletableFuture attemptPingPassthrough(PingPassthroughMode mode, List servers, ProtocolVersion pingingVersion) { ServerPing fallback = constructLocalPing(pingingVersion); @@ -88,20 +73,27 @@ public class StatusSessionHandler implements MinecraftSessionHandler { case ALL: return pingResponses.thenApply(responses -> { // Find the first non-fallback - return responses.stream() - .filter(ping -> ping != fallback) - .findFirst() - .orElse(fallback); + for (ServerPing response : responses) { + if (response == fallback) { + continue; + } + return response; + } + return fallback; }); case MODS: return pingResponses.thenApply(responses -> { - // Find the first non-fallback that contains a non-empty mod list - Optional modInfo = responses.stream() - .filter(ping -> ping != fallback) - .map(ServerPing::getModinfo) - .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) - .findFirst(); - return modInfo.map(mi -> fallback.asBuilder().mods(mi).build()).orElse(fallback); + // Find the first non-fallback that contains a mod list + for (ServerPing response : responses) { + if (response == fallback) { + continue; + } + Optional modInfo = response.getModinfo(); + if (modInfo.isPresent()) { + return fallback.asBuilder().mods(modInfo.get()).build(); + } + } + return fallback; }); default: // Not possible, but covered for completeness. @@ -109,11 +101,24 @@ public class StatusSessionHandler implements MinecraftSessionHandler { } } + private CompletableFuture getInitialPing() { + VelocityConfiguration configuration = server.getConfiguration(); + ProtocolVersion shownVersion = ProtocolVersion.isSupported(connection.getProtocolVersion()) + ? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION; + PingPassthroughMode passthrough = configuration.getPingPassthrough(); + + if (passthrough == PingPassthroughMode.DISABLED) { + return CompletableFuture.completedFuture(constructLocalPing(shownVersion)); + } else { + return attemptPingPassthrough(configuration.getPingPassthrough(), + configuration.getAttemptConnectionOrder(), shownVersion); + } + } + @Override public boolean handle(LegacyPing packet) { - createInitialPing() - .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inboundWrapper, - ping))) + getInitialPing() + .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping))) .thenAcceptAsync(event -> { connection.closeWith(LegacyDisconnect.fromServerPing(event.getPing(), packet.getVersion())); @@ -129,9 +134,8 @@ public class StatusSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(StatusRequest packet) { - createInitialPing() - .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inboundWrapper, - ping))) + getInitialPing() + .thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping))) .thenAcceptAsync( (event) -> { StringBuilder json = new StringBuilder(); From 62c6adb2041b3e483408f429f38cbf3606700dd3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 6 Aug 2019 03:26:55 -0400 Subject: [PATCH 26/37] Consider forced hosts in ping passthrough --- .../proxy/config/VelocityConfiguration.java | 5 +++-- .../proxy/connection/client/StatusSessionHandler.java | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) 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 7891d778c..6d8c24f4d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -93,8 +93,9 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi "- \"disabled\": No pass-through will be done. The velocity.toml and server-icon.png", " will determine the initial server list ping response.", "- \"mods\": Passes only the mod list from your backend server into the response.", - " This is the recommended replacement for announce-forge = true. If no backend", - " servers can be contacted, Velocity will not display any mod information.", + " The first server in your try list (or forced host) with a mod list will be", + " used. If no backend servers can be contacted, Velocity will not display any", + " mod information.", "- \"all\": Passes everything from the backend server into the response. The Velocity", " configuration is used if no servers could be contacted." }) 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 12d54e06a..613271733 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 @@ -20,7 +20,9 @@ import com.velocitypowered.proxy.protocol.packet.StatusRequest; import com.velocitypowered.proxy.protocol.packet.StatusResponse; 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; @@ -110,8 +112,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler { if (passthrough == PingPassthroughMode.DISABLED) { return CompletableFuture.completedFuture(constructLocalPing(shownVersion)); } else { - return attemptPingPassthrough(configuration.getPingPassthrough(), - configuration.getAttemptConnectionOrder(), shownVersion); + String virtualHostStr = inbound.getVirtualHost().map(InetSocketAddress::getHostString) + .orElse(""); + List serversToTry = server.getConfiguration().getForcedHosts().getOrDefault( + virtualHostStr, server.getConfiguration().getAttemptConnectionOrder()); + return attemptPingPassthrough(configuration.getPingPassthrough(), serversToTry, shownVersion); } } From e3c75a7fcc79e84f115ac94df100f123075b56de Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 6 Aug 2019 06:43:27 -0400 Subject: [PATCH 27/37] Silence TCP_FASTOPEN warnings when not using epoll and fast open enabled --- .../com/velocitypowered/proxy/network/ConnectionManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 208ce766e..6bea37b6e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -104,7 +104,7 @@ public final class ConnectionManager { .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); - if (server.getConfiguration().useTcpFastOpen()) { + if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) { bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3); } @@ -164,7 +164,7 @@ public final class ConnectionManager { this.server.getConfiguration().getConnectTimeout()) .group(group == null ? this.workerGroup : group) .resolver(this.resolverGroup); - if (server.getConfiguration().useTcpFastOpen()) { + if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) { bootstrap.option(EpollChannelOption.TCP_FASTOPEN_CONNECT, true); } return bootstrap; From 195a506117a3c08182c1da52c8a3c14d59f4dfeb Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 7 Aug 2019 15:02:48 -0400 Subject: [PATCH 28/37] Flush consolidation --- .../proxy/connection/backend/VelocityServerConnection.java | 7 ++++++- .../com/velocitypowered/proxy/network/Connections.java | 1 + .../proxy/network/ServerChannelInitializer.java | 7 ++++++- 3 files changed, 13 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 5922238a1..2f0a45333 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 @@ -3,12 +3,14 @@ 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.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 io.netty.handler.flush.FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; @@ -37,6 +39,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.handler.flow.FlowControlHandler; +import io.netty.handler.flush.FlushConsolidationHandler; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -90,7 +93,9 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.CLIENTBOUND)) .addLast(MINECRAFT_ENCODER, - new MinecraftEncoder(ProtocolUtils.Direction.SERVERBOUND)); + new MinecraftEncoder(ProtocolUtils.Direction.SERVERBOUND)) + .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( + DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)); } }) .connect(registeredServer.getServerInfo().getAddress()) 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 33112a089..1ac5635a0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java @@ -15,6 +15,7 @@ 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"; private Connections() { throw new AssertionError(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java index b71aa01e2..f91c8b5eb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java @@ -1,5 +1,6 @@ package com.velocitypowered.proxy.network; +import static com.velocitypowered.proxy.network.Connections.FLUSH_CONSOLIDATION; 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.LEGACY_PING_DECODER; @@ -7,6 +8,7 @@ import static com.velocitypowered.proxy.network.Connections.LEGACY_PING_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.READ_TIMEOUT; +import static io.netty.handler.flush.FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; @@ -22,6 +24,7 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; +import io.netty.handler.flush.FlushConsolidationHandler; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.TimeUnit; @@ -45,7 +48,9 @@ public class ServerChannelInitializer extends ChannelInitializer { .addLast(LEGACY_PING_ENCODER, LegacyPingEncoder.INSTANCE) .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.SERVERBOUND)) - .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolUtils.Direction.CLIENTBOUND)); + .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolUtils.Direction.CLIENTBOUND)) + .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( + DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)); final MinecraftConnection connection = new MinecraftConnection(ch, this.server); connection.setSessionHandler(new HandshakeSessionHandler(connection, this.server)); From 32ef77f9541229d04bdc35c4d5d2ade2ef86b772 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 7 Aug 2019 16:04:34 -0400 Subject: [PATCH 29/37] Flush consolidation tweaks --- .../proxy/connection/backend/VelocityServerConnection.java | 6 +++--- .../proxy/network/ServerChannelInitializer.java | 7 +++---- 2 files changed, 6 insertions(+), 7 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 2f0a45333..c67d32608 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 @@ -84,6 +84,8 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline() + .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( + DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)) .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) @@ -93,9 +95,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.CLIENTBOUND)) .addLast(MINECRAFT_ENCODER, - new MinecraftEncoder(ProtocolUtils.Direction.SERVERBOUND)) - .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( - DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)); + new MinecraftEncoder(ProtocolUtils.Direction.SERVERBOUND)); } }) .connect(registeredServer.getServerInfo().getAddress()) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java index f91c8b5eb..9f54a4e53 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java @@ -14,7 +14,6 @@ import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.netty.LegacyPingDecoder; import com.velocitypowered.proxy.protocol.netty.LegacyPingEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; @@ -40,6 +39,8 @@ public class ServerChannelInitializer extends ChannelInitializer { @Override protected void initChannel(final Channel ch) { ch.pipeline() + .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( + DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)) .addLast(READ_TIMEOUT, new ReadTimeoutHandler(this.server.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) @@ -48,9 +49,7 @@ public class ServerChannelInitializer extends ChannelInitializer { .addLast(LEGACY_PING_ENCODER, LegacyPingEncoder.INSTANCE) .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.SERVERBOUND)) - .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolUtils.Direction.CLIENTBOUND)) - .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( - DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)); + .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolUtils.Direction.CLIENTBOUND)); final MinecraftConnection connection = new MinecraftConnection(ch, this.server); connection.setSessionHandler(new HandshakeSessionHandler(connection, this.server)); From 3b9db94ce9be1982f46be5657e53e9eebc484175 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 7 Aug 2019 16:21:03 -0400 Subject: [PATCH 30/37] Explicitly parse IP addresses before using an unresolved address This allows plugins to more correctly use InetSocketAddress#getAddress(), however "gotchas" remain. --- .../java/com/velocitypowered/proxy/util/AddressUtil.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java index 8874509aa..033babcb9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java @@ -1,6 +1,8 @@ package com.velocitypowered.proxy.util; import com.google.common.base.Preconditions; +import com.google.common.net.InetAddresses; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; @@ -19,7 +21,12 @@ public class AddressUtil { public static InetSocketAddress parseAddress(String ip) { Preconditions.checkNotNull(ip, "ip"); URI uri = URI.create("tcp://" + ip); - return InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()); + try { + InetAddress ia = InetAddresses.forUriString(uri.getHost()); + return new InetSocketAddress(ia, uri.getPort()); + } catch (IllegalArgumentException e) { + return InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()); + } } /** From e476ee815ab7f7c63586f0d2b41b0579d45192f2 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 8 Aug 2019 08:22:37 -0400 Subject: [PATCH 31/37] Revert "Merge branch 'feature/flush-consolidation' into dev/1.1.0" This reverts commit f72f50819d85af81fe3e0944022dc1f3dc613b3d, reversing changes made to 3b9db94ce9be1982f46be5657e53e9eebc484175. --- .../proxy/connection/backend/VelocityServerConnection.java | 5 ----- .../java/com/velocitypowered/proxy/network/Connections.java | 1 - .../proxy/network/ServerChannelInitializer.java | 6 +----- 3 files changed, 1 insertion(+), 11 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 c67d32608..5922238a1 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 @@ -3,14 +3,12 @@ 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.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 io.netty.handler.flush.FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; @@ -39,7 +37,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.handler.flow.FlowControlHandler; -import io.netty.handler.flush.FlushConsolidationHandler; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -84,8 +81,6 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline() - .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( - DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)) .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) 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 1ac5635a0..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,7 +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"; private Connections() { throw new AssertionError(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java index 9f54a4e53..b71aa01e2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ServerChannelInitializer.java @@ -1,6 +1,5 @@ package com.velocitypowered.proxy.network; -import static com.velocitypowered.proxy.network.Connections.FLUSH_CONSOLIDATION; 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.LEGACY_PING_DECODER; @@ -8,12 +7,12 @@ import static com.velocitypowered.proxy.network.Connections.LEGACY_PING_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.READ_TIMEOUT; -import static io.netty.handler.flush.FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.netty.LegacyPingDecoder; import com.velocitypowered.proxy.protocol.netty.LegacyPingEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; @@ -23,7 +22,6 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; -import io.netty.handler.flush.FlushConsolidationHandler; import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.TimeUnit; @@ -39,8 +37,6 @@ public class ServerChannelInitializer extends ChannelInitializer { @Override protected void initChannel(final Channel ch) { ch.pipeline() - .addLast(FLUSH_CONSOLIDATION, new FlushConsolidationHandler( - DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)) .addLast(READ_TIMEOUT, new ReadTimeoutHandler(this.server.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) From 648c14987234b62f724bf9356d380cf398ea3bd8 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 8 Aug 2019 17:22:38 -0400 Subject: [PATCH 32/37] Fix bug where connect() wouldn't reset in-flight connections --- .../proxy/connection/client/ConnectedPlayer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 97140e06e..19d19d9c7 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 @@ -746,8 +746,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { 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(); } - }) + }, minecraftConnection.eventLoop()) .thenApply(x -> x); } From 661ec3103ac8fd5d51a9dae3e08994fede649024 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 9 Aug 2019 15:38:36 -0400 Subject: [PATCH 33/37] Bump to Guice 4.2.2 for Java 11 plugin support --- api/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/build.gradle b/api/build.gradle index 2a43ac049..a9e466865 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -24,7 +24,7 @@ dependencies { compile "net.kyori:text-serializer-plain:${textVersion}" compile 'com.moandjiezana.toml:toml4j:0.7.2' compile "org.slf4j:slf4j-api:${slf4jVersion}" - compile 'com.google.inject:guice:4.2.0' + compile 'com.google.inject:guice:4.2.2' compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}" compile "org.spongepowered:configurate-hocon:${configurateVersion}" From b3ca066a177d188d803fcb8664692d01c8f4ea7f Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 10 Aug 2019 14:08:45 -0400 Subject: [PATCH 34/37] Lower write water mark to 1MB --- .../com/velocitypowered/proxy/network/ConnectionManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0323d24a3..765af2261 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -34,7 +34,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; public final class ConnectionManager { - private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 21, + private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20, 1 << 21); private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class); private final Map endpoints = new HashMap<>(); From 7f3e7a82c4559fb494a86cd2509c385a76786660 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 11 Aug 2019 20:36:00 -0400 Subject: [PATCH 35/37] Update text 3.0.2 cc @lucko --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5add3e122..8d9b45507 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ allprojects { ext { // dependency versions - textVersion = '3.0.1' + textVersion = '3.0.2' junitVersion = '5.3.0-M1' slf4jVersion = '1.7.25' log4jVersion = '2.11.2' From d68cbbd9060a43a6d602094d5a857140cf8bf219 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 17 Aug 2019 20:12:09 -0400 Subject: [PATCH 36/37] Remove inaccurate "Status" section I'm just one person, I can't keep this stuff updated everywhere. --- README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index c83e79669..4b35a9a02 100644 --- a/README.md +++ b/README.md @@ -34,15 +34,4 @@ Once you've built Velocity, you can copy and run the `-all` JAR from and you can configure it from there. Alternatively, you can get the proxy JAR from the [downloads](https://www.velocitypowered.com/downloads) -page. - -## Status - -Velocity is currently in beta. Production networks are successfully running -Velocity with many hundreds of concurrent players online, but your mileage -may vary. - -Velocity supports Minecraft 1.8-1.14.4. Velocity is best supported with Paper -and SpongeVanilla. Minecraft Forge is fully supported but mod compatibility -may vary. Generally, Velocity will support many mods better than BungeeCord -or Waterfall do but compatibility can not always be ensured. +page. \ No newline at end of file From dbfbd1e2a982343026ff1b25855ff562ee7bfa99 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 17 Aug 2019 20:17:12 -0400 Subject: [PATCH 37/37] Rename ConnectedPlayer#minecraftConnection -> ConnectedPlayer#connection This is consistent with VelocityServerConnection --- .../velocitypowered/proxy/VelocityServer.java | 2 +- .../backend/BackendPlaySessionHandler.java | 5 +- .../backend/TransitionSessionHandler.java | 8 +-- .../backend/VelocityServerConnection.java | 6 +- .../client/ClientPlaySessionHandler.java | 27 ++++---- .../connection/client/ConnectedPlayer.java | 61 +++++++++---------- .../LegacyForgeHandshakeBackendPhase.java | 2 +- .../LegacyForgeHandshakeClientPhase.java | 2 +- .../proxy/util/bossbar/VelocityBossBar.java | 2 +- 9 files changed, 55 insertions(+), 60 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 5b032e180..2f260cce6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -505,7 +505,7 @@ public class VelocityServer implements ProxyServer { Preconditions.checkNotNull(component, "component"); Chat chat = Chat.createClientbound(component); for (ConnectedPlayer player : connectionsByUuid.values()) { - player.getMinecraftConnection().write(chat); + player.getConnection().write(chat); } } 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 571a7323c..b43fc0a2a 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 @@ -5,20 +5,17 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.tree.LiteralCommandNode; import com.velocitypowered.api.event.connection.PluginMessageEvent; -import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; -import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.packet.AvailableCommands; import com.velocitypowered.proxy.protocol.packet.AvailableCommands.ProtocolSuggestionProvider; import com.velocitypowered.proxy.protocol.packet.BossBar; import com.velocitypowered.proxy.protocol.packet.Disconnect; -import com.velocitypowered.proxy.protocol.packet.JoinGame; import com.velocitypowered.proxy.protocol.packet.KeepAlive; import com.velocitypowered.proxy.protocol.packet.PlayerListItem; import com.velocitypowered.proxy.protocol.packet.PluginMessage; @@ -39,7 +36,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) { this.server = server; this.serverConn = serverConn; - this.playerConnection = serverConn.getPlayer().getMinecraftConnection(); + this.playerConnection = serverConn.getPlayer().getConnection(); MinecraftSessionHandler psh = playerConnection.getSessionHandler(); if (!(psh instanceof ClientPlaySessionHandler)) { 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 b8d7ed75f..1c6a0b05b 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 @@ -84,13 +84,13 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { .whenCompleteAsync((x, error) -> { // Strap on the ClientPlaySessionHandler if required. ClientPlaySessionHandler playHandler; - if (serverConn.getPlayer().getMinecraftConnection().getSessionHandler() + if (serverConn.getPlayer().getConnection().getSessionHandler() instanceof ClientPlaySessionHandler) { - playHandler = (ClientPlaySessionHandler) serverConn.getPlayer().getMinecraftConnection() + playHandler = (ClientPlaySessionHandler) serverConn.getPlayer().getConnection() .getSessionHandler(); } else { playHandler = new ClientPlaySessionHandler(server, serverConn.getPlayer()); - serverConn.getPlayer().getMinecraftConnection().setSessionHandler(playHandler); + serverConn.getPlayer().getConnection().setSessionHandler(playHandler); } playHandler.handleBackendJoinGame(packet, serverConn); @@ -167,7 +167,7 @@ public class TransitionSessionHandler implements MinecraftSessionHandler { return true; } - serverConn.getPlayer().getMinecraftConnection().write(packet.retain()); + serverConn.getPlayer().getConnection().write(packet.retain()); return true; } 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 5922238a1..42d06fd89 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 @@ -76,7 +76,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, CompletableFuture result = new CompletableFuture<>(); // Note: we use the event loop for the connection the player is on. This reduces context // switches. - server.createBootstrap(proxyPlayer.getMinecraftConnection().eventLoop()) + server.createBootstrap(proxyPlayer.getConnection().eventLoop()) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { @@ -138,13 +138,13 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode(); // Initiate the handshake. - ProtocolVersion protocolVersion = proxyPlayer.getMinecraftConnection().getNextProtocolVersion(); + ProtocolVersion protocolVersion = proxyPlayer.getConnection().getNextProtocolVersion(); Handshake handshake = new Handshake(); handshake.setNextStatus(StateRegistry.LOGIN_ID); handshake.setProtocolVersion(protocolVersion); if (forwardingMode == PlayerInfoForwarding.LEGACY) { handshake.setServerAddress(createLegacyForwardingAddress()); - } else if (proxyPlayer.getMinecraftConnection().getType() == ConnectionTypes.LEGACY_FORGE) { + } else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) { handshake.setServerAddress(handshake.getServerAddress() + HANDSHAKE_HOSTNAME_TOKEN); } else { handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString()); 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 32a67b755..2ae0d08a1 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 @@ -40,7 +40,6 @@ import java.util.List; import java.util.Optional; import java.util.Queue; import java.util.UUID; -import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; import org.apache.logging.log4j.LogManager; @@ -78,7 +77,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { .getProtocolVersion()); if (!channels.isEmpty()) { PluginMessage register = constructChannelsPacket(player.getProtocolVersion(), channels); - player.getMinecraftConnection().write(register); + player.getConnection().write(register); player.getKnownChannels().addAll(channels); } } @@ -275,7 +274,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { public void writabilityChanged() { VelocityServerConnection serverConn = player.getConnectedServer(); if (serverConn != null) { - boolean writable = player.getMinecraftConnection().getChannel().isWritable(); + boolean writable = player.getConnection().getChannel().isWritable(); MinecraftConnection smc = serverConn.getConnection(); if (smc != null) { smc.setAutoReading(writable); @@ -295,7 +294,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { if (!spawned) { // Nothing special to do with regards to spawning the player spawned = true; - player.getMinecraftConnection().delayedWrite(joinGame); + player.getConnection().delayedWrite(joinGame); // Required for Legacy Forge player.getPhase().onFirstJoin(player); @@ -315,12 +314,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // Most notably, by having the client accept the join game packet, we can work around the need // to perform entity ID rewrites, eliminating potential issues from rewriting packets and // improving compatibility with mods. - player.getMinecraftConnection().delayedWrite(joinGame); + player.getConnection().delayedWrite(joinGame); int tempDim = joinGame.getDimension() == 0 ? -1 : 0; - player.getMinecraftConnection().delayedWrite( + player.getConnection().delayedWrite( new Respawn(tempDim, joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType())); - player.getMinecraftConnection().delayedWrite( + player.getConnection().delayedWrite( new Respawn(joinGame.getDimension(), joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType())); } @@ -331,7 +330,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { BossBar deletePacket = new BossBar(); deletePacket.setUuid(serverBossBar); deletePacket.setAction(BossBar.REMOVE); - player.getMinecraftConnection().delayedWrite(deletePacket); + player.getConnection().delayedWrite(deletePacket); } serverBossBars.clear(); @@ -348,11 +347,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } // Clear any title from the previous server. - player.getMinecraftConnection() + player.getConnection() .delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion())); // Flush everything - player.getMinecraftConnection().flush(); + player.getConnection().flush(); serverMc.flush(); destination.completeJoin(); } @@ -410,7 +409,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { resp.setLength(length); resp.getOffers().addAll(offers); - player.getMinecraftConnection().write(resp); + player.getConnection().write(resp); return true; } @@ -450,7 +449,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { response.getOffers().add(new Offer(offer, null)); } response.getOffers().sort(null); - player.getMinecraftConnection().write(response); + player.getConnection().write(response); } catch (Exception e) { logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(), @@ -469,8 +468,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { for (String s : e.getSuggestions()) { response.getOffers().add(new Offer(s)); } - player.getMinecraftConnection().write(response); - }, player.getMinecraftConnection().eventLoop()); + player.getConnection().write(response); + }, player.getConnection().eventLoop()); } /** 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 19d19d9c7..83c6c86a6 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 @@ -58,7 +58,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import net.kyori.text.Component; import net.kyori.text.TextComponent; @@ -84,7 +83,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { /** * The actual Minecraft connection. This is actually a wrapper object around the Netty channel. */ - private final MinecraftConnection minecraftConnection; + private final MinecraftConnection connection; private final @Nullable InetSocketAddress virtualHost; private GameProfile profile; private PermissionFunction permissionFunction; @@ -103,14 +102,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { private @MonotonicNonNull List serversToTry = null; ConnectedPlayer(VelocityServer server, GameProfile profile, - MinecraftConnection minecraftConnection, @Nullable InetSocketAddress virtualHost) { + MinecraftConnection connection, @Nullable InetSocketAddress virtualHost) { this.server = server; - this.tabList = new VelocityTabList(minecraftConnection); + this.tabList = new VelocityTabList(connection); this.profile = profile; - this.minecraftConnection = minecraftConnection; + this.connection = connection; this.virtualHost = virtualHost; this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED; - this.connectionPhase = minecraftConnection.getType().getInitialClientPhase(); + this.connectionPhase = connection.getType().getInitialClientPhase(); this.knownChannels = CappedSet.create(MAX_PLUGIN_CHANNELS); } @@ -134,8 +133,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return profile; } - public MinecraftConnection getMinecraftConnection() { - return minecraftConnection; + public MinecraftConnection getConnection() { + return connection; } @Override @@ -170,7 +169,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) minecraftConnection.getRemoteAddress(); + return (InetSocketAddress) connection.getRemoteAddress(); } @Override @@ -184,12 +183,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public boolean isActive() { - return minecraftConnection.getChannel().isActive(); + return connection.getChannel().isActive(); } @Override public ProtocolVersion getProtocolVersion() { - return minecraftConnection.getProtocolVersion(); + return connection.getProtocolVersion(); } @Override @@ -205,7 +204,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { TitlePacket pkt = new TitlePacket(); pkt.setAction(TitlePacket.SET_ACTION_BAR); pkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(component)); - minecraftConnection.write(pkt); + connection.write(pkt); return; } else { // Due to issues with action bar packets, we'll need to convert the text message into a @@ -221,7 +220,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { Chat chat = new Chat(); chat.setType(pos); chat.setMessage(json); - minecraftConnection.write(chat); + connection.write(chat); } @Override @@ -258,23 +257,23 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { public void disconnect(Component reason) { logger.info("{} has disconnected: {}", this, LegacyComponentSerializer.legacy().serialize(reason)); - minecraftConnection.closeWith(Disconnect.create(reason)); + connection.closeWith(Disconnect.create(reason)); } @Override public void sendTitle(Title title) { Preconditions.checkNotNull(title, "title"); - ProtocolVersion protocolVersion = minecraftConnection.getProtocolVersion(); + ProtocolVersion protocolVersion = connection.getProtocolVersion(); if (title.equals(Titles.reset())) { - minecraftConnection.write(TitlePacket.resetForProtocolVersion(protocolVersion)); + connection.write(TitlePacket.resetForProtocolVersion(protocolVersion)); } else if (title.equals(Titles.hide())) { - minecraftConnection.write(TitlePacket.hideForProtocolVersion(protocolVersion)); + connection.write(TitlePacket.hideForProtocolVersion(protocolVersion)); } else if (title instanceof TextTitle) { TextTitle tt = (TextTitle) title; if (tt.isResetBeforeSend()) { - minecraftConnection.delayedWrite(TitlePacket.resetForProtocolVersion(protocolVersion)); + connection.delayedWrite(TitlePacket.resetForProtocolVersion(protocolVersion)); } Optional titleText = tt.getTitle(); @@ -282,7 +281,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_TITLE); titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(titleText.get())); - minecraftConnection.delayedWrite(titlePkt); + connection.delayedWrite(titlePkt); } Optional subtitleText = tt.getSubtitle(); @@ -290,7 +289,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { TitlePacket titlePkt = new TitlePacket(); titlePkt.setAction(TitlePacket.SET_SUBTITLE); titlePkt.setComponent(GsonComponentSerializer.INSTANCE.serialize(subtitleText.get())); - minecraftConnection.delayedWrite(titlePkt); + connection.delayedWrite(titlePkt); } if (tt.areTimesSet()) { @@ -298,9 +297,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { timesPkt.setFadeIn(tt.getFadeIn()); timesPkt.setStay(tt.getStay()); timesPkt.setFadeOut(tt.getFadeOut()); - minecraftConnection.delayedWrite(timesPkt); + connection.delayedWrite(timesPkt); } - minecraftConnection.flush(); + connection.flush(); } else { throw new IllegalArgumentException("Unknown title class " + title.getClass().getName()); } @@ -454,7 +453,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } else { sendMessage(VelocityMessages.MOVED_TO_NEW_SERVER.append(friendlyReason)); } - }, minecraftConnection.eventLoop()); + }, connection.eventLoop()); } else if (event.getResult() instanceof Notify) { Notify res = (Notify) event.getResult(); if (event.kickedDuringServerConnect()) { @@ -466,7 +465,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { // In case someone gets creative, assume we want to disconnect the player. disconnect(friendlyReason); } - }, minecraftConnection.eventLoop()); + }, connection.eventLoop()); } /** @@ -579,7 +578,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(data, "data"); PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data)); - minecraftConnection.write(message); + connection.write(message); return true; } @@ -598,7 +597,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { ResourcePackRequest request = new ResourcePackRequest(); request.setUrl(url); request.setHash(""); - minecraftConnection.write(request); + connection.write(request); } @Override @@ -610,7 +609,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { ResourcePackRequest request = new ResourcePackRequest(); request.setUrl(url); request.setHash(ByteBufUtil.hexDump(hash)); - minecraftConnection.write(request); + connection.write(request); } /** @@ -619,10 +618,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { * ID last sent by the server. */ public void sendKeepAlive() { - if (minecraftConnection.getState() == StateRegistry.PLAY) { + if (connection.getState() == StateRegistry.PLAY) { KeepAlive keepAlive = new KeepAlive(); keepAlive.setRandomId(ThreadLocalRandom.current().nextLong()); - minecraftConnection.write(keepAlive); + connection.write(keepAlive); } } @@ -749,7 +748,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } else if ((status != null && !status.isSuccessful())) { resetInFlightConnection(); } - }, minecraftConnection.eventLoop()) + }, connection.eventLoop()) .thenApply(x -> x); } @@ -782,7 +781,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { // The only remaining value is successful (no need to do anything!) break; } - }, minecraftConnection.eventLoop()) + }, connection.eventLoop()) .thenApply(Result::isSuccessful); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java index 1c8594a22..bf44814aa 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java @@ -114,7 +114,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase { serverConnection.setConnectionPhase(newPhase); // Write the packet to the player, we don't need it now. - player.getMinecraftConnection().write(message.retain()); + player.getConnection().write(message.retain()); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java index f75dc04b7..b394cb137 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java @@ -135,7 +135,7 @@ public enum LegacyForgeHandshakeClientPhase implements ClientConnectionPhase { COMPLETE(null) { @Override public void resetConnectionPhase(ConnectedPlayer player) { - player.getMinecraftConnection().write(LegacyForgeUtil.resetPacket()); + player.getConnection().write(LegacyForgeUtil.resetPacket()); player.setPhase(LegacyForgeHandshakeClientPhase.NOT_STARTED); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/VelocityBossBar.java b/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/VelocityBossBar.java index 98dc81d50..6b4d9b01a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/VelocityBossBar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/bossbar/VelocityBossBar.java @@ -271,6 +271,6 @@ public class VelocityBossBar implements com.velocitypowered.api.util.bossbar.Bos private void sendPacket(Player player, MinecraftPacket packet) { ConnectedPlayer connected = (ConnectedPlayer) player; - connected.getMinecraftConnection().write(packet); + connected.getConnection().write(packet); } }