资源(Resources)
资源(Resources)是指被游戏使用但不属于代码的外部文件。最常见的资源类型是贴图(textures),不过在 Minecraft 生态系统中还存在许多其他类型的资源。当然,所有这些资源在代码端都需要有对应的消费系统,因此本节也会对这些消费系统进行归类说明。
Minecraft 通常包含两类资源:用于 逻辑客户端 的资源,被称为资产(assets),以及用于 逻辑服务端 的资源,被称为数据(data)。资产主要用于展示相关的信息,例如贴图、显示模型、翻译文本或音效;而数据则包括会影响游戏玩法的各种内容,比如掉落表、合成配方或世界生成信息。它们分别通过资源包(resource packs)和数据包(data packs)进行加载。NeoForge 会为每个模组自动生成一个内置的资源包和数据包。
无论是资源包还是数据包,通常都需要包含一个 pack.mcmeta 文件;不过,现代的 NeoForge 会在运行时自动为你生成这些文件 ,因此你无需手动处理。
如果你对某些内容的格式感到困惑,可以参考原版的资源。你的 NeoForge 开发环境不仅包含了原版代码,还包含了原版资源。这些资源可以在 External Resources(IntelliJ)或 Project Libraries(Eclipse)部分找到,名称为 ng_dummy_ng.net.minecraft:client:client-extra:<minecraft_version>(用于 Minecraft 资源)或 ng_dummy_ng.net.neoforged:neoforge:<neoforge_version>(用于 NeoForge 资源)。
资产(Assets)
另见:资源包(Minecraft Wiki)
资产(Assets),也称为客户端资源(client-side resources),是所有仅在 客户端 相关的资源。这些资源通过资源包(resource packs)加载,有时也被称为旧称“贴图包”(texture packs),这是因为早期版本中它们只能影响贴图。一个资源包本质上就是一个 assets 文件夹。assets 文件夹下包含了资源包所涉及的各个命名空间(namespace)的子文件夹;每个命名空间对应一个子文件夹。例如,一个 mod 的 id 为 coolmod,那么它的资源包通常会包含一个名为 coolmod 的命名空间,但也有可能包含其他命名空间,比如 minecraft。
NeoForge 会自动将所有模组的资源包收集到一个名为 Mod resources 的资源包中,该资源包在资源包菜单中位于已选资源包列表的最底部。目前无法禁用 Mod resources 包。然而,位于 Mod resources 包上方的其他资源包会覆盖下方资源包中定义的资源。这个机制允许资源包制作者覆盖你的模组资源,同时也允许模组开发者在需要时覆盖 Minecraft 的原版资源。
资源包可以包含影响以下内容的文件夹:
| 文件夹名称 | 内容描述 |
|---|---|
atlases | 纹理图集来源 |
blockstates | 方块状态文件 |
equipment | 装备信息 |
font | 字体定义 |
items | 客户端物品 |
lang | 翻译文件 |
models | 模型 |
particles | 粒子定义 |
post_effect | 后处理屏幕特效 |
shaders | 元数据、片元着色器和顶点着色器 |
sounds | 音效文件 |
texts | 杂项文本文件 |
textures | 纹理 |
数据(Data)
另请参阅:数据包(Data Packs)在 Minecraft Wiki 上的相关内容
与资源(assets)相对,数据(data)指的是所有 服务器 端资源。类似于资源包(resource packs),数据是通过数据包(data packs 或 datapacks)加载的。与资源包类似,一个数据包包含一个 pack.mcmeta 文件 和一个名为 data 的根文件夹。然后,和资源包一样,data 文件夹中包含了该资源包所涉及的各个命名空间(namespace)的子文件夹;每个命名空间对应一个子文件夹。例如,一个 mod 的 id 为 coolmod,那么它的数据包通常会包含一个 coolmod 命名空间,但也有可能包含其他命名空间,比如 minecraft。
NeoForge 会在新世界创建时自动应用所有模组数据包。目前无法禁用模组数据包。不过,大多数数据文件都可以被优先级更高的数据包覆盖(因此也可以通过用空文件替换来移除)。你可以通过将其他数据包放入世界的 datapacks 子文件夹,并通过 /datapack 命令启用或禁用这些数据包,从而管理额外的数据包。
目前没有内置方式可以让每个世界都自动应用一组自定义数据包。不过,有不少模组实现了这一功能。
数据包可以包含以下内容相关的文件夹和文件:
| 文件夹名称 | 内容 |
|---|---|
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
另见:pack.mcmeta(资源包) 和 pack.mcmeta(数据包),参见 Minecraft Wiki
pack.mcmeta 文件用于存储资源包或数据包的元数据。对于模组(mod)来说,NeoForge 让这个文件变得不再必要,因为 pack.mcmeta 会被自动生成。如果你确实需要一个 pack.mcmeta 文件,完整的规范可以在前面链接的 Minecraft Wiki 文章中找到。
数据生成(Data Generation)
数据生成(Data generation,常被简称为 datagen)是一种通过编程方式生成 JSON 资源文件的方法,目的是避免手动编写这类文件时的繁琐和易出错。虽然名字叫“数据生成”,但它同样适用于资源(assets)和数据(data)。
Datagen 是通过 Data 运行配置(Data run configuration)来执行的,这个配置会和 Client(客户端)与 Server(服务端)的运行配置一起自动生成。Data 运行配置会遵循 模组生命周期,直到注册事件(registry events)触发之后。此时会触发某个 GatherDataEvent 事件,你可以在这个事件中以数据提供者(data provider)的形式注册你需要生成的对象,系统会将这些对象写入磁盘,然后结束整个流程。
有两种子类型在 物理端(physical side) 上运行:GatherDataEvent.Client 和 GatherDataEvent.Server。GatherDataEvent.Client 可以包含所有需要生成的提供器(provider)。而 GatherDataEvent.Server 只能包含用于生成数据包(datapack)条目的提供器。
关于如何注册你的数据提供器,有两种推荐方式。第一种是将所有提供器都注册到 GatherDataEvent.Client,并使用 runClientData 任务来生成数据。第二种是将客户端提供器注册到 GatherDataEvent.Client,将服务端提供器注册到 GatherDataEvent.Server,然后分别通过运行 runClientData 和 runServerData 任务来生成对应的数据。
由于 MDK 采用了第一种方案,即设置了默认的 clientData 配置,所以下面的所有示例都将采用第一种方式,把所有提供器注册到 GatherDataEvent.Client。
所有数据提供器都实现了 DataProvider 接口,并且通常只需要重写一个方法。下面列出了 Minecraft 和 NeoForge 提供的一些值得注意 的数据生成器(相关链接会提供更多信息,例如辅助方法等):
| 类(Class) | 方法(Method) | 生成内容(Generates) | 运行端(Side) | 备注(Notes) |
|---|---|---|---|---|
ModelProvider | registerModels() | 模型、方块状态文件、客户端物品 | 客户端(Client) | |
LanguageProvider | addTranslations() | 翻译文本 | 客户端(Client) | 构造函数中还需要传入语言参数。 |
ParticleDescriptionProvider | addDescriptions() | 粒子效果定义 | 客户端(Client) | |
SoundDefinitionsProvider | registerSounds() | 声音定义 | 客户端(Client) | |
SpriteSourceProvider | gather() | 精灵图源 / 图集 | 客户端(Client) | |
AdvancementProvider | generate() | 进度(Advancements) | 服务端(Server) | 请确保使用 NeoForge 版本,而不是 Minecraft 原版。 |
LootTableProvider | generate() | 战利品表(Loot tables) | 服务端(Server) | 需要额外的方法和类才能正确工作,详见相关链接文章。 |
RecipeProvider | buildRecipes(RecipeOutput) | 合成配方 (Recipes) | 服务端(Server) | |
TagsProvider 的各种子类 | addTags(HolderLookup.Provider) | 标签(Tags) | 服务端(Server) | 存在多个专用子类,详见相关链接文章。 |
DataMapProvider | gather() | 数据映射条目(Data map entries) | 服务端(Server) | |
GlobalLootModifierProvider | start() | 全局战利品修改器(Global loot modifiers) | 服务端(Server) | |
DatapackBuiltinEntriesProvider | N/A | 数据包内置条目,例如世界生成(worldgen)和 伤害类型 | 服务端(Server) | 不需要重写方法,而是在构造函数中的 Lambda 表达式里添加条目。详见相关链接文章。 |
JsonCodecProvider (抽象类) | gather() | 带有编解码器(codec)的对象 | 双端(Both) | 可以拓展用于任何拥有 编解码器 以实现数据编码的对象。 |
所有这些提供者(providers)都遵循相同的模式。首先,你需要创建一个子类,并添加你希望生成的自定义资源。然后,在 事件处理器 中将该提供者添加到事件中。以下是使用 RecipeProvider 的示例: |
public class MyRecipeProvider extends RecipeProvider {
public MyRecipeProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider) {
super(output, lookupProvider);
}
@Override
protected void buildRecipes(RecipeOutput output) {
// 在这里注册你的配方。
}
}
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD, modid = "examplemod")
public class MyDatagenHandler {
@SubscribeEvent
public static void gatherData(GatherDataEvent.Client event) {
// 数据提供者应当首先调用 event.createDatapackRegistryObjects(...)
// 来注册它们的数据包注册表对象(datapack registry objects)。
// 这样其他提供者在生成数据时也可以使用这些对象。
// 此后,通常可以通过 event.createProvider(...) 注册提供者,
// 该方法接受一个函数,提供 PackOutput 以及可选的
// CompletableFuture<HolderLookup.Provider> 参数。
// 注册你的数据提供者。
event.createProvider(MyRecipeProvider::new);
// 在这里注册其他数据提供者。
// 如果你希望在全局包(global pack)中创建一个数据包(datapack),
// 可以调用 DataGenerator#getBuiltinDatapack。
// 之后,必须使用 PackGenerator#addProvider 方法
// 向该数据包添加任何数据提供者。
DataGenerator.PackGenerator examplePack = event.getGenerator().getBuiltinDatapack(
true, // 这里应始终为 true。
"examplemod", // 模组 ID。
"example_pack" // 数据包名称。
);
examplePack.addProvider(output -> ...);
}
}
该事件(event)为你提供了一些辅助方法和上下文信息,可供使用:
event.createDatapackRegistryObjects(...)会使用你提供的RegistrySetBuilder创建并注册一个DatapackBuiltinEntriesProvider。同时,这也会强制所有后续对查找提供者(lookup provider)的使用都包含你通过数据生成(datagen)生成的条目。event.createProvider(...)通过传入PackOutput,以及可选的CompletableFuture<HolderLookup.Provider>(作为 lambda 的一部分),来注册一个 provider(提供者)。event.createBlockAndItemTags(...)会注册一个TagsProvider<Block>和一个TagsProvider<Item>,其中TagsProvider<Item>是通过TagsProvider<Block>构造得到的。event.getGenerator()返回你用于注册 provider 的DataGenerator。event.getPackOutput()返回一个PackOutput,部分 provider 会用它来确定文件的输出位置。event.getResourceManager(PackType)返回一个ResourceManager,provider 可用它来检查已有文件是否存在。event.getLookupProvider()返回一个CompletableFuture<HolderLookup.Provider>,主要被标签(tags)和数据生成注册表(datagen registries)用来引用其他可能还不存在的元素。event.includeDev()和event.includeReports()是boolean类型的方法,用于判断特定的命令行参数(见下文)是否被启用。
命令行参数(Command Line Arguments)
数据生成器(data generator)可以接受多个命令行参数:
--mod examplemod:告知数据生成器为此 mod 运行数据生成。对于当前 mod id,NeoGradle 会自动添加此参数;如果你的项目中有多个 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 文件中的资源。- 生成器模式(generator modes)(以下均为 boolean 参数,无需额外参数):
--includeDev:是否运行开发工具。 通常 mod 不应使用。可在运行时通过GatherDataEvent#includeDev()检查。--includeReports:是否导出已注册对象的列表。可在运行时通过GatherDataEvent#includeReports()检查。--all:启用所有生成器模式。
所有参数都可以通过在你的 build.gradle 中添加如下内容,加入到运行配置(run configurations)中:
runs {
// 这里可以有其他运行配置
clientData {
programArguments.addAll '--arg1', 'value1', '--arg2', 'value2', '--all' // boolean 参数无需值
}
}
例如,为了复现默认参数,你可以指定如下内容:
runs {
// 其他运行配置写在这里
clientData {
programArguments.addAll '--mod', 'examplemod', // 插入你自己的 mod id
'--output', file('src/generated/resources').getAbsolutePath(),
'--all'
}
}
进度数据生成器
进度
方块状态文件
聊天类型
物品模型
编解码器
伤害类型
数据映射
数据映射数据生成
数据包命令
数据包注册表的数据生成
装备模型
事件
事件处理器注册
函数
GLM
GLM 数据生成
物品修改器
语言文件数据生成
生命周期
逻辑侧
战利品表数据生成
战利品表
Minecraft Wiki
Minecraft 数据包
Minecraft 资源包
模型数据生成
模型
pack.mcmeta 文件
pack.mcmeta 数据包
pack.mcmeta 资源包
粒子数据生成
粒子
物理侧
谓词
配方数据生成
配方
侧
音效数据生成
音效
标签
标签数据生成
纹理
翻译