Lagback

From Minecraft Parkour Wiki
Revision as of 07:25, 4 February 2023 by KK kaku (talk | contribs) (Created page with "スニークグリッチを行う際、サーバーが想定するプレイヤーの動きと実際の動きが異なる。以下がその理由:")
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)。

スニークのタイミングは正確である必要はないが、早すぎると水平方向の速度が著しく低下する。

垂直方向の速度を-1m/t以上にするために必要なジャンプの高さは-7.3125b。この方法を試すための簡単なセットアップは-9.5b。以下がその例:


スニークグリッチ

スニークグリッチ(もしくはシフトグリッチ)はブロック縁に着地しながらスニークすると発生し、スニークしているにもかかわらず落下してしまうというもの(1.16.2で修正済み)。

スニークグリッチを行う際、サーバーが想定するプレイヤーの動きと実際の動きが異なる。以下がその理由:

  • 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.


ソースコード

シングルプレイヤーでは、lagbackはソースコードのこの部分によって引き起こされる(大幅に簡略化済み、飛行・睡眠・テレポート・リスポーン・エンティティへの騎乗などの例外的なケースは無視する):

//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();}

    //プレイヤーが当たり判定の内部から動き始めているかをチェック: その場合、lagbackは発生しない
    boolean noCollisionInside = worldserver.getCollidingBoundingBoxes(this.playerEntity, this.playerEntity.getEntityBoundingBox().contract(0.0625, 0.0625, 0.0625).isEmpty();

    //動きの検証を開始
    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();
    
    //予想位置と実際の位置の誤差を算出
    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());

    //プレイヤーを実際の位置に動かし、衝突判定をチェックする
    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();

    //エラーを検出する: プレイヤーが不正に、もしくは当たり判定の内部を移動した
    if (noCollisionInside && (movedWrongly || !next_noCollisionInside))
    {
        //プレイヤーを前の位置に戻す
        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.