Fabric for Minecraft 1.21.9
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.11 (at the time of writing) to develop mods for Minecraft 1.21.9. Players should install the latest stable version of Fabric Loader (currently 0.17.2).
Yarn Mappings
In this update, several mapping name changes were forced by changes in the vanilla class hierarchy. While a full diff may be found here, there is one major change affecting almost all mods: Entity#getWorld
was renamed to Entity#getEntityWorld
.
World Render Events
The current event suite for rendering in the world has been removed. A suitable replacement is planned asap, but not ready yet. In the meantime, please use mixins to implement what your mod needs.
Resource Loader API v1
A major rework of the resource loader API is present in the 1.21.9 version of Fabric API. This will make current functionality easier to acomplish, as well as opening doors for features like runtime resource generation in the future.
The first part of this rework has just landed with the focus being on ResourceReloader
.
Historically this API based itself on the IdentifiableResourceReloadListener
interface which allowed ResourceReloader
to both be identifiable and specify dependencies.
However this API had limits, which prevented to run before another ResourceReloader
or was difficult to use in multiloader environments.
This has been fixed with this new iteration of the API.
From now on, ResourceReloader
do not need to implement a Fabric-provided interface, instead they can be registered with an identifier directly:
- ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new CustomResourceReloader())
+ ResourceLoader.get(ResourceType.SERVER_DATA).registerReloader(Identifier.of("modid", "custom_resource_reloader"), new CustomResourceReloader());
Ordering is now specified akin to events:
ResourceLoader.get(ResourceType.SERVER_DATA).addReloaderOrdering(
Identifier.of("other", "other_reloader_to_depend"), // Triggers first
Identifier.of("modid", "custom_resource_reloader") // Triggers second
);
You can also order based on Vanilla reloaders thanks to ResourceReloaderKeys
, which provides both per-resource-reloader an identifier, and two global keys: before and after vanilla.
Thanks to 1.21.9 Vanilla changes the way to register reloaders which need registry access has been simplified, instead of using a specialized registration method, now you can get registries and feature flags in ResourceType.SERVER_DATA
reloaders via the shared state Store
:
class DataReloader implements ResourceReloader {
@Override
public CompletableFuture<Void> reload(
Store store,
Executor prepareExecutor,
Synchronizer reloadSynchronizer,
Executor applyExecutor
) {
RegistryWrapper.WrapperLookup registries = store.getOrThrow(ResourceLoader.RELOADER_REGISTRY_LOOKUP_KEY);
FeatureSet featureSet = store.getOrThrow(ResourceLoader.RELOADER_FEATURE_SET_KEY);
// Code
}
}
This change also allows for ResourceReloader
s to communicate data between them.
See #4574 for more details about what else is planned and the current progress.
Enchantments
Transitive access wideners have been added for all of the utility methods in EnchantmentHelper
and Enchantment
. We hope that this will make it easier for developers to implement custom enchantment effect components.
See #4819 for more details.
Serialization
We have added a new module for utilities related to serialization. Currently, this module includes additional methods in ReadView
and WriteView
, and provides Codec
s for some types that base vanilla doesn’t. Please feel free to suggest anything else that may be useful in this area.
See #4745 for more details.
Block Conversions
To closer align with vanilla code flow, OxidizableBlocksRegistry
now supports registering a CopperBlockSet
direclty. Simply call OxidizableBlocksRegistry.registerCopperBlockSet(set)
to register. The prior methods for block pairs are still avalible for those that prefer them.
See #4807 for more details.
StrippableBlockRegistry
now provides multiple overloads to satisfy all your stripping needs. As before, simple conversions can be registered by calling StrippableBlockRegistry.register(Block, Block)
. You may also call StrippableBlockRegistry.registerCopyState(Block, Block)
to register a stripping conversion that automatically copies all properties from the previous state, or register(Block, Block, StrippingTransformer)
to have full control over the stripping process.
See #4829 for more details.
GUI Rendering
When using a custom RenderPipeline
to render in the GUI, vanilla may assume that the VertexFormat
being used is QUADS
. Fabric now provides a way to override this behavior with RenderPipeline.Builder.withUsePipelineDrawModeForGui
.
var pipeline = RenderPipeline.builder(
snippet1,
snippet1
)
.withUsePipelineDrawModeForGui(true)
.build();
// ...
fictionalRenderInGuiInNonGuads(pipeline); // will respect the VertexFormat set in the pipeline
See #4824 for more details.
Mixin & MixinExtras
With version 0.17.0 of Fabric Loader, MixinExtras 5.0.0 and Fabric Mixin 0.16.3+mixin.0.8.7 are now bundled.
- MixinExtras 5.0.0 brings expressions, a new way to discribe mixins to java code in a syntax that mirrors the target, leading to mixins that are easier to maintain, more expressive, and potentially even more compatible with other mixins. See the release notes and wiki pages for more details.
- Fabric Mixin 0.16.3 brings many bug fixes, along with scaffolding in mixin for potential widespread performance increases.
Screen Key Events
With many breaking changes from mojang regarding keybindings, we’ve taken the opportunity to improve our events. Most parameters in the event have been consolidated into a context object known as a KeyInput
. The afterMouseX style events now also take and return a boolean
representing whether the event has been consumed. Returning true
from these events will prevent further vanilla handling.
See #4846 and #4620 for more details.
Minecraft Changes
Rendering
Almost all world rendering has been reworked to group objects with similar rendering requirements together. Most places now use OrderedRenderCommandQueue
to submit things to be drawn later, when all objects of a similar type have been rendered.
Block Entities
Block entities now use OrderedRenderCommandQueue
. The following example shows rendering text on the block using the queue.
public class TestBlockEntityRenderer implements BlockEntityRenderer<TestBlockEntity, BlockEntityRenderState> {
public TestBlockEntityRenderer(BlockEntityRendererFactory.Context context) {
}
@Override
public BlockEntityRenderState createRenderState() {
return new BlockEntityRenderState();
}
@Override
public void render(BlockEntityRenderState state, MatrixStack matrices, OrderedRenderCommandQueue queue, CameraRenderState cameraRenderState) {
queue.submitText(
matrices,
0,
0,
Text.literal("Hello, world!").asOrderedText(),
false,
TextRenderer.TextLayerType.NORMAL,
state.lightmapCoordinates,
Colors.WHITE,
0,
Colors.BLACK
);
}
}
public class TestBlockEntityRenderer implements BlockEntityRenderer<TestBlockEntity> {
public TestBlockEntityRenderer(BlockEntityRendererFactory.Context context) {
}
@Override
public void render(TestBlockEntity entity, float tickProgress, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, Vec3d cameraPos) {
MinecraftClient.getInstance().textRenderer.drawWithOutline(
Text.literal("Hello, world!").asOrderedText(),
0,
0,
Colors.WHITE,
Colors.BLACK,
matrices.peek().getPositionMatrix(),
vertexConsumers,
light
);
}
}
Particles
Particle rendering has been changed to use the same queue system as above. Many good examples for implementing custom rendering via the queue can be found in the vanilla particle classes.
Entities
Entity rendering has changed to use the same queue as shown above. Changes will be similar.
Keybinding Changes
Keybinding categories have become more structured. You could do the following before:
public class ModKeybindings {
private static final KeyBinding RANDOM_KEYBIND = new KeyBinding(
"key.test.random_keybind",
InputUtil.Type.KEYSYM,
InputUtil.UNKNOWN_KEY.getCode(),
"key.category.test.main"
);
private static void tickKeybindings(MinecraftClient client) {
while (RANDOM_KEYBIND.wasPressed()) {
System.out.println("Random keybind pressed!");
}
}
public static void init() {
ClientTickEvents.END_CLIENT_TICK.register(ModKeybindings::tickKeybindings);
}
}
Now you could do:
public class ModKeybindings {
private static final KeyBinding.Category TEST_CATEGORY = KeyBinding.Category.create(Identifier.of("test", "main"));
private static final KeyBinding RANDOM_KEYBIND = new KeyBinding(
"key.test.random_keybind",
InputUtil.Type.KEYSYM,
InputUtil.UNKNOWN_KEY.getCode(),
TEST_CATEGORY
);
// ...
}
Each category may only be registered once; Registering a category twice, or two categories with the same id will lead to an exception.
When Fabric API is installed, mod-provided categories will be sorted by alphabetically according to their identifiers, first by namespace, and then by path. All vanilla categories will remain in their natural order.
Debug Text API
It is now possible to register debug HUD entries to be added to the debug (F3) overlay. The debug overlay is now also available outside of a world. Use DebugHudEntries
to register entries to be rendered as follows:
DebugHudEntries.register(
Identifier.of("test", "example"),
new DebugHudEntry() {
@Override
public void render(
DebugHudLines lines,
@Nullable World world,
@Nullable WorldChunk clientChunk,
@Nullable WorldChunk chunk
) {
if (world != null) lines.addLine("Example in-world line :)");
else lines.addLine("Example out-of-world line :(");
}
@Override
public boolean canShow(boolean reducedDebugInfo) {
// return false if your debug text
// is not applicable with reduced debug info
return true;
}
}
);
Misc
MacOS
MinecraftClient.IS_SYSTEM_MAC
has been replaced by SystemKeycodes.IS_MAC_OS
.