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

Entities teleport though portals at the start of a tick (before they move) instead of at the end of a tick (after they move)

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • None
    • 1.16.2 Pre-release 1, 1.16.2 Pre-release 2, 1.16.2 Pre-release 3, 1.16.3
    • None
    • Unconfirmed
    • Block states, Entities

      I was suggested to remake and reword a similar ticket I made, MC-196897, as the wording of the ticket was incorrect for what the actual bug was.

      The Bug:

      Currently there is an inconsistency with how mobs enter the nether portal versus the end portal. A mob can enter the end portal after its movement as the end portal block itself calls the change dimensions. However this does not match the nether portal which requires two gameticks to go through due to the move method collision not calling the change dimensions directly but rather changing a boolean that is checked before the move method is called. This leads to the inconsistency in how the game handles the changing of dimensions and thus a bug. 

      How to reproduce:

      1. Create a flat world and teleport to 1000, 10 1000 via /tp 1000 10 1000
      2. Build a nether portal @ the cords 1009 4 1000 to 1009 4 997
      3. Light the portal and go thru to load and link the other side.
      4. Build a wall behind the portal on the east side.
      5. Wait 15 seconds so that the portal no longer loads the chunk as you came through it.
      6. Turn your render distance down to 2 and your entity distance up to max
      7. Go to the coords 989 5 1000 to make the portal chunk non-entity processing
      8. /summon pig 1007.00 5.67 999.45 {Motion:[4.0,0.0,0.0]}
      9. Observe that the pig is still there.
      10. Change the nether portal to end portals with a fill command.
      11. Re-summon the pig and watch it go through
      12. Video for the steps: https://youtu.be/2dg7jUIGeho
      13. Video excludes the end portal steps

      Solution:

      To fix the inconsistency as well as the bug, entities should change dimensions at the end of the tick by repositioning the tickNetherPortal() after the methods that call the move method (self movement/velocity) for most mobs.

      Code analysis:

      This code analysis is using Yarn mappings for 20w30a.

      For this example, we will be using a pig and skip  the beginning of the calling chain the starting the tick methods and go directly to the living entity part.

      Here the Living entity calls the super first which will lead it down the nether portal check and changing of the dimensions. Later on, it calls the tickMovement(), which will call the move method and the impact check to say it hit a portal. Let's explore the super first

       

      public abstract class LivingEntity extends Entity {
       //removed between
       public void tick() {
       super.tick();
       //removed some lines between
       this.tickMovement();
      
      

      From the super it goes to the entity class and calls the basetick almost right away to lets go down another level.

      public abstract class Entity /
       /removed between 
       public void tick() { 
       //removed some lines between 
       this.baseTick(); 
       }

      Here at the base tick it does a lot of stuff, but we are only interested in the ticknetherportal(), so let's look at that.

      public void baseTick(){ 
      //removed some lines between 
      this.tickNetherPortal();
      //removed some lines between
      }
      

      In the tick nether portal, it gets the this.getMaxNetherPortalTime() which is used in a if check, but we can ignore it as it is zero for almost everything but players. The main thing we need to look at is that a if check looks at this.inNetherPortal, which can only be successful if the pig hits a portal in the previous tick, so as of now, this checks stops as the pig has not touched a portal yet.

      protected void tickNetherPortal() {
       //removed some lines between
      
       int i = this.getMaxNetherPortalTime(); // =0 for non-players
      
       //removed some lines between
      
       if (this.inNetherPortal) {
          //removed some lines between
      
          if (lv3 != null && minecraftServer.isNetherAllowed() && !this.hasVehicle() &&   this.netherPortalTime++ >= i) {
      
          //removed some lines between
          this.changeDimension(lv3);
      
          }
       }

      Now lets look around at the move method for the pig. It was called back from a chain of calls steaming from tickmovement(). Here in the move method, it will call this.checkBlockCollision(), which handles the effect of colliding with certain blocks.

      public void move(MovementType arg2, Vec3d arg22) {
       //removed some lines between
       try{ 
          this.checkBlockCollision();
       }
      
       //removed some lines between
      }
      

      Here it will call the the entity hitting the block. Let's first explore it hitting an end portal.

      protected void checkBlockCollision() {
       //removed some lines between
        lv5.onEntityCollision(this.world, lv4, this);
      //removed some lines between
      }

      If it hits an endportal, it will do some checks then make it change dimensions, thus allowing it to go the end after it's move method unlike the nether portal. Now let's look at the nether portal check.

      public class EndPortalBlock
       @Override
       public void onEntityCollision(BlockState arg, World arg2, BlockPos arg3, Entity arg4) {
       //removed some lines between
       arg4.changeDimension(lv2);
       }
      

      The nether portal check does a few checks then call the entity's setInNetherPortal method. So lets look at that for the final thing.

      public class NetherPortalBlock
       public void onEntityCollision(BlockState arg, World arg2, BlockPos arg3, Entity arg4) {
       if (!arg4.hasVehicle() && !arg4.hasPassengers() && arg4.canUsePortals())
      { arg4.setInNetherPortal(arg3); }
      }
      

      Here is basically says the pig is in a nether portal so now when the portal tick method of the entity is called the pig can go in. This makes it unlike the end portal in which it will happen on the second tick instead of the first/same tick. 

      public abstract class Entity
       public void setInNetherPortal(BlockPos arg) {
       //removed some lines between
       this.inNetherPortal = true;
       }
      

       

       

            Unassigned Unassigned
            Kman032317 Kyle Weber
            Votes:
            12 Vote for this issue
            Watchers:
            11 Start watching this issue

              Created:
              Updated:
              Resolved: