From a58fa798051f3fde7810d34da8ea07a899069ea1 Mon Sep 17 00:00:00 2001 From: oryxel Date: Sun, 16 Nov 2025 21:49:32 +0700 Subject: [PATCH] Fix: Player pose switching behaviour (#5987) * More accurate pose switching behaviour. * Revert some change. --- .../entity/type/player/AvatarEntity.java | 8 ++- .../entity/type/player/PlayerEntity.java | 17 ++++++ .../type/player/SessionPlayerEntity.java | 16 ++--- .../geyser/session/GeyserSession.java | 60 +------------------ .../BedrockPlayerAuthInputTranslator.java | 22 ++++--- 5 files changed, 44 insertions(+), 79 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/AvatarEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/AvatarEntity.java index f8bb7f658..68625220c 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/AvatarEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/AvatarEntity.java @@ -322,15 +322,19 @@ public class AvatarEntity extends LivingEntity { setFlag(EntityFlag.SWIMMING, true); } else { setFlag(EntityFlag.CRAWLING, true); + // Look at https://github.com/GeyserMC/Geyser/issues/5316, we're fixing this by spoofing player pitch to 0. - updateRotation(this.yaw, 0, this.onGround); + // Don't do this for session player however, as that teleport them back and messed up their rotation. + if (!(this instanceof SessionPlayerEntity)) { + updateRotation(this.yaw, 0, this.onGround); + } } } } @Override public void setPitch(float pitch) { - super.setPitch(getFlag(EntityFlag.CRAWLING) ? 0 : pitch); + super.setPitch(getFlag(EntityFlag.CRAWLING) && !(this instanceof SessionPlayerEntity) ? 0 : pitch); } @Override 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 4e1eb2dcb..71f294ccb 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 @@ -30,6 +30,7 @@ import lombok.Setter; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; @@ -40,6 +41,7 @@ import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import java.util.Collections; @@ -224,4 +226,19 @@ public class PlayerEntity extends AvatarEntity implements GeyserPlayerEntity { public Vector3f position() { return this.position.down(definition.offset()); } + + // From 1.21.8 code, should be correct since some pose should be prioritized. + public Pose getDesiredPose() { + if (this.getBedPosition() != null) { + return Pose.SLEEPING; + } else if (this.getFlag(EntityFlag.SWIMMING) || this.getFlag(EntityFlag.CRAWLING)) { + return Pose.SWIMMING; + } else if (this.getFlag(EntityFlag.GLIDING)) { + return Pose.FALL_FLYING; + } else if (this.getFlag(EntityFlag.DAMAGE_NEARBY_MOBS)) { + return Pose.SPIN_ATTACK; + } else { + return this.getFlag(EntityFlag.SNEAKING) && !session.isFlying() ? Pose.SNEAKING : Pose.STANDING; + } + } } 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 04117b131..e5f742d73 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 @@ -235,11 +235,6 @@ public class SessionPlayerEntity extends PlayerEntity { } } - @Override - protected void setGliding(boolean value) { - session.setGliding(value); - } - @Override protected void setSneaking(boolean value) { if (value) { @@ -250,11 +245,6 @@ public class SessionPlayerEntity extends PlayerEntity { } } - @Override - protected void setSpinAttack(boolean value) { - session.setSpinAttack(value); - } - /** * Since 1.19.40, the client must be re-informed of its bounding box on respawn * See issue 3370 @@ -279,7 +269,11 @@ public class SessionPlayerEntity extends PlayerEntity { @Override public void setPose(Pose pose) { super.setPose(pose); - session.setPose(pose); + + if (pose != session.getPose()) { + session.setPose(pose); + updateBedrockMetadata(); + } } public float getMaxHealth() { 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 fdac156b6..f4e4e5bb3 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -635,6 +635,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * If the current player is flying */ + @Setter private boolean flying = false; /** @@ -1320,26 +1321,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { public void stopSneaking(boolean updateMetaData) { disableBlocking(); - setSneaking(false, updateMetaData); } - public void setSpinAttack(boolean spinAttack) { - switchPose(spinAttack, EntityFlag.DAMAGE_NEARBY_MOBS, Pose.SPIN_ATTACK); - } - - public void setGliding(boolean gliding) { - switchPose(gliding, EntityFlag.GLIDING, Pose.FALL_FLYING); - } - private void setSneaking(boolean sneaking, boolean update) { this.sneaking = sneaking; - // Update pose and bounding box on our end - if (!flying) { - // The pose and bounding box should not be updated if the player is flying - setSneakingPose(sneaking); - } + playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); collisionManager.updateScaffoldingFlags(false); if (update) { @@ -1352,38 +1340,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } } - private void setSneakingPose(boolean sneaking) { - if (this.pose == Pose.SNEAKING && !sneaking) { - this.pose = Pose.STANDING; - playerEntity.setBoundingBoxHeight(playerEntity.getDefinition().height()); - } else if (sneaking) { - this.pose = Pose.SNEAKING; - playerEntity.setBoundingBoxHeight(1.5f); - } - playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); - } - - public void setSwimming(boolean swimming) { - if (!swimming && playerEntity.getFlag(EntityFlag.CRAWLING)) { - // Do not update bounding box. - playerEntity.setFlag(EntityFlag.SWIMMING, false); - playerEntity.updateBedrockMetadata(); - return; - } - switchPose(swimming, EntityFlag.SWIMMING, Pose.SWIMMING); - } - - public void setCrawling(boolean crawling) { - switchPose(crawling, EntityFlag.CRAWLING, Pose.SWIMMING); - } - - private void switchPose(boolean value, EntityFlag flag, Pose pose) { - this.pose = value ? pose : this.pose == pose ? Pose.STANDING : this.pose; - playerEntity.setDimensionsFromPose(this.pose); - playerEntity.setFlag(flag, value); - playerEntity.updateBedrockMetadata(); - } - public void setNoClip(boolean noClip) { if (this.noClip == noClip) { return; @@ -1393,16 +1349,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { this.sendAdventureSettings(); } - public void setFlying(boolean flying) { - this.flying = flying; - - if (sneaking) { - // update bounding box as it is not reduced when flying - setSneakingPose(!flying); - playerEntity.updateBedrockMetadata(); - } - } - public void setGameMode(GameMode newGamemode) { boolean currentlySpectator = this.gameMode == GameMode.SPECTATOR; this.gameMode = newGamemode; @@ -2228,7 +2174,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } public float getEyeHeight() { - return switch (pose) { + return switch (this.pose) { case SNEAKING -> 1.27f; case SWIMMING, FALL_FLYING, // Elytra diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java index 9e70f3339..96b714d45 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java @@ -83,10 +83,10 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator processItemUseTransaction(session, packet.getItemUseTransaction()); case PERFORM_ITEM_STACK_REQUEST -> session.getPlayerInventoryHolder().translateRequests(List.of(packet.getItemStackRequest())); - case START_SWIMMING -> session.setSwimming(true); - case STOP_SWIMMING -> session.setSwimming(false); - case START_CRAWLING -> session.setCrawling(true); - case STOP_CRAWLING -> session.setCrawling(false); + case START_SWIMMING -> entity.setFlag(EntityFlag.SWIMMING, true); + case STOP_SWIMMING -> entity.setFlag(EntityFlag.SWIMMING, false); + case START_CRAWLING -> entity.setFlag(EntityFlag.CRAWLING, true); + case STOP_CRAWLING -> entity.setFlag(EntityFlag.CRAWLING, false); case START_SPRINTING -> { if (!leftOverInputData.contains(PlayerAuthInputData.STOP_SPRINTING)) { if (!session.isSprinting()) { @@ -140,11 +140,11 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator session.setSpinAttack(true); - case STOP_SPIN_ATTACK -> session.setSpinAttack(false); + case START_SPIN_ATTACK -> entity.setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, true); + case STOP_SPIN_ATTACK -> entity.setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, false); case STOP_GLIDING -> { // Java doesn't allow elytra gliding to stop mid-air. boolean shouldBeGliding = entity.isGliding() && entity.canStartGliding(); // Always update; Bedrock can get real weird if the gliding state is mismatching entity.forceFlagUpdate(); - session.setGliding(shouldBeGliding); + entity.setFlag(EntityFlag.GLIDING, shouldBeGliding); } case MISSED_SWING -> { session.setLastAirHitTick(session.getTicks()); @@ -183,6 +183,10 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator