Lagback/zh: Difference between revisions
(Created page with "=== 在潜行时从高处落下 ===") |
(Created page with "此变种是通过掉落和潜行实现的,具有以下要求:") |
||
Line 46: | Line 46: | ||
=== 在潜行时从高处落下 === |
=== 在潜行时从高处落下 === |
||
此变种是通过掉落和潜行实现的,具有以下要求: |
|||
<div lang="en" dir="ltr" class="mw-content-ltr"> |
|||
This variant is achieved by landing and sneaking with the following requirements: |
|||
</div> |
|||
<div lang="en" dir="ltr" class="mw-content-ltr"> |
<div lang="en" dir="ltr" class="mw-content-ltr"> |
Revision as of 16:41, 6 September 2022
回彈是指玩家被傳送回之前的位置。它可以被利用於卡進牆壁或以類似卡角的方式跳得更高,儘管這些做法在多人伺服器上可能是禁止的。在本文中,我們主要關注1.8的單人模式,但大多數信息都與多人模式和1.9+相關。
以下是已知的回彈觸發因素:
常規解釋
觸發回彈有兩種方法:
- 與碰撞箱相交(卡在方塊中,或與船相撞)。
- "錯誤地"移動。
第一種方法是無需解釋的。
關於第二種方法,我們必須首先了解玩家的移動是如何在服務端處理的(即使是在單人遊戲中,遊戲也運行在一個集成的伺服器上)。每過一刻,客戶端就會計算玩家的下一個位置,並向伺服器發送一個數據包。伺服器通過從玩家的最後一個位置重新計算來驗證玩家的移動,理論上兩者計算結果應相同。然而,伺服器不會消除玩家移動的副作用,這可能會導致玩家預期和實際位置之間的差異。如果兩者之間的水平距離超過 0.25 m ,那麼這種差異就會被認為是顯著的,在這種情況下,玩家會被回彈至他們之前的位置。
在驗證之前未消除的副作用包括:
onGround
着陸時被設置為true
(這會改變潛行的行為)。inWeb
在與蜘蛛網碰撞時被設置為false
。- 當與牆壁或天花板發生碰撞時,各種
collided
被設置為true
。
總之,玩家的移動每刻計算兩次,但第一次計算的副作用可能會影響第二次計算的結果。如果玩家移動足夠快,伺服器就會認為玩家移動錯誤,從而觸發回彈。
特殊觸發的解釋
與碰撞箱相交
玩家不應該移動到一個碰撞箱中。但是,伺服器在每個方向都有 0.0625b 的寬容度,這意味着玩家的邊界箱可以與方塊的"表面"相交並正常移動。如果玩家進一步向內移動,就會觸發回彈。
如果玩家已經完全進入方塊的碰撞箱,則玩家在移動時不會回彈。
與蜘蛛網碰撞
當與蜘蛛網碰撞時(在滴答結束時檢測,但在玩家的上一個位置),玩家的 inWeb
被設置為 true
。當玩家移動並且 inWeb
為 true
時,它會立即被設置為 false
,並且蜘蛛網的效果會應用在玩家的速度上。這會導致原始計算和重新計算之間存在差異,如果滿足足夠的速度,則會觸發回彈。
在潛行時從高處落下
此變種是通過掉落和潛行實現的,具有以下要求:
- 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+).
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. You are not required to time the sneak exactly, but sneaking too early will significantly reduce the player's horizontal speed.
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());
}