Minecraft 1.21.11
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
In Minecraft 1.21.11, Balm's registry methods and event system have received major refactors. Almost all packages have been reorganized to be more consistent and align with Minecraft's package structure.
The new registry methods severely clean up the registration calls as well as their internal implementations, matching the style that Minecraft uses to register its own blocks, adjusted to the needs of mod loaders and modder convenience.
The new event system comes with better performance and better integration into mod loaders. For example, events now support phases on Fabric (priority equivalent on Neo/Forge), and the system supports firing back into the native mod loader bus for selected events, which can be used to e.g. notify other non-Balm mods about native events like block breaks.
The new consistent package structure makes it easier to distinguish API classes from internal implementations and avoids ambiguities between Vanilla and mod loader concepts (e.g. there are two concepts of Permissions, one based on Vanilla's new 1.21.11 permission system, and one based on the Bukkit-esque permissio ecosystem, which are now clearly distinguished).
This was the first refactor Balm has undergone since it started as a roughly put together library for my own mods four years ago. These changes resulted in many of Balm's internals being cleaned up and modernized to match the current modding ecosystem.
⚠️ Known Issues
/balm export icons is not yet functional
The command has not yet been updated to Minecraft's rendering changes.
Breaking Changes
Balm no longer has an additional Minecraft version suffix in its versions
Instead of 21.11.1+1.21.11, the version is now just 21.11.1.
You also no longer need to include Kuma in your build manually, as Balm correctly defines it as an exposed dependency now.
Many net.blay09.mods.balm packages have moved
I recommend cleaning out the broken imports and just reimporting using your IDE.
- import net.blay09.mods.balm.Balm;
+ import net.blay09.mods.balm.api.Balm;
- import net.blay09.mods.balm.api.module.BalmModule;
+ import net.blay09.mods.balm.platform.module.BalmModule;
- import net.blay09.mods.balm.api.client.module.BalmClientModule;
+ import net.blay09.mods.balm.client.platform.module.BalmClientModule;
- import net.blay09.mods.balm.api.block.entity.OnLoadHandler;
+ import net.blay09.mods.balm.world.level.block.entity.OnLoadHandler;
- import net.blay09.mods.balm.api.loot.BalmLootModifier;
+ import net.blay09.mods.balm.world.level.storage.loot.BalmLootModifier;
- import net.blay09.mods.balm.api.container.BalmContainerProvider;
+ import net.blay09.mods.balm.world.BalmContainerProvider;
- import net.blay09.mods.balm.api.container.DefaultContainer;
+ import net.blay09.mods.balm.world.DefaultContainer;
- import net.blay09.mods.balm.api.container.SubContainer;
+ import net.blay09.mods.balm.world.SubContainer;
- import net.blay09.mods.balm.api.container.CombinedContainer;
+ import net.blay09.mods.balm.world.CombinedContainer;
- import net.blay09.mods.balm.api.fluid.BalmFluidTankProvider;
+ import net.blay09.mods.balm.platform.fluid.BalmFluidTankProvider;
- import net.blay09.mods.balm.api.fluid.DefaultFluidTank;
+ import net.blay09.mods.balm.platform.fluid.DefaultFluidTank;
- import net.blay09.mods.balm.api.fluid.FluidTank;
+ import net.blay09.mods.balm.platform.fluid.FluidTank;
- import net.blay09.mods.balm.api.menu.BalmMenuFactory;
+ import net.blay09.mods.balm.world.BalmMenuFactory;
- import net.blay09.mods.balm.api.menu.BalmMenuProvider;
+ import net.blay09.mods.balm.world.BalmMenuProvider;
- import net.blay09.mods.balm.common.CommonCapabilities;
+ import net.blay09.mods.balm.platform.capabilities.CommonCapabilities;
- import net.blay09.mods.balm.api.tag.BalmItemTags;
+ import net.blay09.mods.balm.tags.BalmItemTags;
- import net.blay09.mods.balm.api.energy.BalmEnergyStorageProvider;
+ import net.blay09.mods.balm.platform.energy.BalmEnergyStorageProvider;
- import net.blay09.mods.balm.api.energy.DefaultEnergyStorage;
+ import net.blay09.mods.balm.platform.energy.DefaultEnergyStorage;
- import net.blay09.mods.balm.api.energy.EnergyStorage;
+ import net.blay09.mods.balm.platform.energy.EnergyStorage;
- import net.blay09.mods.balm.api.container.ContainerUtils;
+ import net.blay09.mods.balm.world.ContainerUtils;
- import net.blay09.mods.balm.api.network.BalmNetworking;
+ import net.blay09.mods.balm.network.BalmNetworking;
- import net.blay09.mods.balm.api.command.BalmCommands;
+ import net.blay09.mods.balm.commands.BalmCommands;
- import net.blay09.mods.balm.api.config.reflection.Comment;
+ import net.blay09.mods.balm.platform.config.reflection.Comment;
- import net.blay09.mods.balm.api.config.reflection.Config;
+ import net.blay09.mods.balm.platform.config.reflection.Config;
- import net.blay09.mods.balm.api.config.reflection.NestedType;
+ import net.blay09.mods.balm.platform.config.reflection.NestedType;
- import net.blay09.mods.balm.neoforge.NeoForgeLoadContext;
+ import net.blay09.mods.balm.neoforge.platform.runtime.NeoForgeLoadContext;
Event and Registry related methods have been moved elsewhere - see below.
Many methods in Balm have been renamed
- Balm.getConfig()
+ Balm.config()
- Balm.getNetworking()
+ Balm.networking()
- Balm.getPermissions()
+ Balm.permissions()
- Balm.getHooks()
+ Balm.hooks()
Event and Registry related methods have been moved elsewhere - see below.
EmptyLoadContext has been removed in favor of loader-specific contexts
Use FabricLoadContext.INSTANCE on Fabric, and construct a NeoForgeLoadContext or ForgeLoadContext on Neo/Forge.
BalmBlockEntity has been removed in favor of BalmBlockEntityUtils
This class was a relic from the past, was misleading developers to think they were required to use it, and couldn't be used when extending existing block entity classes. Therefore it has been removed.
You can instead implement getUpdateTag and getUpdatePacket on your block entity by calling the respective BalmBlockEntityUtils method inside. You can call what used to be sync() via BalmBlockEntityUtils.sync(BlockEntity).
Once deobfuscation is gone for good, we may be able to provide an interface defaulting those methods (which isn't possible at the moment due to remapping).
The old registry methods (Balm.getBlocks() etc.) have been removed in favor of the new BalmRegistrars
See the short blog post and docs on adding content for how to use the new registrars.
DeferredObject no longer exists, you can use DeferredBlock, DeferredItem or Holder now using .asDeferredBlock() or .asHolder() etc. in registration.
Most things that used to be a Balm* class like BalmBlocks have now become BalmBlockRegistrar. Your mod's initializer will be passed a BalmRegistrars or BalmClientRegistrars instance that provides access to all registrars. BalmModule implementations have their methods called with registrars as before.
The old event system has been removed in favor of the new event mappers
Instead of Balm keeping track of its own event bus, event mappers instead take your registrations (and invocations) and map them directly to the native mod loader events (or a Balm supplemental event in cases where mod loaders do not support the event). This allows for better compatibility and avoids wrapping every event in a Balm-specific class.
These new events can be found in *Callback classes such as BlockCallback.Use. You can also search for EventMapper.createUnbound usages to find them all.
Listeners are registered directly on these event mappers via .register(<handler>) rather than using Balm.getEvents().onEvent.
Event handlers receive the callback parameters directly, i.e. there is no longer an event object.
If you're using a BalmModule, instead of defining handlers in registerEvents you can just do it in initialize() or anywhere else now.
To define your own events, you can use EventMapper.createBound() with your existing event class, and fire them using its .invoker().
Note that these events won't fire on native mod loader buses anymore. Third party mods must instead use your event mapper directly to call .register() with their handler on them.
If you do want to provide backwards compatibility or continue to have your mods available on mod-loader specific buses, you will have to use EventMapper.createUnbound() instead and then provide a mapping on each loader entrypoint using .configureMapping().
Kuma by default no longer fires screen handler events if a widget has focus
This is to avoid key mappings from being handled when the user has a widget like a text field focused.
You can override this behaviour by calling .ignoreScreenFocus() in your key mapping builder.
CustomRenderBoundingBox has been removed
This interface was not actually used anymore.