NeoForge 21.0 for Minecraft 1.21

It’s that time of the year again! Mojang has just released a new Minecraft major version: 1.21, followed by the first beta release of NeoForge 21.0: 21.0.0-beta.

Previous changes

Before diving into the changes brought by 1.21, we would like to quickly go through the changes made to NeoForge since 1.20.1, for those that haven’t updated all the way to 1.20.6. You will also find the respective blog posts for each version, outlining important Vanilla changes:

The Vanilla 1.21 changes

Now, let’s get into the most important changes in Vanilla.

First of all, the ResourceLocation constructors were made private, and replaced with static factory methods:

  • ResourceLocation#fromNamespaceAndPath: takes in the namespace and the path of the resource location. This is functionally equivalent to the ResourceLocation(String, String) constructor in 1.20.6 and below. Accompanied by #tryBuild which returns null instead of throwing if the parameters aren’t valid.
  • ResourceLocation#parse: takes in a resource location string composed of a namespace and path, separated by :. This method is functionally equivalent to the ResourceLocation(String) constructor in 1.20.6 and below. Accompanied by #tryParse which returns null instead of throwing if the parameters aren’t valid.
  • ResourceLocation#withDefaultNamespace: takes in the path of the resource location, and always uses minecraft as its namespace.

Depluralisation

Several data folder names that were plural have been renamed:

  • tag folders:
    • tags/blocks -> tags/block
    • tags/entity_types -> tags/entity_type
    • tags/fluids -> tags/fluid
    • tags/items -> tags/item
    • tags/game_events -> tags/game_event
    • tags/functions -> tags/function
  • recipes -> recipe
  • advancements -> advancement
  • structures -> structure
  • loot_tables -> loot_table
  • predicates -> predicate
  • item_modifiers -> item_modifier
  • functions -> function

Data-driven enchantments

Enchantments are no longer a static, built-in registry, and are instead a datapack registry.
With this change, methods that previously took an Enchantment will now take a Holder<Enchantment> (which can be grabbed from a registry obtained through a level’s RegistryAccess), and all fields in the Enchantments class are ResourceKey<Enchantment>s.
Additionally, your code should now use EnchantmentEffectComponents to determine whether an enchantment should affect something, instead of relying on direct level checks.

For more information, we encourage you to consult ChampionAsh’s 1.21 primer.

The NeoForge changes

The first NeoForge beta release for 1.21 comes with some changes too.

Deprecations

Members deprecated for removal have been mostly removed:

  • IItemExtension#onArmorTick: use Item#inventoryTick instead, with the armor slot indices being 36, 37, 38 and 39.
  • ItemHandlerHelper#copyStackWithSize -> ItemStack#copyWithCount
  • ItemHandlerHelper#canItemStacksStack -> ItemStack#isSameItemSameComponents
  • the ComposterBlock#COMPOSTABLES map is now ignored. Use the data map instead.
  • the VibrationSystem#VIBRATION_FREQUENCY_FOR_EVENT map is now ignored. Use the data map instead.
  • the Parrot#MOB_SOUND_MAP map is now ignored. Use the data map instead.

FML also had its deprecated members removed:

  • the ModLoadingContext#registerConfig methods are replaced by ModContainer#registerConfig
  • FMLJavaModLoadingContext#get was removed. The direct replacement is ModLoadingContext#get, however use of it is discouraged. You should use direct references to your container where possible, and consider that the mod’s event bus is provided as a mod class constructor argument.
  • DistExecutor was removed without replacement. You should either use if (FMLLoader.dist == <dist>) checks, or separate entrypoints for client and common code.

Enum extensions rework (introduced in NeoForge 21.0.10-beta)

Enum extensions were reworked to solve several shortcomings of the previous approach. Instead of adding new entries at an arbitrary time by calling a static method on the target enum, the values are now added by specifying them in an enum extensions JSON file which is used to add the new values when the enum is class-loaded. The JSON file is specified through the enumExtensions key in a [[mods]] block of the neoforge.mods.toml.
The entries specified in the file consist of the target enum, the field name, the constructor to be used and the parameters (specified as an array of constants, as a reference to a field of type EnumProxy or as a reference to a method):

{
  "entries": [
    {
      "enum": "net/minecraft/world/item/ItemDisplayContext",
      "name": "EXAMPLEMOD_STANDING",
      "constructor": "(ILjava/lang/String;Ljava/lang/String;)V",
      "parameters": [ -1, "examplemod:standing", null ]
    }
  ]
}

See the documentation for further information on how to use the new system and the FML PR for further details on the motivations of this change.

Damage Pipeline rework (introduced in NeoForge 21.0.31-beta)

In NeoForge 21.0.31-beta, damage events have been reworked.
The motivation behind this change was that the previous poorly named and documented events were causing constant confusion, especially among beginners.

DamageContainer

DamageContainer is a new class exposed via the damage events. This object allows modifications to all aspects of the damage pipeline, including absorption and invulnerability ticks.
You can modify the different types of reduction (ARMOR, ENCHANTMENTS, MOB_EFFECTS and ABSORPTION) through addModifier:

// Reduce armor reduction by 1 hear
container.addModifier(DamageContainer.Reduction.ARMOR, (container, currentReduction) -> Math.max(0, currentReduction - 1));

EntityInvulnerabilityCheckEvent

This new event represents the earliest point modders can use to cancel damage, and can be used to override entities that would otherwise be invulnerable in vanilla.
This event will always be fired when an entity is attacked, even if the attack would not actually damage the entity.

LivingIncomingDamageEvent (formerly LivingAttackEvent)

This is the first event of the damage pipeline. Most modifications to the incoming damage should happen during this event.

LivingShieldBlockEvent (formerly ShieldBlockEvent)

This event can be used to decide whether the entity is actually blocking the damage through the shield, and if so, how much of the damage is blocked.

LivingDamageEvent.Pre (formerly LivingDamageEvent)

This event is the lastest point at which the final damage may be modified, or reduced to 0. However, the event is not cancellable as by when it is fired, armor and shield durability losses have already occurred.

LivingDamageEvent.Post (formerly LivingHurtEvent)

This is the last event of the damage pipeline. It indicates that all damage has been applied to the entity, and can only be used for reacting to the damage being applied (i.e. to reduce custom hunger or thirst values).

ILivingEntityExtension#onDamageTaken

This method has been added to living entities in order to allow them to react to already applied damage, without needing to override actuallyHurt and call super.

We would like to thank Caltinor for their work on the Pull Request, which you can consult for more information.

PlantType system is now replaced (introduced in NeoForge 21.0.39-beta)

The original PlantType system was buggy and quite confusing to deal with. Instead, we deleted that system and added SpecialPlantable item interface, patched in a canSustainPlant block method, and added a neoforge:villager_farmlands block tag.

The SpecialPlantable interface allows mods to more easily tell if a modded item can spawn a plant and exposes methods for easy checking if a spot is valid for the item and to spawn the plant itself.

The patched-in canSustainPlant method allows blocks to forcibly sustain many plants and now works with all vanilla plants. Modded plants would need to call this method if the plant block override canSurvive without calling super.

And the SpecialPlantable block tag now lets Farmer Villagers detect and plant on modded blocks more easier. This can also be used by mods to know if a modded block is farmland-like.

ToolActions renamed to ItemAbilities (renamed in NeoForge 21.0.40-beta)

To help promote the usage of our ToolActions system, it has been renamed to ItemAbilities to help show that it can be used for more than just tools and more than just representing actions. This system is a special alternative to tags for when mods need to do itemstack sensitive checks where an item can be in many different states and have different abilities. It is a good for cross-mod compatibility that is itemstack sensitive without needing to add compile-time dependencies. The NeoForge provided ItemAbilities for vanilla tools are still in place such as shears_dig to verify the itemstack used is supposed to allow shearing for modded stuff.

Config GUI returns! (Added in NeoForge 21.0.110-beta)

After many years, we now again have a default configuration GUI in NeoForge. It isn’t quite as capable or pretty as some of the mods/libraries for this, but it is built-in and available for all mods all the time. And it is completely optional. You can still use your favorite library for your configuration UI or write your own. It is also extendible, so you can tweak how it works easily (But do note we may do internal changes to it over time to improve it).

For a mod to opt into having this screen show up in mod list, you only need to add one line to your mod’s client-entry-point: modContainer.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new);

Also, if you use lists in your config, you need to add a new parameter to your defineList() calls so the UI knows how to add a new element to those lists. The new param is the default value to use when a player clicks new entry for the config list in the GUI. This is not a breaking change, but the old calls without the new (nullable) parameter may be removed in the future.

And lastly, be sure to add translations for your configs! If you visit all your config screen pages and then back out to the mod list, all untranslated config entries encountered will be printed to the console to make it easier to know what to translate and what the translation keys are.

Special thanks to Henry Loenwind for all the work put in to bring this back!

Note: There is a small breaking change with IConfigScreenFactory#createScreen method’s params.

Experimental Gradle Plugin

We’re currently developing a new experimental Gradle Plugin, focused on simpler buildscripts and improved caching. You can find information on its usage here, and support is provided in the thread on our Discord server.

NeoGradle will continue to be supported.

A note on stability

As was the case with the 1.20 lifecycle, we expect 1.21 (or 1.21.1) to be the stable version packs will focus on during the 1.21 lifecycle.
This means that 1.21 may have a longer beta period so that we can make sure that the version packs will use for the year to come is as good as it gets.
However, we will, as always, refrain from making useless breaking changes, and will make sure to announce them in the Dev Announcements channel of our Discord server. Make sure to select the Dev Announcement Pings role if you want to be kept up to date with the most important changes!

As always, we want to thank everyone for their contributions during the 1.20 lifecycle, and we hope to see more mods be developed for NeoForge in the coming months.