NeoForge 21.5 for Minecraft 1.21.5

Hello everyone,

The first beta release of NeoForge 21.5 for Minecraft 1.21.5 is now available: 21.5.0-beta. And with that the Team wants to hand out a large thanks for all the porting efforts this time around to the entire team, you all did amazing work!

Now let’s get into the changes, shall we?

Changes

This section will draw heavily from the Porting Primer, as well as from changes noticed by the porting team.

Weapons, Tools, and Armor: Removing the Redundancies

There have been a lot of updates to weapons, tools, and armor that removes the reliance on the hardcoded base classes of SwordItem, DiggerItem, and ArmorItem, respectively. These have been replaced with their associated data components WEAPON for damage, TOOL for mining, ARMOR for protection, and BLOCKS_ATTACKS for shields. Additionally, the missing attributes are usually specified by setting the ATTRIBUTE_MODIFIERS, MAX_DAMAGE, MAX_STACK_SIZE, DAMAGE, REPAIRABLE, and ENCHANTABLE. Given that pretty much all the non-specific logic has moved to a data component, these classes have now been completely removed. Use one of the available item property methods or call Item$Properties#component directly to set up each item as a weapon, tool, armor, or some combination of the three. We have also removed the _DIG ItemAbilities these tools previously had in favour of the new components.

Extrapolating the Saddles: Equipment Changes

A new EquipmentSlot has been added for saddles, which brings with it new changes for generalizing slot logic.

First, rendering an equipment slot for an entity can now be handled as an additional RenderLayer called SimpleEquipmentLayer. This takes in the entity renderer, the EquipmentLayerRenderer, the layer type to render, a function to get the ItemStack from the entity state, and the adult and baby models. The renderer will attempt to look up the client info from the associated equippable data component and use that to render the laters as necessary.

Next, instead of having individual lists for each equipment slot on the entity, there is now a general EntityEquipment object that holds a delegate to a map of slots to ItemStacks. This simplifies the storage logic greatly.

Finally, equippables can now specify whether wearing one will contribute to the bonus XP earned when killing a mob by setting equipOnInteract.

Data Component Getters

The data component system can now be represented on arbitrary objects through the use of the DataComponentGetter. As the name implies, the getter is responsible for getting the component from the associated type key. Both block entities and entities use the DataComponentGetter to allow querying internal data, such as variant information or custom names. They both also have methods for collecting the data components from another holder (via applyImplicitComponents or applyImplicitComponent). Block entities also contain a method for collection to another holder via collectImplicitComponents.

NBT Tags and Parsing

NBT tags have received a rewrite, removing any direct references to types while also sealing and finalizing related classes. Getting a value from the tag now returns an Optional-wrapped entry, unless you use one of the get*Or methods, which have a parameter for a fallback default value. Objects, on the other hand, do not take in a default, instead returning an empty variant of the desired tag.

Writing with Codecs

CompoundTags now have methods to write and read using a Codec or MapCodec. For a Codec, it will store the serialized data inside the key specified. For a MapCodec, it will merge the fields into the top level tag.

// For some Codec<ExampleObject> CODEC and MapCodec<ExampleObject> MAP_CODEC
// We will also have ExampleObject example
CompoundTag tag = new CompoundTag();

// For a codec
tag.store("example_key", CODEC, example);
Optional<ExampleObject> fromCodec = tag.read("example_key", CODEC);

// For a map codec
tag.store(MAP_CODEC, example);
Optional<ExampleObject> fromMapCodec = tag.read(MAP_CODEC);

Render Pipeline Rework

Rendering an object to the screen, whether through a shader or a RenderType, has been fully or partially reworked, depending on what systems you were using previously. As such, a lot of things need to be reexplained, the primer mentioned below will provide a very in-depth look. However, for the people who don’t care, here’s the TL;DR.

First, core-shader JSONs no longer exist. They are replaced by a RenderPipeline, which is effectively an in-code substitute. Second, the RenderPipelines turns most flag values into enum constants. For example, instead of storing the blend function mode id, you store a BlendFunction. Similarly, you no longer store or set up the direct texture objects, but instead manage it through a GpuTexture. Finally, the VertexBuffer can either draw to the framebuffer by directly passing in the RenderPipeline, updating any necessary uniforms in the consumer, or by passing in the RenderType.

Abstracting OpenGL

As many are aware, Minecraft has been abstracting away their OpenGL calls and constants, and this release is no different. All the calls to GL codes, except BufferUsage, have been moved out of object references, to be obtained typically by calling GlConst$toGl. However, with all the other rendering reworks, there are numerous changes and complexities that require learning an entirely new system, assuming you’re not using RenderTypes.

Warning

Please be aware that while these changes are wide sweeping, OpenGL is still the underlying platform, so they will probably not cause compile errors. Mods are however advised switch to the new platform.

Model rework

Besides the abstracting of OpenGL, there are also significant changes to how models are loaded into the game, and how they are processed. Examples of this include rewrites to model discovery, parsing and instantiation. Additionally, NeoForge has aligned its BakedModel framework closer to what vanilla wants to use in the new model part system.

Note

The changes in this area are significant, and would blow up the size of this announcement. We will post additional information and documentation in the next few days that describe the changes and how to use them.

Fabric is also going in this direction, causing less headaches for cross-platform modders.

Client Mod Initialization

Previously, in the case of the client, mods were instantiated during client initialization. This meant that Minecraft.getInstance() returned a valid object and was usable to a certain degree.

To better align with vanilla changes to game option registration (such as keybinds), your @Mod entrypoints will now be constructed before Minecraft. As a consequence, accessing the Minecraft instance during mod loading is not possible, and mods that require access to it will have to adjust their code (for example, to use the FMLClientSetupEvent).

NeoForge internal changes

Besides the myriad of user and modder facing changes, there are also some internal changes to NeoForge’s build system, which modders will not experience.

Split SourceSets

To prepare for supporting split sourcesets in mod development environments, we needed to split the NeoForge code base first, based on whether the code in question was common, or client only. This split has now been performed and PRs written for older versions will need to be adapted, especially if they touch client side code. Players will not experience any changes, however some classes had to be moved from a common package to a client one or vice versa so some mods will have to be updated to account for this.

Porting Primer

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

We might update this blog post later with more NeoForge-specific changes.

Stay tuned, and happy porting!