Recipes are a core concept within the use of items. They allow you to transform some item into another through the use of crafting, smelting, smithing, etc. all of which can be defined in JSON through datapacks. However, the recipe system in its entirety is not only limited to what vanilla can provide. Any modder can create their own custom recipe implementation.
Creating a New Recipe
Creating a new recipe boils down to three things:
For a general overview:
Recipe: The actual recipe implementation which handles the matching logic, construction logic, and the display data. All custom recipes need some implementation of this class.
RecipeType: The category the recipe belongs to. For example, a recipe placed in the
#CRAFTINGcategory is expected to work within the crafting table. A custom recipe category indicates that recipes of that type will be used in that particular context.
RecipeSerializer: The serializer which handles decoding the data from JSON and handling the data when syncing across the network. Each serializer can serialize to one
typefield within any recipe JSON references the registry name of this serializer.
How one uses the recipe and its data is up to the implementing modder.
Recipe is an interface which holds data about the recipe being represented and how to test whether it is valid. It has a single parameterized type of some
Container subtype. This is used to get a snapshot of the current inventory container, and as such should only be read from in this class. In most cases, this can just be left as
The following methods must be implemented within your implementation:
|getId||This method represents the unique name of the recipe. This usually refers to the name of the JSON file and is passed in while decoding.|
|getSerializer||This method represents the serializer used to decode and send this recipe across the network.|
|getType||This method represents the category the recipe is in.|
|canCraftInDimensions||This takes in a width and height and checks whether the recipe can be created within those bounds. This is only used within the Recipe Book.|
|matches||This method checks if the passed in inventory container can craft using the recipe stored in this class. Using the standard |
|assemble||This creates the resulting |
|getResultItem||This shows the resulting item in displays like the Recipe Book. This does not need to return a unique instance and usually represents the decoded result directly.|
There are some defaulted methods as well. Although optional, they will be covered as well:
|getGroup||This gets the group the recipe is currently associated with. This is used by the Recipe Book to group similar recipes into one entry to reduce cluttering. This would be passed in from the decoded JSON. By default, this returns an empty string.|
|getIngredients||This gets the ingredients of the recipe. This is once again used by the Recipe Book to display the ingredients of the recipe. These are usually the decoded |
|isSpecial||This usually signals that the recipe cannot purely be represented in JSON as it has some dynamic metadata preventing it from being so. Special recipes will not appear in the Recipe Book for this reason as they provide a number of combinations which cannot be simply expressed. By default, this is false.|
|getToastSymbol||This returns the symbol that appears when the recipe is seen in a toast, usually from unlocking via an advancement. This is not the actual recipe output itself, but it signifies what the recipe was made within. By default this returns a crafting table stack.|
|getRemainingItems||This returns what items remain in the inventory container after the recipe has been created. By default, this will return a list of container items.|
|isIncomplete||This returns whether the supplied ingredients is empty or if any |
When in practice, these will be decoded from a
RecipeSerializer and stored in a map with a key of the
RecipeType category it belongs to. The recipe itself is dynamically registered to the
RecipeManager for each decoded instance.
RecipeType is another interface; however, it does not store any data itself. It is simply a non-forge wrapped registry object which represents the category the
Recipe implementation belongs to. As such, it only needs to be supplied its registry name. This can be registered via
RecipeType#register by either static initialization or deferring the registration until
RecipeSerializer is an interface which takes some JSON and transforms it into a
Recipe. It is also responsible for syncing the recipe data to the client. This is a forge wrapped registry object; however, since it is an interface, there is a bit more work needed for ease of implementation. This is because
IForgeRegistryEntry which has three abstract methods. For ease of use, the implementation should extends
ForgeRegistryEntry<RecipeSerializer<?>> such that you do not need to implement these methods yourself.
There are only three methods within
RecipeSerializer, but they all must be implemented:
|fromJson||This decodes a |
|toNetwork||This encodes a |
|fromNetwork||This decodes a |
This can be registered like any other forge wrapped registry object. The registry name supplied will represent the
type supplied in the recipe JSON.
Once that is done, you can create a recipe JSON within
data/<modid>/recipes/<path>.json and set the
type field, along with any other data, to that handled by your serializer.
Custom recipes can also be generated by the
RecipeProvider or as your own implementation. For simplicity purposes, this will be addressed as if one was creating a recipe within
For a recipe to be consumed by the data provider, whatever builder that is implemented must result in a
All finished recipes must implement the following methods:
|getId||This gets the id of the recipe. When serialized, the recipe will be saved to |
|getType||This gets the |
|serializeRecipeData||This encodes the data into the supplied JSON object. The data supplied should be the same as when needed to decode via the |
|getAdvancementId||This gets the advancement id of the recipe. This will be saved to |
|serializeAdvancement||This encodes the advancement data into a JSON. If working with an |
Once done, have the consumer accept the finished recipe.