fix: 装备属性面板避让逻辑 + 多项功能更新

- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
This commit is contained in:
shen
2026-09-21 16:38:59 -07:00
parent 1522a8a1a1
commit c93894313a
5498 changed files with 4558004 additions and 1442 deletions
@@ -0,0 +1,86 @@
# 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`