Minecraft 1.20 - the Trails and Tales Update - releases on June 7th 2023, again with a number of changes that impact many mods.

As usual, we ask players to be patient, and give mod developers time to update to this new version.

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.2 (at the time of writing) to develop for Minecraft 1.20. Players should install the latest stable version of Fabric Loader (currently 0.14.19) to play 1.20.

Loom 1.2

Loom 1.2 is a small update focused on game library handling improvements and official Windows ARM native support. Loom 1.2 requires Gradle 8.1.

New Fabric API changes

Fabric API added many features since the last update blog post:

  • Data Generation: add codec data provider. (ErrorCraft)
  • Interaction Events: provide fake player API. (Technici4n)
  • Networking: add packet-based API similar to vanilla networking system. See the gist for migration tips. Note that the existing API is not deprecated in any way. (apple502j)
  • Renderer API: add material inspection and glint material property, remove texture indices. (PepperCode1, Technici4n)
  • Transfer API: add slotted storage and non-empty iterator. (Technici4n)
  • Other small additions to various APIs, such as Convention Tag and Object Builder

Breaking changes and deprecations

Aside from changes related to Minecraft code changes (which are discussed below), no breaking change to the API was introduced.

During one of the code refactors, one bug related to the Registry Sync was “accidentally” fixed. The bug previously allowed clients with Fabric API (but without content mods) to join a server with modded contents, despite the mod being missing on the client. The refactor was merged to both 1.19.4 and 1.20 snapshot branches; however, the bug was reintroduced to 1.19.4 only to prevent unexpected breakage. If you have previously relied on the bug, make sure to have users install the mods on the client.

A few rarely-used APIs within EventFactory were deprecated. They were intended to be used for profiling events; however, the standard Java profiler provides a better result.

  • invalidate method was terminally deprecated, to be removed in future versions.
  • isProfilingEnabled method was deprecated and now always returns false.
  • getHandlerName method was also deprecated.

Fabric Rendering API deprecations

Many methods of the renderer API had an int spriteIndex parameter that was always 0. It was removed, and the naming of some methods was improved in the process. The boolean disableAo material property was also replaced by a more flexible TriState for ambientOcclusion.

The old methods are now deprecated, here are a few examples of migrating away from them:

  MaterialFinder finder = ...;
- materialFinder.blendMode(0, <blend mode>);
+ materialFinder.blendMode(<blend mode>);
- materialFinder.disableAo(0, true);
+ materialFinder.ambientOcclusion(TriState.FALSE);

  QuadView quadView = ...;
- quadView.spriteColor(0, -1, -1, -1, -1);
+ quadView.color(-1, -1, -1, -1);

The full list of renames can be found in the pull request description.

The new methods do not have a default implementation in 1.20. This does not affect mods using the Renderer API, however third-party renderers such as Canvas or Indium must add support for them.

Minecraft changes

Minecraft 1.20 introduces some breaking changes to major developer-facing APIs.

Material (or the lack thereof)

Material was a class that indicated the type of blocks, such as plant or metal. This class no longer exists:

  • To specify the properties, such as the map color and whether the block is replaceable, use the block settings. One note on the new solid and notSolid block settings: These two methods override the default solid block check. If a block is not specified as solid or not solid explicitly, then the game checks whether the shape’s average side length is more than 0.73 OR whether the block is taller than 1 block. If either is true, the block is solid.
  • To get those values, use BlockState methods. (You may need to refactor your code if your method takes Block instead of BlockState.)
  • To check if a block is of a certain type, use block tags or instanceof.

Related changes:

  • BlockSetType now has canOpenByHand field, used by doors and trapdoors.
  • Block#canMobSpawnInside now takes a BlockState. Previously it took no arguments.
  • BlockState#blocksMovement, which returns true for all solid blocks except cobwebs and bamboo shoots, was added.
  • Block.Settings#of method was renamed to create. Fabric API’s FabricBlockSettings#of was renamed as well, and the old method was deprecated.
  • Fabric API: FabricMaterialBuilder was removed.
- new Block(AbstractBlock.Settings.of(Material.PLANT, MapColor.GREEN))
+ new Block(AbstractBlock.Settings.create().mapColor(MapColor.GREEN).pistonBehavior(PistonBehavior.DESTROY))

World changes

One of the biggest, yet subtle changes in this version is that Entity#world is now private. Use Entity#getWorld or Entity#getServerWorld:

- World world = player.world;
+ World world = player.getWorld();

RedstoneView provides redstone-related methods that previously existed in World.

Screen and rendering changes

DrawableHelper, a utility that is frequently used by subclassing it, turned into an object passed to rendering methods: DrawContext. This replaces the various MatrixStack matrices parameters. You usually do not need to construct one yourself.

- public void render(MatrixStack matrices) {
-   RenderSystem.setShaderTexture(0, TEXTURE);
-   drawTexture(matrices, ...);
+ public void render(DrawContext context) {
+   context.drawTexture(TEXTURE, ...); // texture ID is now specified here

The following new methods were added to DrawContext, replacing other methods in various places:

  • setShaderColor (wraps RenderSystem one, which still exists)
  • drawTextWithShadow (replaces TextRenderer#drawWithShadow)
  • drawText (wraps TextRenderer#draw)
  • drawTextWrapped
  • drawItem
  • drawItemWithoutEntity
  • drawItemInSlot
  • drawItemTooltip
  • drawTooltip (replaces Screen#renderTooltip)
  • drawHoverEvent

In addition, various rendering methods now take DrawContext instead of MatrixStack. If you still somehow need the matrix stack, you can use DrawContext#getMatrices.

Fabric API changes related to this:

  • Rendering API’s HudRenderCallback, Screen API’s ScreenEvents.beforeRender, and ScreenEvents.afterRender now take DrawContext instead of MatrixStack.
  • Screens.getItemRenderer is removed. This can be easily replaced with MinecraftClient#getItemRenderer, although this is usually not necessary.

And one unrelated change: Screen#passEvents was removed, therefore screens can no longer pass events.

Item group changes

ItemGroups are now registered in a registry. This means that the vanilla game tracks them using identifiers, just like blocks and items - and it is registered in the same way as those.

- public static final ItemGroup ITEM_GROUP = FabricItemGroup.builder(new Identifier(MOD_ID, "example"))
-   .icon(() -> new ItemStack(Items.DIAMOND_PICKAXE))
-   .displayName(Text.translatable("example-mod.group.example"))
-   .build();
+ public static final RegistryKey<ItemGroup> ITEM_GROUP = RegistryKey.of(RegistryKeys.ITEM_GROUP, new Identifier(MOD_ID, "example"));
+ @Override
+ public void onInitialize() {
+   Registry.register(Registries.ITEM_GROUP, ITEM_GROUP, FabricItemGroup.builder()
+       .icon(() -> new ItemStack(Items.DIAMOND_PICKAXE))
+       .displayName(Text.translatable("example-mod.group.example"))
+       .build()); // build() no longer registers by itself
+ }

Notice that FabricItemGroup#builder takes no arguments now, since the ID is assigned by the registry. Note, you must now call displayName manually or it will crash!

ItemGroupEvents.modifyEntriesEvent will now take RegistryKey<ItemGroup> instead of ItemGroup or Identifier. Note that vanilla ItemGroups fields are now registry keys, so code using those just needs to be recompiled. And with the game now providing the identifier, IdentifiableItemGroup is removed.

Finally, one small breaking change to Data Generation: FabricLanguageProvider.TranslationBuilder#add will now take RegistryKey<ItemGroup> instead of ItemGroup.

ItemStack changes

Code modifying ItemStack should now use the combined methods (such as copyWithCount) instead of 2 method calls (such as copy + setCount) to properly handle empty stacks. ItemStack#copyAndEmpty was added to move the contents of one stack into a new copy.

- ItemStack copy = stack.copy();
- copy.setCount(1);
+ ItemStack copy = stack.copyWithCount(1);

A couple of equality methods in ItemStack were removed:

  • ItemStack#isItemEqual: use the static areItemsEqual() method instead.
  • ItemStack#areNbtEqual: use canCombine (which also checks for items) or compare NBT yourself instead.

Loot changes

Predicates and item modifiers (or, in legacy terms, loot conditions and loot functions) are now managed by LootManager. They are identified using LootDataKey, similar to RegistryKey. getTable is renamed to getLootTable, and getElement can be used to get loot tables, predicates, or item modifiers.

LootContext.Builder is moved to LootContextParameterSet.Builder and its parameter method was renamed to add. The putDrop method was renamed to addDynamicDrop. The getNullable method was renamed to getOptional for consistency. The loot inventory seed is now given via supplyInventory.

Other small, but noteworthy changes

  • Fabric Content Registries’ VillagerPlantableRegistry was replaced with ItemTags.VILLAGER_PLANTABLE_SEEDS (data pack tags).
  • Herobrine was removed.
  • RecipeProvider#offerWoolDyeingRecipe and similar methods were merged to offerDyeableRecipes (which also offers re-coloring).
  • ServerCommandSource#sendFeedback now takes Supplier<Text> instead of Text for performance reasons. Add () -> and it should be good to go.