These files were loaded only by their own test_*_parity.gd and never by the runtime (transitively checked, incl. client_phase_integration_system). Removes 76 manifest implementation/evidence references across 18 contracts; contract docs get a note that conclusions based on them are void. Live systems kept: chest, consumable, item_attr, metin_socket, whisper_chat. Full headless suite: no new failures (7 failing + 5 headless-hanging render tests are identical before the deletion). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
104 lines
14 KiB
Markdown
104 lines
14 KiB
Markdown
# social.exchange_safebox
|
||
|
||
## Scope
|
||
|
||
审计玩家面对面交易、交易确认/防调包、仓库(Safebox)、仓库密码与分页、商城(Mall)只读取出、金币变化和窗口距离/关闭清理。判断标准是当前端是否复现 40250 的客户端请求门控、服务器回包权威状态、物品 socket/属性传递、错误分支和窗口时序;本地脚本能够独立完成一次交易或仓库存取,不能作为与 40250 实现统一的证明。
|
||
|
||
## Reference call chain
|
||
|
||
- `UserInterface/PythonNetworkStreamPhaseGame.cpp`
|
||
- `RecvExchangePacket` 按 `START/ITEM_ADD/ITEM_DEL/ELK_ADD/ACCEPT/END` 更新 `CPythonExchange`,每个物品同时保存 socket 与 attribute,并在物品变化后刷新交易和背包窗口。
|
||
- `SendExchangeStartPacket`、`SendExchangeElkAddPacket`、`SendExchangeItemAddPacket`、`SendExchangeAcceptPacket`、`SendExchangeExitPacket` 均先经过 `__CanActMainInstance()`,再发送 `CG_EXCHANGE` 并追加 sequence;客户端不直接结算交易。
|
||
- `UserInterface/PythonExchange.cpp` / `Client/Eternexus/root/uiexchange.py`
|
||
- `CPythonExchange` 保存双方姓名、12 个展示槽、socket/attribute、金币和接受状态;交易结束由 `GC_END` 驱动 `EndExchange`,成功/失败详情通过服务器聊天提示提供。
|
||
- 交易 UI 只提交物品、金币、接受或退出请求,等待服务器回包刷新;物品源位置、anti flag、背包和金币结果由服务端确认。
|
||
- `UserInterface/PythonNetworkStreamPhaseGameItem.cpp`
|
||
- Safebox checkin/checkout/move、密码错误、大小、金币变化和 Mall open/set/del/checkout 都是协议路径;Safebox/Mall item set 回包复制 vnum/count/flags/anti_flags、socket 和 attribute 后刷新窗口。
|
||
- Safebox 金币主动修改包在该版本明确未启用;Mall checkout 还触发对应掉落音效。
|
||
- `Client/Eternexus/root/uisafebox.py` / `game.py`
|
||
- 仓库由服务器返回页数和物品状态,5×9 每页 45 格;拖放/右键发送 checkin、checkout 或 move,关闭和密码命令走服务器流程。商城只允许取出,不允许向 Mall 存入。
|
||
|
||
## Current call chain
|
||
|
||
- native `M2Client`/`ClassicSession` 已提供 exchange start/item/gold/accept/cancel、safebox checkin/checkout/move/password/change/close、mall checkout/password,以及 `EntityStore` 的 exchange/safebox/mall parser、dirty 标记和 signal。
|
||
- `EntityStore` 能保存交易双方 12 槽、金币、接受标志、notice,能够复制交易物品 socket/attribute;Safebox/Mall parser 能复制 item flags、anti_flags、socket 和 attribute,并处理 open/set/del/close/wrong-password 基础事件。
|
||
- `project/ui/exchange_ui.gd`、`safebox_ui.gd`、`mall_ui.gd` 已提供 12 槽交易 UI、45 格分页仓库、商城只读出、tooltip、密码和距离自动关闭;主要 UI 已从 native getter/signal 刷新,而不是只依赖本地物品列表。
|
||
- `project/player_trade_exchange_system.gd` 和 `project/safebox_storage_keeper_system.gd` 是另外一套本地状态机:它们直接保存玩家字典、交易物品、金币、仓库密码、库存和金条熔铸结果。这些 helper 可用于单元测试,但不是 40250 客户端的网络 authority 链。
|
||
|
||
## Branch matrix
|
||
|
||
| 场景 | 40250 | 当前端 | 结论 |
|
||
|---|---|---|---|
|
||
| 开始交易 | `__CanActMainInstance`→`CG_EXCHANGE START`→服务器 `GC_START` | native 能发 start,UI/helper 能打开交易 | PARTIAL:native 缺同等动作门控和 active/partner 状态限制;helper 可绕开服务器 |
|
||
| 放入物品 | 客户端只发源位置和展示槽,服务器回 `GC_ITEM_ADD`;变化时双方接受状态由服务器清除 | UI 做 anti-give/重复源位置检查后立即记录 `_offered_cells`,native 只做位置边界 | PARTIAL:本地 optimistic 集合没有按 `GC_ITEM_ADD/ITEM_DEL` 重建,拒绝或删除后可能仍阻塞源物品 |
|
||
| 放入金币 | `SendExchangeElkAddPacket` 只负责请求,服务器决定金额并回包 | UI 限制 1..9,999,999,native 只做 u32/in-game 检查 | PARTIAL:本地上限和已放金币判断不是 40250 客户端 authority;native 缺交易 active/拥有金币/动作门控 |
|
||
| 接受/取消/结束 | 接受和退出发包,窗口等 `GC_END` 关闭;交易结果由服务器聊天和回包提供 | 接受有本地 pending;取消和距离超限会发包后立即 `close()` | PARTIAL:取消/距离分支不等待权威 `GC_END`,真实拒绝、断线和乱序关闭未覆盖 |
|
||
| 交易安全/原子结算 | 客户端不计算交换、不改双方背包/金币;服务端负责锁定、空间、金额和最终交付 | `player_trade_exchange_system.gd` 本地计算 ready、金币、空间和原子转移 | GAP(本地 helper):这不是 40250 客户端实现,不能作为 live parity 依据 |
|
||
| Safebox 打开/关闭 | `GC_SAFEBOX_SIZE` 打开,服务端控制大小;关闭走 `/safebox_close`,窗口状态由服务端同步 | native/UI 有 open/close、页数和 10m 自动关闭;close 后本地立即隐藏 | PARTIAL:基础链存在,但本地先隐藏/请求失败回滚、乱序重开和真实服务端错误缺测试 |
|
||
| Safebox 存取/移动 | checkin/checkout/move 请求后由 `GC_SAFEBOX_SET/DEL` 更新 | 拖放使用目标槽,移动/右键取出存在;移动端 deposit 先找本地首个空位并记录 pending | PARTIAL:pending 没有与回包关联;本地 helper 直接存取/堆叠,不能证明服务器拒绝、堆叠和物品属性结果一致 |
|
||
| Safebox 密码/金币 | wrong-password 和 size/money change 由服务器回包;客户端不保存真实密码 | native 通过命令发送密码;本地 `safebox_storage_keeper_system.gd` 保存密码、冷却、锁定和金条规则 | PARTIAL/GAP:helper 的密码/冷却/金条熔铸是自定义本地 authority,不能替代 40250 server command/GC 结果 |
|
||
| Mall 打开/取出 | `GC_MALL_OPEN/SET/DEL` 驱动只读窗口,checkout 后服务器更新背包/Mall | native parser/UI/getter/checkout 存在,UI 禁止插入 | PARTIAL:主路径接近,但服务端失败、背包回包、重连清理和掉落音效没有端到端证据 |
|
||
|
||
## Implementation-equivalence matrix
|
||
|
||
| 项目 | 状态 | 证据/限制 |
|
||
|---|---|---|
|
||
| Preconditions | PARTIAL | 位置、展示槽、u32、窗口和部分 anti flag 检查存在;exchange native 缺 `__CanActMainInstance` 等阶段/动作门控,真实库存/交易 active/拥有金币由服务端决定且未在客户端等价复现 |
|
||
| Branch structure | PARTIAL | exchange/safebox/mall 的协议分支和 UI 分支基本存在;取消立即关闭、移动端首空位 deposit、local helper 结算和密码/金条旁路与参考客户端不一致 |
|
||
| Algorithms/formulas | GAP | 40250 客户端不实现交易结算、库存空间、仓库密码存储或金条熔铸公式;当前本地 helper 复制这些服务器职责,不能宣称与客户端统一 |
|
||
| State transition order | PARTIAL | native store 大体是请求→GC→dirty/signal→UI;交易 `_offered_cells`、safebox pending 和 close 先隐藏造成本地 optimistic 状态,拒绝/乱序时序尚未闭合 |
|
||
| Constants/units | PARTIAL | 12 交易槽、45 格/页、socket/attribute 槽和距离限制存在;金币 9,999,999 UI 上限、helper 45 格/单页和自定义冷却/金条常量需避免冒充 40250 client 常量 |
|
||
| Timing/event sources | PARTIAL | exchange/safebox/mall dirty signal、10m 距离关闭和密码错误 UI 存在;真实 GC_END、GC_ITEM_SET/DEL、断线、重复请求和重新打开时序缺回归 |
|
||
| Resource/data sources | PARTIAL | live item tooltip 使用协议 item 数据并保留 socket/attribute;本地交易/仓库 helper 使用脚本 Dictionary,移动端 pending/首空位选择可能在服务器结果前改变显示 |
|
||
| Protocol side effects | PARTIAL | CG exchange、safebox、mall 基础布局和 store parser 存在;exchange 的动作门控、完整错误结果、商城掉落音效和实际服务器回包副作用不完整 |
|
||
| Interruption/failure/cleanup | GAP | helper 可无服务器回包直接成功;UI 的本地关闭、optimistic 源物品/pending 集合、密码失败、背包满、断线和乱序恢复没有完整回滚/重放证明 |
|
||
|
||
## Regression evidence
|
||
|
||
- `project/test_player_trade_exchange_parity.gd`:28/28 通过;覆盖的是本地交易 helper 的距离、不可交易物品、双方锁定、金币、空间和原子交付,不是 `GC_EXCHANGE` live 回包。
|
||
- `project/test_safebox_mall_parity.gd`:37/37 通过;覆盖 fake client 的 45 格、距离、socket/attribute、checkin/checkout/move、商城禁止插入和密码命令。
|
||
- `project/test_safebox_storage_keeper_parity.gd`:37/37 通过;覆盖本地密码/冷却、库存堆叠、移动、金条熔铸/承兑和非法格位,不能证明 40250 server authority。
|
||
- `project/shop_cube_mall_test.gd`、`project/test_mall_affect_parity.gd`、`project/popup_windows_parity_test.gd`、`project/test_security_lock_parity.gd`、`build/extension/net_entity_test`、`build/extension/net_classic_session_test`:通过;主要验证 UI/helper、item-mall 辅助效果或基础 packet/store,没有完整 exchange/safebox/mall 服务器拒绝、乱序、断线和回包回滚。
|
||
- `project/complex_item_drop_test.gd`:当前 1 项失败(购买模式仍允许把物品拖到出售目标),说明统一拖放门控仍有可复现差异;该失败属于相邻商店合同,但会影响交易/仓库共用拖放边界审计。
|
||
|
||
## Deep audit round 2026-09-21
|
||
|
||
### Fresh regression results
|
||
|
||
- `godot --headless --path project --script test_player_trade_exchange_parity.gd`:退出码 0,28/28 通过;覆盖本地交易距离、anti-give、锁定、金币、空间、原子转移和取消 helper。
|
||
- `godot --headless --path project --script test_safebox_mall_parity.gd`:退出码 0,37/37 通过;覆盖 fake client 的仓库/商城窗口、45 格、距离关闭、属性显示、存取移动和密码命令。
|
||
- `godot --headless --path project --script test_safebox_storage_keeper_parity.gd`:退出码 0,37/37 通过;覆盖本地密码/冷却、堆叠、45 格和金条 helper。
|
||
- `godot --headless --path project --script shop_cube_mall_test.gd`、`test_mall_affect_parity.gd`、`popup_windows_parity_test.gd`、`test_security_lock_parity.gd`:均退出码 0;商城效果、窗口和安全锁局部测试通过,其中安全锁为 48/48。
|
||
- `build/extension/net_entity_test` 与 `build/extension/net_classic_session_test`:均退出码 0;基础 exchange/safebox/mall parser/session 通过。
|
||
- `godot --headless --path project --script complex_item_drop_test.gd`:退出码 1;购买模式出售目标门控失败,属于相邻 shop/drag contract 的持续回归。
|
||
|
||
### Static parity findings
|
||
|
||
- 40250 交易请求全部先经过 `__CanActMainInstance`,客户端只发送源位置/金币/接受/退出,`GC_EXCHANGE_*` 决定展示和最终结束;当前 native exchange 请求主要做阶段/范围校验,`player_trade_exchange_system.gd` 仍可脱离网络直接完成双方金币、空间和物品转移。
|
||
- 当前 `exchange_ui.gd` 的 `_offered_cells`、pending 和取消/距离关闭逻辑没有统一由 `GC_ITEM_ADD/DEL`、`GC_ACCEPT`、`GC_END` 重建;拒绝、乱序、断线和重新打开可能保留乐观状态。
|
||
- 40250 Safebox/Mall item 回包保留 vnum/count/flags/anti_flags/socket/attribute,并由服务端决定页数、堆叠、空间和金币;当前 live native/store 路径能承接这些字段,但 `safebox_storage_keeper_system.gd` 仍保存密码、冷却、堆叠和金条 authority。
|
||
- Safebox/Mall UI 的距离自动关闭、密码命令和商城只出路径有局部验证;没有真实 `GC_SAFEBOX_SIZE/SET/DEL/WRONG_PASSWORD`、`GC_MALL_*` 与背包回包的成功/失败/重连/断线顺序 fixture,也没有 Mall checkout 音效的端到端验证。
|
||
- `M2Client`/`ClassicSession` 的 exchange/safebox/mall 包布局基础存在,但 native action gate、交易 active/partner/金币拥有量、服务器拒绝文案、SendSequence 和关闭幂等性未形成统一合同。
|
||
|
||
### Round conclusion
|
||
|
||
本轮仍为 `PARTIAL`:交易/仓库/商城 native 骨架和局部 UI 测试通过,但本地结算/密码/库存 helper 旁路、乐观关闭/pending 状态、动作门控与真实 GC 回包回滚仍未与 40250 统一。本轮未修改实现代码。
|
||
|
||
## Active UserInterface exchange, safebox and shop review
|
||
|
||
- `PythonExchange.h`/`PythonExchangeModule.cpp` 的状态读取覆盖双方名称、金钱、接受状态、槽位 vnum/count、metin socket、attribute 和 elk mode;这些值由交易回包写入,UI getter 不承担结算。
|
||
- `PythonSafeBox.h` 区分 safebox 与 mall 的打开尺寸、slot item、删除、金钱和当前大小;`PythonShop.h` 另外维护普通商店 tab/coin/name/item,以及私有商店 stock/display position/price/build/close,不能把它们合并为一个背包字典。
|
||
- 当前 native/store 已覆盖部分字段,但交易、仓库和商城 helper 仍有本地 optimistic/persistent 状态;真实 GC 添加/删除/接受/错误、密码失败、页尺寸、私店重建、断线和重复关闭顺序没有逐包等价测试,合同保持 `PARTIAL`。
|
||
|
||
## Static review evidence
|
||
|
||
已将 `PythonExchange.h`、`PythonExchangeModule.cpp`、`PythonSafeBox.h`、`PythonShop.h` 纳入 active source 证据;本轮确认了交易/仓库/商城的独立 owner 和回包字段边界,未修改实现。
|
||
|
||
## Status
|
||
|
||
`PARTIAL`。当前端已经具备 exchange/safebox/mall 的 native 协议骨架、回包存储和主要 UI,交易物品属性与仓库商城的基本网络状态能够展示;但 native exchange 请求缺 40250 动作门控,交易/仓库 UI 存在 optimistic 状态和提前关闭,本地 helper 直接承担交易结算、密码、库存和金条 authority,且真实错误、乱序、断线和回包回滚证据不足。本轮完成审计并记录,未修改实现。
|
||
|
||
### 2026-09-22 死代码删除
|
||
|
||
以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:`player_trade_exchange_system.gd`, `safebox_storage_keeper_system.gd`, `test_mall_affect_parity.gd`, `test_player_trade_exchange_parity.gd`, `test_safebox_storage_keeper_parity.gd`, `test_security_lock_parity.gd`。
|