gt:             for (int l = list.size() - 1; l >= 0; --l)
context: package net.minecraft.block;
  
  import com.google.common.collect.Lists;
  import java.util.List;
  import javax.annotation.Nullable;
  import net.minecraft.block.material.EnumPushReaction;
  import net.minecraft.block.material.Material;
  import net.minecraft.block.properties.IProperty;
  import net.minecraft.block.properties.PropertyBool;
  import net.minecraft.block.state.BlockPistonStructureHelper;
  import net.minecraft.block.state.BlockStateContainer;
  import net.minecraft.block.state.IBlockState;
  import net.minecraft.creativetab.CreativeTabs;
  import net.minecraft.entity.Entity;
  import net.minecraft.entity.EntityLivingBase;
  import net.minecraft.entity.player.EntityPlayer;
  import net.minecraft.init.Blocks;
  import net.minecraft.init.SoundEvents;
  import net.minecraft.item.ItemStack;
  import net.minecraft.tileentity.TileEntity;
  import net.minecraft.tileentity.TileEntityPiston;
  import net.minecraft.util.EnumFacing;
  import net.minecraft.util.Mirror;
  import net.minecraft.util.Rotation;
  import net.minecraft.util.SoundCategory;
  import net.minecraft.util.math.AxisAlignedBB;
  import net.minecraft.util.math.BlockPos;
  import net.minecraft.util.math.MathHelper;
  import net.minecraft.world.IBlockAccess;
  import net.minecraft.world.World;
  
  public class BlockPistonBase extends BlockDirectional
  {
      public static final PropertyBool EXTENDED = PropertyBool.create("extended");
      protected static final AxisAlignedBB PISTON_BASE_EAST_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.75D, 1.0D, 1.0D);
      protected static final AxisAlignedBB PISTON_BASE_WEST_AABB = new AxisAlignedBB(0.25D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D);
      protected static final AxisAlignedBB PISTON_BASE_SOUTH_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.75D);
      protected static final AxisAlignedBB PISTON_BASE_NORTH_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.25D, 1.0D, 1.0D, 1.0D);
      protected static final AxisAlignedBB PISTON_BASE_UP_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
      protected static final AxisAlignedBB PISTON_BASE_DOWN_AABB = new AxisAlignedBB(0.0D, 0.25D, 0.0D, 1.0D, 1.0D, 1.0D);
      /** This piston is the sticky one? */
      private final boolean isSticky;
  
      public BlockPistonBase(boolean isSticky)
      {
          super(Material.PISTON);
          this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH).withProperty(EXTENDED, Boolean.valueOf(false)));
          this.isSticky = isSticky;
          this.setSoundType(SoundType.STONE);
          this.setHardness(0.5F);
          this.setCreativeTab(CreativeTabs.REDSTONE);
      }
  
      public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
      {
          if (((Boolean)state.getValue(EXTENDED)).booleanValue())
          {
              switch ((EnumFacing)state.getValue(FACING))
              {
                  case DOWN:
                      return PISTON_BASE_DOWN_AABB;
                  case UP:
                  default:
                      return PISTON_BASE_UP_AABB;
                  case NORTH:
                      return PISTON_BASE_NORTH_AABB;
                  case SOUTH:
                      return PISTON_BASE_SOUTH_AABB;
                  case WEST:
                      return PISTON_BASE_WEST_AABB;
                  case EAST:
                      return PISTON_BASE_EAST_AABB;
              }
          }
          else
          {
              return FULL_BLOCK_AABB;
          }
      }
  
      /**
       * Checks if an IBlockState represents a block that is opaque and a full cube.
       */
      public boolean isFullyOpaque(IBlockState state)
      {
          return !((Boolean)state.getValue(EXTENDED)).booleanValue() || state.getValue(FACING) == EnumFacing.DOWN;
      }
  
      public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List<AxisAlignedBB> collidingBoxes, @Nullable Entity entityIn)
      {
          addCollisionBoxToList(pos, entityBox, collidingBoxes, state.getBoundingBox(worldIn, pos));
      }
  
      /**
       * Used to determine ambient occlusion and culling when rebuilding chunks for render
       */
      public boolean isOpaqueCube(IBlockState state)
      {
          return false;
      }
  
      /**
       * Called by ItemBlocks after a block is set in the world, to allow post-place logic
       */
      public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
      {
          worldIn.setBlockState(pos, state.withProperty(FACING, getFacingFromEntity(pos, placer)), 2);
  
          if (!worldIn.isRemote)
          {
              this.checkForMove(worldIn, pos, state);
          }
      }
  
      /**
       * Called when a neighboring block was changed and marks that this state should perform any checks during a neighbor
       * change. Cases may include when redstone power is updated, cactus blocks popping off due to a neighboring solid
       * block, etc.
       */
      public void neighborChanged(IBlockState state, World worldIn, BlockPos pos, Block blockIn)
      {
          if (!worldIn.isRemote)
          {
              this.checkForMove(worldIn, pos, state);
          }
      }
  
      public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state)
      {
          if (!worldIn.isRemote && worldIn.getTileEntity(pos) == null)
          {
              this.checkForMove(worldIn, pos, state);
          }
      }
  
      /**
       * Called by ItemBlocks just before a block is actually set in the world, to allow for adjustments to the
       * IBlockstate
       */
      public IBlockState onBlockPlaced(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
      {
          return this.getDefaultState().withProperty(FACING, getFacingFromEntity(pos, placer)).withProperty(EXTENDED, Boolean.valueOf(false));
      }
  
      private void checkForMove(World worldIn, BlockPos pos, IBlockState state)
      {
          EnumFacing enumfacing = (EnumFacing)state.getValue(FACING);
          boolean flag = this.shouldBeExtended(worldIn, pos, enumfacing);
  
          if (flag && !((Boolean)state.getValue(EXTENDED)).booleanValue())
          {
              if ((new BlockPistonStructureHelper(worldIn, pos, enumfacing, true)).canMove())
              {
                  worldIn.addBlockEvent(pos, this, 0, enumfacing.getIndex());
              }
          }
          else if (!flag && ((Boolean)state.getValue(EXTENDED)).booleanValue())
          {
              worldIn.addBlockEvent(pos, this, 1, enumfacing.getIndex());
          }
      }
  
      private boolean shouldBeExtended(World worldIn, BlockPos pos, EnumFacing facing)
      {
          for (EnumFacing enumfacing : EnumFacing.values())
          {
              if (enumfacing != facing && worldIn.isSidePowered(pos.offset(enumfacing), enumfacing))
              {
                  return true;
              }
          }
  
          if (worldIn.isSidePowered(pos, EnumFacing.DOWN))
          {
              return true;
          }
          else
          {
              BlockPos blockpos = pos.up();
  
              for (EnumFacing enumfacing1 : EnumFacing.values())
              {
                  if (enumfacing1 != EnumFacing.DOWN && worldIn.isSidePowered(blockpos.offset(enumfacing1), enumfacing1))
                  {
                      return true;
                  }
              }
  
              return false;
          }
      }
  
      /**
       * 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.
       *  
       * @param state The block state retrieved from the block position prior to this method being invoked
       * @param pos The position of the block event. Can be used to retrieve tile entities.
       */
      public boolean eventReceived(IBlockState state, World worldIn, BlockPos pos, int id, int param)
      {
          EnumFacing enumfacing = (EnumFacing)state.getValue(FACING);
  
          if (!worldIn.isRemote)
          {
              boolean flag = this.shouldBeExtended(worldIn, pos, enumfacing);
  
              if (flag && id == 1)
              {
                  worldIn.setBlockState(pos, state.withProperty(EXTENDED, Boolean.valueOf(true)), 2);
                  return false;
              }
  
              if (!flag && id == 0)
              {
                  return false;
              }
          }
  
          if (id == 0)
          {
              if (!this.doMove(worldIn, pos, enumfacing, true))
              {
                  return false;
              }
  
              worldIn.setBlockState(pos, state.withProperty(EXTENDED, Boolean.valueOf(true)), 2);
              worldIn.playSound((EntityPlayer)null, pos, SoundEvents.BLOCK_PISTON_EXTEND, SoundCategory.BLOCKS, 0.5F, worldIn.rand.nextFloat() * 0.25F + 0.6F);
          }
          else if (id == 1)
          {
              TileEntity tileentity1 = worldIn.getTileEntity(pos.offset(enumfacing));
  
              if (tileentity1 instanceof TileEntityPiston)
              {
                  ((TileEntityPiston)tileentity1).clearPistonTileEntity();
              }
  
              worldIn.setBlockState(pos, Blocks.PISTON_EXTENSION.getDefaultState().withProperty(BlockPistonMoving.FACING, enumfacing).withProperty(BlockPistonMoving.TYPE, this.isSticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT), 3);
              worldIn.setTileEntity(pos, BlockPistonMoving.createTilePiston(this.getStateFromMeta(param), enumfacing, false, true));
  
              if (this.isSticky)
              {
                  BlockPos blockpos = pos.add(enumfacing.getFrontOffsetX() * 2, enumfacing.getFrontOffsetY() * 2, enumfacing.getFrontOffsetZ() * 2);
                  IBlockState iblockstate = worldIn.getBlockState(blockpos);
                  Block block = iblockstate.getBlock();
                  boolean flag1 = false;
  
                  if (block == Blocks.PISTON_EXTENSION)
                  {
                      TileEntity tileentity = worldIn.getTileEntity(blockpos);
  
                      if (tileentity instanceof TileEntityPiston)
                      {
                          TileEntityPiston tileentitypiston = (TileEntityPiston)tileentity;
  
                          if (tileentitypiston.getFacing() == enumfacing && tileentitypiston.isExtending())
                          {
                              tileentitypiston.clearPistonTileEntity();
                              flag1 = true;
                          }
                      }
                  }
  
                  if (!flag1 && !iblockstate.getBlock().isAir(iblockstate, worldIn, pos) && canPush(iblockstate, worldIn, blockpos, enumfacing.getOpposite(), false) && (iblockstate.getMobilityFlag() == EnumPushReaction.NORMAL || block == Blocks.PISTON || block == Blocks.STICKY_PISTON))
                  {
                      this.doMove(worldIn, pos, enumfacing, false);
                  }
              }
              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;
      }
  
      public boolean isFullCube(IBlockState state)
      {
          return false;
      }
  
      @Nullable
      public static EnumFacing getFacing(int meta)
      {
          int i = meta & 7;
          return i > 5 ? null : EnumFacing.getFront(i);
      }
  
      public static EnumFacing getFacingFromEntity(BlockPos pos, EntityLivingBase p_185647_1_)
      {
          if (MathHelper.abs((float)p_185647_1_.posX - (float)pos.getX()) < 2.0F && MathHelper.abs((float)p_185647_1_.posZ - (float)pos.getZ()) < 2.0F)
          {
              double d0 = p_185647_1_.posY + (double)p_185647_1_.getEyeHeight();
  
              if (d0 - (double)pos.getY() > 2.0D)
              {
                  return EnumFacing.UP;
              }
  
              if ((double)pos.getY() - d0 > 0.0D)
              {
                  return EnumFacing.DOWN;
              }
          }
  
          return p_185647_1_.getHorizontalFacing().getOpposite();
      }
  
      /**
       * Checks if the piston can push the given BlockState.
       */
      public static boolean canPush(IBlockState blockStateIn, World worldIn, BlockPos pos, EnumFacing facing, boolean destroyBlocks)
      {
          Block block = blockStateIn.getBlock();
  
          if (block == Blocks.OBSIDIAN)
          {
              return false;
          }
          else if (!worldIn.getWorldBorder().contains(pos))
          {
              return false;
          }
          else if (pos.getY() >= 0 && (facing != EnumFacing.DOWN || pos.getY() != 0))
          {
              if (pos.getY() <= worldIn.getHeight() - 1 && (facing != EnumFacing.UP || pos.getY() != worldIn.getHeight() - 1))
              {
                  if (block != Blocks.PISTON && block != Blocks.STICKY_PISTON)
                  {
                      if (blockStateIn.getBlockHardness(worldIn, pos) == -1.0F)
                      {
                          return false;
                      }
  
                      if (blockStateIn.getMobilityFlag() == EnumPushReaction.BLOCK)
                      {
                          return false;
                      }
  
                      if (blockStateIn.getMobilityFlag() == EnumPushReaction.DESTROY)
                      {
                          return destroyBlocks;
                      }
                  }
                  else if (((Boolean)blockStateIn.getValue(EXTENDED)).booleanValue())
                  {
                      return false;
                  }
  
                  return !block.hasTileEntity(blockStateIn);
              }
              else
              {
                  return false;
              }
          }
          else
          {
              return false;
          }
      }
  
      private boolean doMove(World worldIn, BlockPos pos, EnumFacing direction, boolean extending)
      {
          if (!extending)
          {
              worldIn.setBlockToAir(pos.offset(direction));
          }
  
          BlockPistonStructureHelper blockpistonstructurehelper = new BlockPistonStructureHelper(worldIn, pos, direction, extending);
  
          if (!blockpistonstructurehelper.canMove())
          {
              return false;
          }
          else
          {
              List<BlockPos> list = blockpistonstructurehelper.getBlocksToMove();
              List<IBlockState> list1 = Lists.<IBlockState>newArrayList();
  
              for (int i = 0; i < list.size(); ++i)
              {
                  BlockPos blockpos = (BlockPos)list.get(i);
                  list1.add(worldIn.getBlockState(blockpos).getActualState(worldIn, blockpos));
              }
  
              List<BlockPos> list2 = blockpistonstructurehelper.getBlocksToDestroy();
              int k = list.size() + list2.size();
              IBlockState[] aiblockstate = new IBlockState[k];
              EnumFacing enumfacing = extending ? direction : direction.getOpposite();
  
              for (int j = list2.size() - 1; j >= 0; --j)
              {
                  BlockPos blockpos1 = (BlockPos)list2.get(j);
                  IBlockState iblockstate = worldIn.getBlockState(blockpos1);
                  // Forge: With our change to how snowballs are dropped this needs to disallow to mimic vanilla behavior.
                  float chance = iblockstate.getBlock() instanceof BlockSnow ? -1.0f : 1.0f;
                  iblockstate.getBlock().dropBlockAsItemWithChance(worldIn, blockpos1, iblockstate, chance, 0);
                  worldIn.setBlockToAir(blockpos1);
                  --k;
                  aiblockstate[k] = iblockstate;
              }
  
  