资源

资源

资源是游戏使用的外部文件,不属于代码。纹理是最常见的一种,但 Minecraft 生态中还有许多其他资源。所有资源都需要代码中的系统来使用,因此本节也会介绍使用这些资源的系统。

Minecraft 通常有两类资源:逻辑客户端使用的资源称为 assets,逻辑服务端使用的资源称为 data。前者主要是纹理、显示模型、译文和声音等表现信息;后者则包括战利品表、配方、世界生成信息等会影响玩法的内容。它们分别从资源包和数据包加载。NeoForge 为每个 Mod 生成内置资源包与数据包。

资源包和数据包通常都需要 pack.mcmeta 文件,但现代 NeoForge 会在运行时自动生成,因此不必自行处理。

如果不清楚某种文件的格式,可以参考原版资源。NeoForge 开发环境不仅包含原版代码,也包含原版资源。在 IntelliJ 的 External Resources 或 Eclipse 的 Project Libraries 中,可以找到名为 ng_dummy_ng.net.minecraft:client:client-extra:<minecraft_version> 的 Minecraft 资源,以及名为 ng_dummy_ng.net.neoforged:neoforge:<neoforge_version> 的 NeoForge 资源。

客户端资源

另见 Minecraft Wiki 的资源包条目。

assets,即客户端资源,是仅在客户端有意义的资源。它们从资源包加载,资源包有时也被称为纹理包(源于旧版资源包只能影响纹理)。资源包本质上是一个 assets 文件夹,其中每个子文件夹对应包所包含的一个命名空间。例如,ID 为 coolmod 的 Mod 所用资源包通常包含 coolmod 命名空间,也可以额外包含 minecraft 等其他命名空间。

NeoForge 会自动将所有 Mod 资源包汇总到 Mod resources 包中,位于资源包菜单“已选资源包”一侧的最下方。目前无法禁用该包。不过,上方资源包可以覆盖下方资源包定义的资源。这既允许资源包作者替换 Mod 资源,也允许 Mod 开发者按需覆盖 Minecraft 资源。

资源包可以包含模型、方块状态文件、纹理、声音、粒子定义与语言文件。

服务端数据

另见 Minecraft Wiki 的数据包条目。

data 指所有服务端资源,通过数据包(data pack 或 datapack)加载。与资源包类似,数据包由 pack.mcmeta 文件和名为 data 的根文件夹组成。data 下的每个子文件夹也对应一个命名空间。例如,ID 为 coolmod 的 Mod 所用数据包通常包含 coolmod 命名空间,也可以额外包含 minecraft 等命名空间。

创建世界时,NeoForge 会自动应用所有 Mod 数据包,目前无法禁用它们。但大多数数据文件可以被优先级更高的数据包覆盖,因此也可以通过替换为空文件来移除。额外数据包可放入世界的 datapacks 子文件夹,再通过 /datapack 命令启用或禁用。

说明 目前没有内置方式将一套自定义数据包应用到所有世界,不过有多种 Mod 可以实现这一点。

数据包可以包含影响以下内容的文件夹:

文件夹 内容
advancement 进度
damage_type 伤害类型
loot_table 战利品表
recipe 配方
tags 标签
neoforge/data_maps 数据映射
neoforge/loot_modifiers 全局战利品修改器
dimension、dimension_type、structure、worldgen、neoforge/biome_modifier 世界生成文件

此外,还可以包含与命令集成的系统所用文件夹。这些系统较少与 Mod 配合使用,但也值得了解:

文件夹 内容
chat_type 聊天类型
function 函数
item_modifier 物品修改器
predicate 谓词

pack.mcmeta

另见 Minecraft Wiki 的 pack.mcmeta(资源包)与 pack.mcmeta(数据包)条目。

pack.mcmeta 保存资源包或数据包的元数据。对 Mod 而言,NeoForge 会自动生成这个文件,因此不再需要手动提供。如果仍需自行创建,完整规范见上述 Minecraft Wiki 链接。

数据生成

数据生成,通常称为 datagen,是以编程方式生成 JSON 资源文件的机制,用来避免手写文件的繁琐过程和常见错误。这个名称略有误导性,因为它既适用于服务端数据,也适用于客户端资源。

数据生成通过 Data 运行配置执行,该配置会与 Client、Server 配置一起自动生成。Data 配置遵循 Mod 生命周期,直到注册表事件触发完毕,然后触发 GatherDataEvent。可在该事件中以数据提供器的形式注册待生成对象;之后程序将对象写入磁盘并结束进程。

所有数据提供器都实现 DataProvider 接口,通常需要重写一个方法。Minecraft 和 NeoForge 提供的主要数据生成器如下(链接文章包含辅助方法等更多信息):

类 方法 生成内容 端 说明
BlockStateProvider registerStatesAndModels() 方块状态文件、方块模型 客户端
ItemModelProvider registerModels() 物品模型 客户端
LanguageProvider addTranslations() 译文 客户端 构造函数还需传入语言。
ParticleDescriptionProvider addDescriptions() 粒子定义 客户端
SoundDefinitionsProvider registerSounds() 声音定义 客户端
SpriteSourceProvider gather() 精灵来源/图集 客户端
AdvancementProvider generate() 进度 服务端 应使用 NeoForge 版本,而非 Minecraft 版本。
LootTableProvider generate() 战利品表 服务端 需要额外方法和类,详见链接文章。
RecipeProvider buildRecipes(RecipeOutput) 配方 服务端
TagsProvider 的各个子类 addTags(HolderLookup.Provider) 标签 服务端 有多个专用子类,详见链接文章。
DataMapProvider gather() 数据映射条目 服务端
GlobalLootModifierProvider start() 全局战利品修改器 服务端
DatapackBuiltinEntriesProvider 不适用 数据包内置条目,例如世界生成和伤害类型 服务端 不重写方法,而是在构造函数的 lambda 中添加条目,详见链接文章。
JsonCodecProvider(抽象类) gather() 带有 codec 的对象 两端 可继承该类,用于任何具有数据编码 codec 的对象。

这些提供器遵循相同模式:先创建子类并添加待生成资源,再在事件处理器中将提供器添加到事件。以 RecipeProvider 为例:

public class MyRecipeProvider extends RecipeProvider {
    public MyRecipeProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider) {
        super(output, lookupProvider);
    }

    @Override
    protected void buildRecipes(RecipeOutput output) {
        // Register your recipes here.
    }
}

// In some event handler class
@SubscribeEvent // on the mod event bus
public static void gatherData(GatherDataEvent event) {
    // Data generators may require some of these as constructor parameters.
    // See below for more details on each of these.
    DataGenerator generator = event.getGenerator();
    PackOutput output = generator.getPackOutput();
    ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
    CompletableFuture<HolderLookup.Provider> lookupProvider = event.getLookupProvider();

    // Register the provider.
    generator.addProvider(
            // A boolean that determines whether the data should actually be generated.
            // The event provides methods that determine this:
            // event.includeClient(), event.includeServer(),
            // event.includeDev() and event.includeReports().
            // Since recipes are server data, we only run them in a server datagen.
            event.includeServer(),
            // Our provider.
            new MyRecipeProvider(output, lookupProvider)
    );
    // Other data providers here.
}

该事件提供以下上下文:

  • event.getGenerator() 返回用于注册提供器的 DataGenerator。
  • event.getPackOutput() 返回 PackOutput,某些提供器用它确定输出位置。
  • event.getExistingFileHelper() 返回 ExistingFileHelper,供可能引用其他文件的提供器使用,例如能指定父模型的方块模型。
  • event.getLookupProvider() 返回 CompletableFuture<HolderLookup.Provider>,主要供标签和数据生成注册表引用其他可能尚不存在的元素。
  • event.includeClient()、event.includeServer()、event.includeDev() 与 event.includeReports() 返回布尔值,可用于检查是否启用了下述特定命令行参数。

命令行参数

数据生成器接受以下命令行参数:

  • --mod examplemod:为指定 Mod 运行数据生成。NeoGradle 会自动为所属 Mod ID 添加;如果一个项目包含多个 Mod 等情况,可自行补充。
  • --output path/to/folder:指定输出文件夹。建议使用 Gradle 的 file(...).getAbsolutePath(),从相对于项目根目录的路径生成绝对路径。默认为 file('src/generated/resources').getAbsolutePath()。
  • --existing path/to/folder:检查现有文件时将指定文件夹纳入范围。同样建议使用 Gradle 的 file(...).getAbsolutePath()。
  • --existing-mod examplemod:检查现有文件时将指定 Mod JAR 中的资源纳入范围。
  • 生成器模式(均为布尔参数,不需要附加值):
    • --includeClient:是否生成客户端资源(assets),通过 GatherDataEvent#includeClient() 在运行时检查。
    • --includeServer:是否生成服务端资源(data),通过 GatherDataEvent#includeServer() 检查。
    • --includeDev:是否运行开发工具。Mod 通常不应使用此项,通过 GatherDataEvent#includeDev() 检查。
    • --includeReports:是否导出已注册对象列表,通过 GatherDataEvent#includeReports() 检查。
    • --all:启用所有生成器模式。

在 build.gradle 中添加以下内容,即可把参数加入运行配置:

runs {
    // other run configurations here

    data {
        programArguments.addAll '--arg1', 'value1', '--arg2', 'value2', '--all' // boolean args have no value
    }
}

例如,要复现默认参数,可以这样设置:

runs {
    // other run configurations here

    data {
        programArguments.addAll '--mod', 'examplemod', // insert your own mod id
                '--output', file('src/generated/resources').getAbsolutePath(),
                '--includeClient',
                '--includeServer'
    }
}

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