Fabric for Minecraft 1.21
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 ModInitializer
s, 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 callEnchantmentHelper#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 modifiesDAMAGE_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.
Enchantment-related Fabric API changes
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 nowtags/block
,tags/entity_type
, andtags/function
, respectively. Thetags
directory itself remains plural. - Advancements (previously
advancements
, nowadvancement
). - Functions (
functions
tofunction
). - Item modifiers/loot functions (
item_modifiers
toitem_modifier
). - Loot tables (
loot_tables
toloot_table
). - Predicates/loot conditions (
predicates
topredicate
). - Recipes (
recipes
torecipe
). - Structures (
structures
tostructure
).
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 takesRegistryKey
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.