paavohuhtala/OpenWA
GitHub: paavohuhtala/OpenWA
OpenWA 是使用 Rust 编写的《百战天虫:末日浩劫》开源重制项目,旨在通过逆向工程精确复刻原版游戏逻辑并实现多平台支持与性能优化。
Stars: 8 | Forks: 0
# OpenWA
OpenWA 是对 1999 年 PC 游戏 Worms Armageddon™ (WA) 的开源重新实现(正在进行中),使用 Rust 编写。其目标是生成一个支持多平台、模组制作和可选增强功能的游戏版本,同时保持与原游戏的高度一致。
**为 OpenWA 做贡献以及使用 OpenWA 需要拥有合法获得的 Worms Armageddon™ 副本**,该游戏可在 [Steam](https://store.steampowered.com/app/217200/Worms_Armageddon/) 和 [GOG](https://www.gog.com/en/game/worms_armageddon) 上购买。本项目不包含也不会包含原游戏的任何素材。
OpenWA 采用 GPLv3 许可证授权。详情请参阅 [LICENSE.md](LICENSE.md)。
## 项目状态 (2026-04)
撰写本文时,OpenWA 仍处于非常早期的开发阶段。尽管相对于其开发时长,该项目已取得了相当多的进展,但 OpenWA 尚未为最终用户提供任何实质性的功能。目前暂不支持 Windows 以外的平台,当前版本本质上是一个针对 WA Steam 版本 (3.8.1) 的模组。
## 快速开始
- 确保你在 Steam 上拥有 Worms Armageddon™ 的副本,并且已经安装并至少运行过一次。
- 克隆该仓库。
- 如果尚未安装,请使用 `rustup` 安装 32 位 MSVC 工具链 (`i686-pc-windows-msvc`)。
- 使用 `start.ps1` 或 `start-debug.ps1`(用于带有调试 UI 的构建)来构建并运行项目。
- OpenWA 应该能够通过注册表自动找到你的 WA 安装位置,但如果找不到,请按照错误信息中的说明提供正确的路径。
- 你还可以使用各种命令运行测试:
- `cargo test` 用于标准的单元测试、集成测试和快照测试。
- `run-tests.ps1` 用于无回放界面的回放测试。你可以传递 `-j` 来指定要运行的并发测试数量。由于进程隔离方面目前存在的未知问题,高并发运行测试有时会导致不稳定。
- `replay-test.ps1` 用于运行一个或多个带有图形和音频(但以高速运行)的回放测试。测试的名称是相对于 `/testdata/replays` 目录的。
## 范围、目标和非目标
主要目标是创建原 WA 的精确_重新实现_,而不是受其启发的游戏,这是它与现有的开源 Worms 类游戏(如 Hedgewars 和 Wormux/Warmux)的主要区别。OpenWA 主要基于对原 WA.exe 的反编译和行为分析,理论上它应该能够完美复刻原游戏的游戏玩法和图形。
### 目标
- Tick 精确的 WA 重新实现,所有的单人模式和多人模式及功能都像原游戏一样工作。
- 支持回放从任何未修改版本的游戏录制的回放,且不会出现不同步或视觉故障。
- 与原游戏无法区分的图形和音频——当然,除非另有配置。
- 长期目标:支持所有常见的桌面平台,包括 Windows、Linux / SteamOS 和 macOS。
- 这还需要支持多种 CPU 架构,例如 x86-64 和 ARM64。
- 长期目标:内存安全、符合 Rust 惯用法的代码。
- 我们离这个目标还有一段距离;目前绝大多数代码库都是 `unsafe` 的。在我们移植每个子系统的大部分代码,或者至少将 WA.exe 的所有字段访问封装为安全的 getter 和 setter 函数之前,我们无法真正使用 Rust 原生结构体(非 `repr(C)`)。
- 这将需要大量的重新架构和重构,因为 WA 是用 C++ 编写的,并且使用了相当多的(多重)继承以及可变的全局状态。
- 长期目标:UI 缩放和高 DPI 支持。
- 长期目标:无障碍功能,例如可重新映射的控件和色盲模式。
- 长期目标:增加引擎限制,例如支持更多武器、更大的地图、更多的队伍/虫子等。
- 长期目标:现代化的 GPU 渲染器,显著降低 CPU 占用。
- 这是一款 1999 年的游戏,但这并不意味着它不能优化以在现代硬件上运行得更好。而且编写渲染器很有趣!
- 限制:GPU 渲染器可能无法在像素级上与原版_完全_一致。
### 待定事项
- WA 的菜单系统(又称前端)是游戏的一个标志性部分,对整体感觉相当重要。然而,它是使用 [MFC](https://en.wikipedia.org/wiki/Microsoft_Foundation_Class_Library) 构建的,与 Windows 的耦合_非常_紧密。
- 我们可能必须首先使用 Rust 原生 UI 框架(例如 [egui](https://github.com/emilk/egui))实现一个基本的前端。
- 最终,我们应该尽可能接近地复刻原前端的外观和感觉,这可能需要使用自定义 UI 框架。
- OpenWA 是否能与原 WA 向后兼容仍有待观察。我们希望能够回放旧的回放并使用相同的方案和关卡,但我们可能希望在某些时候引入我们自己的格式以增加限制并添加功能。因此,OpenWA 很可能能够读取 WA.exe 生成的数据,但反之则不行。
### 非目标 / 超出范围
- 不支持 WormKit。WormKit 并不是一个真正的模组 API,而只是一个内置的 DLL 加载器。所有的 WormKit 模组本质上是 DLL,它们以各种方式挂钩和修改游戏(就像目前的 OpenWA 本身一样),并且与 WA.exe 的内存布局和其他内部实现细节紧密耦合。
- 由于 OpenWA 是开源的,因此它比原 WA 更容易进行模组制作。
- 与未修改的 WA 进行多人游戏不是我们要支持的目标。OpenWA 和原版之间的任何不兼容都会导致多人游戏中的不同步,困扰那些只想在线游戏的用户。OpenWA 的开放性结合游戏的架构(确定性锁步)意味着某些形式的作弊是无法阻止的(尽管某些作弊在设计上也是不可能的),而且目前我们没有能力实施任何有意义的反作弊措施。
- 不计划支持旧平台(例如旧版本的 Windows)。代码是开源的,因此你可以尝试将其移植到你想要的任何平台,但 OpenWA 的主要重点是现代桌面平台。
- OpenWA 可能不会支持 WA 3.8.1 支持的所有图形后端。软件渲染很可能会无限期支持,但在 `$current_year` 编写新的 DirectDraw 或 D3D7 代码没有充分的理由(当然,除了为了好玩和怀旧)。OpenWA 可能会通过 `wgpu` 或 SDL3 支持几种现代图形 API。
## WA 基础
- WA.exe 是一个 32 位 x86 Windows 应用程序。当前的 Steam 版本 (3.8.1) 是使用 MSVC 2005 编译的。
- WA 是用 C++ 编写的,但它很少使用 C++ 特性,这在那个时代的游戏中很常见。可以想象一下类(单一和多重继承)、少数地方的异常,以及极少数模板和/或 STL 的迹象。
- WA 严重依赖 Windows API 和库,例如 MFC、DirectDraw 和 DirectSound。游戏逻辑与平台 API 大部分是解耦的。
- 虽然 WA 的 Steam 版本支持多种图形 API(DirectDraw、D3D 和 OpenGL),但实际上该游戏 99.99% 基于软件渲染。图形 API 仅用于将最终的帧缓冲区呈现到屏幕,并且在某些后端中使用简单的像素/片段着色器来应用调色板效果。
- WA 使用确定性锁步模拟来进行游戏逻辑,就像大多数 RTS 游戏一样。
- 回放和多人游戏通过记录和回放玩家输入来工作,游戏状态从未被序列化或通过网络传输。
- 尽管存在伪随机数生成,但游戏逻辑必须是 100% 确定性的,这是通过在比赛/回放开始时同步 RNG 状态,并确保 RNG 在所有机器上以相同的顺序采样来实现的。
- 为了更容易实现确定性(可能也是因为游戏的 Amiga/DOS 遗产),WA 几乎从不使用浮点数学运算。相反,它对所有游戏逻辑和大多数图形代码使用 16.16 有符号定点算术(OpenWA 中的 `struct Fixed`)。
- 该游戏具有相当标准的实体/游戏对象层次结构。实体在 OpenWA 中被称为“任务”,这是我继承自 WormKit / wkJellyWorm 的命名约定(将来可能会更改)。任务使用消息传递进行通信;很大一部分游戏逻辑是在每个任务类的 `handle_message` 方法中实现的。
## 技术方法
重新实现的方法与 OpenRCT2 在早期使用的方法非常相似:
- 使用自定义启动器将 DLL (`openwa.dll`) 注入到 WA.exe 中。
- 该项目最初是作为 WormKit DLL 启动的,但我不得不用自定义注入器替换它,因为在主线程运行时修补游戏是不可靠的(并且根本无法修补所有内容)。
- 该 DLL 使用 Rust 实现替换了原游戏中的关键函数,使用 [MinHook](https://github.com/tsudakageyu/minhook) 和 vtable 修补将执行重定向到 Rust 代码。
- 在这个阶段,大多数 Rust 代码都是 `unsafe` 的,可能其中一些甚至是不健全的,并且看起来更像是 C 而不是 Rust。目前的重点是尽可能接近地匹配原行为,即使以安全性和代码美观为代价。
- 随着越来越多的函数在 Rust 中重新实现,挂钩和 FFI 调用可以被直接的 Rust-to-Rust 调用替换。
- 在某个时候,我们可以开始使用标准 Rust 分配器分配的结构体,这能够使用 Rust 原生数据结构和安全代码。
- 不再被调用的 WA.exe 函数会被捕获(这意味着它们在被调用时会导致崩溃),以确保它们永远不会被意外使用。
- 在某个时候,整个游戏都已经在 Rust 中重新实现,原 WA.exe 可以被一个新的完全独立的可执行文件替换,该可执行文件直接使用 Rust 代码,而无需任何挂钩或 FFI。
- 这最终实现了对 32 位 x86 Windows 以外平台的支持。
## 测试
为了确保重新实现的正确性和稳定性,测试至关重要。OpenWA 有几种类型的测试,但到目前为止最重要的测试是_回放测试_。事实证明,WA 的回放系统可以很容易地改造成一个非常强大的测试工具。
如前所述,回放是玩家输入的记录。这意味着从游戏玩法逻辑的角度来看,回放回放运行与正常进行比赛几乎相同的代码路径。游戏在回放记录期间定期记录游戏状态的校验和,并在回放期间验证校验和以检测不同步。
WA 还可以无头方式(即没有图形或音频)运行回放。此功能主要通过 `/getlog` 启动参数访问,该参数运行提供的回放文件并生成所有主要游戏事件(例如武器发射、虫子受到伤害等)的带时间戳的日志。
结合这些功能,可以记录来自原始未修改的 WA.exe 的游戏玩法,然后将录制的回放文件(及其关联的日志)用作 OpenWA 的测试用例。借助一些自定义工具和特定于测试的 API 挂钩,我们可以不受限制地以高速度并发运行多个测试,在几秒钟内模拟数小时的游戏玩法。
如果所有回放都通过且没有崩溃,不产生任何不同步警告,并且生成的日志与原版逐字节相同,那么只要被测试的功能被回放覆盖,我们就可以相当确信重新实现是正确的。测试既可以无头运行,也可以有头运行(带有图形和音频),但目前大多数与图形和音频相关的更改仍然需要人工验证。
这就是使 OpenWA 能够高度忠实于原游戏的原因。
当然还有其他类型的测试。我们对操作相对大量数据且必须完全准确的一些函数使用快照测试——即线条绘制和精灵位块传输函数。当然,还有单元测试和集成测试,正如你所期望的那样。
## 问答
### 这是 AI 生成的垃圾吗?几乎所有的提交都有 Claude 作为共同作者!
什么构成“AI 生成的垃圾”取决于人们对它的定义。
是的,这个代码库在很大程度上是在 Claude Code 的帮助下开发的,使用 [Ghidra MCP](https://github.com/bethington/ghidra-mcp) 让 Claude 执行绝大多数逆向工程和重新实现工作。然而,我不会将其描述为一种_氛围编程_(vibe coding)项目,因为我非常积极地参与了开发过程,我做出了所有的架构决策,并且我确实非常了解代码库和原游戏的架构。
这个项目一直是一项个人实验,旨在调查最先进的 AI 工具在逆向工程相当复杂的游戏方面能做到什么程度,目前的答案是:出奇地好。语言模型从根本上说是模式识别引擎,而这在很大程度上就是逆向工程的全部内容。我喜欢逆向工程和游戏开发(我曾在 [我的博客](https://blog.paavo.me/) 中写过这两个话题),但老实说,我并不特别喜欢阅读或编写汇编,或者调试调用约定问题直到凌晨两点。
无论好坏,虽然 Claude 在函数级 RE 工作方面相当出色,但它仍然需要持续的人工监督和建议,以防止它完全发疯。Claude 在这类工作中可能非常懒惰;它经常生成使用无类型原始指针、指针算术和魔数的代码,即使对于被操作的数据有完全可用的结构体和枚举。这需要手动重构或额外的提示才能让它产生想要的结果。
我控制着每一个架构决策,例如使用哪些依赖项、如何构建代码库以及什么样的调试和测试工具对项目有用。这并不意味着这些决定是_好_的,但你主要可以怪我。
即使一个提交涉及大量传统的体力工作,我也经常使用 Claude 为我提交更改,只是为了保持进展。就我个人而言,我不认为 AI 代理特别擅长编写有用的提交信息(因为它们很少知道所涉及决策背后的人类背景),但在项目这么的时候,坦率地说,我不太关心提交历史看起来很乱。
就我个人而言,我会让结果自己说话。代码有时可能会有点_草率_;虽然我已经做了大量的重构和清理,但目前的重点是让某些东西工作起来,以便我们可以尽快进入独立可执行文件阶段。我确实相信我持续的监督和严格的测试方法使其不会变成彻头彻尾的_垃圾_。
### 如果 90% 的代码是 `unsafe` 并且看起来像 C,那么使用 Rust 有什么意义?
简单的回答:我不喜欢用 C 或 C++ 编程,我确实喜欢编写 Rust。我把 Rust 作为我的主要业余编程语言已经快 10 年了,所以对我来说这是一个自然的选择。
但确实,这是我曾经工作过甚至见过的最奇怪的 Rust 代码库之一。OpenWA 目前作为一个注入到用 90 年代末 C++ 编写的游戏中的 DLL 存在,这意味着无论采用哪种方法,它都不会是漂亮的。几乎每个结构体都必须是 `repr(C)`,并且字段在精确的偏移量处,以保持与 WA.exe 的兼容性,并且 Rust 和 C++ 之间的调用有时需要大量的 FFI 粘合代码。我试图通过一些巧妙的技巧让它更容易忍受,例如带有宏生成方法包装器的类型化 vtable,但这仍然会是一团乱麻,持续一段时间。
## 致谢和来源
- [WormKit](https://github.com/CyberShadow/WormKit)
- [wkJellyWorm](https://github.com/nizikawa-worms/wkJellyWorm)
- [Worms Knowledge Base](https://worms2d.info/Main_Page)
## 免责声明
“Worms Armageddon”是 Team17 Software Limited 在欧盟和其他国家的注册商标。OpenWA 是一个独立的且严格的非营利性粉丝项目,与 Team17 Software Limited 无关或不受其认可。OpenWA 不包含原游戏的任何素材,并且需要合法获得的“Worms Armageddon”副本才能使用。
标签:Cargo, DNS解析, GOG, GPLv3, Mod支持, MSVC, Python 3.9+, Rust, Steam, worms armageddon, 云资产清单, 可视化界面, 回合制策略, 安全意识培训, 开源项目, 游戏开发, 游戏引擎, 网络流量审计, 虫子世界, 逆向工程, 通知系统, 重制版