Minecraft 1.21 is expected to be released on June 13, with some significant changes affecting mod makers.

It’s been only a month and a half since we last released a blogpost. Yet, this update is nothing small - some changes are likely to affect most, if not all, mods.

As always, we ask all players to be patient, and give mod developers time to update to this new version. We ask everyone kindly not to pester them. We also recommend all players to make a backup of their worlds.

Here is a list of all major modder-facing changes in this version. Note that all code references are using Yarn mappings; modders using alternative mappings may need to use different names.

Fabric changes

Developers should use Loom 1.6 (at the time of writing) to develop mods for Minecraft 1.21. Players should install the latest stable version of Fabric Loader (currently 0.15.11).

New Fabric API changes

With the help of many contributors, Fabric API has received some new features since the last update blog post:

  • Conventional Tags: Default English Translations for c namespaced tags (TelepathicGrunt)
  • Data Generation: Support extending dynamic registries in datagen (SquidDev)
  • Item API: Add API to modify default item components (modmuss50)
  • Resource Conditions: Support loading a single resource condition instead of array (Apollo)
  • Resource Conditions: Registry resource conditions (SquidDev)
  • Removed Herobrine (Tiny Potato)

Tag translation

Mods are now strongly encouraged to provide an English translation for item tags it provides. For example, the translation for test:potatoes should be declared like this:

{
    "tag.item.test.potatoes": "Potatoes"
}

Although these are not used by the API itself, other mods (such as recipe viewers) rely on them for proper display.

Breaking Changes

Note: breaking changes related to vanilla changes are addressed separately below.

One deprecated module was removed: fabric-models-v0.

In Item API, EquipmentSlotProvider#getPreferredEquipmentSlot is now passed a LivingEntity. Implementations are expected to check themselves whether the slot is available, using entity.canUseSlot(EquipmentSlot). FabricItem#getAttributeModifiers was removed; use Default Components API instead.

Registry Sync now freezes the registry at an earlier point. This does not affect mods that register things in their ModInitializers, but might affect more complex mods. Make sure to register everything during ModInitializer.

Minecraft changes

This version brought many breaking changes, including data-driven Enchantments. Data pack structures also changed, using singular nouns (instead of plurals) in many places.

Identifier

Identifier constructor is now protected. Use the static method of or ofVanilla to construct Identifier. Replacing new Identifier with Identifier.of using text editors or IDEs should be sufficient.

- var id = new Identifier("example", "foo_bar");
- var id2 = new Identifier("test:single_argument");
- var creeper = new Identifier("minecraft", "creeper");
- @Nullable var customId = Identifier.of("namespace", unsanitizedInput);
+ var id = Identifier.of("example", "foo_bar");
+ var id2 = Identifier.of("test:single_argument");
+ var creeper = Identifier.ofVanilla("creeper"); // very slightly improved performance
+ @Nullable var customId = Identifier.tryParse("namespace", unsanitizedInput);

Pro tip: make a method to construct Identifier for your mod’s namespace; this can be imported later using import static to shorten your code.

public static Identifier id(String path) {
    return Identifier.of("mod_namespace", path);
}

Enchantments

Enchantments are now data-driven. This means that they are no longer hardcoded - instead, they’re part of data packs. Consequentially, there are several important changes in how mods handle enchantments.

  • Generally, enchantments either “do something” (like causing an explosion), or “modify a value” (like attack damage). Example: Instead of checking if the player has Thorns and applying damage to the attacker, a mod should now call EnchantmentHelper#onTargetDamaged which should automatically handle this. Example 2: Instead of checking the level of Knockback, a mod should now call EnchantmentHelper#modifyKnockback with the original knockback.
  • Sometimes, a code needs to check if an item has certain enchantments. Because enchantments are no longer hard-coded, this is done using enchantment tags. Example 3: Breaking a beehive with Silk Touch prevents bees from getting released. Previously, this was done by checking for the Silk Touch enchantment. Now, this is done by calling EnchantmentHelper#hasAnyEnchantmentsIn(stack, EnchantmentTags#PREVENTS_BEE_SPAWNS_WHEN_MINING).

Enchantment Code Structures

An Enchantment is, well, an enchantment. Because it is now defined by a dynamic registry, it is often handled as RegistryEntry<Enchantment>. These can be obtained from the dynamic registry, i.e. world.getRegistryManager() or the registryLookup passed to various methods.

An enchantment has effect components, which is a map of effect types to list of effects. Effect types are pre-defined keys that enchantments can define behaviors for. For example, an enchantment may want to add “multiply by 2” behavior for DAMAGE effect type, or “explode” behavior for POST_ATTACK effect type. These are defined in EnchantmentEffectComponentTypes.

The values of effect components are EnchantmentEffectEntry. This is a pair of the effect and an optional condition. LootCondition is reused for this purpose.

Enchantment effects can be categorized into three categories:

  • EnchantmentValueEffect. When given a value, the effect calculates the modified value. Examples include Protection (which modifies DAMAGE_PROTECTION), Lure (FISHING_LUCK_BONUS), Unbreaking (ITEM_DAMAGE), and Multishot (PROJECTILE_COUNT).
  • EnchantmentEntityEffect. This effect does something when triggered. Examples include Thorns and Channeling (POST_ATTACK) and Flame (PROJECTILE_SPAWNED).
  • AttributeEnchantmentEffect, which adds an attribute to the entity. Unlike entity effects, attributes persist until the enchantment becomes inapplicable.

EnchantmentLocationBasedEffect is an interface implemented by EnchantmentEntityEffect and AttributeEnchantmentEffect. In addition, some enchantment effect types simply require effective values to be specified directly - like CROSSBOW_CHARGING_SOUNDS, which is a list of sounds. Some effects, like PREVENT_ARMOR_CHANGE, cannot be configured further.

Using Vanilla Enchantments

EnchantmentHelper methods can be used to handle vanilla enchantments.

// Example of modifying a value
float newDamage = EnchantmentHelper.getDamage(
    world,
    stack,
    attackTarget,
    damageSource,
    baseDamage
);

// For values with no base value (such as Protection)
float protection = EnchantmentHelper.getProtectionAmount(
    world,
    user,
    damageSource
);

// Entity-based effect
EnchantmentHelper.onTargetDamaged(
    world,
    attackTarget,
    damageSource,
    weapon
);

// Checking if an ItemStack is enchanted with certain enchantments
if (EnchantmentHelper.hasAnyEnchantmentsIn(
    stack,
    EnchantmentTags.PREVENTS_BEE_SPAWNS_WHEN_MINING
)) {
    return;
}

// Checking for nonconfigurable effects
// e.g. checking for Curse of Binding
if (EnchantmentHelper.hasAnyEnchantmentsWith(
    stack,
    EnchantmentEffectComponentTypes.PREVENT_ARMOR_CHANGE
)) {
    return;
}

Adding a new Enchantment

Adding a new enchantment is now done by writing JSON files. You can use Data Generation to generate enchantments easily.

An enchantment can perform many tasks, like applying attributes, replacing blocks or summoning an entity. However, not all things are possible. To add a custom effect type, a new effect class must be created and its codec registered. An effect type must exist in both the client and the server.

Enchantment tags are used to specify exclusive enchantments (e.g. Fortune and Silk Touch) and where the enchantments can be obtained. Make sure to add your enchantments to those tags.

The following event callbacks now pass RegistryEntry<Enchantment> instead of Enchantment:

  • EnchantmentEvents.AllowEnchanting#allowEnchanting
  • FabricItem#canBeEnchantedWith
  • FabricItemStack#canBeEnchantedWith

EnchantmentContext was reworked. Previously, a context was one of RANDOM_ENCHANTMENT, ANVIL, ENCHANT_COMMAND, or LOOT_RANDOM_ENCHANTMENT. To clarify its purpose, they are now one of ACCEPTABLE (e.g. anvils) or PRIMARY (e.g. enchanting tables). For example, a helmet can be enchanted with Thorns using anvils but not in enchanting tables, because only chestplates are PRIMARY in case of Thorns.

Teleportation

Both intra-dimensional and cross-dimensional teleportation can now be performed using Entity#teleportTo (previously known as moveToWorld). TeleportTarget now contains the destination world and position.

FabricDimensions was removed, because its only API, teleport, is effectively superseded with Entity#teleportTo.

Data Pack Paths

Data packs now use singular nouns in the following paths:

  • Tag subdirectories such as tags/blocks, tags/entity_types, tags/functions. They are now tags/block, tags/entity_type, and tags/function, respectively. The tags directory itself remains plural.
  • Advancements (previously advancements, now advancement).
  • Functions (functions to function).
  • Item modifiers/loot functions (item_modifiers to item_modifier).
  • Loot tables (loot_tables to loot_table).
  • Predicates/loot conditions (predicates to predicate).
  • Recipes (recipes to recipe).
  • Structures (structures to structure).

In Fabric API news, GameTest structure directories are now singular as well; instead of gametest/structures, they are now placed in gametest/structure.

Rendering Changes

In the Fabric Rendering API, HudRenderCallback now passes RenderTickCounter instead of tickDelta. tickDelta can be obtained from the counter. WorldRenderContext’s tickDelta and limitTime methods are also replaced with tickCounter.

There are some other, significant internal refactors to rendering. For example, BufferBuilder#next() was removed, and the methods for beginning or ending the building process has changed:

- BufferBuilder builder = tessellator.getBuffer();
- builder.begin(...);
+ BufferBuilder builder = tessellator.begin(...);
- builder.vertex(...).texture(...).color(...).next();
+ builder.vertex(...).texture(...).color(...)
- tessellator.draw();
+ BufferRenderer.drawWithGlobalProgram(builder.end());

Miscellaneous

  • FabricCodecProvider now has a new constructor that takes RegistryKey instead of directory name. This can be used with, for example, item modifier providers. The old constructor can still be used for non-registered codecs.