Mojira Archive
MC-17851

Blocks requiring any support block (carpets, signs) placed on piston head break when sticky piston retracts

The bug

Blocks requiring any support block, like carpets or signs, normally do not drop when the supporting block is moved (see MC-8836 which may be intended), however they do break when the supporting block is the piston head which retracts even though a different block is pulled beneath them.

For carpets this happens as well when the carpet itself is moved because it is on top of an upwards facing piston.

See also the attached video Carpets and signs dropping when supporting piston head retracts.mp4.

How to reproduce

  1. Place an upwards facing sticky piston
  2. Place a carpet on top
  3. Power the piston
    → The carpet drops
  4. Now place a slime block between the piston and carpet
  5. Power the piston
    → The carpet does not drop as expected

Code analysis

Based on 1.11.2 decompiled using MCP 9.37

The problem is that the methods BlockPistonBase.eventReceived(IBlockState, World, BlockPos, int, int) and BlockPistonBase.doMove(World, BlockPos, EnumFacing, boolean) update neighboring blocks when setting blockstates.

Suggested fix

Note: It would be good if someone familiar with expected piston behavior and their use in farms or slime block machines could verify that this fix solves this problem as expected.

net.minecraft.block.BlockPistonBase.eventReceived(IBlockState, World, BlockPos, int, int)
/**
 * Called on both Client and Server when World#addBlockEvent is called. On the Server, this may perform additional
 * changes to the world, like pistons replacing the block with an extended base. On the client, the update may
 * involve replacing tile entities, playing sounds, or performing other visual actions to reflect the server side
 * changes.
 */
public boolean eventReceived(IBlockState state, World worldIn, BlockPos pos, int id, int param)
{
    EnumFacing enumfacing = (EnumFacing)state.getValue(FACING);

    // ...

    if (id == 0)
    {
        // ...
    }
    else if (id == 1)
    {
        // ...

        // Comment: Replaced this
        // worldIn.setBlockState(pos, Blocks.PISTON_EXTENSION.getDefaultState().withProperty(BlockPistonMoving.FACING, enumfacing).withProperty(BlockPistonMoving.TYPE, this.isSticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT), 3);
        worldIn.setBlockState(pos, Blocks.PISTON_EXTENSION.getDefaultState().withProperty(BlockPistonMoving.FACING, enumfacing).withProperty(BlockPistonMoving.TYPE, this.isSticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT), 2);
        worldIn.setTileEntity(pos, BlockPistonMoving.createTilePiston(this.getStateFromMeta(param), enumfacing, false, true));

        if (this.isSticky)
        {
            // ...

            if (!flag1 && iblockstate.getMaterial() != Material.AIR && canPush(iblockstate, worldIn, blockpos, enumfacing.getOpposite(), false) && (iblockstate.getMobilityFlag() == EnumPushReaction.NORMAL || block == Blocks.PISTON || block == Blocks.STICKY_PISTON))
            {
                this.doMove(worldIn, pos, enumfacing, false);
            }
            // Comment: Added this
            else {
                worldIn.setBlockToAir(pos.offset(enumfacing));
            }
        }
        else
        {
            worldIn.setBlockToAir(pos.offset(enumfacing));
        }

        worldIn.playSound((EntityPlayer)null, pos, SoundEvents.BLOCK_PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5F, worldIn.rand.nextFloat() * 0.15F + 0.6F);
    }

    return true;
}
net.minecraft.block.BlockPistonBase.doMove(World, BlockPos, EnumFacing, boolean)
private boolean doMove(World worldIn, BlockPos pos, EnumFacing direction, boolean extending)
{
    if (!extending)
    {
        // Comment: Replaced this
        // worldIn.setBlockToAir(pos.offset(direction));
        worldIn.setBlockState(pos.offset(direction), Blocks.AIR.getDefaultState(), 2);
    }
    
    // ...
}