Skip to main content
Version: 1.21.4

组织你的 Mod 结构(Structuring Your Mod)

结构化的 Mod 有助于后期维护、协作开发,并让代码库更清晰易懂。下面列出了一些来自 Java、Minecraft 和 NeoForge 的组织建议。

note

你并不一定要遵循以下建议;你可以按照自己的方式组织 Mod 结构。不过,强烈建议你采纳这些做法。

包结构(Packaging)

在组织你的 Mod 时,应该选择一个独特的顶级包(package)结构。许多程序员会为不同的类、接口等使用相同的名字。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,这个包会导致 Mod 加载器崩溃
- class R
- class S
- class T

因此,你的顶级包应该是你拥有的某个标识:比如域名、邮箱地址、(子)网站等。只要你能保证其在目标范围内唯一,也可以使用你的名字或用户名。此外,顶级包还应与你的 group id 保持一致。

类型示例值顶级包(Top-Level Package)
域名example.comcom.example
子域名example.github.ioio.github.example
邮箱example@gmail.comcom.gmail.example

下一层包应该是你的 Mod 的 id,例如 com.example.examplemod,其中 examplemod 就是 mod id。这样可以保证,除非你有两个同名 mod(这绝不应该发生),你的包不会出现加载冲突。

你可以在 Oracle 的命名规范页面 上找到更多命名约定。

子包组织(Sub-package Organization)

除了顶级包之外,强烈建议你将 Mod 的类分别放在不同的子包中。主要有两种常见的组织方式:

  • 按功能分组(Group By Function):为具有相同用途的类创建子包。例如,方块相关的类可以放在 block 包下,物品相关的放在 item 包下,实体相关的则放在 entity 包下。Minecraft 本身也采用了类似的结构(但有一些例外)。
  • 按逻辑分组(Group By Logic):为具有相同逻辑的类创建子包。例如,如果你要创建一种新的工作台类型,可以将其方块、菜单、物品等都放在 feature.crafting_table 包下。

客户端、服务端与数据包(Client, Server, and Data Packages)

通常,专属于某一端或运行时环境的代码,应当与其他类隔离,放在单独的子包中。例如,与 数据生成 相关的代码应放在 data 包中,而仅在专用服务端运行的代码则应放在 server 包中。

强烈建议将 仅限客户端的代码 隔离在 client 子包中。因为专用服务端无法访问 Minecraft 中任何仅限客户端的包,如果你的模组尝试访问这些内容,服务端会直接崩溃。因此,单独的包结构可以作为有效的自检机制,帮助你确认没有在模组内部跨端访问。

类命名规范(Class Naming Schemes)

统一的类命名规范可以让你更容易理解类的用途,也便于快速定位特定的类。

类名通常会以其类型作为后缀,例如:

  • 一个名为 PowerRingItemPowerRingItem
  • 一个名为 NotDirtBlockNotDirtBlock
  • 一个用于 Oven 的菜单 → OvenMenu
tip

Mojang 通常也采用类似的结构,唯一的例外是实体(entity)类。实体类通常只用其名称表示(例如 PigZombie 等)。

多种方法中选择一种(Choose One Method from Many)

完成某项任务(如注册对象、监听事件等)通常有多种方法。建议你在同一项目中保持一致,只使用一种方法来完成某项任务。这样可以提升代码可读性,并避免出现奇怪的交互或冗余现象(例如事件监听器被触发两次)。

分组 命名规范 数据生成 端(Sides)