Files
mtgodot-poc/audit/contracts/quest.quest-event-navigation.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

23 KiB
Raw Blame History

quest.quest_event_navigation

Scope

审计 GC_QUEST_INFO 任务日志、任务计时/计数、任务信件按钮、GC_SCRIPT NPC 对话与 CPythonEventManager 事件标签、确认/输入/选择、任务地图信号、Atlas/小地图导航、剧情镜幕、相机/淡入淡出和副本结果提示。判断标准是当前端是否与 40250 的协议字段、任务生命周期、事件处理顺序、脚本按钮语义和地图/过场副作用统一;本地生物学家、悬赏、活动调度等脚本 helper 的独立测试不等于 40250 客户端 parity。

Reference call chain

  • UserInterface/PythonNetworkStreamPhaseGame.cpp
    • RecvQuestInfoPacket 先按 QUEST_SEND_IS_BEGIN 判定 BEGIN/UPDATE/END,再按 flag 读取 title、clock name/value、counter name/value、iconBEGIN 注册新任务并记录开始时间,UPDATE 只更新带 flag 的字段,END 删除指定任务,最后刷新任务 UI。
    • RecvScriptPacket 读取动态 GC_SCRIPT,交给 CPythonEventManager::RegisterEventSetFromString,然后以 server skin 打开 QuestDialogRecvQuestConfirmPacket 将 message、timeout、request PID 交给 UI。
    • SendScriptAnswerPacket 使用 8-bit answerSendScriptButtonPacket 使用 unsigned 32-bit index,任务日志点击通过 -2147483648 + questIndex 形成 unsigned wire value。
  • UserInterface/PythonQuest.cpp
    • CPythonQuest::MakeQuest/RegisterQuestInstance/SetQuestClockValue 都会更新 iStartTimeGetQuestLastTimeiStartTime + iClockValue - currentSecond,因此任务窗口关闭时计时仍继续。
    • CPythonQuest 保持任务实例容器的顺序,GetQuestIndex 将 UI 行号映射到真实 quest index。
  • UserInterface/PythonEventManager.cpp / PythonEventManager.h
    • ProcessEventSet 逐命令处理 42 类标签:LETTER/DELAY/ENTER/WAIT/NEXT/DONE/QUESTION、图片、QUESTBUTTON、地图信号、相机、FADE/WHITE、ITEM/MOB、WINDOW_SIZE、INPUT、CONFIRM_WAIT、SELECT_ITEM 和 DUNGEON_RESULT。
    • NEXT/取消发送 answer 254DONE 只执行 DoneEvent/CloseSelf,不发送 answer。RUN_CINEMA 只在首命令时替换为 .msc 事件文件;FADE/WHITE 设置 wait flag,动画结束后由 EndEventProcess 解锁。
  • Client/Eternexus/root/uiquest.py / interfacemodule.py / uicharacter.py
    • QuestDialog 的 skin、按钮和 ESC 语义由 UI 层执行;任务信件点击发送 unsigned script button 后删除信件按钮;任务日志最多显示 5 条并使用原始任务顺序。
  • UserInterface/PythonMiniMap.cpp / Client/Eternexus/root/uiminimap.py / atlasinfo.txt
    • ADDMAPSIGNAL 写入 CPythonMiniMap 信号点并打开 AtlasCLEARMAPSIGNAL 清除信号点;SETCMAPPOS 改变 Atlas 中心,不改变玩家世界坐标。

Current call chain

  • native EntityStore::apply 已解析 GC_SCRIPTGC_QUEST_CONFIRM 和 flag-driven GC_QUEST_INFOM2Client 暴露 script_answer/script_button/script_select_item/quest_input/quest_confirm/quest_cancel/get_questsM2Client::poll 通过 signal 将脚本、确认和任务变更交给 Godot。
  • project/ui/quest_dialog.gd 已实现 42 个事件标签的解析/状态机、图片、选择分页、输入、确认等待、地图信号、相机、淡入淡出、电影镜幕和副本结果信号;project/game_scene.gd 已连接对话、相机、淡入淡出、选择物品、小地图和 Atlas。
  • project/ui/quest_log.gdproject/ui/char_status_ui.gd 提供任务按钮和 5 条分页列表;project/ui/atlas_ui.gd 提供地图拼接、拖动、玩家/队伍/公会领地/观察者标记;project/atlas_navigation_system.gd 是另一个带硬编码地图/NPC/距离规则的本地 helper。
  • project/biologist_quest_system.gddynamic_bounty_event_system.gdevent_manager_system.gdmetin_spawn_event_system.gd 是本地游戏规则/活动 helper40250 Windows 客户端没有对应的客户端 authority 实现;它们不能替代任务/事件回包链。

Branch matrix

场景 40250 当前端 结论
任务 BEGIN/UPDATE BEGIN 注册全新实例并记录开始时间;UPDATE 只更新带 flag 字段 EntityStore 的 BEGIN/未知 UPDATE 会记录 clock_start_ms,并追加到 m_quest_order;普通 UPDATE 保持位置和起点,clock_value 更新会重置起点 PARTIAL:生命周期与本地计时已对齐,CTimer/服务端时间基准仍需端到端验证
任务 END DeleteQuestInstance(index) 后刷新 UI EntityStore::apply 与 classic parser 都删除 m_quests[index]、移除 m_quest_order 项并排入一次 quest_changes 刷新 PARTIAL:删除和顺序分支已对齐,BEGIN 时间戳和 UI 重建仍未闭合
任务倒计时 iStartTime 从 BEGIN 或 clock update 时记录,窗口关闭也继续倒计时 native 保存 clock_start_msQuestLog 和 CharStatusUI 每次显示按起点重新计算,关闭窗口期间不重置 PARTIAL:关闭/重开语义已对齐,精确 CTimer 时间基准、过期任务清理和后台恢复仍未端到端验证
任务列表顺序/索引 vector 顺序与 GetQuestIndex(row) 一致 EntityStore::m_quest_order 按 CPythonQuest vector 语义枚举;UPDATE 保持位置、BEGIN 追加、END 删除 PARTIALnative 顺序已对齐,真实 UI 行号/服务器序列仍未端到端验证
任务日志点击 -2147483648 + questIndex 按 unsigned 32-bit 发 CG_SCRIPT_BUTTON QuestLog/CharStatusUI 传入负数 sentinelM2Client 按 40250 的范围转换为 uint32 PARTIALwire sentinel 已对齐,真实 server 回包和任务窗口生命周期仍未测
任务信件按钮 新信件同 index 先删除旧按钮、插入最前;点击发送 script button 并删除按钮 QuestLog.recv_quest 顺序/点击删除逻辑已存在 PARTIAL:UI 结构接近,但实际任务日志负索引门控仍会阻断另一入口,重连/服务器重新发送未测
GC_SCRIPT 事件标签 42 标签按逐命令、等待、锁定和事件回调处理 parser/state machine 基本覆盖 42 标签;首命令 RUN_CINEMA 会读取 .msc 并替换事件流,缺失资源不打开空窗口 PARTIAL:真实 .msc 资源序列和中断清理仍未验证
对话按钮语义 QUESTION 发送 0-based answerNEXT/取消发送 254DONE 只关闭;ESC 根据按钮类型分支 QUESTION/NEXT/取消、DONE 的 DoneEvent/CloseSelf 和 ESC 的 QUESTION/NEXT/CANCEL 分支已对齐 PARTIAL:真实 server 脚本结束/重复提交/中断时序仍未完全覆盖
输入/选择/确认 INPUT→CG_QUEST_INPUT_STRINGSELECT_ITEM 打开选择窗;CONFIRM_WAIT 显示倒计时并超时取消;END_CONFIRM_WAIT 关闭 native/UI 信号和主要控件存在,选择/输入/确认测试通过 PARTIAL:真实超时、重复提交、断线和 server close 清理未覆盖
地图信号/Atlas ADDMAPSIGNAL 同时写小地图 signal point/Atlas waypoint 并打开 AtlasCLEAR 清除;SETCMAPPOS 调中心 GameScene 将同一坐标写入 Minimap 与 AtlasUIAtlas overlay 绘制橙色 waypoint 并支持清除 PARTIAL:两处消费链已对齐,真实 Atlas 资源、地图切换和多点生命周期仍未端到端验证
相机/淡入淡出/镜幕 相机设置、blend、restore 和 fade wait 由应用/事件窗完成;动画完成后释放 wait GameScene 已连接 camera/fadeQuestCurtain 可开关 PARTIAL:没有真实 .msc/资源和中断清理回归,skin/Done/ESC 边界与原版不一致
自定义任务/活动 helper 40250 客户端只负责显示/请求,规则通常由服务端给回包 当前 helper 本地推进生物学家、悬赏、活动掉落和倍率 GAPcustom authority):本地测试通过不能说明与 40250 Windows 客户端统一

Implementation-equivalence matrix

项目 状态 证据/限制
Preconditions PARTIAL native 阶段、answer/input 和 script-button sentinel 边界存在;事件窗口/重复提交/服务器任务存在性缺少门控
Branch structure PARTIAL 42 标签和 UI 主分支已覆盖;RUN_CINEMA 的缺失资源分支、Atlas 任务信号和部分中断分支已补齐/接通,真实过场消费者仍未验证
Algorithms/formulas PARTIAL 事件等待、颜色、相机参数和地图坐标转换大体存在;任务计时缺 reference 的 start-time 算法,硬编码 Atlas/NPC/活动 helper 不是 40250 客户端算法
State transition order PARTIAL 参考的 GC 任务包→创建/更新/删除→刷新主顺序已补齐;QuestLog 只在可见时递减,脚本 close/answer 仍有额外本地动作
Constants/units PARTIAL 5 条任务、30/16/24 字段、42 标签、5×9/按钮布局、254/255 answer 和 uint32 script-button sentinel 已对齐;skin 和地图信号显示语义未闭合
Timing/event sources PARTIAL native signal、毫秒事件等待和 fade callback 存在;任务 start timestamp、真实 .msc 事件、超时/断线/重连时序缺证据
Resource/data sources PARTIAL quest icon、ITEM/MOB 名称和 minimap DDS 使用资源接口;Atlas/NPC helper 另有硬编码 registryRUN_CINEMA 仍缺真实 .msc 资源与消费者证据
Protocol side effects PARTIAL GC_SCRIPT/QUEST_INFO/CONFIRM parser 和基本 CG quest packets 存在;任务按钮 wire、DONE close-only、ESC 的 answer 分支已补,电影文件注册未统一
Interruption/failure/cleanup GAP 任务结束删除已覆盖 native 两条解析路径,但地图切换、对话 ESC/DONE、确认超时、fade/cinema 中断、信号清理和任务列表重建没有完整 server-sequence 回归

Regression evidence

  • project/quest_test.gdproject/quest_event_test.gd:通过,覆盖脚本对话、确认、任务按钮和 42 标签/状态字段;使用 fake client/离线 parser,没有真实 GC_QUEST_INFO END、unsigned button 和 server sequence。
  • project/test_quest_dialog_parity.gd:修正 DONE/ESC/CANCEL 断言后 47 项通过;该测试现在确认 [DONE] 处理时发 done_event、按钮点击只关闭,QUESTION 的 ESC 发送最后选项,CONFIRM_WAIT 的取消发送 254。
  • project/test_quest_hud_parity.gd:19/19 通过;任务按钮布局和 fake quest array 通过,未覆盖 native unordered map、计时持续性和 M2Client::script_button 负数拒绝。
  • project/quest_curtain_test.gdproject/atlas_test.gdproject/test_atlas_navigation_parity.gdproject/test_map_name_shower_parity.gdproject/test_minimap_radar_parity.gd:通过;主要验证本地窗口、硬编码导航 registry、地图标记和假数据,未证明 ADDMAPSIGNAL 在 Atlas 的实际资源/生命周期等价。
  • project/test_biologist_quest_parity.gd:28/28 通过;验证本地生物学家任务 helper,不是 40250 Windows 客户端功能实现。
  • project/test_event_manager_parity.gd:24/24 通过;验证本地活动倍率/宝盒 helper,40250 客户端没有对应 authority,不能作为 parity PASS。
  • build-debug/extension/net_entity_testbuild-debug/extension/net_classic_session_testbuild-debug/extension/net_bounds_testbuild-debug/extension/net_classic_wire_test:通过基础 quest/script packet、quest END 删除、vector 顺序/UPDATE 活跃状态和负数 sentinel 检查;任务计时、DONE 语义、RUN_CINEMA 消费者和 Atlas 信号仍未覆盖。

Deep audit round 2026-09-21

Fresh regression results

  • godot --headless --path project --script quest_test.gdquest_event_test.gd:均退出码 0;脚本解析、确认、任务日志和 42 标签本地状态机通过。
  • godot --headless --path project --script test_quest_dialog_parity.gd:退出码 0,45/45 通过;但 DONE 断言明确要求发送 script_answer(254),与 40250 DONE 只关闭事件的实现相反,因此该条不能作为正确 parity 证据。
  • godot --headless --path project --script test_quest_hud_parity.gd:退出码 019/19 通过;fake quest array 的按钮、分页、计数和时间格式通过,但使用负数 script button 值的 fake client,未经过 native wire gate。
  • godot --headless --path project --script quest_curtain_test.gdatlas_test.gdtest_atlas_navigation_parity.gdtest_map_name_shower_parity.gdtest_minimap_radar_parity.gd:均退出码 0;窗口、Atlas、地图标记和小地图 helper 通过。
  • godot --headless --path project --script test_biologist_quest_parity.gd:退出码 0,28/28 通过;只验证本地生物学家任务 helper。
  • godot --headless --path project --script test_event_manager_parity.gd:退出码 0,24/24 通过;只验证本地活动倍率/宝盒 helper。
  • build/extension/net_entity_testnet_classic_session_testnet_classic_wire_test:均退出码 0;基础任务/脚本包通过。

Static parity findings

  • 40250 GC_QUEST_INFO BEGIN/UPDATE/END 的 END 会真正删除 quest instance;当前 EntityStore 对 END 未删除对应记录,旧任务可能继续出现在 get_quests 和日志中。
  • 40250 在 BEGIN/clock update 保存 iStartTime,窗口关闭时计时仍继续;当前端已在 QuestInfo::clock_start_ms 保存起点,并让 QuestLog/CharStatusUI 按起点计算剩余值,仍需验证 Godot 单调时钟与参考 CTimer::GetCurrentSecond() 的时间基准和后台行为。
  • 40250 任务列表保持 vector 顺序并通过真实 quest index 映射;当前端已加入 m_quest_order 并覆盖 BEGIN/UPDATE/END 的位置语义,仍缺真实任务日志回包到 UI 行号的端到端验证。
  • 40250 任务日志按钮使用 -2147483648 + questIndex 的 unsigned 32-bit wire 值;当前 M2Client::script_button(int) 看到负数直接拒绝,虽然 fake UI 测试把负数记录为成功。
  • QuestDialog 的 DONE、QUESTION/NEXT/CANCEL ESC 和确认取消已按 40250 分支对齐;真实 server 断线、重复输入和事件窗口中断仍缺端到端证据。
  • RUN_CINEMA 的首命令替换和 .msc loose-resource 读取已在 QuestDialog.begin_script();缺失/空文件现在按 40250 注册失败语义不打开空对话框,仍缺真实 .msc 资源序列/中断验证。ADDMAPSIGNAL 现在同时进入 Minimap 与 AtlasUI,仍缺真实地图资源和切图生命周期验证。
  • 本地 biologist_quest_system、活动倍率/宝盒 helper 是服务端规则的复制状态机,不属于 40250 Windows 客户端 authority,不能因 28/28、24/24 通过而标记 parity。

Round conclusion

本轮仍为 PARTIAL:任务/对话/Atlas 基础骨架和多数局部测试可运行,但 END 删除、持续计时、任务按钮 wire、DONE/ESC、电影播放消费者和 Atlas 信号消费存在明确差异,且现有 DONE 测试断言本身与 40250 不一致。本轮未修改实现代码。

Active UserInterface quest-event bridge review

  • PythonEventManagerMoudle.cpp 暴露从文件/字符串注册 event set、清理、限制数量、逐行位置/宽度、等待/跳过/结束、handler/answer、可见起始行和 SendScriptButtonPacket;位置更新使用事件管理器的坐标变换,不能只用一个对话框文本数组替代。
  • PythonGameEventManagerModule.cpp 将中心位置注入 CGameEventManager 后调用 Update,是过场/地图事件的逐帧桥接;PythonQuest.h 维护 quest instance 的标题、clock/counter 名和值、图标、创建/删除和索引查询。
  • 当前端有脚本解析和 QuestDialog/Atlas 骨架,但 event-set 生命周期、handler 回调、持续计时/逐帧位置、任务实例删除以及日志按钮 wire 值尚未统一;RUN_CINEMA 和地图信号也没有发现完整消费者,合同保持 PARTIAL

Static review evidence

已将 PythonEventManagerMoudle.cppPythonGameEventManagerModule.cppPythonQuest.h 纳入 active source 证据;本轮只确认 reference bridge 的完整入口和状态字段,没有把局部脚本测试当作行为等价。

Status

PARTIAL。当前端已经有较完整的任务脚本解析、对话控件、任务 HUD、地图和过场骨架,42 个事件标签与基础 native packet 可工作;但任务生命周期/计时/顺序、ESC 语义、电影事件消费和 Atlas 任务信号存在明确差异,另有多个本地规则 helper 不是 40250 客户端实现。DONE、任务 END 和 script-button wire 已有对应修复。

Implementation fix round 2026-09-21 — quest END removes the instance

按 40250 CPythonNetworkStream::RecvQuestInfoPacket() 的明确分支复刻任务结束语义: QUEST_SEND_IS_BEGIN 旗标存在且后续 isBegin == 0 时,参考端将包分类为 QUEST_PACKET_TYPE_END,调用 CPythonQuest::DeleteQuestInstance(index),随后刷新任务 列表。当前端此前只把 begin 改成 falseget_quests() 仍会枚举原有记录,造成已结束 任务继续显示。

现已在 EntityStore::mut_quest_infoclassic parser)和 EntityStore::apply(协议兼容 路径)加入同一删除分支,并保留一次 quest_changes 刷新事件。新增的 native 回归先在 修复前确认“结束任务仍存在”失败,修复后 net_entity_testnet_classic_session_testgit diff --check 均通过。

合同仍为 PARTIAL:任务 BEGIN 的 start timestamp、vector 顺序、负数 script-button sentinel、DONE/ESC、RUN_CINEMA 消费者、Atlas 信号和断线清理仍未闭合。

Implementation fix round 2026-09-21 — quest-log script-button sentinel

继续按 40250 uicharacter.py::QuestButtonClickCPythonNetworkStream::SendScriptButtonPacket(unsigned int) 核对,确认任务日志入口的 -2147483648 + questIndex 不是非法参数,而是写入 uint32 wire 字段的任务索引 sentinel。此前 M2Client::script_button(int)idx < 0 直接拒绝,导致 QuestLog/CharStatusUI 的任务日志点击无法到达服务器。

现已允许普通非负 uint32 索引,以及 INT32_MIN .. INT32_MIN + 0xffff 的任务索引 sentinel;其他负数仍拒绝,避免把任意错误值包装成大整数。net_bounds_test 已覆盖 普通端点、sentinel 端点和越界负值,构建与运行通过。

本合同仍为 PARTIAL:任务 BEGIN 的 start timestamp、vector 顺序、RUN_CINEMA 消费者、 Atlas 信号和断线清理仍未闭合。

Implementation fix round 2026-09-21 — CANCEL and ESC answer branches

继续按 40250 uiquest.py::MakeNextButton / OnPressEscapeKey 修复剩余对话关闭分支: BUTTON_TYPE_CANCEL 的取消按钮发送 SCRIPT_ANSWER(254)ESC 在 QUESTION 时发送最后 一个选项索引,在 NEXT/CANCEL 时发送 254,在 DONE 时只关闭;没有可见答复按钮时才 回落到 native 的 255 cancel sentinel。此前当前端统一调用 quest_cancel(),会把 CONFIRM_WAIT 和 QUESTION/NEXT 的关闭语义压成错误的 255/取消路径。

新增回归先确认修复前 QUESTION ESC 与 CONFIRM_WAIT 取消断言失败,修复后 test_quest_dialog_parity.gd 47 项、quest_test.gdquest_event_test.gdgit diff --check 通过。

本合同仍为 PARTIAL:任务时钟与列表顺序、RUN_CINEMA 资源消费者、Atlas 信号、真实 服务端脚本序列和断线/重连清理仍未闭合。

Implementation fix round 2026-09-21 — DONE event and close-only button

按 40250 PythonEventManager::ProcessEventSet(EVENT_TYPE_DONE)uiquest.py::MakeNextButton(BUTTON_TYPE_DONE) 修复当前任务对话时序:[DONE] token 被消费时立即触发 DoneEvent;随后生成的“关闭”按钮只调用 CloseSelf(),不发送 CG_SCRIPT_ANSWER(254)。此前当前端把 done_event 延迟到按钮点击,并把 DONE 错误地 当成 NEXT/取消发送 254。

新增回归先把测试断言改为参考行为并确认修复前失败;修复后 test_quest_dialog_parity.gd 45 项、quest_test.gdgit diff --check 通过。

本合同仍为 PARTIALESC 尚未按 CANCEL/DONE/NEXT 分支完全复刻,任务时钟、索引顺序、 RUN_CINEMA 消费者、Atlas 信号和断线清理仍未闭合。

Implementation fix round 2026-09-21 — quest instance vector order

按 40250 CPythonQuest::RegisterQuestInstanceMakeQuestDeleteQuestInstance 的容器语义修复任务列表顺序:参考端使用 vector 保存任务实例, 普通 UPDATE 不改变位置,未知 UPDATE 在末尾创建,BEGIN 先删除同 index 的旧实例再把新 实例追加到末尾,END 同时删除实例和对应顺序项。当前端此前直接枚举 unordered_map<uint16_t, QuestInfo>,相同任务回包在不同运行中可能改变任务行顺序。

现已在 EntityStore 增加 m_quest_orderclassic mutator 与协议兼容 apply 两条路径 共用相同的注册/删除规则;quest_indices() 只按该顺序返回仍存在的任务。修复前新增的 顺序回归按预期失败,修复后 net_entity_testnet_classic_session_testnet_bounds_testgit diff --check 均通过。

本合同仍为 PARTIAL:精确时间基准、真实任务日志到 UI 行号映射、 RUN_CINEMA/Atlas 生命周期、服务端脚本序列和断线清理仍未闭合。

Implementation fix round 2026-09-21 — classic UPDATE keeps the quest active

复核发现 classic parser 的 mut_quest_info 与协议兼容 apply 对同一 UPDATE 的结果不 一致:前者会把已有任务的 begin 暂存字段写成 false,后者保持为有效实例。40250 在 QUEST_PACKET_TYPE_UPDATE 中只调用 MakeQuest(仅当任务不存在)和各字段 setter, 不会把已存在任务变成结束态;只有 QUEST_PACKET_TYPE_END 才删除实例。

现已让 classic mutator 对所有非 END 的任务包保持 QuestInfo.begin == true,并增加了 “UPDATE 保持活动实例且更新标题”的回归。修复前该断言失败,修复后 net_entity_testnet_classic_session_testgit diff --check 通过。

本合同仍为 PARTIAL:任务 start timestamp、真实 UI 行号映射、RUN_CINEMA/Atlas 生命周期、 服务端脚本序列和断线清理仍未闭合。

Implementation fix round 2026-09-21 — ADDMAPSIGNAL reaches Atlas

按 40250 PythonEventManager::ProcessEventSet(EVENT_TYPE_ADD_MAP_SIGNAL)CPythonMiniMap::AddSignalPoint 核对,任务脚本坐标不只是圆形小地图的边缘提示;参考端 同时调用 AddWayPoint(TYPE_WAYPOINT, ...),因此 Atlas 也能显示同一个 waypoint CLEARMAPSIGNAL 会移除两处记录。

当前端的 GameScene 现在把 map_signal_added 同时转发给 MinimapAtlasUI Atlas overlay 使用与小地图相同的 server-centimetre→world→map 坐标转换绘制橙色标记, 并由 map_signals_cleared 同时清理。atlas_test.gd 新增添加/清理回归,测试通过。

本合同仍为 PARTIAL:真实 Atlas 资源、地图切换后的 signal 生命周期、RUN_CINEMA 真实 .msc 序列和断线/中断清理仍未闭合。

Implementation fix round 2026-09-21 — missing RUN_CINEMA resource

按 40250 CPythonEventManager::RegisterEventSetFromString() 的首命令特例和 CPythonNetworkStream::RecvScriptPacket() 的返回值判断修复过场脚本失败分支: RUN_CINEMA 指向不存在或空路径时,参考端注册返回 -1,不会调用 OnScriptEventStart();当前端现在清理临时 event set、保持窗口关闭且不发出 opened, 不再显示空白锁定的任务对话框。有效 .msc 仍沿当前 loose-resource 读取路径替换事件流。

test_quest_dialog_parity.gd 新增缺失资源回归,当前 49 项全部通过;真实 .msc 文件的 完整事件序列、过场播放器消费者和中断清理仍保持 PARTIAL