Skip to content

Commit

Permalink
feat(docs): Update recipes section
Browse files Browse the repository at this point in the history
  • Loading branch information
ChampionAsh5357 committed Oct 24, 2024
1 parent ceac726 commit dda28d5
Show file tree
Hide file tree
Showing 3 changed files with 557 additions and 211 deletions.
178 changes: 99 additions & 79 deletions docs/resources/server/recipes/builtin.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,15 @@ Some of the most important recipes - such as the crafting table, sticks, or most
{
"type": "minecraft:crafting_shaped",
"category": "equipment",
"key": {
"#": "minecraft:stick",
"X": "minecraft:iron_ingot"
},
"pattern": [
"XXX",
" # ",
" # "
],
"key": {
"#": {
"item": "minecraft:stick"
},
"X": {
"item": "minecraft:iron_ingot"
}
},
"result": {
"count": 1,
"id": "minecraft:iron_pickaxe"
Expand All @@ -37,20 +33,21 @@ Some of the most important recipes - such as the crafting table, sticks, or most
Let's digest this line for line:

- `type`: This is the id of the shaped recipe serializer, `minecraft:crafting_shaped`.
- `category`: This optional field defines the category in the crafting book.
- `category`: This optional field defines the `CraftingBookCategory` in the crafting book.
- `key` and `pattern`: Together, these define how the items must be put into the crafting grid.
- The pattern defines up to three lines of up to three-wide strings that define the shape. All lines must be the same length, i.e. the pattern must form a rectangular shape. Spaces can be used to denote slots that should stay empty.
- The key associates the characters used in the pattern with [ingredients][ingredient]. In the above example, all `X`s in the pattern must be iron ingots, and all `#`s must be sticks.
- `result`: The result of the recipe. This is [an item stack's JSON representation][itemjson].
- Not shown in the example is the `group` key. This optional string property creates a group in the recipe book. Recipes in the same group will be displayed as one in the recipe book.
- Not shown in the example is `show_notification`. This optional boolean, when false, disables the toast shown on the top right hand corner on first use or unlock.

And then, let's have a look at how you'd generate this recipe:
And then, let's have a look at how you'd generate this recipe within `RecipeProvider#buildRecipes`:

```java
// We use a builder pattern, therefore no variable is created. Create a new builder by calling
// ShapedRecipeBuilder#shaped with the recipe category (found in the RecipeCategory enum)
// and a result item, a result item and count, or a result item stack.
ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, Items.IRON_PICKAXE)
ShapedRecipeBuilder.shaped(this.registries.lookupOrThrow(Registries.ITEM), RecipeCategory.TOOLS, Items.IRON_PICKAXE)
// Create the lines of your pattern. Each call to #pattern adds a new line.
// Patterns will be validated, i.e. their shape will be checked.
.pattern("XXX")
Expand All @@ -64,13 +61,13 @@ ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, Items.IRON_PICKAXE)
// the recipe builder will crash if you omit this. The first parameter is the advancement name,
// and the second one is the condition. Normally, you want to use the has() shortcut for the condition.
// Multiple advancement requirements can be added by calling #unlockedBy multiple times.
.unlockedBy("has_iron_ingot", has(Items.IRON_INGOT))
.unlockedBy("has_iron_ingot", this.has(Items.IRON_INGOT))
// Stores the recipe in the passed RecipeOutput, to be written to disk.
// If you want to add conditions to the recipe, those can be set on the output.
.save(output);
.save(this.output);
```

Additionally, you can call `#group` to set the recipe book group.
Additionally, you can call `#group` and `#showNotification` to set the recipe book group and toggle the toast pop-up, respectively.

### Shapeless Crafting

Expand All @@ -81,15 +78,9 @@ Unlike shaped crafting recipes, shapeless crafting recipes do not care about the
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
{
"item": "minecraft:brown_mushroom"
},
{
"item": "minecraft:red_mushroom"
},
{
"item": "minecraft:bowl"
}
"minecraft:brown_mushroom",
"minecraft:red_mushroom",
"minecraft:bowl"
],
"result": {
"count": 1,
Expand All @@ -106,13 +97,13 @@ Like before, let's digest this line for line:
- `result`: The result of the recipe. This is [an item stack's JSON representation][itemjson].
- Not shown in the example is the `group` key. This optional string property creates a group in the recipe book. Recipes in the same group will be displayed as one in the recipe book.

And then, let's have a look at how you'd generate this recipe:
And then, let's have a look at how you'd generate this recipe in `RecipeProvider#buildRecipes`:

```java
// We use a builder pattern, therefore no variable is created. Create a new builder by calling
// ShapelessRecipeBuilder#shapeless with the recipe category (found in the RecipeCategory enum)
// and a result item, a result item and count, or a result item stack.
ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, Items.MUSHROOM_STEW)
ShapelessRecipeBuilder.shapeless(this.registries.lookupOrThrow(Registries.ITEM), RecipeCategory.MISC, Items.MUSHROOM_STEW)
// Add the recipe ingredients. This can either accept Ingredients, TagKey<Item>s or ItemLikes.
// Overloads also exist that additionally accept a count, adding the same ingredient multiple times.
.requires(Blocks.BROWN_MUSHROOM)
Expand All @@ -122,13 +113,13 @@ ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, Items.MUSHROOM_STEW)
// the recipe builder will crash if you omit this. The first parameter is the advancement name,
// and the second one is the condition. Normally, you want to use the has() shortcut for the condition.
// Multiple advancement requirements can be added by calling #unlockedBy multiple times.
.unlockedBy("has_mushroom_stew", has(Items.MUSHROOM_STEW))
.unlockedBy("has_bowl", has(Items.BOWL))
.unlockedBy("has_brown_mushroom", has(Blocks.BROWN_MUSHROOM))
.unlockedBy("has_red_mushroom", has(Blocks.RED_MUSHROOM))
.unlockedBy("has_mushroom_stew", this.has(Items.MUSHROOM_STEW))
.unlockedBy("has_bowl", this.has(Items.BOWL))
.unlockedBy("has_brown_mushroom", this.has(Blocks.BROWN_MUSHROOM))
.unlockedBy("has_red_mushroom", this.has(Blocks.RED_MUSHROOM))
// Stores the recipe in the passed RecipeOutput, to be written to disk.
// If you want to add conditions to the recipe, those can be set on the output.
.save(output);
.save(this.output);
```

Additionally, you can call `#group` to set the recipe book group.
Expand All @@ -137,26 +128,70 @@ Additionally, you can call `#group` to set the recipe book group.
One-item recipes (e.g. storage blocks unpacking) should be shapeless recipes to follow vanilla standards.
:::

### Special Crafting
### Transmute Crafting

Transmute recipes are a special type of single item crafting recipes where the input stack's data components are completely copied to the resulting stack. Transmutations usually occur between two different items where one is the dyed version of another. For example:

```json5
{
"type": "minecraft:crafting_transmute",
"category": "misc",
"group": "shulker_box_dye",
"input": "#minecraft:shulker_boxes",
"material": "minecraft:blue_dye",
"result": "minecraft:blue_shulker_box"
}
```

In some cases, outputs must be created dynamically from inputs. Most of the time, this is to set data components on the output by copying or calculating their values from the input stacks. These recipes usually only specify the type and hardcode everything else. For example:
Like before, let's digest this line for line:

- `type`: This is the id of the shapeless recipe serializer, `minecraft:crafting_transmute`.
- `category`: This optional field defines the category in the crafting book.
- `group`: This optional string property creates a group in the recipe book. Recipes in the same group will be displayed as one in the recipe book, which typically makes sense for transmuted recipes.
- `input`: The [ingredient] to transmute.
- `material`: The [ingredient] that transforms the stack into its result.
- `result`: The result of the recipe. This is an item, as the stack would be constructed given the components of the input.

And then, let's have a look at how you'd generate this recipe in `RecipeProvider#buildRecipes`:

```java
// We use a builder pattern, therefore no variable is created. Create a new builder by calling
// TransmuteRecipeBuilder#transmute with the recipe category (found in the RecipeCategory enum),
// the ingredient input, the ingredient material, and the resulting item.
TransmuteRecipeBuilder.transmute(RecipeCategory.MISC, this.tag(ItemTags.SHULKER_BOXES),
Ingredient.of(DyeItem.byColor(DyeColor.BLUE)), ShulkerBoxBlock.getBlockByColor(DyeColor.BLUE).asItem())
// Sets the group of the recipe to display in the recipe book.
.group("shulker_box_dye")
// Creates the recipe advancement. While not mandated by the consuming background systems,
// the recipe builder will crash if you omit this. The first parameter is the advancement name,
// and the second one is the condition. Normally, you want to use the has() shortcut for the condition.
// Multiple advancement requirements can be added by calling #unlockedBy multiple times.
.unlockedBy("has_shulker_box", this.has(ItemTags.SHULKER_BOXES))
// Stores the recipe in the passed RecipeOutput, to be written to disk.
// If you want to add conditions to the recipe, those can be set on the output.
.save(this.output);
```

### Special Crafting

In some cases, outputs must be created dynamically from inputs. Most of the time, this is to set data components on the output by calculating their values from the input stacks. These recipes usually only specify the type and hardcode everything else. For example:

```json5
{
"type": "minecraft:crafting_special_armordye"
}
```

This recipe, which is for leather armor dyeing, just specifies the type and hardcodes everything else - most notably the color calculation, which would be hard to express in JSON. Minecraft prefixes all special crafting recipes with `crafting_special_`, however this practice is not necessary to follow.
This recipe, which is for leather armor dyeing, just specifies the type and hardcodes everything else - most notably the color calculation, which would be hard to express in JSON. Minecraft prefixes most special crafting recipes with `crafting_special_`, however this practice is not necessary to follow.

Generating this recipe looks as follows:
Generating this recipe looks as follows in `RecipeProvider#buildRecipes`:

```java
// The parameter of #special is a Function<CraftingBookCategory, Recipe<?>>.
// All vanilla special recipes use a constructor with one CraftingBookCategory parameter for this.
SpecialRecipeBuilder.special(ArmorDyeRecipe::new)
// This overload of #save allows us to specify a name. It can also be used on shaped or shapeless builders.
.save(output, "armor_dye");
.save(this.output, "armor_dye");
```

Vanilla provides the following special crafting serializers (mods may add more):
Expand All @@ -172,7 +207,6 @@ Vanilla provides the following special crafting serializers (mods may add more):
- `minecraft:crafting_special_repairitem`: For repairing two broken items into one.
- `minecraft:crafting_special_shielddecoration`: For applying a banner to a shield.
- `minecraft:crafting_special_shulkerboxcoloring`: For coloring a shulker box while preserving its contents.
- `minecraft:crafting_special_suspiciousstew`: For crafting suspicious stews depending on the input flower.
- `minecraft:crafting_special_tippedarrow`: For crafting tipped arrows depending on the input potion.
- `minecraft:crafting_decorated_pot`: For crafting decorated pots from sherds.
Expand Down Expand Up @@ -204,7 +238,7 @@ Let's digest this line by line:
- `ingredient`: The input [ingredient] of the recipe.
- `result`: The result of the recipe. This is [an item stack's JSON representation][itemjson].

Datagen for these recipes looks like this:
Datagen for these recipes looks like this in `RecipeProvider#buildRecipes`:

```java
// Use #smoking for smoking recipes, #blasting for blasting recipes, and #campfireCooking for campfire recipes.
Expand All @@ -222,9 +256,9 @@ SimpleCookingRecipeBuilder.smelting(
200
)
// The recipe advancement, like with the crafting recipes above.
.unlockedBy("has_kelp", has(Blocks.KELP))
.unlockedBy("has_kelp", this.has(Blocks.KELP))
// This overload of #save allows us to specify a name.
.save(p_301191_, "dried_kelp_smelting");
.save(this.output, "dried_kelp_smelting");
```

:::info
Expand All @@ -238,9 +272,7 @@ Stonecutter recipes use the `minecraft:stonecutting` recipe type. They are about
```json5
{
"type": "minecraft:stonecutting",
"ingredient": {
"item": "minecraft:andesite"
},
"ingredient": "minecraft:andesite",
"result": {
"count": 2,
"id": "minecraft:andesite_slab"
Expand All @@ -250,12 +282,12 @@ Stonecutter recipes use the `minecraft:stonecutting` recipe type. They are about

The `type` defines the recipe serializer (`minecraft:stonecutting`). The ingredient is an [ingredient], and the result is a basic [item stack JSON][itemjson]. Like crafting recipes, they can also optionally specify a `group` for grouping in the recipe book.

Datagen is also simple:
Datagen is also simple in `RecipeProvider#buildRecipes`:

```java
SingleItemRecipeBuilder.stonecutting(Ingredient.of(Items.ANDESITE), RecipeCategory.BUILDING_BLOCKS, Items.ANDESITE_SLAB, 2)
.unlockedBy("has_andesite", has(Items.ANDESITE))
.save(output, "andesite_slab_from_andesite_stonecutting");
.unlockedBy("has_andesite", this.has(Items.ANDESITE))
.save(this.output, "andesite_slab_from_andesite_stonecutting");
```

Note that the single item recipe builder does not support actual ItemStack results, and as such, no results with data components. The recipe codec, however, does support them, so a custom builder would need to be implemented if this functionality was desired.
Expand All @@ -270,20 +302,14 @@ This recipe serializer is for transforming two input items into one, preserving

```json5
{
"type": "minecraft:smithing_transform",
"base": {
"item": "minecraft:diamond_axe"
},
"template": {
"item": "minecraft:netherite_upgrade_smithing_template"
},
"addition": {
"item": "minecraft:netherite_ingot"
},
"result": {
"count": 1,
"id": "minecraft:netherite_axe"
}
"type": "minecraft:smithing_transform",
"addition": "#minecraft:netherite_tool_materials",
"base": "minecraft:diamond_axe",
"result": {
"count": 1,
"id": "minecraft:netherite_axe"
},
"template": "minecraft:netherite_upgrade_smithing_template"
}
```

Expand All @@ -295,7 +321,7 @@ Let's break this down line by line:
- `addition`: The addition [ingredient] of the recipe. Usually, this is some sort of material, for example a netherite ingot.
- `result`: The result of the recipe. This is [an item stack's JSON representation][itemjson].

During datagen, call on `SmithingTransformRecipeBuilder#smithing` to add your recipe:
During datagen, call on `SmithingTransformRecipeBuilder#smithing` to add your recipe in `RecipeProvider#buildRecipes`:

```java
SmithingTransformRecipeBuilder.smithing(
Expand All @@ -304,17 +330,17 @@ SmithingTransformRecipeBuilder.smithing(
// The base ingredient.
Ingredient.of(Items.DIAMOND_AXE),
// The addition ingredient.
Ingredient.of(Items.NETHERITE_INGOT),
this.tag(ItemTags.NETHERITE_TOOL_MATERIALS),
// The recipe book category.
RecipeCategory.TOOLS,
// The result item. Note that while the recipe codec accepts an item stack here, the builder does not.
// If you need an item stack output, you need to use your own builder.
Items.NETHERITE_AXE
)
// The recipe advancement, like with the other recipes above.
.unlocks("has_netherite_ingot", has(Items.NETHERITE_INGOT))
.unlocks("has_netherite_ingot", this.has(ItemTags.NETHERITE_TOOL_MATERIALS))
// This overload of #save allows us to specify a name.
.save(output, "netherite_axe_smithing");
.save(this.output, "netherite_axe_smithing");
```

### Trim Smithing
Expand All @@ -323,16 +349,10 @@ Trim smithing is the process of applying armor trims to armor:

```json5
{
"type": "minecraft:smithing_trim",
"addition": {
"tag": "minecraft:trim_materials"
},
"base": {
"tag": "minecraft:trimmable_armor"
},
"template": {
"item": "minecraft:bolt_armor_trim_smithing_template"
}
"type": "minecraft:smithing_trim",
"addition": "#minecraft:trim_materials",
"base": "#minecraft:trimmable_armor",
"template": "minecraft:bolt_armor_trim_smithing_template"
}
```

Expand All @@ -345,23 +365,23 @@ Again, let's break this down into its bits:
This recipe serializer is notably missing a result field. This is because it uses the base input and "applies" the template and addition items on it, i.e., it sets the base's components based on the other inputs and uses the result of that operation as the recipe's result.
During datagen, call on `SmithingTrimRecipeBuilder#smithingTrim` to add your recipe:
During datagen, call on `SmithingTrimRecipeBuilder#smithingTrim` to add your recipe in `RecipeProvider#buildRecipes`:
```java
SmithingTrimRecipeBuilder.smithingTrim(
// The base ingredient.
Ingredient.of(ItemTags.TRIMMABLE_ARMOR),
// The template ingredient.
Ingredient.of(Items.BOLT_ARMOR_TRIM_SMITHING_TEMPLATE),
// The base ingredient.
this.tag(ItemTags.TRIMMABLE_ARMOR),
// The addition ingredient.
Ingredient.of(ItemTags.TRIM_MATERIALS),
this.tag(ItemTags.TRIM_MATERIALS),
// The recipe book category.
RecipeCategory.MISC
)
// The recipe advancement, like with the other recipes above.
.unlocks("has_smithing_trim_template", has(Items.BOLT_ARMOR_TRIM_SMITHING_TEMPLATE))
.unlocks("has_smithing_trim_template", this.has(Items.BOLT_ARMOR_TRIM_SMITHING_TEMPLATE))
// This overload of #save allows us to specify a name. Yes, this name is copied from vanilla.
.save(output, "bolt_armor_trim_smithing_template_smithing_trim");
.save(this.output, "bolt_armor_trim_smithing_template_smithing_trim");
```
[ingredient]: ingredients.md
Expand Down
Loading

1 comment on commit dda28d5

@neoforged-pages-deployments
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploying with Cloudflare Pages

Name Result
Last commit: dda28d5ecbf59844fa8b891b01ec6ae976baf0b5
Status: ✅ Deploy successful!
Preview URL: https://079058b4.neoforged-docs-previews.pages.dev
PR Preview URL: https://pr-178.neoforged-docs-previews.pages.dev

Please sign in to comment.