Recipes

Author crafting recipes that turn ingredients into products, choose how players trigger them, and gate them on skill levels with XP rewards.

9 min read

What recipes do#

A recipe consumes ingredients from a player's inventory and produces items. It says which items go in, what comes out (with optional secondary and failure outputs), how the player triggers it (using a tool on a log, using ore on a furnace, clicking an option on an item, typing a command), which skill levels are required and how much XP a success grants. The Recipe Editor in the Tools hub builds recipes; the bundled crafting module loads them when the server starts and listens for the matching interactions.

Use recipes for anything shaped like "items in, items out": smelting, cooking, fletching, potion brewing, combining two halves of a key. For gathering from the world (chopping a tree, mining a rock) use a task instead; see Tasks and quests. For random rewards from an item or object, use a loot table.

Opening the tool#

  1. Click Tools in the top bar.
  2. Click Recipe Editor on the hub.

The tool is laid out in three panels:

  • Recipes (left) — a Categories list (starting with All) with a + button to add a category, then a Search recipes... box, the recipe list and + Add Recipe. Each list entry shows the primary product's icon, an arrow and the ingredient icons. Right-click a recipe for Duplicate or Delete; right-click a category for Rename or Delete.
  • Editor (center) — open recipes appear as tabs along the top, titled after their primary product (right-click a tab for Close Tab, Close Other Tabs, Close All Tabs). The header row holds the Category dropdown and Save. Below it, Products and Ingredient Groups sit side by side, and under them the Main fields and the Requirements and Experience Rewards lists.
  • Items (right) — every item in the project with a Qty quick-add value (default 1), a Search items... box and the hint "Double-click to add, or drag onto a group".

Recipes save when you click Save, press Ctrl+S, or leave the tool; tabs with unsaved changes carry a *. Deleting a recipe writes the file immediately.

Create a recipe#

  1. Select a category on the left if you want the new recipe filed under it (otherwise it goes to General), then click + Add Recipe. A recipe opens with one empty ingredient group and an empty primary product group.
  2. Click the Item Group 1 card under Ingredient Groups so it is highlighted, set Qty in the Items panel, and double-click the ingredient items (or drag them onto the card). Each item appears as a tile with its quantity in the corner; click the quantity to edit it in place, and use the tile's remove button to take it out.
  3. In the group's Actions list, set how the recipe fires. A new recipe's first group starts with no actions ("No actions — add one below"): click + Action to add one, which defaults to Use On Tool, then pick a type from the dropdown and fill in the field beside it (see the activation table below). + Action again adds an alternative trigger to the same group; + Group adds another ingredient group, created with one Use On Tool action of its own.
  4. Click the Primary Product card under Products and add the output items the same way.
  5. Under Requirements, click + Add to add a row: choose the skill from the dropdown (filled from your project's skills) and the minimum Lv (1–120). Under Experience Rewards, click + Add and set the skill and XP (0–99,999, in quarter-point steps; default 10).
  6. Set Crafting Ticks and Animation ID in Main if you use them (see the runtime section).
  7. Click Save.

Categories#

Categories are labels used to file recipes in the left panel and in the Category dropdown. + opens the New Category dialog; the category appears in the list even before a recipe uses it. Deleting a category moves its recipes to General. Categories are stored on each recipe, not in a separate list, so a category with no recipes is not saved and is gone after you restart the editor.

Activation types#

Each ingredient group carries one or more actions. An action is the interaction that triggers the recipe; any of a group's actions fires it.

ActionFieldFires when
Use On ToolTool item idThe player uses the tool on an item in the group, or the item on the tool. The tool must be in the inventory and is not consumed.
Use On Each OthernoneThe player uses one item in the group on another item in the same group.
Use On ObjectObj object idThe player uses an item in the group on that world object (a furnace, an anvil, a range).
Use On NPCNPC npc idThe player uses an item in the group on that NPC.
Item Interactionoption text, e.g. CraftThe player clicks that option on an item in the group. The option is matched case-insensitively against what the client sends.
Commandcommand name, e.g. craft_bronzeThe player types ::craft_bronze. The module registers the command at startup; the first recipe to claim a name keeps it.
Custommethod nameIgnored by the bundled module. Use it to mark recipes that your own scripts trigger.

Item and object ids are the numbers shown in the Data Editor. For a station-based craft, put the ingredient in one group with a Use On Object action pointing at the station; for a tool craft, list the ingredients and name the tool in a Use On Tool action.

Products#

Products are grouped, and each group has a Chance (0–100%). Every recipe has a Primary Product group. + Add Secondary Group adds a yellow secondary group (created at 25%), and + Add Failure Group adds a red failure group (created at 1%). Secondary and failure groups can be removed; the primary cannot. When you change one group's chance the editor rebalances the others so the three sum to 100, and a recipe with only a primary group is pinned to 100%.

Files written#

Recipes are saved to server/schemas/Crafting/recipes/recipes_data.json, with a recipes.json schema description created beside it on first save. The file is a JSON array of recipes.

JSON
[
	{
		"recipe_id": "recipe_1776543210123",
		"category": "Smithing",
		"animation_id": 0,
		"crafting_ticks": 4,
		"skill_requirements": [ { "skill_id": 10, "level": 5 } ],
		"skill_rewards": [ { "skill_id": 10, "experience": 25 } ],
		"ingredients": [
			{
				"items": [ { "item_id": 10, "quantity": 1 } ],
				"actions": [ { "activation_type": "use_on_object", "activation_data": "4" } ]
			}
		],
		"products": [
			{ "type": "primary", "chance": 100.0, "items": [ { "item_id": 11, "quantity": 1 } ] }
		]
	}
]
FieldTypeMeaning
recipe_idstringUnique id. The editor generates recipe_<timestamp>; duplicating appends _copy. Scripts and the MCP tool address recipes by this id.
namestringOptional display name written by the MCP tool. The editor shows recipes by their primary product instead and does not edit this field.
categorystringFiling label, default General.
animation_idintLoaded by the server but not played by the bundled module.
crafting_ticksint1–999, default 4. Loaded by the server but not used by the bundled module, which crafts instantly.
skill_requirements[]arrayskill_id + level. The player must meet every entry.
skill_rewards[]arrayskill_id + experience. Granted on success. A recipe with no rewards grants no XP.
ingredients[]arrayGroups of items[] (item_id, quantity) and actions[] (activation_type, activation_data).
products[]arrayGroups of type (primary, secondary, failure), chance, optional name, and items[].
skill_id, required_level, experience_reward, success_rate, tool_item_idlegacyWritten by the MCP tool for older modules, and declared in the recipes.json the editor generates when none exists. The server does not read them; use the arrays and a Use On Tool action instead.

Older files that stored a flat ingredients list, a flat products list or a single activation_type per group are migrated to the grouped shape when the tool opens them.

How the server crafts#

The Crafting gameplay system owns CraftingModule.cs and the Crafting schema folder. At startup the module loads every recipe with a non-empty recipe_id, registers an item script for every ingredient and tool item that appears in a non-Custom group, and registers a ::command for every Command action.

When a matching interaction arrives the module attempts the craft:

  1. Every Requirements entry is checked against the player's level ("You need a higher level to craft this.").
  2. Every Use On Tool tool must be in the inventory ("You don't have the required tool.").
  3. Every ingredient across all groups must be present in the required quantity ("You don't have the required ingredients.").
  4. Each product group is rolled. The player needs one free inventory slot per granted product entry, counted before ingredients are removed ("Your inventory is full.").
  5. Ingredients are removed, products are added, and each Experience Rewards entry grants XP (which can level the player up; see Skills).

A failed attempt still counts as handled, so the player sees the reason instead of "Nothing interesting happens." Item clicks are offered to task triggers first and to item scripts (including crafting) second; see NPC, object and interaction scripts for the full order.

Scripts can drive crafting directly: RecipeRegistry.Instance.AttemptCraft(player, "smelt_bronze") returns a CraftResult (Success, InsufficientLevel, MissingTool, MissingIngredients, NoInventorySpace, RecipeNotFound), and GetById, GetByCategory, GetBySkill and GetAll read the registry. This is how a workbench object script or a crafting interface can offer a menu of recipes rather than relying on use-item interactions.

With AI (MCP)#

  • define_recipe — upserts one recipe by recipe_id. The simple form takes ingredients, products, activation_type (default use_on_object) and activation_data; the full form takes ingredient_groups (each with its own activation) and product_groups (primary, secondary, failure with chance). Level gates and XP are skill_requirements and skill_rewards; the flat skill_id, required_level and experience_reward spellings are folded into one-element arrays. Every item must exist and every skill must be in skills_config.json. Fields the tool does not model are preserved on update. Pass dryRun: true to preview.
  • get_schema_data with the table name recipes reads the raw rows.

See the tools reference for full argument lists.

Spotted a mistake or something missing?Tell us on Discord