1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

Fix: Player pose switching behaviour (#5987)

* More accurate pose switching behaviour.

* Revert some change.
This commit is contained in:
oryxel
2025-11-16 21:49:32 +07:00
committed by GitHub
parent 659369a913
commit a58fa79805
5 changed files with 44 additions and 79 deletions

View File

@@ -322,15 +322,19 @@ public class AvatarEntity extends LivingEntity {
setFlag(EntityFlag.SWIMMING, true); setFlag(EntityFlag.SWIMMING, true);
} else { } else {
setFlag(EntityFlag.CRAWLING, true); setFlag(EntityFlag.CRAWLING, true);
// Look at https://github.com/GeyserMC/Geyser/issues/5316, we're fixing this by spoofing player pitch to 0. // Look at https://github.com/GeyserMC/Geyser/issues/5316, we're fixing this by spoofing player pitch to 0.
// 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); updateRotation(this.yaw, 0, this.onGround);
} }
} }
} }
}
@Override @Override
public void setPitch(float pitch) { public void setPitch(float pitch) {
super.setPitch(getFlag(EntityFlag.CRAWLING) ? 0 : pitch); super.setPitch(getFlag(EntityFlag.CRAWLING) && !(this instanceof SessionPlayerEntity) ? 0 : pitch);
} }
@Override @Override

View File

@@ -30,6 +30,7 @@ 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.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.EntityLinkData; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; 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.entity.type.living.animal.tameable.ParrotEntity;
import org.geysermc.geyser.session.GeyserSession; 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.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import java.util.Collections; import java.util.Collections;
@@ -224,4 +226,19 @@ public class PlayerEntity extends AvatarEntity implements GeyserPlayerEntity {
public Vector3f position() { public Vector3f position() {
return this.position.down(definition.offset()); 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;
}
}
} }

View File

@@ -235,11 +235,6 @@ public class SessionPlayerEntity extends PlayerEntity {
} }
} }
@Override
protected void setGliding(boolean value) {
session.setGliding(value);
}
@Override @Override
protected void setSneaking(boolean value) { protected void setSneaking(boolean value) {
if (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 * Since 1.19.40, the client must be re-informed of its bounding box on respawn
* See <a href="https://github.com/GeyserMC/Geyser/issues/3370">issue 3370</a> * See <a href="https://github.com/GeyserMC/Geyser/issues/3370">issue 3370</a>
@@ -279,7 +269,11 @@ public class SessionPlayerEntity extends PlayerEntity {
@Override @Override
public void setPose(Pose pose) { public void setPose(Pose pose) {
super.setPose(pose); super.setPose(pose);
if (pose != session.getPose()) {
session.setPose(pose); session.setPose(pose);
updateBedrockMetadata();
}
} }
public float getMaxHealth() { public float getMaxHealth() {

View File

@@ -635,6 +635,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
/** /**
* If the current player is flying * If the current player is flying
*/ */
@Setter
private boolean flying = false; private boolean flying = false;
/** /**
@@ -1320,26 +1321,13 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
public void stopSneaking(boolean updateMetaData) { public void stopSneaking(boolean updateMetaData) {
disableBlocking(); disableBlocking();
setSneaking(false, updateMetaData); 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) { private void setSneaking(boolean sneaking, boolean update) {
this.sneaking = sneaking; this.sneaking = sneaking;
// Update pose and bounding box on our end playerEntity.setFlag(EntityFlag.SNEAKING, sneaking);
if (!flying) {
// The pose and bounding box should not be updated if the player is flying
setSneakingPose(sneaking);
}
collisionManager.updateScaffoldingFlags(false); collisionManager.updateScaffoldingFlags(false);
if (update) { 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) { public void setNoClip(boolean noClip) {
if (this.noClip == noClip) { if (this.noClip == noClip) {
return; return;
@@ -1393,16 +1349,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.sendAdventureSettings(); 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) { public void setGameMode(GameMode newGamemode) {
boolean currentlySpectator = this.gameMode == GameMode.SPECTATOR; boolean currentlySpectator = this.gameMode == GameMode.SPECTATOR;
this.gameMode = newGamemode; this.gameMode = newGamemode;
@@ -2228,7 +2174,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
} }
public float getEyeHeight() { public float getEyeHeight() {
return switch (pose) { return switch (this.pose) {
case SNEAKING -> 1.27f; case SNEAKING -> 1.27f;
case SWIMMING, case SWIMMING,
FALL_FLYING, // Elytra FALL_FLYING, // Elytra

View File

@@ -83,10 +83,10 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
switch (input) { switch (input) {
case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction()); case PERFORM_ITEM_INTERACTION -> processItemUseTransaction(session, packet.getItemUseTransaction());
case PERFORM_ITEM_STACK_REQUEST -> session.getPlayerInventoryHolder().translateRequests(List.of(packet.getItemStackRequest())); case PERFORM_ITEM_STACK_REQUEST -> session.getPlayerInventoryHolder().translateRequests(List.of(packet.getItemStackRequest()));
case START_SWIMMING -> session.setSwimming(true); case START_SWIMMING -> entity.setFlag(EntityFlag.SWIMMING, true);
case STOP_SWIMMING -> session.setSwimming(false); case STOP_SWIMMING -> entity.setFlag(EntityFlag.SWIMMING, false);
case START_CRAWLING -> session.setCrawling(true); case START_CRAWLING -> entity.setFlag(EntityFlag.CRAWLING, true);
case STOP_CRAWLING -> session.setCrawling(false); case STOP_CRAWLING -> entity.setFlag(EntityFlag.CRAWLING, false);
case START_SPRINTING -> { case START_SPRINTING -> {
if (!leftOverInputData.contains(PlayerAuthInputData.STOP_SPRINTING)) { if (!leftOverInputData.contains(PlayerAuthInputData.STOP_SPRINTING)) {
if (!session.isSprinting()) { if (!session.isSprinting()) {
@@ -140,11 +140,11 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
session.setFlying(false); session.setFlying(false);
session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false)); session.sendDownstreamGamePacket(new ServerboundPlayerAbilitiesPacket(false));
} }
session.setGliding(true); entity.setFlag(EntityFlag.GLIDING, true);
session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING)); session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(entity.getEntityId(), PlayerState.START_ELYTRA_FLYING));
} else { } else {
entity.forceFlagUpdate(); entity.forceFlagUpdate();
session.setGliding(false); entity.setFlag(EntityFlag.GLIDING, false);
// return to flying if we can't start gliding // return to flying if we can't start gliding
if (session.isFlying()) { if (session.isFlying()) {
session.sendAdventureSettings(); session.sendAdventureSettings();
@@ -152,14 +152,14 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
} }
} }
} }
case START_SPIN_ATTACK -> session.setSpinAttack(true); case START_SPIN_ATTACK -> entity.setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, true);
case STOP_SPIN_ATTACK -> session.setSpinAttack(false); case STOP_SPIN_ATTACK -> entity.setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, false);
case STOP_GLIDING -> { case STOP_GLIDING -> {
// Java doesn't allow elytra gliding to stop mid-air. // Java doesn't allow elytra gliding to stop mid-air.
boolean shouldBeGliding = entity.isGliding() && entity.canStartGliding(); boolean shouldBeGliding = entity.isGliding() && entity.canStartGliding();
// Always update; Bedrock can get real weird if the gliding state is mismatching // Always update; Bedrock can get real weird if the gliding state is mismatching
entity.forceFlagUpdate(); entity.forceFlagUpdate();
session.setGliding(shouldBeGliding); entity.setFlag(EntityFlag.GLIDING, shouldBeGliding);
} }
case MISSED_SWING -> { case MISSED_SWING -> {
session.setLastAirHitTick(session.getTicks()); session.setLastAirHitTick(session.getTicks());
@@ -183,6 +183,10 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
} }
} }
// The player will calculate the "desired" pose at the end of every tick, if this pose still invalid then
// it will consider the smaller pose, but we don't need to calculate that, we can go off what the client sent us.
entity.setPose(entity.getDesiredPose());
// Vehicle input is send before player movement // Vehicle input is send before player movement
processVehicleInput(session, packet, wasJumping); processVehicleInput(session, packet, wasJumping);