added Version auto detection

This commit is contained in:
FlorianMichael 2023-04-19 20:51:51 +02:00
parent 2dd154203c
commit 5e7e3a954d
4 changed files with 128 additions and 0 deletions

View File

@ -17,15 +17,139 @@
*/
package de.florianmichael.viafabricplus.protocolhack.provider.vialoadingbase;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import de.florianmichael.viafabricplus.ViaFabricPlus;
import de.florianmichael.viafabricplus.protocolhack.ProtocolHack;
import de.florianmichael.viafabricplus.settings.groups.GeneralSettings;
import de.florianmichael.vialoadingbase.ViaLoadingBase;
import de.florianmichael.vialoadingbase.model.ComparableProtocolVersion;
import de.florianmichael.vialoadingbase.provider.VLBBaseVersionProvider;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ServerAddress;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.NetworkSide;
import net.minecraft.network.NetworkState;
import net.minecraft.network.listener.ClientQueryPacketListener;
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
import net.minecraft.network.packet.c2s.query.QueryRequestC2SPacket;
import net.minecraft.network.packet.s2c.query.QueryPongS2CPacket;
import net.minecraft.network.packet.s2c.query.QueryResponseS2CPacket;
import net.minecraft.text.Text;
import net.minecraft.util.Lazy;
import org.jetbrains.annotations.NotNull;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class ViaFabricPlusVLBBaseVersionProvider extends VLBBaseVersionProvider {
// Based on https://github.com/ViaVersion/ViaFabric/blob/main/viafabric-mc119/src/main/java/com/viaversion/fabric/mc119/service/ProtocolAutoDetector.java
private final static LoadingCache<InetSocketAddress, CompletableFuture<ProtocolVersion>> AUTO_DETECTION_CACHE = CacheBuilder.newBuilder().
expireAfterWrite(30, TimeUnit.SECONDS).
build(CacheLoader.from(address -> {
CompletableFuture<ProtocolVersion> future = new CompletableFuture<>();
try {
final ClientConnection clientConnection = new ClientConnection(NetworkSide.CLIENTBOUND);
final boolean useEpoll = Epoll.isAvailable() && MinecraftClient.getInstance().options.shouldUseNativeTransport();
final Class class_ = useEpoll ? EpollSocketChannel.class : NioSocketChannel.class;
final Lazy lazy = useEpoll ? ClientConnection.EPOLL_CLIENT_IO_GROUP : ClientConnection.CLIENT_IO_GROUP;
final ChannelFuture channelFuture = new Bootstrap().group((EventLoopGroup) lazy.get()).handler(new ChannelInitializer<>() {
protected void initChannel(@NotNull Channel channel) {
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
channel.config().setOption(ChannelOption.IP_TOS, 0x18);
} catch (ChannelException ignored) {
}
ChannelPipeline channelPipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30));
ClientConnection.addHandlers(channelPipeline, NetworkSide.CLIENTBOUND);
channelPipeline.addLast("packet_handler", clientConnection);
}
}).channel(class_).connect(address);
channelFuture.addListener(future1 -> {
if (!future1.isSuccess()) {
future.completeExceptionally(future1.cause());
} else {
channelFuture.channel().eventLoop().execute(() -> { // needs to execute after channel init
clientConnection.setPacketListener(new ClientQueryPacketListener() {
@Override
public void onResponse(QueryResponseS2CPacket packet) {
if (packet.metadata() != null && packet.metadata().version().isPresent()) {
final ProtocolVersion version = ViaLoadingBase.fromProtocolId(packet.metadata().version().get().protocolVersion());
future.complete(version);
ViaFabricPlus.LOGGER.info("Auto-detected " + version + " for " + address);
} else {
future.completeExceptionally(new IllegalArgumentException("Null version in query response"));
}
clientConnection.disconnect(Text.empty());
}
@Override
public void onPong(QueryPongS2CPacket packet) {
clientConnection.disconnect(Text.literal("Pong not requested!"));
}
@Override
public void onDisconnected(Text reason) {
future.completeExceptionally(new IllegalStateException(reason.getString()));
}
@Override
public boolean isConnectionOpen() {
return channelFuture.channel().isOpen();
}
});
clientConnection.send(new HandshakeC2SPacket(address.getHostString(), address.getPort(), NetworkState.STATUS));
clientConnection.send(new QueryRequestC2SPacket());
});
}
});
} catch (Throwable throwable) { // You never know...
future.completeExceptionally(throwable);
}
return future;
}));
@Override
public int getClosestServerProtocol(UserConnection connection) throws Exception {
if (connection.isClientSide()) {
if (GeneralSettings.INSTANCE.autoDetectVersion.getValue()) {
final SocketAddress target = connection.getChannel().remoteAddress();
if (target instanceof final InetSocketAddress socketAddress) {
AUTO_DETECTION_CACHE.get(socketAddress).whenComplete((version, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
return;
}
if (version != null) {
final ComparableProtocolVersion remapped = ViaLoadingBase.fromProtocolId(version.getVersion());
if (remapped != null) {
ProtocolHack.getForcedVersions().put(socketAddress, remapped);
}
}
});
}
}
return ProtocolHack.getTargetVersion(connection.getChannel()).getVersion();
}
return super.getClosestServerProtocol(connection);

View File

@ -37,6 +37,7 @@ public class GeneralSettings extends SettingGroup {
public final BooleanSetting showSuperSecretSettings = new BooleanSetting(this, Text.translatable("general.viafabricplus.secret"), true);
public final BooleanSetting showExtraInformationInDebugHud = new BooleanSetting(this, Text.translatable("general.viafabricplus.extrainformation"), true);
public final BooleanSetting showClassicLoadingProgressInConnectScreen = new BooleanSetting(this, Text.translatable("general.viafabricplus.classicloading"), true);
public final BooleanSetting autoDetectVersion = new BooleanSetting(this, Text.translatable("general.viafabricplus.autodetect"), false);
public GeneralSettings() {
super("General");

View File

@ -20,6 +20,7 @@
"general.viafabricplus.creative": "Remove not available items from creative tab",
"general.viafabricplus.classiccommands": "Allow classic protocol command usage",
"general.viafabricplus.protocolsync": "Automatically change Settings based on the current version",
"general.viafabricplus.autodetect": "Auto detect version",
"experimental.viafabricplus.chunkborderfix": "Fix Chunk borders",
"experimental.viafabricplus.watermovement": "Water movement edge detection",

View File

@ -8,5 +8,7 @@ accessible field net/minecraft/client/network/ServerAddress INVALID Lnet/minecra
accessible field net/minecraft/client/network/ServerAddress hostAndPort Lcom/google/common/net/HostAndPort;
accessible field net/minecraft/client/MinecraftClient fontManager Lnet/minecraft/client/font/FontManager;
accessible field net/minecraft/client/font/FontManager fontStorages Ljava/util/Map;
accessible field net/minecraft/network/ClientConnection EPOLL_CLIENT_IO_GROUP Lnet/minecraft/util/Lazy;
accessible field net/minecraft/network/ClientConnection LOCAL_CLIENT_IO_GROUP Lnet/minecraft/util/Lazy;
accessible class net/minecraft/client/gui/screen/GameModeSelectionScreen$GameModeSelection