mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Fix: Bedrock players dying of fall damage, instead of falling in the void (#4704)
* fix https://github.com/GeyserMC/Geyser/issues/4649 * add javadoc
This commit is contained in:
parent
c00a02e5ea
commit
110470726a
2 changed files with 106 additions and 27 deletions
|
@ -27,15 +27,18 @@ package org.geysermc.geyser.entity.type.player;
|
|||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
|
@ -69,6 +72,15 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
|
||||
private int lastAirSupply = getMaxAir();
|
||||
|
||||
/**
|
||||
* Determines if our position is currently out-of-sync with the Java server
|
||||
* due to our workaround for the void floor
|
||||
* <p>
|
||||
* Must be reset when dying, switching worlds, or being teleported out of the void
|
||||
*/
|
||||
@Getter @Setter
|
||||
private boolean voidPositionDesynched;
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
|
@ -87,10 +99,25 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
||||
if (voidPositionDesynched) {
|
||||
if (!isBelowVoidFloor()) {
|
||||
voidPositionDesynched = false; // No need to fix our offset; we've been moved
|
||||
}
|
||||
}
|
||||
super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
if (valid) { // Don't update during session init
|
||||
|
@ -225,6 +252,9 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
} else {
|
||||
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
|
||||
}
|
||||
|
||||
// We're either respawning or switching worlds, either way, we are no longer desynched
|
||||
this.setVoidPositionDesynched(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -276,4 +306,48 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
public void resetAir() {
|
||||
this.setAirSupply(getMaxAir());
|
||||
}
|
||||
|
||||
private boolean isBelowVoidFloor() {
|
||||
return position.getY() < voidFloorPosition();
|
||||
}
|
||||
|
||||
public int voidFloorPosition() {
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
return bedrockDimension.minY() - 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles teleporting the player below or above the Bedrock void floor.
|
||||
* The Java server should never see this desync as we adjust the position that we send to it
|
||||
*
|
||||
* @param up in which direction to teleport - true to resync our position, or false to be
|
||||
* teleported below the void floor.
|
||||
*/
|
||||
public void teleportVoidFloorFix(boolean up) {
|
||||
// Safety to avoid double teleports
|
||||
if ((voidPositionDesynched && !up) || (!voidPositionDesynched && up)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
Vector3f newPosition = this.getPosition();
|
||||
if (up) {
|
||||
newPosition = newPosition.up(4f);
|
||||
voidPositionDesynched = false;
|
||||
} else {
|
||||
newPosition = newPosition.down(4f);
|
||||
voidPositionDesynched = true;
|
||||
}
|
||||
|
||||
this.setPositionManual(newPosition);
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(newPosition);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacketImmediately(movePlayerPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,20 +25,19 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.level.BedrockDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.mcprotocollib.network.packet.Packet;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerRotPacket;
|
||||
|
||||
@Translator(packet = MovePlayerPacket.class)
|
||||
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
||||
|
@ -93,29 +92,42 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
|
||||
if (position != null) { // A null return value cancels the packet
|
||||
boolean onGround = packet.isOnGround();
|
||||
boolean isBelowVoid = entity.isVoidPositionDesynched();
|
||||
|
||||
boolean teleportThroughVoidFloor;
|
||||
boolean teleportThroughVoidFloor, mustResyncPosition;
|
||||
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY()) {
|
||||
if (entity.getPosition().getY() >= packet.getPosition().getY() && !isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
// The void floor is offset about 40 blocks below the bottom of the world
|
||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
||||
int voidFloorLocation = bedrockDimension.minY() - 40;
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation;
|
||||
if (teleportThroughVoidFloor) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
onGround = false;
|
||||
}
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||
} else {
|
||||
teleportThroughVoidFloor = false;
|
||||
}
|
||||
|
||||
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||
onGround = false;
|
||||
}
|
||||
|
||||
if (isBelowVoid) {
|
||||
int floorY = position.getFloorY();
|
||||
int voidFloorLocation = entity.voidFloorPosition();
|
||||
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||
} else {
|
||||
mustResyncPosition = false;
|
||||
}
|
||||
|
||||
double yPosition = position.getY();
|
||||
if (entity.isVoidPositionDesynched()) { // not using the cached variable on purpose
|
||||
yPosition += 4; // We are de-synched since we had to teleport below the void floor.
|
||||
}
|
||||
|
||||
Packet movePacket;
|
||||
if (rotationChanged) {
|
||||
// Send rotation updates as well
|
||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||
onGround,
|
||||
position.getX(), position.getY(), position.getZ(),
|
||||
position.getX(), yPosition, position.getZ(),
|
||||
yaw, pitch
|
||||
);
|
||||
entity.setYaw(yaw);
|
||||
|
@ -123,7 +135,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||
entity.setHeadYaw(headYaw);
|
||||
} else {
|
||||
// Rotation did not change; don't send an update with rotation
|
||||
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), position.getY(), position.getZ());
|
||||
movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), yPosition, position.getZ());
|
||||
}
|
||||
|
||||
entity.setPositionManual(packet.getPosition());
|
||||
|
@ -133,16 +145,9 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||
session.sendDownstreamGamePacket(movePacket);
|
||||
|
||||
if (teleportThroughVoidFloor) {
|
||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
||||
// Moving from below to above the void floor works fine
|
||||
entity.setPosition(entity.getPosition().sub(0, 4f, 0));
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
entity.teleportVoidFloorFix(false);
|
||||
} else if (mustResyncPosition) {
|
||||
entity.teleportVoidFloorFix(true);
|
||||
}
|
||||
|
||||
session.getSkullCache().updateVisibleSkulls();
|
||||
|
|
Loading…
Reference in a new issue