[Parity][input/skills][S1][R/F] F1–F8 技能热键被捕获阶段空表拦截失效、绑定未持久化且 0x7B 被误解码为 ActiveSkill #585

Closed
opened 2026-10-02 11:42:42 +00:00 by troytt · 0 comments
Owner

1.13c 客户端保真度审计立项(基准快照:7bd1090 / main @ 77a20c7)

证据标记说明:V-data = 1.13c MPQ 数据表 (.txt / animdata.d2 / .tbl / pal.pl2);V-src = D2MOO 逆向源码;V-bin = 1.13c DLL 反汇编;K = 1.13c 公认行为。

来源审计项:motion-input#5 — [S1][R/F] 5. F1–F8 技能热键在游戏内完全失效:命令映射器吞键,绑定表又从不填充

  • 位置:command-mapper.ts:168(skillHotkeys Map)、:211-216(bindSkillHotkey,全仓没有调用方,已用 tsrch 确认)、:738-758(F1–F8 分支即使没有绑定也 return true);main.ts:897-900(handled 为真时在捕获阶段 e.stopImmediatePropagation());ui/hud-manager.ts:1412-1415(hotkeys.triggerFunctionKey(key),监听器在 :1431 以冒泡阶段注册,永远收不到这个事件)
  • 代码:if (/^F[1-8]$/.test(keyUpper)) { … const binding = this.skillHotkeys.get(keyUpper); if (binding) {…} return true }
  • 原版:F1–F8 切换到指派给该键的技能。指派由服务器通过 S2C 0x7B AssignSkillHotkey 下发,切换技能发 C2S 0x3C(K-高)。
  • 影响:所有技能热键都无效,只能用鼠标点技能栏换技能。范围外但相关:netproto 的 decode0x7BAssignSkillHotkey(netproto/d2gs/s2c/skills.ts:136-150)把 0x7B 解成 type: 'ActiveSkill', unitId: slot,丢掉了热键指派的语义。所以即使不再吞键,也拿不到服务器下发的指派。
  • 建议:热键的唯一来源是 0x7B 指派(按 slot 存表)。command-mapper 在没有绑定时不要吞键,或者直接交给 hud-manager 处理;两个重复的处理器删掉一个。
  • 已有 issue:和 #534(按键冲突、重复处理器)部分相关,但 #534 没写明 F1–F8 完全失效。建议在 #534 中补充,或单独立项。

来源审计项:inv-skills#10 — [S2][R] F 键技能绑定只存在本地 Map:不随角色保存、不发给服务器;0x7B 被误解码成"当前技能切换"

  • 位置:
    • src/client/ui/hotkeys.ts:226(readonly bindings = new Map<...>())。
    • src/client/ui/hotkeys.ts:375-386(bindHotkey/setHotkey):只写 Map,不清除同一技能在其他键上的旧绑定。
    • 跨文件:src/netproto/d2gs/s2c/skills.ts:135-150,0x7B 被解码为 { type: 'ActiveSkill', unitId: slot, ... }。
    • 跨文件:src/client/world/client-world.ts:2174-2186。
  • 代码:this.bindings.set(fKey.toUpperCase(), { side, skillId })
  • 原版:
    • 服务器为每个客户端保存 16 个热键,进游戏时逐个下发 0x7B(V-src D2MOO D2Game/src/GAME/Clients.cpp:427-433,D2GAME_PACKETS_SendPacket0x7B_6FC3F720(pClient, i, nSkill, nHand, nItemGUID))。
    • 客户端绑定时发包给服务器(V-src D2Game/src/PLAYER/PlrMsg.cpp:3590-3612,D2MOO 命名为 Rcv0x51_BindHotkeyToSkill,9 字节,高 16 位为热键号)。
    • 一个技能同一时间只占一个热键(K-中)。
  • 影响:
    • 每次进游戏,F 键绑定全部丢失。
    • 同一技能可以同时挂在多个 F 键上。
    • 潜在问题:0x7B 的 slot 被当成了 unitId。slot==0 或 slot==selfId 时,client-world.ts 会把热键分配当成左右键技能切换;selfId===0 时还会把 selfId 设为热键槽号。仓库自带的服务器不发 0x7B,所以目前不会触发,接入按原版协议实现的服务器时会暴露。
  • 建议:
    • 实现 C2S 热键绑定包,并消费 S2C 0x7B 来恢复 bindings。
    • 修正 0x7B 的解码语义:它是热键分配,不是 ActiveSkill。
    • 绑定时清除该技能在其他键上的旧绑定(先确认原版规则)。
  • 已有 issue:—

来源审计项:net-world#17 — [S3][R] F17 0x7B(技能热键)被当成"设置当前技能",slot 被塞进 unitId

  • 位置:src/netproto/d2gs/s2c/skills.ts:135-150;src/client/world/client-world.ts:2174-2187
  • 原版:进游戏时对 16 个热键槽各发一条 0x7B,紧接着发两条 0x23 设置左右手技能(Clients.cpp:427-439,SCmd.cpp:2144-2155)(V-src)。0x7B 只是热键绑定。
  • 实测:slot 1 加上 selfId 1(抓包里自己的 id 就是 1)→ rightSkill 被改成热键 F2 的技能;slot 0 总会命中 unitId === 0 分支。
  • 影响:进游戏时会短暂改动左右手技能,随后被 0x23 覆盖,所以一般看不出来;但热键绑定本身没有保存,F1–F16 不会恢复服务器存档里的绑定。
  • 建议:0x7B 解成 SkillHotkey{slot, skill, hand, itemId},存进热键表。
  • 已有 issue:—
> **1.13c 客户端保真度审计立项**(基准快照:`7bd1090` / `main @ 77a20c7`) > 证据标记说明:`V-data` = 1.13c MPQ 数据表 (`.txt` / `animdata.d2` / `.tbl` / `pal.pl2`);`V-src` = D2MOO 逆向源码;`V-bin` = 1.13c DLL 反汇编;`K` = 1.13c 公认行为。 ### 来源审计项:`motion-input#5` — [S1][R/F] 5. F1–F8 技能热键在游戏内完全失效:命令映射器吞键,绑定表又从不填充 - 位置:`command-mapper.ts:168`(`skillHotkeys` Map)、`:211-216`(`bindSkillHotkey`,全仓没有调用方,已用 tsrch 确认)、`:738-758`(F1–F8 分支即使没有绑定也 `return true`);`main.ts:897-900`(handled 为真时在捕获阶段 `e.stopImmediatePropagation()`);`ui/hud-manager.ts:1412-1415`(`hotkeys.triggerFunctionKey(key)`,监听器在 `:1431` 以冒泡阶段注册,永远收不到这个事件) - 代码:`if (/^F[1-8]$/.test(keyUpper)) { … const binding = this.skillHotkeys.get(keyUpper); if (binding) {…} return true }` - 原版:F1–F8 切换到指派给该键的技能。指派由服务器通过 S2C 0x7B AssignSkillHotkey 下发,切换技能发 C2S 0x3C(K-高)。 - 影响:所有技能热键都无效,只能用鼠标点技能栏换技能。范围外但相关:netproto 的 `decode0x7BAssignSkillHotkey`(`netproto/d2gs/s2c/skills.ts:136-150`)把 0x7B 解成 `type: 'ActiveSkill', unitId: slot`,丢掉了热键指派的语义。所以即使不再吞键,也拿不到服务器下发的指派。 - 建议:热键的唯一来源是 0x7B 指派(按 slot 存表)。command-mapper 在没有绑定时不要吞键,或者直接交给 hud-manager 处理;两个重复的处理器删掉一个。 - 已有 issue:和 #534(按键冲突、重复处理器)部分相关,但 #534 没写明 F1–F8 完全失效。建议在 #534 中补充,或单独立项。 ### 来源审计项:`inv-skills#10` — [S2][R] F 键技能绑定只存在本地 Map:不随角色保存、不发给服务器;0x7B 被误解码成"当前技能切换" - 位置: - `src/client/ui/hotkeys.ts:226`(`readonly bindings = new Map<...>()`)。 - `src/client/ui/hotkeys.ts:375-386`(`bindHotkey`/`setHotkey`):只写 Map,不清除同一技能在其他键上的旧绑定。 - 跨文件:`src/netproto/d2gs/s2c/skills.ts:135-150`,0x7B 被解码为 `{ type: 'ActiveSkill', unitId: slot, ... }`。 - 跨文件:`src/client/world/client-world.ts:2174-2186`。 - 代码:`this.bindings.set(fKey.toUpperCase(), { side, skillId })` - 原版: - 服务器为每个客户端保存 16 个热键,进游戏时逐个下发 0x7B(V-src D2MOO `D2Game/src/GAME/Clients.cpp:427-433`,`D2GAME_PACKETS_SendPacket0x7B_6FC3F720(pClient, i, nSkill, nHand, nItemGUID)`)。 - 客户端绑定时发包给服务器(V-src `D2Game/src/PLAYER/PlrMsg.cpp:3590-3612`,D2MOO 命名为 `Rcv0x51_BindHotkeyToSkill`,9 字节,高 16 位为热键号)。 - 一个技能同一时间只占一个热键(K-中)。 - 影响: - 每次进游戏,F 键绑定全部丢失。 - 同一技能可以同时挂在多个 F 键上。 - 潜在问题:0x7B 的 slot 被当成了 unitId。slot==0 或 slot==selfId 时,`client-world.ts` 会把热键分配当成左右键技能切换;`selfId===0` 时还会把 selfId 设为热键槽号。仓库自带的服务器不发 0x7B,所以目前不会触发,接入按原版协议实现的服务器时会暴露。 - 建议: - 实现 C2S 热键绑定包,并消费 S2C 0x7B 来恢复 `bindings`。 - 修正 0x7B 的解码语义:它是热键分配,不是 ActiveSkill。 - 绑定时清除该技能在其他键上的旧绑定(先确认原版规则)。 - 已有 issue:— ### 来源审计项:`net-world#17` — [S3][R] F17 0x7B(技能热键)被当成"设置当前技能",slot 被塞进 unitId - 位置:`src/netproto/d2gs/s2c/skills.ts:135-150`;`src/client/world/client-world.ts:2174-2187` - 原版:进游戏时对 16 个热键槽各发一条 0x7B,紧接着发两条 0x23 设置左右手技能(`Clients.cpp:427-439`,`SCmd.cpp:2144-2155`)(V-src)。0x7B 只是热键绑定。 - 实测:slot 1 加上 selfId 1(抓包里自己的 id 就是 1)→ rightSkill 被改成热键 F2 的技能;slot 0 总会命中 `unitId === 0` 分支。 - 影响:进游戏时会短暂改动左右手技能,随后被 0x23 覆盖,所以一般看不出来;但热键绑定本身没有保存,F1–F16 不会恢复服务器存档里的绑定。 - 建议:0x7B 解成 `SkillHotkey{slot, skill, hand, itemId}`,存进热键表。 - 已有 issue:—
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#585
No description provided.