package net.minecraft.world.level.block; import com.mojang.logging.LogUtils; import com.mojang.serialization.MapCodec; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; import net.minecraft.BlockUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.Relative; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; 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.EnumProperty; import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.portal.PortalShape; import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.slf4j.Logger; public class NetherPortalBlock extends Block implements Portal { private static final Logger LOGGER = LogUtils.getLogger(); public static final MapCodec CODEC = simpleCodec(NetherPortalBlock::new); public static final EnumProperty AXIS = BlockStateProperties.HORIZONTAL_AXIS; private static final Map SHAPES = Shapes.rotateHorizontalAxis(Block.column(4.0, 16.0, 0.0, 16.0)); @Override public MapCodec codec() { return CODEC; } public NetherPortalBlock(BlockBehaviour.Properties p_54909_) { super(p_54909_); this.registerDefaultState(this.stateDefinition.any().setValue(AXIS, Direction.Axis.X)); } @Override protected VoxelShape getShape(BlockState p_54942_, BlockGetter p_54943_, BlockPos p_54944_, CollisionContext p_54945_) { return SHAPES.get(p_54942_.getValue(AXIS)); } @Override protected VoxelShape getEntityInsideCollisionShape(BlockState p_396779_, BlockGetter p_395715_, BlockPos p_396107_, Entity p_395206_) { return p_396779_.getShape(p_395715_, p_396107_); } @Override protected void randomTick(BlockState p_221799_, ServerLevel p_221800_, BlockPos p_221801_, RandomSource p_221802_) { if (p_221800_.dimensionType().natural() && p_221800_.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && p_221802_.nextInt(2000) < p_221800_.getDifficulty().getId() && p_221800_.anyPlayerCloseEnoughForSpawning(p_221801_)) { while (p_221800_.getBlockState(p_221801_).is(this)) { p_221801_ = p_221801_.below(); } if (p_221800_.getBlockState(p_221801_).isValidSpawn(p_221800_, p_221801_, EntityType.ZOMBIFIED_PIGLIN)) { Entity entity = EntityType.ZOMBIFIED_PIGLIN.spawn(p_221800_, p_221801_.above(), EntitySpawnReason.STRUCTURE); if (entity != null) { entity.setPortalCooldown(); Entity entity1 = entity.getVehicle(); if (entity1 != null) { entity1.setPortalCooldown(); } } } } } @Override protected BlockState updateShape( BlockState p_54928_, LevelReader p_367647_, ScheduledTickAccess p_363604_, BlockPos p_54932_, Direction p_54929_, BlockPos p_54933_, BlockState p_54930_, RandomSource p_368816_ ) { Direction.Axis direction$axis = p_54929_.getAxis(); Direction.Axis direction$axis1 = p_54928_.getValue(AXIS); boolean flag = direction$axis1 != direction$axis && direction$axis.isHorizontal(); return !flag && !p_54930_.is(this) && !PortalShape.findAnyShape(p_367647_, p_54932_, direction$axis1).isComplete() ? Blocks.AIR.defaultBlockState() : super.updateShape(p_54928_, p_367647_, p_363604_, p_54932_, p_54929_, p_54933_, p_54930_, p_368816_); } @Override protected void entityInside(BlockState p_54915_, Level p_54916_, BlockPos p_54917_, Entity p_54918_, InsideBlockEffectApplier p_392916_) { if (p_54918_.canUsePortal(false)) { p_54918_.setAsInsidePortal(this, p_54917_); } } @Override public int getPortalTransitionTime(ServerLevel p_342064_, Entity p_344634_) { return p_344634_ instanceof Player player ? Math.max(0, p_342064_.getGameRules().getInt(player.getAbilities().invulnerable ? GameRules.RULE_PLAYERS_NETHER_PORTAL_CREATIVE_DELAY : GameRules.RULE_PLAYERS_NETHER_PORTAL_DEFAULT_DELAY)) : 0; } @Nullable @Override public TeleportTransition getPortalDestination(ServerLevel p_342106_, Entity p_343065_, BlockPos p_344977_) { ResourceKey resourcekey = p_342106_.dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER; ServerLevel serverlevel = p_342106_.getServer().getLevel(resourcekey); if (serverlevel == null) { return null; } else { boolean flag = serverlevel.dimension() == Level.NETHER; WorldBorder worldborder = serverlevel.getWorldBorder(); double d0 = DimensionType.getTeleportationScale(p_342106_.dimensionType(), serverlevel.dimensionType()); BlockPos blockpos = worldborder.clampToBounds(p_343065_.getX() * d0, p_343065_.getY(), p_343065_.getZ() * d0); return this.getExitPortal(serverlevel, p_343065_, p_344977_, blockpos, flag, worldborder); } } @Nullable private TeleportTransition getExitPortal( ServerLevel p_343269_, Entity p_343673_, BlockPos p_343381_, BlockPos p_343194_, boolean p_343644_, WorldBorder p_343185_ ) { Optional optional = p_343269_.getPortalForcer().findClosestPortalPosition(p_343194_, p_343644_, p_343185_); BlockUtil.FoundRectangle blockutil$foundrectangle; TeleportTransition.PostTeleportTransition teleporttransition$postteleporttransition; if (optional.isPresent()) { BlockPos blockpos = optional.get(); BlockState blockstate = p_343269_.getBlockState(blockpos); blockutil$foundrectangle = BlockUtil.getLargestRectangleAround( blockpos, blockstate.getValue(BlockStateProperties.HORIZONTAL_AXIS), 21, Direction.Axis.Y, 21, p_343533_ -> p_343269_.getBlockState(p_343533_) == blockstate ); teleporttransition$postteleporttransition = TeleportTransition.PLAY_PORTAL_SOUND.then(p_343530_ -> p_343530_.placePortalTicket(blockpos)); } else { Direction.Axis direction$axis = p_343673_.level().getBlockState(p_343381_).getOptionalValue(AXIS).orElse(Direction.Axis.X); Optional optional1 = p_343269_.getPortalForcer().createPortal(p_343194_, direction$axis); if (optional1.isEmpty()) { LOGGER.error("Unable to create a portal, likely target out of worldborder"); return null; } blockutil$foundrectangle = optional1.get(); teleporttransition$postteleporttransition = TeleportTransition.PLAY_PORTAL_SOUND.then(TeleportTransition.PLACE_PORTAL_TICKET); } return getDimensionTransitionFromExit(p_343673_, p_343381_, blockutil$foundrectangle, p_343269_, teleporttransition$postteleporttransition); } private static TeleportTransition getDimensionTransitionFromExit( Entity p_344252_, BlockPos p_343376_, BlockUtil.FoundRectangle p_343595_, ServerLevel p_343963_, TeleportTransition.PostTeleportTransition p_368919_ ) { BlockState blockstate = p_344252_.level().getBlockState(p_343376_); Direction.Axis direction$axis; Vec3 vec3; if (blockstate.hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) { direction$axis = blockstate.getValue(BlockStateProperties.HORIZONTAL_AXIS); BlockUtil.FoundRectangle blockutil$foundrectangle = BlockUtil.getLargestRectangleAround( p_343376_, direction$axis, 21, Direction.Axis.Y, 21, p_342174_ -> p_344252_.level().getBlockState(p_342174_) == blockstate ); vec3 = p_344252_.getRelativePortalPosition(direction$axis, blockutil$foundrectangle); } else { direction$axis = Direction.Axis.X; vec3 = new Vec3(0.5, 0.0, 0.0); } return createDimensionTransition(p_343963_, p_343595_, direction$axis, vec3, p_344252_, p_368919_); } private static TeleportTransition createDimensionTransition( ServerLevel p_344368_, BlockUtil.FoundRectangle p_345089_, Direction.Axis p_345454_, Vec3 p_344397_, Entity p_344167_, TeleportTransition.PostTeleportTransition p_361307_ ) { BlockPos blockpos = p_345089_.minCorner; BlockState blockstate = p_344368_.getBlockState(blockpos); Direction.Axis direction$axis = blockstate.getOptionalValue(BlockStateProperties.HORIZONTAL_AXIS).orElse(Direction.Axis.X); double d0 = p_345089_.axis1Size; double d1 = p_345089_.axis2Size; EntityDimensions entitydimensions = p_344167_.getDimensions(p_344167_.getPose()); int i = p_345454_ == direction$axis ? 0 : 90; double d2 = entitydimensions.width() / 2.0 + (d0 - entitydimensions.width()) * p_344397_.x(); double d3 = (d1 - entitydimensions.height()) * p_344397_.y(); double d4 = 0.5 + p_344397_.z(); boolean flag = direction$axis == Direction.Axis.X; Vec3 vec3 = new Vec3(blockpos.getX() + (flag ? d2 : d4), blockpos.getY() + d3, blockpos.getZ() + (flag ? d4 : d2)); Vec3 vec31 = PortalShape.findCollisionFreePosition(vec3, p_344368_, p_344167_, entitydimensions); return new TeleportTransition(p_344368_, vec31, Vec3.ZERO, i, 0.0F, Relative.union(Relative.DELTA, Relative.ROTATION), p_361307_); } @Override public Portal.Transition getLocalTransition() { return Portal.Transition.CONFUSION; } @Override public void animateTick(BlockState p_221794_, Level p_221795_, BlockPos p_221796_, RandomSource p_221797_) { if (p_221797_.nextInt(100) == 0) { p_221795_.playLocalSound( p_221796_.getX() + 0.5, p_221796_.getY() + 0.5, p_221796_.getZ() + 0.5, SoundEvents.PORTAL_AMBIENT, SoundSource.BLOCKS, 0.5F, p_221797_.nextFloat() * 0.4F + 0.8F, false ); } for (int i = 0; i < 4; i++) { double d0 = p_221796_.getX() + p_221797_.nextDouble(); double d1 = p_221796_.getY() + p_221797_.nextDouble(); double d2 = p_221796_.getZ() + p_221797_.nextDouble(); double d3 = (p_221797_.nextFloat() - 0.5) * 0.5; double d4 = (p_221797_.nextFloat() - 0.5) * 0.5; double d5 = (p_221797_.nextFloat() - 0.5) * 0.5; int j = p_221797_.nextInt(2) * 2 - 1; if (!p_221795_.getBlockState(p_221796_.west()).is(this) && !p_221795_.getBlockState(p_221796_.east()).is(this)) { d0 = p_221796_.getX() + 0.5 + 0.25 * j; d3 = p_221797_.nextFloat() * 2.0F * j; } else { d2 = p_221796_.getZ() + 0.5 + 0.25 * j; d5 = p_221797_.nextFloat() * 2.0F * j; } p_221795_.addParticle(ParticleTypes.PORTAL, d0, d1, d2, d3, d4, d5); } } @Override protected ItemStack getCloneItemStack(LevelReader p_310044_, BlockPos p_54912_, BlockState p_54913_, boolean p_376456_) { return ItemStack.EMPTY; } @Override protected BlockState rotate(BlockState p_54925_, Rotation p_54926_) { switch (p_54926_) { case COUNTERCLOCKWISE_90: case CLOCKWISE_90: switch ((Direction.Axis)p_54925_.getValue(AXIS)) { case X: return p_54925_.setValue(AXIS, Direction.Axis.Z); case Z: return p_54925_.setValue(AXIS, Direction.Axis.X); default: return p_54925_; } default: return p_54925_; } } @Override protected void createBlockStateDefinition(StateDefinition.Builder p_54935_) { p_54935_.add(AXIS); } }