# Forge Datagen Server

> Forge 服务端数据生成：AdvancementProvider 进度 advancement 生成（ForgeAdvancementProvider、Advancement$Builder、criteria/rewards/display）、DatapackBuiltinEntriesProvider 数据包注册表对象生成（RegistrySetBuilder、BootstapContext#register、HolderGetter 查找）、GlobalLootModifierProvider 全局战利品修改器生成（GlobalLootModifierProvider#start、LootItemCondition）、LootTableProvider 战利品表生成（LootTableSubProvider、BlockLootSubProvider、EntityLootSubProvider、LootTable$Builder、LootPool、LootPoolEntryContainer、LootItemCondition、LootItemFunction、NumberProvider）、RecipeProvider 配方生成（RecipeBuilder、ShapedRecipeBuilder、ShapelessRecipeBuilder、SimpleCookingRecipeBuilder、SingleItemRecipeBuilder、SmithingTransformRecipeBuilder、SmithingTrimRecipeBuilder、SpecialRecipeBuilder、ConditionalRecipe、IConditionBuilder）、TagsProvider 标签生成（TagAppender、add/addOptional/addTag/addOptionalTag/replace/remove、BlockTagsProvider/ItemTagsProvider/EntityTypeTagsProvider/FluidTagsProvider、IntrinsicHolderTagsProvider）、GatherDataEvent 数据收集、ExistingFileHelper 文件验证。

- Skill: `zmjjkk123-hub/forge-datagen-server` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zmjjkk123-hub/forge-datagen-server`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zmjjkk123-hub/forge-datagen-server/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ZMJJKK123-hub (https://skillmd.com/u/zmjjkk123-hub)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/zmjjkk123-hub/forge-datagen-server

---


Advancement Generation
======================

[Advancements] can be generated for a mod by constructing a new `AdvancementProvider` and providing `AdvancementSubProvider`s. Advancements can either be created and supplied manually or, for convenience, created using `Advancement$Builder`. The provider must be [added][datagen] to the `DataGenerator`.

!!! note
    Forge provides an extension for the `AdvancementProvider` called `ForgeAdvancementProvider` which integrates better for generating advancements. So, this documentation will use `ForgeAdvancementProvider` along with the sub provider interface `ForgeAdvancementProvider$AdvancementGenerator`.

```java
// On the MOD event bus
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
    event.getGenerator().addProvider(
        // Tell generator to run only when server data are generating
        event.includeServer(),
        output -> new ForgeAdvancementProvider(
          output,
          event.getLookupProvider(),
          event.getExistingFileHelper(),
          // Sub providers which generate the advancements
          List.of(subProvider1, subProvider2, /*...*/)
        )
    );
}
```

`ForgeAdvancementProvider$AdvancementGenerator`
-----------------------------------------------

A `ForgeAdvancementProvider$AdvancementGenerator` is responsible for generating advancements, containing a method which takes in a registry lookup, the writer (`Consumer<Advancement>`), and the existing file helper..

```java
// In some subclass of ForgeAdvancementProvider$AdvancementGenerator or as a lambda reference

@Override
public void generate(HolderLookup.Provider registries, Consumer<Advancement> writer, ExistingFileHelper existingFileHelper) {
  // Build advancements here
}
```

`Advancement$Builder`
---------------------

`Advancement$Builder` is a convenience implementation for creating `Advancement`s to generate. It allows the definition of the parent advancement, the display information, the rewards when the advancement has been completed, and the requirements to unlock the advancement. Only the requirements need to be specified to create an `Advancement`.

Although not required, there are a number of methods that are important to know of:

Method         | Description
:---:          | :---
`parent`       | Sets the advancement which this advancement is directly linked to. Can either specify the name of the advancement or the advancement itself if its generated by the modder.
`display`      | Sets the information to display to the chat, toast, and advancement screen.
`rewards`      | Sets the rewards obtained when this advancement is completed.
`addCriterion` | Adds a condition to the advancement.
`requirements` | Specifies if the conditions must all return true or at least one must return true. An additional overload can be used to mix-and-match those operations.

Once an `Advancement$Builder` is ready to be built, the `#save` method should be called which takes in the writer, the registry name of the advancement, and the file helper used to check whether the supplied parent exists.

```java
// In some ForgeAdvancementProvider$AdvancementGenerator#generate(registries, writer, existingFileHelper)
Advancement example = Advancement.Builder.advancement()
  .addCriterion("example_criterion", triggerInstance) // How the advancement is unlocked
  .save(writer, name, existingFileHelper); // Add data to builder
```

[advancements]: ../../resources/server/advancements.md
[datagen]: ../index.md#data-providers
[conditional]: ../../resources/server/conditional.md

---

Datapack Registry Object Generation
==================================

Datapack registry objects can be generated for a mod by constructing a new `DatapackBuiltinEntriesProvider` and providing a `RegistrySetBuilder` with the new objects to register. The provider must be [added][datagen] to the `DataGenerator`.

!!! note
    `DatapackBuiltinEntriesProvider` is a Forge extension on top of `RegistriesDatapackGenerator` which properly handles referencing existing datapack registry objects without exploding the entry. So, this documentation will use `DatapackBuiltinEntriesProvider`.

```java
// On the MOD event bus
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
    event.getGenerator().addProvider(
        // Tell generator to run only when server data are generating
        event.includeServer(),
        output -> new DatapackBuiltinEntriesProvider(
          output,
          event.getLookupProvider(),
          // The builder containing the datapack registry objects to generate
          new RegistrySetBuilder().add(/* ... */),
          // Set of mod ids to generate the datapack registry objects of
          Set.of(MOD_ID)
        )
    );
}
```

`RegistrySetBuilder`
--------------------

A `RegistrySetBuilder` is responsible for building all datapack registry objects to be used within the game. The builder can add a new entry for a registry, which can then register objects to that registry.

First, a new instance of a `RegistrySetBuilder` can be initialized by calling the constructor. Then, the `#add` method (which takes in the `ResourceKey` of the registry, a `RegistryBootstrap` consumer containing the `BootstapContext` to register the objects, and an optional `Lifecycle` argument to indicate the registry's current lifecycle status) can be called to handle a specific registry for registration.

```java
new RegistrySetBuilder()
  // Create configured features
  .add(Registries.CONFIGURED_FEATURE, bootstrap -> {
    // Register configured features here
  })
  // Create placed features
  .add(Registries.PLACED_FEATURE, bootstrap -> {
    // Register placed features here
  });
```

!!! note
    Datapack registries created through Forge can also generate their objects using this builder by also passing in the associated `ResourceKey`.

Registering with `BootstapContext`
----------------------------------

The `#register` method in the `BootstapContext` provided by the builder can be used to register objects. It takes in the `ResourceKey` representing the registry name of the object, the object to register, and an optional `Lifecycle` argument to indicate the registry object's current lifecycle status. 

```java
public static final ResourceKey<ConfiguredFeature<?, ?>> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create(
  Registries.CONFIGURED_FEATURE,
  ResourceLocation.fromNamespaceAndPath(MOD_ID, "example_configured_feature")
);

// In some constant location or argument
new RegistrySetBuilder()
  // Create configured features
  .add(Registries.CONFIGURED_FEATURE, bootstrap -> {
    // Register configured features here
    bootstrap.register(
      // The resource key for the configured feature
      EXAMPLE_CONFIGURED_FEATURE,
      new ConfiguredFeature<>(
        Feature.ORE, // Create an ore feature
        new OreConfiguration(
          List.of(), // Does nothing
          8 // in veins of at most 8
        )
      )
    );
  })
  // Create placed features
  .add(Registries.PLACED_FEATURE, bootstrap -> {
    // Register placed features here
  });
```

### Datapack Registry Object Lookup

Sometimes datapack registry objects may want to use other datapack registry objects or tags containing datapack registry objects. In those cases, you can look up another datapack registry using `BootstapContext#lookup` to get a `HolderGetter`. From there, you can get a `Holder$Reference` to the datapack registry object or a `HolderSet$Named` for the tag via `#getOrThrow` by passing in the associated key.

```java
public static final ResourceKey<ConfiguredFeature<?, ?>> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create(
  Registries.CONFIGURED_FEATURE,
  ResourceLocation.fromNamespaceAndPath(MOD_ID, "example_configured_feature")
);

public static final ResourceKey<PlacedFeature> EXAMPLE_PLACED_FEATURE = ResourceKey.create(
  Registries.PLACED_FEATURE,
  ResourceLocation.fromNamespaceAndPath(MOD_ID, "example_placed_feature")
);

// In some constant location or argument
new RegistrySetBuilder()
  // Create configured features
  .add(Registries.CONFIGURED_FEATURE, bootstrap -> {
    // Register configured features here
    bootstrap.register(
      // The resource key for the configured feature
      EXAMPLE_CONFIGURED_FEATURE,
      new ConfiguredFeature(/* ... */)
    );
  })
  // Create placed features
  .add(Registries.PLACED_FEATURE, bootstrap -> {
    // Register placed features here

    // Get configured feature registry
    HolderGetter<ConfiguredFeature<?, ?>> configured = bootstrap.lookup(Registries.CONFIGURED_FEATURE);

    bootstrap.register(
      // The resource key for the placed feature
      EXAMPLE_PLACED_FEATURE,
      new PlacedFeature(
        configured.getOrThrow(EXAMPLE_CONFIGURED_FEATURE), // Get the configured feature
        List.of() // and do nothing to the placement location
      )
    )
  });
```

[datagen]: ../index.md#data-providers

---

Global Loot Modifier Generation
===============================

[Global Loot Modifiers (GLMs)][glm] can be generated for a mod by subclassing `GlobalLootModifierProvider` and implementing `#start`. Each GLM can be added generated by calling `#add` and specifying the name of the modifier and the [modifier instance][instance] to be serialized. After implementation, the provider must be [added][datagen] to the `DataGenerator`.

```java
// On the MOD event bus
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
    event.getGenerator().addProvider(
        // Tell generator to run only when server data are generating
        event.includeServer(),
        output -> new MyGlobalLootModifierProvider(output, MOD_ID)
    );
}

// In some GlobalLootModifierProvider#start
this.add("example_modifier", new ExampleModifier(
  new LootItemCondition[] {
    WeatherCheck.weather().setRaining(true).build() // Executes when raining
  },
  "val1",
  10,
  Items.DIRT
));
```

[glm]: ../../resources/server/glm.md
[instance]: ../../resources/server/glm.md#igloballootmodifier
[datagen]: ../index.md#data-providers

---

Loot Table Generation
=====================

[Loot tables][loottable] can be generated for a mod by constructing a new `LootTableProvider` and providing `LootTableProvider$SubProviderEntry`s. The provider must be [added][datagen] to the `DataGenerator`.

```java
// On the MOD event bus
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
    event.getGenerator().addProvider(
        // Tell generator to run only when server data are generating
        event.includeServer(),
        output -> new MyLootTableProvider(
          output,
          // Specify registry names of tables that are required to generate, or can leave empty
          Collections.emptySet(),
          // Sub providers which generate the loot
          List.of(subProvider1, subProvider2, /*...*/)
        )
    );
}
```

`LootTableSubProvider`
----------------------

Each `LootTableProvider$SubProviderEntry` takes in a supplied `LootTableSubProvider`, which generates the loot table, for a given `LootContextParamSet`. The `LootTableSubProvider` contains a method which takes in the writer (`BiConsumer<ResourceLocation, LootTable.Builder>`) to generate a table.

```java
public class ExampleSubProvider implements LootTableSubProvider {

  // Used to create a factory method for the wrapping Supplier
  public ExampleSubProvider() {}

  // The method used to generate the loot tables
  @Override
  public void generate(BiConsumer<ResourceLocation, LootTable.Builder> writer) {
    // Generate loot tables here by calling writer#accept
  }
}
```

The table can then be added to `LootTableProvider#getTables` for any available `LootContextParamSet`:

```java
// In the list passed into the LootTableProvider constructor
new LootTableProvider.SubProviderEntry(
  ExampleSubProvider::new,
  // Loot table generator for the 'empty' param set
  LootContextParamSets.EMPTY
)
```

### `BlockLootSubProvider` and `EntityLootSubProvider` Subclasses

For `LootContextParamSets#BLOCK` and `#ENTITY`, there are special types (`BlockLootSubProvider` and `EntityLootSubProvider` respectively) which provide additional helper methods for creating and validating that there are loot tables.

The `BlockLootSubProvider`'s constructor takes in a list of items, which are explosion resistant to determine whether the loot table can be generated if a block is exploded, and a `FeatureFlagSet`, which determines whether the block is enabled so that a loot table is generated for it.

```java
// In some BlockLootSubProvider subclass
public MyBlockLootSubProvider() {
  super(Collections.emptySet(), FeatureFlags.REGISTRY.allFlags());
}
```

The `EntityLootSubProvider`'s constructor takes in a `FeatureFlagSet`, which determines whether the entity type is enabled so that a loot table is generated for it.

```java
// In some EntityLootSubProvider subclass
public MyEntityLootSubProvider() {
  super(FeatureFlags.REGISTRY.allFlags());
}
```

To use them, all registered objects must be supplied to either `BlockLootSubProvider#getKnownBlocks` and `EntityLootSubProvider#getKnownEntityTypes` respectively. These methods are to make sure all objects within the iterable has a loot table.

!!! tip
    If `DeferredRegister` is being used to register a mod's objects, then the `#getKnown*` methods can be supplied the entries via `DeferredRegister#getEntries`:

    ```java
    // In some BlockLootSubProvider subclass for some DeferredRegister BLOCK_REGISTRAR
    @Override
    protected Iterable<Block> getKnownBlocks() {
      return BLOCK_REGISTRAR.getEntries() // Get all registered entries
        .stream() // Stream the wrapped objects
        .flatMap(RegistryObject::stream) // Get the object if available
        ::iterator; // Create the iterable
    }
    ```

The loot tables themselves can be added by implementing the `#generate` method.

```java
// In some BlockLootSubProvider subclass
@Override
public void generate() {
  // Add loot tables here
}
```

Loot Table Builders
-------------------

To generate loot tables, they are accepted by the `LootTableSubProvider` as a `LootTable$Builder`. Afterwards, the specified `LootContextParamSet` is set in the `LootTableProvider$SubProviderEntry` and then built via `#build`. Before being built, the builder can specify entries, conditions, and modifiers which affect how the loot table functions.

!!! note
    The functionality of loot tables is so expansive that it will not be covered by this documentation in its entirety. Instead, a brief description of each component will be mentioned. The specific subtypes of each component can be found using an IDE. Their implementations will be left as an exercise to the reader.

### LootTable

Loot tables are the base object and can be transformed into the required `LootTable$Builder` using `LootTable#lootTable`. The loot table can be built with a list of pools (via `#withPool`) applied in the order they are specified along with functions (via `#apply`) to modify the resulting items of those pools.

### LootPool

Loot pools represents a group to perform operations and can generate a  `LootPool$Builder` using `LootPool#lootPool`. Each loot pool can specify the entries (via `#add`) which define the operations in the pool, the conditions (via `#when`) which define if the operations in the pool should be performed, and functions (via `#apply`) to modify the resulting items of the entries. Each pool can be executed as many times as specified (via `#setRolls`). Additionally, bonus executions can be specified (via `#setBonusRolls`) which is modified by the luck of the executor.

### LootPoolEntryContainer

Loot entries define the operations to occur when selected, typically generating items. Each entry has an associated, [registered] `LootPoolEntryType`. They also have their own associated builders which subtype `LootPoolEntryContainer$Builder`. Multiple entries can execute at the same time (via `#append`) or sequentially until one fails (via `#then`). Additionally, entries can default to another entry on failure (via `#otherwise`).

### LootItemCondition

Loot conditions define requirements which need to be met for some operation to execute. Each condition has an associated, [registered] `LootItemConditionType`. They also have their own associated builders which subtype `LootItemCondition$Builder`. By default, all loot conditions specified must return true for an operation to execute. Loot conditions can also be specified such that only one must return true instead (via `#or`). Additionally, the resulting output of a condition can be inverted (via `#invert`).

### LootItemFunction

Loot functions modify the result of an execution before passing it to the output. Each function has an associated, [registered] `LootItemFunctionType`. They also have their own associated builders which subtype `LootItemFunction$Builder`.

#### NbtProvider

NBT providers are a special type of functions defined by `CopyNbtFunction`. They define where to pull tag information from. Each provider has an associated, [registered] `LootNbtProviderType`.

### NumberProvider

Number providers determine how many times a loot pool executes. Each provider has an associated, [registered] `LootNumberProviderType`.

#### ScoreboardNameProvider

Scoreboard providers are a special type of number providers defined by `ScoreboardValue`. They define the name of the scoreboard to pull the number of rolls to execute from. Each provider has an associated, [registered] `LootScoreProviderType`.

[loottable]: ../../resources/server/loottables.md
[datagen]: ../index.md#data-providers
[registered]: ../../concepts/registries.md#registries-that-arent-forge-registries

---

Recipe Generation
=================

Recipes can be generated for a mod by subclassing `RecipeProvider` and implementing `#buildRecipes`. A recipe is supplied for data generation once a `FinishedRecipe` view is accepted by the consumer. `FinishedRecipe`s can either be created and supplied manually or, for convenience, created using a `RecipeBuilder`.

After implementation, the provider must be [added][datagen] to the `DataGenerator`.

```java
// On the MOD event bus
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
    event.getGenerator().addProvider(
        // Tell generator to run only when server data are generating
        event.includeServer(),
        MyRecipeProvider::new
    );
}
```

`RecipeBuilder`
---------------

`RecipeBuilder` is a convenience implementation for creating `FinishedRecipe`s to generate. It provides basic definitions for unlocking, grouping, saving, and getting the result of a recipe. This is done through `#unlockedBy`, `#group`, `#save`, and `#getResult` respectively.

!!! important
    [`ItemStack` outputs][stack] in recipes are not supported within vanilla recipe builders. A `FinishedRecipe` must be built in a different manner for existing vanilla recipe serializers to generate this data.

!!! warning
    The item results being generated must have a valid `RecipeCategory` specified; otherwise, a `NullPointerException` will be thrown.

All recipe builders except for [`SpecialRecipeBuilder`] require an advancement criteria to be specified. All recipes generate a criteria unlocking the recipe if the player has used the recipe previously. However, an additional criteria must be specified that allows the player to obtain the recipe without any prior knowledge. If any of the criteria specified is true, then the played will obtain the recipe for the recipe book.

!!! tip
    Recipe criteria commonly use `InventoryChangeTrigger` to unlock their recipe when certain items are present in the user's inventory.

### ShapedRecipeBuilder

`ShapedRecipeBuilder` is used to generate shaped recipes. The builder can be initialized via `#shaped`. The recipe group, input symbol pattern, symbol definition of ingredients, and the recipe unlock criteria can be specified before saving.

```java
// In RecipeProvider#buildRecipes(writer)
ShapedRecipeBuilder builder = ShapedRecipeBuilder.shaped(RecipeCategory.MISC, result)
  .pattern("a a") // Create recipe pattern
  .define('a', item) // Define what the symbol represents
  .unlockedBy("criteria", criteria) // How the recipe is unlocked
  .save(writer); // Add data to builder
```

#### Additional Validation Checks

Shaped recipes have some additional validation checks performed before building:

* A pattern must be defined and take in more than one item.
* All pattern rows must be the same width.
* A symbol cannot be defined more than once.
* The space character (`' '`) is reserved for representing no item in a slot and, as such, cannot be defined.
* A pattern must use all symbols defined by the user.

### ShapelessRecipeBuilder

`ShapelessRecipeBuilder` is used to generate shapeless recipes. The builder can be initialized via `#shapeless`. The recipe group, input ingredients, and the recipe unlock criteria can be specified before saving.

```java
// In RecipeProvider#buildRecipes(writer)
ShapelessRecipeBuilder builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, result)
  .requires(item) // Add item to the recipe
  .unlockedBy("criteria", criteria) // How the recipe is unlocked
  .save(writer); // Add data to builder
```

### SimpleCookingRecipeBuilder

`SimpleCookingRecipeBuilder` is used to generate smelting, blasting, smoking, and campfire cooking recipes. Additionally, custom cooking recipes using the `SimpleCookingSerializer` can also be data generated using this builder. The builder can be initialized via `#smelting`, `#blasting`, `#smoking`, `#campfireCooking`, or `#cooking` respectively. The recipe group and the recipe unlock criteria can be specified before saving.

```java
// In RecipeProvider#buildRecipes(writer)
SimpleCookingRecipeBuilder builder = SimpleCookingRecipeBuilder.smelting(input, RecipeCategory.MISC, result, experience, cookingTime)
  .unlockedBy("criteria", criteria) // How the recipe is unlocked 
  .save(writer); // Add data to builder
```

### SingleItemRecipeBuilder

`SingleItemRecipeBuilder` is used to generate stonecutting recipes. Additionally, custom single item recipes using a serializer like `SingleItemRecipe$Serializer` can also be data generated using this builder. The builder can be initialized via `#stonecutting` or through the constructor respectively. The recipe group and the recipe unlock criteria can be specified before saving.

```java
// In RecipeProvider#buildRecipes(writer)
SingleItemRecipeBuilder builder = SingleItemRecipeBuilder.stonecutting(input, RecipeCategory.MISC, result)
  .unlockedBy("criteria", criteria) // How the recipe is unlocked
  .save(writer); // Add data to builder
```

Non-`RecipeBuilder` Builders
----------------------------

Some recipe builders do not implement `RecipeBuilder` due to lacking features used by all previously mentioned recipes.

### SmithingTransformRecipeBuilder

`SmithingTransformRecipeBuilder` is used to generate smithing recipes which transform an item. Additionally, custom recipes using a serializer like `SmithingTransformRecipe$Serializer` can also be data generated using this builder. The builder can be initialized via `#smithing` or through the constructor respectively. The recipe unlock criteria can be specified before saving.

```java
// In RecipeProvider#buildRecipes(writer)
SmithingTransformRecipeBuilder builder = SmithingTransformRecipeBuilder.smithing(template, base, addition, RecipeCategory.MISC, result)
  .unlocks("criteria", criteria) // How the recipe is unlocked
  .save(writer, name); // Add data to builder
```

### SmithingTrimRecipeBuilder

`SmithingTrimRecipeBuilder` is used to generate smithing recipes for armor trims. Additionally, custom upgrade recipes using a serializer like `SmithingTrimRecipe$Serializer` can also be data generated using this builder. The builder can be initialized via `#smithingTrim` or through the constructor respectively. The recipe unlock criteria can be specified before saving.

```java
// In RecipeProvider#buildRecipes(writer)
SmithingTrimRecipe builder = SmithingTrimRecipe.smithingTrim(template, base, addition, RecipeCategory.MISC)
  .unlocks("criteria", criteria) // How the recipe is unlocked
  .save(writer, name); // Add data to builder
```

### SpecialRecipeBuilder

`SpecialRecipeBuilder` is used to generate empty JSONs for dynamic recipes that cannot easily be constrained to the recipe JSON format (dying armor, firework, etc.). The builder can be initialized via `#special`.

```java
// In RecipeProvider#buildRecipes(writer)
SpecialRecipeBuilder.special(dynamicRecipeSerializer)
  .save(writer, name); // Add data to builder
```

Conditional Recipes
-------------------

[Conditional recipes][conditional] can also be data generated via `ConditionalRecipe$Builder`. The builder can be obtained using `#builder`.

Conditions for each recipe can be specified by first calling `#addCondition` and then calling `#addRecipe` after all conditions have been specified. This process can be repeated as many times as the programmer would like.

After all recipes have been specified, advancements can be added for each recipe at the end using `#generateAdvancement`. Alternatively, the conditional advancement can be set using `#setAdvancement`.

```java
// In RecipeProvider#buildRecipes(writer)
ConditionalRecipe.builder()
  // Add the conditions for the recipe
  .addCondition(...)
  // Add recipe to return when conditions are true
  .addRecipe(...)

  // Add the next conditions for the next recipe
  .addCondition(...)
  // Add next recipe to return when the next conditions are true
  .addRecipe(...)

  // Create conditional advancement which uses the conditions
  // and unlocking advancement in the recipes above
  .generateAdvancement()
  .build(writer, name);
```

### IConditionBuilder

To simplify adding conditions to conditional recipes without having to construct the instances of each condition instance manually, the extended `RecipeProvider` can implement `IConditionBuilder`. The interface adds methods to easily construct condition instances.

```java
// In ConditionalRecipe$Builder#addCondition
(
  // If either 'examplemod:example_item'
  // OR 'examplemod:example_item2' exists
  // AND
  // NOT FALSE

  // Methods are defined by IConditionBuilder
  and( 
    or(
      itemExists("examplemod", "example_item"),
      itemExists("examplemod", "example_item2")
    ),
    not(
      FALSE()
    )
  )
)
```

Custom Recipe Serializers
-------------------------

Custom recipe serializers can be data generated by creating a builder that can construct a `FinishedRecipe`. The finished recipe encodes the recipe data and its unlocking advancement, when present, to JSON. Additionally, the name and serializer of the recipe is also specified to know where to write to and what can decode the object when loading. Once a `FinishedRecipe` is constructed, it simply needs to be passed to the `Consumer` supplied by `RecipeProvider#buildRecipes`.

!!! tip
    `FinishedRecipe`s are flexible enough that any object transformation can be data generated, not just items.

[datagen]: ../index.md#data-providers
[ingredients]: ../../resources/server/recipes/ingredients.md#forge-types
[stack]: ../../resources/server/recipes/index.md#recipe-itemstack-result
[conditional]: ../../resources/server/conditional.md
[special]: #specialrecipebuilder

---

Tag Generation
==============

[Tags] can be generated for a mod by subclassing `TagsProvider` and implementing `#addTags`. After implementation, the provider must be [added][datagen] to the `DataGenerator`.

```java
// On the MOD event bus
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
    event.getGenerator().addProvider(
        // Tell generator to run only when server data are generating
        event.includeServer(),
        // Extends net.minecraftforge.common.data.BlockTagsProvider
        output -> new MyBlockTagsProvider(
          output,
          event.getLookupProvider(),
          MOD_ID,
          event.getExistingFileHelper()
        )
    );
}
```

`TagsProvider`
--------------

The tags provider has two methods used for generating tags: creating a tag with objects and other tags via `#tag`, or using tags from other object types to generate the tag data via `#getOrCreateRawBuilder`.

!!! note
    Typically, a provider will not call `#getOrCreateRawBuilder` directly unless a registry contains a representation of objects from a different registry (blocks have item representations to obtain the blocks in the inventory).

When `#tag` is called, a `TagAppender` is created which acts as a chainable consumer of elements to add to the tag:

Method           | Description
:---:            | :---
`add`            | Adds an object to a tag through its resource key. 
`addOptional`    | Adds an object to a tag through its name. If the object is not present, then the object will be skipped when loading.
`addTag`         | Adds a tag to a tag through its tag key. All elements within the inner tag are now a part of the outer tag.
`addOptionalTag` | Adds a tag to a tag through its name. If the tag is not present, then the tag will be skipped when loading.
`replace`        | When `true`, all previously loaded entries added to this tag from other datapacks will be discarded. If a datapack is loaded after this one, then it will still append the entries to the tag.
`remove`         | Removes an object or tag from a tag through its name or key.

```java
// In some TagProvider#addTags
this.tag(EXAMPLE_TAG)
  .add(EXAMPLE_OBJECT) // Adds an object to the tag
  .addOptional(ResourceLocation.fromNamespaceAndPath("othermod", "other_object")) // Adds an object from another mod to the tag

this.tag(EXAMPLE_TAG_2)
  .addTag(EXAMPLE_TAG) // Adds a tag to the tag
  .remove(EXAMPLE_OBJECT) // Removes an object from this tag
```

!!! important
    If the mod's tags softly depends on another mod's tags (the other mod may or may not be present at runtime), the other mods' tags should be referenced using the optional methods.

### Existing Providers

Minecraft contains a few tag providers for certain registries that can be subclassed instead. Additionally, some providers contain additional helper methods to more easily create tags.

Registry Object Type         | Tag Provider
:---:                        | :---
`Block`                      | `BlockTagsProvider`\*
`Item`                       | `ItemTagsProvider`
`EntityType`                 | `EntityTypeTagsProvider`
`Fluid`                      | `FluidTagsProvider`
`GameEvent`                  | `GameEventTagsProvider`
`Biome`                      | `BiomeTagsProvider`
`FlatLevelGeneratorPreset`   | `FlatLevelGeneratorPresetTagsProvider`
`WorldPreset`                | `WorldPresetTagsProvider`
`Structure`                  | `StructureTagsProvider`
`PoiType`                    | `PoiTypeTagsProvider`
`BannerPattern`              | `BannerPatternTagsProvider`
`CatVariant`                 | `CatVariantTagsProvider`
`PaintingVariant`            | `PaintingVariantTagsProvider`
`Instrument`                 | `InstrumentTagsProvider`
`DamageType`                 | `DamageTypeTagsProvider`

\* `BlockTagsProvider` is a Forge added `TagsProvider`.

#### `ItemTagsProvider#copy`

Blocks have item representations to obtain them in the inventory. As such, many of the block tags can also be an item tag. To easily generate item tags to have the same entries as block tags, the `#copy` method can be used which takes in the block tag to copy from and the item tag to copy to.

```java
//In ItemTagsProvider#addTags
this.copy(EXAMPLE_BLOCK_TAG, EXAMPLE_ITEM_TAG);
```

Custom Tag Providers
--------------------

A custom tag provider can be created via a `TagsProvider` subclass which takes in the registry key to generate tags for.

```java
public RecipeTypeTagsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, ExistingFileHelper fileHelper) {
  super(output, Registries.RECIPE_TYPE, registries, MOD_ID, fileHelper);
}
```

### Intrinsic Holder Tags Providers

One special type of `TagProvider`s are `IntrinsicHolderTagsProvider`s. When creating a tag using this provider via `#tag`, the object itself can be used to add itself to the tag via `#add`. To do so, a function is provided within the constructor to turn an object into its `ResourceKey`.

```java
// Subtype of `IntrinsicHolderTagsProvider`
public AttributeTagsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, ExistingFileHelper fileHelper) {
  super(
    output,
    ForgeRegistries.Keys.ATTRIBUTES,
    registries,
    attribute -> ForgeRegistries.ATTRIBUTES.getResourceKey(attribute).get(),
    MOD_ID,
    fileHelper
  );
}
```

[tags]: ../../resources/server/tags.md
[datagen]: ../index.md#data-providers
[custom]: ../../concepts/registries.md#creating-custom-forge-registries

