Lagback

    From Minecraft Parkour Wiki
    This page is a translated version of the page Lagback and the translation is 56% complete.
    Other languages:

    Lagbackとは、プレイヤーが以前の位置にテレポートで戻される現象である。悪用することで、壁にめり込んだりBlipと同様により高くジャンプしたりできるが、マルチプレイヤーサーバーでは実行が禁止されている場合がある。このページでは1.8のシングルプレイヤーに焦点を当てているが、ほとんどの情報はマルチプレイヤー及び1.9以降にも関連している。

    lagbackのトリガーとして知られている動作は以下の通り:

    • ブロックに埋まる。
    • ボートにぶつかる。
    • クモの巣の中で素早く移動する。
    • 充分な速度で、スニークしながら着地する。
    • stepping発動可能なブロックを、超高速(移動速度上昇100など)で通り抜ける。
    • 壁に向かって素早く移動する。


    一般的な説明

    lagbackを引き起こす方法は、大きく分けて2つある:

    • 当たり判定と重なる(ブロックに埋まる、ボートにぶつかるなど)。
    • 不正に」移動する。

    1つ目の方法については、説明の必要はないだろう。

    2つ目の方法については、まずプレイヤーの動きがサーバー側でどのように処理されているかを理解する必要がある(シングルプレイヤーであっても、ゲームは統合サーバー上で動作している)。
    毎tick、クライアントはプレイヤーの次の位置を計算し、サーバーにパケットを送信する。サーバーは、プレイヤーの動きを前回の位置から再計算することで検証する。この時、理論上は計算結果は同じ位置になるはずである。しかし、サーバーはプレイヤーの移動による副作用を元に戻さず計算するため、プレイヤーの予想される位置と実際の位置にズレが生じることがある。2つの計算結果のズレは、水平方向の距離が0.25mを超えていると重大と判断され、その場合、プレイヤーは前回の位置に戻される。

    検証の過程で元に戻されない副作用には以下が含まれる:

    • 着地時にonGroundフラグがtrueに設定される(スニークの挙動が変わる)。
    • クモの巣に接触時にinWebフラグがfalseに設定される。
    • 壁や天井に衝突時に各種collidedフラグがtrueに設定される。

    つまり、プレイヤーの動きは1tickに2回計算されるが、1回目の計算による副作用が2回目の計算結果に影響を与える可能性がある。プレイヤーの動きが十分に速ければ、サーバーはプレイヤーが不正に移動したと判断し、lagbackを発生させる。


    具体的なトリガーの説明

    当たり判定と重なる

    プレイヤーが当たり判定の中に入ることは想定されていない。しかし、サーバーが当たり判定の各方向に0.0625bの猶予を設けているため、プレイヤーのバウンディングボックスはブロックの「外殻」と重なりながらでも通常通り動くことができる。それ以上内側に動くと、lagbackが引き起こされる。

    既に完全に当たり判定の内側にいる場合は、動いても戻されることはない。


    クモの巣に接触する(WIP)

    クモの巣に接触する(tickの終わりに検知される)と、プレイヤーのinWebフラグがtrueに設定される。プレイヤーが移動してinWebtrueになると、すぐにfalseに設定され、プレイヤーの速度にクモの巣の効果が適用される。これにより元の計算と再計算の間にズレが生じ、条件速度が満たされればlagbackが引き起こされる。


    高所から落下しスニークしながら着地する

    この種類は、以下の必要条件のもとで着地・スニークすることで実現される:

    • 水平方向に0.25m/tより高い速度で移動している。
    • 着地1tick前に地面の最低1ブロック上にいる(1.11+では0.6b)。

    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();}
    </div>
    
        <div lang="en" dir="ltr" class="mw-content-ltr">
    //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();
    </div>
    
        <div lang="en" dir="ltr" class="mw-content-ltr">
    //begin movement verification
        double deltaX = next_posX - this.playerEntity.posX;
        double deltaY = next_posY - this.playerEntity.posY;
        double deltaZ = next_posZ - this.playerEntity.posZ;
    </div>
    
        <div lang="en" dir="ltr" class="mw-content-ltr">
    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());
    </div>
    
        <div lang="en" dir="ltr" class="mw-content-ltr">
    //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();
    </div>
    
        <div lang="en" dir="ltr" class="mw-content-ltr">
    //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;
        }
    </div>
    
        <div lang="en" dir="ltr" class="mw-content-ltr">
    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.