Lagback

From Minecraft Parkour Wiki
Other languages:

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 these practices may be bannable offenses on multiplayer servers. In this article, we focus on singleplayer 1.8, but most of the information is relevant for multiplayer and 1.9+.

Here are the known triggers for lagbacks:

  • Being stuck in a block.
  • Colliding with a boat.
  • Moving inside a cobweb too quickly.
  • Landing while sneaking with an adequate velocity.
  • Moving through a steppable block with an obscene amount of speed (Speed 100, for example).
  • Moving quickly against a wall.


General 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 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 the player's next position, and sends a packet to the server. The server validates the player's movement by recalculating it from their last position, which should theoretically result in the same position. However, the server does not undo the side effects of the player's 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 (which changes the behaviour of sneaking).
  • 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 calculated twice each tick, but the side effects of the first calculation may influence the result of the second one. If the player moves fast enough, the server thinks the player moves wrongly, and triggers a lagback.


Explanations for specific triggers

Intersecting with a collision box

The player is not supposed to move into a collision box. However, the server has a leniency of 0.0625b in each direction, meaning the player's bounding box can intersect with the "exterior shell" of a block and move normally. If the player move further inward, it triggers a lagback.

If the player is already fully inside a collision box, they do not get lagged back when moving.


Colliding with a cobweb (WIP)

When colliding with a cobweb (detected at the end of the tick), the player's inWeb flag is set to true. When the player is moved and and inWeb is true, it is immediately set to false, and the cobweb's effect is applied on the player's velocity. This causes a discrepancy between the original calculation and the recalculation, which triggers a lagback if sufficient speed is met.


Landing from a high fall while sneaking

This variant is achieved by landing and sneaking with the following requirements:

  • Moving horizontally at strictly more than 0.25m/t.
  • Being at least one block above ground on the tick before landing (or 0.6b in 1.11+).

You are not required to time the sneak exactly, but sneaking too early will significantly reduce the player's horizontal speed.

The minimum jump height required to achieve at least -1m/t of vertical speed is -7.3125b. An easy setup to try this method is -9.5b. Here is an example:


Sneak Glitch

A sneak glitch (or shift glitch) happens when the player sneaks while landing on an edge, which causes them to fall anyway (this was fixed in 1.16.2).

When performing a sneak glitch, the server's expected movement for the player is different from their actual movement. Here's why:

  • During the original calculation, the player is sneaking but not "on ground", hence they move past the edge. However, they still land, which sets onGround = true.
  • During the recalculation, the player is now considered "on ground" before actually landing, which changes the behaviour of sneaking: they can not "fall off the edge", despite not being at ground level.

With at least 0.25m/t of horizontal speed and appropriate placement, performing a sneak glitch can therefore trigger a lagback. After being teleported back to their previous position, the player is now able to jump. The resulting jump looks similar to a blip-up, in the sense that the player can start jumping mid-air. Here is an example:

Some jump heights do not work. This is still being investigated.


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. Since 1.15 (when the boat fall damage bug was fixed), jumping resets the player's fall distance, which can be abused to survive high falls when combined with lagback.