# 40250 客户端对拍缺陷发现与修复工程指南 (CLIENT-PARITY-AUDIT-AND-FIX-GUIDE) > **目标**:建立系统化的排查、探针探测与修复流程,解决 Godot 客户端从 40250 移植过程中由于“管线实现不完整”、“只实现裸数据漏掉复合层”导致的隐性细节缺陷(典型案例如:桥梁可行走但高度采样遗漏 `.mdatr` 导致掉落穿透)。 --- ## 一、问题本质剖析:为何会出现此类缺陷? 在 Metin2 40250 原版中,几乎所有物理、移动、战斗与视觉表现都是**“多层复合叠加”**的。在向 Godot 移植的早期原型阶段,很多系统往往只实现了最底层的第 1 层,缺少了第 2 层和第 3 层的复合逻辑: ```mermaid flowchart TD subgraph 原版 40250 表现 L1["基础数据层 (如: PRTerrain height.raw 裸高度图)"] L2["静态实体层 (如: CGraphicThingInstance / .mdatr 高度多边形)"] L3["动态状态层 (如: 骨骼动作高度、骑乘偏置、水体/浮力判定)"] L1 --> L2 --> L3 --> R1["最终真实表现 (如: 角色稳稳立于桥面)"] end subgraph 缺陷实现模式 D1["基础数据层 (fmt::terrain_height_at)"] D1 --> R2["直接作为最终结果 (角色跌落到桥底裸地形)"] end ``` 如果仅依赖玩家跑图偶然触发,发现成本极高且容易重复反弹。必须转为**白盒走查、探针自动化与协议日志审计**。 --- ## 二、高危缺陷领域清单与 40250 真实机制对照 | 领域 | 常见隐性缺陷表现 | 40250 原版真实机制(唯一真相源) | 关键代码位置 | | :--- | :--- | :--- | :--- | | **1. 空间与地貌 (Terrain & Objects)** | • 桥梁、楼梯、门廊台阶悬空或掉落穿透
• 水陆交界处判断错误(干地游泳或踩深水不减速/不可钓鱼)
• 地下城/山洞进出门廊穿透掉落 | • `CMapOutdoor::GetHeight` 查询 `.mdatr` 中的 `heights` 网格多边形,与裸高度图取 `fMAX`
• `attr.atr` 中的 `ATTRIBUTE_WATER` 与水面多边形绝对高度差
• `CDungeonBlock::GetObjectHeight` | `GameLib/MapOutdoor.cpp`
`EterLib/AttributeInstance.cpp`
`GameLib/AreaTerrain.cpp` | | **2. 物理与移动 (Physics & Locomotion)** | • 遇到陡坡时直接穿山或以相同速度瞬移上山
• 被怪物或技能击退(Knockback)时穿透墙体飞出地图
• 自动寻路走直线卡死在栅栏/岩石缝隙 | • `ActorInstanceSync.cpp` 坡度倾角与重力滑落向量计算
• `ActorInstanceCollisionDetection.cpp` 击退时沿障碍物法线切向滑动
• `CMapOutdoor::is_blocked` 的栅格阻挡光栅化与逐轴滑动降级 | `GameLib/ActorInstanceSync.cpp`
`GameLib/ActorInstanceCollisionDetection.cpp` | | **3. 动作与挂点 (Animation & Attachment)** | • 武器/盾牌朝向偏转或拿反、脱离手部
• 骑马时双手未握缰绳或双腿穿透马鞍
• 更换头盔/发型时头部穿模或发色异常 | • `CItemData::GetItemScale` 与骨骼挂点矩阵 `m_matBone` 变换
• `InstanceBaseMotion.cpp` 的骑乘姿势 override (`COMBO_TYPE_HORSE`)
• `CRaceData::SetHair` 的 MSM 顶点隐藏机制与 Alpha Blending 管道 | `GameLib/ActorInstanceAttach.cpp`
`UserInterface/InstanceBaseMotion.cpp`
`GameLib/RaceData.cpp` | | **4. 战斗与射弹 (Combat & Flying)** | • 弓箭/法球穿透墙体或直接打入地底
• 普攻挥刀动作还没劈下来伤害数值已提前跳出
• 怪物死后不立刻倒地或死亡姿势嵌入地面 | • `CFlyingInstance::Update` 中的 `BackgroundHit` 碰撞(与地面/建筑交点)
• `.msa` 中的 `MotionEvent`(命中帧 `HIT` 严格帧事件驱动)
• `ActorInstanceBlend.cpp` 死亡动作的地面投影与骨骼终帧冻结 | `GameLib/FlyingInstance.cpp`
`GameLib/ActorInstanceBattle.cpp`
`GameLib/ActorInstanceBlend.cpp` | | **5. 交互与拾取 (Picking & Decal)** | • 鼠标点击桥面、屋顶或 NPC 拾取点偏移到桥下
• 地面范围技能光圈(Decal)漏到桥底或悬空 | • `CMapOutdoor::GetPickingPointWithRay` 射线先求交物体再求交地形
• `CTerrainDecal` 的多边形表面投影裁切 | `GameLib/MapOutdoor.cpp`
`GameLib/TerrainDecal.cpp` | --- ## 三、四大系统化排查手段(主动发现) ### 手段 1:全图几何与阻挡坐标探针(Headless Coordinate Probes) 由于地图静态放置数据由 `areadata.txt` 和 `attr.atr` 完全定义,无需人工跑图,可由自动化脚本扫盘检测: 1. **特殊静态模型巡检**: - 提取地图上全部引用了含有 `.mdatr`(带有 `heights` 网格)的物体(桥梁、台阶、悬崖栈道等)。 - 在该物体几何中心上方向下打垂直射线或采样 `sample_height(x, z)`。 - **断言**:采样高度必须落在物体表面标高区间内,与裸地形高度应有明显的阶跃高差;若 `sample_height == terrain_height`,直接报警并定位对应模型路径与坐标。 2. **地貌高度一阶导数(梯度突变)扫描**: - 按 0.5m 步长对可通行路网进行网格采样。 - 检测相邻点高度差:若连续两点高度差 $> 2.5\text{m}$ 且未标记 `ATTRIBUTE_BLOCK`,判定为地貌穿透或缝隙掉落点。 ### 手段 2:40250 核心链路白盒对拍(Static Call-Graph Audit) 以 40250 源码的关键函数为索引,走查当前客户端对应的实现: - **查阅 40250 源码**:`40250/Server Client TMP4/ClientVS22/source/` - **重点审查模式**: - 40250 中包含 `fMAX(...)`、`min(...)`、多层分支判断的函数,当前客户端是否只实现了其中单条简单路径? - 40250 中从资源读取参数的逻辑(如 `.msa` 帧事件、`.msm` 挂点、`.mdatr` 碰撞体),当前客户端是否被硬编码(hardcoded)常量替代? ### 手段 3:未决协议与资源缺失日志审计(Log & Protocol Auditing) 在测试与实机运行期间,关注并收集控制台日志中的警告: - 过滤关键 Warning:`unhandled packet`、`missing model`、`cannot find mdatr`、`mesh has no height`。 - 检查服务端发送的所有 GC 封包(如 `GC_MOVE`, `GC_SYNC_POSITION`, `GC_ATTACK`, `GC_FLY_TARGETING`),确认不仅有网络层的解析反序列化,更在表现层驱动了正确的物理与动画状态。 ### 手段 4:自动化无头路网巡逻测试(Headless Patrol Suite) 利用 Godot `--headless` 启动无头客户端: - 让角色沿关键地貌路网(广场 $\to$ 跨河桥梁 $\to$ 仓库台阶 $\to$ 城门通道 $\to$ 郊外野怪区)自动移动。 - 监控指标: - 角色 Y 坐标是否发生非预期的下坠(掉水、穿地); - `_blocked` 是否产生虚假阻挡导致角色提前停步卡死; - 状态机是否在 `walk` / `run` / `wait` 之间异常震荡。 --- ## 四、工程化修改与防回退治理原则 每次发现并修复类似细节问题时,必须严格执行以下三条工程原则: ### 原则 1:拒绝打补丁式硬编码(Treat the Root, Never Patch Coordinates) - **禁止**针对个别坐标做特殊条件判断(如 `if x > 750 and x < 770: pos.y = 206.0`)。 - **必须**在底层管线实现通用机制(如在 C++ 引擎层解析 `.mdatr` 的 `heights` 并求交平面),确保全服数十张地图、数百座桥梁与所有建筑台阶自动获得修复。 ### 原则 2:路线 A(严格 40250 原版实现) - 不凭空发明“更好”的经验算法,遇到模糊细节直接翻阅 40250 对应 C++ 模块源码,将原版的数学公式、坐标系旋转(Z-up 转 Y-up、米制转换)与浮点容差 1:1 映射。 ### 原则 3:修一处问题,必补一个 Parity 自动化回归测试 - 修复缺陷后,必须在 `project/` 下建立对应的 `test_*_parity.gd` 单元/集成测试(例如针对桥梁建立的 `test_bridge_height_parity.gd`)。 - 将测试纳入持续集成验证集(134+ Parity 测试套件),任何一次打包或构建前运行,确保旧缺陷绝不回退。