package net.minecraft.world.level.block.piston; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.BlockTags; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.SignalGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.DirectionalBlock; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.PistonType; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; public class PistonBaseBlock extends DirectionalBlock { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( p_360537_ -> p_360537_.group(Codec.BOOL.fieldOf("sticky").forGetter(p_309381_ -> p_309381_.isSticky), propertiesCodec()) .apply(p_360537_, PistonBaseBlock::new) ); public static final BooleanProperty EXTENDED = BlockStateProperties.EXTENDED; public static final int TRIGGER_EXTEND = 0; public static final int TRIGGER_CONTRACT = 1; public static final int TRIGGER_DROP = 2; public static final int PLATFORM_THICKNESS = 4; private static final Map SHAPES = Shapes.rotateAll(Block.boxZ(16.0, 4.0, 16.0)); private final boolean isSticky; @Override public MapCodec codec() { return CODEC; } public PistonBaseBlock(boolean p_60163_, BlockBehaviour.Properties p_60164_) { super(p_60164_); this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(EXTENDED, false)); this.isSticky = p_60163_; } @Override protected VoxelShape getShape(BlockState p_60220_, BlockGetter p_60221_, BlockPos p_60222_, CollisionContext p_60223_) { return p_60220_.getValue(EXTENDED) ? SHAPES.get(p_60220_.getValue(FACING)) : Shapes.block(); } @Override public void setPlacedBy(Level p_60172_, BlockPos p_60173_, BlockState p_60174_, LivingEntity p_60175_, ItemStack p_60176_) { if (!p_60172_.isClientSide) { this.checkIfExtend(p_60172_, p_60173_, p_60174_); } } @Override protected void neighborChanged(BlockState p_60198_, Level p_60199_, BlockPos p_60200_, Block p_60201_, @Nullable Orientation p_366407_, boolean p_60203_) { if (!p_60199_.isClientSide) { this.checkIfExtend(p_60199_, p_60200_, p_60198_); } } @Override protected void onPlace(BlockState p_60225_, Level p_60226_, BlockPos p_60227_, BlockState p_60228_, boolean p_60229_) { if (!p_60228_.is(p_60225_.getBlock())) { if (!p_60226_.isClientSide && p_60226_.getBlockEntity(p_60227_) == null) { this.checkIfExtend(p_60226_, p_60227_, p_60225_); } } } @Override public BlockState getStateForPlacement(BlockPlaceContext p_60166_) { return this.defaultBlockState().setValue(FACING, p_60166_.getNearestLookingDirection().getOpposite()).setValue(EXTENDED, false); } private void checkIfExtend(Level p_60168_, BlockPos p_60169_, BlockState p_60170_) { Direction direction = p_60170_.getValue(FACING); boolean flag = this.getNeighborSignal(p_60168_, p_60169_, direction); if (flag && !p_60170_.getValue(EXTENDED)) { if (new PistonStructureResolver(p_60168_, p_60169_, direction, true).resolve()) { p_60168_.blockEvent(p_60169_, this, 0, direction.get3DDataValue()); } } else if (!flag && p_60170_.getValue(EXTENDED)) { BlockPos blockpos = p_60169_.relative(direction, 2); BlockState blockstate = p_60168_.getBlockState(blockpos); int i = 1; if (blockstate.is(Blocks.MOVING_PISTON) && blockstate.getValue(FACING) == direction && p_60168_.getBlockEntity(blockpos) instanceof PistonMovingBlockEntity pistonmovingblockentity && pistonmovingblockentity.isExtending() && ( pistonmovingblockentity.getProgress(0.0F) < 0.5F || p_60168_.getGameTime() == pistonmovingblockentity.getLastTicked() || ((ServerLevel)p_60168_).isHandlingTick() )) { i = 2; } p_60168_.blockEvent(p_60169_, this, i, direction.get3DDataValue()); } } private boolean getNeighborSignal(SignalGetter p_277378_, BlockPos p_60179_, Direction p_60180_) { for (Direction direction : Direction.values()) { if (direction != p_60180_ && p_277378_.hasSignal(p_60179_.relative(direction), direction)) { return true; } } if (p_277378_.hasSignal(p_60179_, Direction.DOWN)) { return true; } else { BlockPos blockpos = p_60179_.above(); for (Direction direction1 : Direction.values()) { if (direction1 != Direction.DOWN && p_277378_.hasSignal(blockpos.relative(direction1), direction1)) { return true; } } return false; } } @Override protected boolean triggerEvent(BlockState p_60192_, Level p_60193_, BlockPos p_60194_, int p_60195_, int p_60196_) { Direction direction = p_60192_.getValue(FACING); BlockState blockstate = p_60192_.setValue(EXTENDED, true); if (!p_60193_.isClientSide) { boolean flag = this.getNeighborSignal(p_60193_, p_60194_, direction); if (flag && (p_60195_ == 1 || p_60195_ == 2)) { p_60193_.setBlock(p_60194_, blockstate, 2); return false; } if (!flag && p_60195_ == 0) { return false; } } if (p_60195_ == 0) { if (!this.moveBlocks(p_60193_, p_60194_, direction, true)) { return false; } p_60193_.setBlock(p_60194_, blockstate, 67); p_60193_.playSound(null, p_60194_, SoundEvents.PISTON_EXTEND, SoundSource.BLOCKS, 0.5F, p_60193_.random.nextFloat() * 0.25F + 0.6F); p_60193_.gameEvent(GameEvent.BLOCK_ACTIVATE, p_60194_, GameEvent.Context.of(blockstate)); } else if (p_60195_ == 1 || p_60195_ == 2) { BlockEntity blockentity = p_60193_.getBlockEntity(p_60194_.relative(direction)); if (blockentity instanceof PistonMovingBlockEntity) { ((PistonMovingBlockEntity)blockentity).finalTick(); } BlockState blockstate1 = Blocks.MOVING_PISTON .defaultBlockState() .setValue(MovingPistonBlock.FACING, direction) .setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT); p_60193_.setBlock(p_60194_, blockstate1, 276); p_60193_.setBlockEntity( MovingPistonBlock.newMovingBlockEntity( p_60194_, blockstate1, this.defaultBlockState().setValue(FACING, Direction.from3DDataValue(p_60196_ & 7)), direction, false, true ) ); p_60193_.updateNeighborsAt(p_60194_, blockstate1.getBlock()); blockstate1.updateNeighbourShapes(p_60193_, p_60194_, 2); if (this.isSticky) { BlockPos blockpos = p_60194_.offset(direction.getStepX() * 2, direction.getStepY() * 2, direction.getStepZ() * 2); BlockState blockstate2 = p_60193_.getBlockState(blockpos); boolean flag1 = false; if (blockstate2.is(Blocks.MOVING_PISTON) && p_60193_.getBlockEntity(blockpos) instanceof PistonMovingBlockEntity pistonmovingblockentity && pistonmovingblockentity.getDirection() == direction && pistonmovingblockentity.isExtending()) { pistonmovingblockentity.finalTick(); flag1 = true; } if (!flag1) { if (p_60195_ != 1 || blockstate2.isAir() || !isPushable(blockstate2, p_60193_, blockpos, direction.getOpposite(), false, direction) || blockstate2.getPistonPushReaction() != PushReaction.NORMAL && !blockstate2.is(Blocks.PISTON) && !blockstate2.is(Blocks.STICKY_PISTON)) { p_60193_.removeBlock(p_60194_.relative(direction), false); } else { this.moveBlocks(p_60193_, p_60194_, direction, false); } } } else { p_60193_.removeBlock(p_60194_.relative(direction), false); } p_60193_.playSound(null, p_60194_, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, p_60193_.random.nextFloat() * 0.15F + 0.6F); p_60193_.gameEvent(GameEvent.BLOCK_DEACTIVATE, p_60194_, GameEvent.Context.of(blockstate1)); } return true; } public static boolean isPushable(BlockState p_60205_, Level p_60206_, BlockPos p_60207_, Direction p_60208_, boolean p_60209_, Direction p_60210_) { if (p_60207_.getY() < p_60206_.getMinY() || p_60207_.getY() > p_60206_.getMaxY() || !p_60206_.getWorldBorder().isWithinBounds(p_60207_)) { return false; } else if (p_60205_.isAir()) { return true; } else if (p_60205_.is(Blocks.OBSIDIAN) || p_60205_.is(Blocks.CRYING_OBSIDIAN) || p_60205_.is(Blocks.RESPAWN_ANCHOR) || p_60205_.is(Blocks.REINFORCED_DEEPSLATE)) { return false; } else if (p_60208_ == Direction.DOWN && p_60207_.getY() == p_60206_.getMinY()) { return false; } else if (p_60208_ == Direction.UP && p_60207_.getY() == p_60206_.getMaxY()) { return false; } else { if (!p_60205_.is(Blocks.PISTON) && !p_60205_.is(Blocks.STICKY_PISTON)) { if (p_60205_.getDestroySpeed(p_60206_, p_60207_) == -1.0F) { return false; } switch (p_60205_.getPistonPushReaction()) { case BLOCK: return false; case DESTROY: return p_60209_; case PUSH_ONLY: return p_60208_ == p_60210_; } } else if (p_60205_.getValue(EXTENDED)) { return false; } return !p_60205_.hasBlockEntity(); } } private boolean moveBlocks(Level p_60182_, BlockPos p_60183_, Direction p_60184_, boolean p_60185_) { BlockPos blockpos = p_60183_.relative(p_60184_); if (!p_60185_ && p_60182_.getBlockState(blockpos).is(Blocks.PISTON_HEAD)) { p_60182_.setBlock(blockpos, Blocks.AIR.defaultBlockState(), 276); } PistonStructureResolver pistonstructureresolver = new PistonStructureResolver(p_60182_, p_60183_, p_60184_, p_60185_); if (!pistonstructureresolver.resolve()) { return false; } else { Map map = Maps.newHashMap(); List list = pistonstructureresolver.getToPush(); List list1 = Lists.newArrayList(); for (BlockPos blockpos1 : list) { BlockState blockstate = p_60182_.getBlockState(blockpos1); list1.add(blockstate); map.put(blockpos1, blockstate); } List list2 = pistonstructureresolver.getToDestroy(); BlockState[] ablockstate = new BlockState[list.size() + list2.size()]; Direction direction = p_60185_ ? p_60184_ : p_60184_.getOpposite(); int i = 0; for (int j = list2.size() - 1; j >= 0; j--) { BlockPos blockpos2 = list2.get(j); BlockState blockstate1 = p_60182_.getBlockState(blockpos2); BlockEntity blockentity = blockstate1.hasBlockEntity() ? p_60182_.getBlockEntity(blockpos2) : null; dropResources(blockstate1, p_60182_, blockpos2, blockentity); if (!blockstate1.is(BlockTags.FIRE) && p_60182_.isClientSide()) { p_60182_.levelEvent(2001, blockpos2, getId(blockstate1)); } p_60182_.setBlock(blockpos2, Blocks.AIR.defaultBlockState(), 18); p_60182_.gameEvent(GameEvent.BLOCK_DESTROY, blockpos2, GameEvent.Context.of(blockstate1)); ablockstate[i++] = blockstate1; } for (int k = list.size() - 1; k >= 0; k--) { BlockPos blockpos3 = list.get(k); BlockState blockstate5 = p_60182_.getBlockState(blockpos3); blockpos3 = blockpos3.relative(direction); map.remove(blockpos3); BlockState blockstate7 = Blocks.MOVING_PISTON.defaultBlockState().setValue(FACING, p_60184_); p_60182_.setBlock(blockpos3, blockstate7, 324); p_60182_.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockpos3, blockstate7, list1.get(k), p_60184_, p_60185_, false)); ablockstate[i++] = blockstate5; } if (p_60185_) { PistonType pistontype = this.isSticky ? PistonType.STICKY : PistonType.DEFAULT; BlockState blockstate4 = Blocks.PISTON_HEAD.defaultBlockState().setValue(PistonHeadBlock.FACING, p_60184_).setValue(PistonHeadBlock.TYPE, pistontype); BlockState blockstate6 = Blocks.MOVING_PISTON .defaultBlockState() .setValue(MovingPistonBlock.FACING, p_60184_) .setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT); map.remove(blockpos); p_60182_.setBlock(blockpos, blockstate6, 324); p_60182_.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockpos, blockstate6, blockstate4, p_60184_, true, true)); } BlockState blockstate3 = Blocks.AIR.defaultBlockState(); for (BlockPos blockpos4 : map.keySet()) { p_60182_.setBlock(blockpos4, blockstate3, 82); } for (Entry entry : map.entrySet()) { BlockPos blockpos5 = entry.getKey(); BlockState blockstate2 = entry.getValue(); blockstate2.updateIndirectNeighbourShapes(p_60182_, blockpos5, 2); blockstate3.updateNeighbourShapes(p_60182_, blockpos5, 2); blockstate3.updateIndirectNeighbourShapes(p_60182_, blockpos5, 2); } Orientation orientation = ExperimentalRedstoneUtils.initialOrientation(p_60182_, pistonstructureresolver.getPushDirection(), null); i = 0; for (int l = list2.size() - 1; l >= 0; l--) { BlockState blockstate8 = ablockstate[i++]; BlockPos blockpos6 = list2.get(l); if (p_60182_ instanceof ServerLevel serverlevel) { blockstate8.affectNeighborsAfterRemoval(serverlevel, blockpos6, false); } blockstate8.updateIndirectNeighbourShapes(p_60182_, blockpos6, 2); p_60182_.updateNeighborsAt(blockpos6, blockstate8.getBlock(), orientation); } for (int i1 = list.size() - 1; i1 >= 0; i1--) { p_60182_.updateNeighborsAt(list.get(i1), ablockstate[i++].getBlock(), orientation); } if (p_60185_) { p_60182_.updateNeighborsAt(blockpos, Blocks.PISTON_HEAD, orientation); } return true; } } @Override protected BlockState rotate(BlockState p_60215_, Rotation p_60216_) { return p_60215_.setValue(FACING, p_60216_.rotate(p_60215_.getValue(FACING))); } @Override protected BlockState mirror(BlockState p_60212_, Mirror p_60213_) { return p_60212_.rotate(p_60213_.getRotation(p_60212_.getValue(FACING))); } @Override protected void createBlockStateDefinition(StateDefinition.Builder p_60218_) { p_60218_.add(FACING, EXTENDED); } @Override protected boolean useShapeForLightOcclusion(BlockState p_60231_) { return p_60231_.getValue(EXTENDED); } @Override protected boolean isPathfindable(BlockState p_60187_, PathComputationType p_60190_) { return false; } }