Fabric for Minecraft 1.21.2
Minecraft 1.21.2, the “Bundles of Bravery” drop, is expected to release soon. Mojang has recently announced that they will release these “drops” throughout the year. Like with other updates, this drop contains some significant changes affecting mod makers.
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.8 (at the time of writing) to develop mods for Minecraft 1.21.2. Players should install the latest stable version of Fabric Loader (currently 0.16.7).
Fabric Loader changes
Fabric Loader 0.16.0 was released some time ago. Most notably, this release updates the bundled MixinExtras to 0.4.0.
In addition, Fabric Loader 0.16.7 added support for Java 23. However, you should continue to use Java 21 for mod development and gameplay.
MixinExtras additions
The following new features have been added:
@WrapMethod, which allows wrapping the entire method@Cancellable, which allows cancellation from all injectors.namespaceparameter in@Shareto share values between Mixins
Loom 1.8
Loom 1.8 adds support for configuration caches, Gradle 8.10, and other changes and fixes.
Configuration cache is an opt-in Gradle performance enhancement feature that will be enabled by default in Gradle 9. Because this may break existing setup, we only recommend using this if you are familiar with Gradle buildscripts. To support this change, multi-project optimization has been removed.
New Fabric API changes
With the help of many contributors, Fabric API has received some new features since the last update blog post:
- Convention Tags: Add more tags (TelepathicGrunt, Juuz)
- Crash Report Info: Print the full stack trace from the dedicated server watchdog (TelepathicGrunt)
- Entity Events: Add after damage event (TheDeathlyCow)
- Item API: Modify enchantment and add component map builder extensions (TheDeathlyCow)
- Item Group API: Add API to control creative inventory screen (modmuss50)
- Loot API: Loot API v3 (modmuss50)
- Networking: Add MinecraftClient/Server instances to networking contexts (modmuss50)
- Networking: Add
ClientConfigurationConnectionEvents#START(modmuss50) - Networking: Add access to ClientConfigurationNetworkHandler in context (Earthcomputer)
- Object Builder: Add an API to add additional supported blocks to block entity types (modmuss50)
- Renderer API: Add
ShadeMode(PepperCode1) - Renderer API: Quads overloads for joml interfaces (SHsuperCM)
- Resource Conditions: Allow conditions inside pack overlays (Apollo)
- Resource Loader: Add API to create reload listeners with a registry lookup (modmuss50)
- Removed Herobrine (Tiny Potato)
- Transfer API: Crafter support (modmuss50)
- Transfer API: Add
ItemVariant#withComponentChanges(BasiqueEvangelist)
Breaking changes and deprecations
Note: breaking changes related to vanilla changes are addressed separately below.
One deprecated module was removed: fabric-renderer-registries-v1.
In Resource Condition, the test method now takes RegistryOps.RegistryInfoGetter. Tags can no longer have resource conditions. Similarly, in Resource Loader, ResourceReloadListenerKeys#TAGS was removed.
The following deprecations were made since the last blogpost:
- Content Registries:
VillagerInteractionRegistries#registerCollectablewas deprecated. Use the vanilla tag,minecraft:villager_picks_up, instead. - Loot API: Loot API v2 was deprecated. Use Loot API v3, which passes registry contexts, instead.
- Networking:
ClientConfigurationConnectionEvents#READYwas renamed toCOMPLETE. (This was done during the 1.21 update cycle, but after we published the last blogpost.)
Minecraft changes
ServerWorld parameters
Many methods that must be executed on the server side only now require ServerWorld to be passed explicitly. Do not cast just to fix a type error; many events and overridden method are still called on both the client side and the server side.
Instead, wrap all logic that must be run only on the server side with if (world instanceof ServerWorld serverWorld). The serverWorld can be passed to those methods.
For example, Entity#damage now requires ServerWorld, and is therefore only called on the server side. There is an additional method, clientDamage, which is called only on the client side.
Block and item settings
Minecraft 1.21.2 uses registry keys to pre-compute certain block or item settings. This allows referencing them in default item components. For example, rather than computing a default name based on an item’s registry key inside getName method if the minecraft:item_name component is not present, every item now contains the minecraft:item_name component.
Because of this, you must manually set registry keys in the settings of every block and item. Failure to do so will result in crashes such as:
NullPointerException: Block id not setNullPointerException: Item id not set
Both settings classes provide a simple method which should be called for every item or block with their respective registry key:
Identifier id = Identifier.of("mymod", "test_item");
RegistryKey<Item> key = RegistryKey.of(RegistryKeys.ITEM, id);
Item.Settings settings = new Item.Settings()
// If your item is based on a block
.useBlockPrefixedTranslationKey()
.registryKey(key);
Registry.register(Registries.ITEM, key, new Item(settings));
Identifier id = Identifier.of("mymod", "test_block");
RegistryKey<Block> key = RegistryKey.of(RegistryKeys.BLOCK, id);
Block.Settings settings = new Block.Settings()
.registryKey(key);
Registry.register(Registries.BLOCK, key, new Block(settings));
Make sure to call the Item.Settings#useBlockPrefixedTranslationKey method for block items so that they use the block.<namespace>.<path> translation key format.
This change affects the following traits:
- Item names (
minecraft:item_namecomponent) - Item models (
minecraft:item_modelcomponent) - Block models
- Block loot table key
Some traits, such as the cooldown group of items with cooldowns, still dynamically look up a registry key, but this pattern should be avoided as much as possible.
Block#getLootTableKey now returns Optional<RegistryKey>. A block without a corresponding loot table now returns an empty optional instead of the minecraft:empty loot table registry key.
In Fabric API, the previously-deprecated FabricBlockSettings class was removed. Use the vanilla AbstractBlock.Settings class instead.
Furnace fuels
Furnace fuels are now registered through an event, which allows access to new parameters:
- FuelRegistry.INSTANCE.add(ModItems.TEST_ITEM, 50);
+ FuelRegistryEvents.BUILD.register((builder, context) -> {
+ builder.add(ModItems.TEST_ITEM, context.baseSmeltTime() / 4);
+ });
The base smelt time can be used to express a fuel’s smelt time in terms of a ratio. The default base smelt time is 200 ticks, or 10 seconds.
Entities
EntityType received a registry key change similar to the block and item one described above. When building an EntityType, pass a RegistryKey for the entity type. The no-argument version of the EntityType.Builder#build method, injected by FabricEntityTypeBuilder, has been removed. For example:
Identifier id = Identifier.of("mymod", "test_entity_type");
RegistryKey<EntityType<?>> key = RegistryKey.of(RegistryKeys.ENTITY_TYPE, id);
EntityType<TestEntity> entityType = EntityType.Builder.<TestEntity>create(TestEntity::new, SpawnGroup.MISC)
.build(key);
Registry.register(Registries.ENTITY_TYPE, key, entityType);
Mob conversion (such as zombie villager curing or slimes splitting on death) was overhauled. The exact behavior of conversion is now handled by methods implemented by values of the EntityConversionType enum. When using the MobEntity#convertTo method to convert a mob, an EntityConversionContext is now required. This context is also now passed to Fabric API’s ServerLivingEntityEvents#MOB_CONVERSION event.
When creating an entity, a spawn reason is now required:
- Entity pig = EntityType.PIG.create(overworld);
+ Entity pig = EntityType.PIG.create(overworld, SpawnReason.SPAWN_ITEM_USE);
The prefixes of attributes in EntityAttributes, like GENERIC, have been dropped. This matches a change in the identifiers of attributes:
- public static final RegistryEntry<EntityAttribute> GENERIC_ATTACK_KNOCKBACK = register(
- "generic.attack_knockback",
- new ClampedEntityAttribute("attribute.name.generic.attack_knockback", 0, 0, 5)
);
+ public static final RegistryEntry<EntityAttribute> ATTACK_KNOCKBACK = register(
"attack_knockback",
new ClampedEntityAttribute("attribute.name.attack_knockback", 0, 0, 5)
);
Data generation
A recipe provider must now override getRecipeGenerator instead of generate. The new method takes the registries and exporter, and returns a new instance of RecipeGenerator that actually generates the recipes.
- public void generate(RecipeExporter exporter) {
- offerPlanksRecipe2(exporter, SIMPLE_BLOCK, ItemTags.ACACIA_LOGS, 1);
+ protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup registries, RecipeExporter exporter) {
+ return new RecipeGenerator(registries, exporter) {
+ @Override
+ public void generate() {
+ offerPlanksRecipe2(SIMPLE_BLOCK, ItemTags.ACACIA_LOGS, 1);
ActionResult
Previously, action results have been represented in several ways, including the ActionResult, ItemActionResult, and TypedActionResult classes. In Minecraft 1.21.2, these classes have been merged into a single ActionResult class, which provides direct replacements for the previous methods for all three classes:
| Old | New |
|---|---|
ActionResult.success(world.isClient()) |
ActionResult.SUCCESS |
TypedActionResult.pass(stack) |
ActionResult.PASS |
TypedActionResult.fail(stack) |
ActionResult.FAIL |
TypedActionResult.success(stack, world.isClient()) |
ActionResult.SUCCESS |
TypedActionResult.consume(stack) |
ActionResult.CONSUME |
If an action replaces the hand stack with another instance of ItemStack, then it should be marked with the ActionResult#withNewHandStack method. For example, an Item#use implementation that replaces the hand stack might be:
public ActionResult use(World world, PlayerEntity user, Hand hand) {
ItemStack stack = user.getStackInHand(hand);
if (stack.getCount() > 16) {
ItemStack newStack = new ItemStack(Items.BLAZE_ROD, 16);
return ActionResult.SUCCESS.withNewHandStack(newStack);
}
return ActionResult.PASS;
}
On the other hand, if the ItemStack instance for the hand stack is the same as the one provided to an interaction method such as Item#use, the ActionResult#withNewHandStack method should not be called.
This change affects all places where TypedActionResult and ItemActionResult were previously used in Minecraft’s code. In Fabric API, the UseItemCallback event in the Events Interaction module now returns ActionResult instead of TypedActionResult<ItemStack>.
Item components
Because elytra behavior is now controlled by a new item component, minecraft:glider, FabricElytraItem was removed. Add the component to your elytra item instead.
Block entities
In Minecraft 1.21.1 (released in August), a change was made that required mods to add all supported blocks to block entities. For example, a mod with a new sign block must add the block to BlockEntityType#SIGN:
BlockEntityType.SIGN.addSupportedBlock(ModBlocks.TEAL_SIGN);
In Minecraft 1.21.2, block entity types are no longer constructed using builders. Therefore, FabricBlockEntityType.Builder was removed, while FabricBlockEntityTypeBuilder is no longer deprecated.
Registries
Registry now implements RegistryEntryLookup. This resulted in several name changes. To query a RegistryEntry from RegistryKey, use getOptional or getOrThrow. (Same applies to RegistryEntryList from TagKey.) If you want to query the registry value, use getValueOrThrow. See the table below for all changes.
| Old | New |
|---|---|
getEntry |
getOptional |
entryOf |
getOrThrow |
getOrThrow |
getValueOrThrow |
getOrEmpty |
getOptionalValue |
getEntryList |
getOptional |
Similarly, DynamicRegistryManager#get and RegistryWrapper.WrapperLookup#getWrapperOrThrow has been renamed to getOrThrow. getOptionalWrapper was renamed tp getOptional.
Rendering
Entity rendering
Entity rendering has received a large refactor that decouples the Entity instance from its respective rendering calls. EntityRenderer now has an additional type parameter, S extends EntityRenderState, which represents a ‘render state’, which is a mutable class containing only the parameters of the entity that are used in rendering.
Previously, renderer methods accessed the entity instance directly. However, accessing entity information now happens in three steps.
First, the EntityRenderer#createRenderState method is called to construct an instance of S for the given entity with placeholder values:
public T createRenderState() {
return new T();
}
Next, the EntityRenderer#updateRenderState method performs the operation of copying information from the entity to its render state:
public void updateRenderState(T entity, S state, float tickDelta) {
super.updateRenderState(entity, state, tickDelta);
// Example: entity has an 'is saddled' field
state.isSaddled = entity.isSaddled();
}
Finally, the EntityRenderer#render method (as well as other renderer methods) accesses information from the entity’s render state:
public void render(S state, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) {
super.render(state, matrices, vertexConsumers, light);
if (state.isSaddled) {
// Render the saddle
}
}
Shaders
In Fabric Rendering API, CoreShaderRegistrationCallback was removed. Vanilla resource pack now allows loading modded core shaders.
Other changes
WorldRenderer methods for rendering certain vertices (like a box) were transferred to VertexRendering.
Recipes
The recipe system was reworked. To summarize:
- Recipes are now identified using
RegistryKey<? extends Recipe>, notIdentifier. Recipeand the recipe ID is now server-side only. It cannot be accessed from the client.- Instead, clients receive
RecipeDisplayEntry. It contains all information needed to display and run the recipe book. Recipes are identified using an integer (NetworkRecipeId) on the client. A recipe can have multiple display entries. IngredientPlacementcontrols the placement of ingredients on the crafting table, whileRecipeMatcherhandles recipe matching.
Ingredient is now internally a list of items, not item stacks.
In Fabric API, CustomIngredientSerializer#getCodec: allowEmpty param removed, empty now always disallowed.
Biome
Water cave carvers (which, before 1.18, were seen in seabeds) were removed, along with GenerationStep.Carver.
In Fabric Biome API, biome modification methods like addCarver and removeCarver no longer takes GenerationStep.Carver argument.
Profiler
The shared profiler is now accessed using Profilers#get.
- world.getProfiler().push("tinyPotato");
+ Profilers.get().push("tinyPotato");
Loot tables
LootTables#EMPTY was removed. The game uses Optional to mark the lack of loot tables.
Some loot context-related logics are now shared with recipes, resulting in name changes. LootContextParameter is now ContextParameter, and LootContextType is now ContextType.
Yarn renames
This section only affects those using the Yarn mapping.
As part of regular Yarn maintainace, the following changes were made. These should be a simple replacement:
ModelTransformationModeis transferred tonet.minecraft.item.RealmsLoadingWidgetis renamed toLoadingWidgetand transferred toclient.gui.widget.- Several
BrainandTask-related names: see the pull request for details. registryLookupFutureis renamed toregistriesFuturein several locations.Codecs#NONNEGATIVE_INTis renamed toNON_NEGATIVE_INT.QUATERNIONFandVECTOR3Fare renamed toQUATERNION_FandVECTOR_3F.Screen#initTabNavigationis renamed torefreshWidgetPositions.ComponentMapImplis renamed toMergedComponentMap.ContainerComponentModifier#createis renamed toapply.ItemStack#encodeis renamed totoNbt.
Fabric