Skip to content

fix: mc server ping error#2691

Open
pynickle wants to merge 10 commits intodevfrom
fix/2518
Open

fix: mc server ping error#2691
pynickle wants to merge 10 commits intodevfrom
fix/2518

Conversation

@pynickle
Copy link
Copy Markdown
Contributor

@pynickle pynickle commented Apr 9, 2026

修复:

  1. 响应解析改为按 Minecraft 状态协议正确拆包,而不是直接把返回流当作 JSON。(参考 Java版网络协议 中协议查看器部分)

额外更改:

  1. 补充 Legacy MC Ping 的 timeout 逻辑
  2. 请求流程除了 handshake 和 status 额外加入 ping 包,并使用 ping 包计算 latency。(同上参考)

Close #2518

Partly Generated with GPT 5.4
Edited By ENC_Euphony

Summary by Sourcery

通过正确封装状态响应并添加符合协议的 ping/延迟处理,修复了现代版 Minecraft 服务器 ping。

Bug Fixes(错误修复):

  • 修正现代版 Minecraft 状态响应解析逻辑,从读取封装的协议数据包入手,而不是将原始数据流直接视为 JSON。
  • 确保旧版(legacy)Minecraft ping 在连接和读取操作时遵守已配置的超时时间。

Enhancements(增强改进):

  • 添加符合协议的 ping 请求/响应处理,通过 ping/pong 往返来测量延迟。
  • 引入结构化的已解析服务器地址类型,以同时保留原始主机名和解析后的 IP,并更新调用方在创建 ping 服务时使用该类型。
  • 扩展 McPingService 及其工厂方法,增加同时接受主机名和已解析端点的重载,以实现更精确的协议握手。
Original summary in English

Summary by Sourcery

Fix modern Minecraft server ping by correctly framing status responses and adding protocol-compliant ping/latency handling.

Bug Fixes:

  • Correct modern Minecraft status response parsing to read framed protocol packets instead of treating the raw stream as JSON.
  • Ensure legacy Minecraft ping respects configured timeouts for connect and read operations.

Enhancements:

  • Add protocol-compliant ping request/response handling to measure latency using ping/pong round trips.
  • Introduce a structured resolved server address type that preserves the original host and resolved IP and update callers to use it when creating ping services.
  • Extend McPingService and its factory with overloads that accept both hostnames and resolved endpoints for more accurate protocol handshakes.

Bug 修复:

  • 修正现代 Minecraft 状态响应处理方式,通过读取带帧的报文包而不是将原始数据流直接视为 JSON。
  • 确保旧版 Minecraft ping 在连接和从服务器读取数据时遵守配置的超时时间。

增强内容:

  • 增加符合协议的 ping 数据包处理逻辑,通过 ping/pong 往返来测量服务器延迟。
  • 引入结构化的已解析服务器地址类型,保留主机名和解析后的 IP,并更新相关调用方在创建 ping 服务时使用该类型。
  • 添加 McPingService 工厂方法和构造函数重载,使其可以同时接受原始主机名和解析后的终结点,以实现更准确的协议握手。
Original summary in English

Summary by Sourcery

通过正确封装状态响应并添加符合协议的 ping/延迟处理,修复了现代版 Minecraft 服务器 ping。

Bug Fixes(错误修复):

  • 修正现代版 Minecraft 状态响应解析逻辑,从读取封装的协议数据包入手,而不是将原始数据流直接视为 JSON。
  • 确保旧版(legacy)Minecraft ping 在连接和读取操作时遵守已配置的超时时间。

Enhancements(增强改进):

  • 添加符合协议的 ping 请求/响应处理,通过 ping/pong 往返来测量延迟。
  • 引入结构化的已解析服务器地址类型,以同时保留原始主机名和解析后的 IP,并更新调用方在创建 ping 服务时使用该类型。
  • 扩展 McPingService 及其工厂方法,增加同时接受主机名和已解析端点的重载,以实现更精确的协议握手。
Original summary in English

Summary by Sourcery

Fix modern Minecraft server ping by correctly framing status responses and adding protocol-compliant ping/latency handling.

Bug Fixes:

  • Correct modern Minecraft status response parsing to read framed protocol packets instead of treating the raw stream as JSON.
  • Ensure legacy Minecraft ping respects configured timeouts for connect and read operations.

Enhancements:

  • Add protocol-compliant ping request/response handling to measure latency using ping/pong round trips.
  • Introduce a structured resolved server address type that preserves the original host and resolved IP and update callers to use it when creating ping services.
  • Extend McPingService and its factory with overloads that accept both hostnames and resolved endpoints for more accurate protocol handshakes.

@pcl-ce-automation pcl-ce-automation bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: L PR 大小评估:大型 labels Apr 9, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 9, 2026

Reviewer's Guide

重构现代版和传统版的 Minecraft Ping 处理逻辑,使其遵循官方分帧协议,增加显式的 ping/pong 延迟测量,并将新的“已解析服务器地址”类型(主机名 + 解析后的 IP + 端口)贯穿到 ping 工厂以及所有 UI 调用方。

现代 Minecraft ping 握手 / 状态 / ping 流程时序图

sequenceDiagram
    participant UIControl
    participant ServerAddressResolver
    participant McPingServiceFactory
    participant McPingService
    participant MinecraftServer

    UIControl->>ServerAddressResolver: GetResolvedServerAddressAsync(address)
    ServerAddressResolver-->>UIControl: ResolvedServerAddress(host, ip, port)

    UIControl->>McPingServiceFactory: CreateService(host, ip, port)
    McPingServiceFactory-->>UIControl: McPingService

    UIControl->>McPingService: PingAsync(cancellationToken)
    activate McPingService
    McPingService->>MinecraftServer: TCP_Connect(endpoint)
    McPingService->>MinecraftServer: Send_HandshakePacket(host, port)
    McPingService->>MinecraftServer: Send_StatusRequestPacket()
    McPingService->>MinecraftServer: Send_PingRequestPacket(timestamp)

    loop Read_framed_packets
        MinecraftServer-->>McPingService: Status_or_Pong_packet
        McPingService->>McPingService: _ReadStatusPayloadAsync(stream, cancellationToken)
        alt packetId_0_status
            McPingService->>McPingService: Store_statusPayload
        else packetId_1_pong
            McPingService->>McPingService: Compute_latency(now - pongTimestamp)
        else unexpected_packet
            McPingService->>McPingService: Ignore_packet
        end
    end

    McPingService-->>UIControl: McPingResult(statusJson, latency)
    deactivate McPingService
Loading

更新后的 McPingService、McPingServiceFactory 与 ServerAddressResolver 类图

classDiagram
    class McPingService {
        -string _host
        -IPEndPoint _endpoint
        -int _timeout
        +McPingService(string ip, int port, int timeout)
        +McPingService(string host, IPEndPoint endpoint, int timeout)
        +Task~McPingResult~ PingAsync(CancellationToken cancellationToken)
        -byte[] _BuildHandshakePacket(string host, int port)
        -byte[] _BuildStatusRequestPacket()
        -byte[] _BuildPingRequestPacket(long timestamp)
        -Task~Tuple~byte[], long~~ _ReadStatusPayloadAsync(Stream stream, CancellationToken cancellationToken)
        -long _ReadInt64BigEndian(byte[] data)
        -Task~byte[]~ _ReadExactAsync(Stream stream, int length, CancellationToken cancellationToken)
    }

    class McPingServiceFactory {
        +IMcPingService CreateService(string ip, int port, int timeout)
        +IMcPingService CreateService(string host, string ip, int port)
        +IMcPingService CreateService(string host, string ip, int port, int timeout)
        +IMcPingService CreateService(string host, IPEndPoint endpoint)
        +IMcPingService CreateService(string host, IPEndPoint endpoint, int timeout)
        +IMcPingService CreateLegacyService(string ip, int port, int timeout)
    }

    class LegacyMcPingService {
        -string _ip
        -IPEndPoint _endpoint
        -int _timeout
        +LegacyMcPingService(string ip, int port, int timeout)
        +Task~McPingResult~ PingAsync(CancellationToken cancellationToken)
    }

    class ServerAddressResolver {
        <<static>>
        +Task~Tuple~string, int~~ GetReachableAddressAsync(string address, CancellationToken cancelToken)
        +Task~ResolvedServerAddress~ GetResolvedServerAddressAsync(string address, CancellationToken cancelToken)
    }

    class ResolvedServerAddress {
        +string Host
        +string Ip
        +int Port
    }

    McPingServiceFactory ..> McPingService : creates
    McPingServiceFactory ..> LegacyMcPingService : creates
    ServerAddressResolver ..> ResolvedServerAddress : returns
    ServerAddressResolver ..> McPingServiceFactory : used_by_UI_to_construct_ping_service
Loading

已解析服务器地址与 ping 服务创建流程图

flowchart TD
    UserAction["User triggers server ping in UI"] --> Resolve["ServerAddressResolver.GetResolvedServerAddressAsync(address)"]
    Resolve --> Resolved["ResolvedServerAddress { Host, Ip, Port }"]

    Resolved --> Factory["McPingServiceFactory.CreateService(host, ip, port)"]
    Factory --> Service["McPingService(host, IPEndPoint)"]

    Service --> Ping["PingAsync(cancellationToken)\nHandshake + Status + Ping"]
    Ping --> Result["McPingResult(status, latency)"]
    Result --> UIUpdate["UI displays server MOTD, players, and latency"]
Loading

文件级变更

Change Details Files
现代 ping 现在会按照 Minecraft 协议读取分帧的状态和 ping/pong 数据包,并使用 pong 时间戳来计算延迟,而不是通过计时首个 VarInt 读取并将原始流视为 JSON。
  • 新增接受原始 host 和 IPEndPoint 的 McPingService 构造函数重载,与现有 ip/port 构造函数并存。
  • 用协议感知的 _ReadStatusPayloadAsync 方法替换原先基于 MemoryStream 的临时累积和手动 VarInt 切片,该方法循环读取数据包、解码包 ID,并提取状态 JSON 负载与 pong 数据。
  • 引入 _BuildPingRequestPacket,发送携带大端序 int64 时间戳的 ping 包,并通过 _ReadInt64BigEndian 从 pong 响应中读取时间戳计算延迟。
  • 新增辅助方法 _ReadExactAsync,从流中读取指定精确字节数,如遇提前 EOF 则抛出 EndOfStreamException,并针对缺失状态包或 pong 包给出特定错误信息。
  • 移除基于 Stopwatch 的延迟测量,改为使用 ping/pong 计算出的延迟,并为数据包长度、ID、尾随字节以及被忽略的数据包类型增加额外的 debug/warn 日志。
PCL.Core/Link/McPing/McPingService.cs
引入结构化的“已解析服务器地址”类型,用于同时保留原始 host 与解析后的 IP 和端口,并更新调用方及工厂方法,以 host + endpoint 信息来构造 ping 服务。
  • ServerAddressResolver 中新增 ResolvedServerAddress 记录结构体(Host, Ip, Port),以及返回该类型的新入口 GetResolvedServerAddressAsync
  • 重构 GetReachableAddressAsync,改为调用 GetResolvedServerAddressAsync,并继续为现有调用方返回之前的 (Ip, Port) 元组。
  • 在整个 ServerAddressResolver 中,对现有可达性/解析逻辑进行封装,以构造 ResolvedServerAddress 实例,在插入已解析 IP 或回退地址的同时保留原始 host/域名。
  • 扩展 McPingServiceFactory,新增接受 host + ip + port(可选 timeout)以及 host + IPEndPoint(可选 timeout)的重载,根据 ip 是否能解析为 IPAddress 在 host/port 与 host/endpoint 构造函数之间进行选择。
  • 更新 UI ping 调用点(MinecraftServer.xaml.vbPageInstanceServer.xaml.vb),改用 GetResolvedServerAddressAsync 并将 HostIp 传入新的 McPingServiceFactory.CreateService 重载中。
PCL.Core/Minecraft/ServerAddressResolver.cs
PCL.Core/Link/McPing/McPingServiceFactory.cs
Plain Craft Launcher 2/Controls/MinecraftServer.xaml.vb
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceServer.xaml.vb
传统 Minecraft ping 现在通过关联的取消令牌,为连接和读取操作统一强制执行配置的超时时间。
  • 用“仅包含超时”的 CancellationTokenSource 替换本地 CancellationTokenSource,并将其与调用方的 cancellationToken 链接,以确保超时和外部取消都会关闭套接字。
  • ConnectAsyncNetworkStream.WriteAsyncCopyToAsync 中使用该关联令牌,使整个传统 ping 流程都遵守超时时间。
  • 保留原有在取消时尝试关闭套接字的回调逻辑,但改为监听关联令牌。
PCL.Core/Link/McPing/LegacyMcPingService.cs

与关联 Issue 的对照评估

Issue Objective Addressed Explanation
#2518 修复百宝箱中“瞅眼服务器”功能在查询服务器状态时的底层 Ping 逻辑,使其按 Minecraft 协议正确解析状态响应并不再报错。
#2518 确保百宝箱/服务器列表界面在查询服务器状态时,使用正确的服务器地址解析结果创建 McPingService,从而能够正常获取并展示服务器状态。

Tips and commands

Interacting with Sourcery

  • 触发新一轮代码审查: 在 pull request 下评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub Issue: 回复 Sourcery 的审查评论,请求从该评论创建 issue。也可以直接回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题中任何位置写上 @sourcery-ai,即可在任意时间生成标题。也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写上 @sourcery-ai summary,即可在需要的位置生成 PR 摘要。也可以在 pull request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成 Reviewer’s Guide: 在 pull request 中评论 @sourcery-ai guide,即可在任何时候(重新)生成 Reviewer’s Guide。
  • 一次性解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可将所有 Sourcery 评论标记为已解决。如果你已经处理了所有评论且不希望再看到它们,这会非常有用。
  • 撤销所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可撤销所有现有的 Sourcery 审查。如果你想从头开始一次新的审查流程,尤其推荐使用——别忘了随后再评论 @sourcery-ai review 来触发新审查!

自定义你的使用体验

打开你的 控制台(dashboard),可以:

  • 启用或禁用审查功能,例如 Sourcery 生成的 pull request 摘要、Reviewer’s Guide 等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查相关设置。

获取帮助

Original review guide in English

Reviewer's Guide

Refactors modern and legacy Minecraft ping handling to follow the official framed protocol, add explicit ping/pong latency measurement, and thread a new resolved-server-address type (host + resolved IP + port) through the ping factory and UI callers.

Sequence diagram for modern Minecraft ping handshake/status/ping flow

sequenceDiagram
    participant UIControl
    participant ServerAddressResolver
    participant McPingServiceFactory
    participant McPingService
    participant MinecraftServer

    UIControl->>ServerAddressResolver: GetResolvedServerAddressAsync(address)
    ServerAddressResolver-->>UIControl: ResolvedServerAddress(host, ip, port)

    UIControl->>McPingServiceFactory: CreateService(host, ip, port)
    McPingServiceFactory-->>UIControl: McPingService

    UIControl->>McPingService: PingAsync(cancellationToken)
    activate McPingService
    McPingService->>MinecraftServer: TCP_Connect(endpoint)
    McPingService->>MinecraftServer: Send_HandshakePacket(host, port)
    McPingService->>MinecraftServer: Send_StatusRequestPacket()
    McPingService->>MinecraftServer: Send_PingRequestPacket(timestamp)

    loop Read_framed_packets
        MinecraftServer-->>McPingService: Status_or_Pong_packet
        McPingService->>McPingService: _ReadStatusPayloadAsync(stream, cancellationToken)
        alt packetId_0_status
            McPingService->>McPingService: Store_statusPayload
        else packetId_1_pong
            McPingService->>McPingService: Compute_latency(now - pongTimestamp)
        else unexpected_packet
            McPingService->>McPingService: Ignore_packet
        end
    end

    McPingService-->>UIControl: McPingResult(statusJson, latency)
    deactivate McPingService
Loading

Updated class diagram for McPingService, McPingServiceFactory, and ServerAddressResolver

classDiagram
    class McPingService {
        -string _host
        -IPEndPoint _endpoint
        -int _timeout
        +McPingService(string ip, int port, int timeout)
        +McPingService(string host, IPEndPoint endpoint, int timeout)
        +Task~McPingResult~ PingAsync(CancellationToken cancellationToken)
        -byte[] _BuildHandshakePacket(string host, int port)
        -byte[] _BuildStatusRequestPacket()
        -byte[] _BuildPingRequestPacket(long timestamp)
        -Task~Tuple~byte[], long~~ _ReadStatusPayloadAsync(Stream stream, CancellationToken cancellationToken)
        -long _ReadInt64BigEndian(byte[] data)
        -Task~byte[]~ _ReadExactAsync(Stream stream, int length, CancellationToken cancellationToken)
    }

    class McPingServiceFactory {
        +IMcPingService CreateService(string ip, int port, int timeout)
        +IMcPingService CreateService(string host, string ip, int port)
        +IMcPingService CreateService(string host, string ip, int port, int timeout)
        +IMcPingService CreateService(string host, IPEndPoint endpoint)
        +IMcPingService CreateService(string host, IPEndPoint endpoint, int timeout)
        +IMcPingService CreateLegacyService(string ip, int port, int timeout)
    }

    class LegacyMcPingService {
        -string _ip
        -IPEndPoint _endpoint
        -int _timeout
        +LegacyMcPingService(string ip, int port, int timeout)
        +Task~McPingResult~ PingAsync(CancellationToken cancellationToken)
    }

    class ServerAddressResolver {
        <<static>>
        +Task~Tuple~string, int~~ GetReachableAddressAsync(string address, CancellationToken cancelToken)
        +Task~ResolvedServerAddress~ GetResolvedServerAddressAsync(string address, CancellationToken cancelToken)
    }

    class ResolvedServerAddress {
        +string Host
        +string Ip
        +int Port
    }

    McPingServiceFactory ..> McPingService : creates
    McPingServiceFactory ..> LegacyMcPingService : creates
    ServerAddressResolver ..> ResolvedServerAddress : returns
    ServerAddressResolver ..> McPingServiceFactory : used_by_UI_to_construct_ping_service
Loading

Flow diagram for resolved server address and ping service creation

flowchart TD
    UserAction["User triggers server ping in UI"] --> Resolve["ServerAddressResolver.GetResolvedServerAddressAsync(address)"]
    Resolve --> Resolved["ResolvedServerAddress { Host, Ip, Port }"]

    Resolved --> Factory["McPingServiceFactory.CreateService(host, ip, port)"]
    Factory --> Service["McPingService(host, IPEndPoint)"]

    Service --> Ping["PingAsync(cancellationToken)\nHandshake + Status + Ping"]
    Ping --> Result["McPingResult(status, latency)"]
    Result --> UIUpdate["UI displays server MOTD, players, and latency"]
Loading

File-Level Changes

Change Details Files
Modern ping now reads framed status and ping/pong packets according to the Minecraft protocol and uses the pong timestamp to compute latency instead of timing the first VarInt read and treating the raw stream as JSON.
  • Add McPingService constructor overload that accepts original host plus IPEndPoint, alongside the existing ip/port constructor.
  • Replace ad‑hoc MemoryStream accumulation and manual VarInt slicing with a protocol-aware _ReadStatusPayloadAsync method that loops over packets, decodes packet IDs, and extracts the status JSON payload and pong data.
  • Introduce _BuildPingRequestPacket to send a ping packet with a big-endian int64 timestamp and compute latency from the pong response using _ReadInt64BigEndian.
  • Add helper _ReadExactAsync to read an exact number of bytes from the stream and throw EndOfStreamException on premature EOF, with specific error messages for missing status or pong packets.
  • Remove Stopwatch-based latency measurement and use computed latency from ping/pong, and add additional debug/warn logging for packet lengths, IDs, trailing bytes, and ignored packet types.
PCL.Core/Link/McPing/McPingService.cs
Introduce a structured resolved server address type that preserves the original host plus resolved IP and port, and update callers and factory methods to construct ping services with both host and endpoint information.
  • Add ResolvedServerAddress record struct (Host, Ip, Port) in ServerAddressResolver and a new GetResolvedServerAddressAsync entry point that returns it.
  • Refactor GetReachableAddressAsync to call GetResolvedServerAddressAsync and continue returning the previous (Ip, Port) tuple for existing callers.
  • Throughout ServerAddressResolver, wrap existing reachable/resolve logic to construct ResolvedServerAddress instances, preserving the original host/domain while plugging in resolved IPs or fallbacks.
  • Extend McPingServiceFactory with overloads that accept host + ip + port (+ optional timeout) and host + IPEndPoint (+ optional timeout), choosing between host/port and host/endpoint constructors based on whether ip parses as an IPAddress.
  • Update UI ping call sites (MinecraftServer.xaml.vb and PageInstanceServer.xaml.vb) to use GetResolvedServerAddressAsync and pass Host and Ip into the new McPingServiceFactory.CreateService overloads.
PCL.Core/Minecraft/ServerAddressResolver.cs
PCL.Core/Link/McPing/McPingServiceFactory.cs
Plain Craft Launcher 2/Controls/MinecraftServer.xaml.vb
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceServer.xaml.vb
Legacy Minecraft ping now enforces the configured timeout for both connect and read operations using linked cancellation tokens.
  • Replace the local CancellationTokenSource with a timeout-only CancellationTokenSource linked to the caller's cancellationToken, ensuring both elapsed timeout and external cancellation close the socket.
  • Use the linked token for ConnectAsync, NetworkStream.WriteAsync, and CopyToAsync, so the entire legacy ping respects the timeout.
  • Retain the existing cancellation callback that attempts to shutdown and close the socket on cancellation, but wire it to the linked token.
PCL.Core/Link/McPing/LegacyMcPingService.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#2518 修复百宝箱中“瞅眼服务器”功能在查询服务器状态时的底层 Ping 逻辑,使其按 Minecraft 协议正确解析状态响应并不再报错。
#2518 确保百宝箱/服务器列表界面在查询服务器状态时,使用正确的服务器地址解析结果创建 McPingService,从而能够正常获取并展示服务器状态。

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 4 个问题,并给出了一些整体性的反馈:

  • MinecraftServer.xaml.vbPageInstanceServer.xaml.vb 中,如果 Ip 包含主机名(这在某些 ResolvedServerAddress 的回退路径中可能发生),IPAddress.Parse(addr.Ip) 会抛异常;可以考虑要么保证 Ip 始终是实际的 IP 地址,要么使用支持主机名的 Dns/IPEndPoint 构造方式,而不是使用 IPAddress.Parse
  • _ReadStatusPayloadAsync 中,循环被硬编码为最多只读取两个数据包;如果服务器发送额外的数据包(例如插件特有的数据包,或顺序不同),你可能希望放宽这个假设(例如循环直到同时拿到 status 和 pong,或直到超时 / EOF),而不是在遇到未知包类型时立刻抛异常。
给 AI Agent 的提示
请根据这次代码审查中的评论进行修改:

## 总体评论
-`MinecraftServer.xaml.vb``PageInstanceServer.xaml.vb` 中,如果 `Ip` 包含主机名(这在某些 `ResolvedServerAddress` 的回退路径中可能发生),`IPAddress.Parse(addr.Ip)` 会抛异常;可以考虑要么保证 `Ip` 始终是实际的 IP 地址,要么使用支持主机名的 `Dns`/`IPEndPoint` 构造方式,而不是使用 `IPAddress.Parse`-`_ReadStatusPayloadAsync` 中,循环被硬编码为最多只读取两个数据包;如果服务器发送额外的数据包(例如插件特有的数据包,或顺序不同),你可能希望放宽这个假设(例如循环直到同时拿到 status 和 pong,或直到超时 / EOF),而不是在遇到未知包类型时立刻抛异常。

## 逐条评论

### 评论 1
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="202-211" />
<code_context>
+    private async Task<(byte[] StatusPayload, long Latency)> _ReadStatusPayloadAsync(Stream stream, CancellationToken cancellationToken)
</code_context>
<issue_to_address>
**issue:** 当缺少 pong 时返回 0 ms 延迟会产生误导;建议暴露一个独立的状态。

在 `statusPayload` 非空但 `latency` 为 null 的路径中,当前方法返回 `(statusPayload, 0)`,将缺失/无效的 pong 与真实的 0 ms 延迟混为一谈。为避免这种情况,可以考虑在 `latency` 为 null 时抛出异常,将返回值签名改为可空延迟(例如 `long?`),或使用一个有文档说明的哨兵值(例如 `-1`)。
</issue_to_address>

### 评论 2
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="266-267" />
<code_context>
+        return BitConverter.ToInt64(buffer, 0);
+    }
+
+    private static async Task<byte[]> _ReadExactAsync(Stream stream, int length, CancellationToken cancellationToken)
+    {
+        var buffer = new byte[length];
+        var offset = 0;
+        while (offset < length)
+        {
+            var readLength = await stream.ReadAsync(buffer, offset, length - offset, cancellationToken);
+            if (readLength == 0)
+                throw new EndOfStreamException();
+            offset += readLength;
+        }
</code_context>
<issue_to_address>
**suggestion:** 提前结束流读取时抛异常,建议附带更多上下文信息,以方便调试。

目前 `EndOfStreamException` 在抛出时没有消息,也没有关于读取内容的任何细节。为提高生产环境诊断能力,建议附加一条描述性消息,并在可能的情况下包含期望读取的字节数与实际读取的字节数,以便明确是哪个读操作失败以及差了多少字节。

```suggestion
            if (readLength == 0)
                throw new EndOfStreamException(
                    $"Unexpected end of stream while attempting to read {length} bytes; only {offset} bytes were read.");
```
</issue_to_address>

### 评论 3
<location path="PCL.Core/Minecraft/ServerAddressResolver.cs" line_range="20-24" />
<code_context>

 public static class ServerAddressResolver
 {
+    public readonly record struct ResolvedServerAddress(string Host, string Ip, int Port);
+
     // Minecraft Java 默认端口
</code_context>
<issue_to_address>
**issue (bug_risk):** 在某些回退路径中,`Ip` 仍然可能是主机名,这与它的语义以及后续使用方式相冲突。

在最终回退分支中,当 `_ResolveFirstIpAsync` 失败时,`Ip` 可能会被设置为 `idnHost`,但下游现在会调用 `IPAddress.Parse(addr.Ip)`,而这在遇到非字面量地址时会抛异常。请确保要么 `Ip` 始终是字面量 IP(并对需要主机名的调用方作相应调整),要么明确区分并维护独立的 `Host``Ip` 字段,并在回退路径中避免将主机名赋给 `Ip`。
</issue_to_address>

### 评论 4
<location path="PCL.Core/Link/McPing/McPingServiceFactory.cs" line_range="34-36" />
<code_context>
         return new McPingService(ip, port, timeout);
     }

+    public static IMcPingService CreateService(string host, IPEndPoint endpoint, int timeout = 10000)
+    {
+        return new McPingService(host, endpoint, timeout);
+    }
+
</code_context>
<issue_to_address>
**suggestion:** 避免将超时时间硬编码;请复用共享默认值以保持行为一致。

该重载将 `timeout = 10000` 写死,而不是使用现有的共享默认值(例如 `McPingService.DefaultTimeout`)。如果默认值将来发生变化,这个方法的行为会与其他地方不一致。更好的做法是:要么要求调用方显式传入 timeout,要么委托给使用共享默认值的构造函数,以保持行为一致。

建议实现如下:

```csharp
    public static IMcPingService CreateService(string host, IPEndPoint endpoint)
    {
        return new McPingService(host, endpoint, McPingService.DefaultTimeout);
    }

    public static IMcPingService CreateService(string host, IPEndPoint endpoint, int timeout)
    {
        return new McPingService(host, endpoint, timeout);
    }

```

这里假定 `McPingService.DefaultTimeout` 已经存在,并且是代码库中其他位置使用的共享默认值。如果该常量或属性的名称或位置不同,请相应调整引用(例如使用 `McPingOptions.DefaultTimeout` 或类似的共享配置值)。
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的审查有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续审查。
Original comment in English

Hey - I've found 4 issues, and left some high level feedback:

  • In MinecraftServer.xaml.vb and PageInstanceServer.xaml.vb, IPAddress.Parse(addr.Ip) will throw if Ip contains a hostname (which can happen in some ResolvedServerAddress fallbacks); consider either ensuring Ip is always an actual IP, or using Dns/IPEndPoint construction that accepts hostnames instead of IPAddress.Parse.
  • In _ReadStatusPayloadAsync, the loop is hard-coded to read at most two packets; if a server sends additional packets (e.g., plugin-specific or in a different order), you may want to relax this assumption (e.g., loop until both status and pong are found or a timeout/EOF occurs) instead of immediately throwing on an unknown packet type.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `MinecraftServer.xaml.vb` and `PageInstanceServer.xaml.vb`, `IPAddress.Parse(addr.Ip)` will throw if `Ip` contains a hostname (which can happen in some `ResolvedServerAddress` fallbacks); consider either ensuring `Ip` is always an actual IP, or using `Dns`/`IPEndPoint` construction that accepts hostnames instead of `IPAddress.Parse`.
- In `_ReadStatusPayloadAsync`, the loop is hard-coded to read at most two packets; if a server sends additional packets (e.g., plugin-specific or in a different order), you may want to relax this assumption (e.g., loop until both status and pong are found or a timeout/EOF occurs) instead of immediately throwing on an unknown packet type.

## Individual Comments

### Comment 1
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="202-211" />
<code_context>
+    private async Task<(byte[] StatusPayload, long Latency)> _ReadStatusPayloadAsync(Stream stream, CancellationToken cancellationToken)
</code_context>
<issue_to_address>
**issue:** Returning 0 ms latency when pong is missing can be misleading; consider surfacing a distinct state.

In the path where `statusPayload` is non-null but `latency` is null, the method currently returns `(statusPayload, 0)`, conflating a missing/invalid pong with a real 0 ms latency. To avoid this, consider either throwing when `latency` is null, changing the signature to return a nullable latency (e.g. `long?`), or using a documented sentinel value (such as `-1`).
</issue_to_address>

### Comment 2
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="266-267" />
<code_context>
+        return BitConverter.ToInt64(buffer, 0);
+    }
+
+    private static async Task<byte[]> _ReadExactAsync(Stream stream, int length, CancellationToken cancellationToken)
+    {
+        var buffer = new byte[length];
+        var offset = 0;
+        while (offset < length)
+        {
+            var readLength = await stream.ReadAsync(buffer, offset, length - offset, cancellationToken);
+            if (readLength == 0)
+                throw new EndOfStreamException();
+            offset += readLength;
+        }
</code_context>
<issue_to_address>
**suggestion:** Include more context when throwing on premature stream end to ease debugging.

Currently the `EndOfStreamException` is thrown without a message or any details about what was being read. To improve production diagnostics, include a descriptive message and, if possible, the expected vs. actual bytes read so it’s clear which read operation failed and by how much.

```suggestion
            if (readLength == 0)
                throw new EndOfStreamException(
                    $"Unexpected end of stream while attempting to read {length} bytes; only {offset} bytes were read.");
```
</issue_to_address>

### Comment 3
<location path="PCL.Core/Minecraft/ServerAddressResolver.cs" line_range="20-24" />
<code_context>

 public static class ServerAddressResolver
 {
+    public readonly record struct ResolvedServerAddress(string Host, string Ip, int Port);
+
     // Minecraft Java 默认端口
</code_context>
<issue_to_address>
**issue (bug_risk):** In some fallback paths `Ip` may still be a hostname, which conflicts with its semantics and downstream usage.

In the final fallback branch, `Ip` can be set to `idnHost` when `_ResolveFirstIpAsync` fails, but downstream now calls `IPAddress.Parse(addr.Ip)`, which will throw on non-literal addresses. Please either ensure `Ip` is always a literal IP (and adjust callers that need a hostname), or introduce/maintain a separate `Host` vs `Ip` distinction and avoid assigning a hostname to `Ip` in the fallback path.
</issue_to_address>

### Comment 4
<location path="PCL.Core/Link/McPing/McPingServiceFactory.cs" line_range="34-36" />
<code_context>
         return new McPingService(ip, port, timeout);
     }

+    public static IMcPingService CreateService(string host, IPEndPoint endpoint, int timeout = 10000)
+    {
+        return new McPingService(host, endpoint, timeout);
+    }
+
</code_context>
<issue_to_address>
**suggestion:** Avoid hardcoding the timeout value; reuse the shared default to keep behavior consistent.

This overload hardcodes `timeout = 10000` instead of using the existing shared default (e.g. `McPingService.DefaultTimeout`). If the default changes, this method will behave differently. Prefer either requiring callers to pass a timeout explicitly, or delegating to the constructor that uses the shared default so behavior stays aligned.

Suggested implementation:

```csharp
    public static IMcPingService CreateService(string host, IPEndPoint endpoint)
    {
        return new McPingService(host, endpoint, McPingService.DefaultTimeout);
    }

    public static IMcPingService CreateService(string host, IPEndPoint endpoint, int timeout)
    {
        return new McPingService(host, endpoint, timeout);
    }

```

These edits assume that `McPingService.DefaultTimeout` exists and is the shared default used elsewhere in the codebase. If the constant or property has a different name or location, adjust the reference accordingly (e.g., `McPingOptions.DefaultTimeout` or a similar shared configuration value).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PCL.Core/Link/McPing/McPingService.cs Outdated
Comment thread PCL.Core/Link/McPing/McPingService.cs Outdated
Comment thread PCL.Core/Minecraft/ServerAddressResolver.cs Outdated
Comment thread PCL.Core/Link/McPing/McPingServiceFactory.cs Outdated
@MoYuan-CN
Copy link
Copy Markdown
Member

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 3 个问题,并留下了一些高层次的反馈:

  • _ReadStatusPayloadAsync 中,考虑给处理的数据包数量或总字节数增加一个硬性上限,这样即使服务器异常、持续发送非预期数据包,也不能一直让循环运行到外部超时才结束。
  • McPingServiceFactory.CreateService(string host, string? ip, int port = 25565) 中,硬编码的 10000 超时时间与 McPingService.DefaultTimeout 是重复的;使用共享常量可以避免“魔法数字”,并保持各处超时时间一致。
  • LegacyMcPingService.PingAsync 中,你创建了一个链接的 CancellationTokenSource,并在其 token 上注册了回调,但没有释放该注册;将注册包在 using 中,或显式对其进行释放,可以防止在长连接场景下可能出现的处理程序泄漏。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_ReadStatusPayloadAsync`, consider adding a hard upper bound on the number of packets or total bytes processed so a misbehaving server that continuously sends unexpected packets can’t keep the loop running until the external timeout fires.
- In `McPingServiceFactory.CreateService(string host, string? ip, int port = 25565)`, the hardcoded `10000` timeout duplicates `McPingService.DefaultTimeout`; using the shared constant would avoid magic numbers and keep timeouts consistent.
- In `LegacyMcPingService.PingAsync`, you create a linked `CancellationTokenSource` and register a callback on its token but don’t dispose the registration; wrapping the registration in a `using` or explicitly disposing it would prevent potential handler leaks on long-lived sockets.

## Individual Comments

### Comment 1
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="90-97" />
<code_context>

         var handshakePacket = _BuildHandshakePacket(_host, _endpoint.Port);
         var statusPacket = _BuildStatusRequestPacket();
+        var pingTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+        var pingPacket = _BuildPingRequestPacket(pingTimestamp);

</code_context>
<issue_to_address>
**suggestion:** The ping timestamp is captured before handshake/status writes, which can inflate measured latency.

Because the timestamp is taken before sending handshake/status, any delay in writing or flushing those packets gets included in the RTT, overstating ping latency. Move the timestamp capture (and ping packet creation) to just before writing the ping so the measurement tracks only the ping round-trip.

Suggested implementation:

```csharp
        var handshakePacket = _BuildHandshakePacket(_host, _endpoint.Port);
        var statusPacket = _BuildStatusRequestPacket();

```

To fully implement the suggestion, you will also need to:

1. Locate the code where the ping request is sent (e.g., where `pingPacket` is written to the network stream or socket).
2. Just before writing the ping, add:

   ```csharp
   var pingTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
   var pingPacket = _BuildPingRequestPacket(pingTimestamp);
   ```

3. Replace any usage of the old `pingTimestamp`/`pingPacket` (which used to be constructed near the handshake) with these new variables scoped near the send.
4. Ensure that the RTT calculation uses this `pingTimestamp` (captured right before sending) to avoid including handshake/status write delays in the ping latency.
</issue_to_address>

### Comment 2
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="193-200" />
<code_context>

+    private byte[] _BuildPingRequestPacket(long timestamp)
+    {
+        List<byte> pingRequest = [];
+        pingRequest.AddRange(VarIntHelper.Encode(9));
+        pingRequest.AddRange(VarIntHelper.Encode(1));
+        pingRequest.AddRange(BitConverter.GetBytes(timestamp).AsEnumerable().Reverse());
</code_context>
<issue_to_address>
**suggestion:** Avoid hardcoding the packet length `9` in the ping packet to reduce protocol coupling.

The literal `9` matches the current protocol (VarInt packet id 1 + 8‑byte long) but tightly couples the code to that specific layout. Instead, derive the length from the payload, e.g. `var payloadLength = 1 + sizeof(long);` then `VarIntHelper.Encode(payloadLength)`, so changes to the ping payload or id encoding won’t require updating a hardcoded value.

```suggestion
    private byte[] _BuildPingRequestPacket(long timestamp)
    {
        // Packet ID is encoded as a VarInt with value 1, followed by an 8-byte long timestamp.
        // Derive payload length from its components to avoid hardcoding protocol details.
        const int packetIdLength = 1; // VarInt value 1 fits in a single byte
        int payloadLength = packetIdLength + sizeof(long);

        List<byte> pingRequest = [];
        pingRequest.AddRange(VarIntHelper.Encode(payloadLength));
        pingRequest.AddRange(VarIntHelper.Encode(1));
        pingRequest.AddRange(BitConverter.GetBytes(timestamp).AsEnumerable().Reverse());
        return pingRequest.ToArray();
    }
```
</issue_to_address>

### Comment 3
<location path="PCL.Core/Link/McPing/McPingServiceFactory.cs" line_range="34-36" />
<code_context>
         return new McPingService(ip, port, timeout);
     }

+    public static IMcPingService CreateService(string host, string? ip, int port = 25565)
+    {
+        return CreateService(host, ip, port, 10000);
+    }
+
</code_context>
<issue_to_address>
**question (bug_risk):** The new overload defaults to a 10s timeout, which may be inconsistent with other factory methods.

This overload hardcodes `10000` as the timeout, whereas other factory methods rely on the `timeout` parameter or the service’s default. If `DefaultTimeout` (or current callers) use a different value, this introduces inconsistent behavior for callers using this overload. Please consider using the same default mechanism (e.g., a shared constant or existing default) so timeout behavior remains consistent across factory methods.
</issue_to_address>

Sourcery 对开源项目免费使用——如果你觉得这些 Review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进 Review 质量。
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • In _ReadStatusPayloadAsync, consider adding a hard upper bound on the number of packets or total bytes processed so a misbehaving server that continuously sends unexpected packets can’t keep the loop running until the external timeout fires.
  • In McPingServiceFactory.CreateService(string host, string? ip, int port = 25565), the hardcoded 10000 timeout duplicates McPingService.DefaultTimeout; using the shared constant would avoid magic numbers and keep timeouts consistent.
  • In LegacyMcPingService.PingAsync, you create a linked CancellationTokenSource and register a callback on its token but don’t dispose the registration; wrapping the registration in a using or explicitly disposing it would prevent potential handler leaks on long-lived sockets.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_ReadStatusPayloadAsync`, consider adding a hard upper bound on the number of packets or total bytes processed so a misbehaving server that continuously sends unexpected packets can’t keep the loop running until the external timeout fires.
- In `McPingServiceFactory.CreateService(string host, string? ip, int port = 25565)`, the hardcoded `10000` timeout duplicates `McPingService.DefaultTimeout`; using the shared constant would avoid magic numbers and keep timeouts consistent.
- In `LegacyMcPingService.PingAsync`, you create a linked `CancellationTokenSource` and register a callback on its token but don’t dispose the registration; wrapping the registration in a `using` or explicitly disposing it would prevent potential handler leaks on long-lived sockets.

## Individual Comments

### Comment 1
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="90-97" />
<code_context>

         var handshakePacket = _BuildHandshakePacket(_host, _endpoint.Port);
         var statusPacket = _BuildStatusRequestPacket();
+        var pingTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+        var pingPacket = _BuildPingRequestPacket(pingTimestamp);

</code_context>
<issue_to_address>
**suggestion:** The ping timestamp is captured before handshake/status writes, which can inflate measured latency.

Because the timestamp is taken before sending handshake/status, any delay in writing or flushing those packets gets included in the RTT, overstating ping latency. Move the timestamp capture (and ping packet creation) to just before writing the ping so the measurement tracks only the ping round-trip.

Suggested implementation:

```csharp
        var handshakePacket = _BuildHandshakePacket(_host, _endpoint.Port);
        var statusPacket = _BuildStatusRequestPacket();

```

To fully implement the suggestion, you will also need to:

1. Locate the code where the ping request is sent (e.g., where `pingPacket` is written to the network stream or socket).
2. Just before writing the ping, add:

   ```csharp
   var pingTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
   var pingPacket = _BuildPingRequestPacket(pingTimestamp);
   ```

3. Replace any usage of the old `pingTimestamp`/`pingPacket` (which used to be constructed near the handshake) with these new variables scoped near the send.
4. Ensure that the RTT calculation uses this `pingTimestamp` (captured right before sending) to avoid including handshake/status write delays in the ping latency.
</issue_to_address>

### Comment 2
<location path="PCL.Core/Link/McPing/McPingService.cs" line_range="193-200" />
<code_context>

+    private byte[] _BuildPingRequestPacket(long timestamp)
+    {
+        List<byte> pingRequest = [];
+        pingRequest.AddRange(VarIntHelper.Encode(9));
+        pingRequest.AddRange(VarIntHelper.Encode(1));
+        pingRequest.AddRange(BitConverter.GetBytes(timestamp).AsEnumerable().Reverse());
</code_context>
<issue_to_address>
**suggestion:** Avoid hardcoding the packet length `9` in the ping packet to reduce protocol coupling.

The literal `9` matches the current protocol (VarInt packet id 1 + 8‑byte long) but tightly couples the code to that specific layout. Instead, derive the length from the payload, e.g. `var payloadLength = 1 + sizeof(long);` then `VarIntHelper.Encode(payloadLength)`, so changes to the ping payload or id encoding won’t require updating a hardcoded value.

```suggestion
    private byte[] _BuildPingRequestPacket(long timestamp)
    {
        // Packet ID is encoded as a VarInt with value 1, followed by an 8-byte long timestamp.
        // Derive payload length from its components to avoid hardcoding protocol details.
        const int packetIdLength = 1; // VarInt value 1 fits in a single byte
        int payloadLength = packetIdLength + sizeof(long);

        List<byte> pingRequest = [];
        pingRequest.AddRange(VarIntHelper.Encode(payloadLength));
        pingRequest.AddRange(VarIntHelper.Encode(1));
        pingRequest.AddRange(BitConverter.GetBytes(timestamp).AsEnumerable().Reverse());
        return pingRequest.ToArray();
    }
```
</issue_to_address>

### Comment 3
<location path="PCL.Core/Link/McPing/McPingServiceFactory.cs" line_range="34-36" />
<code_context>
         return new McPingService(ip, port, timeout);
     }

+    public static IMcPingService CreateService(string host, string? ip, int port = 25565)
+    {
+        return CreateService(host, ip, port, 10000);
+    }
+
</code_context>
<issue_to_address>
**question (bug_risk):** The new overload defaults to a 10s timeout, which may be inconsistent with other factory methods.

This overload hardcodes `10000` as the timeout, whereas other factory methods rely on the `timeout` parameter or the service’s default. If `DefaultTimeout` (or current callers) use a different value, this introduces inconsistent behavior for callers using this overload. Please consider using the same default mechanism (e.g., a shared constant or existing default) so timeout behavior remains consistent across factory methods.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PCL.Core/Link/McPing/McPingService.cs Outdated
Comment thread PCL.Core/Link/McPing/McPingService.cs
Comment thread PCL.Core/Link/McPing/McPingServiceFactory.cs
@pynickle pynickle changed the title fix: fix mc server ping error fix: mc server ping error Apr 10, 2026
Copy link
Copy Markdown
Contributor

@tangge233 tangge233 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

协议的 JSON 处理没有问题,你真正修复的方式是额外传入 host 字段,在构造握手包的时候传入 host 而不是 ip。

下次请自己先 review AI 更改

Comment thread PCL.Core/Link/McPing/McPingService.cs
Comment thread PCL.Core/Link/McPing/McPingService.cs
Comment thread PCL.Core/Link/McPing/McPingService.cs Outdated
Comment thread PCL.Core/Minecraft/ServerAddressResolver.cs Outdated
@ruattd
Copy link
Copy Markdown
Contributor

ruattd commented Apr 11, 2026

再次提醒,如果你不确保真的看懂了 AI 写了什么就不要浪费 reviewer 的时间了

ruattd
ruattd previously approved these changes Apr 12, 2026
@MoYuan-CN MoYuan-CN requested a review from tangge233 April 12, 2026 01:20
Comment thread PCL.Core/Link/McPing/McPingService.cs Outdated
Comment thread PCL.Core/Link/McPing/McPingService.cs Outdated
Copy link
Copy Markdown
Member

@LinQingYuu LinQingYuu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pynickle pynickle requested review from a team and LinQingYuu April 17, 2026 04:54
Copy link
Copy Markdown
Member

@LinQingYuu LinQingYuu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: L PR 大小评估:大型 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查

Projects

None yet

Development

Successfully merging this pull request may close these issues.

百宝箱无法查询服务器状态

5 participants