NeoForge 21.2 for Minecraft 1.21.2

Update time, once again! Mojang has just released Minecraft version 1.21.2.

The first beta release of NeoForge 21.2 is already available: 21.2.0-beta.

This version brings many large technical changes under the hood, so let’s talk about version support for a second.

Version Support

With Minecraft’s new development policy, minor updates often come with large internal technical changes. These changes are great, and usually benefit modders in the long run. However, they also fragment the community because not all mods are ported or can be ported immediately.

To avoid that fragmentation, we recommend that modders continue to actively maintain the 1.21.1 version of their mods until the release of Minecraft 1.22. Of course, we encourage modders who have the time to also try out and release their mods for 1.21.2 too, and we need their feedback to continue improving NeoForge!

Per our usual policy, all Pull Requests now need to be accepted into our 1.21.x branch which currently targets Minecraft 1.21.2, before a backport can be accepted in the 1.21.1 branch.

As usual, we will have a few weeks of beta where breaking changes are allowed in the 21.2 series. Remember that we announce breaking changes 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!

Minecraft Changes

Here is a selection of the changes in vanilla 1.21.2 that will affect many modders.

ID in Block Properties

The Block properties (of type BlockBehaviour.Properties) now require the ID of the block to be set:

- new MyBlock(BlockBehaviour.Properties.of().xxx())
+ new MyBlock(BlockBehaviour.Properties.of().xxx().setId(ResourceKey.create(Registries.BLOCK, resourceLocation)))

This is done automatically when using DeferredRegister.Blocks with the registerBlock method:

BLOCKS.registerBlock("block_name", MyBlock::new, properties -> properties.xxx())

ID in Item Properties

The same is true for Item properties (of type Item.Properties). We suggest using DeferredRegister.Items with the registerItem method that will take care of this.

By default, items use the item.namespace.path translation key. For block items, remember to call useBlockDescriptionPrefix() on the properties to use the block.namespace.path translation key:

// On block items only:
+ properties.useBlockDescriptionPrefix()

Block Entity Type Creation

There is no builder for block entity types anymore. Now, they are constructed directly:

- BlockEntityType.Builder.of(MyBlockEntity::new, BLOCK1.get(), BLOCK2.get()).build(null)
+ new BlockEntityType<>(MyBlockEntity::new, BLOCK1.get(), BLOCK2.get())

Registry Method Renames

RegistryAccess.registry was renamed to RegistryAccess.lookup, and similarly for the throwing methods:

- registryAccess.registryOrThrow(Registries.DAMAGE_TYPE)
+ registryAccess.lookupOrThrow(Registries.DAMAGE_TYPE)

Registry.getHolder was renamed to get, and similarly for the throwing methods:

- registry.getHolderOrThrow(resourceKey)
+ registry.getOrThrow(resourceKey)

Note that it returns a Holder. To retrieve the object directly, use getValue:

- registry.get(resourceLocation)
+ registry.getValue(resourceLocation)

ServerLevel Methods

Many methods were moved from Level down to ServerLevel. Beware of casts, some of these methods might still be called with client levels too! In that case, the recommended pattern is to use an instanceof check:

if (level instanceof ServerLevel serverLevel) {
    serverLevel.serverSpecificMethod(...);
}
// Continue with more common code

GuiGraphics.blit

The blit method now takes a Function<ResourceLocation, RenderType> as its first parameter. In most cases, you’ll want to pass RenderType::guiTextured. For example:

- guiGraphics.blit(TEXTURE, ...);
+ guiGraphics.blit(RenderType::guiTextured, TEXTURE, ...);

Recipe Changes

The recipe system was largely changed. Notably:

  • Recipes are no longer synced with clients.
    • Instead, only RecipeDisplays are.
    • Custom subclasses of RecipeDisplay can be created by modders, as long as the RecipeDisplay.Type is registered.
    • The Recipe interface has a new List<RecipeDisplay> display() method to obtain recipe displays.
  • The recipe book system was refactored, and is now more accessible to modders.
  • Ingredients were changed too:
    • They are generally not synced anymore. Instead, SlotDisplays are, similarly to the RecipeDisplays.
    • The format changed, and now follows the holder set format. For example:
- { "item": "minecraft:diamond" }
+ "minecraft:diamond"
- { "tag": "c:ingots/copper" }
+ "#c:ingots/copper"

Custom holder sets are supported too. For example, here is an ingredient that accepts any item:

{ "type": "neoforge:any" }

Custom ingredients are still supported too, using the neoforge:ingredient_type key:

  {
-   "type": "neoforge:compound",
+   "neoforge:ingredient_type": "neoforge:compound",
    "children": [
-     /* old ingredient syntax */
+     /* new ingredient syntax */
    ]
  }

Porting Primer

Finally, here is a more extensive list of changes, courtesy of @ChampionAsh5357: https://github.com/ChampionAsh5357/neoforged-github/blob/port/1212-or-122/primers/1.21.2/index.md.

Happy porting!