组织 Mod 代码结构
良好的 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 尝试访问这些包,就会崩溃。为客户端代码设置专用包,也能让你在检查代码时更容易发现跨端访问问题。
类命名方式
统一的类命名方式能帮助读者理解类的用途,也方便定位特定的类。
常见做法是在类名末尾附加其类型,例如:
- 名为
PowerRing的Item→PowerRingItem。 - 名为
NotDirt的Block→NotDirtBlock。 Oven的菜单 →OvenMenu。
建议 Mojang 通常也采用类似的结构,但实体类例外:实体类直接使用其名称,例如
Pig、Zombie等。
同一任务采用一致的方法
注册对象、监听事件等任务往往有多种实现方式。通常建议在同一任务上保持一致,只使用一种方式。这能提高可读性,也能避免意外交互或重复执行,例如同一个事件监听器运行两次。
来源: NeoForged/Documentation(26.1)。中文翻译依据提交
4a680cdd4d15。