package net.minecraft.client.multiplayer; import com.google.common.collect.Lists; import com.mojang.authlib.GameProfile; import com.mojang.logging.LogUtils; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.socket.nio.NioSocketChannel; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.client.gui.screens.ConnectScreen; import net.minecraft.client.multiplayer.resolver.ResolvedServerAddress; import net.minecraft.client.multiplayer.resolver.ServerAddress; import net.minecraft.client.multiplayer.resolver.ServerNameResolver; import net.minecraft.network.Connection; import net.minecraft.network.DisconnectionDetails; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.ping.ClientboundPongResponsePacket; import net.minecraft.network.protocol.ping.ServerboundPingRequestPacket; import net.minecraft.network.protocol.status.ClientStatusPacketListener; import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; import net.minecraft.network.protocol.status.ServerStatus; import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.slf4j.Logger; @OnlyIn(Dist.CLIENT) public class ServerStatusPinger { private static final Logger LOGGER = LogUtils.getLogger(); private static final Component CANT_CONNECT_MESSAGE = Component.translatable("multiplayer.status.cannot_connect").withColor(-65536); private final List connections = Collections.synchronizedList(Lists.newArrayList()); public void pingServer(final ServerData p_105460_, final Runnable p_105461_, final Runnable p_335024_) throws UnknownHostException { final ServerAddress serveraddress = ServerAddress.parseString(p_105460_.ip); Optional optional = ServerNameResolver.DEFAULT.resolveAddress(serveraddress).map(ResolvedServerAddress::asInetSocketAddress); if (optional.isEmpty()) { this.onPingFailed(ConnectScreen.UNKNOWN_HOST_MESSAGE, p_105460_); } else { final InetSocketAddress inetsocketaddress = optional.get(); final Connection connection = Connection.connectToServer(inetsocketaddress, false, null); this.connections.add(connection); p_105460_.motd = Component.translatable("multiplayer.status.pinging"); p_105460_.playerList = Collections.emptyList(); ClientStatusPacketListener clientstatuspacketlistener = new ClientStatusPacketListener() { private boolean success; private boolean receivedPing; private long pingStart; @Override public void handleStatusResponse(ClientboundStatusResponsePacket p_105489_) { if (this.receivedPing) { connection.disconnect(Component.translatable("multiplayer.status.unrequested")); } else { this.receivedPing = true; ServerStatus serverstatus = p_105489_.status(); p_105460_.motd = serverstatus.description(); serverstatus.version().ifPresentOrElse(p_273307_ -> { p_105460_.version = Component.literal(p_273307_.name()); p_105460_.protocol = p_273307_.protocol(); }, () -> { p_105460_.version = Component.translatable("multiplayer.status.old"); p_105460_.protocol = 0; }); serverstatus.players().ifPresentOrElse(p_273230_ -> { p_105460_.status = ServerStatusPinger.formatPlayerCount(p_273230_.online(), p_273230_.max()); p_105460_.players = p_273230_; if (!p_273230_.sample().isEmpty()) { List list = new ArrayList<>(p_273230_.sample().size()); for (GameProfile gameprofile : p_273230_.sample()) { list.add(Component.literal(gameprofile.getName())); } if (p_273230_.sample().size() < p_273230_.online()) { list.add(Component.translatable("multiplayer.status.and_more", p_273230_.online() - p_273230_.sample().size())); } p_105460_.playerList = list; } else { p_105460_.playerList = List.of(); } }, () -> p_105460_.status = Component.translatable("multiplayer.status.unknown").withStyle(ChatFormatting.DARK_GRAY)); serverstatus.favicon().ifPresent(p_272704_ -> { if (!Arrays.equals(p_272704_.iconBytes(), p_105460_.getIconBytes())) { p_105460_.setIconBytes(ServerData.validateIcon(p_272704_.iconBytes())); p_105461_.run(); } }); this.pingStart = Util.getMillis(); connection.send(new ServerboundPingRequestPacket(this.pingStart)); this.success = true; } } @Override public void handlePongResponse(ClientboundPongResponsePacket p_329322_) { long i = this.pingStart; long j = Util.getMillis(); p_105460_.ping = j - i; connection.disconnect(Component.translatable("multiplayer.status.finished")); p_335024_.run(); } @Override public void onDisconnect(DisconnectionDetails p_343233_) { if (!this.success) { ServerStatusPinger.this.onPingFailed(p_343233_.reason(), p_105460_); ServerStatusPinger.this.pingLegacyServer(inetsocketaddress, serveraddress, p_105460_); } } @Override public boolean isAcceptingMessages() { return connection.isConnected(); } }; try { connection.initiateServerboundStatusConnection(serveraddress.getHost(), serveraddress.getPort(), clientstatuspacketlistener); connection.send(ServerboundStatusRequestPacket.INSTANCE); } catch (Throwable throwable) { LOGGER.error("Failed to ping server {}", serveraddress, throwable); } } } void onPingFailed(Component p_171815_, ServerData p_171816_) { LOGGER.error("Can't ping {}: {}", p_171816_.ip, p_171815_.getString()); p_171816_.motd = CANT_CONNECT_MESSAGE; p_171816_.status = CommonComponents.EMPTY; } void pingLegacyServer(InetSocketAddress p_171812_, final ServerAddress p_300887_, final ServerData p_171813_) { new Bootstrap().group(Connection.NETWORK_WORKER_GROUP.get()).handler(new ChannelInitializer() { @Override protected void initChannel(Channel p_105498_) { try { p_105498_.config().setOption(ChannelOption.TCP_NODELAY, true); } catch (ChannelException channelexception) { } p_105498_.pipeline().addLast(new LegacyServerPinger(p_300887_, (p_325482_, p_325483_, p_325484_, p_325485_, p_325486_) -> { p_171813_.setState(ServerData.State.INCOMPATIBLE); p_171813_.version = Component.literal(p_325483_); p_171813_.motd = Component.literal(p_325484_); p_171813_.status = ServerStatusPinger.formatPlayerCount(p_325485_, p_325486_); p_171813_.players = new ServerStatus.Players(p_325486_, p_325485_, List.of()); })); } }).channel(NioSocketChannel.class).connect(p_171812_.getAddress(), p_171812_.getPort()); } public static Component formatPlayerCount(int p_105467_, int p_105468_) { Component component = Component.literal(Integer.toString(p_105467_)).withStyle(ChatFormatting.GRAY); Component component1 = Component.literal(Integer.toString(p_105468_)).withStyle(ChatFormatting.GRAY); return Component.translatable("multiplayer.status.player_count", component, component1).withStyle(ChatFormatting.DARK_GRAY); } public void tick() { synchronized (this.connections) { Iterator iterator = this.connections.iterator(); while (iterator.hasNext()) { Connection connection = iterator.next(); if (connection.isConnected()) { connection.tick(); } else { iterator.remove(); connection.handleDisconnection(); } } } } public void removeAll() { synchronized (this.connections) { Iterator iterator = this.connections.iterator(); while (iterator.hasNext()) { Connection connection = iterator.next(); if (connection.isConnected()) { iterator.remove(); connection.disconnect(Component.translatable("multiplayer.status.cancelled")); } } } } }