
【免费下载链接】d2r-mobileOpen-source iOS / macOS compatibility layer for Diablo II: Resurrected with the Reign of the Warlock expansion. Runs your own Switch copy natively on iPhone, iPad and Mac — full touch controls, 60 fps, new endgame modes. Bring your own game files. 开源的《暗黑破坏神 II狱火重生》术士君临 iOS / macOS 兼容层项目地址https://gitcode.com/gh_mirrors/d2/d2r-mobile点击查看免费下载导读d2r-mobile 用改写器把 Switch 版《暗黑破坏神 II狱火重生》3.3.94012 程序搬到 iOS / macOS 上直接执行因此改写过后的宿主程序与原版游戏是否跑得一模一样是这个项目的立身之本。dm/verify/tick_state_format.zh-CN.md定义了其中最关键的一环逐拍游戏状态哈希格式——游戏服务端逻辑按拍推进每一拍开始前把全场单位的关键字段压缩成 6 格 FNV-1a 64 哈希、记成一行 JSON真值侧打了补丁的 Ryujinx 模拟器跑原版与宿主侧dm/host的dmhost分别产出ticks.jsonl逐拍对齐即可逐字节判定逻辑是否一致。读完本文你将掌握该格式的采集点与基址推导、五类单位表的内存布局、哈希折叠规则以及真值录制、宿主侧DM_TICKS系列环境变量、确定性对拍前置条件与仓库内验证闸门的完整用法。逐拍哈希在验证体系中的定位d2r-mobile 的验证目录dm/verify回答两个问题改写后的程序行为是否与原版一致、某个改动是否破坏了已工作的功能。dm/verify/README.zh-CN.md将本格式与命令包序列格式、UI 消息流格式并列作为三种记录文件格式之一真值侧dm/verify/oracle/swtick.py经模拟器 GDB 接口读录一局时写成局目录/ticks.jsonl宿主侧环境变量DM_TICKS路径打开DM_TICKS_N拍数记满即正常退出DM_TICKS_FULL1另出逐单位明细对拍两份ticks.jsonl按拍号对齐报每一格首个不同的拍号共同拍数少于 10 不下结论。必须强调的是两边跑的是同一份 Switch 版 3.3.94012 程序——模拟器跑的main带着改写器登记的剪口标为仅宿主的除外所以真值与宿主跑的是带同样定点修改的程序。真值侧的录制用模拟器驱动脚本完成这些脚本不随仓库提供依赖真值的检查如rulegate.sh、accept.sh的第 ② 项在仓库外跑不了仓库内保留的是读取这一侧的最小部件oracle/swgdb.py、oracle/swtick.py与格式说明。采集点与基址 G哈希的采样时机和基址是整套格式的地基两者都钉死在一个函数入口上采集点服务端拍入口main 0x5d3bc0在该入口第一条指令执行之前取此时拍号尚未自增。基址 G 入口处的x0。G 是游戏对象嵌在游戏哈希表节点里G 节点 0x10节点 槽数组main 0x2d665d8的 ID 查哈希表*(main 0x2d685e0)所得。本文所有偏移都以 G 为基址这与游戏内部以节点为基址的写法存在一个 0x10 的换算例如建局函数0x5d0bd0以节点为基址写入0x1b8对应这里的G 0x1a8。拍号 u32 [G0x170]入口处读等于本拍开始前的值。两条采集纪律同一拍号只取一次模拟器会重复报告同一次断点命中必须按拍号去重拍号必须逐一连续断档报红。这两条纪律在真值侧与宿主侧都有实现。swtick.py主循环里if tick last: dup 1; g.step_over(bp, stop); continue去重、if last is not None and tick ! last 1: gaps.append(...)记断档、结束时if gaps: sys.exit(拍号不连续…)以非零码退出宿主侧dm/host/diag.c的ticks_hashdiag.c同样if (tick g_tklast) return并在断档时输出【红】DM_TICKS 拍号断档。宿主还处理了一个合法特例同一进程先后进两次局退回选角再进、连建两个角色新一局的拍号从 0 / 1 重数这不叫断档只有局内跳号才报红。每拍一行 JSON真值与宿主的输出都遵循同一行格式一局一个 JSONL 文件{tick:39,n:[4,12,24,0,21],h:[…16 位小写十六进制… ×6],bad:0}各字段含义字段含义是否参与对拍tick本拍拍号入口处读拍尚未自增作为对齐键n5 类单位的个数0 玩家 / 1 怪 / 2 物件 / 3 飞弹 / 4 物品由h隐含校验h6 格哈希5 类单位 全局种子每格 16 位小写十六进制是bad读不到的单位指针数 自报类型与所在表不符的单位数是非 0 即异常信号game、dt、reads游戏对象地址、相对耗时、读取次数否只供人看宿主侧在ticks_hash中输出该行时字段与swtick.py的json.dumps同序同分隔diag.c保证两份文件可直接按列对齐宿主额外多输出一个frames字段dm_nvn_frame_count()同样只供人看、不参与对拍。单位表与字段布局五类单位各占一张哈希表偏移相对 G表中的每个桶都是 u64 指针桶内是单链表。表地址见下表类 t表说明0 玩家G0x2230每表 128 个桶u64 指针桶内单链next [u 0x158]每链最多 40961 怪G0x26302 物件G0x2a303 飞弹G0x3230注意 3、4 的表序与地址不单调4 物品G0x2e30真值侧实现印证了这一点swtick.py的TBASE (0x2230, 0x2630, 0x2a30, 0x3230, 0x2e30)一次读下0x2230..0x3630连续区5 类 × 128 桶 × 8 字节再逐桶沿next 0x158走链NEXT, MAXS 0x158, 96宿主侧ticks_hash的TB[5]常量与之完全一致diag.c。每个单位u的字段读法偏移相对单位结构起点字段读法type, cls, id, modeu32 [u0x0], [u0x4], [u0x8], [u0xC]actu8 [u0x18]rlo, rhi单位种子u32 [u0x28], [u0x2C]坐标仅 t 0/1/3path u64 [u0x38]u16 xf[path0], x[path2], yf[path4], y[path6]path 为 0 或读不到时四项都取 0其余类四项恒 0statlistt 0/1/4sl u64 [u0x88]arr u64 [sl0x30]cnt u32 [sl0x38]取 min(cnt, 96) 项每项 8 字节原样u32 a 层16 | statIdu32 b 值物品附加仅 t 4d u64 [u0x10]iq[d0x0]is_lo[d0x4]is_hi[d0x8]iflags[d0x18]iinit[d0x10]u32d 为 0 时五项取 0其中swtick.py的文件头还记录了一条口径来源这些偏移出自 PC 版 M0 插件 v7Switch 94012 的静态证据是拍入口结构逐项相同、五个单位表偏移各出现约 260 处立即数、LCG 附近0x28出现最频繁另有--check活体复核。哈希折叠规则哈希采用 FNV-1a 64偏移基0xcbf29ce484222325乘数0x100000001b3逐字节折叠所有数值一律小端 u32。两侧实现逐字节可比——宿主侧tk_fnvdiag.c与swtick.py的fnv是同一段逻辑。折叠分四步每类 t 的哈希从偏移基起全部单位按 (type, id) 升序后依次并入其 type 对应的格type ≥ 5 的不计。排序必须稳定swtick.py用 Python 的稳定排序宿主侧tk_cmp以 (type, id) 为主键、采集次序为稳定化次序diag.c保证同键单位两侧次序一致。每个单位依次并入9 个 u32type, cls, id, mode, act, x, y, xf, yf2 个 u32rlo, rhi仅 t 45 个 u32iq, is_lo, is_hi, iflags, iinit仅 t 0/1/4u32 项数再逐项 8 字节原样。第 6 格全局种子 偏移基并入 8 字节[G0x1a8]u32 lo u32 hi 原样。注意这里就是建局种子落地的位置建局函数0x5d0bd0以节点为基址写入0x1b8换算成 G 基址恰好是G 0x1a8。输出为 16 位小写十六进制字符串%016x。swtick.py中h[5] fnv(h[5], gs)处理全局种子、struct.pack(9I, ...)/II/5I分别折叠 9 个 u32、2 个种子 u32 与物品附加 5 个 u32与ticks_hash的f9/f2/it/st折叠逐字节对应diag.c。确定性逐拍对拍的前提两边要逐拍相同输入必须相同。文档明确四个维度全部有环境变量或脚本支撑建局种子建局函数0x5d0bd0读u32 [main 0x20a0074]该值 ≠0xffffffff时种子 {该值, 666}否则取系统时钟。两边须在建局前写同一个值真值侧是驱动脚本的--game-seed N宿主DM_GAME_SEEDN。宿主实现见 main.cgame_seed_maybe直接写 main 数据段、不剪口放在各模块 init_array 之后、nnMain 之前以免被全局构造函数覆盖写前断言原值等于0xffffffff构建号不符即报红中止。建局时刻建局时读一次系统时间。真值侧是驱动脚本的--fix-time 秒宿主DM_FIXED_TIME秒。宿主侧还有一个细节只有确定性对拍DM_FIXED_TIME会把时区钉成 UTCsetenv(TZ, UTC, 1)main.c因为同一份回放在东八区本机与东九区的 mini 上必须换出同样的日历时刻。存档两边各用同一份标准存档$DM_ROOT/data/switch/save_ref_warlock的新副本开局存档内容指纹用 dm/verify/savesha.sh 算跑前记下。savesha.sh对用户存档目录按MANIFEST.sha256的同一格式sha 0000000000000001/相对路径逐行、按路径排序出清单并打印清单本身的 sha256.lock不计可直接与data/switch/存档/MANIFEST.sha256比对。输入客户端发给服务端的输入一律以真值录下的命令包序列为准格式见 command_packet_format.zh-CN.md。命令包与系统类消息0x6d心跳、0x6f进度等都要按原拍号注入原字节宿主自己产生的同类消息丢弃进局时的0x67加入照常放行。系统类消息到达的拍号取决于客户端快慢其中0x6f决定服务端何时插入同步属于时序相关输入不注入的话逐拍状态对不上。输入注入在宿主侧由拍入口观测门统一编排tick_gatediag.c先注入本拍命令包再取状态哈希——if (g_rp) c2s_inject(regs[0])在前、if (g_tkfp) ticks_hash(regs)在后保证哈希里包含本拍命令包的效果与真值口径一致。真值侧swtick.py 与 GDB 客户端swtick.py是仓库内唯一随附的真值读取端用法swtick.py [--port 55571] [--ticks N] [--out 文件] [--full]默认端口 55571、默认 25 拍、默认输出到 stdout、--full时除每拍一行外再按排序后次序逐单位输出{t: 拍号, u: {…}}明细宿主侧DM_TICKS_FULL1输出同构明细diag.c。它的工作循环体现了与模拟器打交道的全部关键细节通过模块表找Game模块基址 TICK_ENTRY0x5d3bc0下软件断点每次停止后先核对 PC 是否为拍入口非真命中则继续等待连续 50 次停在非入口即退出同拍号去重、断档记录、step_over越过入口断点否则继续后又停在同拍结束时向 stderr 打印{重复报告: dup, 断档: gaps}有断档则非零退出。其底层是dm/verify/oracle/swgdb.py——Ryujinx GDB stub 的最小客户端读/写内存、读寄存器、软件断点、继续/冻结协议细节来自实测stub 一次只服务一个客户端0x03冻结全部客体线程并回停止包c先回 OK命中断点时再异步来停止包。wait_stop自动切线程、_stash按线程去重、step_over本线程走过断点这三条规矩在 command_packet_format.zh-CN.md 中有更详细的说明都实现在这个客户端里。宿主侧DM_TICKS 环境变量族宿主侧全部由环境变量驱动入口在ticks_installdiag.c环境变量作用DM_TICKS路径打开逐拍状态哈希输出到指定 JSONL 文件DM_TICKS_N拍数记满指定拍数即正常退出_exit(0)用于控制一局录制长度DM_TICKS_FULL1额外输出逐单位明细对应swtick.py --full安装时机与产物约束ticks_install在宿主装载阶段main.c的host_preload中probe_install之后被调用若产物没有拍入口观测门OBS_TICK需 r8 起的产物带DM_TICKS/DM_STATE/DM_C2S_REPLAY/DM_C2S_LOG启动会直接die。拍入口观测门本身也被产品缺省挂载即使不带任何测试变量UIKit 底栏数字与难度条落地也在拍入口完成tick_gate_product这部分与哈希录制无关但说明观测门是多用途的。录制期间ticks_hash还做了两件工程性保障一是静态缓冲与单线程约束——拍入口只在一条客体线程上跑tick_gate用原子比较交换记录持有线程换线程即abort()防止 static 缓冲被竞争写乱二是记录reads计数并从读拍号算起与swtick.tick_line首读同口径保证两侧读取次数可比。仓库内的消费方逻辑闸门与验收逐拍状态哈希在仓库内被两道闸门直接消费rulegate.sh [拍数400] [产物rules]宿主用rules产物回放一局真值输入逐拍 6 格状态与真值的ticks.jsonl对拍对拍脚本selfcmp.py属于模拟器驱动脚本那一组不随仓库提供真值局目录由DM_RULE_TRUTH指定须含meta.json、c2s.jsonl、ticks.jsonl末行RULEGATE OK|RED。logicself.sh [产物touch] [拍数230]产物与它自己的基线比——回放同一局真值输入把逐拍的单位数与哈希同仓库内的 dm/verify/logicself/touch.json 比用来发现改了不该影响逻辑的东西、逻辑却变了LOGICSELF_REBASE1重录基线且连跑两遍、两遍一致才写入。验收链accept.sh的第 ② 项dm/tools/host_savecmp.sh则以更严格的口径综合使用本格式宿主回放一局真值的输入、保存并退出要求6 格状态逐拍一致 存档逐字节一致 阴性对照成立真值存档与起始存档必须不同第 ⑤ 项再叠加DM_TEST_LAG400在拍 400 入口人工睡一下制造补拍积压验证冻结/补拍改动后仍落在预期拍上。真值局目录本身由meta.json种子、时刻、起始存档指纹等、ticks.jsonl、c2s.jsonl、uimsg.jsonl、events.jsonl、save_after/、shots/组成种子与时刻即录制时通过--game-seed、--fix-time定下的那两个值。从格式到判据为什么六格哈希够用理解这套格式的关键是它的设计目标不是精确重现任何单个单位而是给每一拍打一个紧凑、确定、两侧同算法的状态指纹。六格哈希把五类单位数量与全局种子放进同一行任何单位增删、坐标漂移、属性变化都会反映到对应格的哈希bad计数又把内存读取失败与类型错位这类格式本身失效的情形显式暴露出来避免把读错的指针静默当成正常状态。配合拍号连续性与确定性输入种子、时刻、存档、命令包四重约束逐拍相等即可作为游戏逻辑走得一模一样的高置信判据——这正是整个 d2r-mobile 验证体系以ticks.jsonl为地基的原因。参考实现索引格式规范dm/verify/tick_state_format.zh-CN.md本文主题文档英文版 tick_state_format.md真值侧读取端dm/verify/oracle/swtick.py、GDB 客户端 dm/verify/oracle/swgdb.py宿主侧实现dm/host/diag.cticks_install、tick_gate、ticks_hash、tk_fnv/tk_cmp、dm/host/main.cgame_seed_maybe、DM_FIXED_TIME时区钉定输入侧格式dm/verify/command_packet_format.zh-CN.md存档指纹dm/verify/savesha.sh消费方与运行前提dm/verify/README.zh-CN.md、基线文件 dm/verify/logicself/touch.json注意运行边界真值依赖的模拟器驱动脚本与对拍脚本selfcmp.py不随仓库提供依赖真值的闸门只能在与仓库配套的工作区$DM_ROOT默认~/dm-work存放游戏文件、构建产物与真值数据中运行仅凭仓库本身可完整运行的是本格式的宿主侧采集DM_TICKS与格式层面的自我一致性检查。赞分享【免费下载链接】d2r-mobileOpen-source iOS / macOS compatibility layer for Diablo II: Resurrected with the Reign of the Warlock expansion. Runs your own Switch copy natively on iPhone, iPad and Mac — full touch controls, 60 fps, new endgame modes. Bring your own game files. 开源的《暗黑破坏神 II狱火重生》术士君临 iOS / macOS 兼容层项目地址https://gitcode.com/gh_mirrors/d2/d2r-mobile点击查看免费下载相关推荐AI-For-Beginners 开发者环境搭建与协作指南基于 AGENTS.md 的完整上手手册AI For Beginners 开发者环境搭建与协作指南基于 AGENTS.md 的完整上手手册 本指南以 AI For Beginners 仓库根目录的wp-calypso ExPlat SDK 中的 GrowthBook 派生代码解析FNV-1a 哈希、分桶与条件求值wp calypso ExPlat SDK 中的 GrowthBook 派生代码解析FNV 1a 哈希、分桶与条件求值 导读 本文聚焦 WordPress.c前端CMS掌握Android Camera2Basic状态机5个核心状态轻松理解拍照全流程掌握Android Camera2Basic状态机5个核心状态轻松理解拍照全流程 Android Camera2Basic是Google提供的相机基础示例项目上一篇VisualCppRedist AIO免费离线修复 Visual C 运行库静默覆盖 2005–2022 共 8 代下一篇FanControl风扇控制软件完整上手一条曲线免费搞定机箱噪音10分钟跑通创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考