Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

MCpypack is a Python library to make the creation of Minecraft datapack easier.

In this book you will learn how to install MCpypack and how to use it properly.

We make the assumption that you already know how Python works.
Although you probably do not need to know a lot.

Installation

Warning

This guide only covers Linux Systems.

Python Version

MCpypack requires Python 3.14 or higher.
Run:

python --version

It should at least show:

Python 3.14.0

Virtual Environment

The next step is to create a new virtual environment in the directory of your project.

python -m venv venv

Then activate it.

source venv/bin/activate

Install MCpypack

Now you can install MCpypack via pip with the following command:

pip install MCpypack

Creating a new Datapack

Importing Datapacks

In order to create a new datapack you first have to import the Datapack class from MCpypack.

from MCpypack import Datapack

Creating Datapacks

Then you create a new datapack object using the Datapack class.

The Datapack class takes 5 arguments.
Out of them 3 are required and 2 are optional.

  • Required:
    • name: Name of the datapack.
    • description: Description of the datapack.
    • version: The Minecraft version the datapack should be created for.
my_pack: Datapack = Datapack(
    name="My Datapack",
    description="My first datapack",
    version="26.1"
)
  • Optional:
    • relative_icon_path: Relative path to an icon that should be used for your datapack.
    • relative_export_dir: Directory to export the datapack to. Default is export/.

Note

Using keyword arguments is suggested to avoid confusion.

In the end you have to export your datapack using the export method.

my_pack.export()

There are 2 optional parameters for this method.

  • overwrite: Whether the old version should be overwritten (True) or get a different name (False). Default is True.
  • zip: Whether a .zip should be created. Default is True.

After creating the datapack through running the code it appears in ‘./export/’ or in the custom directory you specified in relative_export_dir.

You now can use the datapack inside Minecraft, altough it does not add any new functionality to the game yet.

Whole code

from MCpypack import Datapack

my_pack: Datapack = Datapack(
    name="My Datapack",
    description="My first datapack",
    version="26.1"
)

my_pack.export()

Adding Namespaces

You learned how to create your first datapack. Now we want to add namespaces to that datapack.

Importing Namespaces

To add new namespaces to your datapack you use the Namespace class and the add_namespaces method from the Datapack class. To do this we have to also import the Namespace class from MCpypack.

from MCpypack import Datapack, Namespace

Creating Namespaces

After that we can create a new datapack with a custom name.

my_namespace: Namespace = Namespace(name="my_namespace")

my_pack.add_namespaces(my_namespace)

Note

The name is currently the only argument the class accepts, but it is still recommended to use keyword arguments here.

Of course you can create multiple namespaces.

my_first_namespace: Namespace = Namespace(name="my_first_namespace")
my_second_namespace: Namespace = Namespace(name="my_second_namespace")

my_pack.add_namespaces(
    my_first_namespace,
    my_second_namespace,
)

Note

As you may have noticed we didn’t include any whitespaces ot other special characters in the names of our namespaces. This is because Minecraft has a special set of rules what characters namespace names can include.

Overwriting Game Content

Later you may want to change certain predefined aspects of the game like recipes. To do that you have to create a new namespace named minecraft.

Whole Code

from MCpypack import Datapack, Namespace

my_pack: Datapack = Datapack(
    name="My Datapack",
    description="My first datapack",
    version="26.1"
)

my_namespace: Namespace = Namespace(name="my_namespace")
my_pack.add_namespaces(my_namespace)

my_pack.export()

Adding Recipes

In this chapter we will discover how to create custom recipes using MCpypack.

Warning

Although creating recipes is quite easy to understand we won’t go into detail about specific recipe types and what they do. To get a better understanding of this you can inform yourself using other sources.

Important

This chapter uses features which recipes depend on but which are not explained in this chapter. These featues include:

  • Item and Tag
  • ItemStack

Crafting Shapeless

The first recipe type we will have a look at is the Crafting Shapeless.

New Datapack and Namespace

Let’s first create a new datapack for our new recipes we will add.

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

We will also add a new namespace for recipes related to grass.

grass: Namespace = Namespace(
    name="grass"
)

Importing

Now we can add a CraftingShapeless recipe.
We import it from MCpypack.recipe.

from MCpypack.recipe import CraftingShapeless

Note

Using CratingShapeless also requires imports from other submodules which are:

from MCpypack.utils import ItemStack
from MCpypack.item.final import Item

New Recipe

The CraftingShapeless class takes the following required arguments.

  • name -> str: The name of the recipe.
  • ingredients -> ItemLike | list[ItemLike]: Ingredients of the recipe.
  • result -> ItemStack: Result of the recipe.

You can also parse two optional arguments.

  • group -> Group | None: String identifier for grouping recipes. Default is None.
  • category -> CategoryLike: Recipe book category. Default is Category.MISC.

It will then look like this.

grass_block: CraftingShapeless = CraftingShapeless(
    name="grass_block",
    ingredients=[Item.DIRT, Item.SHORT_GRASS],
    result=ItemStack(
        item_id=Item.GRASS_BLOCK,
    )
)

Add to Namespace

Then we just add the recipe into our namespace

grass.add_recipes(grass_block)

Note

You can also instantly add the recipe to the namespace.

grass.add_recipes(
    CraftingShapeless(
        name="grass_block",
        ingredients=[Item.DIRT, Item.SHORT_GRASS],
        result=ItemStack(
            item_id=Item.GRASS_BLOCK,
        )
    )
)

Export Datapack

Now we can export our datapack.

my_recipes.add_namespaces(grass)

my_recipes.export()

If you now add it to your Minecraft world you should be able to craft a grass block using one dirt and one short grass item regardless of how you arrange them.

Whole Code

from MCpypack import Datapack, Namespace
from MCpypack.recipe import CraftingShapeless
from MCpypack.utils import ItemStack
from MCpypack.item.final import Item

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

grass: Namespace = Namespace(
    name="grass"
)

grass.add_recipes(
    CraftingShapeless(
        name="grass_block",
        ingredients=[Item.DIRT, Item.SHORT_GRASS],
        result=ItemStack(
            item_id=Item.GRASS_BLOCK,
        )
    )
)

my_recipes.add_namespaces(grass)

my_recipes.export()

Crafting Shaped

Now we will have a look at the Crafting Shaped recipe type.

Importing

First let’s import the CraftingShaped class from MCpypack.recipe.

from MCpypack.recipe import CraftingShaped

Note

Using CratingShaped requires the same additional imports as CraftinShapeless.

New Recipe

The CraftingShaped class takes the following required arguments.

  • name -> str: The name of the recipe.
  • pattern -> list[str]: Pattern representing the crafting grid.
  • key -> dict[str, ItemLike]: All keys used in pattern and the corresponding items.
  • result -> ItemStack: Result of the recipe.

You can also parse three optional arguments.

  • group -> Group | None: String identifier for grouping recipes. Default is None.
  • category -> CategoryLike: Recipe book category. Default is Category.MISC.
  • show_notification -> bool | None: Determines if a notification is shown when unlocking the recipe. Default is None.

It will then look like this.

grass.add_recipes(
    CraftingShaped(
        name="grow_grass",
        pattern=[
            "A",
            "A",
        ],
        key={"A": Item.SHORT_GRASS},
        result=ItemStack(
            item_id=Item.TALL_GRASS,
        )
    )
)

If you now place two short grass item above each other you get one tall grass.
This recipe works even for a 2 by 2 crafting grid.

Full Grid

You can also extend it to a 3 by 3 grid.

For that we add a new namespace for mining.

mining: Namespace = Namespace(
    name="mining"
)

my_recipes.add_namespaces(mining)

Now we create a new recipe in that namespace.

mining.add_recipes(
    CraftingShaped(
        name="diamond_in_stone",
        pattern=[
            "AAA",
            "ABA",
            "AAA",
        ],
        key={
            "A": Item.STONE,
            "B": Item.DIAMOND,
        },
        result=ItemStack(
            item_id=Item.DIAMOND_ORE,
        )
    )
)

Empty Fields

If you want to leave certain fields empty you just use a space for that in the string.

mining.add_recipes(
    CraftingShaped(
        name="best_iron_pick",
        pattern=[
            "AAA",
            " B ",
            " B ",
        ],
        key={
            "A": Item.IRON_BLOCK,
            "B": Item.BLAZE_ROD,
        },
        result=ItemStack(
            item_id=Item.IRON_PICKAXE,
            components=components.ItemComponents(
                components.Unbreakable(),
                components.Enchantments({
                    Enchantment.EFFICIENCY: 10,
                })
            )
        )
    )
)

Important

For this example you also need to import the following:

from MCpypack import components
from MCpypack.item.final import Enchantment

Whole code

from MCpypack import Datapack, Namespace, components
from MCpypack.recipe import CraftingShaped
from MCpypack.utils import ItemStack
from MCpypack.item.final import Item, Enchantment

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

grass: Namespace = Namespace(
    name="grass"
)

grass.add_recipes(
    CraftingShaped(
        name="grow_grass",
        pattern=[
            "A",
            "A",
        ],
        key={"A": Item.SHORT_GRASS},
        result=ItemStack(
            item_id=Item.TALL_GRASS,
        )
    )
)

mining: Namespace = Namespace(
    name="mining"
)

mining.add_recipes(
    CraftingShaped(
        name="diamond_in_stone",
        pattern=[
            "AAA",
            "ABA",
            "AAA",
        ],
        key={
            "A": Item.STONE,
            "B": Item.DIAMOND,
        },
        result=ItemStack(
            item_id=Item.DIAMOND_ORE,
        )
    ),
    CraftingShaped(
        name="best_iron_pick",
        pattern=[
            "AAA",
            " B ",
            " B ",
        ],
        key={
            "A": Item.IRON_BLOCK,
            "B": Item.BLAZE_ROD,
        },
        result=ItemStack(
            item_id=Item.IRON_PICKAXE,
            components=components.ItemComponents(
                components.Unbreakable(),
                components.Enchantments({
                    Enchantment.EFFICIENCY: 10,
                })
            )
        )
    )
)

my_recipes.add_namespaces(grass, mining)

my_recipes.export()

Crafting Transmute

If you want to preserve all components of an ingredient you have to use the Crafting Transmute recipe type.

Importing

You also import the CraftingTransmute class from MCpypack.recipe.

from MCpypack.recipe import CraftingTransmute

New Recipe

The CraftingTransmute class takes the following required arguments.

  • name -> str: The name of the recipe.
  • input -> ItemLike: Item whose components will be copied.
  • material -> ItemLike: Additional item for the recipe.
  • result -> ItemStack: Result of the recipe.

You can also parse two optional arguments.

  • group -> Group | None: String identifier for grouping recipes. Default is None.
  • category -> CategoryLike: Recipe book category. Default is Category.MISC.

It will then look like this.

mining.add_recipes(
    CraftingTransmute(
        name="shimmer",
        input=Item.DIAMOND_BLOCK,
        material=Item.AMETHYST_SHARD,
        result=ItemStack(
            item_id=Item.DIAMOND,
            components=components.ItemComponents(
                components.EnchantmentGlintOverride(True)
            )
        )
    )
)

Important

You need to import the following for this example.

from MCpypack import components

You now are able to use a diamond block and an amethyst shard to craft a diamond that shimmers and also copy the data of the diamond block.

You can also use it to keep enchantments on tools.

mining.add_recipes(
    CraftingTransmute(
        name="always_copper",
        input=Tag.ITEM.PICKAXES,
        material=Item.COPPER_BLOCK,
        result=ItemStack(
            item_id=Item.COPPER_PICKAXE,
        )
    )
)

Important

You need to import the following for this example.

from MCpypack.item.final import Tag

You can now upgrade every pickaxe to a copper pickaxe.

Note

You cannot insert a copper pickaxe as Minecraft does not allow to result to be the same as the input in CraftingTransmute recipes.

Whole code

from MCpypack import Datapack, Namespace, components
from MCpypack.recipe import CraftingTransmute
from MCpypack.utils import ItemStack
from MCpypack.item.final import Item, Tag

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

mining: Namespace = Namespace(
    name="mining"
)

mining.add_recipes(
    CraftingTransmute(
        name="shimmer",
        input=Item.DIAMOND_BLOCK,
        material=Item.AMETHYST_SHARD,
        result=ItemStack(
            item_id=Item.DIAMOND,
            components=components.ItemComponents(
                components.EnchantmentGlintOverride(True)
            )
        )
    ),
    CraftingTransmute(
        name="always_copper",
        input=Tag.ITEM.PICKAXES,
        material=Item.COPPER_BLOCK,
        result=ItemStack(
            item_id=Item.COPPER_PICKAXE,
        )
    )
)

my_recipes.add_namespaces(mining)

my_recipes.export()

Crafting Decorated Pot

Next we will cover the Crafting Decorated Pot recipe type. It copies the four ingredients and saves them as the decorated pot component.

Importing

We import the CraftingDecoratedPot class from MCpypack.recipe.

from MCpypack.recipe import CraftingDecoratedPot

New Recipe

The CraftingDecoratedPot class takes the following required arguments.

  • name -> str: The name of the recipe.
  • left -> ItemLike: Left ingredient of the recipe.
  • richt -> ItemLike: Right ingredient of the recipe.
  • front -> ItemLike: Front ingredient of the recipe.
  • back -> ItemLike: Back ingredient of the recipe.
  • result -> ItemStack: Result of the recipe.

left, right, front, and back are the positions of the items. In a 3 by 3 crafting grid it would look like this:

" B " -> B = back
"L R" -> L = left; R = right
" F " -> F = front

It will then look like this.

luxury.add_recipes(
    CraftingDecoratedPot(
        name="gold_inside_diamond",
        left=Item.GOLD_INGOT,
        right=Item.GOLD_INGOT,
        back=Item.GOLD_INGOT,
        front=Item.GOLD_INGOT,
        result=ItemStack(
            item_id=Item.DIAMOND
        )
    )
)

Important

Don’t forget to create the luxury namespace.

Whole Code

from MCpypack import Datapack, Namespace
from MCpypack.recipe import CraftingDecoratedPot
from MCpypack.utils import ItemStack
from MCpypack.item.final import Item

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

luxury: Namespace = Namespace(
    name="luxury"
)

luxury.add_recipes(
    CraftingDecoratedPot(
        name="gold_inside_diamond",
        left=Item.GOLD_INGOT,
        right=Item.GOLD_INGOT,
        back=Item.GOLD_INGOT,
        front=Item.GOLD_INGOT,
        result=ItemStack(
            item_id=Item.DIAMOND
        )
    )
)

my_recipes.add_namespaces(luxury)

my_recipes.export()

Campfire Cooking

Now we will have a look at the Campfire Cooking recipe type. It is the only recipe type for the campfire.

Importing

We import the CampfireCooking class from MCpypack.recipe.

from MCpypack.recipe import CampfireCooking

New Recipe

The CampfireCooking class takes the following required arguments.

  • name -> str: The name of the recipe.
  • ingredient -> ItemLike: Ingredient of the recipe.
  • result -> ItemStack: Result of the recipe.

Warning

In versions before 26.1 the count field was not allowed for the result.

You can also parse two optional arguments.

  • cookingtime -> Time: Cookingtime in real-life time values. Default is None.
  • experience -> Experience: The output experience of the recipe. Default is None.

It will then look like this.

CampfireCooking(
    name="edible_gold",
    ingredient=Item.GOLD_BLOCK,
    result=ItemStack(
        item_id=Item.GOLD_INGOT,
        count=2,
        components=components.ItemComponents(
            components.Consumable(),
            components.Food(
                nutrition=20,
                saturation=20.0,
                can_always_eat=True,
            )
        )
    ),
    cookingtime=Time(Seconds(90)),
    experience=25,
)

Now we are able to put gold blocks onto a campfire and as a result we get two edible pieces of gold ingots, which are a very strong source of food.

Whole Code

from MCpypack import Datapack, Namespace, Seconds, components, Time
from MCpypack.item.final import Item
from MCpypack.recipe import CampfireCooking
from MCpypack.utils import ItemStack

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

luxury: Namespace = Namespace(
    name="luxury"
)

luxury.add_recipes(
    CampfireCooking(
        name="edible_gold",
        ingredient=Item.GOLD_BLOCK,
        result=ItemStack(
            item_id=Item.GOLD_INGOT,
            count=2,
            components=components.ItemComponents(
                components.Consumable(),
                components.Food(
                    nutrition=20,
                    saturation=20.0,
                    can_always_eat=True,
                )
            )
        ),
        cookingtime=Time(Seconds(90)),
        experience=25,
    )
)

my_recipes.add_namespaces(luxury)

my_recipes.export()

Stonecutting

Next we will cover the Stonecutting recipe type. It is the only recipe type for the stonecutter.

Importing

We import the Stonecutting class from MCpypack.recipe.

from MCpypack.recipe import Stonecutting

New Recipe

The Stonecutting class takes the following required arguments.

  • name -> str: The name of the recipe.
  • ingredient -> ItemLike: Ingredient of the recipe.
  • result -> ItemStack: Result of the recipe.

The Stonecutting class does not take any optional arguments.

It will look like this.

Stonecutting(
    name="oak_stairs",
    ingredient=Item.OAK_PLANKS,
    result=ItemStack(
        item_id=Item.OAK_STAIRS,
    )
)

Now we can use a stonecutter to turn one oak plank into one oak stair.

Whole Code

from MCpypack import Datapack, ItemStack, Namespace
from MCpypack.item.final import Item
from MCpypack.recipe import Stonecutting

my_recipes: Datapack = Datapack(
    name="My Recipes",
    description="Adding missing recipes to make the world a better place.",
    version="26.1",
)

carpenter: Namespace = Namespace(
    name="carpenter"
)

carpenter.add_recipes(
    Stonecutting(
        name="oak_stairs",
        ingredient=Item.OAK_PLANKS,
        result=ItemStack(
            item_id=Item.OAK_STAIRS,
        )
    )
)

my_recipes.add_namespaces(carpenter)

my_recipes.export()