Migration Notes

Minecraft 26.2

These migration notes cover only the changes made to Balm APIs. For Minecraft and mod-loader specific migrations, check out their respective release announcements or primers.

Overview

Minecraft 26.2 is a fairly small update, most noticeably featuring some more rendering-related method renames, the introduction of BlockItemId, and the sealing of Holder, resulting in some changes to Balm's DeferredBlock and DeferredItem

⚠️ Known Issues

Some features are not available on Forge

For a full list of features unsupported on Forge, check the new Loader Parity overview.

If you depend on any of those features, my recommendation is to drop support for Forge. Balm won't be putting major efforts into feature parity with Forge.

Breaking Changes

Mojang has sealed Holder in this release, which makes it harder for other parties to create their own subclasses of it. This makes sense, as there is places where Holders are used as identity, meaning it is not a safe construct to subclass even if the Holder would be otherwise equal.

In the past, Balm matched NeoForge by introducing its own DeferredHolder class and a workaround mixin for the equality checks, however, sticking to this solution would mean actively fighting Mojang on this design choice.

Therefore, DeferredHolder has been removed from Balm and DeferredItem and DeferredBlock no longer implement Holder.

This results in the following changes for uses of asHolder():

  • On Fabric, nothing changes, because registry registrations are instant. asHolder() can be called instantly.
  • On NeoForge, the last I've read was that DeferredHolder was going to be kept, so similarly asHolder() can likely still be called instantly.
  • On Forge, there was never a native deferred Holder subclass, which means asHolder() was using Balm's now removed DeferredHolder wrapper. This means, in 26.2+, asHolder() on Forge must only be called after registries have loaded. It is recommended to switch to any of the other methods provided by the *Registration instance.

Additionally, any Vanilla method that previously accepted a DeferredItem or DeferredBlock via Holder<Item> or Holder<Block> will no longer accept it directly. Use .asItem() or .asBlock() to resolve it when passing them.

See here for an example of this change.

RenderCallback.BlockHighlight is now fired during extraction rather than rendering

This makes the event follow Mojang's changes towards separate extraction/submit rendering stages, and makes it more specialized towards its purpose.

See here for an example of how to update to the new event signature.

LivingEntityCallback.Death.Before is now consistent between loaders and receives damage parameter

Prior to this, Fabric would call LivingEntityCallback.Death.Before before totem activation, while Neo/Forge would fire it after totems were already checked.

Fabric's approach seems more correct. Totems prevent death, therefore they should only be checked after a death has not been cancelled.

To resolve this inconsistency, Balm now uses a supplemental event instead of relying on NeoForge's native event.

This also includes a new float damage parameter added to its signature.

FluidTank now supports multiple slots

FluidTank is now slot-aware so Balm can expose multi-tank implementations correctly to Fabric, Forge and NeoForge's native fluid APIs.

If you implement FluidTank yourself, update every method to receive a slot parameter and add getSlotCount():

-int fill(Fluid fluid, int maxFill, boolean simulate);
+int fill(int slot, Fluid fluid, int maxFill, boolean simulate);

-int drain(Fluid fluid, int maxDrain, boolean simulate);
+int drain(int slot, Fluid fluid, int maxDrain, boolean simulate);

-Fluid getFluid();
+Fluid getFluid(int slot);

-void setFluid(Fluid fluid, int amount);
+void setFluid(int slot, Fluid fluid, int amount);

-int getAmount();
+int getAmount(int slot);

-void setAmount(int amount);
+void setAmount(int slot, int amount);

-int getCapacity();
+int getCapacity(int slot);

-boolean canDrain(Fluid fluid);
+boolean canDrain(int slot, Fluid fluid);

-boolean canFill(Fluid fluid);
+boolean canFill(int slot, Fluid fluid);

-boolean isEmpty();
+boolean isEmpty(int slot);
+
+int getSlotCount();

For existing single-tank implementations, return 1 from getSlotCount() and treat slot 0 as the only valid slot. This is also what DefaultFluidTank does.

Call sites using a FluidTank directly must now pass a slot, usually 0 for existing single-tank code:

-tank.fill(fluid, amount, simulate);
+tank.fill(0, fluid, amount, simulate);

-tank.getFluid();
+tank.getFluid(0);

Removed deprecated BalmBlockRegistrar#enableBlockDescriptionPrefixForItems workaround

This is now the default, as it should be for block items. You can use Item.Properties#useItemDescriptionPrefix to use an item. lang key instead on a case-by-base basis.

Removed deprecated BalmLootModifier overload without lootTableId

Use the version that takes a lootTableId instead. See here for an example.

Removed deprecated UnpackedLootTableHolder

This is no longer needed or useful now that BalmLootModifier receives a lootTableId parameter. See here for an example.

Removed deprecated Supplier-based #withItems and #withDefaultItems overloads

In order for Balm to set appropriate defaults without overwriting any consumer-made changes, you must now use the Function or BiFunction overloads that receive an existing Item.Properties instance.

You don't have to use this instance if you need a separate one (e.g. a copy of an existing block's properties), but if you create your own instance, you must fully set it up yourself (including a setId call that Balm already does on the prepared instance).

Removed deprecated SimpleRecipeTransferRegistration in favor of IdentifiableRecipeTypeTransferRegistration

The previous implementation was unusable for anything other than custom recipe types also registered through the same layer. The new implementation correctly supports existing recipe types, so something like registrar.registerRecipeTransferHandler(PortableCraftingMenu.class, ModMenus.portableCrafting, RecipeType.CRAFTING, 1, 9, 10, 36); can now work correctly.

Removed deprecated prefix/suffix methods in DiscriminatedBlocks and DiscriminatedItems

The new variants prefixWith, suffixWith and surroundWith are a lot more clear about their intent and easier to use in general.

See here for an example of this change.

Other Changes

  • Added DeferredBlock#asBlockItemId, useful for item tag generators
  • Added DeferredBlock#asResourceKey and DeferredItem#asResourceKey