Minecraft Modpack Making: About Datapacks - katubug/HowToModpack GitHub Wiki

Minecraft Modpack Making: About Datapacks

Finding out what's data-driven and how to change it

One of the most powerful ways to edit your modpack is through datapacks, which can be as drastic a change as mods, in the right hands (disclaimer, those do not include my hands, I am a beginner at datapacks). At a basic level, they can change recipes, drops, tags, and other behaviors, which are the things we're going to cover in this guide.

What a Datapack is

A datapack is a piece of code, stored in a .json file, which changes or defines the behavior of the game. Mod makers use them inside their mods, and so does Minecraft itself. If something is contained or altered through a datapack, it is considered "data driven" - this is opposed to things being "hard coded" into the Java code itself. If it's hard-coded, only the mod author can change it. If it's data-driven, the power is in your hands! You can create your own datapack file to override a vanilla or modded one, or add to it.

Datapacks are read in a certain order when the game loads. First, the game reads all the basic vanilla datapacks and adds those to the game. Then, it reads all the modded datapacks and adds them - and if any of those change the vanilla ones, the modded ones will override them. Finally, it reads the datapacks added by the player or modpack maker and adds them. If those change vanilla or modded datapacks, the modpack-author-added ones will override them all. This means you have the final say in how something behaves.

The Anatomy of a Datapack

Let's take a look at an example datapack for a recipe. Note that I'm adding in comments preceded by // - this actually doesn't work in JSON, I am just doing it as an example. This file is located in /kubejs/data/bakery/recipes/sandwich.json:

Example: A Shapeless Crafting Bench Recipe

{
  "replace": true,                          // This is a line I added. We'll talk about it in a moment.
  "type": "minecraft:crafting_shapeless",   // This determines what kind of recipe it is.
  "ingredients": [
    {
      "tag": "bakery:bread"          // These first few ingredients are TAGS
    },
    {
      "tag": "bakery:cooked_beef"
    },
    {
      "tag": "forge:crops/cabbage"
    },
    {
      "tag": "forge:crops/tomato"
    },
    {
      "item": "candlelight:butter"    // This ingredient is a specific ITEM
    }
  ],
  "result": {
    "item": "bakery:sandwich",       // The recipe's output
    "count": 1                       // How many of the output you get per recipe
  }
}

Let's talk about the moving parts.

  • The type. This can be minecraft:crafting_shaped, minecraft:crafting_shapeless, minecraft:stonecutting and so on - even recipe types from other mods, like farmersdelight:cooking
  • Tags vs items. In most recipes, you will want to use tags, for the best compatibility. Things like forge:wheat and minecraft:stone. But sometimes you'll want to make sure the item is only made with one specific item. Make sure you specify which in the initial part of the entry, where it says tag or item. Then put the tag or item id in the second part of the entry.
  • The output must always be a specific item. You can determine the count in this section as well.

To Replace, or not to Replace

You'll notice at the top, there's a line that says "replace": true. I will be completely honest, I am not sure when it is or is not needed. I have had examples where it was required for the datapack to work (such as Ars Nouveau's Drygmy Blacklist tag) and examples where using it prevented the datapack from working (Woodwalkers' Transform Blacklist Tag).

According to the Minecraft Wiki: replace: Optional. Whether or not the contents of this tag should completely replace tag contents from different lower priority data packs with the same resource location. When false the tag's content is appended to the contents of the higher priority data packs, instead. Defaults to false.

In general, if you are adding onto a datapack such as a tag, you'll either use "replace": false or omit the line entirely. If you are replacing a datapack such as a recipe, you'll use `"replace":true``. When in doubt, I usually include it, and remove it if the datapack isn't working for some reason.

How to Load Datapacks

The way datapacks function is that they are placed into each world file/save upon creation of the world. However, it's not feasible to ship out saves with a modpack, so you'll need a mod to automatically load (and replace, when changes are made) the datapacks in your player's saves and on their servers. It so happens that KubeJS does this natively, but you can also use mods like OpenLoader or Paxi. This guide will assume you're using KubeJS, however.

Most of your datapacks will likely be "loose" - which is to say, folders with files in them, instead of a zip with a pack.mcmeta file in it. However, perhaps you've downloaded some datapacks from other authors, and want to include those. You can place the zip directly into the /kubejs/data/ folder without unzipping it.

How To Find Existing Datapacks

Many, if not most, of the datapacks you'll want to use already exist within the .jar file of the mod you want to change. We cover this in the Intermediate Configuration section of the guide, but we'll repeat some of that information here.

To open a .jar file, you may need 7zip, although some versions of windows allow you to navigate into jars and other zips through Explorer. For this tutorial, we’ll assume 7zip. Right click on the mod you want and choose “7zip > Open Archive.” Ta-da, it was that easy.

Inside the /data/ folder will be a number of other folders. Most commonly, it will include the mod’s namespace*, /forge/, and /minecraft/ folders, and sometimes it will contain other mod namespaces, such as /create/ or /sereneseasons/ - in these cases, this is compatibility that the mod author has already crafted between the listed mods. Most of the time, you’ll ignore these modded compatibility folders, and focus on the main mod or its compatibility with Forge or Minecraft.

  • /namespace/ - This is where most of the mod’s data will be. It will contain custom recipes, tags, loot tables, advancements, and much more. You’ll usually check here first for information.
  • /forge/ - This folder generally contains tags which the mod uses to ensure wider compatibility in recipes. It can also contain loot modifiers.
  • /minecraft/ - This folder often contains tags, and information for world gen - such as what biomes X or Y can generate or spawn in. It also contains any changes the mod makes to vanilla behavior, such advancements, or such as custom drops from vanilla mobs, blocks, or other loot tables.

*A “namespace” is the ID of a mod, which the game uses to communicate with its data. It is all lowercase and only contains alphanumeric characters and underscores - so no spaces. An example of this would be “fairylights” or “easy_mob_farm.”

How to Make Datapacks that Don't Exist

Sometimes, something will be data-driven, but not have a datapack. Commonly this occurs when a block or entity doesn't have an included loot table. It could also be that you want to change something in base Minecraft, but of course it doesn't have a mod jar to dig around in. For these circumstances, I recommend using Misode's Data Pack Generators. You can select the kind of datapack you want:

Select the correct Minecraft version:

And then choose a template to work from:

You can then edit the datapack using their helpful GUI, and copy the text from the bottom right to paste into your .json file.

Maintaining Folder Structure

A major component of making datapacks work correctly is that you need to maintain the folder/file structure to be identical to the version you're overriding. If you want to override a block's drop table, and you put it in /galosphere/loot_tables/silver_ore.json, but the file you want to override is in /galosphere/loot_tables/blocks/silver_ore.json, then your datapack will have no effect. They need to perfectly match in order to properly work.

When digging through mod jars, you can easily see the correct folder structure for the files you want by looking at the URL bar in 7zip. Begin with the /data/galosphere/ part and match it to your local /kubejs/data/galosphere folders. This also applies to datapacks you've made yourself - they need to be where the game will think to look for them. Let's look at an example:

Example: Making Budding Amethyst mineable with Silk Touch

Let's say you want Budding Amethyst to drop itself when you mine it, but only if you have Silk Touch on your tool. You can achieve this several ways - you can install a mod that adds this functionality, you can add it via KubeJS, or you can make a datapack. We're going to do the third option, because that's the section of the guide we're in.

So let's start with the datapack generator we mentioned earlier. It so happens that we know of a block with the exact behavior we want - ice! So let's select blocks/ice from the template selector. It gives us the following code:

{
  "type": "minecraft:block",
  "pools": [
    {
      "rolls": 1,
      "bonus_rolls": 0,
      "entries": [
        {
          "type": "minecraft:item",
          "name": "minecraft:ice"
        }
      ],
      "conditions": [
        {
          "condition": "minecraft:match_tool",
          "predicate": {
            "enchantments": [
              {
                "enchantment": "minecraft:silk_touch",
                "levels": {
                  "min": 1
                }
              }
            ]
          }
        }
      ]
    }
  ],
  "random_sequence": "minecraft:blocks/ice"
}

Now all we need to do is change it to our liking. Let's replace the references to ice with budding_amethyst. We end up with this:

{
    "replace":true,
    "type": "minecraft:block",
    "pools": [
      {
        "rolls": 1,
        "bonus_rolls": 0,
        "entries": [
          {
            "type": "minecraft:item",
            "name": "minecraft:budding_amethyst"
          }
        ],
        "conditions": [
          {
            "condition": "minecraft:match_tool",
            "predicate": {
              "enchantments": [
                {
                  "enchantment": "minecraft:silk_touch",
                  "levels": {
                    "min": 1
                  }
                }
              ]
            }
          }
        ]
      }
    ],
    "random_sequence": "minecraft:blocks/budding_amethyst"
  }

We'll save this file in /kubejs/data/minecraft/loot_tables/blocks/budding_amethyst.json. Then, we reload our game with /reload and test mining Budding Amethyst with a Silk Touch pick! Hooray, you just saved yourself the trouble of adding a whole new mod for this function. Congrats!