It’s only been two months since we published the last article on Minecraft updates, but here we are. Minecraft 1.20.3 is to be released in the near future with some changes affecting mod makers.

As usual, we ask players to be patient, and give mod developers time to update to this new version. Given the rather short interval between updates, some authors may choose to have a rest and skip some versions; we ask everyone kindly not to pester them.

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

Fabric is about to celebrate its 5th birthday on December 10th! As we approach this milestone, we’re thrilled to share that our team is actively working on introducing exciting new features and enhancements. The following sections outline some of these advancements.

Developers should use Loom 1.4 (at the time of writing) to develop mods for Minecraft 1.20.3. Players should install the latest stable version of Fabric Loader (currently 0.14.25), or the 0.15 beta if feeling adventurous (see below).

Loader 0.15

Fabric Loader 0.15 has been released recently, which features built-in support for LlamaLad7’s MixinExtras library. This version is currently in beta. Should you discover compatibility issues with mods that already bundle MixinExtras, please file a bug report.

MixinExtras is a companion library to Mixin, providing new annotations to write your mixins in more expressive and compatible ways. It also features an easier way of specifying local captures. Due to its usefulness, MixinExtras is currently being Jar-in-Jar’d by a large amount of mods, increasing modpack sizes. With Fabric Loader bundling it now, all mods can use the library without needing to worry about distribution.

Please note that Fabric Loader 0.15 went through a number of internal changes, notably the transition from TMP to Mapping-IO and improvements to the lazy mapping loading, which reduce memory consumption. These changes do not affect most users, but mods that depend on unsupported, internal code might break with this update.

Loom 1.4

Loom 1.4 includes enhancement for decompilers, support for deprecated modules in fabricApi.module (which allows dependencies on individual Fabric API modules), and many more bug fixes and performance improvements. Loom 1.4 requires Gradle 8.3 or later.

Loom now includes built-in support for the Vineflower decompiler. It’s a fork of Fernflower, but with drastically improved output quality compared to the original. CFR remains the default decompiler for the time being. To decompile with Vineflower, use genSourcesWithVineflower instead of genSources.

A new DSL has been introduced to make configuring data generation with best practices easier. This DSL automatically creates a run configuration and optional source set for data generation while also excluding the cache from the built JAR:

fabricApi {
    configureDataGeneration()
}

New Fabric API changes

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

  • Data Generation: add a method to register custom keys with priorities (ErrorCraft)
  • Loot API: add LootTableEvents.LOADED (LLytho)

In addition to those new features, this update also contains performance improvements for PacketType-based networking. It now skips the serialization entirely in singleplayer, and the packet is now serialized on the network thread, not the main thread. Thanks to deirn for making these improvements!

Breaking changes in 1.20.3

BlockSetTypeRegistry and WoodTypeRegistry of the Object Builder API, previously deprecated, were removed. Please use the builders provided in the same API.

The following APIs in the Transfer API module, all previously deprecated, were removed.

  • ContainerItemContext#withInitial
  • Storage#simulateInsert, simulateExtract, exactView

Minecraft changes

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

Block codecs

Mojang now uses codecs to serialize blocks - specifically, instances of a particular type of block. You might need to implement the getCodec method in your Blocks.

However, the block codecs are currently unused. This means that you can return null or throw UnsupportedOperationException in getCodec method.

That being said, it is still recommended to familiarize yourself with using codecs, to future-proof your code and prevent potential porting pains in the future.

Text

Text also got a codec, and now is serialized using it (including when interacting with Gson). Over the network the text is now sent as an NBT.

  • Important change: Text#translatable now expects all arguments to be numbers, boolean, strings, or other Texts. To pass things like BlockPos or Identifier, which were previously implicitly converted to strings, use stringifiedTranslatable.
  • For those who have previously used Codecs.TEXT or STRINGIFIED_TEXT, it’s time to switch to TextCodecs.CODEC or TextCodecs.STRINGIFIED_CODEC.
  • Text.Serializer inner class was split into Serializer (to use as a Gson adapter) and Serialization (contains static methods). The methods were also renamed; toJson is now toJsonString to clarify return value.
  • Style.Serializer is gone; it is now Style.Codecs and holds the codec only.
  • In related news, PacketByteBuf#readUnlimitedText was added; this reads an infinite amount of text with no size limit. Most S2C packets now use this.

Ticks

The /tick command from the Carpet mod arrived in the vanilla game. This change affects both the server and the client. To support this functionality in your mod:

  • Use getTickManager().shouldTick() to check if things should tick in your START_WORLD_TICK/END_WORLD_TICK/START_SERVER_TICK/END_SERVER_TICK event.
  • TickManager can be obtained from MinecraftServer#getTickManager or World#getTickManager (works with ClientWorld as well).
  • Use shouldSkipTick for checking if an entity should be ticked (beware, it is the inverse of shouldTick).
  • Note: You do not need to call these checks inside Entity/BlockEntity tick methods or events. Only call inside server/world tick events.

It is also now possible for the game to run faster or slower than 50 MSPT intentionally (as opposed to through lag). Call TickManager#getMillisPerTick() to determine the intended MSPT.

Blocks

  • AbstractBlock#randomTick no longer calls scheduledTick.
  • BlockSetType received new fields specifying pressure plate activation conditions and button interactions with projectiles/Breezes.
  • ColoredFallingBlock was added, replacing GravelBlock and SandBlock.
  • PowderSnowCauldronBlock was removed; LeveledCauldronBlock handles the logic now.
  • TransparentBlock was split into TranslucentBlock, which renders in a translucent way (like slime and ice blocks), and TransparentBlock, which extends TranslucentBlock and is used by grates and glass blocks. AbstractGlassBlock and GlassBlock were removed.
  • FernBlock is renamed to ShortPlantBlock.
  • SaplingGenerator is now in block package, and individual generator classes were removed and replaced with fields.
  • AbstractBlock#onStateReplaced implementations for block entities were consolidated to ItemScatterer#onStateReplaced. Make sure to call super AFTER calling ItemScatterer.
  • LootableContainerBlockEntity logics were split into LootableInventory. checkLootInteraction was renamed to generateLoot. LootableContainerBlockEntity still implements LootableInventory, but this allows other block entities like decorated pots to share the logic.

Entities

  • Boats, minecarts, and other player-constructed vehicles should now extend VehicleEntity. This provides the wobble effect seen when hitting that entity.
  • Various non-living entities (including projectiles and TNT) received copyFrom override to copy the owner.
  • PersistentProjectileEntity and its subclasses now take the ItemStack in the constructor, representing the item stack form that players can pick up. getItemStack returns the stack as-is, while asItemStack returns a copy (potentially reflecting the changes like potion override).
  • SpawnReason got two methods: isAnySpawner (spawner & trial spawner) and isTrialSpawner. canSpawn methods should check isAnySpawner, and make the entity exempt from biome/height requirements imposed for natural spawn.

Client

  • PopupScreen was added. This screen, unlike other screens, renders on top of an existing screen. The popup can include a message, an image, and buttons (such as “Open Link”). To create an instance, use Builder.
  • CheckboxWidget is now built using a builder.
  • EntryListWidget no longer takes the bottom coordinate in the constructor. It also actually became a Widget.
  • ClickableWidget#render was made final to prevent overrides of that method from not calling super.render and preventing the tooltip from rendering. Subclasses must override renderWidget instead.

Misc

  • CommandManager#execute methods no longer return an integer return value. To get the return value, use ServerCommandSource#withReturnValueConsumer.
  • RecipeProvider methods received some changes. Some JSON builders and generateCookingRecipes now require passing the constructor of recipe class, like CampfireCookingRecipe::new. Other builders no longer require the serializer.
  • EntityType#HEROBRINE was removed. (Wait, was this ever added in the first place?)