A new version of Minecraft is coming soon with some changes that affect most mod makers. As always, we ask all players to be patient, and give mod developers time to update to this new version. We kindly ask everyone not to pester them. We also recommend all players make backups of their worlds.

Here is a list of several 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.10 (at the time of writing) to develop mods for Minecraft 1.21.5. Players should install the latest stable version of Fabric Loader (currently 0.16.10).

Loom 1.10

Loom 1.10 requires Gradle 8.12, and comes with performance improvements and enhanced testing setup support. You can checkout the new Loom documentation here.

Deprecations and removals

In Content Registries, VillagerInteractionsRegistries#registerGiftLootTable overload that takes an Identifier was removed. This method was previously deprecated.

In Object Builder, two deprecated classes, namely VillagerProfessionBuilder and VillagerTypeHelper, have been removed. Use the VillagerProfession constructor and VillagerType#create instead. TradeOfferHelper#refreshOffers, which had been deprecated and did nothing, was also removed.

HudRenderCallback has been deprecated in favor of newly added HudLayerRegistrationCallback.

Breaking changes

When a mod creates a new dynamic registry, the data pack JSON files for the registry must now be placed inside namespaced directories. For example, if the new registry is example:potato_variant, the file for variant test:tiny will be placed in data/test/example/potato_variant/tiny.json.

BiomeModificationContext#addSpawn has a new parameter, weight. This was previously part of SpawnEntry.

TradeOfferHelper now takes a RegistryKey of the profession instead of VillagerProfession. Wandering trader trades must now be added via the builder, which was previously used for the Rebalance experiment. (The rebalanced trades are now used in all worlds.)

New Fabric API changes

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

  • New module: Client Game Test API, which can be used to automatically test client rendering and GUI (Earthcomputer)
  • New module: Fabric Tag API, which currently handles tag aliases (Juxxel)
  • Convention Tags: Sync remaining c tags with NeoForge (TelepathicGrunt)
  • Convention Tags: Add c:flowers, c:flowers/tall, and c:flowers/small block and item tags (TelepathicGrunt)
  • Convention Tags: Add TagKey for c:tools/wrench (TelepathicGrunt)
  • Convention Tags: Convention Drink Tags (TheDeathlyCow)
  • Convention Tags: Add Pumpkin Block and Item Tags (JT122406)
  • Data Generation: Add vararg helper methods for multi-tag support in FabricTagBuilder (Starexify)
  • Data Generation: Add FabricEntityLootTableProvider (Antikyth)
  • Item API: Add a method for overriding modelId in item settings (Patbox)
  • Item API: Add contains method to FabricComponentMapBuilder (TheDeathlyCow)
  • Item Group API: Change Creative Buttons Texture (matthewperiut)
  • Item Group API: Use page up/down to change creative inventory pages (modmuss50)
  • Model Loading API: Allow retrieving model loading plugins (PepperCode1)
  • Networking API: Add ServerPlayNetworking.reconfigure (modmuss50)
  • Object Builder: Allow setting canPotentiallyExecuteCommands in builders (PepperCode1)
  • Recipe API: Add getAllMatches and getAllOfType methods to ServerRecipeManager (Patbox)
  • Registry Sync: Add RegistryAttribute#OPTIONAL that can be used to not disconnect clients lacking an entire registry; note that optional registry values are still unsupported (modmuss50)
  • Registry Sync: Registry aliasing (Syst3ms)
  • Rendering: Add SpecialBlockRendererRegistry (PepperCode1)
  • Rendering: Add HUD Render Events (kevinthegreat1)
  • Resource Loader: Implement builtin mod resource/data pack sorting (Apollo)

Tag and registry aliases

The new tag and registry alias APIs allow for mods to seemlessly migrate their tags and registry aliases to new names. Thanks to Juuxel and Syst3ms respectively for implementing these new APIs.

Registries.BLOCK.addAlias(Identifier.of("my_mod", "old"), Registries.BLOCK.get(Identifier.of("my_mod", "new")));

Registry aliases are really simple; with the code above, any access to the old ID in the registry is redirected to the new ID. This allows worlds to be upgraded to use the new ID; for example, a block with the old ID becomes the one with the new ID.

Tag aliases are defined in data packs like tags. For example, a block tag alias group for fences would be located at data/my_mod/fabric/tag_aliases/block/fences.json with the contents:

{
  "tags": [
    "minecraft:fences",
    "c:fences"
  ]
}

Client GameTest

For a long time, Fabric has had an internal client test framework that was used for testing that Fabric API was working correctly on the client. In a series of PRs, Earthcomputer has worked to expand and expose this framework to mod developers. The new experimental API has the following features:

  • World creation API, for specifying options used to generate the test world.
  • Screenshot API, with support for comparing against golden images.
  • Input API, to simulate a user interacting with the game.
  • Advanced threading setup, to make the tests more repoducible.
  • Network synchronization, to ensure packets are handled consistently.
  • Herobrine removal, to bring peace to the Kingdom of Tiny Potato.

For more information checkout the full documentation here.

HUD Render Events

Fabric API 0.116.0 added HudLayerRegistrationCallback event, providing full control over the HUD rendering process. The new API assigns idenftifiers to each layer that can be used to specify where things will be rendered. The new API also allows replacing or removing existing layers. A big thanks to kevinthegreat1 and many other people for making this happen!

Minecraft changes

NBT

Significant changes to NBT handling code have been made.

NbtCompound methods now return Optional instead of the value. If the key is not in the compound or if the value is not of the correct type, an empty optional is now returned instead of that type’s default value.

- int value = nbt.getInt("value");
+ Optional<Integer> value = nbt.getInt("value"); // not OptionalInt

For primitives (numbers and strings), instead of checking for the existence of a key and handling a fallback, the fallback can now be passed to get methods:

- int value;
- if (nbt.contains("value", NbtElement.NUMBER_TYPE)) {
-   value = nbt.getInt("value");
- } else {
-   value = 1000;
- }
+ int value = nbt.getInt("value", 1000);

Also note that NbtElement#NUMBER_TYPE and type-aware contains have been removed. There is no fallback method for getIntArray, getLongArray, and getByteArray. (Use Optional#orElse.)

For getCompound and getList methods, methods with the previous behavior (returning empty objects) are provided with OrEmpty suffix.

- NbtCompound config = nbt.getCompound("config");
+ NbtCompound config = nbt.getCompoundOrEmpty("config");

Finally, for those who want to encode/decode codec-based values (such as Identifier) stored in a NbtCompound field, new methods simplify the process:

NbtCompound nbt = new NbtCompound();
nbt.put("Id", Identifier.CODEC, id);
// for reading
Optional<Identifier> id = nbt.get("Id", Identifier.CODEC);

And if there is a codec for the whole object:

NbtCompound nbt = new NbtCompound();
// have to use the RegistryOps since an item is a registry entry
nbt.copyFromCodec(ItemStack.MAP_CODEC, wrapperLookup.getOps(NbtOps.INSTANCE), stack);
// for reading
Optional<ItemStack> stack = nbt.decode(ItemStack.MAP_CODEC, wrapperLookup.getOps(NbtOps.INSTANCE));

Typed arrays (ByteArray, IntArray, and LongArray) are no longer Lists. They can be converted to a list using .stream().toList(), but this is usually not necessary. NbtList can now contain values of differing types, but this should be a transparent change unless you work with binary data.

GameTest

In Minecraft 25w03a, Mojang totally refactored the vanilla testing framework, exposing it to datapack developers. Unfortunately, the new API is a little bit cumbersome for mod developers. Fabric API now provides its own @GameTest annotation that functions similarly to the old one. The options in the new Fabric-provided @GameTest annotation directly map the vanilla data-driven options, removing the need to have a JSON file for each test function. Data driven tests will still work if you wish to use the vanilla system.

Miscellaneous

  • DataPool has been replaced with Pool.