NeoForge 21.11 for Minecraft 1.21.11

The first beta release of NeoForge 21.11 for Minecraft 1.21.11 is now available: 21.11.0-beta. Let’s see what has changed.

Future removal of obfuscation

Mojang has recently announced that they will soon be removing obfuscation, starting from the next series of snapshots. Unobfuscated versions of Minecraft have already been made available to help mod loaders update their tooling, but they are not accessible by launchers out of the box. NeoForge 21.11 thus continues to be built on top of an obfuscated Minecraft.

We have already started to update our tooling to support unobfuscated versions, such that we will be ready for the next series of snapshots. For NeoForge modders, this mostly means that the official parameter names used by the developers of Minecraft should become available in the future, but not much will otherwise change since we already removed obfuscation of mods from our toolchain two years ago.

Fabric however was affected more severely. If you are interested, you can read more on this in their blog post. Notably, they are finally discontinuing Yarn and embracing Mojang’s official names.

Renaming of ResourceLocation to Identifier

ResourceLocation was renamed to Identifier.

This change will likely affect many mods, but should be fairly mechanical, especially with find-and-replace. Here are some migration examples:

- ResourceLocation key = BuiltInRegistries.ITEM.getKey(...);
+ Identifier key = BuiltInRegistries.ITEM.getKey(...);

- register(ResourceLocation.fromNamespaceAndPath("mymod", "thing"), ...);
+ register(Identifier.fromNamespaceAndPath("mymod", "thing"), ...);

Note

Yarn, Fabric’s set of mappings, famously used Identifier whereas Mojang used ResourceLocation. We will let you speculate as to whether this rename is a tribute to Yarn, a way to make Fabric modders not regret switching to official names, or whether Mojang simply found it to be a better name.

Other renames

There were other renames and/or package changes, notably of the Util class. For the full list and the corresponding replacements, consult the porting primer.

JSpecify nullability annotations

Following many other Java projects (e.g. Gradle, Spring Boot, …), Minecraft and NeoForge switched to JSpecify’s nullability annotations.

The convention is that nullable parameters, fields, etc… get a @Nullable annotation, whereas non-annotated variables are assumed to be non-null. Minecraft and NeoForge add @NullMarked to all their packages via package-info.java files to make IDEs aware of that.

Migrating from javax.annotation.Nullable to org.jspecify.annotations.Nullable is fairly straightforward, however pay attention to these two points:

  1. JSpecify are exclusively “type use”, meaning that they can only be put in specific places. For example, @Nullable Map.Entry will not compile. Instead, use Map.@Nullable Entry.
  2. For arrays, @Nullable can either be placed in front of the element type, or between the element type and the []. These have different meanings:
    • @Nullable Object[]: a non-null array of nullable Objects.
    • Object @Nullable[]: a nullable array of non-null Objects.
    • @Nullable Object @Nullable[]: a nullable array of nullable Objects.

JSpecify currently only covers nullability, so NeoForge is still using and making available JetBrains’ annotations, for example for the @ApiStatus annotations.

RenderType changes

Vanilla render types are now in the RenderTypes class (previously they were in RenderType). Additionally, the construction of RenderTypes has been refactored a little.

Baked quad changes

BakedQuad no longer stores its data encoded in an int[]. Instead, it now has explicit fields for vertex positions and uvs. Hopefully these changes will make BakedQuad a lot more understandable for modders who previously could not make sense of the int[]!

In NeoForge, we add bakedNormals and bakedColors fields to it, such that customizing normals and colors is still possible. We did not add a field to allow for lightmap customization, as that should already be covered by the lightEmission field.

For example, the color for a quad is stored in a BakedColors type, either with one color per vertex or with a color for the entire quad:

// Same color for all vertices:
BakedColors bakedColors = BakedColors.of(color);
// Different colors for each vertex:
BakedColors bakedColors = BakedColors.of(color0, color1, color2, color3);

To save memory, the Vector3fc (for the vertex positions), BakedNormals, and BakedColors instances should be cached across models. This is made possible thanks to the ModelBaker.PartCache class that is available from a ModelBaker. For example, use the following pattern to instantiate a Vector3fc for usage in a baked quad:

ModelBaker modelBaker = /* ... */;

// Do this, allows the memory to be shared for identical vectors:
Vector3fc position0 = modelBaker.parts().vector(0.25F, 0.5F, 0.0F);

// AVOID this as it forces a new instance to be allocated!
Vector3fc position0_bad = new Vector3f(0.25F, 0.5F, 0.0F);

Light values in model JSON

Related to the baked quad changes, the neoforge_data of a model element or face will not recognize the block_light and sky_light keys anymore, however it will now recognize the light_emission key which acts as a combined block+sky light setter.

Note that for model elements light_emission is already recognized by vanilla Minecraft, and does not need to be put under neoforge_data.

Finding out what needs to be migrated

A good strategy to migrate your mod is to have a working Minecraft 1.21.10 workspace alongside your in-progress Minecraft 1.21.11 workspace. This allows you to quickly find out how to update specific pieces of code. The workflow is as follows:

  1. Find a function call that doesn’t compile anymore in 1.21.11.
  2. Find the same function call in the 1.21.10 workspace that still compiles.
  3. Right click the function call, and go to a vanilla usage of the method. Note: In IntelliJ, this requires the Minecraft sources to be attached, and the scope for the search needs to be set to All Places!
  4. Go to the same vanilla usage in 1.21.11, and compare to see how vanilla updated their code.
  5. In many cases it is enough to make the same change to your code.

This can also be applied on a larger scale than a function call, for example to learn how baked quads changed between 1.21.10 and 1.21.11.

Porting Primer

A porting primer covering a lot more of Minecraft’s changes is available here (courtesy of @ChampionAsh5357).

Happy porting!