资源(Resources)
资源(Resources)是游戏使用的外部文件,但不是代码。最突出的资源类型是纹理,然而,Minecraft生态系统中存在许多其他类型的资源。当然,所有这些资源都需要代码端的消费者,因此消费系统也分组在本节中。
Minecraft通常有两种资源:用于逻辑客户端(logical client)的资源,称为资产(assets) ,以及用于逻辑服务器(logical server)的资源,称为数据(data)。资产主要是仅显示信息,例如纹理、显示模型、翻译或声音,而数据包括影响游戏玩法的各种内容,例如战利品表、配方或世界生成信息。它们分别从资源包和数据包加载。NeoForge为每个模组生成内置的资源包和数据包。
资源包和数据包通常都需要一个pack.mcmeta文件;然而,现代NeoForge在运行时为你生成这些文件,所以你不需要担心它。
如果你对某些内容的格式感到困惑,请查看原版资源。你的NeoForge开发环境不仅包含原版代码,还包含原版资源。它们可以在外部资源部分(IntelliJ)/项目库部分(Eclipse)中找到,名称为ng_dummy_ng.net.minecraft:client:client-extra:<minecraft_version>(用于Minecraft资源)或ng_dummy_ng.net.neoforged:neoforge:<neoforge_version>(用于NeoForge资源)。
资产
另请参见:Minecraft Wiki上的资源包(Resource Packs)
资产(Assets),或客户端资源,是所有仅与客户端(client)相关的资源。它们从资源包加载,有时也称为旧术语纹理包(源于旧版本,当时它们只能影响纹理)。资源包基本上是一个assets文件夹。assets文件夹包含资源包包含的各种命名空间的子文件夹;每个命名空间都是一个子文件夹。例如,一个模组ID为coolmod的资源包可能包含一 个coolmod命名空间,但可能还包括其他命名空间,例如minecraft。
NeoForge自动将所有模组资源包收集到Mod resources包中,该包位于资源包菜单的"已选包"侧底部。目前无法禁用Mod resources包。然而,位于Mod resources包上方的资源包会覆盖下方资源包中定义的资源。这种机制允许资源包制作者覆盖你的模组资源,也允许模组开发者在需要时覆盖Minecraft资源。
资源包可能包含影响以下内容的文件夹:
| 文件夹名称 | 内容 |
|---|---|
atlases | 纹理图集源 |
blockstates | 方块状态文件(Blockstate Files) |
equipment | 装备信息(Equipment Info) |
font | 字体定义 |
items | 客户端物品(Client Items) |
lang | 翻译文件(Translation Files) |
models | 模型(Models) |
particles | 粒子定义(Particle Definitions) |
post_effect | 后期处理屏幕效果 |
shaders | 元数据、片段和顶点着色器 |
sounds | 声音文件(Sound Files) |
texts | 杂项文本文件 |
textures | 纹理(Textures) |
数据
另请参见:Minecraft Wiki上的数据包(Data Packs)
与资产相反,数据(data)是所有服务器(server)资源的术语。类似于资源包,数据通过数据包(或datapacks)加载。像资源包一样,数据包由一个pack.mcmeta文件和一个名为data的根文件夹组成。然后,再次像资源包一样,data文件夹包含数据包包含的各种命名空间的子文件夹;每个命名空间都是一个子文件夹。例如,一个模组ID为coolmod的数据包可能包含一个coolmod命名空间,但可能还包括其他命名空间,例如minecraft。
NeoForge在创建新世界时自动应用所有模组数据包。目前无法禁用模组数据包。然而,大多数数据文件可以被具有更高优先级的数据包覆盖(从而通过用空文件替换它们来移除)。可以通过将额外的数据包放置在世界datapacks子文件夹中,然后通过/datapack命令启用或禁用来启用或禁用它们。
目前没有内置方法将一组自定义数据包应用于每个世界。然而,有许多模组实现了这一点。
数据包可能包含影响以下内容的文件夹:
| 文件夹名称 | 内容 |
|---|---|
advancement | 进度 |
banner_pattern | 旗帜图案 |
cat_variant, chicken_variant, cow_variant, frog_variant, pig_variant, wolf_variant | 实体变种 |
damage_type | 伤害类型 |
enchantment, enchantment_provider | 附魔 |
instrument, jukebox_song, wolf_sound_variant | 声音参考元数据 |
painting_variant | 画作 |
loot_table | 战利品表 |
recipe | 配方 |
tags | 标签 |
test_environment, test_instance | 游戏测试 |
trial_spawner | 战斗挑战 |
trim_material, trim_pattern | 盔甲纹饰 |
neoforge/data_maps | 数据映射 |
neoforge/loot_modifiers | 全局战利品修改器 |
dimension, dimension_type, structure, worldgen, neoforge/biome_modifier | 世界生成文件 |
此外,它们还可能包含一些与命令集成的系统的子文件夹。这些系统很少与模组一起使用,但无论如何都值得一提:
| 文件夹名称 | 内容 |
|---|---|
chat_type | 聊天类型 |
function | 函数 |
item_modifier | 物品修改器 |
predicate | 谓词 |
pack.mcmeta
另请参见:Minecraft Wiki上的pack.mcmeta(资源包)和pack.mcmeta(数据包)
pack.mcmeta 文件保存资源包或数据包的元数据。对于模组,NeoForge 使此文件过时,因为 pack.mcmeta 是合成生成的。如果您仍然需要 pack.mcmeta 文件,完整的规范可以在链接的 Minecraft Wiki 文章中找到。
数据生成(Data Generation)
数据生成,俗称 datagen,是一种以编程方式生成 JSON 资源文件的方法,以避免手动编写它们的繁琐且容易出错的过程。这个名称有点误导性,因为它既适用于资产也适用于数据。
Datagen 通过 Data 运行配置运行,该配置与 Client 和 Server 运行配置一起为您生成。数据运行配置遵循模组生命周期,直到注册表事件触发之后。然后它触发一个 GatherDataEvent,您可以在其中以数据提供者的形式注册要生成的对象,将所述对象写入磁盘,并结束该过程。
有两种子类型在物理侧上运行:GatherDataEvent.Client 和 GatherDataEvent.Server。GatherDataEvent.Client 可能包含所有要生成的提供者。另一方面,GatherDataEvent.Server 可能只包含用于生成数据包条目的提供者。
关于如何注册您的提供者有两个建议。前者是在 GatherDataEvent.Client 中注册所有提供者,并使用 runClientData 任务生成数据。后者是将客户端提供者注册到 GatherDataEvent.Client,将服务器提供者注册到 GatherDataEvent.Server,分别通过运行 runClientData 和 runServerData 任务来生成它们。
由于 MDK 通过设置默认的 clientData 配置使用前一种解决方案,所有显示的示例都将使用前一种方法,将所有提供者注册到 GatherDataEvent.Client。
所有数据提供者都扩展 DataProvider 接口,通常需要重写一个方法。以下是 Minecraft 和 NeoForge 提供的重要数据生成器列表(链接的文章提供了更多信息,例如辅助方法):
| 类 | 方法 | 生成内容 | 侧 | 备注 |
|---|---|---|---|---|
ModelProvider | 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 | N/A | 数据包内置条目,例如世界生成和伤害类型 | 服务器 | 没有方法重写,而是在构造函数的 lambda 中添加条目。有关详细信息,请参阅链接的文章。 |
JsonCodecProvider(抽象类) | gather() | 具有编解码器的对象 | 两者 | 可以扩展以用于任何具有编解码器来编码数据的对象。 |
所有这些提供者都遵循相同的模式。首先,您创建一个子类并添加要生成的自己的资源。然后,您将提供者添加到事件处理程序中的事件。使用 RecipeProvider 的示例:
public class MyRecipeProvider extends RecipeProvider {
public MyRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) {
super(registries, output);
}
@Override
protected void buildRecipes() {
// 在此处注册您的配方。
}
// 数据提供者类
public static class Runner extends RecipeProvider.Runner {
public Runner(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries);
}
@Override
protected abstract RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) {
return new MyRecipeProvider(registries, output);
}
}
}
// 在某个事件处理程序类中
@SubscribeEvent // 在模组事件总线上
public static void gatherData(GatherDataEvent.Client event) {
// 数据提供者应首先调用 event.createDatapackRegistryObjects(...)
// 来注册它们的数据包注册表对象。这允许其他提供者
// 在它们自己的数据生成期间使用这些对象。
// 从那里,提供者通常可以使用 event.createProvider(...) 注册,
// 它充当一个函数,提供 PackOutput 和可选的
// CompletableFuture<HolderLookup.Provider>。
// 注册提供者。
event.createProvider(MyRecipeProvider.Runner::new);
// 其他数据提供者在此处。
// 如果要在全局包内创建数据包,可以调用
// DataGenerator#getBuiltinDatapack。从那里,您必须使用
// PackGenerator#addProvider 方法将任何提供者添加到该包。
DataGenerator.PackGenerator examplePack = event.getGenerator().getBuiltinDatapack(
true, // 应始终为 true。
"examplemod", // 模组 ID。
"example_pack" // 包的名称。
);
examplePack.addProvider(output -> ...);
}
事件提供了一些辅助工具和上下文供您使用:
event.createDatapackRegistryObjects(...)使用提供的RegistrySetBuilder创建并注册一个DatapackBuiltinEntriesProvider。它还强制任何将来使用的查找提供者包含您的数据生成条目。event.createProvider(...)通过提供PackOutput和可选的CompletableFuture<HolderLookup.Provider>作为 lambda 的一部分来注册提供者。event.createBlockAndItemTags(...)通过使用TagsProvider<Block>构造TagsProvider<Item>来注册TagsProvider<Block>和TagsProvider<Item>。event.getGenerator()返回您注册提供者的DataGenerator。event.getPackOutput()返回一个PackOutput,某些提供者使用它来确定其文件输出位置。event.getResourceManager(PackType)返回一个ResourceManager,提供者可以使用它来检查已存在的文件。event.getLookupProvider()返回一个CompletableFuture<HolderLookup.Provider>,主要由标签和数据生成注册表用于引用其他可能尚未存在的元素。event.includeDev()和event.includeReports()是boolean方法,允许您检查是否启用了特定的命令行参数(见下文)。
命令行参数(Command Line Arguments)
数据生成器可以接受几个命令行参数:
--mod examplemod:告诉数据生成器为此模组运行 datagen。由 NeoGradle 自动为所属模组 ID 添加,如果您在一个项目中有多个模组,请添加此参数。--output path/to/folder:告诉数据生成器输出到给定文件夹。建议使用 Gradle 的file(...).getAbsolutePath()为您生成绝对路径(相对于项目根目录的路径)。默认为file('src/generated/resources').getAbsolutePath()。--existing path/to/folder:告诉数据生成器在检查现有文件时考虑给定文件夹。与输出一样,建议使用 Gradle 的file(...).getAbsolutePath()。--existing-mod examplemod:告诉数据生成器在检查现有文件时考虑给定模组 JAR 文件中的资源。- 生成器模式(所有这些都是布尔参数,不需要任何额外参数):
--includeDev:是否运行开发工具。通常不应由模组使用。在运行时使用GatherDataEvent#includeDev()检查。--includeReports:是否转储注册对象列表。在运行时使用GatherDataEvent#includeReports()检查。--all:启用所有生成器模式。
所有参数都可以通过将以下内容添加到您的 build.gradle 来添加到运行配置中:
runs {
// 其他运行配置在此处
clientData {
arguments.addAll '--arg1', 'value1', '--arg2', 'value2', '--all' // 布尔参数没有值
}
}
例如,要复制默认参数,您可以指定以下内容:
runs {
// 其他运行配置在此处
clientData {
arguments.addAll '--mod', 'examplemod', // 插入您自己的模组 ID
'--output', file('src/generated/resources').getAbsolutePath(),
'--all'
}
}