组织 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 尝试访问它们,便会崩溃。单独划分子包,可以帮助你检查 Mod 是否错误地跨端访问了代码。

类的命名方式

统一的类名规则能够帮助读者理解类的用途,也便于快速查找特定类。

常见做法是在名称后添加类型后缀,例如:

  • 名为 PowerRing 的 Item → PowerRingItem。
  • 名为 NotDirt 的 Block → NotDirtBlock。
  • Oven 对应的菜单 → OvenMenu。

建议 Mojang 的大部分类也遵循类似规则,实体类除外。实体类通常直接以实体名称命名,例如 Pig、Zombie。

对同一任务使用一致的方式

注册对象、监听事件等任务往往有多种实现方式。通常建议在同一项目中选择一种并保持一致。这能提高可读性,也能避免意外的交互或重复执行,例如同一个事件监听器运行两次。


来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交 4a680cdd4d15。