Lagback/zh: Difference between revisions
No edit summary |
(Updating to match new version of source page) |
||
(8 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
<languages/> |
<languages/> |
||
回弹是指玩家被传送回之前的位置。它可以被利用于卡进墙壁或以类似[[Special:MyLanguage/Blip|卡角]]的方式跳得更高,尽管这些做法在多人服务器上可能是禁止的。在本文中,我们主要关注1.8的单人模式,但大多数信息都与多人模式和1.9+相关。 |
回弹是指玩家被传送回之前的位置。它可以被利用于卡进墙壁或以类似[[Special:MyLanguage/Blip|卡角]]的方式跳得更高,尽管这些做法在多人服务器上可能是禁止的。在本文中,我们主要关注 1.8 的单人模式,但大多数信息都与多人模式和 1.9+ 相关。 |
||
以下是已知的回弹触发因素: |
以下是已知的回弹触发因素: |
||
Line 7: | Line 7: | ||
* 在[[Special:MyLanguage/cobweb|蜘蛛网]]中移动过快。 |
* 在[[Special:MyLanguage/cobweb|蜘蛛网]]中移动过快。 |
||
* 以足够的速度在[[Special:MyLanguage/sneaking|潜行]]时降落 |
* 以足够的速度在[[Special:MyLanguage/sneaking|潜行]]时降落 |
||
* 以极快的速度 |
* 以极快的速度(例如速度 100)通过[[Special:MyLanguage/Stepping|步行辅助]]移动。 |
||
* 靠着墙快速移动。 |
* 靠着墙快速移动。 |
||
<span id="General_Explanation"></span> |
|||
== 常规解释 == |
== 常规解释 == |
||
触发回弹有两种方法: |
触发回弹有两种方法: |
||
* 与碰撞箱相交(卡在方块中,或与船相撞)。 |
* 与碰撞箱相交(卡在方块中,或与船相撞)。 |
||
* |
* “''错误地''”移动。 |
||
第一种方法是无需解释的。 |
第一种方法是无需解释的。 |
||
Line 29: | Line 30: | ||
<span id="Explanations_for_specific_triggers"></span> |
|||
== 特殊触发的解释 == |
== 特殊触发的解释 == |
||
<span id="Intersecting_with_a_collision_box"></span> |
|||
=== 与碰撞箱相交 === |
=== 与碰撞箱相交 === |
||
Line 39: | Line 42: | ||
<span id="Colliding_with_a_cobweb_(WIP)"></span> |
|||
=== 与蜘蛛网碰撞 === |
=== 与蜘蛛网碰撞(WIP) === |
||
⚫ | |||
⚫ | |||
<span id="Landing_from_a_high_fall_while_sneaking"></span> |
|||
=== 在潜行时从高处落下 === |
=== 在潜行时从高处落下 === |
||
Line 58: | Line 62: | ||
<span id="Sneak_Glitch"></span> |
|||
=== 潜行故障 === |
=== 潜行故障 === |
||
当玩家[[Special:MyLanguage/Sneaking|潜行]] |
当玩家[[Special:MyLanguage/Sneaking|潜行]]时落在方块边缘会发生潜行故障(或下蹲故障),导致他们掉落(在 1.16.2 中被修复)。 |
||
执行潜行故障时,服务器对玩家的预期移动与玩家的实际移动不同。原因如下: |
执行潜行故障时,服务器对玩家的预期移动与玩家的实际移动不同。原因如下: |
||
* 在最初的计算过程中,玩家是在潜行,但不 |
* 在最初的计算过程中,玩家是在潜行,但并不“在地面上”,因此他们会走出方块边缘。但是,之后玩家仍然会落地,并设置 <code>onGround = true</code>。 |
||
* 在重新计算期间,玩家现在在实际 |
* 在重新计算期间,玩家现在在实际落地之前被认为是“在地面上”,这改变了潜行的行为:他们不能“从边缘掉落”,尽管不在地面上。 |
||
以至少 0.25m/t 的水平速度和适当的位置,执行潜行故障,则会引发回弹。在被传送回之前的位置后,玩家可以跳跃。由此产生的跳跃看起来类似于[[Special:MyLanguage/Blip|卡角]],即玩家可以在半空中开始跳跃。下面是一个例子: |
以至少 0.25m/t 的水平速度和适当的位置,执行潜行故障,则会引发回弹。在被传送回之前的位置后,玩家可以跳跃。由此产生的跳跃看起来类似于[[Special:MyLanguage/Blip|卡角]],即玩家可以在半空中开始跳跃。下面是一个例子: |
||
Line 74: | Line 79: | ||
<span id="Code"></span> |
|||
== 代码 == |
== 代码 == |
||
在单人游戏中,回弹是由这部分代码引起的(高度简化,我们忽略了飞行、睡眠、传送、重生、加载实体等异常情况......):<syntaxhighlight lang="java" line="1"> |
在单人游戏中,回弹是由这部分代码引起的(高度简化,我们忽略了飞行、睡眠、传送、重生、加载实体等异常情况......):<syntaxhighlight lang="java" line="1"> |
||
// |
//位于 NetHandlerPlayServer.java |
||
public void processPlayer(PacketPlayer packetIn) |
public void processPlayer(PacketPlayer packetIn) |
||
{ |
{ |
||
Line 114: | Line 120: | ||
boolean next_noCollisionInside = worldserver.getCollidingBoundingBoxes(this.playerEntity, this.playerEntity.getEntityBoundingBox().contract(0.0625, 0.0625, 0.0625).isEmpty(); |
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)) |
if (noCollisionInside && (movedWrongly || !next_noCollisionInside)) |
||
{ |
{ |
Latest revision as of 12:57, 3 February 2023
回彈是指玩家被傳送回之前的位置。它可以被利用於卡進牆壁或以類似卡角的方式跳得更高,儘管這些做法在多人伺服器上可能是禁止的。在本文中,我們主要關注 1.8 的單人模式,但大多數信息都與多人模式和 1.9+ 相關。
以下是已知的回彈觸發因素:
常規解釋
觸發回彈有兩種方法:
- 與碰撞箱相交(卡在方塊中,或與船相撞)。
- 「錯誤地」移動。
第一種方法是無需解釋的。
關於第二種方法,我們必須首先了解玩家的移動是如何在服務端處理的(即使是在單人遊戲中,遊戲也運行在一個集成的伺服器上)。每過一刻,客戶端就會計算玩家的下一個位置,並向伺服器發送一個數據包。伺服器通過從玩家的最後一個位置重新計算來驗證玩家的移動,理論上兩者計算結果應相同。然而,伺服器不會消除玩家移動的副作用,這可能會導致玩家預期和實際位置之間的差異。如果兩者之間的水平距離超過 0.25 m ,那麼這種差異就會被認為是顯著的,在這種情況下,玩家會被回彈至他們之前的位置。
在驗證之前未消除的副作用包括:
onGround
着陸時被設置為true
(這會改變潛行的行為)。inWeb
在與蜘蛛網碰撞時被設置為false
。- 當與牆壁或天花板發生碰撞時,各種
collided
被設置為true
。
總之,玩家的移動每刻計算兩次,但第一次計算的副作用可能會影響第二次計算的結果。如果玩家移動足夠快,伺服器就會認為玩家移動錯誤,從而觸發回彈。
特殊觸發的解釋
與碰撞箱相交
玩家不應該移動到一個碰撞箱中。但是,伺服器在每個方向都有 0.0625b 的寬容度,這意味着玩家的邊界箱可以與方塊的"表面"相交並正常移動。如果玩家進一步向內移動,就會觸發回彈。
如果玩家已經完全進入方塊的碰撞箱,則玩家在移動時不會回彈。
與蜘蛛網碰撞(WIP)
當與蜘蛛網碰撞時(在每刻結束時檢測),玩家的 inWeb
被設置為 true
。當玩家移動並且 inWeb
為 true
時,它會立即被設置為 false
,並且蜘蛛網的效果會應用在玩家的速度上。這會導致原始計算和重新計算之間存在差異,如果滿足足夠的速度,則會觸發回彈。
在潛行時從高處落下
此變種是通過掉落和潛行實現的,具有以下要求:
- 水平移動速度超過 0.25m/t。
- 落地前一刻至少在地面上方一格(在 1.11+ 中為 0.6 格)。
你不需要準確地計算潛行時間,但過早潛行會顯著降低玩家的水平速度。
達到至少 -1m/t 垂直速度所需的最小跳躍高度為 -7.3125b。嘗試這種方法的一個簡單高度是 -9.5b。這是一個例子:
潛行故障
當玩家潛行時落在方塊邊緣會發生潛行故障(或下蹲故障),導致他們掉落(在 1.16.2 中被修復)。
執行潛行故障時,伺服器對玩家的預期移動與玩家的實際移動不同。原因如下:
- 在最初的計算過程中,玩家是在潛行,但並不「在地面上」,因此他們會走出方塊邊緣。但是,之後玩家仍然會落地,並設置
onGround = true
。 - 在重新計算期間,玩家現在在實際落地之前被認為是「在地面上」,這改變了潛行的行為:他們不能「從邊緣掉落」,儘管不在地面上。
以至少 0.25m/t 的水平速度和適當的位置,執行潛行故障,則會引發回彈。在被傳送回之前的位置後,玩家可以跳躍。由此產生的跳躍看起來類似於卡角,即玩家可以在半空中開始跳躍。下面是一個例子:
一些跳躍高度沒有作用。這仍在研究中。
代碼
在單人遊戲中,回彈是由這部分代碼引起的(高度簡化,我們忽略了飛行、睡眠、傳送、重生、加載實體等異常情況......):
//位于 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();}
//检查玩家是否在碰撞箱内开始:这种情况下不会回弹
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());
}
需要注意的是,玩家回彈時,掉落傷害不會被處理。這可能會導致玩家在最終落地時遭受意外的高墜落傷害。自1.15以來(被當作船掉落傷害bug修復),跳躍重置玩家的墜落距離,這使回彈可以在高版本被利用在高墜落時倖存下來。