原版 Group 实例条件与唯一性列表
后续更新:Group候选池构建与解析期唯一性剔除已完成解析期控制及装载组合测试,并明确失效候选的诊断边界。下文保留本批历史状态。
本批结果
新增:
- NativeGroupConditions:61F660 及四个实例条件门。
- NativeLevelUniqueGroups:原 CLevel 唯一 Group 列表的追加与存在查询。
- NativeGroupQuestState:基于明确任务状态输入的活动/完成查询。
2095 组原版条件参考的结果和查询顺序一致;唯一性列表另有 11 个原生操作参考。累计 1332 条核心断言、185 项相关 Python 回归通过;Godot C# 构建零警告、零错误,coresimlint 通过。
解析期唯一组剔除、真实任务/关卡上下文绑定及实际世界宿主仍未完成。本批不将实例条件门和唯一性容器当作整张地图逻辑完成。
两种时机
pool 读取阶段
50AD70 读取一个 Group 及其后代后,若 byte+26 LEVEL UNIQUE 非零,会查询场景 +384 提供的唯一性接口。已使用则销毁该子树并返回空。
另有更早的 50AB80 候选入队检查。它和读完子树后的剔除不是同一次操作。
本批继续确认了这些位置,但尚未声称完成整个剔除宿主。尤其父唯一组已使用、后代曾加入 feature 候选时,仍需核查销毁与候选引用的完整关系,不能擅自“修复”为另一个行为。
对象实例阶段
前批 50B680 装载器在工厂创建和属性处理之后,对 pooled Group 调用 61F660。失败时销毁对象并跳过其后代,不进入接受/extra/初始化列表。
本批实现这一实例门,不把它提前到尚未应用属性的位置。
61F660 的短路顺序
- Group byte+601 非零,直接通过。
- 玩家类型字符串门 61F5A0。
- 任务状态门 61F4B0。
- 关卡字符串门 61F3F0。
- 难度门 61F460。
前一项失败,不读取后续状态。原直接调用的 6A4650 仍为恒 false;不拿它替代 byte+601。
输入字符串保留原内容,不在条件入口额外 trim 或转大写。
玩家类型字符串门
Group +512 为要求的字符串,+532 是原 wstring 长度字段。
- byte+601 非零或要求为空,通过。
- 没有 GameClient,失败。
- 否则取 41E2F0 返回的玩家类型名称,按精确字符串比较。
41E2F0 会走当前玩家的 5B3430,或者无玩家时的配置字符串路径;具体名称生产方仍由实际上下文提供。
它不是用户显示昵称,也不应直接拿 HUD 上的角色名字替代。
任务状态门
61F4B0 的五项检查顺序:
| Group 字符串偏移 | 规则 |
|---|---|
| +372 | 要求任务完成 |
| +484 | 要求任务未完成 |
| +400 | 要求任务处于活动列表 |
| +456 | 要求已开始:活动或完成 |
| +428 | 要求未开始:既不活动也未完成 |
每项空字符串跳过。已开始项若活动查询成功,不再查询完成状态;未开始项若活动查询成功,立即拒绝。
GameClient 存在但没有玩家时,任务门直接通过。原 61F4B0 没有检查 GameClient 本身是否为空;核心在这种无效输入下报缺输入,不能伪装成允许。
任务管理器也按实际需要读取;没有任何非空任务条件时,不因一个未使用的空任务管理器而额外拒绝。
活动与完成查询不同
NativeGroupQuestState 根据 6F5FB0、6F6C00、6F6720:
- 活动查询按活动任务名称列表做 ordinal 精确匹配。
- 完成查询先以原名称查任务映射;未找到才尝试大写名称。
- 找到任务后通过其完整 ID 查完成标记。
- 保留原 byte 标记,Group 条件将非零视为真。
- 找不到任务或完成标记,返回 0。
- 不做前后空格清理。
完成名称大写回退当前只对明确支持的 ASCII 域执行;非 ASCII 回退的原 locale 语义未验证时显式报缺输入。原名直接命中和活动精确比较不需要这种转换。
这是任务管理器输入的只读副本,不是新的任务状态权威来源;真实活动列表、名称/ID 表和完成状态仍需由现有任务系统接入。
关卡字符串门
Group +568 的要求为空时通过。
否则:
- 没有关卡对象,通过。
- 关卡 +772 的身份字符串为空,通过。
- 否则精确比较关卡字符串与 Group 要求。
原路径会重新调用关卡 getter,核心保留其调用顺序,不把缺少关卡和字符串不匹配混为一类。
这里保留为 LevelIdentity 输入,不凭字段位置猜成地图显示名或文件 basename。
难度门
- 要求值以 unsigned DWORD 比较;大于等于 4 不限制。
- byte+601 非零不限制。
- 没有 GameClient 时,这个单独的门通过。
- 否则读取 GameClient +7048,与要求精确相等。
因此 FFFFFFFF 是“不限制”路径,不能按 signed -1 再与一个难度数字比较。
注意完整 61F660 在此之前还有任务门;不能据单独难度门的空 client 行为推断整个实例条件都允许空 client。
唯一性容器是列表
CLevel 的唯一 Group 接口:
- +4:5ED720,追加完整 64 位 ID。
- +8:5ED6D0,线性检查是否存在。
它不去重,不过滤 -1,也不排序。重复登记会保留重复元素,只是存在查询仍返回真。
NativeLevelUniqueGroups 保留顺序和重复项,并提供独立副本,方便未来分阶段装载使用;没有借此宣称唯一性数据已持久化或跨地图共享方式已完成。
本批原生操作覆盖重复 ID、-1、long.MinValue、long.MaxValue 和未出现的 ID。
验证范围
原 EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
原参考执行:
- 61F660。
- 61F5A0、61F4B0、61F3F0、61F460。
- 5ED720、5ED6D0。
GameClient/关卡提供的状态、任务查询和标准字符串操作为明示边界。校验内容包含允许/拒绝、短路、外部 getter 顺序,而不是仅比较最终布尔值。
无效原生空指针解引用组合未伪装成正常参考;核心对此使用诊断错误。任务名称查询的具体实现另依据原函数和核心测试核对,不把它计为 2095 组原生条件函数之外的完整任务系统验证。
产物:
- tools/build_native_group_conditions_oracle.py/tools/native_group_conditions_oracle.c。
- build/native_group_conditions_oracle/results.json。
- build/native_group_conditions_core.log:1332 断言。
- build/native_group_conditions_python.log:185 检查。
- build/native_group_conditions_godot.log:零警告、零错误。
剩余和边界
下一步继续解析期 LEVEL UNIQUE 的完整剔除/引用生命周期、条件字段绑定和实际世界宿主。之后仍需完成节点/碰撞注册、世界分区、攻击型触发技能与全武器验收。
按 godot-master 的数据/表现分层,本批不替换正式地图画面。唯一性列表仍是分阶段每关卡状态,尚未纳入持久世界和快照;v29 未改。
main 原地工作、未提交、未 attach 原游戏。默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。