Lagback/ja: Difference between revisions
(Created page with "垂直方向の速度を-1m/tよりも大きくするために必要なジャンプの高さは-7.3125b。この方法を試すための簡単なセットアップは-9.5b。以下がその例:") |
No edit summary |
||
Line 59: | Line 59: | ||
スニークのタイミングは正確である必要はないが、早すぎると水平方向の速度が著しく低下する。 |
スニークのタイミングは正確である必要はないが、早すぎると水平方向の速度が著しく低下する。 |
||
垂直方向の速度を-1m/t |
垂直方向の速度を-1m/t以上にするために必要なジャンプの高さは-7.3125b。この方法を試すための簡単なセットアップは-9.5b。以下がその例: |
||
<youtube>https://youtu.be/-fABDG3YVpw</youtube> |
<youtube>https://youtu.be/-fABDG3YVpw</youtube> |
Revision as of 12:35, 3 February 2023
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
に設定される。プレイヤーが移動してinWeb
がtrue
になると、すぐにfalse
に設定され、プレイヤーの速度にクモの巣の効果が適用される。これにより元の計算と再計算の間にズレが生じ、条件速度が満たされればlagbackが引き起こされる。
高所から落下しスニークしながら着地する
この種類は、以下の必要条件のもとで着地・スニークすることで実現される:
- 水平方向に0.25m/tより高い速度で移動している。
- 着地1tick前に地面の最低1ブロック上にいる(1.11+では0.6b)。
スニークのタイミングは正確である必要はないが、早すぎると水平方向の速度が著しく低下する。
垂直方向の速度を-1m/t以上にするために必要なジャンプの高さは-7.3125b。この方法を試すための簡単なセットアップは-9.5b。以下がその例:
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 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());
}