Files
mtgodot-poc/audit/contracts/network.error-timeout-server-state.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

87 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# network.error_timeout_server_state
## Scope
对比认证失败、连接失败、超时、频道状态检查、服务器状态通知和恢复/回到登录界面的完整分支。
## Reference call chain
- `UserInterface/AccountConnector.cpp`
- `UserInterface/ServerStateChecker.cpp`
- `UserInterface/PythonNetworkStreamPhaseLogin.cpp`
- `EterLib/NetStream.cpp`
## Current call chain
- `extension/src/net/auth_client.h`
- `extension/src/net/classic/classic_session.cpp`
- `project/net/channel_status.gd`
- `project/net/serverinfo.gd`
- `project/app_flow.gd`
- `project/ui/reconnect_ui.gd`
## Branch matrix
| 场景 | 40250 | 当前端 | 结论 |
|---|---|---|---|
| auth 失败 | AccountConnector 消费错误并通知 login UI,断开 auth/game 状态 | `login_failed` 返回 AppFlow 登录页;旧 session 清理顺序未完全证明 | PARTIAL |
| 连接超时 | `Process`/connector failure 进入 offline/disconnect | `ClassicStream``NetStream` 均保留 3 秒 non-blocking deadline,并在 socket 可写后再用 `SO_ERROR` 确认连接完成 | MAPPED:仍缺少所有上层失败通知组合证明 |
| channel status | `ServerStateChecker` 请求首 channel,解析 count 和 `{port,status}`,逐频道通知 | 当前查询首频道,解析 classic 210;失败时 fallback TCP probe | PARTIAL:功能相近但多了 approximation |
| 部分响应 | NetStream 保留接收缓存等待完整状态包 | 当前已缓存状态 body,遇到半条记录会继续等待后续 TCP 数据 | MAPPED:分片行为已补回归测试 |
| 空/非法 count | 由包长度和状态处理逻辑拒绝 | `_try_parse_classic` 检查范围;body 内仍需完善负值/超大值保护 | PARTIAL |
| 恢复 | 失败后按 connector/server state 回调回到正确 phase | 当前由 AppFlow signal 和 ReconnectUI timer 驱动 | PARTIAL |
## Equivalence matrix
| 项目 | 状态 | 证据 |
|---|---|---|
| Preconditions | PARTIAL | 认证/连接和状态请求有检查;完整失败通知、远端关闭与上层重入仍需补齐 |
| Branch structure | PARTIAL | auth/channel/fallback 主分支存在,失败通知和超时分支不同 |
| Algorithms/formulas | MAPPED | classic count/record 解析和分片 body 累积已有实现与测试 |
| State transition order | PARTIAL | 当前 signal/UI retry,参考 connector/phase offline 回调顺序不同 |
| Constants/units | MAPPED | classic/m2dev socket 与 channel probe 均为 3 秒;状态 count 上限为 4096,仍需边界夹具覆盖 |
| Timing/event sources | PARTIAL | timer/fallback probe 与参考 network process/state checker 不同 |
| Resource/data sources | PARTIAL | serverinfo 推导端口,参考接收显式 channel entries |
| Protocol side effects | PARTIAL | 206/210 classic 包存在;fallback TCP probe 是当前扩展副作用 |
| Interruption/failure/cleanup | PARTIAL | 连接中断、重复 probe、重试终止仍缺少测试 |
## Deep audit round 2026-09-20
本轮沿 40250 `AccountConnector::Connect/Process``CServerStateChecker::Request/Update``CPythonNetworkStream::LoginPhase``CNetworkStream::Process/Connect` 逐分支核对,得到以下结论:
1. 40250 的认证连接通过 `CNetworkStream::Connect` 建立非阻塞 socket`Process` 用 socket 可写事件确认连接成功,并使用 `m_connectLimitTime` 超时清理;当前 `NetStream``ClassicStream` 现在都保留 3 秒 deadline,且只有可写事件后才用 `SO_ERROR` 确认完成。连接超时/远端错误的上层通知组合仍需扩展测试。
2. 40250 `CAccountConnector::__AuthState_RecvPhase` 发送 `CG_LOGIN3` 后立即 `ClearLoginInfo()` 清掉密码;当前 `AuthClient` 在构造登录包后清零并清空 `m_pw``M2Client::pump_auth()` 同步丢弃 host-side `cfg_pw`,没有 login key 时的空密码重连会被拒绝。该边界已有 loopback 断言。
3. 40250 的 `ServerStateChecker::Request` 只连接首频道并发送一次 `HEADER_CG_STATE_CHECKER`,连接/发送失败时将所有已登记频道通知为关闭;`Update` 收到 `HEADER_GC_RESPOND_CHANNELSTATUS` 后按端口通知并 `Initialize()`。当前 `AppFlow::_probe_channels` 使用 `ChannelStatus` 查询首频道,未知端口则逐频道做额外 TCP probe,属于当前端的 approximation,不是 1:1 分支;多个“检测频道”点击可以并行创建查询节点,未见 in-flight/cancel/generation 保护。
4. 当前状态查询的 206/210 classic 解析已累积 body,负数和大于 4096 的 count 会拒绝;`ChannelStatus` 现在会先取消旧 probe,并在回调重入时固定旧 callback,避免旧结果投递给新请求。自动测试仍未覆盖连接后无响应超时、count=0/截断/尾随包及远端关闭。默认协议和 `MT_PROTOCOL=classic` 两次测试均通过。
5. 当前 AppFlow 登录失败/断线的 signal 分类存在,但账号/游戏连接失败后由 `M2Client` 直接 reset session,再由 AppFlow 重建 Login;这与 40250 的统一 Offline phase / `SetLoginPhase` 顺序不同。`p10_test` 还暴露了 Loading→Game 未隐藏和无 SceneTree 时 char-select timer 空树的测试/生命周期问题,恢复链不能标记为完成。
## Required tests
- auth success/failure、connect refusal、connect timeout、remote close。
- 206/210 完整包、分片包、半条 record、count=0、负值和超大 count。
- channel status 失败后的 fallback、重试上限和回到登录页。
- 同一 session 同时 login/reconnect/probe 的竞态与清理。
## Evidence run
- `godot --headless --path project --script channel_status_test.gd`PASS。
- `MT_PROTOCOL=classic godot --headless --path project --script channel_status_test.gd`PASS,覆盖 40250 classic 的 206 请求和 210 响应夹具。
- 认证 loopback 成功路径、密码清除、挂起连接 deadline 已在 `extension/tests/net_loopback_test.cpp` 覆盖;classic transport 的同等 deadline/可写门禁在 `extension/tests/net_classic_stream_test.cpp` 覆盖。
- 当前合同继续 `PARTIAL`:通过的状态包解析测试不能替代 40250 的认证失败、远端关闭、失败通知顺序、count 边界和完整恢复链证据。
## Active UserInterface server-state checker review
- `ServerStateChecker.h`/`ServerStateCheckerModule.cpp` 维护独立 `CNetworkStream`、channel 地址/端口表、Create/AddChannel/Request/Update/Initialize 和 Python window 回调;它是登录前服务器状态探测链,不等同于游戏 session 的重连。
- 当前端 channel status 有 classic packet smoke test,且单个 `ChannelStatus` 的旧 socket/重入 callback 已有清理逻辑;仍没有证明多 channel 请求的超时、远端关闭、失败通知顺序、窗口销毁后回调和与 login phase 的竞态清理一致;本轮纳入 active source 证据,合同仍为 `PARTIAL`
## Implementation fix round 2026-09-20
- `AuthClient::on_phase(PHASE_AUTH)` 先把密码拷贝进 `CG_LOGIN3`,随后显式清零并清空 `m_pw`,再检查 `send_packet()``M2Client::pump_auth()` 看到同一边界后清空 `cfg_pw``reconnect()` 在没有 login key 时拒绝空凭据重试。
- `NetStream``ClassicStream` 都增加 3000ms non-blocking connect deadlineclassic 路径改为 `select` 可写后再 `SO_ERROR`,修复第一次 process 把仍在连接中的 socket 误报 Online 的差异。
- `ChannelStatus` 取消旧 probe、清空 mutable callback 后再发 `done`,支持 callback 内启动下一次查询;`AppFlow` 增加 channel probe in-flight guard。
- 新增/扩展 `net_loopback_test``net_classic_stream_test``channel_status_test.gd`,覆盖凭据清除、挂起连接 deadline 和 re-entrant probe callback;成功路径及默认/classic 两套状态包夹具通过。
## Status
`PARTIAL`