关于 port-a-mod
port-a-mod 是用于将现有 Mod 忠实移植到其他游戏版本或加载器的编码技能。它将固定的原始版本作为规范,强调提取数据、逐单元实现、许可核查和基于测试证据的验证。
主要功能
- 01固定并审查原始版本
- 02用矩阵跟踪子系统适配
- 03提取注册数据而非手动重录
- 04逐单元移植机制并编写测试
- 05通过服务端和客户端测试验证
- 06记录许可与已知差异
适用场景
- 01在不同游戏版本间移植 Mod
- 02审查移植内容是否偏离原版
- 03规划游戏逻辑和客户端行为测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rehan-remade/ universal-modder --skill port-a-mod安装后在 Claude Code 中输入/port-a-mod确认已加载。
附带文件references/minecraft-1-7-10-forge-to-1-20-fabric.md
详细介绍
忠实移植 Mod
移植现有 Mod 时,原版就是规范。移植很少会因为缺少代码而失败,真正的问题是偏移:
- 凭记忆或 Wiki 还原数值;
- 借用同一 Mod 后续重写版本的机制;
- 悄悄做出“改进”;
- 渲染效果大致相似;
- 忘了原版在客户端执行的检查。
下面的方法将 Thaumcraft 4.2.3.5 从 Minecraft 1.7.10 Forge 移植到了 1.20.1 Fabric:
- 原版的 859 个类变成了约 77,000 行移植代码;
- 323 个 GameTest 覆盖了它,并通过脚本化的真实客户端场景检查测试无法覆盖的内容;
- 工作跨越了多个会话,使用了两个 Agent 账户。
实地记录 knowledge/games/minecraft/porting-thaumcraft-4-1-7-10-forge-to-minecraft-1-20-1-fabric.md 记载了其中的注意事项。Minecraft 特定映射见 references/minecraft-1-7-10-forge-to-1-20-fabric.md。
围绕此技能运行 mod-any-game 工作循环(intake、recon、lab、verify、publish、field note)。此技能会将其中的“垂直切片”步骤替换为一系列较长的切片,每个原版单元对应一个切片。将一款游戏的内容或机制引入另一款游戏,属于 mashup-mods 技能,而非此技能。
开始前:许可证
原版的代码和素材归其作者所有。移植基于这些内容构建,通常还会使用它们的反编译版本,因此原版许可证决定了移植版可以发布哪些内容。(这不是法律建议;如有疑问,请咨询作者。)
- 查找你要移植版本的许可证:Mod 的仓库(
LICENSE)、Mod 页面、jar 中的文件或作者的发布帖。将许可证及链接写入状态文档(第 7 节)。 - 开放许可证(MIT、LGPL、GPL、Creative Commons 许可证):遵循其条款。保留版权声明,注明作者,并在许可证要求时使用相同许可证(GPL、LGPL、CC BY-SA)。
- **保留所有权利、完全没有许可证,或包含“禁止移植”条款:**发布移植版的任何内容(jar、公开仓库、原版纹理或声音)都需要获得作者或当前权利人的许可。向他们征求许可,并将回复(链接或引文)保存在状态文档中。未经许可,移植版必须保持私有。
- 此处作为实例的 Thaumcraft 4 是闭源的。2014 年,其作者曾让 GitHub 下架一份反编译副本 (DMCA 通知)。
- **开始前告知用户属于哪种情况。**是否为自己的整合包制作私有移植版,由用户自行决定。
- 在移植版及任何发行版本中注明原作者,且不要将移植版说成官方版本。
0. 确定唯一的事实来源
- 锁定确切版本:jar、其哈希值(SHA-1)、来源和变更日志。对于已停止更新的 Mod 系列,通常应以最后一个版本为准。
- **只使用一个版本作为参考。**同名的后续重写版本(Thaumcraft 5 和 6 相对于 4,或“Reborn”“Legacy”分支)都是不同的 Mod。
- Thaumcraft 移植项目一开始选错了基础版本,中途才更换。
- 数周后,代码中仍然残留着 TC5 网格、纹理、输入处理器和挥击钩子,尽管每次审计都在将游戏玩法与 TC4 对比。
- 如果基础版本确实要更换,请归档旧状态,将每个部分分类为保留/调整/替换/移除,并搜索注释和标识符,查找旧版本名称。
- **将规则写在每位后续 Agent 都能看到的地方:**项目的
AGENTS.md(任何特定于 Agent 的说明文件都应指向它)。示例: “ 是唯一的游戏玩法来源。保留其数值、特性和可观察到的 Bug。不要参考后续版本。不要悄悄改进游戏玩法。除非实际运行过,否则绝不要声称构建或测试已通过。”
1. 构建私有参考资料
- 如果原项目的源代码已发布(其代码仓库、sources jar),请基于固定版本标签中的源代码开展工作,并且只反编译 jar 来确认二者一致。
- 先使用 Vineflower 反编译。 对于无法处理的类,再改用 CFR,并统计这些类的数量。逆向工程技能中提供了相关工具。
- 重映射为易读名称:
- 1.7.10 Forge jar 使用 SRG 名称(
func_12345_a、field_1234_b)。使用 MCPstable_12CSV(methods.csv、fields.csv)重映射;用替换标记的脚本即可。1.12 使用stable_39。 - 在目标端,通过
javap读取加载器缓存中的映射后游戏 jar。它会显示 Mixin 的名称、lambda 名称和局部变量槽位。Loom 的genSources会生成易读的源代码。
- 1.7.10 Forge jar 使用 SRG 名称(
- 提取原始资源(纹理、模型、lang、声音),并放在反编译文件旁边,以便资源审计可以与其哈希值进行比对。
- 保持私有。 反编译内容绝不随项目发布,也绝不提交到公开代码仓库(硬性规定)。
- 确保未来每个会话都能访问这些内容:使用独立的私有代码仓库,或将其放在移植项目代码仓库之外的固定文件夹中。
- 不要将其纳入移植项目自身的历史记录。日后公开的代码仓库会发布其全部历史记录。
- Thaumcraft 移植项目将
reference/保存在私有代码仓库中,因此每次开启新的云端会话,都能在更改任何内容之前读取原始项目。
2. 清点内容并制定适配矩阵
- **列出原项目的每个子系统:**注册表、事件处理器、数据包、GUI 和屏幕、渲染器、世界生成、战利品、配置、命令、公共 API、兼容钩子。
- **编写矩阵:**针对每个领域,列出原项目类、移植项目类和结论(保留/适配/替换/移除/缺失)。从其他基础项目移植而来的代码也要按同样方式处理。
- 按依赖关系安排里程碑:
- 数据基础:ID、物品和方块注册表、模组核心数据(要素、研究);
- 核心机制;
- 装置、合成、世界生成和生物;
- 渲染和特效;
- 兼容性。 从第一天起就维护状态文档(第 7 节)。
3. 提取数据,不要手动重录
- 注册代码其实是伪装成代码的数据(
ConfigItems、ConfigResearch、配方类):- 使用小型分词器脚本解析反编译后的调用链,并将 JSON 写入移植项目的资源目录。
- 在脚本中记录源代码行号。
- 重新运行脚本,而不是手动编辑脚本的输出。
- 在 Thaumcraft 移植项目中,这样提取出了 48 种要素、457 个物品要素注册项、包含页面和扭曲值的研究树,以及所有配方。
- 旧版 ID。 旧物品通常以
(物品,元数据)对表示,而新版引擎不再使用元数据。- 为每个组合分配一个扁平化 ID。
- 让每个移植的物品注册其所替代的旧版引用。
- 通过该映射解析数据文件中的引用。
- 如果目录中有条目尚未注册,则在启动时失败。
- 对于基础游戏中已重命名的 ID,使用目标端的数据修复器。
- **统计所有内容:**测试每个原始物品和元数据变体是否存在,可以发现未被察觉的缺漏(Thaumcraft 移植项目中有 225 个物品 + 119 个方块物品)。
- 保留原始顺序。 旧模组中的查找通常采用首次匹配:第一个匹配的配方或类别会胜出。数值也可能累积(例如在一次注册中重复添加要素)。提取器若对条目排序或覆盖映射键,就会改变行为。
- 追踪尚未解析的引用。 每次处理后统计未解析引用的数量(Thaumcraft 移植项目中从 101 → 47 → 24),以免有内容一直被悄悄搁置。
- 移植前确认原项目确实调用了相关代码。 在反编译内容中查找调用位置和发送位置,并记录死代码,以免后续审计将其算作缺失。Thaumcraft 4 发布了 7 个从未被调用的特效类和 3 个从未发送过的数据包。
4. 逐个单元移植
每个单元(方块、设备、生物、机制)都遵循相同步骤:
- 阅读原始类。
- 移植。
- 标记保留的怪癖。
- 编写测试。
- 运行测试。
- 在状态文档中记录该单元。
- 提交。
-
**保留可观察到的怪癖和 Bug,**并添加注释,指出原始类或代码行,以免下一个 Agent 将其“修复”。Thaumcraft 移植中的示例:
- 按住某个按键会阻止所有方块的右键操作,因为原始实现会对每个方块都返回“已处理”;
- 即使关闭了选项,Boss 仍会随机获得冠军修饰效果;
- 带有容器物品的材料不会添加任何要素。
但不要复现崩溃或客户端不同步问题。请逐一列出此类情况。
-
记录每项强制偏离(引擎限制、已移除的 API)及其原因,并将其列入状态文档中的“已知偏离”清单。
-
在日志中使用两个标签:
- “保留的怪癖”:原始 Bug,出于有意而保留;
- “偏离”:新引擎无法或不应复现的内容,并且必须附上原因。 后续读者便能区分保留的 Bug 与移植 Bug。
-
**原始代码继承自旧引擎。**子类可能继承自原版生物、原版树木生成器或旧版移动代码,而新引擎中的对应版本并不相同。也要移植旧引擎的相关部分,并关闭新父类新增的行为(在 Minecraft 中,1.20 的僵尸子类会获得转化为溺尸的能力)。
-
**标记凭记忆推断的平台行为。**Mod 的反编译代码中不包含旧引擎或加载器(Forge、原版 1.7.10)。凡是依据记忆得出的内容,都要标为 UNVERIFIED,或者也对旧引擎和加载器进行反编译。
-
**尚不存在的内容要使用替代项。**为每项缺失依赖添加具名钩子或标签,将其列为待处理,并在对应单元完成后接入。
-
**确保服务器具有权威性。**重新验证客户端曾由原始实现信任的请求。同时仍要移植原始实现的客户端预检查和消息。
-
**忠实度与可玩性之间如何取舍,由用户决定。**例如,在新地形上几乎无法通过的地点检查、新生态已不再需要的回退方案,以及超出原始内容的美术设计,都要记录为待用户决定的事项。不要擅自敲定。
-
**有意映射引擎概念。**不同版本之间,事件顺序、刻(tick)、元数据、渲染和世界生成都发生了变化。Minecraft 的对照表位于
references/minecraft-1-7-10-forge-to-1-20-fabric.md;移植过程中也要为你的引擎编写一份。 -
**绝不要凭记忆移植。**当 Wiki 与反编译结果不一致时,以反编译结果为准。
5. 从低成本到真实环境的验证方式
通用方法见 knowledge/techniques/oracles-how-agents-know-a-mod-works.md。移植时,按从低成本到真实环境的顺序:
- 在真实服务器上运行无头测试(Minecraft GameTests:
runGametest)验证逻辑。解析 JUnit XML;不要轻信控制台输出。设置步骤和常见陷阱见:knowledge/techniques/fabric-1-20-1-gametests-as-the-oracle-of-a-large-mod-port.md。- **编写可能失败的测试。**选择错误行为会产生不同结果的输入,并在编写断言前读取原始实现的数值。
- 每项测试都要清理自身位置周围的全局状态(测试间会泄漏共享状态)。
- 使用脚本控制真实客户端,测试点击流程、屏幕、渲染、自然世界生成和第二名玩家:
knowledge/techniques/a-scenario-harness-in-a-minecraft-fabric-dev-client-real-cli.md。- 服务器测试会直接调用物品代码。它们无法发现移植时遗漏了原始的客户端预检查及其消息。
- **在真实服务器上运行发布版 jar 和真实整合包的 jar。**重映射、jar 嵌套和依赖冲突只有在此时才会显现。
- 由真人进行游戏流程测试,覆盖以上方式都无法验证的内容。请他人进行测试;不要声称已经测试。
请按系统如实报告证据等级:
- 已实现;
- 已测试;
- 已在真实客户端中观察;
- 未验证。
如果某次运行并未实际执行,绝不要写“通过”。如果主机或工具无法访问,请说明哪些测试没有运行。
6. 按层与原版进行审计
逐个功能开展工作会造成系统性偏差,单个测试无法发现这些问题。
- 功能完成后,每次审计一层:
- 事件处理器、数据包、物品、方块、实体、GUI;
- 世界生成、战利品、研究与配方、配置;
- 渲染器,细分为瓦片、方块、实体和物品。
- 每次审计都作为只读检查运行。 它会将该层中的每个原版类与移植版本逐行比对,并返回按优先级排序的发现,注明文件和行号。
- 并行安排多名审查者,每组一名,一轮即可覆盖一层。
- 处理完待办事项后,重新运行对应的视觉场景。
- 在所有游戏玩法测试都已通过后,Thaumcraft 渲染器审计仍发现了大约 60 处差异。
- 每当更换基底或源版本后,运行一次遗留内容审计:搜索旧版本名称,并将每个纹理和模型与原始 jar 进行哈希比对。
- 检查原版在客户端执行的检查:例如拒绝操作时显示聊天消息、工具提示、取消挥动。移植版本常常只保留服务器端的二次检查,导致操作被静默拒绝。
7. 让移植工作可续接
大型移植项目往往会超出单次会话的上下文容量。让仓库记录状态,并且先构建,再信任代码。有次会话在 Thaumcraft 移植中留下了转换到一半的代码:调用旧基底的方法、使用旧枚举值。每次会话开始时都要构建,并根据参考代码重新移植出错的调用,而不是用存根代替。
- 状态文档(
docs/MIGRATION_STATUS.md):- 原版的许可证,以及所需的作者许可;
- 固定的版本和适配矩阵;
- 按单元记录的日志:已移植的内容、原版类、保留的特性、差异、测试;
- 已知的错误和差异;
- 已知的未经验证区域;
- 确切的下一项任务;
- 根据证据级别而非提交数量估算完成度。
- 简短的交接文档(
docs/HANDOFF.md):项目现状、不可妥协的决策、命令、最近一次真实测试结果,以及需要用户回答的待解决问题。 - Agent 说明(
AGENTS.md),注明可信来源和验证规则。 - **每个单元一个提交,**每次会话结束时推送(除非可以公开发布,否则推送到私有远程仓库)。 临时云容器会丢失所有未推送的内容。
- 记录构建前置条件。 云环境必须允许加载器的 Maven 仓库和游戏厂商的下载主机访问。对于 Minecraft,这些主机包括
maven.fabricmc.net、piston-meta.mojang.com、piston-data.mojang.com、libraries.minecraft.net和resources.download.minecraft.net,以及每个依赖项的 Maven 仓库。缺少这些条件,首次 Gradle 构建就无法运行。 - 随时记录实地笔记(
docs/field-notes/):每条笔记记录一个事实,并附上证据。最后将它们整理为知识库笔记。
8. 分享
- 为每次移植编写一份游戏笔记(
um kb new):版本、移植路线、原版许可证及移植版本是否公开、注意事项。 - 为可用于其他模组的方法编写技术笔记:渲染状态映射、测试框架、数据提取。
- 参阅 share-field-notes 技能。任何 PR 之前都要先询问用户。
硬性规则(在 mod-any-game 的规则之上)
- 除非原版许可证允许,否则不得随移植版本发布反编译参考代码和原版资源。
- 只有在许可证允许,或作者(或现任权利持有人)已授予许可时,才能发布他人模组的移植版本。否则必须保持私有。无论如何都要注明原作者。
- 不要因为实现更容易,就从模组的其他版本引入机制。
- 不得声称运行过实际上并未运行的测试、构建或视觉检查。