package net.minecraft.world.level.portal; import java.util.Optional; import java.util.function.Predicate; import javax.annotation.Nullable; import net.minecraft.BlockUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.NetherPortalBlock; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.apache.commons.lang3.mutable.MutableInt; public class PortalShape { private static final int MIN_WIDTH = 2; public static final int MAX_WIDTH = 21; private static final int MIN_HEIGHT = 3; public static final int MAX_HEIGHT = 21; private static final BlockBehaviour.StatePredicate FRAME = (p_77720_, p_77721_, p_77722_) -> p_77720_.is(Blocks.OBSIDIAN); private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F; private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0; private final Direction.Axis axis; private final Direction rightDir; private final int numPortalBlocks; private final BlockPos bottomLeft; private final int height; private final int width; private PortalShape(Direction.Axis p_77697_, int p_361774_, Direction p_367618_, BlockPos p_77696_, int p_370026_, int p_368760_) { this.axis = p_77697_; this.numPortalBlocks = p_361774_; this.rightDir = p_367618_; this.bottomLeft = p_77696_; this.width = p_370026_; this.height = p_368760_; } public static Optional findEmptyPortalShape(LevelAccessor p_77709_, BlockPos p_77710_, Direction.Axis p_77711_) { return findPortalShape(p_77709_, p_77710_, p_77727_ -> p_77727_.isValid() && p_77727_.numPortalBlocks == 0, p_77711_); } public static Optional findPortalShape(LevelAccessor p_77713_, BlockPos p_77714_, Predicate p_77715_, Direction.Axis p_77716_) { Optional optional = Optional.of(findAnyShape(p_77713_, p_77714_, p_77716_)).filter(p_77715_); if (optional.isPresent()) { return optional; } else { Direction.Axis direction$axis = p_77716_ == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X; return Optional.of(findAnyShape(p_77713_, p_77714_, direction$axis)).filter(p_77715_); } } public static PortalShape findAnyShape(BlockGetter p_362003_, BlockPos p_369293_, Direction.Axis p_363410_) { Direction direction = p_363410_ == Direction.Axis.X ? Direction.WEST : Direction.SOUTH; BlockPos blockpos = calculateBottomLeft(p_362003_, direction, p_369293_); if (blockpos == null) { return new PortalShape(p_363410_, 0, direction, p_369293_, 0, 0); } else { int i = calculateWidth(p_362003_, blockpos, direction); if (i == 0) { return new PortalShape(p_363410_, 0, direction, blockpos, 0, 0); } else { MutableInt mutableint = new MutableInt(); int j = calculateHeight(p_362003_, blockpos, direction, i, mutableint); return new PortalShape(p_363410_, mutableint.getValue(), direction, blockpos, i, j); } } } @Nullable private static BlockPos calculateBottomLeft(BlockGetter p_366894_, Direction p_361188_, BlockPos p_77734_) { int i = Math.max(p_366894_.getMinY(), p_77734_.getY() - 21); while (p_77734_.getY() > i && isEmpty(p_366894_.getBlockState(p_77734_.below()))) { p_77734_ = p_77734_.below(); } Direction direction = p_361188_.getOpposite(); int j = getDistanceUntilEdgeAboveFrame(p_366894_, p_77734_, direction) - 1; return j < 0 ? null : p_77734_.relative(direction, j); } private static int calculateWidth(BlockGetter p_362377_, BlockPos p_369982_, Direction p_367434_) { int i = getDistanceUntilEdgeAboveFrame(p_362377_, p_369982_, p_367434_); return i >= 2 && i <= 21 ? i : 0; } private static int getDistanceUntilEdgeAboveFrame(BlockGetter p_366562_, BlockPos p_77736_, Direction p_77737_) { BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); for (int i = 0; i <= 21; i++) { blockpos$mutableblockpos.set(p_77736_).move(p_77737_, i); BlockState blockstate = p_366562_.getBlockState(blockpos$mutableblockpos); if (!isEmpty(blockstate)) { if (FRAME.test(blockstate, p_366562_, blockpos$mutableblockpos)) { return i; } break; } BlockState blockstate1 = p_366562_.getBlockState(blockpos$mutableblockpos.move(Direction.DOWN)); if (!FRAME.test(blockstate1, p_366562_, blockpos$mutableblockpos)) { break; } } return 0; } private static int calculateHeight(BlockGetter p_366874_, BlockPos p_367382_, Direction p_369713_, int p_364755_, MutableInt p_366395_) { BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); int i = getDistanceUntilTop(p_366874_, p_367382_, p_369713_, blockpos$mutableblockpos, p_364755_, p_366395_); return i >= 3 && i <= 21 && hasTopFrame(p_366874_, p_367382_, p_369713_, blockpos$mutableblockpos, p_364755_, i) ? i : 0; } private static boolean hasTopFrame( BlockGetter p_360937_, BlockPos p_362624_, Direction p_365783_, BlockPos.MutableBlockPos p_77731_, int p_77732_, int p_369385_ ) { for (int i = 0; i < p_77732_; i++) { BlockPos.MutableBlockPos blockpos$mutableblockpos = p_77731_.set(p_362624_).move(Direction.UP, p_369385_).move(p_365783_, i); if (!FRAME.test(p_360937_.getBlockState(blockpos$mutableblockpos), p_360937_, blockpos$mutableblockpos)) { return false; } } return true; } private static int getDistanceUntilTop( BlockGetter p_366399_, BlockPos p_367032_, Direction p_362252_, BlockPos.MutableBlockPos p_77729_, int p_361664_, MutableInt p_363201_ ) { for (int i = 0; i < 21; i++) { p_77729_.set(p_367032_).move(Direction.UP, i).move(p_362252_, -1); if (!FRAME.test(p_366399_.getBlockState(p_77729_), p_366399_, p_77729_)) { return i; } p_77729_.set(p_367032_).move(Direction.UP, i).move(p_362252_, p_361664_); if (!FRAME.test(p_366399_.getBlockState(p_77729_), p_366399_, p_77729_)) { return i; } for (int j = 0; j < p_361664_; j++) { p_77729_.set(p_367032_).move(Direction.UP, i).move(p_362252_, j); BlockState blockstate = p_366399_.getBlockState(p_77729_); if (!isEmpty(blockstate)) { return i; } if (blockstate.is(Blocks.NETHER_PORTAL)) { p_363201_.increment(); } } } return 21; } private static boolean isEmpty(BlockState p_77718_) { return p_77718_.isAir() || p_77718_.is(BlockTags.FIRE) || p_77718_.is(Blocks.NETHER_PORTAL); } public boolean isValid() { return this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21; } public void createPortalBlocks(LevelAccessor p_366077_) { BlockState blockstate = Blocks.NETHER_PORTAL.defaultBlockState().setValue(NetherPortalBlock.AXIS, this.axis); BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1)) .forEach(p_360642_ -> p_366077_.setBlock(p_360642_, blockstate, 18)); } public boolean isComplete() { return this.isValid() && this.numPortalBlocks == this.width * this.height; } public static Vec3 getRelativePosition(BlockUtil.FoundRectangle p_77739_, Direction.Axis p_77740_, Vec3 p_77741_, EntityDimensions p_77742_) { double d0 = (double)p_77739_.axis1Size - p_77742_.width(); double d1 = (double)p_77739_.axis2Size - p_77742_.height(); BlockPos blockpos = p_77739_.minCorner; double d2; if (d0 > 0.0) { double d3 = blockpos.get(p_77740_) + p_77742_.width() / 2.0; d2 = Mth.clamp(Mth.inverseLerp(p_77741_.get(p_77740_) - d3, 0.0, d0), 0.0, 1.0); } else { d2 = 0.5; } double d5; if (d1 > 0.0) { Direction.Axis direction$axis = Direction.Axis.Y; d5 = Mth.clamp(Mth.inverseLerp(p_77741_.get(direction$axis) - blockpos.get(direction$axis), 0.0, d1), 0.0, 1.0); } else { d5 = 0.0; } Direction.Axis direction$axis1 = p_77740_ == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X; double d4 = p_77741_.get(direction$axis1) - (blockpos.get(direction$axis1) + 0.5); return new Vec3(d2, d5, d4); } public static Vec3 findCollisionFreePosition(Vec3 p_260315_, ServerLevel p_259704_, Entity p_259626_, EntityDimensions p_259816_) { if (!(p_259816_.width() > 4.0F) && !(p_259816_.height() > 4.0F)) { double d0 = p_259816_.height() / 2.0; Vec3 vec3 = p_260315_.add(0.0, d0, 0.0); VoxelShape voxelshape = Shapes.create( AABB.ofSize(vec3, p_259816_.width(), 0.0, p_259816_.width()).expandTowards(0.0, 1.0, 0.0).inflate(1.0E-6) ); Optional optional = p_259704_.findFreePosition(p_259626_, voxelshape, vec3, p_259816_.width(), p_259816_.height(), p_259816_.width()); Optional optional1 = optional.map(p_259019_ -> p_259019_.subtract(0.0, d0, 0.0)); return optional1.orElse(p_260315_); } else { return p_260315_; } } }