Basic Modding - Blockstates and Metadata

Goal

I want to show you how to use blockstates and metadata to create blocks with several variable properties.

Difficulty

3/10 - Relatively Easy... (be aware! Its not too simple to understand!)

Prerequisites

Forge Version

This Tutorial was created with Forge 11.14.1.1334 for Minecraft 1.8. If anything doesn't work with other versions, please contact me!


Blockstates and Metadata

How does Minecraft work - Blockstates and Metadata:
Blockstates - the new concept in Minecraft 1.8:
Blockstates define a state of a block in the world, divided in several properties. Every block can have as much properties as it needs, but, as a modder, you need to be aware of the fact that you need to store these properties somehow.
Blocks have a metadata, a 4 bit value between 0 and 15. This is the only additional storage a block has (except TileEntities), so it's necessary to fit the data from the properties that needs to be saved into these 4 bits. In general, not every property needs to be saved though. For instance, a stained glass pane has the properties "color", "north", "east", "south" and "west". Only the color property needs to be saved, because the other values (the connections of this glass pane) can be found by checking the neighbour blocks of the glass pane.
The properties of the block are used in multiple places, for instance in the rendering. The blockstates file for each block is used to choose the right block model depending on the blockstates. Also, most methods that do something in the world use blockstates instead of blocks and metadata.

Items with metadata:
The metadata of an item is stored in the itemstack's damage value which is normally used to save the durability of tools. Because metaItems use this value they cannot have a damage value, so you can't make pickaxes (which are damageable) with metadata. The metadata of items is not restricted to 16 numbers, instead it can be any number from -32768 to 32767.

Creating a block with a special property

To create a block with a property, I'm going to start with a simple block class:

BlockProperties.class:
package com.bedrockminer.tutorial.blocks;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;

public class BlockProperties extends Block {

    public BlockProperties(String unlocalizedName, Material material, float hardness, float resistance) {
        super(material);
        this.setUnlocalizedName(unlocalizedName);
        this.setCreativeTab(CreativeTabs.tabBlock);
        this.setHardness(hardness);
        this.setResistance(resistance);
    }
}

The next thing we'll need is the property itself. A property is an object implementing the interface IProperty. Mostly, the Vanilla classes PropertyInteger, PropertyBool, PropertyEnum or PropertyDirection will be used in a custom block. PropertyInteger stores an Integer, PropertyBool a boolean value, PropertyEnum an enum constant and PropertyDirection a direction value.

In this example, I'll use the PropertyEnum with an enum which defines subtypes of my block. A property is created using the static create method of the class because all the constructors are protected.

BlockProperties.class:
public static final PropertyEnum TYPE = PropertyEnum.create("type", BlockProperties.EnumType.class);

You may wonder why the property is final. This is because a property does only store the type of a value, not the value itself. This is done somewhere in the Block class, but it does not matter anyway.


Currently, you have an error below EnumType.class. That happens because we didn't create the class yet. I recommend creating it as an enclosed class, to save some files. It will be a simple enum, but it must implement the interface IStringSerializable.

BlockProperties.EnumType.class:
public enum EnumType implements IStringSerializable {
    WHITE(0, "white"),
    BLACK(1, "black");

    private int ID;
    private String name;
    
    private EnumType(int ID, String name) {
        this.ID = ID;
        this.name = name;
    }
    
    @Override
    public String getName() {
        return name;
    }

    public int getID() {
        return ID;
    }
}

Unfortunately, it is not enough to return the correct name in the getName() method: we also need to override toString() and return the same value.

BlockProperties.EnumType.class:
@Override
public String toString() {
    return getName();
}

Back in the block class, we need to override three methods to tell Minecraft that our block has a property and to convert the state into a metadata and vice-versa.


We start with the first method, createBlockState(). This methods returns a BlockState which contains every property the block has.

BlockProperties.class:
@Override
protected BlockState createBlockState() {
    return new BlockState(this, new IProperty[] { TYPE });
}

The next two methods are used to convert a IBlockState into metadata and the other way round.

BlockProperties.class:
@Override
public IBlockState getStateFromMeta(int meta) {
    return getDefaultState().withProperty(TYPE, meta == 0 ? EnumType.WHITE : EnumType.BLACK);
}

@Override
public int getMetaFromState(IBlockState state) {
    EnumType type = (EnumType) state.getValue(TYPE);
    return type.getID();
}

We also need to override the method damageDropped so that the block drops the item with the corresponding metadata.

BlockProperties.class:
@Override
public int damageDropped(IBlockState state) {
    return getMetaFromState(state);
}

Now, we need to set the default block state. This is one small line in the constructor of the block:

BlockProperties.class:
public BlockProperties(String unlocalizedName, Material material, float hardness, float resistance) {
    super(material);
    this.setUnlocalizedName(unlocalizedName);
    this.setCreativeTab(CreativeTabs.tabBlock);
    this.setHardness(hardness);
    this.setResistance(resistance);
    this.setDefaultState(this.blockState.getBaseState().withProperty(TYPE, EnumType.WHITE));
}

Next, we want the block to appear in the creative tab in both states, thus we need to override getSubBlocks. In this method, we add the ItemStacks with the different metadata values we want to the list of itemstacks that is finally displayed in the creative tab.

BlockProperties.class:
@Override
public void getSubBlocks(Item itemIn, CreativeTabs tab, List list) {
    list.add(new ItemStack(itemIn, 1, 0)); //Meta 0
    list.add(new ItemStack(itemIn, 1, 1)); //Meta 1
}

Now, we need to define an ItemBlock. This is the Item that represents our block in the item state. We need to redefine this class to use the metadata of the block and to show the right name for the block state. I created a default class named ItemBlockMeta which can be used for every block with metadata.

ItemBlockMeta.class:
package com.bedrockminer.tutorial.blocks;

import net.minecraft.block.Block;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;

public class ItemBlockMeta extends ItemBlock {

    public ItemBlockMeta(Block block) {
        super(block);
        if (!(block instanceof IMetaBlockName)) {
            throw new IllegalArgumentException(String.format("The given Block %s is not an instance of ISpecialBlockName!", block.getUnlocalizedName()));
        }
        this.setMaxDamage(0);
        this.setHasSubtypes(true);
    }

    public int getMetadata(int damage)
    {
        return damage;
    }

    @Override
    public String getUnlocalizedName(ItemStack stack) {
        return super.getUnlocalizedName(stack) + "." + ((IMetaBlockName)this.block).getSpecialName(stack);
    }
}

This class uses the interface IMetaBlockName, which was also created by me.

IMetaBlockName.class:
package com.bedrockminer.tutorial.blocks;

import net.minecraft.item.ItemStack;

public interface IMetaBlockName {

        String getSpecialName(ItemStack stack);
}

With these two classes, the metadata of the item is directly transferred to the block's metadata, so it's possible to place down both the black and the white type of our block. But the ItemBlockMeta requires the block to implement IMetaBlockName, so we need to do this.

BlockProperties.class:
public class BlockProperties extends Block implements IMetaBlockName {

[...]

@Override
public String getSpecialName(ItemStack stack) {
    return stack.getItemDamage() == 0 ? "white" : "black";
}

Note that my programming style in the getSpecialName method is quite dirty, you better create a switch statement or a method in EnumType to get the type name from the metadata.

 

The next thing is something I always forget myself (I even forgot to add it here first, now it's there!): We need to define, which ItemStack we'll get when we pick the block in creative mode (middle mouse). Also, sometimes we need to define which ItemStack should be dropped if the block is mined with SilkTouch.

The method canSilkHarvest(World, BlockPos, IBlockState, EntityPlayer) returns true, if the block can be harvested with silk touch. If it returns true, the method createStackedBlock(IBlockState) is called which returns the dropped ItemStack.

For our example block, we don't need to define special silk touch behaviour but we need to define a special behaviour for the PickBlock function: Here the block needs to be converted into an ItemStack that is equal to the block. We do this in the method getPickBlock(MovingObjectPosition, World, BlockPos). This method has already a convenient implementation in Block.class, but to ensure everything works right for our block, we should override it when using Blockstates. I use a very simple implementation here, but its exactly what we need for our block.

BlockProperties.class:
@Override
public ItemStack getPickBlock(MovingObjectPosition target, World world, BlockPos pos) {
    return new ItemStack(Item.getItemFromBlock(this), 1, this.getMetaFromState(world.getBlockState(pos)));
}

The last step is the registration of our block. In the preInit method of the CommonProxy (or in the ModBlocks class) we need to register the block itself:

ModBlocks.class:
GameRegistry.registerBlock(propertyBlock = new BlockProperties("block_properties"), ItemBlockMeta.class, "block_properties");

Here it's important that you add the ItemBlockMeta class to the arguments of the registerBlock method, because it defines the class for the block's item. Now, we need to create the blockstates file and the models.

blockstates/block_properties.json:
{
    "variants": {
        "type=white": { "model":"tutorial:block_properties_white" },
        "type=black": { "model":"tutorial:block_properties_black" }
    }
}

The properties of the block are put together in a string like this:

"property1=value,property2=value". The order is (mostly) alphabetically. If not, you'll get an error message which shows you the right order. You must not leave out any property in your variants tags.

models/block/block_properties_black.json:
{
    "parent":"block/cube_all",
    "textures": {
        "all": "tutorial:blocks/block_properties_black"
    }
}

The white block is the same, apart from the different name and texture. This also applies to the item models:

models/item/block_properties_black.json:
{
    "parent":"tutorial:block/block_properties_black",
    "display": {
        "thirdperson": {
            "rotation": [ 10, -45, 170 ],
            "translation": [ 0, 1.5, -2.75 ],
            "scale": [ 0.375, 0.375, 0.375 ]
        }
    }
}

The textures I'm using are these ones:

We still need to define that the block can have multiple models. Therefore, we need to create a new method in our BlockRenderRegister, which is called during the preInit phase. I usually call it preInit as well, but you can choose a different name.

BlockRenderRegister.class:
public static void preInit() {
}
ClientProxy.class:
@Override
public void preInit(FMLPreInitializationEvent e) {
    super.preInit(e);
                
    BlockRenderRegister.preInit();
}

In this method, we need to define the different models our block can ever have. This is done with the method ModelBakery.addVariantName. This is only used for the Item state of the block. If your block defines other models only in the blockstates file, this is not necesssary.

BlockRenderRegister.class:
public static void preInit() {
    ModelBakery.addVariantName(Item.getItemFromBlock(ModBlocks.propertyBlock), "tutorial:block_properties_black", "tutorial:block_properties_white");
}

Always remember to add a "modid:" in front of your model names.

The method tells MC that more than one single model is used for the block's item.

Finally, we need to register the renderer for the Items with special metadata. Therefore, we can create a new utility method in the BlockRenderRegister class.

BlockRenderRegister.class:
public static void reg(Block block, int meta, String file) {
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher()
    .register(Item.getItemFromBlock(block), meta, new ModelResourceLocation(modid + ":" + file, "inventory"));
}

With this method we can register the two block metadata states of our block as an item:

BlockRenderRegister.class:
public static void registerBlockRenderer() {
    reg(ModBlocks.tutorialBlock);
    reg(ModBlocks.propertyBlock, 0, "block_properties_white");
    reg(ModBlocks.propertyBlock, 1, "block_properties_black");
}

Finally, our block is completed! If we run Minecraft it looks like this:

Screenshots - Show

Creating a dynamic property (getActualState)

If we want our block to have a property that is set not by metadata but by the surrounding blocks or other values, we have to override getActualState.

BlockProperties.class:
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos) {
    return super.getActualState(state, worldIn, pos);
}

This method allows us to modify the state which was calculated using the metadata. The calculated state is passed in as the argument "state". We can use .withProperty on this field to modify or add certain properties. For instance, we could add the property "sky" which is a boolean and true if the block has no other block above it.

BlockProperties.class:
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos) {
    return state.withProperty(SKY, ((World)worldIn).canBlockSeeSky(pos));
}

Note that you'd have to add the SKY property to the array in createBlockState.

Also note that you must write down every property in the blockstates file.


Metadata for Items

Metadata for Items is much easier than creating Blocks with properties and blockstates. You only need a few methods to make an item able to use metadata.

First, we create the item class:

MetaItem.class:
package com.bedrockminer.tutorial.items;

import [...];

public class MetaItem extends Item {

    public MetaItem(String unlocalizedName) {
        super();
        this.setHasSubtypes(true);
        this.setUnlocalizedName(unlocalizedName);
        this.setCreativeTab(CreativeTabs.tabMaterials);
    }
}

The most important method for metadata items is setHasSubtypes. It is used to tell Minecraft that Items with different metadata may not be stacked. For Blocks this was not needed because it's included in my ItemBlockMeta class.

 

In this example, I'll create an item that has the texture of the BlockProperties, white for meta 0 and black for meta 1.

Luckily, we need to override only two methods: getUnlocalizedName and getSubItems for the creativeTabs.

MetaItem.class:
@Override
public String getUnlocalizedName(ItemStack stack) {
    return super.getUnlocalizedName() + "." + (stack.getItemDamage() == 0 ? "white" : "black");
}

@Override
public void getSubItems(Item itemIn, CreativeTabs tab, List subItems) {
    subItems.add(new ItemStack(itemIn, 1, 0));
    subItems.add(new ItemStack(itemIn, 1, 1));
}

We now can register our Item and create model files for the two states:

models/item/meta_item_white.json:
{
    "parent": "tutorial:item/standard_item",
    "textures": {
        "layer0": "tutorial:blocks/block_properties_white"
    }
}

The black one is, of course, the same apart from the texture.

 

We need to add a preInit method in the ItemRenderRegister as well, because we also want to register variants here. We also add the second reg method and register our items:

ItemRenderRegister.class:
public static void preInit() {
    ModelBakery.addVariantName(ModItems.metaItem, "tutorial:meta_item_white", "tutorial:meta_item_black");
}

public static void registerItemRenderer() {
    reg(ModItems.tutorialItem);
    reg(ModItems.metaItem, 0, "meta_item_white");
    reg(ModItems.metaItem, 1, "meta_item_black");
}

public static void reg(Item item, int meta, String file) {
    Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item, meta, new ModelResourceLocation(modid + ":" + file, "inventory"));
}

Now (if the preInit method is called correctly) our Item is finished as well!

The result looks like this:

Screenshots - Show

You can download the code used in this tutorial as a .zip file from here.


Recommended tutorials to continue with

Take a look at the tutorial overview and decide what you want to do next!


Comments and Questions:

If you want to report modding problems, please make sure to include the code in a pastebin link or something else! Don't just write "It doesn't work", otherwise your post will be deleted. For more complicated problems, please use the troubleshooter form.