diff --git a/build.gradle b/build.gradle index ffb2b57..3aadc55 100644 --- a/build.gradle +++ b/build.gradle @@ -13,13 +13,16 @@ dependencies { mappings(loom.officialMojangMappings()) modImplementation("net.fabricmc:fabric-loader:${loader_version}") - implementation("io.netty.incubator:netty-incubator-codec-classes-quic:0.0.49.Final") + include(implementation("io.netty.incubator:netty-incubator-codec-classes-quic:0.0.49.Final")) + + for (def classifier : ["linux-aarch_64", "linux-x86_64", "osx-aarch_64", "osx-x86_64", "windows-x86_64"]) { + include("io.netty.incubator:netty-incubator-codec-native-quic:0.0.49.Final:${classifier}") + } + runtimeOnly("io.netty.incubator:netty-incubator-codec-native-quic:0.0.49.Final:linux-x86_64") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0") - - testImplementation("com.google.code.gson:gson:2.10.1") } java { diff --git a/src/main/java/me/ramidzkh/qc/QuicConnect.java b/src/main/java/me/ramidzkh/qc/QuicConnect.java index 45f4a1c..2b82bb6 100644 --- a/src/main/java/me/ramidzkh/qc/QuicConnect.java +++ b/src/main/java/me/ramidzkh/qc/QuicConnect.java @@ -3,4 +3,6 @@ public interface QuicConnect { String APPLICATION_NAME = "minecraft quic connect/0.0.1"; + + long IDLE_TIMEOUT_SECONDS = 10; } diff --git a/src/main/java/me/ramidzkh/qc/client/QuicConnection.java b/src/main/java/me/ramidzkh/qc/client/QuicConnection.java index 0c7ac1f..b41d28e 100644 --- a/src/main/java/me/ramidzkh/qc/client/QuicConnection.java +++ b/src/main/java/me/ramidzkh/qc/client/QuicConnection.java @@ -24,7 +24,7 @@ public class QuicConnection { public static ChannelFuture connect(InetSocketAddress address, boolean useNativeTransport, Connection connection) throws ExecutionException, InterruptedException { - useNativeTransport &= Epoll.isAvailable(); + useNativeTransport &= false && Epoll.isAvailable(); var context = QuicSslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) @@ -33,7 +33,7 @@ public static ChannelFuture connect(InetSocketAddress address, boolean useNative var codec = new QuicClientCodecBuilder() .sslContext(context) - .maxIdleTimeout(5 /* 30 */, TimeUnit.SECONDS) + .maxIdleTimeout(QuicConnect.IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) .initialMaxData(10000000) // As we don't want to support remote initiated streams just setup the limit for local initiated streams .initialMaxStreamDataBidirectionalLocal(1000000) @@ -63,7 +63,7 @@ public void channelActive(@NotNull ChannelHandlerContext ctx) { @Override protected void initChannel(@NotNull Channel channel) { ((ConnectionAccessor) connection).setEncrypted(true); - ChannelPipeline pipeline = channel.pipeline(); + var pipeline = channel.pipeline(); Connection.configureSerialization(pipeline, PacketFlow.CLIENTBOUND); pipeline.addLast("packet_handler", connection); } diff --git a/src/main/java/me/ramidzkh/qc/mixin/ConnectionAccessor.java b/src/main/java/me/ramidzkh/qc/mixin/ConnectionAccessor.java index dc385c7..b25cc0c 100644 --- a/src/main/java/me/ramidzkh/qc/mixin/ConnectionAccessor.java +++ b/src/main/java/me/ramidzkh/qc/mixin/ConnectionAccessor.java @@ -1,12 +1,21 @@ package me.ramidzkh.qc.mixin; +import io.netty.channel.Channel; import net.minecraft.network.Connection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import java.net.SocketAddress; + @Mixin(Connection.class) public interface ConnectionAccessor { + @Accessor + Channel getChannel(); + + @Accessor + void setAddress(SocketAddress address); + @Accessor void setEncrypted(boolean encrypted); } diff --git a/src/main/java/me/ramidzkh/qc/mixin/ConnectionMixin.java b/src/main/java/me/ramidzkh/qc/mixin/ConnectionMixin.java index aefd6a6..599ae51 100644 --- a/src/main/java/me/ramidzkh/qc/mixin/ConnectionMixin.java +++ b/src/main/java/me/ramidzkh/qc/mixin/ConnectionMixin.java @@ -16,6 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.concurrent.ExecutionException; @Mixin(Connection.class) @@ -39,6 +40,10 @@ private static void onConnect(InetSocketAddress address, boolean useNativeTransp } } + @Redirect(method = "channelActive", at = @At(value = "FIELD", target = "Lnet/minecraft/network/Connection;address:Ljava/net/SocketAddress;")) + private void dropSetAddress(Connection instance, SocketAddress value) { + } + @Redirect(method = "disconnect", at = @At(value = "FIELD", target = "Lnet/minecraft/network/Connection;channel:Lio/netty/channel/Channel;")) private Channel getChannelForDisconnect(Connection self) { if (channel instanceof QuicStreamChannel quic) { diff --git a/src/main/java/me/ramidzkh/qc/mixin/server/DedicatedServerMixin.java b/src/main/java/me/ramidzkh/qc/mixin/server/DedicatedServerMixin.java index 3c7edc7..d69a8d9 100644 --- a/src/main/java/me/ramidzkh/qc/mixin/server/DedicatedServerMixin.java +++ b/src/main/java/me/ramidzkh/qc/mixin/server/DedicatedServerMixin.java @@ -42,7 +42,7 @@ public DedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess @Inject(method = "initServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerConnectionListener;startTcpServerListener(Ljava/net/InetAddress;I)V", shift = At.Shift.AFTER)) private void openQuic(CallbackInfoReturnable callbackInfoReturnable) throws IOException { - int port = ((ExtraServerProperties) getProperties()).getQuicPort(); + var port = ((ExtraServerProperties) getProperties()).getQuicPort(); if (port != -1) { LOGGER.info("Starting Quic bind on {}:{}", getLocalIp().isEmpty() ? "*" : getLocalIp(), port); diff --git a/src/main/java/me/ramidzkh/qc/server/QuicServerConnectionListener.java b/src/main/java/me/ramidzkh/qc/server/QuicServerConnectionListener.java index 87b204d..f94e01b 100644 --- a/src/main/java/me/ramidzkh/qc/server/QuicServerConnectionListener.java +++ b/src/main/java/me/ramidzkh/qc/server/QuicServerConnectionListener.java @@ -1,10 +1,12 @@ package me.ramidzkh.qc.server; +import com.mojang.logging.LogUtils; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; +import io.netty.incubator.codec.quic.QuicConnectionEvent; import io.netty.incubator.codec.quic.QuicServerCodecBuilder; import io.netty.incubator.codec.quic.QuicSslContextBuilder; import me.ramidzkh.qc.QuicConnect; @@ -19,18 +21,23 @@ import net.minecraft.server.network.ServerHandshakePacketListenerImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.List; +import java.util.WeakHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; public class QuicServerConnectionListener { + private static final Logger LOGGER = LogUtils.getLogger(); + public static void startQuicServerListener(MinecraftServer server, List channels, List connections, @Nullable InetAddress address, int port) { - var useNativeTransport = Epoll.isAvailable() && server.isEpollEnabled(); + var useNativeTransport = false && Epoll.isAvailable() && server.isEpollEnabled(); var config = FabricLoader.getInstance().getConfigDir().resolve("quic-connect"); var keyFile = config.resolve("key.pem"); @@ -40,9 +47,11 @@ public static void startQuicServerListener(MinecraftServer server, List(); + var codec = new QuicServerCodecBuilder() .sslContext(context) - .maxIdleTimeout(5, TimeUnit.SECONDS) + .maxIdleTimeout(QuicConnect.IDLE_TIMEOUT_SECONDS, TimeUnit.SECONDS) // Configure some limits for the maximal number of streams (and the data) that we want to handle. .initialMaxData(10000000) .initialMaxStreamDataBidirectionalLocal(1000000) @@ -50,18 +59,57 @@ public static void startQuicServerListener(MinecraftServer server, List() { @Override protected void initChannel(@NotNull Channel channel) { - ChannelPipeline pipeline = channel.pipeline(); + var pipeline = channel.pipeline(); Connection.configureSerialization(pipeline, PacketFlow.SERVERBOUND); - int pps = server.getRateLimitPacketsPerSecond(); - Connection connection = pps > 0 ? new RateKickingConnection(pps) + var pps = server.getRateLimitPacketsPerSecond(); + var connection = pps > 0 ? new RateKickingConnection(pps) : new Connection(PacketFlow.SERVERBOUND); ((ConnectionAccessor) connection).setEncrypted(true); connections.add(connection); pipeline.addLast("packet_handler", connection); connection.setListener(new ServerHandshakePacketListenerImpl(server, connection)); + + var address = inheritAddresses.get(channel.parent()); + + if (address != null) { + ((ConnectionAccessor) connection).setAddress(address); + } } }) .build(); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 71e8ca0..3418ae7 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -4,7 +4,7 @@ "version": "${version}", "mixins": ["quic-connect.mixins.json"], "depends": { - "minecraft": ">=1.19.4" + "minecraft": ">=1.20.1" }, "name": "Quic Connect", "description": "Connect to Minecraft servers using QUIC",