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>
14 KiB
14 KiB
npc.dialog_shop
Scope
审计 NPC 点击与脚本对话、普通 NPC 商店、扩展多货架商店、购买/出售、商店错误和结束清理,以及个人商店招牌、点击打开、摆摊编辑和关闭流程。判断标准是当前端是否复现 40250 的服务端货架数据来源、全局槽位索引、NPC/玩家商店分支、动作门控、请求包副作用和失败清理,而不是本地 helper 是否能算出一个价格或库存结果。
Reference call chain
UserInterface/PythonNetworkStreamPhaseGame.cppRecvShopPacket处理START、START_EX、END、UPDATE_ITEM、UPDATE_PRICE与错误子包;普通商店固定读取 40 个槽位,多货架按 tab 读取每个货架的名称、货币类型和 40 个槽位。CPythonShop::SetItemData(globalIndex, item)将增量更新的全局位置映射为tabIdx = globalIndex / SHOP_HOST_ITEM_MAX_NUM、slot = globalIndex % SHOP_HOST_ITEM_MAX_NUM,因此多货架位置不是永远落在 tab 0。SendOnClickPacket发送 NPC/个人商店点击请求并追加 sequence;脚本回包通过事件管理器打开对话窗口。RecvShopSignPacket将玩家商店招牌绑定到已有角色实体;个人商店广告牌点击后由uiprivateshopbuilder.py发送SendOnClickPacket(vid)。
UserInterface/PythonNetworkStreamPhaseGameItem.cppSendShopEndPacket、SendShopBuyPacket、SendShopSellPacket和SendShopSellPacketNew都先经过__CanActMainInstance(),再写CG_SHOP子头、槽位/数量并SendSequence()。- 购买包使用数量 1 加显示位置;出售分旧版单槽包和新版槽位+数量包。结果、金币、背包和售罄状态由服务器回包更新。
UserInterface/PythonShop.cpp、Client/Eternexus/root/uishop.pyCPythonShop保存所有 tab 和全部 40 个位置,普通、NPC 扩展、多货架和个人商店由窗口状态区分。ShopDialog.Open(vid)根据 NPC、他人玩家和主角自己的商店分别显示买卖、货架或关闭控件;自己商店不能走买卖分支。- 左键购买有确认,右键/取消选中可直接购买;出售检查反出售标记、贵重物品确认并发送服务端请求。
Client/Eternexus/root/uiprivateshopbuilder.py、PythonShop.cpp、Packet.h- 摆摊最多 39 个商品,商品按展示位置排序,源物品位置和价格由服务端消费;标题有长度限制,禁止状态和 anti flag 在提交前检查。
BuildPrivateShop发送CG_MYSHOP,关闭自己的摊位也使用相应的个人商店协议语义。
PythonNonPlayer.cpp、PythonPlayerInput.cpp、PythonPlayerInputMouse.cpp- NPC/商店点击由实体类型、可点击状态和距离/输入流程进入
SendOnClickPacket;个人商店广告牌是可点击的独立入口,不等同于普通 PC 攻击或选中。
- NPC/商店点击由实体类型、可点击状态和距离/输入流程进入
Current call chain
project/net_play.gd::_on_pick对 NPC/WARP 走click_npc,有距离预约和同 VID 节流;但没有对带shop_sign的 PC 建立 40250 广告牌点击分支,当前project/net_world.gd::_refresh_shop_sign只创建显示用Label3D。project/ui/quest_dialog.gd和网络事件可以承接脚本/对话显示;extension/src/net/m2_client.cpp::click_npc、classic_session.cpp::send_click_npc能发送基础点击包。extension/src/net/entity_store.cpp已解析普通START、扩展START_EX、END、更新和常见错误,并保存商店 VID、tab 和 item;但mut_shop_update_item拒绝pos >= 40,且多 tab 时只更新 tab 0,未复现 40250 的全局位置映射。project/ui/shop_ui.gd有买/卖模式、40 格、多货架、确认框、距离关闭和右键购买;但是_drop_to_sell在购买模式下仍继续执行出售请求,当前project/complex_item_drop_test.gd已复现该失败。ShopUI.refresh没有根据shop.vid区分主角自己的个人商店、他人个人商店和 NPC 商店,因此当前买/卖按钮状态不等价于ShopDialog.Open。project/npc_shop_system.gd使用硬编码 NPC 库存与本地buy_item/sell_item直接修改金币和背包;它不是CPythonShop的服务器货架回包链,且目前主要由孤立 parity 测试使用。project/private_shop_system.gd同样是本地 stock/价格 helper;liveproject/ui/private_shop_ui.gd能提交M2Client.open_private_shop,但未在 live 提交前统一调用死亡、骑乘、战斗状态和 50200 摆摊道具校验。M2Client::shop_buy、shop_sell只做 in-game、u8 和数量边界,缺少 40250 的__CanActMainInstance、实际物品/anti flag/商店状态门控;open_private_shop只验证字段 wire bounds 与最多 39 条。classic_session.cpp的买卖、关闭和个人商店包布局及 sequence 基础存在;现有 native 测试验证布局,但没有真实GC_SHOP多阶段包和失败回包驱动的 UI 状态测试。
Branch matrix
| 场景 | 40250 | 当前端 | 结论 |
|---|---|---|---|
| NPC 点击/脚本对话 | 实体输入、距离和 SendOnClickPacket 后由服务器脚本回包驱动 |
NPC 点击、距离预约、节流和基础点击存在 | PARTIAL:事件/脚本结果与异常清理缺真实包覆盖 |
| 普通商店 START | 服务器提供 VID 和固定 40 格,空槽也有确定位置 | 能解析并保存非空 item,UI 补 40 格 | PARTIAL:空槽原始状态和真实 UI 回包顺序未证明 |
| START_EX 多货架 | 每 tab 40 格,更新位置是全局 tab*40+slot |
初始 tab 能显示;增量更新限制 <40 且只更新 tab 0 |
GAP |
| 商品更新/售罄/错误 | SetItemData、价格更新、错误事件和 UI 刷新 |
基础错误字符串和刷新存在 | PARTIAL:更新索引和回包失败后 UI/金币/库存清理不足 |
| 买入 | __CanActMainInstance→CG_SHOP/BUY→sequence→服务器结果 |
UI/native 能发买包并有确认 | PARTIAL:缺动作门控、pos 全范围和服务器结果闭环 |
| 卖出/批量卖出 | anti-sell/贵重确认后旧 SELL 或 SELL2,服务端扣物品加金币 | UI 有部分 anti flag/确认;native 只做 u8 边界 | PARTIAL:购买模式拖入仍可出售,实际库存/服务端回包未闭环 |
| 自己的个人商店 | 隐藏买卖,只显示关闭;个人商店协议关闭自己的摊位 | ShopUI 主要按普通买卖模式显示,未按 shop VID 统一区分 | GAP |
| 他人个人商店/招牌 | 广告牌点击发送 SendOnClickPacket(vid) 后打开玩家商店 |
招牌目前只有 Label3D,PC 点击未路由到点击商店 |
GAP |
| 摆摊编辑/提交 | 39 条、标题/禁止状态/anti flag/源物品校验后 CG_MYSHOP |
UI/Native 字段和数量限制存在 | PARTIAL:live 状态校验和服务端消费/拒绝反馈不足 |
| 关闭/离开/断线 | END 清理访问商店;个人商店关闭遵循独立协议语义 |
基础 close 和本地 UI 关闭存在 | PARTIAL:访问商店、自己的摆摊和断线/乱序清理未统一验证 |
Implementation-equivalence matrix
| 项目 | 状态 | 证据/限制 |
|---|---|---|
| Preconditions | PARTIAL | NPC 距离和部分 UI anti flag 存在;native 买卖缺 __CanActMainInstance、真实物品/商店状态和动作门控,摆摊 live 缺死亡/骑乘/战斗及 bundle 校验 |
| Branch structure | PARTIAL | 普通商店、多货架、购买确认和脚本对话分支存在;自己的个人商店、玩家招牌点击和购买模式拖放分支不等价 |
| Algorithms/formulas | PARTIAL | 货架槽位和价格由协议保存;硬编码 NPC stock、本地买卖 helper 和本地出售价格不是 40250 的服务端权威实现 |
| State transition order | GAP | 参考是请求→服务端判定→item/gold/shop update/error→UI 刷新;本地 helper 可直接改库存/金币,且 UI 购买模式错误放行出售 |
| Constants/units | PARTIAL | 40 格、最多 3 tab、最多 39 条、标题/招牌和字段 wire bounds 基本存在;全局多 tab 更新范围未实现,价格/数量服务端约束未闭合 |
| Timing/event sources | PARTIAL | 点击节流、距离关闭、确认框和 shop signal 存在;真实 START→UPDATE→ERROR/END 时序、招牌点击和重复请求未覆盖 |
| Resource/data sources | GAP | 40250 商品库存来自服务器 CPythonShop/item data;当前 NpcShopSystem 额外维护硬编码本地库存,和 live authority 不是同一数据源 |
| Protocol side effects | PARTIAL | CG_SHOP、CG_ON_CLICK、CG_MYSHOP 的基础布局和 sequence 存在;动作门控、个人招牌点击和完整错误/结果副作用不完整 |
| Interruption/failure/cleanup | GAP | 购买模式拖放泄漏到出售;多 tab 更新可能落错/丢失;断线、拒绝、售罄、自己摊位关闭和未找到实体的招牌清理没有统一端到端回滚 |
Regression evidence
project/test_npc_shop_parity.gd:26/26 通过,但主要验证硬编码NpcShopSystem的本地库存、金币和背包变化,不能作为 40250 服务器商店等价证明。project/test_npc_shop_flow.gd:NPC 点击距离/节流、UI 多 tab/右键购买和距离关闭通过;使用 fake client,未覆盖真实 GC shop 包。project/test_npc_dialog_shop_ui_parity.gd:窗口尺寸、格子、按钮、脚本对话与 fake client 通过;没有多 tab 全局更新和 PC 招牌点击。project/private_shop_ui_test.gd、project/test_private_shop_bundle_parity.gd、project/shop_rules_test.gd、project/p8_test.gd:本地摆摊编辑与规则通过;未证明 live UI 的状态门控、CG_MYSHOP服务端拒绝/关闭和招牌打开。project/complex_item_drop_test.gd:当前有 1 项失败,购买模式仍允许把背包物品拖到商店出售目标;这是可复现的实现回归。build/extension/net_classic_session_test、build/extension/net_entity_test:基础 packet/store 行为通过,但没有START_EX位置 40..119 的更新回归,也没有完整错误/结束/重新打开序列。
Deep audit round 2026-09-21
Fresh regression results
godot --headless --path project --script test_npc_shop_parity.gd:退出码 0,26/26 通过;覆盖的是NpcShopSystem的本地库存、价格、金币和背包 helper。godot --headless --path project --script test_npc_shop_flow.gd:退出码 0,38 项通过;覆盖 NPC 距离/预约/节流、fake shop 多货架 UI、右键购买和距离关闭,没有真实GC_SHOP START_EX/UPDATE/ERROR/END包。godot --headless --path project --script test_npc_dialog_shop_ui_parity.gd:退出码 0;窗口、40 格、买卖按钮、脚本对话和 QuestCurtain 通过,仍使用 fake client。godot --headless --path project --script private_shop_ui_test.gd、test_private_shop_bundle_parity.gd、shop_rules_test.gd、p8_test.gd:均退出码 0;本地摆摊规则、bundle、价格和社交窗口通过,不等价于真实CG_MYSHOP/个人商店招牌/失败回包。godot --headless --path project --script complex_item_drop_test.gd:退出码 1;购买模式下卖出目标仍错误接受背包物品拖放。build/extension/net_classic_session_test与build/extension/net_entity_test:退出码 0;基础包/store 行为通过,但没有覆盖多货架全局位置 40..119 的增量更新。
Static parity findings
- 40250
START_EX的每个 tab 固定 40 格,UPDATE_ITEM使用全局位置tab * 40 + slot;当前EntityStore::mut_shop_update_item仍要求pos < 40,并且只更新m_shop_tabs[0],所以 tab 1+ 的增量回包会被丢失或落错货架。 ShopUI::_drop_to_sell没有以_mode == 2作为硬门,购买模式拖拽仍能调用_request_sell;这与ShopDialog的买/卖分支不一致,也是本轮失败测试的直接原因。- 40250
ShopDialog.Open(vid)按 NPC、他人个人商店和自己的个人商店分支显示;当前ShopUI主要按通用买卖窗口处理,未以shop_vid和主角 VID 做统一分支,自己的摊位与他人摊位行为仍可能混用。 shop_sign在 native/entity store 中有保存和显示数据,但NetPlay::_on_pick没有完整复刻SendOnClickPacket(shop_vid)的个人商店广告牌分支;显示招牌不等于可打开玩家商店。NpcShopSystem、PrivateShopSystem等本地 helper 可以直接修改金币、库存和 stock;live UI 的 native 请求路径存在,但两套权威来源并存,测试通过不能证明服务端回包覆盖本地结果。M2Client::shop_buy/shop_sell/shop_close/open_private_shop的基础 wire bounds 存在,但没有统一__CanActMainInstance、商店状态、实际 item/anti-flag 和重复/失败请求门控;SendSequence/错误/售罄/重开顺序也未由真实 fixture 验证。
Round conclusion
本轮仍为 PARTIAL:NPC 点击、普通商店 UI、基础买卖/摆摊包和局部规则测试可运行,但多货架增量索引、购买模式拖放门、个人商店招牌/自有摊位分支和服务器权威回包仍未统一;本轮未修改实现代码。
Status
PARTIAL。NPC 点击、基础对话承接、普通/扩展商店协议骨架、购买出售 UI 和个人商店字段已经存在,但当前端仍有明确的多 tab 更新索引错误、购买模式错误放行出售、个人商店招牌不可点击打开、自己商店状态不分支、native 动作门控缺失以及本地硬编码库存/金币旁路。已完成审计并记录,未修改实现代码。
2026-09-22 死代码删除
以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:npc_shop_system.gd, private_shop_system.gd, test_npc_shop_parity.gd, test_private_shop_bundle_parity.gd。