Lagback

From Minecraft Parkour Wiki
Revision as of 10:06, 6 September 2022 by MCPK (talk | contribs) (complete explanation)

A lagback is an occurrence of the player getting teleported back to a previous position. It can be abused to clip inside walls, or jump higher in a similar fashion as Blips, though this practice may be a bannable offense on multiplayer servers. In this article, we focus on singleplayer 1.8.


Here are the known triggers for lagbacks:

  • Being stuck in a block.
  • Colliding with a boat.
  • Falling and sneaking at the last moment.
  • Moving inside a cobweb too quickly.
  • Moving through a steppable block with an obscene amount of speed (Speed 100, for example).
  • Moving quickly against a wall.

Explanation

There are two main ways to trigger a lagback:

  • Intersecting with a collision box (stuck in a block, or colliding with a boat).
  • Moving "wrongly".

The first method is pretty self-explanatory. As for the second method, one must first understand how player movement is processed server-side (even in singleplayer, the game runs on an integrated server). Each tick, the client calculates their next position, and sends a packet to the server. The server attempts to validate the player's movement by recalculating it from their last position, which should theoretically result in the same position. However, the server forgets to undo the side effects of the player's original movement, which may cause a discrepancy between the player's expected and actual positions; the discrepancy is considered significant if the horizontal distance between the two is strictly more than 0.25m, in which case the player is lagged back to their previous position.

Side effects that aren't undone prior to the validation process include:

  • The onGround flag being set to true when landing.
  • The inWeb flag being set to false when colliding with a cobweb.
  • The various collided flags being set to true when colliding with a wall or ceiling.

In short, the player's movement is essentially calculated twice each tick, but the side effects of the first calculation may influence the result of the second. If the player moves fast enough, the server thinks the player moves wrongly, and triggers a lagback.

Code

In singleplayer, lagbacks are caused by this section of code (heavily simplified, we ignore exceptional cases such as flying, sleeping, teleporting, respawning, mounting an entity...):

//in NetHandlerPlayServer.java
public void processPlayer(PacketPlayer packetIn)
{
    double posY_original = this.playerEntity.posY;
    this.lastPosX = this.playerEntity.posX;
    this.lastPosY = this.playerEntity.posY;
    this.lastPosZ = this.playerEntity.posZ;
    double next_posX = packetIn.getPositionX();
    double next_posY = packetIn.getPositionY();
    double next_posZ = packetIn.getPositionZ();
    float yaw = this.playerEntity.rotationYaw;
    float pitch = this.playerEntity.rotationPitch;
    if (packetIn.getRotating()) {yaw = packetIn.getYaw(); pitch = packetIn.getPitch();}

    //checks whether the player is starting from inside a collision box: in this case, there is no lagback
    boolean noCollisionInside = worldserver.getCollidingBoundingBoxes(this.playerEntity, this.playerEntity.getEntityBoundingBox().contract(0.0625, 0.0625, 0.0625).isEmpty();

    //begin movement verification
    double deltaX = next_posX - this.playerEntity.posX;
    double deltaY = next_posY - this.playerEntity.posY;
    double deltaZ = next_posZ - this.playerEntity.posZ;

    if (this.playerEntity.onGround && !packetIn.isOnGround() && deltaY > 0.0)
        this.playerEntity.jump();
    this.playerEntity.moveEntity(deltaX, deltaY, deltaZ);
    this.playerEntity.onGround = packetIn.isOnGround();
    
    //calculates error between the expected and actual positions
    double errorX = next_posX - this.playerEntity.posX;
    double errorZ = next_posZ - this.playerEntity.posZ;
    double error_squared = errorX * errorX + errorZ * errorZ;
    boolean movedWrongly = (error_squared > 0.0625 && !this.playerEntity.theItemInWorldManager.isCreative());

    //sets the player to their actual position, and checks for collisions
    this.playerEntity.setPositionAndRotation(next_posX, next_posY, next_posZ, yaw, pitch);
    boolean next_noCollisionInside = worldserver.getCollidingBoundingBoxes(this.playerEntity, this.playerEntity.getEntityBoundingBox().contract(0.0625, 0.0625, 0.0625).isEmpty();

    //detects an error: player moved wrongly, or moved inside a collision box
    if (noCollisionInside && (movedWrongly || !next_noCollisionInside))
    {
        //lags the player back to their previous position
        this.setPlayerLocation(this.lastPosX, this.lastPosY, this.lastPosZ, yaw, pitch);
        return;
    }

    this.playerEntity.handleFalling(this.playerEntity.posY - posY_original, packetIn.isOnGround());
}

Note that the handling of fall damage is not processed if the player gets lagbacked. This may cause the player to suffer unexpectedly high fall damage when they eventually land.