ServerStatusPinger.java

net.minecraft.client.multiplayer.ServerStatusPinger

信息

  • 全限定名:net.minecraft.client.multiplayer.ServerStatusPinger
  • 类型:public class
  • 包:net.minecraft.client.multiplayer
  • 源码路径:src/main/java/net/minecraft/client/multiplayer/ServerStatusPinger.java
  • 起始行号:L47
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L48
    • 说明:

      TODO

  • CANT_CONNECT_MESSAGE

    • 类型: Component
    • 修饰符: private static final
    • 源码定位: L49
    • 说明:

      TODO

  • DESCRIPTION_SANITIZE_CONTEXT

    • 类型: ResolutionContext
    • 修饰符: private static final
    • 源码定位: L50
    • 说明:

      TODO

  • connections

    • 类型: List<Connection>
    • 修饰符: private final
    • 源码定位: L55
    • 说明:

      TODO

内部类/嵌套类型

构造器

方法

下面的方法块按源码顺序生成。

public void pingServer(ServerData data, Runnable onPersistentDataChange, Runnable onPongResponse, EventLoopGroupHolder eventLoopGroupHolder) @ L57

  • 方法名:pingServer
  • 源码定位:L57
  • 返回类型:void
  • 修饰符:public

参数:

  • data: ServerData
  • onPersistentDataChange: Runnable
  • onPongResponse: Runnable
  • eventLoopGroupHolder: EventLoopGroupHolder

说明:

TODO

private void onPingFailed(Component reason, ServerData data) @ L171

  • 方法名:onPingFailed
  • 源码定位:L171
  • 返回类型:void
  • 修饰符:private

参数:

  • reason: Component
  • data: ServerData

说明:

TODO

private void pingLegacyServer(InetSocketAddress resolvedAddress, ServerAddress rawAddress, ServerData data, EventLoopGroupHolder eventLoopGroupHolder) @ L177

  • 方法名:pingLegacyServer
  • 源码定位:L177
  • 返回类型:void
  • 修饰符:private

参数:

  • resolvedAddress: InetSocketAddress
  • rawAddress: ServerAddress
  • data: ServerData
  • eventLoopGroupHolder: EventLoopGroupHolder

说明:

TODO

public static Component formatPlayerCount(int curPlayers, int maxPlayers) @ L201

  • 方法名:formatPlayerCount
  • 源码定位:L201
  • 返回类型:Component
  • 修饰符:public static

参数:

  • curPlayers: int
  • maxPlayers: int

说明:

TODO

public void tick() @ L207

  • 方法名:tick
  • 源码定位:L207
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void removeAll() @ L223

  • 方法名:removeAll
  • 源码定位:L223
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

代码

@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 static final ResolutionContext DESCRIPTION_SANITIZE_CONTEXT = ResolutionContext.builder()
        .withObjectInfoValidator(description -> !(description instanceof PlayerSprite))
        .setDepthLimit(16)
        .setDepthLimitBehavior(ResolutionContext.LimitBehavior.DISCARD_REMAINING)
        .build();
    private final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
 
    public void pingServer(ServerData data, Runnable onPersistentDataChange, Runnable onPongResponse, EventLoopGroupHolder eventLoopGroupHolder) throws UnknownHostException {
        final ServerAddress rawAddress = ServerAddress.parseString(data.ip);
        Optional<InetSocketAddress> resolvedAddress = ServerNameResolver.DEFAULT.resolveAddress(rawAddress).map(ResolvedServerAddress::asInetSocketAddress);
        if (resolvedAddress.isEmpty()) {
            this.onPingFailed(ConnectScreen.UNKNOWN_HOST_MESSAGE, data);
        } else {
            final InetSocketAddress address = resolvedAddress.get();
            final Connection connection = Connection.connectToServer(address, eventLoopGroupHolder, null);
            this.connections.add(connection);
            data.motd = Component.translatable("multiplayer.status.pinging");
            data.playerList = Collections.emptyList();
            ClientStatusPacketListener listener = new ClientStatusPacketListener() {
                private boolean success;
                private boolean receivedPing;
                private long pingStart;
 
                {
                    Objects.requireNonNull(ServerStatusPinger.this);
                }
 
                @Override
                public void handleStatusResponse(ClientboundStatusResponsePacket packet) {
                    if (this.receivedPing) {
                        connection.disconnect(Component.translatable("multiplayer.status.unrequested"));
                    } else {
                        this.receivedPing = true;
                        ServerStatus status = packet.status();
                        data.motd = sanitizeDescription(status.description());
                        status.version().ifPresentOrElse(version -> {
                            data.version = Component.literal(version.name());
                            data.protocol = version.protocol();
                        }, () -> {
                            data.version = Component.translatable("multiplayer.status.old");
                            data.protocol = 0;
                        });
                        status.players().ifPresentOrElse(players -> {
                            data.status = ServerStatusPinger.formatPlayerCount(players.online(), players.max());
                            data.players = players;
                            if (!players.sample().isEmpty()) {
                                List<Component> playerNames = new ArrayList<>(players.sample().size());
 
                                for (NameAndId profile : players.sample()) {
                                    Component playerName;
                                    if (profile.equals(MinecraftServer.ANONYMOUS_PLAYER_PROFILE)) {
                                        playerName = Component.translatable("multiplayer.status.anonymous_player");
                                    } else {
                                        playerName = Component.literal(profile.name());
                                    }
 
                                    playerNames.add(playerName);
                                }
 
                                if (players.sample().size() < players.online()) {
                                    playerNames.add(Component.translatable("multiplayer.status.and_more", players.online() - players.sample().size()));
                                }
 
                                data.playerList = playerNames;
                            } else {
                                data.playerList = List.of();
                            }
                        }, () -> data.status = Component.translatable("multiplayer.status.unknown").withStyle(ChatFormatting.DARK_GRAY));
                        status.favicon().ifPresent(newIcon -> {
                            if (!Arrays.equals(newIcon.iconBytes(), data.getIconBytes())) {
                                data.setIconBytes(ServerData.validateIcon(newIcon.iconBytes()));
                                onPersistentDataChange.run();
                            }
                        });
                        this.pingStart = Util.getMillis();
                        connection.send(new ServerboundPingRequestPacket(this.pingStart));
                        this.success = true;
                    }
                }
 
                private static Component sanitizeDescription(Component original) {
                    try {
                        return ComponentUtils.resolve(ServerStatusPinger.DESCRIPTION_SANITIZE_CONTEXT, original);
                    } catch (CommandSyntaxException var2) {
                        ServerStatusPinger.LOGGER.warn("Failed to sanitize status {}", original, var2);
                        return Component.empty();
                    }
                }
 
                @Override
                public void handlePongResponse(ClientboundPongResponsePacket packet) {
                    long then = this.pingStart;
                    long now = Util.getMillis();
                    data.ping = now - then;
                    connection.disconnect(Component.translatable("multiplayer.status.finished"));
                    onPongResponse.run();
                }
 
                @Override
                public void onDisconnect(DisconnectionDetails details) {
                    if (!this.success) {
                        ServerStatusPinger.this.onPingFailed(details.reason(), data);
                        ServerStatusPinger.this.pingLegacyServer(address, rawAddress, data, eventLoopGroupHolder);
                    }
                }
 
                @Override
                public boolean isAcceptingMessages() {
                    return connection.isConnected();
                }
            };
 
            try {
                connection.initiateServerboundStatusConnection(rawAddress.getHost(), rawAddress.getPort(), listener);
                connection.send(ServerboundStatusRequestPacket.INSTANCE);
            } catch (Throwable var11) {
                LOGGER.error("Failed to ping server {}", rawAddress, var11);
            }
        }
    }
 
    private void onPingFailed(Component reason, ServerData data) {
        LOGGER.error("Can't ping {}: {}", data.ip, reason.getString());
        data.motd = CANT_CONNECT_MESSAGE;
        data.status = CommonComponents.EMPTY;
    }
 
    private void pingLegacyServer(InetSocketAddress resolvedAddress, ServerAddress rawAddress, ServerData data, EventLoopGroupHolder eventLoopGroupHolder) {
        new Bootstrap().group(eventLoopGroupHolder.eventLoopGroup()).handler(new ChannelInitializer<Channel>() {
            {
                Objects.requireNonNull(ServerStatusPinger.this);
            }
 
            @Override
            protected void initChannel(Channel channel) {
                try {
                    channel.config().setOption(ChannelOption.TCP_NODELAY, true);
                } catch (ChannelException var3) {
                }
 
                channel.pipeline().addLast(new LegacyServerPinger(rawAddress, (protocolVersion, gameVersion, motd, players, maxPlayers) -> {
                    data.setState(ServerData.State.INCOMPATIBLE);
                    data.version = Component.literal(gameVersion);
                    data.motd = Component.literal(motd);
                    data.status = ServerStatusPinger.formatPlayerCount(players, maxPlayers);
                    data.players = new ServerStatus.Players(maxPlayers, players, List.of());
                }));
            }
        }).channel(eventLoopGroupHolder.channelCls()).connect(resolvedAddress.getAddress(), resolvedAddress.getPort());
    }
 
    public static Component formatPlayerCount(int curPlayers, int maxPlayers) {
        Component current = Component.literal(Integer.toString(curPlayers)).withStyle(ChatFormatting.GRAY);
        Component max = Component.literal(Integer.toString(maxPlayers)).withStyle(ChatFormatting.GRAY);
        return Component.translatable("multiplayer.status.player_count", current, max).withStyle(ChatFormatting.DARK_GRAY);
    }
 
    public void tick() {
        synchronized (this.connections) {
            Iterator<Connection> 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<Connection> iterator = this.connections.iterator();
 
            while (iterator.hasNext()) {
                Connection connection = iterator.next();
                if (connection.isConnected()) {
                    iterator.remove();
                    connection.disconnect(Component.translatable("multiplayer.status.cancelled"));
                }
            }
        }
    }
}

引用的其他类

  • LegacyServerPinger

    • 引用位置: 构造调用
    • 关联成员: LegacyServerPinger()
  • ServerData

    • 引用位置: 参数/方法调用
    • 关联成员: ServerData.validateIcon()
  • ServerAddress

    • 引用位置: 参数/方法调用
    • 关联成员: ServerAddress.parseString()
  • Connection

    • 引用位置: 字段/方法调用
    • 关联成员: Connection.connectToServer()
  • Component

    • 引用位置: 参数/字段/方法调用/返回值
    • 关联成员: Component.empty(), Component.literal(), Component.translatable()
  • ComponentUtils

    • 引用位置: 方法调用
    • 关联成员: ComponentUtils.resolve()
  • ResolutionContext

    • 引用位置: 字段/方法调用
    • 关联成员: ResolutionContext.builder()
  • ServerboundPingRequestPacket

    • 引用位置: 构造调用
    • 关联成员: ServerboundPingRequestPacket()
  • ClientStatusPacketListener

    • 引用位置: 构造调用
    • 关联成员: ClientStatusPacketListener()
  • ServerStatus

    • 引用位置: 方法调用/构造调用
    • 关联成员: Players(), ServerStatus.Players()
  • EventLoopGroupHolder

    • 引用位置: 参数
  • Util

    • 引用位置: 方法调用
    • 关联成员: Util.getMillis()