Modpack Authors
6 TOP-LEVEL ITEMSCreate Custom Recipe Wizard Generators
Customizing the Recipe Wizard essentially means adding new rules that answer: “when the player right-clicks this target block, how should a file be generated from the current in-world context?”
It is very close to the Runtime Data Generation System:
- the target block match acts like “when to trigger”
pathdecides where the generated file is writtentemplatedecides what the file content looks like- placeholders extract values from the current click context
The main difference from a normal data generator is that the input here is not a static entry set, but the UseOnContext from the player’s current right-click.
Location
Recipe Wizard generator files are stored at:
<filePath>/recipe_wizard/generatorsHere, filePath is the custom file root from Configuration and Commands.
This path is hardcoded in the source. Note that the directory name is generators, not generator.
When the Recipe Wizard first reads this directory, it also exports built-in templates there as editable starting points.
File Formats
RecipeWizardGenerator.read(...) ultimately reads files through DgReader, so it supports the same formats as normal generators:
tomljsoncdg
The built-in templates currently use toml, and that is also the most suitable format for handwritten files.
Minimal example:
block = "croparia:infusor"
path = "example/infusor_${datetime}.json"
extensions = "croparia:infusor"
template = """
{
"type": "croparia:infusor",
"element": "${infusor_element}",
"ingredient": ${item._qis},
"result": ${off_hand._qis}
}
"""Full Field List
The fields of RecipeWizardGenerator come from the CODEC in the source code:
enabled: booleandependencies: Dependenciesblock: BlockInputpath: Templateextensions: ResourceLocation | ResourceLocation[]template: Template
The sections below explain them one by one.
enabled
- Type:
boolean - Optional, defaults to
true
Controls whether this Recipe Wizard generator is enabled. If disabled, the file is still read, but it does not enter the list of available templates.
dependencies
- Type: dependency object
- Optional, defaults to empty
It behaves the same way as dependency checks elsewhere in the docs: the generator file only loads when its dependencies are satisfied.
If the dependencies are not satisfied, the source code skips the file directly rather than throwing an error and continuing to use it.
block
- Type: BlockInput
- Required
Determines which target blocks this generator applies to.
The generator only triggers when the block state right-clicked by the player matches block.
Example:
block = "croparia:infusor"
block = "#croparia:ritual_stands"path
- Type:
Template - Required
The relative output path under the recipeWizard export directory.
For example:
path = "croparia/ritual_${datetime}.json"If the configured recipeWizard directory is D:/packs/croparia/recipe_wizard/output, the final output becomes:
D:/packs/croparia/recipe_wizard/output/croparia/ritual_2026-04-06_12.30.00.jsonpath is itself a template, so it can use any placeholders introduced later on this page.
extensions
- Type:
stringorstring[] - Optional, defaults to an empty list
These are not plain strings. They are IDs of extension placeholder groups. The source currently registers:
croparia:infusorcroparia:ritualcroparia:soakminecraft:furnace
The default group is always enabled automatically and does not need to be written manually.
If you want to use placeholders from one of these groups, you must list it in extensions. For example:
extensions = "croparia:ritual"or:
extensions = [
"croparia:ritual",
"minecraft:furnace"
]template
- Type:
Template - Required
The final file content to write, usually a JSON template.
Its placeholders are resolved at click time, then the resolved result is written to disk.
Example:
template = """
{
"type": "croparia:soak",
"element": "${soak_element}",
"input": ${neighbor._qis},
"output": ${below._qis},
"probability": 1.0
}
"""The template syntax itself matches the Placeholder Parsers.
Default Placeholders
Whether or not you list anything in extensions, the default placeholder group is always enabled.
These placeholders all come from the UseOnContext of the click event.
${datetime}- current timestamp, formatted as
yyyy-MM-dd_HH.mm.ss
- current timestamp, formatted as
${main_hand}- main-hand item, with a type equivalent to
ItemOutput
- main-hand item, with a type equivalent to
${off_hand}- off-hand item, with a type equivalent to
ItemOutput
- off-hand item, with a type equivalent to
${item}- dropped item at the clicked position, with a type equivalent to
ItemOutput
- dropped item at the clicked position, with a type equivalent to
${block}- block currently being clicked, with a type equivalent to
BlockOutput
- block currently being clicked, with a type equivalent to
${neighbor}- any non-air neighboring block, with a type equivalent to
BlockOutput
- any non-air neighboring block, with a type equivalent to
${below}- block below, with a type equivalent to
BlockOutput
- block below, with a type equivalent to
block also has several common subpaths:
${block.facing}: the block in front of the clicked face${block.opposite}: the block behind the clicked face${block.left}: the block to the clockwise side of the clicked face${block.right}: the block to the counter-clockwise side of the clicked face${block.offset(x,y,z)}: a block offset relative to the clicked position
Most of these are structured types, so in practice they are usually used with _qis, just like the built-in templates:
${item._qis}
${block._qis}
${neighbor._qis}If the required context is missing, such as no main-hand item or no dropped item at the target position, the source code immediately shows an overlay prompt and aborts generation.
Extension Placeholders
extensions decides which extra domain-specific placeholders are enabled.
croparia:infusor
${infusor_element}
Requires the clicked target to be an infused Infusor. If the element is empty, the player is prompted to infuse the Infusor first.
croparia:ritual
${ritual_input}
Requires the clicked target to be a Ritual Stand, and the current structure must correctly identify the input block positions. This placeholder returns the input block at the $ positions of the ritual structure.
croparia:soak
${soak_element}
Requires the clicked target to be an Elemental Stone with an infused Infusor above it. The returned value is the name of the current soak element.
minecraft:furnace
${furnace_input}${furnace_time}
This group is meant for furnace-like blocks:
furnace_inputreads the input-slot itemfurnace_timereads the total burn time of the current fuel
If the target is not a furnace, or if the input or fuel slot is empty, the item will also show a prompt and abort generation.
Built-in Template Examples
The built-in 1.1.0a templates include three files, and they also reflect the most typical writing patterns.
Infusor Recipe
path = "croparia/infusor_${datetime}.json"
extensions = "croparia:infusor"
block = "croparia:infusor"
template = """
{
"type": "croparia:infusor",
"element": "${infusor_element}",
"ingredient": ${item._qis},
"result": ${off_hand._qis}
}
"""Ritual Recipe
path = "croparia/ritual_${datetime}.json"
extensions = "croparia:ritual"
block = "#croparia:ritual_stands"
template = """
{
"type": "croparia:ritual",
"ritual": ${block._qis},
"ingredient": ${item._qis},
"block": ${ritual_input._qis},
"result": ${off_hand._qis}
}
"""Soak Recipe
path = "croparia/soak_${datetime}.json"
extensions = "croparia:soak"
block = "croparia:elemental_stone"
template = """
{
"type": "croparia:soak",
"element": "${soak_element}",
"input": ${neighbor._qis},
"output": ${below._qis},
"probability": 1.0
}
"""The two details most worth noticing here are:
blockdecides “when to trigger”, not which input block should appear in the generated result- when writing JSON, structured values such as
ItemOutputandBlockOutputusually need_qisso they become text that can be embedded directly into JSON
Recommendations
- When customizing one for the first time, copying and editing a built-in template is more reliable than starting from scratch.
- It is best for
pathto include${datetime}, so repeated clicks do not overwrite older files. - If a template depends on multiple special contexts, make sure every matching
extensionsgroup has been listed. - Target block matching only depends on
block, not on which placeholders appear inside the template. - The generated content is usually still a JSON recipe file, so after generation you can continue validating fields against Recipes and Structures.