构建 Mod 的结构

组织 Mod 的结构

合理组织 Mod 有利于维护和接受贡献,也能让人更清楚地理解底层代码。以下列出 Java、Minecraft 和 Forge 的一些建议。

拥有的域名转换为唯一顶层包,再加入 Mod ID;按功能或逻辑组织子包,并隔离 client、server 和 data 代码

原创说明图,依据 Forge 1.21.x 官方文档 绘制。

这些建议并非强制要求,你可以按自己的方式组织 Mod,但文档仍强烈建议遵循它们。

包结构

选择唯一的顶层包结构。不同程序员可能给类或接口使用相同名称;Java 允许不同包中的类同名,但如果两个类的包名和类名完全相同,通常只会加载其中一个,很可能导致游戏崩溃。

a.jar
  - com.example.ExampleClass
b.jar
  - com.example.ExampleClass  // 此类通常不会被加载

模块加载时这一点更加重要:不同模块中出现同名包会导致 Mod 加载器启动时崩溃,因为 Mod 模块会导出给游戏和其他 Mod。

module A
  - package X
    - class I
    - class J
module B
  - package X  // 已有模块导出 package X,因此会导致加载器崩溃
    - class R
    - class S
    - class T

顶层包应使用你拥有的标识,例如域名、电子邮件地址或网站子域名;只要能保证在预期范围内唯一,也可以使用姓名或用户名。

类型 示例 顶层包
域名 example.com com.example
子域名 example.github.io io.github.example
邮箱 example@gmail.com com.gmail.example

下一层包应使用 Mod ID,例如 com.example.examplemod 中的 examplemod。只要不存在两个相同 ID 的 Mod,包加载就不应发生冲突。更多命名约定可参考 Oracle 教程。

子包组织

建议将类拆分到子包中,主要有两种方法:

  • 按功能分组:将用途相同的类放在一起,例如方块放在 block 或 blocks,实体放在 entity 或 entities。Mojang 使用单数形式。
  • 按逻辑分组:将同一逻辑的类放在一起。例如,新工作台的方块、菜单、物品等可放在 feature.crafting_table 中。

客户端、服务端与数据生成代码包

通常应将只用于特定端或运行环境的代码放在单独的子包中。例如,数据生成代码放在 data 包,仅用于独立服务器的代码放在 server 包。

强烈建议将仅客户端代码放在 client 子包。独立服务器无法访问 Minecraft 的仅客户端包;单独的包有助于检查 Mod 是否错误地跨端引用。

类命名方式

统一的命名方式有助于理解类的用途并定位类。类名通常以其类型作为后缀:

  • 名为 PowerRing 的 Item:PowerRingItem。
  • 名为 NotDirt 的 Block:NotDirtBlock。
  • Oven 的菜单:OvenMenu。

Mojang 通常也采用类似结构,但实体类除外:实体通常直接使用名称,例如 Pig、Zombie。

从多种方式中选择一种

注册对象、监听事件等任务通常有多种实现方式。建议对同一任务始终使用一种方式,保持一致。这不仅能改善代码格式,也能避免意外的交互或重复操作,例如事件监听器执行两次。

原文:Structuring Your Mod。