Skip to content

Commit

Permalink
fix: fix the bug that player's pos sometimes get frozen after telepor…
Browse files Browse the repository at this point in the history
…t. This is caused by the issue that sometimes client doesn't send back teleport ack after server sends teleport packet to client
  • Loading branch information
smartcmd committed Jan 16, 2025
1 parent ef6afe1 commit d674917
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 30 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ Unless otherwise specified, any version comparison below is the comparison of se
- Fixed the bug that player can still open enchant table even if he is sneaking.
- Fixed NaN motion caused by liquid in some very special cases.
- Fixed the bug that entity will still get ticked after called `removeEntity()`.
- Fixed the bug that player's pos sometimes get frozen after teleport. This is caused by the issue that sometimes client doesn't send
back teleport ack after server sends teleport packet to client.

## [0.1.2](https://github.com/AllayMC/Allay/releases/tag/0.1.2) (API 0.3.0) - 2024-12-31

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ public void teleport(Location3fc target, EntityTeleportEvent.Reason reason) {
}

target = event.getTo();
beforeTeleport();
beforeTeleport(target);
this.fallDistance = 0;
if (this.location.dimension() == target.dimension()) {
// Teleporting in the current same dimension,
Expand All @@ -441,7 +441,7 @@ public void teleport(Location3fc target, EntityTeleportEvent.Reason reason) {
}
}

protected void beforeTeleport() {
protected void beforeTeleport(Location3fc target) {
// This method is used by EntityPlayer
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,26 @@ public class EntityPlayerBaseComponentImpl extends EntityBaseComponentImpl imple
@Getter
@Setter
protected boolean usingItemOnBlock;
/**
* expectedTeleportPos is used to solve the desynchronization of data at both ends.
* Because PlayerAuthInputPacket will be sent from the client to the server at a rate of 20 per second.
* After teleporting, the server still receives the PlayerAuthInputPacket sent by the client before teleporting.
* The following is a simple simulation (initial player position is (0, 1000, 0)):
* <p>
* [C->S] Send PlayerAuthInputPacket with pos (0, 999, 0) `pk1` <br>
* [S] Set player pos to ground (0, 100, 0) without fall distance calculation <br>
* [S->C] Send new pos (0, 100, 0) `pk2` <br>
* [S] Receive `pk1`, set player pos to (0, 999 ,0) <br>
* [C] Receive `pk2`, set player pos to (0, 100, 0) <br>
* [C->S] Send PlayerAuthInputPacket with pos (0, 100, 0) `pk3` <br>
* [S] Receive `pk3`, set player pos from (0, 999, 0) to (0, 100, 0), deltaY=899 -> death
* <p>
*
* @see <a href="https://github.com/AllayMC/Allay/issues/517">teleport method should reset fall distance</a>
*/
@Getter
@Setter
protected boolean awaitingTeleportACK;
protected Vector3fc expectedTeleportPos;
// Set enchantment seed to a random value
// and if player has enchantment seed previously,
// this random value will be covered
Expand Down Expand Up @@ -303,26 +320,9 @@ protected void tryPickUpItems() {
}
}

/**
* awaitingTeleportACK is used to solve the desynchronization of data at both ends.
* Because PlayerAuthInputPacket will be sent from the client to the server at a rate of 20 per second.
* After teleporting, the server still receives the PlayerAuthInputPacket sent by the client before teleporting.
* The following is a simple simulation (initial player position is (0, 1000, 0)):
* <p>
* [C->S] Send PlayerAuthInputPacket with pos (0, 999, 0) `pk1` <br>
* [S] Set player pos to ground (0, 100, 0) without fall distance calculation <br>
* [S->C] Send new pos (0, 100, 0) `pk2` <br>
* [S] Receive `pk1`, set player pos to (0, 999 ,0) <br>
* [C] Receive `pk2`, set player pos to (0, 100, 0) <br>
* [C->S] Send PlayerAuthInputPacket with pos (0, 100, 0) `pk3` <br>
* [S] Receive `pk3`, set player pos from (0, 999, 0) to (0, 100, 0), deltaY=899 -> death
* <p>
*
* @see <a href="https://github.com/AllayMC/Allay/issues/517">teleport method should reset fall distance</a>
*/
@Override
protected void beforeTeleport() {
this.awaitingTeleportACK = true;
protected void beforeTeleport(Location3fc target) {
this.expectedTeleportPos = new Vector3f(target);
}

@Override
Expand Down Expand Up @@ -350,16 +350,15 @@ protected void teleportOverDimension(Location3fc target, EntityTeleportEvent.Rea
location.dimension().removePlayer(thisPlayer, () -> {
targetDim.getChunkService().getOrLoadChunkSync((int) target.x() >> 4, (int) target.z() >> 4);
setLocationBeforeSpawn(target);
sendLocationToSelf(reason);
if (currentDim.getDimensionInfo().dimensionId() != targetDim.getDimensionInfo().dimensionId()) {
awaitingDimensionChangeACK = true;
var packet = new ChangeDimensionPacket();
packet.setDimension(targetDim.getDimensionInfo().dimensionId());
packet.setPosition(MathUtils.JOMLVecToCBVec(target));
packet.setRespawn(!thisPlayer.isAlive());
networkComponent.sendPacket(packet);
awaitingDimensionChangeACK = true;
}
targetDim.addPlayer(thisPlayer);
targetDim.addPlayer(thisPlayer, () -> sendLocationToSelf(reason));
});
}

Expand Down Expand Up @@ -1010,4 +1009,12 @@ public void setCrawling(boolean crawling) {

setAndSendEntityFlag(EntityFlag.CRAWLING, crawling);
}

public boolean isAwaitingTeleportACK() {
return expectedTeleportPos != null;
}

public void ackTeleported() {
this.expectedTeleportPos = null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@
@Slf4j
public class PlayerAuthInputPacketProcessor extends PacketProcessor<PlayerAuthInputPacket> {

// TODO: Accurate breaking time calculations when player keeping jumping
// TODO: Accurate breaking time calculations when player keeping jumping, maybe use BLOCK_BREAKING_DELAY_ENABLED?
// The current implementation will work fine in most cases
// But it doesn't work out the same breaking time as the client when player keep jumping
// It is hard for us to calculate the exact breaking time when player keep jumping
protected static final int BLOCK_BREAKING_TIME_FAULT_TOLERANCE = Integer.MAX_VALUE;
public static final int TELEPORT_ACK_DIFF_TOLERANCE = 1;

protected int blockToBreakX = Integer.MAX_VALUE;
protected int blockToBreakY = Integer.MAX_VALUE;
Expand Down Expand Up @@ -282,11 +283,15 @@ public PacketSignal handleAsync(EntityPlayer player, PlayerAuthInputPacket packe
}

var baseComponent = ((EntityPlayerBaseComponentImpl) ((EntityPlayerImpl) player).getBaseComponent());
if (packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT)) {
baseComponent.setAwaitingTeleportACK(false);
}
if (baseComponent.isAwaitingTeleportACK()) {
return PacketSignal.HANDLED;
var diff = baseComponent.getExpectedTeleportPos().sub(MathUtils.CBVecToJOMLVec(packet.getPosition().sub(0, player.getNetworkOffset(), 0)), new org.joml.Vector3f()).length();
if (diff > TELEPORT_ACK_DIFF_TOLERANCE) {
// The player has moved before it received the teleport packet. Ignore this movement entirely and
// wait for the client to sync itself back to the server. Once we get a movement that is close
// enough to the teleport position, we'll allow the player to move around again.
return PacketSignal.HANDLED;
}
baseComponent.ackTeleported();
}

if (!validateClientLocation(player, packet.getPosition(), packet.getRotation())) {
Expand Down

0 comments on commit d674917

Please sign in to comment.