Block Data Reorganization

Now that I’m actually trying to use blocks a little bit, I’ve realized they needed a bit of changing from my earlier theories.  The changes had the convenient side effect of shrinking the amount of data stored in each block by a little, so I can use a little less memory in the game and a decent amount less on the disk.  However, due to the prolific number of references in the code to blocks (the smallest piece of the world) there were a ton of places that needed to be changed to line up with this new arrangement of information.

The fields on a block when I started this process were laid out like this:

short blockType;  // 2 bytes
byte variant;     // 1 byte
byte spatial;     // 1 byte
long extra;       // 8 bytes

These combined to be a 12 byte block with loads of space in the extra area to store fancy bits.

The problem was that almost immediately I came up with a couple of spatial arrangements that wouldn’t fit in the space I’d provided for spatial data.  This made me want to increase the spatial data from 1 byte to 2.  It’s worth pointing out that every byte on a block takes up roughly 4096 bytes in an in-memory chunk, or over 2MB in a fully realized cluster.  That means making a block bigger is something that deserves a bit of pondering before you just start throwing around bytes.

Next I considered the variant data and realized that it is a seldom used data value for when there are distinct parts of the same type of block.  In other words almost none of the normal types of blocks will ever have a variant.  That makes it a waste of 8 bits of storage almost all the time.

Most blocks that are related are simply different block types.  For example, oak wood and oak leaves are two different types of block, not two variants of oak.  A good example of a block with variants is a bed.  When you place a bed it takes up multiple blocks in space but all of the blocks occupied are still bed blocks.  For narrow beds there are the Head and the Foot variants and for a wide bed there are the Head Left, Head Right, Foot Left, and Foot Right variants.  What that boils down to is that I can move the variant information into the extra data area and free up that mostly wasted byte.

Next I considered tinting.  This is the fun ability to make a torch light up blue instead of yellow or to create fancy colored sheep.  This information needs to be present on nearly every single type of block and yet it currently lives in the extra data area.  That chews up 12 bits of the extra data area almost all the time.  That’s not really a problem, but it’s a pretty big chunk of extra data that’s really there all the time.  That means that the tint should really be moved into its own field on the block.

Tinting data is a little bit wasteful because the new field for it on a block is 16 bits and the tinting itself is 12 bits, but at the end of the day wasting 4 bits for tinting all the time is better than wasting 8 bits almost all the time for variant information that’s almost never used.

Next I moved on to the extra data area.  I came up with an idea of making a sort of extra data overflow that lives in the chunk.  This data space means that there is less concern about running out of bits in the extra data area and just being stuck.  With that in mind I decided to shrink the extra data area from 8 bytes to 4 bytes.

That means that the new layout of the fields on a block are:

short blockType;  // 2 bytes 
short tint;       // 2 bytes 
short spatial;    // 2 bytes
int extra;        // 4 bytes

This new layout gives me more useful spatial data space, turns tint into a first class citizen, pushes the variant information into the extra data area, and shrinks the extra data area with the option of chunk level expansion.  That means that I can now fit a block in only 10 bytes.

I like this arrangement better, but as with anything else, we’ll just have to see how it plays out and whether or not I’ll end up making more changes.