Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-25 15:50:14 +01: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
Dieser Commit ist enthalten in:
Ursprung
c00a02e5ea
Commit
110470726a
@ -27,15 +27,18 @@ package org.geysermc.geyser.entity.type.player;
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.cloudburstmc.math.vector.Vector3f;
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||||
import org.geysermc.geyser.item.Items;
|
import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.level.BedrockDimension;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.util.AttributeUtils;
|
import org.geysermc.geyser.util.AttributeUtils;
|
||||||
import org.geysermc.geyser.util.DimensionUtils;
|
import org.geysermc.geyser.util.DimensionUtils;
|
||||||
@ -69,6 +72,15 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||||||
|
|
||||||
private int lastAirSupply = getMaxAir();
|
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) {
|
public SessionPlayerEntity(GeyserSession session) {
|
||||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||||
|
|
||||||
@ -87,10 +99,25 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
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);
|
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||||
session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset()));
|
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
|
@Override
|
||||||
public void setPosition(Vector3f position) {
|
public void setPosition(Vector3f position) {
|
||||||
if (valid) { // Don't update during session init
|
if (valid) { // Don't update during session init
|
||||||
@ -225,6 +252,9 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||||||
} else {
|
} else {
|
||||||
dirtyMetadata.put(EntityDataTypes.PLAYER_HAS_DIED, false);
|
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
|
@Override
|
||||||
@ -276,4 +306,48 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||||||
public void resetAir() {
|
public void resetAir() {
|
||||||
this.setAirSupply(getMaxAir());
|
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;
|
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.Vector3d;
|
||||||
import org.cloudburstmc.math.vector.Vector3f;
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||||
import org.geysermc.geyser.level.BedrockDimension;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
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)
|
@Translator(packet = MovePlayerPacket.class)
|
||||||
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPacket> {
|
||||||
@ -93,21 +92,34 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||||||
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
|
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
|
||||||
if (position != null) { // A null return value cancels the packet
|
if (position != null) { // A null return value cancels the packet
|
||||||
boolean onGround = packet.isOnGround();
|
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
|
// 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();
|
int floorY = position.getFloorY();
|
||||||
// The void floor is offset about 40 blocks below the bottom of the world
|
int voidFloorLocation = entity.voidFloorPosition();
|
||||||
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
|
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 1) && floorY >= voidFloorLocation;
|
||||||
int voidFloorLocation = bedrockDimension.minY() - 40;
|
} else {
|
||||||
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation;
|
teleportThroughVoidFloor = false;
|
||||||
if (teleportThroughVoidFloor) {
|
}
|
||||||
|
|
||||||
|
if (teleportThroughVoidFloor || isBelowVoid) {
|
||||||
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
|
||||||
onGround = false;
|
onGround = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isBelowVoid) {
|
||||||
|
int floorY = position.getFloorY();
|
||||||
|
int voidFloorLocation = entity.voidFloorPosition();
|
||||||
|
mustResyncPosition = floorY < voidFloorLocation && floorY >= voidFloorLocation - 1;
|
||||||
} else {
|
} else {
|
||||||
teleportThroughVoidFloor = false;
|
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;
|
Packet movePacket;
|
||||||
@ -115,7 +127,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||||||
// Send rotation updates as well
|
// Send rotation updates as well
|
||||||
movePacket = new ServerboundMovePlayerPosRotPacket(
|
movePacket = new ServerboundMovePlayerPosRotPacket(
|
||||||
onGround,
|
onGround,
|
||||||
position.getX(), position.getY(), position.getZ(),
|
position.getX(), yPosition, position.getZ(),
|
||||||
yaw, pitch
|
yaw, pitch
|
||||||
);
|
);
|
||||||
entity.setYaw(yaw);
|
entity.setYaw(yaw);
|
||||||
@ -123,7 +135,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||||||
entity.setHeadYaw(headYaw);
|
entity.setHeadYaw(headYaw);
|
||||||
} else {
|
} else {
|
||||||
// Rotation did not change; don't send an update with rotation
|
// 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());
|
entity.setPositionManual(packet.getPosition());
|
||||||
@ -133,16 +145,9 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
|||||||
session.sendDownstreamGamePacket(movePacket);
|
session.sendDownstreamGamePacket(movePacket);
|
||||||
|
|
||||||
if (teleportThroughVoidFloor) {
|
if (teleportThroughVoidFloor) {
|
||||||
// Work around there being a floor at the bottom of the world and teleport the player below it
|
entity.teleportVoidFloorFix(false);
|
||||||
// Moving from below to above the void floor works fine
|
} else if (mustResyncPosition) {
|
||||||
entity.setPosition(entity.getPosition().sub(0, 4f, 0));
|
entity.teleportVoidFloorFix(true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
session.getSkullCache().updateVisibleSkulls();
|
session.getSkullCache().updateVisibleSkulls();
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren