Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-128509

X-axis portals break when upgrading from 1.12.2 when certain blocks are above/near them


    • Type: Bug
    • Status: Resolved
    • Resolution: Fixed
    • Affects Version/s: Minecraft 18w15a, Minecraft 18w19b, Minecraft 18w20c
    • Fix Version/s: Minecraft 18w22a
    • Labels:
    • Confirmation Status:


      In certain cases, portals on the X axis that were fine in 1.12.2 and are actually still fine in 1.13 will break during the upgrade.

      To reproduce

      1. Download the attached world, which contains many different portal setups.
      2. Load it in Minecraft 1.12.2, and confirm that all of the portals are lit.
      3. Load it in Minecraft 18w15a (or another 1.13 snapshot), and confirm that portals are no longer lit.

      Affected conditions

      This only appears to affect portals on the X axis (ones with the axis: x value for the block state). It also only appears to affect portals on the chunk border. The problematic block may be at any y value, and any x value in the same chunk other than the very edge; the z value appears to need to match the portal (not all positions have been checked). The simplest case is to put 4 stairs on top of the portal.

      Affected blocks:

      • Stairs (all varieties)
      • Cobblestone walls
      • Fence gates
      • Glass panes
      • Vines
      • Redstone
      • Redstone Repeaters
      • Fire
      • Tripwire
      • Chests

      Non-affected blocks:

      • Stone
      • Anvils
      • Carpet
      • Ladders
      • Banners (only tested floor)
      • Signs (both wall and floor)
      • Doors
      • Leaves
      • End rods
      • Buttons
      • Observers
      • Daylight detector
      • Trapdoors
      • Droppers

      Special cases

      Some special things that can happen with this bug:

      • Portals over chunk borders can be partially deleted:
      • Portals that are partially valid in one direction but not in another can end up as a single block:
      • Portals that are valid in either direction are not affected at all (but of course you can't enter these):
      • If you stand in the portal's location in 1.12.2 (possible by entering the nether and then returning), and then update to 1.13, then you will be teleported to the nether on world load (due to creative instant teleportation). However, upon returning, the portal will be gone.

      (Partial) code analysis

      The portals appear to decay because they are updated with a facing of down, and the portal code isn't written to support that anymore. In 1.12.2 BlockPortal.neighborChanged looked like this:

          public void neighborChanged(IBlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos)
              EnumFacing.Axis enumfacing$axis = (EnumFacing.Axis)state.getValue(AXIS);
              if (enumfacing$axis == EnumFacing.Axis.X)
                  BlockPortal.Size blockportal$size = new BlockPortal.Size(worldIn, pos, EnumFacing.Axis.X);
                  if (!blockportal$size.isValid() || blockportal$size.portalBlockCount < blockportal$size.width * blockportal$size.height)
                      worldIn.setBlockState(pos, Blocks.AIR.getDefaultState());
              else if (enumfacing$axis == EnumFacing.Axis.Z)
                  BlockPortal.Size blockportal$size1 = new BlockPortal.Size(worldIn, pos, EnumFacing.Axis.Z);
                  if (!blockportal$size1.isValid() || blockportal$size1.portalBlockCount < blockportal$size1.width * blockportal$size1.height)
                      worldIn.setBlockState(pos, Blocks.AIR.getDefaultState());

      However, now, it is this (using forge's experimental MCPConfig mappings):

          public IBlockState func_196271_a(IBlockState p_196271_1_, EnumFacing p_196271_2_, IBlockState p_196271_3_, IWorld p_196271_4_, BlockPos p_196271_5_, BlockPos p_196271_6_)
              EnumFacing.Axis enumfacing$axis = p_196271_2_.getAxis();
              if (p_196271_3_.getBlock() != this && !enumfacing$axis.isHorizontal() || p_196271_1_.getValue(AXIS) == enumfacing$axis)
                  BlockPortal.Size blockportal$size = new BlockPortal.Size(p_196271_4_, p_196271_5_, enumfacing$axis);
                  if (!blockportal$size.isValid() || !blockportal$size.func_196899_f())
                      return Blocks.AIR.getDefaultState();
              return super.func_196271_a(p_196271_1_, p_196271_2_, p_196271_3_, p_196271_4_, p_196271_5_, p_196271_6_);

      If an UP or DOWN facing got passed into this, then the Y axis will be used to construct the BlockPortal$Size. And that contains this code:

                  public Size(World worldIn, BlockPos p_i45694_2_, EnumFacing.Axis p_i45694_3_)
                      this.world = worldIn;
                      this.axis = p_i45694_3_;
                      if (p_i45694_3_ == EnumFacing.Axis.X)
                          this.leftDir = EnumFacing.EAST;
                          this.rightDir = EnumFacing.WEST;
                          this.leftDir = EnumFacing.NORTH;
                          this.rightDir = EnumFacing.SOUTH;
                      // ... continues ...

      If a Y axis gets into here, the portal thinks it is a Z-axis portal, and that causes X-axis portals to break. In 1.12.2, this wouldn't have happened, as it neighborChanged only checks for X and Z, but in 1.13 it's been generalized to either use any axis and just check if it matches... or if it's vertical (written as not horizontal). I'm not entirely sure why the !enumfacing$axis.isHorizontal() is present, and if that were removed this issue probably wouldn't happen. Or, use the axis in the state instead of the calculated one... though updating the portal randomly still isn't good.

      But, normally, it shouldn't be possible to get a vertical update except for when the portal is already decaying (though in that case I'm not sure if this method is used), but somehow it happens here. The actual cause of this update is that of MC-128512, but it's not clear why that happens specifically. I have captured some stacktraces in a debugger, though.

      Call stacks

      Obtained via JDB.

      Obf, as found in 18w15a:

      > stop at bct:109
      Set breakpoint bct:109
      Breakpoint hit: "thread=Server thread", bct.a(), line=109 bci=0
      Server thread[1] where
        [1] bct.a (SourceFile:109)
        [2] bhj.a (SourceFile:244)
        [3] bjc$b$1.a (SourceFile:210)
        [4] bjc.a (SourceFile:155)
        [5] bjc.a (SourceFile:147)
        [6] bjc.a (SourceFile:58)
        [7] biv.B (SourceFile:1,043)
        [8] biv$$Lambda$905.848302274.run (null)
        [9] java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:511)
        [10] java.util.concurrent.FutureTask.run (FutureTask.java:266)
        [11] k.a (SourceFile:146)
        [12] net.minecraft.server.MinecraftServer.w (SourceFile:671)
        [13] net.minecraft.server.MinecraftServer.v (SourceFile:627)
        [14] dal.v (SourceFile:155)
        [15] net.minecraft.server.MinecraftServer.run (SourceFile:532)
        [16] java.lang.Thread.run (Thread.java:748)

      And as found in 18w14b:

      > stop at bcj:109
      Set breakpoint bcj:109
      Breakpoint hit: "thread=WorldGen-Worker-3", bcj.a(), line=109 bci=0
      WorldGen-Worker-3[1] where
        [1] bcj.a (SourceFile:109)
        [2] bgy.a (SourceFile:244)
        [3] bir$b$1.a (SourceFile:210)
        [4] bir.a (SourceFile:155)
        [5] bir.a (SourceFile:147)
        [6] bir.a (SourceFile:58)
        [7] bik.B (SourceFile:1,044)
        [8] bik.G (SourceFile:1,148)
        [9] se.a (SourceFile:33)
        [10] se.put (SourceFile:11)
        [11] it.unimi.dsi.fastutil.longs.Long2ObjectMaps$SynchronizedMap.put (Long2ObjectMaps.java:371)
        [12] sg.a (SourceFile:207)
        [13] sg$$Lambda$937.1969878285.apply (null)
        [14] java.util.concurrent.CompletableFuture.uniApply (CompletableFuture.java:602)
        [15] java.util.concurrent.CompletableFuture$UniApply.tryFire (CompletableFuture.java:577)
        [16] java.util.concurrent.CompletableFuture.postComplete (CompletableFuture.java:474)
        [17] java.util.concurrent.CompletableFuture.postFire (CompletableFuture.java:561)
        [18] java.util.concurrent.CompletableFuture$UniCompose.tryFire (CompletableFuture.java:929)
        [19] java.util.concurrent.CompletableFuture$Completion.exec (CompletableFuture.java:443)
        [20] java.util.concurrent.ForkJoinTask.doExec (ForkJoinTask.java:289)
        [21] java.util.concurrent.ForkJoinPool$WorkQueue.runTask (ForkJoinPool.java:1,056)
        [22] java.util.concurrent.ForkJoinPool.runWorker (ForkJoinPool.java:1,692)
        [23] java.util.concurrent.ForkJoinWorkerThread.run (ForkJoinWorkerThread.java:157)

      An example of the parameters in the portal update method: minecraft:portal[axis=x], down, minecraft:obsidian, net.minecraft.world.WorldServer@77c2b4b9, MutableBlockPos(x=-279, y=7, z=-352), MutableBlockPos(x=-279, y=6, z=-352).


          Issue Links



              • Assignee:
                fry [Mojang] Georgii Gavrichev
                pokechu22 [Mod] Pokechu22
              • Votes:
                3 Vote for this issue
                4 Start watching this issue


                • Created: