From 701a7c7fc195e1c15bb75a60e52e29bb56157929 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 30 Oct 2023 14:31:39 -0700 Subject: [PATCH] Improve server ping list response Change the read timeout and connection timeout both to 5s. This is to decrease the time to label servers as offline. Additionally, increase the worker count for pinging servers to 128 from 5. This allows many more server pings to occur at the same time, which allows users to scroll their serverlist. --- .../mixin/serverlist/ConnectionMixin.java | 111 ++++++++++++++++++ .../serverlist/ServerSelectionListMixin.java | 23 ++++ .../serverlist/ServerListConnection.java | 8 ++ src/main/resources/moonrise.mixins.json | 2 + 4 files changed, 144 insertions(+) create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ConnectionMixin.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ServerSelectionListMixin.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/patches/serverlist/ServerListConnection.java diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ConnectionMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ConnectionMixin.java new file mode 100644 index 0000000..7aa9730 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ConnectionMixin.java @@ -0,0 +1,111 @@ +package ca.spottedleaf.moonrise.mixin.serverlist; + +import ca.spottedleaf.moonrise.patches.serverlist.ServerListConnection; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.timeout.ReadTimeoutHandler; +import net.minecraft.client.multiplayer.ServerStatusPinger; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.Packet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.net.InetSocketAddress; + +@Mixin(Connection.class) +public abstract class ConnectionMixin extends SimpleChannelInboundHandler> implements ServerListConnection { + + @Shadow + private Channel channel; + + @Unique + private static final String TIMEOUT_PIPELINE_NAME = "timeout"; + + @Unique + private static final int DEFAULT_TIMEOUT = 30; + + @Unique + private volatile int timeout; + + /** + * @reason Initialise fields during construction + * @author Spottedleaf + */ + @Inject( + method = "", + at = @At( + value = "RETURN" + ) + ) + private void init(final CallbackInfo ci) { + this.timeout = DEFAULT_TIMEOUT; + } + + @Override + public final int moonrise$getReadTimeout() { + return this.timeout; + } + + @Override + public final void moonrise$setReadTimeout(final int seconds) { + if (this.channel != null) { + this.channel.eventLoop().execute(() -> { + this.timeout = seconds; + if (this.channel != null) { + this.channel.pipeline().replace(TIMEOUT_PIPELINE_NAME, TIMEOUT_PIPELINE_NAME, new ReadTimeoutHandler(seconds)); + this.channel.pipeline().channel().config().setOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, seconds * 1000); + } + }); + } else { + this.timeout = seconds; + } + } + + /** + * @reason Used to set the timeout handler for connectToServer + * @author Spottedleaf + */ + @Inject( + method = "configurePacketHandler", + at = @At( + value = "RETURN" + ) + ) + private void delayedTimeout(final ChannelPipeline pipeline, final CallbackInfo ci) { + final int timeout = this.timeout; + if (timeout != DEFAULT_TIMEOUT) { + pipeline.replace(TIMEOUT_PIPELINE_NAME, TIMEOUT_PIPELINE_NAME, new ReadTimeoutHandler(timeout)); + + pipeline.channel().config().setOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout * 1000); + } + } + + /** + * @reason Dirty hack to set the timeout before connecting for server ping list + * @author Spottedleaf + */ + @Redirect( + method = "connectToServer", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/Connection;connect(Ljava/net/InetSocketAddress;ZLnet/minecraft/network/Connection;)Lio/netty/channel/ChannelFuture;" + ) + ) + private static ChannelFuture setReadTimeoutHook(final InetSocketAddress address, final boolean epoll, + final Connection connection) { + if (StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk((s -> s.filter(f -> f.getDeclaringClass() == ServerStatusPinger.class).findAny())).isPresent()) { + final int timeout = 5; + + // reduce timeout to 5s so that non-responding servers release the thread allocation fast + ((ServerListConnection)connection).moonrise$setReadTimeout(timeout); + } + return Connection.connect(address, epoll, connection); + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ServerSelectionListMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ServerSelectionListMixin.java new file mode 100644 index 0000000..93b2eaf --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/serverlist/ServerSelectionListMixin.java @@ -0,0 +1,23 @@ +package ca.spottedleaf.moonrise.mixin.serverlist; + +import net.minecraft.client.gui.screens.multiplayer.ServerSelectionList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +@Mixin(ServerSelectionList.class) +public class ServerSelectionListMixin { + + /** + * @reason Massively increase the threadpool count so that slow servers do not stall the pinging of other servers + * on the status list + * @author Spottedleaf + */ + @ModifyConstant( + method = "", + constant = @Constant(intValue = 5, ordinal = 0) + ) + private static int noPingLimitExecutor(final int constant) { + return 128; + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/serverlist/ServerListConnection.java b/src/main/java/ca/spottedleaf/moonrise/patches/serverlist/ServerListConnection.java new file mode 100644 index 0000000..b041ea4 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/serverlist/ServerListConnection.java @@ -0,0 +1,8 @@ +package ca.spottedleaf.moonrise.patches.serverlist; + +public interface ServerListConnection { + + public int moonrise$getReadTimeout(); + + public void moonrise$setReadTimeout(final int seconds); +} diff --git a/src/main/resources/moonrise.mixins.json b/src/main/resources/moonrise.mixins.json index 0f83592..43473af 100644 --- a/src/main/resources/moonrise.mixins.json +++ b/src/main/resources/moonrise.mixins.json @@ -47,6 +47,7 @@ "poi_lookup.AcquirePoiMixin", "poi_lookup.PoiManagerMixin", "poi_lookup.PortalForcerMixin", + "serverlist.ConnectionMixin", "starlight.blockstate.BlockStateBaseMixin", "starlight.chunk.ChunkAccessMixin", "starlight.chunk.EmptyLevelChunkMixin", @@ -67,6 +68,7 @@ "collisions.LiquidBlockRendererMixin", "collisions.ParticleMixin", "profiler.MinecraftMixin", + "serverlist.ServerSelectionListMixin", "starlight.multiplayer.ClientPacketListenerMixin", "starlight.world.ClientLevelMixin" ],