[P0][T23] 实现 1.13c PRNG:64 位 LCG(0x6AC690C5)+ per-unit seed #87
Labels
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: troytt/diablo2-web#87
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题
现在
src/game/rng.ts用的是 mulberry32(32 位状态、state += 0x6D2B79F5+ 两轮 xorshift/imul、返回[0,1)浮点、全局流 +fork(label))。D2 1.13c 用的是完全不同的东西。三个维度都不兼容:
lo % max(2 的幂走 AND)floor(next()*span)净效果:永久失去与 1.13c 对拍的能力。对 M10 这种「以原版为金标准」性质的里程碑,这是不可接受的。
金标准数据
D2Common!6FD510B0的反汇编(另有6FD51100、6FD51180变体;D2Game!6FC211D0/6FC21180是完全相同的副本,此外还有大量内联展开点):即:
三个 API:
Rand(seed)lo(内联点常直接lo & 0x3FF做 1024 判定)Rand(seed, max)max <= 0→ 0;max 是 2 的幂 →lo & (max-1);否则 →lo % maxRand(seed, min, max)min >= max→min;否则span = max-min+1,返回min + lo % span(或min + (lo & (span-1)))seed 的归属(确证):
UnitAny + 0x20(dwSeed[2]= lo/hi);+0x28是初始 seed。lea 0x20(%unit)后调 RNG。.d2s只存 32 位 id 就能完整复原词缀数值的原因。实现要求
D2Rng类,内部持{ lo: number, hi: number }(两个 u32)。Math.imul拆 16 位手写,不要用 BigInt —— 掉落是热点路径,BigInt 会慢一个数量级。参考写法:把lo拆成lo & 0xFFFF与lo >>> 16,与常量的高低 16 位做四次Math.imul,再拼回 64 位。Rand重载,严格区分 2 的幂与非 2 的幂两条分支。D2Rng,每个 unit 持有自己的 seed 实例。陷阱
验收标准
state64 = lo * 0x6AC690C5 + hi的前 N 个输出与手算序列逐位相等(写成单测的已知向量)Rand(seed, 1024)走 AND 分支、Rand(seed, 1000)走取模分支,两条分支都有覆盖Rand(seed, min, max)在min >= max时返回minnpm run typecheck0 errornpx vitest run全绿npx tsx scripts/verify-items.ts新增断言通过溯源
D2Common!6FD510B0/D2Common!6FD51180/D2Game!6FC211D0(objdump -d,D2Common 基址0x6FD50000、D2Game0x6FC10000)已完成并通过 1.13c 逐位对齐测试,合并入 main (commit
928d422)。