原版 feature-tag 配额认领与 Group 预选

本批结果

新增 NativeFeatureLedger/NativeFeaturePreselection,落实实际配额判定、认领、候选随机移除、反向重试、强制标签及兜底组处理;不是将这些规则继续整体留给一个成功/失败回调。

  • 4704 组原版机器码参考在 PC24/PC53/PC64 下,对象状态、配额计数、随机状态、标签查询和记录名均一致。
  • 1302 条核心断言、175 项相关 Python 回归通过。
  • Godot C# 构建零警告、零错误,coresimlint 通过。
  • 衔接测试完成:预选认领 → 恢复 RNG → 普通 CHOICE,配额和禁用状态按原语义保留。

请求的初始总量/每块配额构造、真实 FEATURETAGS.HIE 矩阵生产、解析期 LEVEL UNIQUE 剔除、具体世界宿主与完整武器验收仍有剩余。本批不将这些输入夹具当成已接通实际世界。

候选入队:50AB80

NativeFeaturePreselection.AddCandidate 保留次序:

  1. Group 为 LEVEL UNIQUE 时,先查该 SourceId 是否已经使用。
  2. 已使用则不入队。
  3. 未被唯一性挡住,且存在 feature manager,并且 NO TAG FOUND 为真或 TAG!=-1,才入队。

唯一性查询发生在 feature manager 存在检查之前。

仅有 ACTIVE/DEACTIVE THEMES 的 Group 不因此进入 feature 候选池。 HasFeatureConstraint byte+85 与候选资格不是同一回事;把所有“有条件”组都加入候选会改变随机消耗。

这个入队入口不包含 50AD70 在读完子树之后的另一轮唯一性检查/剔除。两者不能合并为一次猜测。

配额状态与容量

NativeFeatureRequest 明确保存:

  • TAG。
  • CHANCE float。
  • mode +88。
  • Total +20、Claimed +52。
  • quota 的逻辑数量,以及完整、已知的容量存储。
  • used 的独立容量存储。
  • LastOwner 记录名。

NativeFeatureLedger 保存当前块下标 +24 和有序请求列表。

原版下标回落按容量而非逻辑数量:下标不小于某个向量容量时读该向量第 0 项。quota 与 used 容量分别计算,不共用一个裁剪下标。

因此不应把已知的容量内填充值丢掉,也不能在容量内但逻辑数量外的值未知时编造数据。核心要求明确存储;空/不一致存储报缺输入。

判定:5FD8D0/5FD9C0

直接调用判定入口时,当前块下标若为 FFFFFFFF,先改为 0,即使后面请求为空或标签表缺失。

随后按顺序检查:

  1. 请求和标签表存在,quota 逻辑数量非零。
  2. 标签关系符合。
  3. quota 为 FFFFFFFF,或者当前 used < quota。
  4. Claimed < Total。
  5. 根据 mode 决定最终资格。

计数比较为 unsigned DWORD 比较,不将 FFFFFFFE 等值先按负数处理。

mode:

  • 0:调用同步 [0,1] 浮点随机路径,以 random<=Chance 判定。没有 Chance==0 的提前失败,也没有 Chance>=1 的提前成功;满足前置门就会消耗随机数。
  • 1:要求当前 quota 同时不为 0、不为 FFFFFFFF,不取浮点随机数。
  • 其他值:失败,不取浮点随机数。

NaN Chance 在取随机数之后判失败。有限值、无穷、零值的比较由明确 x87 数值层处理。

正反方向

  • 正向:Match(候选TAG, 请求TAG)。
  • 反向:Match(请求TAG, 候选TAG)。

标签关系查询使用原 643DE0 的 byte 结果,非零即匹配;这与普通 Group 的主题提供者 byte==1 检查不同。

查找与认领

5FDBB0:

  • 先检查标签表;不存在则直接返回空,不修改块下标。
  • 按请求原顺序找第一条通过对应方向判定的请求。
  • 不打乱请求,不比较“最合适”分数。

因此“直接 CanClaim”和“FindClaim 在标签表缺失时返回”的块下标副作用不同。

5FDAB0:

  • 请求为空不处理。
  • Claimed 加一。
  • 当前块 used 加一。
  • 最后写入 owner 名称。

两个计数是 DWORD 增量,保留无符号回绕语义。调用方必须在记录之前取好原场景/父场景提供的名称,不把名字读取移到配额变更之后。

50A730 的三轮处理

随机取出候选

如果没有 feature manager,整个入口不处理候选、标记或 RNG。

否则反复:

  1. 取同步整数随机 [0,count-1];仅一个候选时原包装器不推进状态。
  2. 设置候选 byte+84 已处理标记。
  3. 用末项补位移出候选数组。
  4. 候选已经 Allowed=false 时跳过后续逻辑。
  5. NO TAG FOUND 候选放入兜底列表。
  6. 有强制标签时,检查强制关系;不匹配则禁用整棵子树。
  7. 无强制标签时,尝试正向认领;成功则记录配额并保存 claim,失败则放入反向重试列表。

已禁用候选仍会被移出并标记已处理;不是先过滤所有失活项后才随机。

反向重试

按失败列表顺序处理仍然有效的组:

  • 反向认领成功则记录配额与 claim。
  • 仍失败时,对当前组及子树设置 byte+98 的“只保留 Layout Link 类”限制。
  • 仅把当前组的 TAG 改为 -1;不把所有后代 TAG 一起清掉。
  • 不因此直接把组和后代 Allowed 改为 false。

这与后续装载器的“上下文限制普通对象,但允许 Layout Link/Layout Link Controller 继续传递”配套。

兜底组

对仍有效且有父组的兜底候选,查看父组的所有子项,包括候选自身:

  • 任一子项有 claim;或
  • 任一子项仍 Allowed 且 TAG!=-1;

就禁用当前兜底子树。

有 claim 的兄弟即使后来失活,也满足这条检查。入口不主动清旧 claim。

强制标签:5099E0

  • NO TAG FOUND 组仅允许空列表或唯一一个 -1。
  • 普通组 TAG=-1 直接通过。
  • 其余组跳过强制列表中的 -1,按列表顺序找 Match(强制TAG, GroupTAG) 的非零结果。

不存在标签矩阵但实际需要做强制查询时,原版会触及无效输入;核心明确报缺输入。参考输入没有将这类原生无效解引用伪装成正常拒绝。

随机流与 CHOICE 的衔接

候选随机移除和配额概率必须使用同一个同步 Mwc。核心禁止把两个不同实例悄悄用于这两种抽取。

预选本身不恢复随机状态。仍由前批装载器在正确边界恢复:

text
保存同步状态
feature 预选(改变配额和组状态)
恢复同步状态(不撤销配额和组状态)
普通 CHOICE(继续消耗后续随机)

组合测试中成功认领的请求保持 Claimed=1,已经禁用的兜底组经过普通 CHOICE 后仍禁用。Mwc.TotalAdvances 是诊断计数,不随状态恢复而倒退。

原版参考范围

原 EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e

参考实际执行:

  • 50A730。
  • 5FDBB0、5FD8D0、5FD9C0、5FDAB0。
  • 5099E0、509B20、509BA0。
  • 643DE0 标签矩阵查询。
  • 原同步整数/浮点随机代码。

分配器、场景名读取和字符串存储为明确 spy;矩阵和请求计数是显式输入。测试矩阵包含 byte=2 的关系,以验证 feature 匹配不是主题查询的 exact-one 规则。

4704 组覆盖三精度、强制/普通模式、先前 claim/失活状态、兜底自己带 TAG、容量内逻辑数量外的已知数据、容量回落、缺失 manager/矩阵、零/NaN/无穷概率等。

候选入队另以原 50AB80 汇编和核心测试核对;没有把它算进上述 4704 组原生预选数量。

产物:

  • tools/build_native_feature_oracle.py/tools/native_feature_oracle.c。
  • build/native_feature_oracle/results.json。
  • build/native_feature_core.log:1302 断言。
  • build/native_feature_python.log:175 检查。
  • build/native_feature_godot.log:零警告、零错误。

当前剩余

  • 从原 TEMPLATE/关卡上下文构造请求的 Total、quota、mode 和概率字段。
  • 真实 FEATURETAGS.HIE 到查询矩阵的生产与身份校验。
  • 解析期唯一性记录、子树剔除以及创建后实例条件门的完整上下文。
  • 将这些核心与普通 CHOICE 接进具体世界宿主、节点/碰撞注册与世界分区。
  • 完整攻击型触发技能和所有武器验收。

NativeFeatureRequest/Ledger/Group 状态当前为分阶段装载状态;未伪装成持久世界或加入快照。构造覆盖仍是 1053/1419,不能因预选测试通过而改算。

按 godot-master 的分层方式,正式地图画面未替换。main 原地工作、未提交、未 attach 原游戏、快照 v29 未改;默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f