标签

标签

标签是同一类型注册对象的列表,从数据文件加载,可用于成员检查。例如,木棍配方接受任意木板组合,即所有带 minecraft:planks 标签的物品。通常用 # 前缀区分标签和普通对象,例如 #minecraft:planks 与 minecraft:oak_planks。

任何注册表都可有标签。方块和物品最常见,流体、实体类型、伤害类型等也常使用。也可按需创建自己的标签。

Minecraft 注册表的标签路径为 data/<tag_namespace>/tags/<registry_path>/<tag_path>.json,其他注册表则为 data/<tag_namespace>/tags/<registry_namespace>/<registry_path>/<tag_path>.json。例如 minecraft:planks 物品标签位于 data/minecraft/tags/item/planks.json。

说明 与大多数 NeoForge 数据文件不同,其新增标签通常使用 c 命名空间,例如 c:ingots/gold,以便与 Fabric 统一,满足多加载器开发者的需求。但某些与 NeoForge 系统紧密相关的标签例外,例如许多伤害类型标签。

标签文件通常累加而非覆盖。同 ID 的多个数据包标签会合并内容,除非另有指定;这区别于大多数直接替换已有内容的数据文件。

文件格式

{
    // The values of the tag.
    "values": [
        // A value object. Must specify the id of the object to add, and whether it is required.
        // If the entry is required, but the object is not present, the tag will not load. The "required" field
        // is technically optional, but when removed, the entry is equivalent to the shorthand below.
        {
            "id": "examplemod:example_ingot",
            "required": false
        }
        // Shorthand for {"id": "minecraft:gold_ingot", "required": true}, i.e. a required entry.
        "minecraft:gold_ingot",
        // A tag object. Distinguished from regular entries by the leading #. In this case, all planks
        // will be considered entries of the tag. Like normal entries, this can also have the "id"/"required" format.
        // Warning: Circular tag dependencies will lead to a datapack not being loaded!
        "#minecraft:planks"
    ],
    // Whether to remove all pre-existing entries before adding your own (true) or just add your own (false).
    // This should generally be false, the option to set this to true is primarily aimed at pack developers.
    "replace": false,
    // A finer-grained way to remove entries from the tag again, if present. Optional, NeoForge-added.
    // Entry syntax is the same as in the "values" array.
    "remove": [
        "minecraft:iron_ingot"
    ]
}

查找和命名标签

查找现有标签时建议依次:

  1. 检查 Minecraft 的 BlockTags、ItemTags、EntityTypeTags 等。
  2. 检查 NeoForge 的 Tags.Blocks、Tags.Items、Tags.EntityTypes 等。
  3. 如果都没有,则自行创建。

创建时考虑用途:改变自己 Mod 行为的标签应放在自己的命名空间,例如决定某物可在哪种方块生成。其他 Mod 也可能使用的标签(例如新金属、宝石)应放在 c。其余默认使用自己的命名空间。

命名应使用复数,如 minecraft:planks、c:ingots。同类多个对象应分文件夹,并有一个总标签,例如 c:ingots/iron、c:ingots/gold,以及包含两者的 c:ingots。这是 NeoForge 惯例,原版多数标签并不遵循。

使用标签

在代码中使用注册表键与资源位置创建 TagKey<T>,T 为 Block、Item、EntityType<?> 等标签对象类型:

public static final TagKey<Block> MY_TAG = TagKey.create(
        // The registry key. The type of the registry must match the generic type of the tag.
        Registries.BLOCK,
        // The location of the tag. This example will put our tag at data/examplemod/tags/blocks/example_tag.json.
        ResourceLocation.fromNamespaceAndPath("examplemod", "example_tag")
);

警告 TagKey 是 record,因此构造函数公开,但不应直接调用,否则可能导致标签条目查询等问题。

随后即可操作标签。先检查对象是否属于标签。以下以方块为例,除特别说明外,其他类型功能相同:

// Check whether dirt is in our tag.
boolean isInTag = BuiltInRegistries.BLOCK.getOrCreateTag(MY_TAG).stream().anyMatch(e -> e == Items.DIRT);

因为上述写法较长,最常用的 BlockState 和 ItemStack 提供 #is 辅助方法:

// Check whether the blockState's block is in our tag.
boolean isInBlockTag = blockState.is(MY_TAG);
// Check whether the itemStack's item is in our tag. Assumes the existence of MY_ITEM_TAG as a TagKey<Item>.
boolean isInItemTag = itemStack.is(MY_ITEM_TAG);

也可以获取条目流:

Stream<Block> blocksInTag = BuiltInRegistries.BLOCK.getOrCreateTag(MY_TAG).stream();

出于性能考虑,建议在字段中缓存条目,并在标签重载时使缓存失效,可通过 TagsUpdatedEvent 监听:

public class MyTagsCacheClass {
    private static Set<Block> blocksInTag = null;

    public static Set<Block> getBlockTagContents() {
        if (blocksInTag == null) {
            // Wrap as an unmodifiable set, as we're not supposed to modify this anyway
            blocksInTag = Collections.unmodifiableSet(BuiltInRegistries.BLOCK.getOrCreateTag(MY_TAG).stream().collect(Collectors.toSet()));
        }
        return blocksInTag;
    }
    
    public static void invalidateCache() {
        blocksInTag = null;
    }
}

// In some event handler class
@SubscribeEvent // on the game event bus
public static void onTagsUpdated(TagsUpdatedEvent event) {
    MyTagsCacheClass.invalidateCache();
}

数据生成

标签也可生成。每种标签都有自己的生成基类,因此需要对应的提供器类。它们均继承 TagsProvider<T>,T 为标签类型。常用类型如下:

类型 标签提供器类
BannerPattern BannerPatternTagsProvider
Biome BiomeTagsProvider
Block BlockTagsProvider
CatVariant CatVariantTagsProvider
DamageType DamageTypeTagsProvider
Enchantment EnchantmentTagsProvider
EntityType EntityTypeTagsProvider
FlatLevelGeneratorPreset FlatLevelGeneratorPresetTagsProvider
Fluid FluidTagsProvider
GameEvent GameEventTagsProvider
Instrument InstrumentTagsProvider
Item ItemTagsProvider
PaintingVariant PaintingVariantTagsProvider
PoiType PoiTypeTagsProvider
Structure StructureTagsProvider
WorldPreset WorldPresetTagsProvider

IntrinsicHolderTagsProvider<T> 是 TagsProvider<T> 的子类,也是 BlockTagsProvider、ItemTagsProvider、FluidTagsProvider、EntityTypeTagsProvider 与 GameEventTagsProvider 的共同父类。为简便,下面称它们为内在提供器;它们有额外生成功能。

以方块标签为例,其他类型的用法相同:

public class MyBlockTagsProvider extends BlockTagsProvider {
    // Get parameters from GatherDataEvent.
    public MyBlockTagsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, ExistingFileHelper existingFileHelper) {
        super(output, lookupProvider, ExampleMod.MOD_ID, existingFileHelper);
    }

    // Add your tag entries here.
    @Override
    protected void addTags(HolderLookup.Provider lookupProvider) {
        // Create a tag builder for our tag. This could also be e.g. a vanilla or NeoForge tag.
        tag(MY_TAG)
                // Add entries. This is a vararg parameter.
                // Non-intrinsic providers must provide ResourceKeys here instead of the actual objects.
                .add(Blocks.DIRT, Blocks.COBBLESTONE)
                // Add optional entries that will be ignored if absent. This example uses Botania's Pure Daisy.
                // Unlike #add, this is not a vararg parameter.
                .addOptional(ResourceLocation.fromNamespaceAndPath("botania", "pure_daisy"))
                // Add a tag entry.
                .addTag(BlockTags.PLANKS)
                // Add multiple tag entries. This is a vararg parameter.
                // Can cause unchecked warnings that can safely be suppressed.
                .addTags(BlockTags.LOGS, BlockTags.WOODEN_SLABS)
                // Add an optional tag entry that will be ignored if absent.
                .addOptionalTag(ResourceLocation.fromNamespaceAndPath("c", "ingots/tin"))
                // Add multiple optional tag entries. This is a vararg parameter.
                // Can cause unchecked warnings that can safely be suppressed.
                .addOptionalTags(ResourceLocation.fromNamespaceAndPath("c", "nuggets/tin"), ResourceLocation.fromNamespaceAndPath("c", "storage_blocks/tin"))
                // Set the replace property to true.
                .replace()
                // Set the replace property back to false.
                .replace(false)
                // Remove entries. This is a vararg parameter. Accepts either resource locations, resource keys,
                // tag keys, or (intrinsic providers only) direct values.
                // Can cause unchecked warnings that can safely be suppressed.
                .remove(ResourceLocation.fromNamespaceAndPath("minecraft", "crimson_slab"), ResourceLocation.fromNamespaceAndPath("minecraft", "warped_slab"));
    }
}

生成的 JSON 如下:

{
    "values": [
        "minecraft:dirt",
        "minecraft:cobblestone",
        {
            "id": "botania:pure_daisy",
            "required": false
        },
        "#minecraft:planks",
        "#minecraft:logs",
        "#minecraft:wooden_slabs",
        {
            "id": "c:ingots/tin",
            "required": false
        },
        {
            "id": "c:nuggets/tin",
            "required": false
        },
        {
            "id": "c:storage_blocks/tin",
            "required": false
        }
    ],
    "remove": [
        "minecraft:crimson_slab",
        "minecraft:warped_slab"
    ]
}

与其他提供器一样,将每个标签提供器加入 GatherDataEvent:

@SubscribeEvent // on the mod event bus
public static void gatherData(GatherDataEvent event) {
    PackOutput output = generator.getPackOutput();
    CompletableFuture<HolderLookup.Provider> lookupProvider = event.getLookupProvider();
    ExistingFileHelper existingFileHelper = event.getExistingFileHelper();

    // other providers here
    event.getGenerator().addProvider(
        event.includeServer(),
        new MyBlockTagsProvider(output, lookupProvider, existingFileHelper)
    );
}

ItemTagsProvider 另有 #copy 辅助方法,用于常见的物品标签与方块标签镜像需求:

// In an ItemTagsProvider's #addTags method, assuming types TagKey<Block> and TagKey<Item> for the two parameters.
copy(EXAMPLE_BLOCK_TAG, EXAMPLE_ITEM_TAG);

自定义标签提供器

自定义注册表,或默认没有提供器的原版/NeoForge 注册表,也可以定义提供器,例如配方类型标签:

public class MyRecipeTypeTagsProvider extends TagsProvider<RecipeType<?>> {
    // Get parameters from GatherDataEvent.
    public MyRecipeTypeTagsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, ExistingFileHelper existingFileHelper) {
        // Second parameter is the registry key we are generating the tags for.
        super(output, Registries.RECIPE_TYPE, lookupProvider, ExampleMod.MOD_ID, existingFileHelper);
    }
    
    @Override
    protected void addTags(HolderLookup.Provider lookupProvider) { /*...*/ }
}

适用时也可继承 IntrinsicHolderTagsProvider<T>,直接传对象而非仅资源键。这需要额外提供一个将对象转换为资源键的函数,例如属性标签:

public class MyAttributeTagsProvider extends TagsProvider<Attribute> {
    // Get parameters from GatherDataEvent.
    public MyAttributeTagsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, ExistingFileHelper existingFileHelper) {
        super(output,
                Registries.ATTRIBUTE,
                lookupProvider,
                // A function that, given an Attribute, returns a ResourceKey<Attribute>.
                attribute -> BuiltInRegistries.ATTRIBUTE.getResourceKey(attribute).orElseThrow(),
                ExampleMod.MOD_ID,
                existingFileHelper);
    }

    // Attributes can now be used here directly, instead of just their resource keys.
    @Override
    protected void addTags(HolderLookup.Provider lookupProvider) { /*...*/ }
}

说明 TagsProvider 还公开 #getOrCreateRawBuilder,返回 TagBuilder,可向标签添加原始 ResourceLocation。TagsProvider#tag 返回的 TagAppender<T> 只是其包装。


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