Fabric for Minecraft 1.20.2
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
fromfabric-content-registries-v0
CriterionRegistry
fromfabric-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 useServerLoginNetworking
. - 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, withServerConfigurationNetworking
.
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 insideattack
method.- In Yarn, several classes relating to session, player reporting, and telemetry were moved to a new package,
client.session
.