diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4fdfa07a0..df4fb54db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,6 +46,7 @@ netty-codec-haproxy = { module = "io.netty:netty-codec-haproxy", version.ref = " netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } netty-handler = { module = "io.netty:netty-handler", version.ref = "netty" } netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" } +netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" } nightconfig = "com.electronwill.night-config:toml:3.6.6" slf4j = "org.slf4j:slf4j-api:2.0.7" snakeyaml = "org.yaml:snakeyaml:1.33" diff --git a/proxy/build.gradle.kts b/proxy/build.gradle.kts index 322888a27..fd7042ecd 100644 --- a/proxy/build.gradle.kts +++ b/proxy/build.gradle.kts @@ -101,6 +101,9 @@ dependencies { implementation(libs.netty.transport.native.epoll) implementation(variantOf(libs.netty.transport.native.epoll) { classifier("linux-x86_64") }) implementation(variantOf(libs.netty.transport.native.epoll) { classifier("linux-aarch_64") }) + implementation(libs.netty.transport.native.kqueue) + implementation(variantOf(libs.netty.transport.native.kqueue) { classifier("osx-x86_64") }) + implementation(variantOf(libs.netty.transport.native.kqueue) { classifier("osx-aarch_64") }) implementation(libs.jopt) implementation(libs.terminalconsoleappender) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index 7374f7985..82342ffe0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -45,7 +45,6 @@ import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; -import net.kyori.adventure.text.Component; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -193,10 +192,7 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(), serverConn.getPlayer().getProtocolVersion())); } else { - // TODO: Change this so its usable for mod loaders - serverConn.disconnect(); - resultFuture.complete(ConnectionRequestResults.forDisconnect( - Component.translatable("multiplayer.disconnect.missing_tags"), serverConn.getServer())); + serverConn.getPlayer().getConnection().write(packet.retain()); } return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java index 83d9fe91b..61f733f0f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java @@ -17,6 +17,7 @@ package com.velocitypowered.proxy.connection.client; +import com.velocitypowered.api.event.player.PlayerClientBrandEvent; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; @@ -25,9 +26,11 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.ClientSettings; import com.velocitypowered.proxy.protocol.packet.KeepAlive; +import com.velocitypowered.proxy.protocol.packet.PingIdentify; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.ResourcePackResponse; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdate; +import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; import java.util.concurrent.CompletableFuture; import net.kyori.adventure.text.Component; @@ -105,6 +108,31 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler { return true; } + @Override + public boolean handle(PluginMessage packet) { + VelocityServerConnection serverConn = player.getConnectionInFlight(); + if (serverConn != null) { + if (PluginMessageUtil.isMcBrand(packet)) { + String brand = PluginMessageUtil.readBrandMessage(packet.content()); + server.getEventManager().fireAndForget(new PlayerClientBrandEvent(player, brand)); + player.setClientBrand(brand); + // Client sends `minecraft:brand` packet immediately after Login, + // but at this time the backend server may not be ready, just discard it. + } else { + serverConn.ensureConnected().write(packet.retain()); + } + } + return true; + } + + @Override + public boolean handle(PingIdentify packet) { + if (player.getConnectionInFlight() != null) { + player.getConnectionInFlight().ensureConnected().write(packet); + } + return true; + } + @Override public void handleGeneric(MinecraftPacket packet) { VelocityServerConnection serverConnection = player.getConnectedServer(); 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 bf596e8e3..6175f82e2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -35,7 +35,6 @@ 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.util.concurrent.GlobalEventExecutor; import java.net.InetSocketAddress; import java.util.HashMap; @@ -123,8 +122,8 @@ public final class ConnectionManager { .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); - if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) { - bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3); + if (server.getConfiguration().useTcpFastOpen()) { + bootstrap.option(ChannelOption.TCP_FASTOPEN, 3); } bootstrap.bind() @@ -186,8 +185,8 @@ public final class ConnectionManager { this.server.getConfiguration().getConnectTimeout()) .group(group == null ? this.workerGroup : group) .resolver(this.resolver.asGroup()); - if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) { - bootstrap.option(EpollChannelOption.TCP_FASTOPEN_CONNECT, true); + if (server.getConfiguration().useTcpFastOpen()) { + bootstrap.option(ChannelOption.TCP_FASTOPEN_CONNECT, true); } return bootstrap; } 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 a7c65fef5..6cbdfd8c5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java @@ -25,6 +25,11 @@ 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; @@ -46,7 +51,11 @@ public enum TransportType { EPOLL("epoll", EpollServerSocketChannel::new, EpollSocketChannel::new, EpollDatagramChannel::new, - (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))); + (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))), + KQUEUE("kqueue", KQueueServerSocketChannel::new, + KQueueSocketChannel::new, + KQueueDatagramChannel::new, + (name, type) -> new KQueueEventLoopGroup(0, createThreadFactory(name, type))); final String name; final ChannelFactory serverSocketChannelFactory; @@ -93,6 +102,10 @@ public enum TransportType { return EPOLL; } + if (KQueue.isAvailable()) { + return KQUEUE; + } + return NIO; }