组织 Mod 项目结构
清晰的项目结构有利于维护和协作,也能让人更容易理解代码库。下文整理了 Java、Minecraft 和 NeoForge 开发中的一些建议。
提示 这些建议并非强制要求。你可以按照适合自己的方式组织 Mod,但仍然强烈建议参考它们。
包结构
组织 Mod 时,应选择唯一的顶层包结构。不同开发者经常会给类、接口等使用相同的名称。Java 允许不同包中的类同名;但如果两个类的包名和类名都相同,就只会加载其中一个,通常会导致游戏崩溃。
a.jar
- com.example.ExampleClass
b.jar
- com.example.ExampleClass // This class will not normally be loaded
模块加载时,这一点更加重要。如果不同模块中存在同名的包,Mod 加载器会在启动时崩溃,因为 Mod 模块需要将包导出给游戏和其他 Mod。
module A
- package X
- class I
- class J
module B
- package X // This package will cause the mod loader to crash, as there already is a module with package X being exported
- class R
- class S
- class T
因此,顶层包名应来自你拥有的标识,例如域名、电子邮件地址或网站及其子域名。也可以使用姓名或用户名,但必须能够保证它在预期使用范围内具有唯一性。此外,顶层包应与你的 group id 一致。
| 类型 | 值 | 顶层包 |
|---|---|---|
| 域名 | example.com | com.example |
| 子域名 | example.github.io | io.github.example |
| 电子邮件 | example@gmail.com | com.gmail.example |
下一层包名应当使用 Mod ID,例如 com.example.examplemod,其中 examplemod 是 Mod ID。这样,只要没有两个 Mod 使用相同 ID(本就不应出现这种情况),包的加载通常就不会发生冲突。
其他命名惯例可参阅 Oracle 教程。
子包组织
除了顶层包,还强烈建议将 Mod 中的类划分到不同子包。主要有两种组织方式:
- 按功能分类:将用途相近的类放入同一个子包。例如,方块放在
block,物品放在item,实体放在entity。Minecraft 本身大体上也采用这种结构,但存在一些例外。 - 按逻辑分类:将属于同一套功能逻辑的类放在一起。例如,新增一种工作台时,可以将它的方块、菜单、物品等类都放在
feature.crafting_table下。
客户端、服务端和数据包
一般来说,仅供某一端或某个运行环境使用的代码,应与其他类分开,放入单独的子包。例如,数据生成代码应放入 data 包,仅供专用服务端使用的代码应放入 server 包。
强烈建议将客户端专用代码放入 client 子包。专用服务端无法访问 Minecraft 中的客户端专用包,如果 Mod 尝试访问它们,便会崩溃。单独划分子包,可以帮助你检查 Mod 是否错误地跨端访问了代码。
类的命名方式
统一的类名规则能够帮助读者理解类的用途,也便于快速查找特定类。
常见做法是在名称后添加类型后缀,例如:
- 名为
PowerRing的Item→PowerRingItem。 - 名为
NotDirt的Block→NotDirtBlock。 Oven对应的菜单 →OvenMenu。
建议 Mojang 的大部分类也遵循类似规则,实体类除外。实体类通常直接以实体名称命名,例如
Pig、Zombie。
对同一任务使用一致的方式
注册对象、监听事件等任务往往有多种实现方式。通常建议在同一项目中选择一种并保持一致。这能提高可读性,也能避免意外的交互或重复执行,例如同一个事件监听器运行两次。
来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交
4a680cdd4d15。