本 PR 修复 StoryPlayer 对 AVG sticker 命令移植中的 6 项行为差异(×ばつP2,另顺带 1 项 P3 告警)。依据是对明日方舟官服客户端(2.7.61 / build 2761,Unity IL2CPP)GameAssembly.dll 的逆向分析,关键结论在下文直接给出,不依赖外部文档。
背景:native 的 sticker 机制(2.7.61 / build 2761 IDA 反编译复核)
Torappu.AVG.StickerPanel._ExecuteSticker(VA 0x183e924f0)按 id 三分支:m_stickerDict 命中 + multi=true → append;命中 + multi=false → hide;未命中 → show。block 默认 show 分支 true、append/hide 分支 false。
- append 分支只调
AVGStickerTextView.AppendText(view, text),完全不读 x/y/width/size/alignment/duration/delay —— 不重新布局、不重新 fade、不重建打字机速度,view 保持上次 show 的全部状态。
- hide 分支先
TryFinishType(瞬间补全打字中的文本)再 HideSticker(duration)(duration ≤ 0 兜底 0.15s),回收进池、dict 移除,最后 RaiseAutoClick(0)。
- show 分支:
_GenParam(VA 0x183e92d60)读布局,x/y 默认 0/0、width 1280(clamp 1280-x)、size 24、alignment "left"、duration -1 哨兵、delay 默认 1.0;坐标越界(x∉[0,1280] 或 y∉[0,720])或空 text → param 的 textContent 为空 → _ExecuteSticker 静默丢弃(不建 view、id 不进 dict、不阻塞)。_GenSticker(VA 0x183e932c0)在 pool+dict ≥ 20 时 LogError("[AVG]avg sticker meets the max num!")并丢弃本次 show。
AVGStickerTextView.RenderSticker(VA 0x183ed43b0):打字机比例 typeWriterDelay = AVGController.typeWriterDelay ×ばつ delay / originDelay(originDelay 资产实测 0.04),缺省 delay=1.0 → 25 倍慢;fade 规则 if (!isMultiline || duration >= 0) m_duration = duration >= 0 ? duration : 0.15 —— multiline show(multi=true 且 id 未注册)缺省 duration 时保留 view 旧 fade(新 view=0 瞬时、池复用 view=上次 hide 写入的 0.15)。
- auto-play 节拍:打字结束回调
_OnStickerTypeEnd(msgLength) → RaiseAutoClick(msgLength);hide 分支 RaiseAutoClick(0)。
修复内容
1. append 分支重放布局参数(P1)
- native:append 只追加文本,view 保持原布局与打字机速度。
- 前端问题:append 走 show 同一通路,缺省
x=0,y=0,width=1280,size=24,left 全部重放 → 无 x/y 的 append 把贴纸跳到左上角、宽度重置 1280(主线 13-05 信件段落大量命中)。
- 修复:runtime 以
stickerSlots(等价 m_stickerDict)记录每个 id 上次 show 的布局(含 delayMs/fadeMs),append 时原样重放,不再读取脚本的布局参数;append 标志同时修正为「id 已存在的 dict 命中」(原先 fresh id + multi=true 也被标成 append)。
2. sticker 无 auto-play 桥接(P1)
- native:打字结束
RaiseAutoClick(msgLength)、hide 时 RaiseAutoClick(0)。
- 前端问题:sticker 路径从不
scheduleAutoClick,button_auto / quick_play 下 block=true 的贴纸(绝大多数 show 样本)永远等手动点击,自动播放停摆。
- 修复:show/append 分支按全量字符数
scheduleAutoClick(charCount),hide 分支对齐 scheduleAutoClick(0);同时把 currentMessageLength/currentTypingComplete 同步为贴纸文本,使等待中途开启自动模式也能正确排程。
3. delay 缺省语义(P2)
- native:
_GenParam 默认 delay=1.0 → scale = 1/0.04 = 25(比全局慢 25 倍);append 不重设速度。
- 前端问题:缺省 scale=1。
- 修复:
stickerTypeDelayMs 缺省按 1.0 计算(scale 25)。缺省分支在数据中可达:activities/act23side/level_act23side_02_end.txt:43 是全新 id 的 multiline show 且无 delay。append 侧随修复 1 消解(不再重算速度)。
4. 空 text 的 show 未静默丢弃(P2)
- native:空 textContent → return false:不建 view、id 不进 dict、无日志、不阻塞。
- 前端问题:id 进注册表并渲染一张空贴纸,后续同 id 命中 hide 分支(native 命中 show 分支)。
- 修复:show 分支在注册 id 前补空 text 静默丢弃(越界拒绝保持不变)。
5. hide 时打字中途的文本状态(P2)
- native:hide 先
TryFinishType 瞬间补全全文再 fade-out。
- 前端问题:
clearSticker 直接从已打出的部分文本 fade-out(无补全)。
- 修复:renderer
clearSticker 在 fade 前把该 id 的 stickerTypingTargets.fullText 写入并重排,再淡出。
6. multiline show 缺省 duration 规则(P2)
- native:
!isMultiline || duration>=0 才写 m_duration;multiline 且缺省 → 保留旧值(新 view=0 瞬时;池复用 view=上次 hide 写入的 0.15)。
- 前端问题:一律 150ms 淡入。
- 修复:runtime 记录 per-id fade(show 显式 duration、hide、stickerclear 150ms 均会写入),
multi=true 的 show 缺省 duration 时沿用(新 id 为 0 = 瞬时出现)。
7. 20 上限告警(P3,顺手)
- native
_GenSticker 在 pool+dict ≥ 20 时 LogError + 丢弃本次 show。数据侧单画面从未超过个位数,故仅补 warn("parse", "avg sticker meets the max num (20)"),不丢弃。
验证用剧情文件
生产剧情语料(本地 torappu/storage/asset/gamedata/latest/story,230 个文件共 5745 条 [Sticker():
- 修复 1 + 6(append 布局重放 / multiline fade):
obt/main/level_main_13-05_beg.txt —— L18 首条信纸(fresh id 的 multiline show,无 duration → native 瞬时出现)、L21/22/25/44/45/46 无 x/y 的 append(修复前跳到左上角,修复后保持 L18/20/24/43 的 x=300,y=270,width=700)、L19/23/26/47 hide(其后 L20/24/43 重显 fade 应为 150ms)。obt/main/level_main_13-05_end.txt:22(无 x/y 的 append)、activities/act23side/level_act23side_02_end.txt:44 同理。
- 修复 2(auto-play 桥接):任意 block=true 的贴纸(如 13-05_beg L18)在 button_auto / quick_play 下验证自动推进;13-05 信纸段落修复前会永久卡住等手动点击。
- 修复 3(delay 缺省 scale=25):
activities/act23side/level_act23side_02_end.txt:43 —— 全新 id st1 的 multiline show 且无 delay,修复前按全局速度打字,修复后按 25 倍慢速(native 语义);其后 L44 append 不再重设速度。
- 修复 4(空 text show 丢弃):语料 0 命中(5745 条中无空 text 的 show)—— 前瞻对齐,由单测锁定:
tests/runtime.spec.ts「drops an empty-text sticker show without registering the id」。
- 修复 5(hide 补全打字):生产样本中打字被 hide 打断的可见窗口极窄(如
obt/main/level_st_17-03.txt:361→363,block=false + delay=0.04 仅 3 字)—— 行为由单测锁定:tests/renderer.spec.ts「completes in-flight sticker typing before the hide fade」。
测试
pnpm test:20 个文件 228 个用例全部通过(基线 222 + 新增 6)。
STORY_CORPUS_ROOT=... pnpm test:story-log:全语料 Log All 回归(符号预算 + 独立 oracle 对拍)通过。
pnpm exec vue-tsc -b 通过;改动文件 eslint 无告警。
本 PR 修复 StoryPlayer 对 AVG
sticker命令移植中的 6 项行为差异(×ばつP2,另顺带 1 项 P3 告警)。依据是对明日方舟官服客户端(2.7.61 / build 2761,Unity IL2CPP)GameAssembly.dll 的逆向分析,关键结论在下文直接给出,不依赖外部文档。背景:native 的 sticker 机制(2.7.61 / build 2761 IDA 反编译复核)
Torappu.AVG.StickerPanel._ExecuteSticker(VA0x183e924f0)按 id 三分支:m_stickerDict命中 +multi=true→ append;命中 +multi=false→ hide;未命中 → show。block默认 show 分支 true、append/hide 分支 false。AVGStickerTextView.AppendText(view, text),完全不读x/y/width/size/alignment/duration/delay—— 不重新布局、不重新 fade、不重建打字机速度,view 保持上次 show 的全部状态。TryFinishType(瞬间补全打字中的文本)再HideSticker(duration)(duration ≤ 0 兜底 0.15s),回收进池、dict 移除,最后RaiseAutoClick(0)。_GenParam(VA0x183e92d60)读布局,x/y 默认 0/0、width 1280(clamp1280-x)、size 24、alignment "left"、duration -1 哨兵、delay 默认 1.0;坐标越界(x∉[0,1280] 或 y∉[0,720])或空 text → param 的 textContent 为空 →_ExecuteSticker静默丢弃(不建 view、id 不进 dict、不阻塞)。_GenSticker(VA0x183e932c0)在 pool+dict ≥ 20 时 LogError("[AVG]avg sticker meets the max num!")并丢弃本次 show。AVGStickerTextView.RenderSticker(VA0x183ed43b0):打字机比例typeWriterDelay = AVGController.typeWriterDelay ×ばつ delay / originDelay(originDelay 资产实测 0.04),缺省 delay=1.0 → 25 倍慢;fade 规则if (!isMultiline || duration >= 0) m_duration = duration >= 0 ? duration : 0.15—— multiline show(multi=true 且 id 未注册)缺省 duration 时保留 view 旧 fade(新 view=0 瞬时、池复用 view=上次 hide 写入的 0.15)。_OnStickerTypeEnd(msgLength) → RaiseAutoClick(msgLength);hide 分支RaiseAutoClick(0)。修复内容
1. append 分支重放布局参数(P1)
x=0,y=0,width=1280,size=24,left全部重放 → 无 x/y 的 append 把贴纸跳到左上角、宽度重置 1280(主线 13-05 信件段落大量命中)。stickerSlots(等价m_stickerDict)记录每个 id 上次 show 的布局(含 delayMs/fadeMs),append 时原样重放,不再读取脚本的布局参数;append标志同时修正为「id 已存在的 dict 命中」(原先 fresh id +multi=true也被标成 append)。2. sticker 无 auto-play 桥接(P1)
RaiseAutoClick(msgLength)、hide 时RaiseAutoClick(0)。scheduleAutoClick,button_auto / quick_play 下 block=true 的贴纸(绝大多数 show 样本)永远等手动点击,自动播放停摆。scheduleAutoClick(charCount),hide 分支对齐scheduleAutoClick(0);同时把currentMessageLength/currentTypingComplete同步为贴纸文本,使等待中途开启自动模式也能正确排程。3.
delay缺省语义(P2)_GenParam默认delay=1.0→ scale = 1/0.04 = 25(比全局慢 25 倍);append 不重设速度。stickerTypeDelayMs缺省按 1.0 计算(scale 25)。缺省分支在数据中可达:activities/act23side/level_act23side_02_end.txt:43是全新 id 的 multiline show 且无 delay。append 侧随修复 1 消解(不再重算速度)。4. 空 text 的 show 未静默丢弃(P2)
5. hide 时打字中途的文本状态(P2)
TryFinishType瞬间补全全文再 fade-out。clearSticker直接从已打出的部分文本 fade-out(无补全)。clearSticker在 fade 前把该 id 的stickerTypingTargets.fullText写入并重排,再淡出。6. multiline show 缺省 duration 规则(P2)
!isMultiline || duration>=0才写m_duration;multiline 且缺省 → 保留旧值(新 view=0 瞬时;池复用 view=上次 hide 写入的 0.15)。multi=true的 show 缺省 duration 时沿用(新 id 为 0 = 瞬时出现)。7. 20 上限告警(P3,顺手)
_GenSticker在 pool+dict ≥ 20 时 LogError + 丢弃本次 show。数据侧单画面从未超过个位数,故仅补warn("parse", "avg sticker meets the max num (20)"),不丢弃。验证用剧情文件
生产剧情语料(本地
torappu/storage/asset/gamedata/latest/story,230 个文件共 5745 条[Sticker():obt/main/level_main_13-05_beg.txt—— L18 首条信纸(fresh id 的 multiline show,无 duration → native 瞬时出现)、L21/22/25/44/45/46 无 x/y 的 append(修复前跳到左上角,修复后保持 L18/20/24/43 的 x=300,y=270,width=700)、L19/23/26/47 hide(其后 L20/24/43 重显 fade 应为 150ms)。obt/main/level_main_13-05_end.txt:22(无 x/y 的 append)、activities/act23side/level_act23side_02_end.txt:44同理。activities/act23side/level_act23side_02_end.txt:43—— 全新 id st1 的 multiline show 且无 delay,修复前按全局速度打字,修复后按 25 倍慢速(native 语义);其后 L44 append 不再重设速度。tests/runtime.spec.ts「drops an empty-text sticker show without registering the id」。obt/main/level_st_17-03.txt:361→363,block=false + delay=0.04 仅 3 字)—— 行为由单测锁定:tests/renderer.spec.ts「completes in-flight sticker typing before the hide fade」。测试
pnpm test:20 个文件 228 个用例全部通过(基线 222 + 新增 6)。STORY_CORPUS_ROOT=... pnpm test:story-log:全语料 Log All 回归(符号预算 + 独立 oracle 对拍)通过。pnpm exec vue-tsc -b通过;改动文件eslint无告警。