Skip to content

Commit

Permalink
Fix: Bedrock players dying of fall damage, instead of falling in the …
Browse files Browse the repository at this point in the history
…void (#4704)

* fix #4649

* add javadoc
  • Loading branch information
onebeastchris authored Jun 21, 2024
1 parent c00a02e commit 1104707
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@

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;
Expand Down Expand Up @@ -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);

Expand All @@ -87,10 +99,25 @@ public void spawnEntity() {

@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
Expand Down Expand Up @@ -225,6 +252,9 @@ public void setLastDeathPosition(@Nullable GlobalPos pos) {
} 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
Expand Down Expand Up @@ -276,4 +306,48 @@ public void resetAttributes() {
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -93,37 +92,50 @@ public void translate(GeyserSession session, MovePlayerPacket packet) {
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);
entity.setPitch(pitch);
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());
Expand All @@ -133,16 +145,9 @@ public void translate(GeyserSession session, MovePlayerPacket packet) {
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();
Expand Down

0 comments on commit 1104707

Please sign in to comment.