Minecraft Forge 1.8: Block Modding

Introduction



Blocks have changed a lot in 1.8.  Pretty much everything from how positions and metadata work, to models and textures, to even some changed method names.

Since I wrote this, there is now a good post for official documentation on block states.

Block Positions (BlockPos) And Sides (EnumFacing)



Every 3-D coordinate in the world has a block associated with it (if there is "no block" in a location that is considered an "air" block).  The coordinates can be represented with the BlockPos class.

Furthermore, the side of the block is not longer expressed as an int but now with an enumerated constant in EnumFacing class.  E.g. EnumFacing.UP or EnumFacing.EAST.

Converting from 1.7.x: So most methods that previously took three int parameters representing x, y, z now take a BlockPos parameter, and methods that took an int for the side now take an EnumFacing parameter.

BlockPos further provides some useful methods such as:
  • add() which can take double, int or Vec3 parameters
  • offset() which takes an EnumFacing and an int parameter and returns position in direction of that side.  There are also a bunch of convenient offset methods such as:
    • offsetUp() and offsetDown() which give position above or below respectively
    • offsetNorth(), offsetSouth(), offsetEast(), and offsetWest() which give positions beside the positions in direction indicated
  • getAllInBox() which returns an Iterable with all the BlockPos within a 3-D region.

Block Metadata (IBlockState with PropertyHelper)


What Is Metadata?


There are just so many blocks in a Minecraft world -- there are 65,536 block positions just in one chunk -- that Minecraft cannot instantiate every actual block. Instead, each block is instantiated once then mapped to the world coordinates along with "metadata" values that represent special things about a specific block.

Due to memory space limits the metadata has to be terse. In 1.7.x it was just one nybble of values from 0 to 15 which worked fine, but from a programming perspective it was a bit obtuse since they didn't have meaningful names -- for example the lowest two bits in the metadata for a bed represented the direction the head of the bed was facing.  See wiki on block metadata values which gives you an idea of what kind of information is stored.

Block Properties


In 1.8, the original sense of metadata is still there but it is now managed with much more readable "properties" that are based on the PropertyHelper class.  There are four built-in property types:
  • PropertyInteger
  • PropertyBool
  • PropertyEnum
  • PropertyDirection
A property is declared as static and final for each block class.  For example, a crop block that grows with age might have something like:

public static final PropertyInteger AGE = PropertyInteger.create(
      "age", 0, 7);

You can see that in this case we're making an integer property with valid range from 0 to 7.

Warning: The name "age" is extremely important because you need to match that exactly in your blockstates model JSON file.  You can name it whatever you want, but it has to match the JSON. Basically, depending on the value of the "age" property Minecraft will use a different model for the block.  This is the key to animating or providing variation in your blocks.

Key Point: You must create the state including your properties by overriding the createBlockState() method in your custom block:


@Override protected BlockState createBlockState() { return new BlockState(this, new IProperty[] {AGE}); } 

To write and read a property value for a particular block, you access it through the IBlockState for the position of that block. For example, something like this would set the AGE property to value of 5:


worldObj.setBlockState(blockPos, worldObj.getBlockState(blockPos) .withProperty(AGE, Integer.valueOf(5))); 


Note that the access is somewhat indirect as you need to get the state, get the property from the state, and then set the state.

Converting Between Block States And Metadata


With the new approach, there is a difference between block states and metadata.  Metadata is those states that you want to store (i.e. in save and load) whereas it is possible to now have additional states used for things like animation.

Warning: Metadata is still stored in only 4 bits per block, so for example you can at most use an int property from 0 to 15 or you can use up to 4 boolean properties.

So for any state information you want to save, each block has methods for getting and setting metadata from state and vice versa.  You need to override getStateFromMeta() and getMetaFromState() in your custom block:

/**
 * Convert the given metadata into a BlockState for this Block
 */
@Override
public IBlockState getStateFromMeta(int meta)
{
    return getDefaultState().withProperty(AGE, Integer.valueOf(meta));
}

/**
 * Convert the BlockState into the correct metadata value
 */
@Override
public int getMetaFromState(IBlockState state)
{
    return ((Integer)state.getValue(AGE)).intValue();
}


Note that it us up to you to figure out how you want to pack your properties in the 4 bits of metadata. For example if you had two boolean properties. It is best to look at examples from vanilla blocks on how to do this, as you need to work with some byte operations.  Here is what BlockBed uses:

/**
 * Convert the BlockState into the correct metadata value
 */
public int getMetaFromState(IBlockState state)
{
    byte b0 = 0;
    int i = b0 | ((EnumFacing)state.getValue(FACING)).getHorizontalIndex();

    if (state.getValue(PART_PROP) == BlockBed.EnumPartType.HEAD)
    {
        i |= 8;

        if (((Boolean)state.getValue(OCCUPIED_PROP)).booleanValue())
        {
            i |= 4;
        }
    }

    return i;
}

Block States


1.8 also now provides an IBlockState class that combines the block type and property/metadata values for a given BlockPos position in the world.

Since the block type is in the IBlockState, you now have to get the block state to get the block. To really drive this home consider this:
  • Key Point: The World.getBlock(x, y, z) function from 1.7.x has changed to World.getBlockState(parPos).getBlock() in 1.8.

Block Models And Textures


Briefly, blocks now use JSON files to map the state properties to models, and to models with textures.

Key Point: It is recommended to use the Forge JSON format instead of the vanilla JSON format as the Forge JSON let's you mix property combinations without having to list every permutation. You indicate that your JSON is a Forge JSON by putting the following at the beginning of the JSON file (just after the initial brace):  "forge_marker": 1,

To understand the format better, make sure to check out the Forge documentation on the blockstate JSON format.

1.8 block modeling and texturing have been already discussed extensively in the modding community, so I won't repeat it here. But check out these tutorials and tools:
Tip: The JSON format allows for sub-models to be added based on property values.


Blocks That Don't Use JSON Models


Thanks to FredTargaryen for this tip.

It is possible that you still want to "hard code" a block model. In that case you may have to trick out the JSON system by creating "dummy" JSONs for the block states and the block item:

  1. Create a blockstates JSON that covers all the property value variants but just write { "model": "" } for each one.
  2. However, this doesn't work for the inventory "variant". Instead of looking in blockstates, it looks in models/item for [block unlocalized name].json. So to make a dummy item model, it's as simple as writing {"parent": "block/cube"}. Also don't set a creative tab for the block.

Blocks With Flexible Models


In 1.8 the JSON model approach has made it tedious for those who have blocks that may have a large number of model variations or states. The answer to this is to use ISmartBlockModel and ExtendedBlockState and related model baking classes.

See a great tutorial on this at Herbix's Flexible Block Model Tutorial.

4 comments:

  1. Hello,

    I'm writing my own blog with Minecraft Tutorials, and when I need to paste some code, I want to have a little border around it just like you do. I was wondering how you did that, as I don't recall seeing a button for a border in the oage editor. Mind explaining?

    - Lars Gerrits

    ReplyDelete
    Replies
    1. Use div's like:
      {div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
      'Content goes here'
      {/div>

      Replacing the { with <

      Delete
  2. Hey, thanks for writing.

    I use the website www.hilite.me and select Java language and color scheme that I like. Then I paste in my code as plain text (after making sure to use spaces instead of tabs in the code, since that allows more controllable formatting). Then I click the Highlight button and copy the result.

    Then in my blog, while in page edit mode, I switch to HTML and paste as plain texte.

    Note that the default line spacing of the www.hilite.me output seems to be 125% which I usually search and replace to 100%.

    The border is included in the output from the www.hilite.me parser. You could probably inspect that to simply figure out what the tags are for creating the border, but I find that just copying the output works well enough for me.

    Hope that helps!

    ReplyDelete
  3. Can you send a pic of your Gui? Just want to know how it is looking

    ReplyDelete