Get rid of BlockPos in Minecraft 1.8

This is a diaries log of my journey to try to get rid of BlockPos, Minecraft 1.8's worst decision in making it more usable by modders. Just click on the headlines to close read chapters.

First steps

Before startup, I discovered some speed tests in java directly using a shrinked class BlockPos and an array. The idea is that there must not be a class that contains coords, but there could be an int array with 3 fields (0 for X, 1 for Y and 2 for Z). With that, creating coordinates is 6 times faster on my computer.

Using Forge 1.8-11.14.1.1329

Starting this project in 24th of Feb. 2015, I used the current forge version and stick to it.

Setting up forge

Typically installed forge source, called gradlew setupForge eclipse, as usual.

Finding the entry point for manipulating everything

After setting up, there is a project Forge (besides Clean) in the workspace. It contains the deobfuscated sources. This is where I change things (not in Clean).

Before any changes on the classes, I got about 25 - 50 fps on moving around.

BlockPos vs int[]

Under net.minecraft.util, you can find the BlockPos class. It contains all methods to be used. All its methods are cloned into static ones that return an int[] instead of BlockPos. From

@Deprecated public BlockPos offset(EnumFacing facing, int n) { return new BlockPos(this.getX() + facing.getFrontOffsetX() * n, this.getY() + facing.getFrontOffsetY() * n, this.getZ() + facing.getFrontOffsetZ() * n); }

I make a final static version and call the original one deprecated.

public final static int[] offset(int[] xyz, EnumFacing facing, int n) { return getBlockPos(xyz[0] + facing.getFrontOffsetX() * n, xyz[1] + facing.getFrontOffsetY() * n, xyz[2] + facing.getFrontOffsetZ() * n); }

This is done for all the other methods also.

First experiences

Trying to measure what the changes would do; here I took a method from World-class in net.minecraft.world.World

The original code was to iterate over a list of new BlockPos classes; I switched it to iterate over a list of int[], but keep the old code running. If I would replace it, I'd use toBlockPos instead of blockpos1 as param for this.getBlockState in the last line.

public Block getGroundAboveSeaLevel(BlockPos pos) { BlockPos blockpos1; FMLLog.info("before getGroundAboveSeaLevel (by BlockPos): %s", new SimpleDateFormat("hh:mm:ss.SSS").format(new Date())); for (blockpos1 = new BlockPos(pos.getX(), 63, pos.getZ()); !this.isAirBlock(blockpos1.up()); blockpos1 = blockpos1.up()) { ; } FMLLog.info("after getGroundAboveSeaLevel (by BlockPos): %s", new SimpleDateFormat("hh:mm:ss.SSS").format(new Date())); FMLLog.info("before getGroundAboveSeaLevel (by int[]): %s", new SimpleDateFormat("hh:mm:ss.SSS").format(new Date())); int[] blockpos; for (blockpos = new int[]{pos.getX(), 63, pos.getZ()}; !this.isAirBlock(blockpos1.up()); blockpos = BlockPos.up(blockpos)) { ; } BlockPos toBlockPos = new BlockPos(blockpos[0], blockpos[1], blockpos[2]); FMLLog.info("after getGroundAboveSeaLevel (by int[]): %s", new SimpleDateFormat("hh:mm:ss.SSS").format(new Date())); return this.getBlockState(blockpos1).getBlock(); }

     [21:53:35] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 09:53:35.043
     [21:53:35] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 09:53:35.166
     [21:53:35] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 09:53:35.167
     [21:53:35] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 09:53:35.167
    

The results are dramatic! Even this routine just runs once on world generation (maybe also on exploring, I don't know), it takes about 120 ms on using BlockPos and 0 ms on using int[]. Suggest this effect in for loops... and yes, there are some. By the way, here are some more logs of this change:

    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.602
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.750
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.750
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.750
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.751
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.804
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.804
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.805
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.805
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.821
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.821
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.821
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.821
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.836
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.836
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.836
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.836
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.852
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.852
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.853
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.853
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.870
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.870
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.870
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.871
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.887
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.887
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.888
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.888
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:10.995
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:10.996
    [22:21:10] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:10.996
    [22:21:10] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:10.996
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:11.012
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:11.012
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:11.013
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:11.013
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:11.026
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:11.026
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:11.027
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:11.027
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:11.038
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:11.038
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:11.039
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by BlockPos): 10:21:11.039
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by BlockPos): 10:21:11.053
    [22:21:11] [Server thread/INFO] [FML]: before getGroundAboveSeaLevel (by int[]): 10:21:11.053
    [22:21:11] [Server thread/INFO] [FML]: after  getGroundAboveSeaLevel (by int[]): 10:21:11.053
    

For changing the code, I need a replacement for the isAirBlock(BlockPos) method by an isAirBlock(int[]) method, but this goes a bit deeper; you can explore this yourself.

With this changes, I generate patch files by gradlew genPatches for download: patches.7z. Even with this view Changes, I got 30 - 60 fps.

Project stopped due to discussion on forge