MultiLoader 26.1+ · Part 16

Custom Armour (MultiLoader 26.1+)

INTERMEDIATE MULTILOADER 26.1-26.1.2 18 min read · Jun 12, 2026
MultiLoader 26.1+ · 17 parts
1 Getting Started with MultiLoader 26.1+ 2 Setting Up RegistrationUtils (MultiLoader 26.1+) 3 Creating Items (MultiLoader 26.1+) 4 Creating Blocks (MultiLoader 26.1+) 5 Data Generation: Block & Item Models (MultiLoader 26.1+) 6 Data Generation: Block Loot Tables (MultiLoader 26.1+) 7 Data Generation: Crafting Recipes (MultiLoader 26.1+) 8 Data Generation: Block & Item Tags (MultiLoader 26.1+) 9 Data Generation: Language Files (MultiLoader 26.1+) 10 Data Generation: Advancements (MultiLoader 26.1+) 11 Data Generation: Sound Definitions (MultiLoader 26.1+) 12 Data Generation: Particle Descriptions (MultiLoader 26.1+) 13 Data Generation: Enchantments (MultiLoader 26.1+) 14 Custom Food Items (MultiLoader 26.1+) 15 Custom Tools (MultiLoader 26.1+) 16 Custom Armour (MultiLoader 26.1+) 17 Block Entities (MultiLoader 26.1+)

ArmorMaterial is a data-driven record registered in the game's built-in registry. Each material declares per-slot defence values, enchantability, an equip sound, a repair ingredient, and one or more texture layers used for the 3D equipped rendering. The inventory icons are separate item textures, just like any other flat item.

NOTE
Complete the Creating Items tutorial before continuing. Armour items are registered in the same ItemRegistry.

Armour Material

Create a new class called ArmourMaterialRegistry in your common registry package. Declare a RegistrationProvider<ArmorMaterial> and register your material:

java
public class ArmourMaterialRegistry {
public static final RegistrationProvider<ArmorMaterial> ARMOUR_MATERIALS =
RegistrationProvider.get(Registries.ARMOR_MATERIAL, Constants.MOD_ID);
public static final RegistryObject<ArmorMaterial, ArmorMaterial> EXAMPLE =
ARMOUR_MATERIALS.register("example", () -> new ArmorMaterial(
Util.make(new EnumMap<>(ArmorItem.Type.class), map -> {
map.put(ArmorItem.Type.HELMET, 2);
map.put(ArmorItem.Type.CHESTPLATE, 6);
map.put(ArmorItem.Type.LEGGINGS, 5);
map.put(ArmorItem.Type.BOOTS, 2);
}),
15,
SoundEvents.ARMOR_EQUIP_IRON,
() -> Ingredient.of(ItemRegistry.IRON_STICK.get()),
List.of(new ArmorMaterial.Layer(
Identifier.fromNamespaceAndPath(Constants.MOD_ID, "example"))),
0.0f,
0.0f
));
public static void init() {}
}

The seven arguments to ArmorMaterial are:

  • defence map: damage reduction points per slot (iron totals 15: 2+6+5+2).
  • enchantmentValue: enchantability (iron is 9, gold is 25, diamond is 10).
  • equipSound: the Holder<SoundEvent> played on equip.
  • repairIngredient: item accepted by an anvil to repair the armour.
  • layers: list of texture layers shown on the player model when equipped.
  • toughness: extra damage reduction for heavy armour (diamond: 2, netherite: 4, iron: 0).
  • knockbackResistance: fraction of knockback absorbed (netherite: 0.1, others: 0).

Call ArmourMaterialRegistry.init() from CommonClass.init() before the item registry so the material is registered before the armour items that reference it:

java
public class CommonClass {
public static void init() {
ArmourMaterialRegistry.init();
ItemRegistry.init();
BlockRegistry.init();
SoundRegistry.init();
ParticleRegistry.init();
CreativeTabRegistry.init();
}
}

Registering Armour Items

ArmorItem takes a Holder<ArmorMaterial>, not the material object directly. Retrieve it with Holder.direct() inside the item supplier, which is safe because the supplier is only evaluated after the material registry has been populated. Set the max stack size to 1 by calling .stacksTo(1) on getItemProperties():

java
public static final RegistryObject<Item, ArmorItem> EXAMPLE_HELMET =
ITEMS.register("example_helmet", () -> new ArmorItem(
Holder.direct(ArmourMaterialRegistry.EXAMPLE.get()),
ArmorItem.Type.HELMET,
getItemProperties("example_helmet").stacksTo(1)));
public static final RegistryObject<Item, ArmorItem> EXAMPLE_CHESTPLATE =
ITEMS.register("example_chestplate", () -> new ArmorItem(
Holder.direct(ArmourMaterialRegistry.EXAMPLE.get()),
ArmorItem.Type.CHESTPLATE,
getItemProperties("example_chestplate").stacksTo(1)));
public static final RegistryObject<Item, ArmorItem> EXAMPLE_LEGGINGS =
ITEMS.register("example_leggings", () -> new ArmorItem(
Holder.direct(ArmourMaterialRegistry.EXAMPLE.get()),
ArmorItem.Type.LEGGINGS,
getItemProperties("example_leggings").stacksTo(1)));
public static final RegistryObject<Item, ArmorItem> EXAMPLE_BOOTS =
ITEMS.register("example_boots", () -> new ArmorItem(
Holder.direct(ArmourMaterialRegistry.EXAMPLE.get()),
ArmorItem.Type.BOOTS,
getItemProperties("example_boots").stacksTo(1)));
TIP
Holder.direct() creates an unkeyed holder, which works at runtime but means the material cannot be referenced by data packs or other mods by its registry key. For full interoperability, retrieve the keyed holder after registration using BuiltInRegistries.ARMOR_MATERIAL.getHolder(ResourceKey...) and store it in a static Holder<ArmorMaterial> field initialised in a post-registration hook.

Armour Textures

The 3D equipped model uses two texture files named after the layer identifier you declared. For the ArmorMaterial.Layer created with Identifier.fromNamespaceAndPath(MOD_ID, "example"), Minecraft looks for:

  • assets/examplemod/textures/models/armor/example_layer_1.png: worn on the helmet, chestplate, and boots model.
  • assets/examplemod/textures/models/armor/example_layer_2.png: worn on the leggings model.

Both files follow the vanilla armour UV layout. The easiest starting point is to copy a vanilla armour texture from the Minecraft assets and recolour or redraw from there.

TIP
The vanilla armour texture template is 64x32 pixels. Layer 1 covers the helmet, chestplate, and boots regions; layer 2 covers only the leggings region. Every standard armour piece fits within those coordinates.

Creative Tab and Language File

Add all four pieces to your creative tab and add display names to ExampleLanguageProvider.addTranslations():

java
output.accept(ItemRegistry.EXAMPLE_HELMET.get());
output.accept(ItemRegistry.EXAMPLE_CHESTPLATE.get());
output.accept(ItemRegistry.EXAMPLE_LEGGINGS.get());
output.accept(ItemRegistry.EXAMPLE_BOOTS.get());
java
add(ItemRegistry.EXAMPLE_HELMET.get(), "Example Helmet");
add(ItemRegistry.EXAMPLE_CHESTPLATE.get(), "Example Chestplate");
add(ItemRegistry.EXAMPLE_LEGGINGS.get(), "Example Leggings");
add(ItemRegistry.EXAMPLE_BOOTS.get(), "Example Boots");

Datagen

Armour items use the standard flat 2D inventory model, so each piece needs registerSimpleFlatItemModel in ExampleModelProvider:

java
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
blockModels.createTrivialCube(BlockRegistry.NEW_DIRT.get());
blockModels.registerSimpleItemModel(
BlockRegistry.NEW_DIRT.get(),
ModelLocationUtils.getModelLocation(BlockRegistry.NEW_DIRT.get()));
blockModels.registerSimpleFlatItemModel(ItemRegistry.IRON_STICK.get());
blockModels.registerSimpleFlatItemModel(ItemRegistry.TOMATO.get());
registerHandheldItem(blockModels, ItemRegistry.EXAMPLE_SWORD.get());
registerHandheldItem(blockModels, ItemRegistry.EXAMPLE_PICKAXE.get());
registerHandheldItem(blockModels, ItemRegistry.EXAMPLE_AXE.get());
registerHandheldItem(blockModels, ItemRegistry.EXAMPLE_SHOVEL.get());
registerHandheldItem(blockModels, ItemRegistry.EXAMPLE_HOE.get());
blockModels.registerSimpleFlatItemModel(ItemRegistry.EXAMPLE_HELMET.get());
blockModels.registerSimpleFlatItemModel(ItemRegistry.EXAMPLE_CHESTPLATE.get());
blockModels.registerSimpleFlatItemModel(ItemRegistry.EXAMPLE_LEGGINGS.get());
blockModels.registerSimpleFlatItemModel(ItemRegistry.EXAMPLE_BOOTS.get());
}

Create a 16x16 PNG inventory icon for each piece under common/src/main/resources/assets/examplemod/textures/item/ and run NeoForge Data to generate the model files.

Testing In-Game

Launch the client and equip each piece from your creative tab. Confirm:

  • The inventory icon for each slot shows your item texture.
  • The equipped 3D model on the player uses your layer textures.
  • The armour bar fills according to your defence values.
  • Placing the pieces in an anvil with Iron Sticks repairs their durability.

If the equipped model appears as the default leather armour texture or shows missing textures, double-check the layer texture file names. The number suffix (_layer_1, _layer_2) must be present and the folder must be textures/models/armor/ not textures/armor/.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Block Entities (MultiLoader 26.1+)

Create a ticking BlockEntity that persists custom data to NBT, widen package-private access with an access transformer and access widener, implement client sync via getUpdateTag and getUpdatePacket, and wire up the ticker through BaseEntityBlock.

Continue →