--- a/net/minecraft/world/level/portal/PortalShape.java
+++ b/net/minecraft/world/level/portal/PortalShape.java
@@ -21,15 +21,22 @@
 import net.minecraft.world.phys.Vec3;
 import net.minecraft.world.phys.shapes.Shapes;
 import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import org.bukkit.craftbukkit.event.CraftPortalEvent;
+import org.bukkit.event.world.PortalCreateEvent;
+// CraftBukkit end
 
 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 = (state, world, pos) -> state.is(Blocks.OBSIDIAN);
+    private static final BlockBehaviour.StatePredicate FRAME = (iblockdata, iblockaccess, blockposition) -> {
+        return iblockdata.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 static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0D;
     private final LevelAccessor level;
     private final Direction.Axis axis;
     private final Direction rightDir;
@@ -38,22 +45,28 @@
     private BlockPos bottomLeft;
     private int height;
     private final int width;
+    org.bukkit.craftbukkit.util.BlockStateListPopulator blocks; // CraftBukkit - add field
 
     public static Optional<PortalShape> findEmptyPortalShape(LevelAccessor world, BlockPos pos, Direction.Axis axis) {
-        return findPortalShape(world, pos, areaHelper -> areaHelper.isValid() && areaHelper.numPortalBlocks == 0, axis);
+        return PortalShape.findPortalShape(world, pos, (blockportalshape) -> {
+            return blockportalshape.isValid() && blockportalshape.numPortalBlocks == 0;
+        }, axis);
     }
 
     public static Optional<PortalShape> findPortalShape(LevelAccessor world, BlockPos pos, Predicate<PortalShape> validator, Direction.Axis axis) {
         Optional<PortalShape> optional = Optional.of(new PortalShape(world, pos, axis)).filter(validator);
+
         if (optional.isPresent()) {
             return optional;
         } else {
-            Direction.Axis axis2 = axis == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
-            return Optional.of(new PortalShape(world, pos, axis2)).filter(validator);
+            Direction.Axis enumdirection_enumaxis1 = axis == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
+
+            return Optional.of(new PortalShape(world, pos, enumdirection_enumaxis1)).filter(validator);
         }
     }
 
     public PortalShape(LevelAccessor world, BlockPos pos, Direction.Axis axis) {
+        this.blocks = new org.bukkit.craftbukkit.util.BlockStateListPopulator(world.getMinecraftWorld()); // CraftBukkit
         this.level = world;
         this.axis = axis;
         this.rightDir = axis == Direction.Axis.X ? Direction.WEST : Direction.SOUTH;
@@ -68,88 +81,101 @@
                 this.height = this.calculateHeight();
             }
         }
+
     }
 
     @Nullable
     private BlockPos calculateBottomLeft(BlockPos pos) {
-        int i = Math.max(this.level.getMinBuildHeight(), pos.getY() - 21);
-
-        while (pos.getY() > i && isEmpty(this.level.getBlockState(pos.below()))) {
-            pos = pos.below();
+        for (int i = Math.max(this.level.getMinBuildHeight(), pos.getY() - 21); pos.getY() > i && PortalShape.isEmpty(this.level.getBlockState(pos.below())); pos = pos.below()) {
+            ;
         }
 
-        Direction direction = this.rightDir.getOpposite();
-        int j = this.getDistanceUntilEdgeAboveFrame(pos, direction) - 1;
-        return j < 0 ? null : pos.relative(direction, j);
+        Direction enumdirection = this.rightDir.getOpposite();
+        int j = this.getDistanceUntilEdgeAboveFrame(pos, enumdirection) - 1;
+
+        return j < 0 ? null : pos.relative(enumdirection, j);
     }
 
     private int calculateWidth() {
         int i = this.getDistanceUntilEdgeAboveFrame(this.bottomLeft, this.rightDir);
+
         return i >= 2 && i <= 21 ? i : 0;
     }
 
     private int getDistanceUntilEdgeAboveFrame(BlockPos pos, Direction direction) {
-        BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
+        BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
 
-        for (int i = 0; i <= 21; i++) {
-            mutableBlockPos.set(pos).move(direction, i);
-            BlockState blockState = this.level.getBlockState(mutableBlockPos);
-            if (!isEmpty(blockState)) {
-                if (FRAME.test(blockState, this.level, mutableBlockPos)) {
+        for (int i = 0; i <= 21; ++i) {
+            blockposition_mutableblockposition.set(pos).move(direction, i);
+            BlockState iblockdata = this.level.getBlockState(blockposition_mutableblockposition);
+
+            if (!PortalShape.isEmpty(iblockdata)) {
+                if (PortalShape.FRAME.test(iblockdata, this.level, blockposition_mutableblockposition)) {
+                    this.blocks.setBlock(blockposition_mutableblockposition, iblockdata, 18); // CraftBukkit - lower left / right
                     return i;
                 }
                 break;
             }
 
-            BlockState blockState2 = this.level.getBlockState(mutableBlockPos.move(Direction.DOWN));
-            if (!FRAME.test(blockState2, this.level, mutableBlockPos)) {
+            BlockState iblockdata1 = this.level.getBlockState(blockposition_mutableblockposition.move(Direction.DOWN));
+
+            if (!PortalShape.FRAME.test(iblockdata1, this.level, blockposition_mutableblockposition)) {
                 break;
             }
+            this.blocks.setBlock(blockposition_mutableblockposition, iblockdata1, 18); // CraftBukkit - bottom row
         }
 
         return 0;
     }
 
     private int calculateHeight() {
-        BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
-        int i = this.getDistanceUntilTop(mutableBlockPos);
-        return i >= 3 && i <= 21 && this.hasTopFrame(mutableBlockPos, i) ? i : 0;
+        BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+        int i = this.getDistanceUntilTop(blockposition_mutableblockposition);
+
+        return i >= 3 && i <= 21 && this.hasTopFrame(blockposition_mutableblockposition, i) ? i : 0;
     }
 
     private boolean hasTopFrame(BlockPos.MutableBlockPos pos, int height) {
-        for (int i = 0; i < this.width; i++) {
-            BlockPos.MutableBlockPos mutableBlockPos = pos.set(this.bottomLeft).move(Direction.UP, height).move(this.rightDir, i);
-            if (!FRAME.test(this.level.getBlockState(mutableBlockPos), this.level, mutableBlockPos)) {
+        for (int j = 0; j < this.width; ++j) {
+            BlockPos.MutableBlockPos blockposition_mutableblockposition1 = pos.set(this.bottomLeft).move(Direction.UP, height).move(this.rightDir, j);
+
+            if (!PortalShape.FRAME.test(this.level.getBlockState(blockposition_mutableblockposition1), this.level, blockposition_mutableblockposition1)) {
                 return false;
             }
+            this.blocks.setBlock(blockposition_mutableblockposition1, this.level.getBlockState(blockposition_mutableblockposition1), 18); // CraftBukkit - upper row
         }
 
         return true;
     }
 
     private int getDistanceUntilTop(BlockPos.MutableBlockPos pos) {
-        for (int i = 0; i < 21; i++) {
+        for (int i = 0; i < 21; ++i) {
             pos.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, -1);
-            if (!FRAME.test(this.level.getBlockState(pos), this.level, pos)) {
+            if (!PortalShape.FRAME.test(this.level.getBlockState(pos), this.level, pos)) {
                 return i;
             }
 
             pos.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, this.width);
-            if (!FRAME.test(this.level.getBlockState(pos), this.level, pos)) {
+            if (!PortalShape.FRAME.test(this.level.getBlockState(pos), this.level, pos)) {
                 return i;
             }
 
-            for (int j = 0; j < this.width; j++) {
+            for (int j = 0; j < this.width; ++j) {
                 pos.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, j);
-                BlockState blockState = this.level.getBlockState(pos);
-                if (!isEmpty(blockState)) {
+                BlockState iblockdata = this.level.getBlockState(pos);
+
+                if (!PortalShape.isEmpty(iblockdata)) {
                     return i;
                 }
 
-                if (blockState.is(Blocks.NETHER_PORTAL)) {
-                    this.numPortalBlocks++;
+                if (iblockdata.is(Blocks.NETHER_PORTAL)) {
+                    ++this.numPortalBlocks;
                 }
             }
+            // CraftBukkit start - left and right
+            this.blocks.setBlock(pos.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, -1), this.level.getBlockState(pos), 18);
+            this.blocks.setBlock(pos.set(this.bottomLeft).move(Direction.UP, i).move(this.rightDir, this.width), this.level.getBlockState(pos), 18);
+            // CraftBukkit end
         }
 
         return 21;
@@ -163,10 +189,35 @@
         return this.bottomLeft != null && this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21;
     }
 
-    public void createPortalBlocks() {
-        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(pos -> this.level.setBlock(pos, blockState, 18));
+    // CraftBukkit start - return boolean
+    @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper
+    public boolean createPortalBlocks() {
+        // Paper start - UseOnContext param
+        return this.createPortalBlocks(null);
+    }
+
+    public boolean createPortalBlocks(@Nullable net.minecraft.world.item.context.UseOnContext useOnContext) {
+        // Paper end - UseOnContext param
+        org.bukkit.World bworld = this.level.getMinecraftWorld().getWorld();
+
+        // Copy below for loop
+        BlockState iblockdata = (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((blockposition) -> {
+            this.blocks.setBlock(blockposition, iblockdata, 18);
+        });
+
+        PortalCreateEvent event = new PortalCreateEvent((java.util.List<org.bukkit.block.BlockState>) (java.util.List) blocks.getList(), bworld, useOnContext == null || useOnContext.getPlayer() == null ? null : useOnContext.getPlayer().getBukkitEntity(), PortalCreateEvent.CreateReason.FIRE); // Paper - pass entity param
+        this.level.getMinecraftWorld().getServer().server.getPluginManager().callEvent(event);
+
+        if (event.isCancelled()) {
+            return false;
+        }
+        // CraftBukkit end
+        BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1)).forEach((blockposition) -> {
+            this.level.setBlock(blockposition, iblockdata, 18);
+        });
+        return true; // CraftBukkit
     }
 
     public boolean isComplete() {
@@ -174,69 +225,64 @@
     }
 
     public static Vec3 getRelativePosition(BlockUtil.FoundRectangle portalRect, Direction.Axis portalAxis, Vec3 entityPos, EntityDimensions entityDimensions) {
-        double d = (double)portalRect.axis1Size - (double)entityDimensions.width();
-        double e = (double)portalRect.axis2Size - (double)entityDimensions.height();
-        BlockPos blockPos = portalRect.minCorner;
-        double g;
-        if (d > 0.0) {
-            double f = (double)blockPos.get(portalAxis) + (double)entityDimensions.width() / 2.0;
-            g = Mth.clamp(Mth.inverseLerp(entityPos.get(portalAxis) - f, 0.0, d), 0.0, 1.0);
+        double d0 = (double) portalRect.axis1Size - (double) entityDimensions.width();
+        double d1 = (double) portalRect.axis2Size - (double) entityDimensions.height();
+        BlockPos blockposition = portalRect.minCorner;
+        double d2;
+        double d3;
+
+        if (d0 > 0.0D) {
+            d2 = (double) blockposition.get(portalAxis) + (double) entityDimensions.width() / 2.0D;
+            d3 = Mth.clamp(Mth.inverseLerp(entityPos.get(portalAxis) - d2, 0.0D, d0), 0.0D, 1.0D);
         } else {
-            g = 0.5;
+            d3 = 0.5D;
         }
 
-        double i;
-        if (e > 0.0) {
-            Direction.Axis axis = Direction.Axis.Y;
-            i = Mth.clamp(Mth.inverseLerp(entityPos.get(axis) - (double)blockPos.get(axis), 0.0, e), 0.0, 1.0);
+        Direction.Axis enumdirection_enumaxis1;
+
+        if (d1 > 0.0D) {
+            enumdirection_enumaxis1 = Direction.Axis.Y;
+            d2 = Mth.clamp(Mth.inverseLerp(entityPos.get(enumdirection_enumaxis1) - (double) blockposition.get(enumdirection_enumaxis1), 0.0D, d1), 0.0D, 1.0D);
         } else {
-            i = 0.0;
+            d2 = 0.0D;
         }
 
-        Direction.Axis axis2 = portalAxis == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
-        double k = entityPos.get(axis2) - ((double)blockPos.get(axis2) + 0.5);
-        return new Vec3(g, i, k);
+        enumdirection_enumaxis1 = portalAxis == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
+        double d4 = entityPos.get(enumdirection_enumaxis1) - ((double) blockposition.get(enumdirection_enumaxis1) + 0.5D);
+
+        return new Vec3(d3, d2, d4);
     }
 
-    public static PortalInfo createPortalInfo(
-        ServerLevel destination,
-        BlockUtil.FoundRectangle portalRect,
-        Direction.Axis portalAxis,
-        Vec3 offset,
-        Entity entity,
-        Vec3 velocity,
-        float yaw,
-        float pitch
-    ) {
-        BlockPos blockPos = portalRect.minCorner;
-        BlockState blockState = destination.getBlockState(blockPos);
-        Direction.Axis axis = blockState.getOptionalValue(BlockStateProperties.HORIZONTAL_AXIS).orElse(Direction.Axis.X);
-        double d = (double)portalRect.axis1Size;
-        double e = (double)portalRect.axis2Size;
-        EntityDimensions entityDimensions = entity.getDimensions(entity.getPose());
-        int i = portalAxis == axis ? 0 : 90;
-        Vec3 vec3 = portalAxis == axis ? velocity : new Vec3(velocity.z, velocity.y, -velocity.x);
-        double f = (double)entityDimensions.width() / 2.0 + (d - (double)entityDimensions.width()) * offset.x();
-        double g = (e - (double)entityDimensions.height()) * offset.y();
-        double h = 0.5 + offset.z();
-        boolean bl = axis == Direction.Axis.X;
-        Vec3 vec32 = new Vec3((double)blockPos.getX() + (bl ? f : h), (double)blockPos.getY() + g, (double)blockPos.getZ() + (bl ? h : f));
-        Vec3 vec33 = findCollisionFreePosition(vec32, destination, entity, entityDimensions);
-        return new PortalInfo(vec33, vec3, yaw + (float)i, pitch);
+    public static PortalInfo createPortalInfo(ServerLevel worldserver, BlockUtil.FoundRectangle blockutil_rectangle, Direction.Axis enumdirection_enumaxis, Vec3 vec3d, Entity entity, Vec3 vec3d1, float f, float f1, CraftPortalEvent portalEventInfo) { // CraftBukkit
+        BlockPos blockposition = blockutil_rectangle.minCorner;
+        BlockState iblockdata = worldserver.getBlockState(blockposition);
+        Direction.Axis enumdirection_enumaxis1 = (Direction.Axis) iblockdata.getOptionalValue(BlockStateProperties.HORIZONTAL_AXIS).orElse(Direction.Axis.X);
+        double d0 = (double) blockutil_rectangle.axis1Size;
+        double d1 = (double) blockutil_rectangle.axis2Size;
+        EntityDimensions entitysize = entity.getDimensions(entity.getPose());
+        int i = enumdirection_enumaxis == enumdirection_enumaxis1 ? 0 : 90;
+        Vec3 vec3d2 = enumdirection_enumaxis == enumdirection_enumaxis1 ? vec3d1 : new Vec3(vec3d1.z, vec3d1.y, -vec3d1.x);
+        double d2 = (double) entitysize.width() / 2.0D + (d0 - (double) entitysize.width()) * vec3d.x();
+        double d3 = (d1 - (double) entitysize.height()) * vec3d.y();
+        double d4 = 0.5D + vec3d.z();
+        boolean flag = enumdirection_enumaxis1 == Direction.Axis.X;
+        Vec3 vec3d3 = new Vec3((double) blockposition.getX() + (flag ? d2 : d4), (double) blockposition.getY() + d3, (double) blockposition.getZ() + (flag ? d4 : d2));
+        Vec3 vec3d4 = PortalShape.findCollisionFreePosition(vec3d3, worldserver, entity, entitysize);
+
+        return new PortalInfo(vec3d4, vec3d2, f + (float) i, f1, worldserver, portalEventInfo); // CraftBukkit
     }
 
     private static Vec3 findCollisionFreePosition(Vec3 fallback, ServerLevel world, Entity entity, EntityDimensions dimensions) {
-        if (!(dimensions.width() > 4.0F) && !(dimensions.height() > 4.0F)) {
-            double d = (double)dimensions.height() / 2.0;
-            Vec3 vec3 = fallback.add(0.0, d, 0.0);
-            VoxelShape voxelShape = Shapes.create(
-                AABB.ofSize(vec3, (double)dimensions.width(), 0.0, (double)dimensions.width()).expandTowards(0.0, 1.0, 0.0).inflate(1.0E-6)
-            );
-            Optional<Vec3> optional = world.findFreePosition(
-                entity, voxelShape, vec3, (double)dimensions.width(), (double)dimensions.height(), (double)dimensions.width()
-            );
-            Optional<Vec3> optional2 = optional.map(pos -> pos.subtract(0.0, d, 0.0));
-            return optional2.orElse(fallback);
+        if (dimensions.width() <= 4.0F && dimensions.height() <= 4.0F) {
+            double d0 = (double) dimensions.height() / 2.0D;
+            Vec3 vec3d1 = fallback.add(0.0D, d0, 0.0D);
+            VoxelShape voxelshape = Shapes.create(AABB.ofSize(vec3d1, (double) dimensions.width(), 0.0D, (double) dimensions.width()).expandTowards(0.0D, 1.0D, 0.0D).inflate(1.0E-6D));
+            Optional<Vec3> optional = world.findFreePosition(entity, voxelshape, vec3d1, (double) dimensions.width(), (double) dimensions.height(), (double) dimensions.width());
+            Optional<Vec3> optional1 = optional.map((vec3d2) -> {
+                return vec3d2.subtract(0.0D, d0, 0.0D);
+            });
+
+            return (Vec3) optional1.orElse(fallback);
         } else {
             return fallback;
         }
