# Forge Resources Server

> Forge 服务端数据：Advancements 进度系统（JSON 数据驱动、CriteriaTriggers 触发器、SimpleCriterionTrigger 自定义触发器、AbstractCriterionTriggerInstance 条件实例、ContextAwarePredicate 玩家条件、#serializeToJson 序列化、FMLCommonSetupEvent#enqueueWork 注册、Advancement Rewards 奖励 experience/loot/recipes/function）、Conditional Loading 条件加载（ICondition、IConditionSerializer、forge:true/false/not/and/or/mod_loaded/item_exists/tag_empty 条件、ConditionalRecipe$Builder/ConditionalAdvancement$Builder）、Global Loot Modifiers 全局战利品修改器（global_loot_modifiers.json、IGlobalLootModifier、LootModifier 子类、Codec<T> 编解码、LootModifier#codecStart、LootItemCondition 条件、堆叠机制）、Datapacks 数据包（data 目录、资源包创建）、Loot Tables 战利品表（LootTable、LootParams、LootContext、LootContextParamSet、LootTableLoadEvent、LootPool 命名、LootingLevelEvent、LootContextParams#KILLER_ENTITY、SmeltItemFunction 多物品熔炼、LootItemCondition forge:loot_table_id/forge:can_tool_perform_action）、Tags 标签（TagKey、ITagManager、IForgeRegistry#tags、Holder#is、forge:/c: 命名空间、remove 数组、replace 机制）、Custom Recipes 自定义配方（Recipe 接口、RecipeType 注册、Recipe

- Skill: `zmjjkk123-hub/forge-resources-server` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zmjjkk123-hub/forge-resources-server`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zmjjkk123-hub/forge-resources-server/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- 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-resources-server

---


Advancements
============

Advancements are tasks that can be achieved by the player which may advance the progress of the game. Advancements can trigger based on any action the player may be directly involved in.

All advancement implementations within vanilla are data driven via JSON. This means that a mod is not necessary to create a new advancement, only a [data pack][datapack]. A full list on how to create and put these advancements within the mod's `resources` can be found on the [Minecraft Wiki][wiki]. Additionally, advancements can be [loaded conditionally and defaulted][conditional] depending on what information is present (mod loaded, item exists, etc.).

Advancement Criteria
--------------------

To unlock an advancement, the specified criteria must be met. Criteria are tracked through triggers which execute when a certain action is performed: killing an entity, changing an inventory, breading animals, etc. Any time an advancement is loaded into the game, the criteria defined are read and added as listeners to the trigger. Afterwards a trigger function is called (usually named `#trigger`) which checks all listeners as to whether the current state meets the conditions of the advancement criteria. The criteria listeners for the advancement are only removed once the advancement has been obtained by completing all requirements.

Requirements are defined as an array of string arrays representing the name of the criteria specified on the advancement. An advancement is completed once one string array of criteria has been met:

```js
// In some advancement JSON

// List of defined criteria to meet
"criteria": {
  "example_criterion1": { /*...*/ },
  "example_criterion2": { /*...*/ },
  "example_criterion3": { /*...*/ },
  "example_criterion4": { /*...*/ }
},

// This advancement is only unlocked once
// - Criteria 1 AND 2 have been met
// OR
// - Criteria 3 and 4 have been met
"requirements": [
  [
    "example_criterion1",
    "example_criterion2"
  ],
  [
    "example_criterion3",
    "example_criterion4"
  ]
]
```

A list of criteria triggers defined by vanilla can be found in `CriteriaTriggers`. Additionally, the JSON formats are defined on the [Minecraft Wiki][triggers].

### Custom Criteria Triggers

Custom criteria triggers can be created by implementing `SimpleCriterionTrigger` for the created `AbstractCriterionTriggerInstance` subclass.

### AbstractCriterionTriggerInstance Subclass

The `AbstractCriterionTriggerInstance` represents a single criteria defined in the `criteria` object. Trigger instances are responsible for holding the defined conditions, returning whether the inputs match the condition, and writing the instance to JSON for data generation.

Conditions are usually passed in through the constructor. The `AbstractCriterionTriggerInstance` super constructor requires the instance to define the registry name of the trigger and the conditions the player must meet as an `ContextAwarePredicate`. The registry name of the trigger should be supplied to the super directly while the conditions of the player should be a constructor parameter.

```java
// Where ID is the registry name of the trigger
public ExampleTriggerInstance(ContextAwarePredicate player, ItemPredicate item) {
  super(ID, player);
  // Store the item condition that must be met
}
```

!!! note
    Typically, trigger instances have a static constructor which allow these instances to be easily created for data generation. These static factory methods can also be statically imported instead of the class itself.

    ```java
    public static ExampleTriggerInstance instance(ContextAwarePredicate player, ItemPredicate item) {
      return new ExampleTriggerInstance(player, item);
    }
    ```

Additionally, the `#serializeToJson` method should be overridden. The method should add the conditions of the instance to the other JSON data.

```java
@Override
public JsonObject serializeToJson(SerializationContext context) {
  JsonObject obj = super.serializeToJson(context);
  // Write conditions to json
  return obj;
}
```

Finally, a method should be added which takes in the current data state and returns whether the user has met the necessary conditions. The conditions of the player are already checked through `SimpleCriterionTrigger#trigger(ServerPlayer, Predicate)`. Most trigger instances call this method `#matches`.

```java
// This method is unique for each instance and is as such not overridden
public boolean matches(ItemStack stack) {
  // Since ItemPredicate matches a stack, a stack is the input
  return this.item.matches(stack);
}
```

### SimpleCriterionTrigger

The `SimpleCriterionTrigger<T>` subclass, where `T` is the type of the trigger instance, is responsible for specifying the registry name of the trigger, creating a trigger instance, and a method to check trigger instances and run attached listeners on success.

The registry name of the trigger is supplied to `#getId`. This should match the registry name supplied to the trigger instance.

A trigger instance is created via `#createInstance`. This method reads a criteria from JSON.

```java
@Override
public ExampleTriggerInstance createInstance(JsonObject json, ContextAwarePredicate player, DeserializationContext context) {
  // Read conditions from JSON: item
  return new ExampleTriggerInstance(player, item);
}
```

Finally, a method is defined to check all trigger instances and run the listeners if their condition is met. This method takes in the `ServerPlayer` and whatever other data defined by the matching method in the `AbstractCriterionTriggerInstance` subclass. This method should internally call `SimpleCriterionTrigger#trigger` to properly handle checking all listeners. Most trigger instances call this method `#trigger`.

```java
// This method is unique for each trigger and is as such not overridden
public void trigger(ServerPlayer player, ItemStack stack) {
  this.trigger(player,
    // The condition checker method within the AbstractCriterionTriggerInstance subclass
    triggerInstance -> triggerInstance.matches(stack)
  );
}
```

Afterwards, an instance should be registered using `CriteriaTriggers#register` during `FMLCommonSetupEvent`.

!!! important
    `CriteriaTriggers#register` must be enqueued to the synchronous work queue via `FMLCommonSetupEvent#enqueueWork` as the method is not thread-safe.

### Calling the Trigger

Whenever the action being checked is performed, the `#trigger` method defined by the `SimpleCriterionTrigger` subclass should be called.

```java
// In some piece of code where the action is being performed
// Where EXAMPLE_CRITERIA_TRIGGER is the custom criteria trigger
public void performExampleAction(ServerPlayer player, ItemStack stack) {
  // Run code to perform action
  EXAMPLE_CRITERIA_TRIGGER.trigger(player, stack);
}
```

Advancement Rewards
-------------------

When an advancement is completed, rewards may be given out. These can be a combination of experience points, loot tables, recipes for the recipe book, or a [function] executed as a creative player.

```js
// In some advancement JSON
"rewards": {
  "experience": 10,
  "loot": [
    "minecraft:example_loot_table",
    "minecraft:example_loot_table2"
    // ...
  ],
  "recipes": [
    "minecraft:example_recipe",
    "minecraft:example_recipe2"
    // ...
  ],
  "function": "minecraft:example_function"
}
```

[datapack]: https://minecraft.wiki/w/Data_pack
[wiki]: https://minecraft.wiki/w/Advancement/JSON_format
[conditional]: ./conditional.md#implementations
[function]: https://minecraft.wiki/w/Function_(Java_Edition)
[triggers]: https://minecraft.wiki/w/Advancement/JSON_format#List_of_triggers

---

Conditionally-Loaded Data
=========================

There are times when modders may want to include data-driven objects using information from another mod without having to explicitly make that mod a dependency. Other cases may be to swap out certain objects with other modded entries when they are present. This can be done through the conditional subsystem.

Implementations
---------------

Currently, conditional loading is implemented for recipes and advancements. For any conditional recipe or advancement, a list of conditions to datum pair is loaded. If the conditions specified for a datum in the list is true, then that datum is returned. Otherwise, the datum is discarded.

```js
{
  // The type needs to be specified for recipes as they can have custom serializers
  // Advancements do not need this type
  "type": "forge:conditional",

  "recipes": [ // Or 'advancements' for Advancements
    {
      // The conditions to check
      "conditions": [
        // Conditions in the list are ANDed together
        {
          // Condition 1
        },
        {
          // Condition 2
        }
      ],
      "recipe": { // Or 'advancement' for Advancements
        // The recipe to use if all conditions succeed
      }
    },
    {
      // Next condition to check if the previous fails
    },
  ]
}
```

Conditionally-loaded data additionally have wrappers for [data generation][datagen] through `ConditionalRecipe$Builder` and `ConditionalAdvancement$Builder`.

Conditions
----------

Conditions are specified by setting `type` to the name of the condition as specified by [`IConditionSerializer#getID`][serializer].

### True and False

Boolean conditions consist of no data and return the expected value of the condition. They are represented by `forge:true` and `forge:false`.

```js
// For some condition
{
  // Will always return true (or false for 'forge:false')
  "type": "forge:true"
}
```

### Not, And, and Or

Boolean operator conditions consist of the condition(s) being operated upon and apply the following logic. They are represented by `forge:not`, `forge:and`, and `forge:or`.

```js
// For some condition
{
  // Inverts the result of the stored condition
  "type": "forge:not",
  "value": {
    // A condition
  }
}
```

```js
// For some condition
{
  // ANDs the stored conditions together (or ORs for 'forge:or')
  "type": "forge:and",
  "values": [
    {
      // First condition
    },
    {
      // Second condition to be ANDed (or ORed for 'forge:or')
    }
  ]
}
```

### Mod Loaded

`ModLoadedCondition` returns true whenever the specified mod with the given id is loaded in the current application. This is represented by `forge:mod_loaded`.

```js
// For some condition
{
  "type": "forge:mod_loaded",
   // Returns true if 'examplemod' is loaded
  "modid": "examplemod"
}
```

### Item Exists

`ItemExistsCondition` returns true whenever the given item has been registered in the current application. This is represented by `forge:item_exists`.

```js
// For some condition
{
  "type": "forge:item_exists",
   // Returns true if 'examplemod:example_item' has been registered
  "item": "examplemod:example_item"
}
```

### Tag Empty

`TagEmptyCondition` returns true whenever the given item tag has no items within it. This is represented by `forge:tag_empty`.

```js
// For some condition
{
  "type": "forge:tag_empty",
   // Returns true if 'examplemod:example_tag' is an item tag with no entries
  "tag": "examplemod:example_tag"
}
```

Creating Custom Conditions
--------------------------

Custom conditions can be created by implementing `ICondition` and its associated `IConditionSerializer`.

### ICondition

Any condition only need to implement two methods:

Method | Description
:---:  | :---
getID  | The registry name of the condition. Must be equivalent to [`IConditionSerializer#getID`][serializer]. Used only for [data generation][datagen].
test   | Returns true if the condition has been satisfied.

!!! note
    Every `#test` has access to some `IContext` representing the state of the game. Currently, only tags can be obtained from a registry.

### IConditionSerializer

Serializers need to implement three methods:

Method | Description
:---:  | :---
getID  | The registry name of the condition. Must be equivalent to [`ICondition#getID`][condition].
read   | Reads the condition data from JSON.
write  | Writes the given condition data to JSON.

!!! note
    Condition serializers are not responsible for writing or reading the type of the serializer, similar to other serializer implementations in Minecraft.

Afterwards, a static instance should be declared to hold the initialized serializer and then registered using `CraftingHelper#register` either during the `RegisterEvent` for `RecipeSerializer`s or during `FMLCommonSetupEvent`.

```java
// In some serializer class
public static final ExampleConditionSerializer INSTANCE = new ExampleConditionSerializer();

// In some handler class
public void registerSerializers(RegisterEvent event) {
  event.register(ForgeRegistries.Keys.RECIPE_SERIALIZERS,
    helper -> CraftingHelper.register(INSTANCE)
  );
}
```

!!! important
    If using `FMLCommonSetupEvent` to register a condition serializer, it must be enqueued to the synchronous work queue via `FMLCommonSetupEvent#enqueueWork` as `CraftingHelper#register` is not thread-safe.

[datagen]: ../../datagen/server/recipes.md
[serializer]: #iconditionserializer
[condition]: #icondition

---

Global Loot Modifiers
===========

Global Loot Modifiers are a data-driven method of handling modification of harvested drops without the need to overwrite dozens to hundreds of vanilla loot tables or to handle effects that would require interactions with another mod's loot tables without knowing what mods may be loaded. Global Loot Modifiers are also stacking, rather than last-load-wins, similar to tags.

Registering a Global Loot Modifier
-------------------------------

You will need 4 things:

1. Create a `global_loot_modifiers.json`.
    * This will tell Forge about your modifiers and works similar to [tags].
2. A serialized json representing your modifier.
    * This will contain all of the data about your modification and allows data packs to tweak your effect.
3. A class that extends `IGlobalLootModifier`.
    * The operational code that makes your modifier work. Most modders can extend `LootModifier` as it supplies base functionality.
4. Finally, a codec to encode and decode your operational class.
    * This is [registered] as any other `IForgeRegistryEntry`.

The `global_loot_modifiers.json`
-------------------------------

The `global_loot_modifiers.json` represents all loot modifiers to be loaded into the game. This file **MUST** be placed within `data/forge/loot_modifiers/global_loot_modifiers.json`.

!!! important
    `global_loot_modifiers.json` will only be read in the `forge` namespace. The file will be neglected if it is under the mod's namespace.

`entries` is an *ordered list* of the modifiers that will be loaded. The [ResourceLocation][resloc]s specified points to their associated entry within `data/<namespace>/loot_modifiers/<path>.json`. This is primarily relevant to data pack makers for resolving conflicts between modifiers from separate mods.

`replace`, when `true`, changes the behavior from appending loot modifiers to the global list to replacing the global list entries entirely. Modders will want to use `false` for compatibility with other mod implementations. Datapack makers may want to specify their overrides with `true`.

```js
{
  "replace": false, // Must be present
  "entries": [
    // Represents a loot modifier in 'data/examplemod/loot_modifiers/example_glm.json'
    "examplemod:example_glm",
    "examplemod:example_glm2"
    // ...
  ]
}
```

The Serialized JSON
-------------------------------

This file contains all of the potential variables related to your modifier, including the conditions that must be met prior to modifying any loot. Avoid hard-coded values wherever possible so that data pack makers can adjust balance if they wish to.

`type` represents the registry name of the [codec] used to read the associated JSON file. This must always be present.

`conditions` should represent the loot table conditions for this modifier to activate. Conditions should avoid being hardcoded to allow datapack creators as much flexibility to adjust the criteria. This must also be always present.

!!! important
    Although `conditions` should represent what is needed for the modifier to activate, this is only the case if using the bundled Forge classes. If using `LootModifier` as a subclass, all conditions will be **ANDed** together and checked to see if the modifier should be applied.

Any additional properties read by the serializer and defined by the modifier can also be specified.

```js
// Within data/examplemod/loot_modifiers/example_glm.json
{
  "type": "examplemod:example_loot_modifier",
  "conditions": [
    // Normal loot table conditions
    // ...
  ],
  "prop1": "val1",
  "prop2": 10,
  "prop3": "minecraft:dirt"
}
```

`IGlobalLootModifier`
---------------------

To supply the functionality a global loot modifier specifies, a `IGlobalLootModifier` implementation must be specified. These are instances generated each time a serializer decodes the information from JSON and supplies it into this object.

There are two methods that needs to be defined in order to create a new modifier: `#apply` and `#codec`. `#apply` takes in the current loot that will be generated along with the context information such as the currently level or additional defined parameters. It returns the list of drops to generate.

!!! note
    The returned list of drops from any one modifier is fed into other modifiers in the order they are registered. As such, modified loot can be modified by another loot modifier.

`#codec` returns the registered [codec] used to encode and decode the modifier to/from JSON.

### The `LootModifier` Subclass

`LootModifier` is an abstract implementation of `IGlobalLootModifier` to provide the base functionality which most modders can easily extend and implement. This expands upon the existing interface by defining the `#apply` method to check the conditions to determine whether or not to modify the generated loot.

There are two things of note within the subclass implementation: the constructor which must take in an array of `LootItemCondition`s and the `#doApply` method.

The array of `LootItemCondition`s define the list of conditions that must be true before the loot can be modified. The supplied conditions are **ANDed** together, meaning that all conditions must be true.

The `#doApply` method works the same as the `#apply` method except that it only executes once all conditions return true.

```java
public class ExampleModifier extends LootModifier {

  public ExampleModifier(LootItemCondition[] conditionsIn, String prop1, int prop2, Item prop3) {
    super(conditionsIn);
    // Store the rest of the parameters
  }

  @NotNull
  @Override
  protected ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
    // Modify the loot and return the new drops
  }

  @Override
  public Codec<? extends IGlobalLootModifier> codec() {
    // Return the codec used to encode and decode this modifier
  }
}
```

The Loot Modifier Codec
-----------------------

The connector between the JSON and the `IGlobalLootModifier` instance is a [`Codec<T>`][codecdef], where `T` represents the type of the `IGlobalLootModifier` to use.

For ease of convenience, a loot conditions codec has been provided for an easy addition to a record-like codec via `LootModifier#codecStart`. This is utilized for [data generation][datagen] of the associated loot modifier.

```java
// For some DeferredRegister<Codec<? extends IGlobalLootModifier>> REGISTRAR
public static final RegistryObject<Codec<ExampleModifier>> = REGISTRAR.register("example_codec", () ->
  RecordCodecBuilder.create(
    inst -> LootModifier.codecStart(inst).and(
      inst.group(
        Codec.STRING.fieldOf("prop1").forGetter(m -> m.prop1),
        Codec.INT.fieldOf("prop2").forGetter(m -> m.prop2),
        ForgeRegistries.ITEMS.getCodec().fieldOf("prop3").forGetter(m -> m.prop3)
      )
    ).apply(inst, ExampleModifier::new)
  )
);
```

[Examples][examples] can be found on the Forge Git repository, including silk touch and smelting effects.

[tags]: ./tags.md
[resloc]: ../../concepts/resources.md#ResourceLocation
[codec]: #the-loot-modifier-codec
[registered]: ../../concepts/registries.md#methods-for-registering
[codecdef]: ../../datastorage/codecs.md
[datagen]: ../../datagen/server/glm.md
[examples]: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/test/java/net/minecraftforge/debug/gameplay/loot/GlobalLootModifiersTest.java

---

Datapacks
=========
In 1.13, Mojang added [datapacks][datapack] to the base game. They allow for the modification of the files for logical servers through the `data` directory. This includes advancements, loot_tables, structures, recipes, tags, etc. Forge, and your mod, can also have datapacks. Any user can therefore modify all the recipes, loot tables, and other data defined within this directory.

### Creating a Datapack
Datapacks are stored within the `data` directory within your project's resources.
Your mod can have multiple data domains, since you can add or modify already existing datapacks, like vanilla's, forge's, or another mod's.
You can then follow the steps found [here][createdatapack] to create any datapack.

Additional reading: [Resource Locations][resourcelocation]

[datapack]: https://minecraft.wiki/w/Data_pack
[createdatapack]: https://minecraft.wiki/w/Tutorials/Creating_a_data_pack
[resourcelocation]: ../../concepts/resources.md#ResourceLocation

---

Loot Tables
===========

Loot tables are logic files which dictate what should happen when various actions or scenarios occur. Although the vanilla system deals purely with item generation, the system can be expanded to perform any number of defined actions.

Data-Driven Tables
------------------

Most loot tables within vanilla are data driven via JSON. This means that a mod is not necessary to create a new loot table, only a [Data pack][datapack]. A full list on how to create and put these loot tables within the mod's `resources` folder can be found on the [Minecraft Wiki][wiki].

Using a Loot Table
------------------

A loot table is referenced by its `ResourceLocation` which points to `data/<namespace>/loot_tables/<path>.json`. The `LootTable` associated with the reference can be obtained using `LootDataResolver#getLootTable`, where `LootDataResolver` can be obtained via `MinecraftServer#getLootData`.

A loot table is always generated with given parameters. The `LootParams` contains the level the table is generated in, luck for better generation, the `LootContextParam`s which define scenario context, and any dynamic information that should occur on activation. The `LootParams` can be created using the constructor of the `LootParams$Builder` builder, and built via `LootParams$Builder#create` by passing in the `LootContextParamSet`.

A loot table may also have some context. The `LootContext` takes in the built `LootParams` and can set some random seeded instance. The context is created via the builder `LootContext$Builder` and built using `LootContext$Builder#create` by passing in a nullable `ResourceLocation` representing the random instance to use.

A `LootTable` can be used to generate `ItemStack`s using one of the available methods which may take in a `LootParams` or a `LootContext`:

Method              | Description
:---:               | :---
`getRandomItemsRaw` | Consumes the items generated by the loot table.
`getRandomItems`    | Returns the items generated by the loot table.
`fill`              | Fills a container with the generated loot table.

!!! note
    Loot tables were built for generating items, so the methods expect some handling for the `ItemStack`s.

Additional Features
-------------------

Forge provides some additional behavior to loot tables for greater control of the system.

### `LootTableLoadEvent`

`LootTableLoadEvent` is an [event] fired on the Forge event bus which is fired whenever a loot table is loaded. If the event is canceled, then an empty loot table will be loaded instead.

!!! important
    Do **not** modify a loot table's drops through this event. Those modifications should be done using [global loot modifiers][glm].

### Loot Pool Names

Loot pools can be named using the `name` key. Any non-named loot pool will be the hash code of the pool prefixed by `custom#`.

```js
// For some loot pool
{
  "name": "example_pool", // Pool will be named 'example_pool'
  "rolls": {
    // ...
  },
  "entries": {
    // ...
  }
}
```

### Looting Modifiers

Loot tables are now affected by the `LootingLevelEvent`, on the Forge event bus, in addition to the looting enchantment.

### Additional Context Parameters

Forge extends certain parameter sets to account for missing contexts which may be applicable. `LootContextParamSets#CHEST` now allows for a `LootContextParams#KILLER_ENTITY` as chest minecarts are entities which can be broken (or 'killed'). `LootContextParamSets#FISHING` also allows for a `LootContextParams#KILLER_ENTITY` since the fishing hook is also an entity which is retracted (or 'killed') when the player retrieves it.

### Multiple Items on Smelting

When using the `SmeltItemFunction`, a smelted recipe will now return the actual number of items from the result instead of a single smelted item (e.g. if a smelting recipe returns 3 items and there are 3 drops, then the result would be 9 smelted items instead of 3).

### Loot Table Id Condition

Forge adds an additional `LootItemCondition` which allows certain items to generate for a specific table. This is typically used within [global loot modifiers][glm].

```js
// In some loot pool or pool entry
{
  "conditions": [
    {
      "condition": "forge:loot_table_id",
      // Will apply when the loot table is for dirt
      "loot_table_id": "minecraft:blocks/dirt"
    }
  ]
}
```

### Can Tool Perform Action Condition

Forge adds an additional `LootItemCondition` which checks whether the given `LootContextParams#TOOL` can perform the specified `ToolAction`.

```js
// In some loot pool or pool entry
{
  "conditions": [
    {
      "condition": "forge:can_tool_perform_action",
      // Will apply when the tool can strip a log like an axe
      "action": "axe_strip"
    }
  ]
}
```

[datapack]: https://minecraft.wiki/w/Data_pack
[wiki]: https://minecraft.wiki/w/Loot_table
[event]: ../../concepts/events.md#creating-an-event-handler
[glm]: ./glm.md

---

Tags
====

Tags are generalized sets of objects in the game used for grouping related things together and providing fast membership checks.

Finding Tags
------------
When looking for existing tags, there's two main places to check:

### Vanilla Tags
Vanilla tags are declared in the `net.minecraft.tags` package. For example, `BlockTags` contains all the Vanilla block tags, `BiomeTags` contains all the Vanilla biome tags, and so on.

### Forge Tags
Forge bundles additional tags useful for mods, both Forge-specific and de-facto common tags that apply across all major mod loaders. You can find all of them in the `net.minecraftforge.common.Tags` class. The method names for each of the fields as well as code comment groups should make it clear which is a Forge-specific tag and which is a common tag.

!!! warning
    The common `c` namespaced tags seen in Forge are common across all loaders, however other loaders may have additional loader-specific tags under the same `c` namespace. When making a multi-loader mod, it is recommended to check the tags for each loader to ensure compatibility if you are considering a `c` tag you saw on other loaders that is missing in Forge. Loader-specific `c` tags may be in Forge under the `forge` namespace until they become common across all loaders.

### Full list of tags in Forge
You can find a full list of tags Forge adds on top of Vanilla Minecraft [here][forgebundledtagslist].

Declaring Your Own Groupings
----------------------------
Tags are declared in your mod's [datapack][datapack]. For example, a `TagKey<Block>` with a given identifier of  `modid:foo/tagname` will reference a tag at `/data/<modid>/tags/blocks/foo/tagname.json`. Tags for `Block`s, `Item`s, `EntityType`s, `Fluid`s, and `GameEvent`s use the plural forms for their folder location while all other registries use the singular version (`EntityType` uses the folder `entity_types` while `Potion` would use the folder `potion`).
Similarly, you may append to or override tags declared in other domains, such as Vanilla, by declaring your own JSONs.
For example, to add your own mod's saplings to the Vanilla sapling tag, you would specify it in `/data/minecraft/tags/blocks/saplings.json`, and Vanilla will merge everything into one tag at reload, if the `replace` option is false.
If `replace` is true, then all entries before the json specifying `replace` will be removed.
Values listed that are not present will cause the tag to error unless the value is listed using an `id` string and `required` boolean set to false, as in the following example:

```js
{
  "replace": false,
  "values": [
    "minecraft:gold_ingot",
    "mymod:my_ingot",
    {
      "id": "othermod:ingot_other",
      "required": false
    }
  ]
}
```

See the [Vanilla wiki][tags] for a description of the base syntax.

There is also a Forge extension on the Vanilla syntax.
You may declare a `remove` array of the same format as the `values` array. Any values listed here will be removed from the tag. This acts as a finer grained version of the Vanilla `replace` option.

Using Tags In Code
------------------
Tags for all registries are automatically sent from the server to any remote clients on login and reload. `Block`s, `Item`s, `EntityType`s, `Fluid`s, and `GameEvent`s are special cased as they have `Holder`s allowing for available tags to be accessible through the object itself.

!!! note
    Intrusive `Holder`s may be removed in a future version of Minecraft. If they are, the below methods can be used instead to query the associated `Holder`s.

### ITagManager

Forge wrapped registries provide an additional helper for creating and managing tags through `ITagManager` which can be obtained via `IForgeRegistry#tags`. Tags can be created using using `#createTagKey` or `#createOptionalTagKey`. Tags or registry objects can also be checked for either or using `#getTag` or `#getReverseTag` respectively.

#### Custom Registries

Custom registries can create tags when constructing their `DeferredRegister` via `#createTagKey` or `#createOptionalTagKey` respectively. Their tags or registry objects can then checked for either using the `IForgeRegistry` obtained by calling `DeferredRegister#makeRegistry`.

### Referencing Tags

There are four methods of creating a tag wrapper:

Method                          | For
:---:                           | :---
`*Tags#create`                  | `BannerPattern`, `Biome`, `Block`, `CatVariant`, `DamageType`, `EntityType`, `FlatLevelGeneratorPreset`, `Fluid`, `GameEvent`, `Instrument`, `Item`, `PaintingVariant`, `PoiType`, `Structure`, and `WorldPreset` where `*` represents one of these types.
`ITagManager#createTagKey`      | Forge wrapped vanilla registries, registries can be obtained from `ForgeRegistries`.
`DeferredRegister#createTagKey` | Custom forge registries.
`TagKey#create`                 | Vanilla registries without forge wrappers, registries can be obtained from `Registry`.

Registry objects can check their tags or registry objects either through their `Holder` or through `ITag`/`IReverseTag` for vanilla or forge registry objects respectively.

Vanilla registry objects can grab their associated holder using either `Registry#getHolder` or `Registry#getHolderOrThrow` and then compare if the registry object has a tag using `Holder#is`.

Forge registry objects can grab their tag definition using either `ITagManager#getTag` or `ITagManager#getReverseTag` and then compare if a registry object has a tag using `ITag#contains` or `IReverseTag#containsTag` respectively.

Tag-holding registry objects contain a method called `#is` in either their registry object or state-aware class to check whether the object belongs to a certain tag.

As an example:
```java
public static final TagKey<Item> myItemTag = ItemTags.create(ResourceLocation.fromNamespaceAndPath("mymod", "myitemgroup"));

public static final TagKey<Potion> myPotionTag = ForgeRegistries.POTIONS.tags().createTagKey(ResourceLocation.fromNamespaceAndPath("mymod", "mypotiongroup"));

public static final TagKey<VillagerType> myVillagerTypeTag = TagKey.create(Registries.VILLAGER_TYPE, ResourceLocation.fromNamespaceAndPath("mymod", "myvillagertypegroup"));

// In some method:

ItemStack stack = /*...*/;
boolean isInItemGroup = stack.is(myItemTag);

Potion potion = /*...*/;
boolean isInPotionGroup  = ForgeRegistries.POTIONS.tags().getTag(myPotionTag).contains(potion);

ResourceKey<VillagerType> villagerTypeKey = /*...*/;
boolean isInVillagerTypeGroup = BuiltInRegistries.VILLAGER_TYPE.getHolder(villagerTypeKey).map(holder -> holder.is(myVillagerTypeTag)).orElse(false);
```

Conventions
-----------

There are several conventions that will help facilitate compatibility in the ecosystem:

* If there is a Vanilla tag that fits your block or item, add it to that tag. See the [list of Vanilla tags][taglist].
* If there is a Forge tag that fits your block or item, add it to that tag. The list of tags declared by Forge can be seen on [GitHub][forgetags].
* If there is a group of something you feel should be shared by the community, use the `forge` namespace instead of your mod id.
* Tag naming conventions should follow Vanilla conventions. In particular, item and block groupings are plural instead of singular (e.g. `minecraft:logs`, `minecraft:saplings`).
* Item tags should be sorted into subdirectories according to their type (e.g. `forge:ingots/iron`, `forge:nuggets/brass`, etc.).

Migration from OreDictionary
----------------------------

* For recipes, tags can be used directly in the vanilla recipe format (see below).
* For matching items in code, see the section above.
* If you are declaring a new type of item grouping, follow a couple naming conventions:
  * Use `domain:type/material`. When the name is a common one that all modders should adopt, use the `forge` domain.
  * For example, brass ingots should be registered under the `forge:ingots/brass` tag and cobalt nuggets under the `forge:nuggets/cobalt` tag.

Using Tags in Recipes and Advancements
--------------------------------------

Tags are directly supported by Vanilla. See the respective Vanilla wiki pages for [recipes] and [advancements] for usage details.

[datapack]: ./index.md
[tags]: https://minecraft.wiki/w/Tag#JSON_format
[taglist]: https://minecraft.wiki/w/Tag#List_of_tags
[forgetags]: https://github.com/MinecraftForge/MinecraftForge/tree/1.19.x/src/generated/resources/data/forge/tags
[recipes]: https://minecraft.wiki/w/Recipe#JSON_format
[advancements]: https://minecraft.wiki/w/Advancement
[forgebundledtagslist]: ./tagslist.md

---

Tags list
=========
Forge bundles many tags useful for mods, both Forge-specific and de-facto common tags that apply across all major mod loaders. You can find all of them in the `net.minecraftforge.common.Tags` class. This page lists all those tags and their contents.

!!! note
    This page does not include Vanilla tags. Refer to the `net.minecraft.tags` package for those.

This page is generated from the [CommonTagsDumper][commontagsdumper] and is correct as of Forge 52.0.20. Note that not all builds of Forge contain tag changes, so just because this page references an older build does not mean this page is outdated. However, you should treat the actual generated JSONs on the Forge GitHub repository found [here][tagsrepo] as the ground truth. This page is provided for convenience and is not guaranteed to be up-to-date.

block
-----
`c:` common tags and `forge:` Forge-specific block tags, covering: barrels, barrels/wooden, bookshelves, budding_blocks, buds, chains, chests, chests/wooden, clusters, cobblestones, concretes, dyed (16 color sub-tags), glass_blocks (cheap / colorless / tinted), glass_panes (colorless), glazed_terracottas, obsidians (crying / normal), ores (netherite_scrap / quartz), player_workstations (crafting_tables / furnaces), sandstone (blocks / red_blocks / red_slabs / red_stairs / slabs / stairs / uncolored_blocks / uncolored_slabs / uncolored_stairs), skulls, stones, storage_blocks (per-material sub-tags), villager_job_sites, plus Forge-specific chests/ender, chests/trapped, cobblestone (deepslate / infested / mossy / normal), end_stones, fence_gates (wooden), fences (nether_brick / wooden), gravel, netherrack, ore_bearing_ground (deepslate / netherrack / stone), ore_rates (dense / singular / sparse), ores (coal / copper / diamond / emerald / gold / iron / lapis / redstone), ores_in_ground (deepslate / netherrack / stone), sand (colorless / red). `c:hidden_from_recipe_viewers`, `c:relocation_not_supported`, `c:ropes`, and `forge:enderman_place_on_blacklist` are empty tags (no members).

Representative members:

- **`c:barrels`**: `minecraft:barrel`
- **`c:dyed/black`**: `minecraft:black_banner`, `minecraft:black_bed`, `minecraft:black_concrete`, etc.
- **`c:storage_blocks/iron`**: `minecraft:iron_block`
- **`c:ores`**: `minecraft:coal_ore`, `minecraft:iron_ore`, `minecraft:diamond_ore`, etc.
- **`forge:chests/ender`**: `minecraft:ender_chest`
- **`forge:fences/wooden`**: `minecraft:oak_fence`, `minecraft:acacia_fence`, etc.

For the complete member lists, see the generated JSONs on the Forge GitHub repository or the Minecraft Wiki.

enchantment
-----------
`c:` enchantment tags grouped by effect: entity_auxiliary_movement_enhancements, entity_defense_enhancements, entity_speed_enhancements, increase_block_drops, increase_entity_drops, weapon_damage_enhancements.

- **`c:entity_defense_enhancements`**: `minecraft:protection`, `minecraft:blast_protection`, `minecraft:fire_protection`, etc.
- **`c:entity_speed_enhancements`**: `minecraft:depth_strider`, `minecraft:soul_speed`, `minecraft:swift_sneak`
- **`c:increase_block_drops`**: `minecraft:fortune`
- **`c:increase_entity_drops`**: `minecraft:looting`
- **`c:weapon_damage_enhancements`**: `minecraft:sharpness`, `minecraft:smite`, `minecraft:bane_of_arthropods`, etc.

For the complete member lists, see the generated JSONs on the Forge GitHub repository or the Minecraft Wiki.

entitytype
----------

- **`c:boats`**: `minecraft:boat`, `minecraft:chest_boat`
- **`c:bosses`**: `minecraft:ender_dragon`, `minecraft:wither`
- **`c:minecarts`**: `minecraft:minecart`, `minecraft:chest_minecart`, `minecraft:tnt_minecart`, etc.
- **`c:capturing_not_supported`** / **`c:teleporting_not_supported`**: empty tags (no members)

For the complete member lists, see the generated JSONs on the Forge GitHub repository or the Minecraft Wiki.

fluid
-----

- **`c:lava`**: `minecraft:lava`, `minecraft:flowing_lava`
- **`c:water`**: `minecraft:water`, `minecraft:flowing_water`
- **`c:hidden_from_recipe_viewers`** / **`c:honey`** / **`c:milk`**: empty tags (no members)
- **`forge:potion`** / **`forge:gaseous`** / **`forge:beetroot_soup`** / **`forge:mushroom_stew`** / **`forge:rabbit_stew`** / **`forge:suspicious_stew`**: empty tags (no members)

For the complete member lists, see the generated JSONs on the Forge GitHub repository or the Minecraft Wiki.

item
----
`c:` common tags and `forge:` Forge-specific item tags, covering: animal_foods, armors, bricks (nether / normal), buckets (empty / entity_water / lava / milk / powder_snow / water), concrete_powders, concretes, crops (per-crop sub-tags), dusts (glowstone / redstone), dyed (16 color sub-tags), dyes (16 color sub-tags), enchantables, ender_pearls, fertilizers, foods (berry / bread / candy / cooked_fish / cooked_meat / cookie / edible_when_placed / food_poisoning / fruit / golden / raw_fish / raw_meat / soup / vegetable), gems (amethyst / diamond / emerald /

…(truncated)
