原版 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 的短路顺序

  1. Group byte+601 非零,直接通过。
  2. 玩家类型字符串门 61F5A0。
  3. 任务状态门 61F4B0。
  4. 关卡字符串门 61F3F0。
  5. 难度门 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