diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 8be3ded49..cc33854cc 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -65,6 +65,8 @@ import java.util.concurrent.TimeUnit; @Getter @Setter public class PlayerEntity extends LivingEntity { + public static final float SNEAKING_POSE_HEIGHT = 1.5f; + private GameProfile profile; private String username; private boolean playerList = true; // Player is in the player list @@ -381,7 +383,7 @@ public class PlayerEntity extends LivingEntity { protected void setDimensions(Pose pose) { float height; switch (pose) { - case SNEAKING -> height = 1.5f; + case SNEAKING -> height = SNEAKING_POSE_HEIGHT; case FALL_FLYING, SPIN_ATTACK, SWIMMING -> height = 0.6f; default -> { super.setDimensions(pose); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index b61110e6c..9b1fb8263 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -28,21 +28,17 @@ package org.geysermc.geyser.entity.type.player; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute; import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; -import com.nukkitx.protocol.bedrock.packet.RespawnPacket; import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.AttributeUtils; import java.util.Collections; @@ -112,12 +108,7 @@ public class SessionPlayerEntity extends PlayerEntity { @Override public void setFlags(ByteEntityMetadata entityMetadata) { super.setFlags(entityMetadata); - // Swimming/crawling is controlled by the Java server - boolean swimming = (entityMetadata.getPrimitiveValue() & 0x10) == 0x10; - if (swimming) { - setPose(Pose.SWIMMING); - } - session.setSwimmingInWater(swimming && getFlag(EntityFlag.SPRINTING)); + session.setSwimmingInWater((entityMetadata.getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); refreshSpeed = true; } diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index 5bd72182f..7a204ba4e 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -362,7 +362,22 @@ public class CollisionManager { * @return true if the block located at the player's floor position plus 1 would intersect with the player, * were they not sneaking */ - public boolean isUnderSlab() { + public boolean mustPlayerSneakHere() { + return checkPose(EntityDefinitions.PLAYER.height()); + } + + /** + * @return true if the block located at the player's floor position plus 1 would intersect with the player, + * were they not crawling + */ + public boolean mustPlayerCrawlHere() { + return checkPose(PlayerEntity.SNEAKING_POSE_HEIGHT); + } + + /** + * @param height check and see if this height is invalid in the current player position + */ + private boolean checkPose(float height) { Vector3i position = session.getPlayerEntity().getPosition().toInt(); BlockCollision collision = BlockUtils.getCollisionAt(session, position); if (collision != null) { @@ -370,7 +385,7 @@ public class CollisionManager { // at the current location. double originalY = playerBoundingBox.getMiddleY(); double originalHeight = playerBoundingBox.getSizeY(); - double standingY = originalY - (originalHeight / 2.0) + (EntityDefinitions.PLAYER.height() / 2.0); + double standingY = originalY - (originalHeight / 2.0) + (height / 2.0); playerBoundingBox.setSizeY(EntityDefinitions.PLAYER.height()); playerBoundingBox.setMiddleY(standingY); diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 02a0814d8..c7e2018dd 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1069,6 +1069,18 @@ public class GeyserSession implements GeyserConnection, CommandSender { playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); } + public void setSwimming(boolean swimming) { + if (swimming) { + this.pose = Pose.SWIMMING; + playerEntity.setBoundingBoxHeight(0.6f); + } else { + this.pose = Pose.STANDING; + playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height()); + } + playerEntity.setFlag(EntityFlag.SWIMMING, swimming); + playerEntity.updateBedrockMetadata(); + } + public void setFlying(boolean flying) { this.flying = flying; @@ -1087,7 +1099,7 @@ public class GeyserSession implements GeyserConnection, CommandSender { public AttributeData adjustSpeed() { AttributeData currentPlayerSpeed = playerEntity.getAttributes().get(GeyserAttributeType.MOVEMENT_SPEED); if (currentPlayerSpeed != null) { - if ((pose.equals(Pose.SNEAKING) && !sneaking && collisionManager.isUnderSlab()) || + if ((pose.equals(Pose.SNEAKING) && !sneaking && collisionManager.mustPlayerSneakHere()) || (!swimmingInWater && playerEntity.getFlag(EntityFlag.SWIMMING) && !collisionManager.isPlayerInWater())) { // Either of those conditions means that Bedrock goes zoom when they shouldn't be AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute / 3.32f); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java index f2edb6fa0..ff094461e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAdventureSettingsTranslator.java @@ -44,6 +44,11 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator<Adventu // We should always be flying in spectator mode session.sendAdventureSettings(); return; + } else if (isFlying && session.getPlayerEntity().getFlag(EntityFlag.SWIMMING) && session.getCollisionManager().isPlayerInWater()) { + // As of 1.18.1, Java Edition cannot fly while in water, but it can fly while crawling + // If this isn't present, swimming on a 1.13.2 server and then attempting to fly will put you into a flying/swimming state that is invalid on JE + session.sendAdventureSettings(); + return; } session.setFlying(isFlying); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java index 0fc1ee5db..0bffc3aae 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockActionTranslator.java @@ -77,16 +77,20 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket session.sendUpstreamPacket(attributesPacket); break; case START_SWIMMING: - if (!session.getPlayerEntity().getFlag(EntityFlag.SWIMMING)) { + if (!entity.getFlag(EntityFlag.SWIMMING)) { ServerboundPlayerCommandPacket startSwimPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING); session.sendDownstreamPacket(startSwimPacket); + + session.setSwimming(true); } break; case STOP_SWIMMING: // Prevent packet spam when Bedrock players are crawling near the edge of a block - if (session.isSwimmingInWater()) { + if (!session.getCollisionManager().mustPlayerCrawlHere()) { ServerboundPlayerCommandPacket stopSwimPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING); session.sendDownstreamPacket(stopSwimPacket); + + session.setSwimming(false); } break; case START_GLIDE: @@ -135,14 +139,14 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket session.setSneaking(false); break; case START_SPRINT: - if (!session.getPlayerEntity().getFlag(EntityFlag.SWIMMING)) { + if (!entity.getFlag(EntityFlag.SWIMMING)) { ServerboundPlayerCommandPacket startSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_SPRINTING); session.sendDownstreamPacket(startSprintPacket); session.setSprinting(true); } break; case STOP_SPRINT: - if (!session.getPlayerEntity().getFlag(EntityFlag.SWIMMING)) { + if (!entity.getFlag(EntityFlag.SWIMMING)) { ServerboundPlayerCommandPacket stopSprintPacket = new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.STOP_SPRINTING); session.sendDownstreamPacket(stopSprintPacket); }