Minecraft 1.20.2 will be released in the near future, again with some 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.3 (at the time of writing) to develop mods for Minecraft 1.20.2. Players should install the latest stable version of Fabric Loader (currently 0.14.22) to play 1.20.2.

Loom 1.3

Loom 1.3 includes fixes to debugging, Kotlin 1.9.0 support, reproducable builds by default and many other smaller fixes and improvements.

New Fabric API changes

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

  • Client Tags: add support for partially synced client tags. (dexman545)
  • Particles v1: add event for preventing particle tinting for colored blocks. (Juxxel)
  • Registry Sync: add dynamic registry API (Juxxel)
  • Transitive Access Wideners: some more TAWs for block creation and block loot tables (Shnupbups)

Breaking changes and deprecations

The following deprecated APIs were removed:

  • fabric-networking-v0
  • fabric-loot-tables-v1
  • LootEntryTypeRegistry from fabric-content-registries-v0
  • CriterionRegistry from fabric-object-builder-api-v1

In addition, there were some module-wide deprecations. Deprecated modules were previously removed from the JAR used by developers by default, while still being distributed to all players. We have not previously classified this as a breaking change, since the deprecation has no effect on players.

This opt-in system, however, caused great confusion among developers during the Models API deprecation process. For this reason, deprecated modules are now available by default in the development environment.

To restore the old behavior and disable deprecated modules, use the following:

modImplementation("net.fabricmc.fabric-api:fabric-api:0.88.2+1.20.2") {
   exclude(module = "fabric-api-deprecated")
}

Breaking change: custom registry tags

Tags for static custom registry entries (created with FabricRegistryBuilder) must now be placed under /tags/registry_namespace/registry_path/, instead of /tags/registry_path/. For example, if the registry is my_mod:tiny_potato, the tags should be placed under /tags/my_mod/tiny_potato/.

This matches the behavior for custom dynamic registries that is recently introduced to Fabric API. This does not affect vanilla tags like blocks, items, or entity types.

BlockSetType and WoodType

To make the API easier to use, BlockSetTypeRegistry and WoodTypeRegistry are now replaced with builders, BlockSetTypeBuilder and WoodTypeBuilder. The registries were deprecated.

- BlockSetTypeRegistry.registerWood(TEAL_TYPE_ID);
- WoodTypeRegistry.register(TEAL_TYPE_ID, TEAL_BLOCK_SET_TYPE);
+ BlockSetTypeBuilder.copyOf(BlockSetType.OAK).build(TEAL_TYPE_ID);
+ WoodTypeBuilder.copyOf(WoodType.OAK).build(TEAL_TYPE_ID, TEAL_BLOCK_SET_TYPE);

Transfer API

Storage#exactView was deprecated for removal due to lack of use and its complexity.

Two methods from Storage moved to StorageUtil. They are: simulateInsert and simulateExtract. The methods in Storage are now deprecated for removal. This allows simulateExtract to work on StorageView.

Models API replacement

This section is currently incomplete. Please check back later!

Rendering Data Attachments replacement

This section is currently incomplete. Please check back later!

Minecraft changes

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

Networking

While the developer-facing breaking changes to Networking API were minimum, the API’s implementation was rewritten to use the new configuration stage, between login and play stages. This change hopefully resolves the longstanding issue with proxy servers.

However, some mods need to change the code to utilize the configuration stage. Here is the summary of the changes; those who work on the raw networking stack (to develop cross-platform APIs or proxies) should read the protocol proposal.

Mods that use ServerPlayNetworking, for non-configuration purposes, are likely unaffected. Mods that use ServerLoginNetworking, or implement configuration syncing/mod version checking feature, should check the note below.

  • Before 1.20.2, most of the server-client communication took place during gameplay, via ServerPlayNetworking. Mods who wanted to communicate before the player joined had to use ServerLoginNetworking.
  • In 1.20.2, after the player authenticates, but before the player joins the world (and ServerPlayerEntity instance is created), there exist a phase where the client and the server can communicate to configure each other. This is the “configuration stage”.
  • During this stage, the server executes “tasks” one by one. For example, you can define a task that queries the client’s mod version and disconnects the client if the versions differ.
  • Once all tasks are completed, the player joins the world.
  • Importantly, the code allows the server to force a player back to the configuration stage. This feature is not used in vanilla. For example, a minigame server might use this feature to move players between the lobby and the minigames.
  • Configuration stage is designed to allow mods to reliably exchange packets before the player joins. If you previously have used ServerLoginNetworking, it is highly recommended to switch to configuration stage, with ServerConfigurationNetworking.

Example of implementing one-way syncing, from server to client:

// define ConfigS2CPacket; see https://gist.github.com/apple502j/9c6b9e5e8dec37cbf6f3916472a79d57

ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
    ServerConfigurationNetworking.send(handler, new ConfigS2CPacket("data"));
});

Client side:

ClientConfigurationNetworking.registerGlobalReceiver(ConfigS2CPacket.TYPE, (packet, sender) -> {
    // do something with packet
});

Example of sending a packet to, and receiving a packet from, a client:

// define ConfigC2SPacket, which is sent as a response to S2CPacket

public record ConfigTask(String config) implements ServerPlayerConfigurationTask {
    public static final ServerPlayerConfigurationTask.Key KEY = new ServerPlayerConfigurationTask.Key("my_mod_id:my_mod_config");

    @Override
    public ServerPlayerConfigurationTask.Key getKey() {
        return KEY;
    }

    @Override
    public void sendPacket(Consumer<Packet<?>> sender) {
        sender.accept(ServerConfigurationNetworking.createS2CPacket(new ConfigS2CPacket(this.config)));
    }
}

ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
    // This if block is required! Otherwise the client gets stuck in connection screen
    // if the client cannot handle the packet.
    if (ServerConfigurationNetworking.canSend(handler, ConfigS2CPacket.TYPE)) {
        handler.addTask(new ConfigTask("config"));
    } else {
        // Optional; if you want to require certain mods, disconnect the client.
        handler.disconnect(Text.of("You need to install Tiny Potato Mod!"));
    }
});

ServerConfigurationNetworking.registerGlobalReceiver(ConfigC2SPacket.TYPE, (packet, handler, sender) -> {
    // Check packet here
    // Warning: if you do not call completeTask, the client gets stuck!
    handler.completeTask(ConfigTask.KEY);
})

Client side:

ClientConfigurationNetworking.registerGlobalReceiver(ConfigS2CPacket.TYPE, (packet, sender) -> {
    // do something with packet
    // send a response
    sender.sendPacket(new ConfigC2SPacket("response"));
});

There is one breaking change to Networking API: ClientPlayNetworking#createC2SPacket now returns Packet<ServerCommonPacketListener> instead of Packet<ServerPlayPacketListener> (and similar changes with createS2CPacket as well.)

Status effects

Status effects are now stored using string ID, instead of integer ID. Fabric does not migrate the data by itself. We expect someone to make a mod for data conversion.

This change also meant that Registry Sync module no longer has to keep track of the raw IDs of registry entries on disk. The tracker file will NOT be automatically deleted. API-wise, this means RegistryAttribute#PERSISTED is gone.

Recipes and advancements

Recipe and advancement code was refactored to use records. In addition, RecipeEntry or AdvancementEntry is used in many places where a raw recipe or advancement is used previously. As the name suggests, this is a pair of the ID and the value. If you want to get the recipe/advancement itself, call .value(). In RecipeProvider, exporter is now of type RecipeExporter, not Consumer<RecipeJsonProvider>. The usage remains the same; to output a recipe, call accept.

Recipes and ingredients are serialized to and from the disk using Codecs. Fabric API’s CustomIngredientSerializer now has codec method that returns codec, instead of read/write taking JsonObject. (read/write taking PacketByteBuf still exists.) To make the migration easier, you can use Codecs#fromJsonSerializer - though this is deprecated.

Yarn mapping received a couple of renames. Most significantly, “input” and “output” are now called “ingredient” and “result”.

Trading

A new experiment for rebalancing villager and wandering trader trades is added. While this is labeled as an experiment, it is an officially-supported feature that can be enabled in a release.

While the internal changes to villagers were minimum, there were substantial changes to how wandering trader offers are picked in the experiment.

Previously, the trades were selected in a similar way to villagers, where level 0 indicated common items and level 1 indicated rare items. The experiment replaces this with a pool-based method; there are several pools, and from each pool the game picks certain numbers of offers.

We are currently designing an API that supports the experiment. For now, the current code works for existing worlds and newly created worlds without the experiment.

GUI

Two Screen related changes: onMouseScrolled method now takes both horizontal and vertical scroll amount. Most mouses do not support horizontal scrolling, so use that feature with caution. Another change is that renderBackground method now takes mouse positions and tickDelta.

In EntryListWidget#mouseClicked, the mouse button is now automatically checked. Override isSelectButton to allow right-clicking entries to select one. The checks in individual entries are now redundant.

Texture rendering methods have changed; drawTexture calls should be replaced with drawGuiTexture. Note that since the game no longer uses atlases in GUI textures, there is no need to specify UV.

Miscellaneous changes

  • MessageDecorator is now synchronous.
  • MeleeAttackGoal#canAttack was added. This is checked inside attack method.
  • In Yarn, several classes relating to session, player reporting, and telemetry were moved to a new package, client.session.