原版 feature-tag 配额认领与 Group 预选
本批结果
新增 NativeFeatureLedger/NativeFeaturePreselection,落实实际配额判定、认领、候选随机移除、反向重试、强制标签及兜底组处理;不是将这些规则继续整体留给一个成功/失败回调。
- 4704 组原版机器码参考在 PC24/PC53/PC64 下,对象状态、配额计数、随机状态、标签查询和记录名均一致。
- 1302 条核心断言、175 项相关 Python 回归通过。
- Godot C# 构建零警告、零错误,coresimlint 通过。
- 衔接测试完成:预选认领 → 恢复 RNG → 普通 CHOICE,配额和禁用状态按原语义保留。
请求的初始总量/每块配额构造、真实 FEATURETAGS.HIE 矩阵生产、解析期 LEVEL UNIQUE 剔除、具体世界宿主与完整武器验收仍有剩余。本批不将这些输入夹具当成已接通实际世界。
候选入队:50AB80
NativeFeaturePreselection.AddCandidate 保留次序:
- Group 为 LEVEL UNIQUE 时,先查该 SourceId 是否已经使用。
- 已使用则不入队。
- 未被唯一性挡住,且存在 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,即使后面请求为空或标签表缺失。
随后按顺序检查:
- 请求和标签表存在,quota 逻辑数量非零。
- 标签关系符合。
- quota 为 FFFFFFFF,或者当前 used < quota。
- Claimed < Total。
- 根据 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。
否则反复:
- 取同步整数随机 [0,count-1];仅一个候选时原包装器不推进状态。
- 设置候选 byte+84 已处理标记。
- 用末项补位移出候选数组。
- 候选已经 Allowed=false 时跳过后续逻辑。
- NO TAG FOUND 候选放入兜底列表。
- 有强制标签时,检查强制关系;不匹配则禁用整棵子树。
- 无强制标签时,尝试正向认领;成功则记录配额并保存 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。核心禁止把两个不同实例悄悄用于这两种抽取。
预选本身不恢复随机状态。仍由前批装载器在正确边界恢复:
保存同步状态
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。