# world.environment_weather —— 天空、环境、光照、昼夜、雪和天气 状态:`PARTIAL` 本合同检查 40250 的 `.msenv` 环境加载、背景/角色光照、线性雾、天空六面、云层、滤色、lens flare、风、雪粒子和地图环境生命周期。判断标准是同一份环境数据是否经过同一类调用链、在同一时序产生相同的可见状态和清理副作用;“字段已解析”或“测试能创建粒子”不能代替渲染与正式场景接入等价。 ## 40250 参考调用链 - `GameLib/MapOutdoorRender.cpp` / `MapOutdoorRenderHTP.cpp` / `MapOutdoorRenderSTP.cpp` - `OnSetEnvironmentDataPtr` 将环境数据分派到 screen filter、SkyBox 和 lens flare;渲染顺序是天空、云、环境开始、地形/角色/水/雪/特效、环境结束和 lens flare。 - `OnBeginEnvironment` 将 `.msenv` 的 fog near/far/color、背景方向光和角色方向光分别应用到 SpeedTree、terrain patch 和角色渲染;风强度进入森林渲染器。 - `SetEnvironmentSkyBox` 使用 scale、gradient、texture render mode、六面纹理、云纹理、云高度/缩放/颜色/滚动速度,并通过 SkyBox transition 更新环境。 - `GameLib/SnowEnvironment.cpp` / `SnowParticle.cpp` - 雪环境由专用粒子池、相机/世界范围、风/重力和粒子生命周期驱动,随 outdoor 渲染阶段更新和绘制,不是一次性创建的通用屏幕粒子节点。 - `Client/Eternexus/root/servercommandparser.py` / `game.py` - 服务器 `xmas_snow 0/1` 经 `ServerCommandParser` 进入 `__XMasSnow_Enable`,先按 `__IsXMasMap` 过滤,再调用 `background.EnableSnow(0/1)`;该命令不是按地图名称自动推导天气。 - `EterLib/EnvironmentMap.cpp` - 环境对象作为 outdoor 环境资源的生命周期边界参与加载/卸载;环境切换必须同步释放旧天空、雾、滤色、lens flare 和粒子状态。 ## 当前端实现映射 - `formats/environment.*` / `extension/src/environment_builder.*` - 已解析 `.msenv` 的方向光、fog、sky gradient/六面/云、filter 和 lens flare 字段;`apply_environment` 已创建背景 `Sun`、角色 `CharacterLight` 和 `WorldEnvironment`,texture-mode sky 已用 shader 采样六面和滚动云层。 - filter、lens flare、wind 目前主要保存在 `WorldEnvironment` metadata;没有对应运行时后处理、光晕节点或 SpeedTree 风更新。fog 使用 Godot depth fog,并主动 clamp/push begin/end、curve/density 与参考端 D3D 线性雾不相同。 - `extension/src/metin2_world.cpp` / `project/game_scene.gd` - `.msenv` 在正式地图加载时接入 `Metin2World`,世界卸载会移除环境节点;GameScene 会优先采用 `.msenv` 环境,避免兜底光照覆盖它。 - 生产场景创建 `fx/weather.gd`,并由 `EntityStore::apply_server_command -> M2Client.weather_changed -> GameScene._on_weather_changed` 接收 `xmas_snow`;场景按 40250 四张排除地图过滤,并在换图后重新应用当前开关。 - `WeatherDayNightSystem` 仍未实例化到正式场景;它的按地图自动天气/昼夜预设不作为 40250 生产行为。 - `project/fx/weather.gd` - 使用跟随相机的单个 `GPUParticles3D` 实现 snow/rain/none,粒子数量、重力、速度和颜色可切换;它没有 40250 `SnowEnvironment/SnowParticle` 的池、风向、范围、渲染阶段或资源失败回退链。 - `project/weather_daynight_system.gd` - 是当前端独立的昼夜/地图天气 helper,能计算 phase、天气和光照字典;但其地图预设与 40250 `.msenv` 静态环境不是同一数据源,且正式 GameScene 未消费它。 ## 分支等价矩阵 | 40250 判据/副作用 | 当前实现 | 结论 | |---|---|---| | `.msenv` 解析、地图加载、环境节点创建和卸载 | `formats/environment` + `Metin2World::load_map` + `apply_environment` | 基础链路通过;复杂地图/重入清理仍需扩展证据 | | 背景/角色两套方向光和可见层 | `Sun` layer 1、`CharacterLight` layer 2 | 部分等价;光照色/能量和角色渲染消费仍需逐场景比对 | | fog near/far/color、density/linear 分支、terrain/HTP/STP 使用 | Godot depth fog,begin/end 被 clamp,curve=0.5、density=0 | 不等价;参考的线性 D3D 雾公式和 LOD 分支没有 1:1 保持 | | 六面天空、gradient、scale、texture mode、cloud plane/UV | Godot sky shader/ProceduralSkyMaterial,六面和云测试通过 | 部分等价;shader 采样与参考 quad/transition 仍需像素和边界比对 | | screen filter alpha blend 和 lens flare | 字段只写 metadata,无运行时后处理/flare 渲染节点 | GAP | | 风强度进入森林/树木更新 | 固定 `msenv_wind_strength=0.2` metadata,无 SpeedTree/树节点风更新 | GAP | | SnowEnvironment/SnowParticle 池、范围、风/重力和 outdoor render 时序 | 单个相机跟随 GPUParticles3D | 部分等价;粒子可切换不代表池/时序/清理等价 | | 服务器 `xmas_snow 0/1`、`__IsXMasMap` 过滤和雪开关 | typed command -> `weather_changed` -> GameScene map filter -> `Weather.set_weather` | 部分等价;命令/过滤时序已对齐,但粒子池/渲染阶段不是 SnowEnvironment 1:1 | | `.msenv` 静态地图环境 vs 昼夜/天气切换 | 正式链消费服务器 `xmas_snow`;`WeatherDayNightSystem` helper 仍独立存在且不驱动生产地图 | 部分等价;40250 的 `DayMode/PRESERVE_DayMode` 和环境切换仍未接入 | | 地图切换、暂停、重载和失败清理 | world 环境节点 queue_free;天气节点未按地图环境重置/验证 | 部分等价;缺少环境/粒子/云资源失败和快速切图组合证据 | ## 已执行测试 - `project/environment_test.gd`:通过;A1 `.msenv`、双方向光、WorldEnvironment、云纹理和 wind/filter metadata 可读取。 - `project/skybox_test.gd`:通过;CapeDragonHead/DawnMistWood texture-mode 六面天空、云层、scale 和 UV speed 断言通过。 - `project/p9_test.gd`:通过;通用 weather snow/rain/none 粒子开关路径通过,但不是参考雪环境池测试。 - `project/test_weather_daynight_parity.gd`:22 passed、0 failed;仅覆盖 `WeatherDayNightSystem` helper 的默认时相、时间推进、天气预设和信号,不覆盖正式 GameScene 接入或 40250 `.msenv` 渲染。 - `extension/tests/net_entity_test.cpp` / `build/extension/net_entity_test`:通过;覆盖 `GC_CHAT/COMMAND` 的 `xmas_snow 1/0` typed event 顺序。 - `project/gamescene_test.gd`:通过;覆盖正式 GameScene 对 eligible map、四张 40250 排除地图和关闭命令的雪开关处理。 - `project/netbridge_test.gd`:通过;确认 `M2Client.weather_changed` 信号已注册。 ## Audit evidence round 2026-09-22T09:30Z - 重新运行 `environment_test.gd`、`skybox_test.gd`、`p9_test.gd` 和 `test_weather_daynight_parity.gd`,结果分别通过、通过、通过、22/22 通过。 - `environment_test.gd` 实际加载 A1 `.msenv` 地图,`skybox_test.gd` 实际加载 CapeDragonHead/DawnMistWood 六面天空;这些结果确认 `.msenv`/天空基础链路仍可用,但不改变 filter、lens flare、wind、线性 fog 和 SnowEnvironment 池的差异结论。 - `test_weather_daynight_parity.gd` 仍然只实例化独立 `WeatherDayNightSystem`;对照 `GameScene::_build_world` 当前只创建 `fx/weather.gd`,未发现正式地图加载时消费该 helper 的调用边沿,因此不能将 22/22 记为生产天气接入通过。 - 本轮修复了服务器 `xmas_snow` 的命令接入和正式场景地图过滤;合同继续保持 `PARTIAL`,下一实现入口是 `DayMode/PRESERVE_DayMode`、SnowEnvironment 池以及已确认的 filter/lens flare/wind GAP。 ## Deep audit round 2026-09-21 ### Fresh regression results - `environment_test.gd`、`skybox_test.gd`、`p9_test.gd` 均退出码 0;真实 A1/CapeDragonHead/DawnMistWood 环境、六面天空、云参数和基础天气切换断言通过。 - `test_weather_daynight_parity.gd` 22/22 通过,但它验证的是独立 `WeatherDayNightSystem` helper,不证明正式 `GameScene` 已实例化并消费天气系统。 ### Static parity conclusion - `.msenv` 解析、背景/角色光、SkyBox 和地图环境节点的基础链路存在;filter、lens flare、wind 仍主要停留在 metadata,未形成 40250 的运行时后处理、光晕和树木风更新。 - Godot depth fog 的 clamp/curve/density 与 40250 线性 D3D 雾不等价;六面天空/云 shader 有结构测试,但没有与参考 transition/render order 的像素 oracle。 - 生产场景已接入 40250 的服务器 `xmas_snow` 命令和地图排除过滤,但没有接入 `DayMode/PRESERVE_DayMode`;单个 GPUParticles3D 也不等价于 SnowEnvironment/SnowParticle 池和 outdoor render 时序。 本轮仍为 `PARTIAL`,完成服务器天气命令的实现和第三轮环境/天气证据登记。 ### Implementation fix round 2026-09-22T10:00Z - 修复前 `gamescene_test.gd` 的生产链回归无法让 `xmas_snow=1` 启用雪;native command-bus 也没有天气事件。 - 按 40250 `ServerCommandParser.__XMasSnow_Enable -> game.__XMasSnow_Enable -> background.EnableSnow`,新增 `EntityStore::WeatherChanged` typed event、`M2Client.weather_changed(bool)` 信号,以及 `GameScene._on_weather_changed -> _apply_server_weather`。 - `_apply_server_weather` 复刻 `__IsXMasMap` 的四张排除地图:`metin2_map_n_flame_01`、`metin2_map_n_desert_01`、`metin2_map_spiderdungeon`、`metin2_map_deviltower1`;地图切换后重新计算开关,避免服务器命令状态污染新地图。 - 修复后 native `net_entity_test`、`gamescene_test.gd`、`netbridge_test.gd`、`p9_test.gd`、`environment_test.gd` 均通过。Godot 粒子仍只是平台适配,SnowEnvironment 池、DayMode、filter/lens flare/wind 和线性 fog 差异继续保留。 ### Active EterLib environment-render review - `EterLib/SkyBox.cpp` 实际维护天空六面纹理、天空/云纹理、gradient、scale、scroll、天气/颜色过渡和 render 顺序;`ScreenFilter.cpp`、`LensFlare.cpp` 是独立的屏幕滤镜与镜头光晕渲染对象,不只是环境字典字段。 - 当前 `WorldEnvironment`/sky shader 能提供天空和雾的部分视觉结果,但 `filter`、lens flare 和风更新主要停留在 metadata,没有对应运行时后处理/光晕对象;Godot depth fog 的 clamp、curve 和 density 也不等价于参考端的 D3D 线性雾分支。 - 因此本轮完成 `SkyBox`、`ScreenFilter`、`LensFlare` 活跃源码静态核对;天空结构测试通过不能证明 transition/render order、滤镜、光晕和地图环境切换副作用一致,合同保持 `PARTIAL`。 ## 已确认差异和未验证项 1. 保持 filter/lens flare/wind 的明确 GAP,或实现对应的后处理、光晕和树木风更新,并为每个资源失败/禁用分支补测试。 2. 以 40250 的 fog near/far、天空 transition、云 plane、背景/角色光照和 render order 建立截图/参数 oracle,禁止只用 metadata 断言代替渲染证据。 3. 补齐 `DayMode/PRESERVE_DayMode` 的正式环境切换和场景生命周期;`WeatherDayNightSystem` 的自动地图预设继续隔离,不能替代 40250 服务器命令。 4. 建立真实地图快速切换、暂停恢复、环境资源缺失、重复加载/卸载和粒子/天空无悬挂节点的组合测试。 5. 对 SnowEnvironment 的池化、世界范围、风/重力、相机边界和 outdoor render 时序补专门实现或明确平台适配合同。 ## 下一轮验证入口 - 进入实现阶段后,优先修复正式 GameScene 的天气/地图环境接入和 fog/filter/lens flare/wind 中的已确认 GAP;修复后重新运行本合同四组测试并增加真实渲染证据。