Files
mtgodot-poc/audit/contracts/audio.bgm-ambience-3d.md
T
shenleiandClaude Opus 5 a989b8f19b chore: delete 106 unreachable *_system.gd and their parity tests
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>
2026-09-22 17:51:52 +09:00

11 KiB
Raw Blame History

audio.bgm_ambience_3d —— BGM、区域环境音、角色音效、3D 监听器和前后台恢复

状态:PARTIAL

本合同检查 40250 的 2D/3D/Stream 三类声音实例、BGM 音乐状态机、区域环境声、角色动作音效、监听器坐标/方向、音量曲线、限频和前后台生命周期。判断标准是播放实例的分配、状态转换、坐标/音量公式、重复播放限制、地图卸载和恢复副作用一致;“测试能播放”不等于 SoundManager 生命周期等价。

40250 参考调用链

  • MilesLib/SoundManager.cpp / SoundManager2D.cpp / SoundManager3D.cpp / SoundManagerStream.cpp
    • Create 按 2D、Stream、3D 顺序初始化驱动;Destroy 先停止音乐和 3D 实例,再关闭 listener/provider/driver。
    • 2D/3D 音效使用固定实例池,实例完成后才可复用;3D 位置按 (world-listener)/soundScale,环境声使用 ambienceSoundScale。
    • 角色音效可按 5000cm 半径过滤,并按文件名限制 0.3 秒内重复播放;StopAllSound3D 同时清空历史。
  • MilesLib/SoundManager.cpp
    • BGM 维护多个 TMusicInstance,状态为 OFF、FADE_IN、FADE_LIMIT_OUT、FADE_OUT、PLAY;Update 每帧递增/递减音量,达到边界后切换状态或停止实例。
    • FadeInMusic 先淡出已有音乐;FadeOutAllMusic 为每个活动实例设置淡出;FadeLimitOutMusic 保留目标音量。
    • SaveVolume 同时把音乐和音效音量置零并保存备份,RestoreVolume 恢复两者;不是只停止或只静音 BGM。
  • MilesLib/SoundManager3D.cpp / PythonSoundManagerModule.cpp
    • 暴露 PlaySound、PlaySound3D、PlayMusic、FadeIn/FadeOut、StopAllSound、音乐/音效音量、SoundScale 和 AmbienceSoundScale。
    • 3D listener 维护位置、方向、up 和速度;环境声由 CArea::UpdateAroundAmbience 每次更新。
  • GameLib/Area.cpp / Area.h
    • TAmbienceInstance 从 AreaAmbienceData/Property 读取位置、范围、最大音量半径、LOOP/ONCE/STEP 和步进随机间隔。
    • LOOP 在范围内创建循环实例并按距离线性衰减;离开范围停止并清理索引。ONCE 只在进入范围时播放,STEP 按时间到期播放,离开时重置下一次时间。

当前端实现映射

  • project/audio.gd
    • 已有 BGM 双播放器、UI 音效池、AudioStreamPlayer3D、listener 重定位、200/1000 缩放、音量比例曲线、.mss 60FPS 事件、角色音效距离/0.3 秒限频,以及 Area LOOP/ONCE/STEP。
    • 3D 播放器按需创建,没有 40250 固定实例池/Provider 生命周期;Godot unit_size/max_distance 衰减也不是 Miles provider 的同一声学模型。
    • BGM 使用 Tween 交叉淡入淡出,不是 TMusicInstance::Update 的逐帧状态机;加载失败时 _bgm_name 先写入,存在“资源失败后同名请求被跳过”的状态风险。
  • project/bgm_director.gd / project/game_scene.gd
    • 已按 bgm_changed 和 world_reset 选曲,服务器音量优先,缺省回落 musicInfo.fieldMusic,并保存最近战场曲目。
    • 生产链已创建 Audio、BgmDirector 并在每帧更新 listener/区域环境声;地图切换时会停止 BGM,但没有完整证明参考端多音乐实例的淡出状态被逐个清理。
  • project/app_lifecycle.gd
    • 已在后台保存并恢复音量、暂停树、降低 FPS,保持 BGM 名称。
    • 后台入口调用 save_volume 后同步执行 set_music_volume(0.0) 和 set_sound_volume(0.0);恢复仍由 restore_volume 一次恢复两类音量。
  • project/game_scene.gd
    • _exit_tree 在卸载 native world 前调用 Audio.shutdown(),停止 BGM/3D 音效、解绑播放器 stream 并清空声音缓存,覆盖退出时的资源释放边界。
  • formats/area_data.* / extension/src/metin2_world.cpp
    • 已读取 AreaAmbienceData、.pra 属性和 playtype/range/maxvolume/playinterval,按流式 tile 暴露 ambience source;world unload 会移除来源。
    • 来源数据和播放状态由不同节点维护,地图卸载/快速切图时音频实例和来源索引的生产级清理尚未有组合测试。

分支等价矩阵

40250 判据/副作用 当前实现 结论
2D/UI/3D/Stream 三类初始化、实例复用和销毁 Audio 创建 BGM/UI/3D 节点,使用 Godot 动态播放器 部分等价;缺少固定实例池、Miles provider/driver 生命周期和完整销毁证据
BGM 多实例 OFF/FADE_IN/FADE_LIMIT_OUT/FADE_OUT/PLAY 状态机 双播放器 Tween 交叉淡入淡出 部分等价;目标可听结果近似,但状态/边界/多个音乐实例和每帧速度公式不一致
FadeIn 先处理旧曲、FadeLimitOut、FadeOutAll 有对应方法和淡出 Tween 部分等价;没有逐实例状态快照和停止时序证明,加载失败状态回滚也不完整
音量比例/等级曲线、音乐/音效独立音量 ratio_to_apply_volume、grade 和 UI 设置测试通过 基础公式等价;后台 BGM/SFX 同时静音与恢复已有回归,真实 3D 播放实例仍需独立证据
SaveVolume/RestoreVolume 同时静音和恢复 BGM/SFX 保存两类值,后台同时调用 set_music_volume(0)/set_sound_volume(0),前台调用 restore_volume 基础行为等价;真实 Miles provider 实例和 3D 播放器清理仍需独立证据
listener 位置/方向、200/1000 缩放和 3D 播放 GameScene._process 更新 listener,Audio 重定位实例 部分等价;单位与主要公式存在,Godot attenuation/provider 行为不是 Miles 1:1
角色动作 .mss 事件、5000cm 距离和 0.3s 限频 PlayerView/Audio 已实现,相关测试通过 基础路径通过;远端角色、全部动作事件和实例池耗尽/失败回退未覆盖
Area LOOP/ONCE/STEP、距离衰减和离开范围清理 world source + update_ambience_sources 已实现 部分等价;A3 真实来源测试通过,但快速换图、音频实例释放和多 source 乱序缺少证据
地图 BGM/区域声/音效在切图、失焦、暂停、重连后恢复 BGM 停止、listener/ambient 每帧更新、后台 BGM/SFX 恢复存在 部分等价;跨图活动实例清理和真实 3D 播放器释放尚未形成生产级闭环

已执行测试

  • project/audio_miles_test.gd:通过;覆盖 200/1000 缩放、音量曲线、.mss 解析、角色音效距离/限频和环境声实例基本路径。
  • project/ambience_test.gd:通过;A3 真实地图来源解析出 1 个 LOOP 环境声,范围、类型和声音路径保持。
  • project/bgm_test.gd:通过;覆盖地图 BGM 选曲、服务端音量、默认回落、换图信号,以及前后台 BGM/SFX 音量保存、同时静音和恢复。
  • project/rendering_scenario_test.gd:通过;Debug 原生库退出无 ObjectDB 音频资源泄漏,验证 GameScene._exit_tree -> Audio.shutdown 的退出边界。
  • project/test_bgm_jukebox_parity.gd:18 passed、0 failed;覆盖点唱机曲目注册、停止、音量钳制、地图默认和覆写恢复,不覆盖真实 Audio 实例状态机。
  • project/app_flow_lifecycle_test.gd:通过;覆盖 AppLifecycle 单一属主和断线重连流程,不覆盖真实音频实例释放。
  • project/system_option_ui_test.gd:通过;覆盖音量设置控件绑定,不覆盖后台和播放失败边界。

Deep audit round 2026-09-21

Fresh regression results

  • audio_miles_test.gd、ambience_test.gd、bgm_test.gd、test_bgm_jukebox_parity.gd 18/18、app_flow_lifecycle_test.gd、system_option_ui_test.gd 均退出码 0。
  • 回归覆盖了 3D 缩放/限频、A3 环境声、地图 BGM 选择、点唱机覆盖/恢复、生命周期属主和设置绑定;FakeAudio 仍不能证明真实播放实例状态机。

Static parity conclusion

  • BGM/区域环境声/角色 .mss 音效的主要入口存在,但 Godot 动态播放器/Tween 不等价于 40250 固定实例池、Provider 和 TMusicInstance 状态机。
  • app_lifecycle.gd 后台路径已同步静音并恢复 BGM/SFX;加载失败后 _bgm_name 可能阻止同名重试,跨图快速切换时来源索引和播放器释放仍缺少生产级证据。
  • listener、缩放和 0.3s/5000cm 规则有局部验证,但远端 Actor 动作音效、实例池耗尽、失焦/暂停/重连/退出清理和 Godot attenuation 与 Miles provider 的边界未闭合。

Active source group review

  • 对 Visual Studio MilesLib 活跃编译单元逐组核对后,参考链补充确认:CSoundManager2D/3D/Stream::Initialize/Destroy 创建并释放固定实例池;CSoundData 维护 pack-backed 数据缓存和 access/play time;CSoundInstance2D/3D/Stream 分别处理 SetSound、Play/Stop、位置/方向/速度与音量;CSoundManager 负责 listener、距离缩放、0.3 秒限频、环境声、音乐实例索引和多状态淡入淡出。
  • 当前端 Audio 的动态播放器和 Tween 只覆盖可听结果的部分路径,尚未证明固定池、sound data cache、播放失败/空槽、逐实例停止和 MusicIndex 状态机等副作用一致;本组静态核对完成,合同仍为 PARTIAL。

Implementation fix round 2026-09-22T08:50Z

  • 修复前 bgm_test.gd 新增的 40250 SaveVolume 回归失败:进入后台后 master_bgm=0,但 master_sfx=0.9。
  • 按 CSoundManager::SaveVolume 的顺序,AppLifecycle._enter_background 在保存备份后同时调用 set_music_volume(0.0) 与 set_sound_volume(0.0);_exit_background 继续由 restore_volume 同时恢复两者。
  • 修复后 bgm_test.gd、audio_miles_test.gd、app_flow_lifecycle_test.gd、disconnect_world_reset_test.gd、netbridge_test.gd 均通过;渲染退出回归无 ObjectDB 泄漏。
  • 该轮只闭合后台音量副作用和退出音频缓存清理,BGM 多实例状态机、Miles 固定实例池、真实 3D 衰减/Provider、快速换图来源清理仍保持 PARTIAL。

本轮仍为 PARTIAL,完成后台音量和退出清理的实现及第二轮证据登记。

已确认差异和未验证项

  1. 验证正在播放的 3D 实例不会绕过 master SFX,并补真实设备/实例路径的后台静音回归。
  2. 建立 BGM 多实例状态快照测试,覆盖 FadeIn、FadeLimitOut、FadeOut、FadeOutAll、相同曲目重入、加载失败回滚和切图清理。
  3. 明确 Godot 3D 衰减与 Miles provider 的适配边界,至少固定 unit_size/max_distance、监听器方向/速度和实例池耗尽行为。
  4. 增加真实流式地图快速切换、来源删除、重复 key、LOOP/ONCE/STEP 乱序和退出时无悬挂播放器的测试。
  5. 补齐远端 Actor 的动作音效触发、重连/死亡/换图清理和资源缺失回退证据。

下一轮验证入口

  • 先审计 lifecycle.platform_release,核对 Windows 窗口、导出、自包含资源、崩溃处理和发布门禁。
  • 回到实现阶段时,优先建立 BGM 多实例状态机和真实 3D 播放器清理测试,再处理固定实例池/Provider 的适配边界。

2026-09-22 死代码删除

以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:test_bgm_jukebox_parity.gd。