1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2026-01-06 15:41:50 +00:00

Send TickEndPacket alongside the auth input packet, store correct Java yaw (#5716)

* Moved ServerboundClientTickEndPacket to PlayerAuthInput translator.

* Fixed sprinting packet sending.

* Make yaw rotation match JE behaviour.

* Use the fixed yaw in ServerboundUseItemPacket.

* Fixed swinging order for attacking.

* Update some comments.

Co-authored-by: chris <github@onechris.mozmail.com>

* Requested changes.

* Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java

Co-authored-by: chris <github@onechris.mozmail.com>

* Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java

Co-authored-by: chris <github@onechris.mozmail.com>

* Update core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockPlayerAuthInputTranslator.java

Co-authored-by: chris <github@onechris.mozmail.com>

---------

Co-authored-by: chris <github@onechris.mozmail.com>
This commit is contained in:
oryxel
2025-08-23 10:58:44 +07:00
committed by GitHub
parent d1706f1908
commit 1dc3f41712
6 changed files with 41 additions and 19 deletions

View File

@@ -117,6 +117,9 @@ public class SessionPlayerEntity extends PlayerEntity {
@Getter @Setter
private Vector2f bedrockInteractRotation = Vector2f.ZERO;
@Getter @Setter
private float javaYaw;
public SessionPlayerEntity(GeyserSession session) {
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
@@ -141,6 +144,12 @@ public class SessionPlayerEntity extends PlayerEntity {
// Already logged in
}
@Override
public void setYaw(float yaw) {
super.setYaw(yaw);
this.javaYaw = yaw;
}
@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
@@ -216,12 +225,6 @@ public class SessionPlayerEntity extends PlayerEntity {
}
}
@Override
protected void setSprinting(boolean value) {
super.setSprinting(value);
session.setSprinting(value);
}
@Override
protected void setGliding(boolean value) {
session.setGliding(value);

View File

@@ -443,6 +443,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter
private Pose pose = Pose.STANDING;
/**
* This is used to keep track of player sprinting and should only change by START_SPRINT and STOP_SPRINT sent by the player, not from flag update.
*/
@Setter
private boolean sprinting;
@@ -1296,12 +1299,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.bundleCache.tick();
this.dialogManager.tick();
this.waypointCache.tick();
if (spawned && protocol.getOutboundState() == ProtocolState.GAME) {
// Could move this to the PlayerAuthInput translator, in the event the player lags
// but this will work once we implement matching Java custom tick cycles
sendDownstreamGamePacket(ServerboundClientTickEndPacket.INSTANCE);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
@@ -1431,7 +1428,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
return;
}
float yaw = playerEntity.getYaw(), pitch = playerEntity.getPitch();
float yaw = playerEntity.getJavaYaw(), pitch = playerEntity.getPitch();
if (useTouchRotation) { // Only use touch rotation when we actually needed to, resolve https://github.com/GeyserMC/Geyser/issues/5704
yaw = playerEntity.getBedrockInteractRotation().getY();
pitch = playerEntity.getBedrockInteractRotation().getX();

View File

@@ -58,7 +58,7 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
// Other times, there is a 1-tick-delay, which would result in the swing packet sent here. The BedrockAuthInputTranslator's
// MISSED_SWING case also accounts for that by checking if a swing was sent a tick ago here.
// Also, delay the swing so entity damage can be processed first
// We also send this right after entity attack to ensure packet order.
session.scheduleInEventLoop(() -> {
if (session.getArmAnimationTicks() != 0 && (session.getTicks() - session.getLastAirHitTick() > 2)) {
// So, generally, a Java player can only do one *thing* at a time.

View File

@@ -478,6 +478,11 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
InteractAction.ATTACK, session.isSneaking());
session.sendDownstreamGamePacket(attackPacket);
// Even though it is true that we already send this in BedrockAnimateTranslator, the behaviour is a bit inconsistent and
// beside we want to ensure that this should be sent right away after we send interact packet or else the order will
// be weird eg: interact - some packet - swing, which is not vanilla behaviour and might flag some anticheats.
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
// Since 1.19.10, LevelSoundEventPackets are no longer sent by the client when attacking entities
CooldownUtils.sendCooldown(session);
}

View File

@@ -38,6 +38,7 @@ import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.level.physics.CollisionResult;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.util.MathUtils;
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;
@@ -82,11 +83,16 @@ final class BedrockMovePlayer {
float pitch = packet.getRotation().getX();
float headYaw = packet.getRotation().getY();
// Even though on Java Edition the yaw rotation should never get wrapped and can get larger than 180, on Bedrock Edition
// the client always seems to wrap and limit it to -180 and 180, which is not vanilla behaviour, and doesn't cause problems
// on the surface - however, some anticheat checks for this, so we account for it
float javaYaw = entity.getJavaYaw() + MathUtils.wrapDegrees(yaw - entity.getJavaYaw());
boolean hasVehicle = entity.getVehicle() != null;
// shouldSendPositionReminder also increments a tick counter, so make sure it's always called unless the player is on a vehicle.
boolean positionChangedAndShouldUpdate = !hasVehicle && (session.getInputCache().shouldSendPositionReminder() || actualPositionChanged);
boolean rotationChanged = hasVehicle || (entity.getYaw() != yaw || entity.getPitch() != pitch || entity.getHeadYaw() != headYaw);
boolean rotationChanged = hasVehicle || (entity.getJavaYaw() != javaYaw || entity.getPitch() != pitch);
// Simulate jumping since it happened this tick, not from the last tick end.
if (entity.isOnGround() && packet.getInputData().contains(PlayerAuthInputData.START_JUMPING)) {
@@ -154,9 +160,10 @@ final class BedrockMovePlayer {
// This isn't needed, but it makes the packets closer to vanilla
// It also means you can't "lag back" while only looking, in theory
if (!positionChangedAndShouldUpdate && rotationChanged) {
ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket(isOnGround, horizontalCollision, yaw, pitch);
ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket(isOnGround, horizontalCollision, javaYaw, pitch);
entity.setYaw(yaw);
entity.setJavaYaw(javaYaw);
entity.setPitch(pitch);
entity.setHeadYaw(headYaw);
@@ -181,9 +188,10 @@ final class BedrockMovePlayer {
isOnGround,
horizontalCollision,
position.getX(), position.getY(), position.getZ(),
yaw, pitch
javaYaw, pitch
);
entity.setYaw(yaw);
entity.setJavaYaw(javaYaw);
entity.setPitch(pitch);
entity.setHeadYaw(headYaw);
} else {

View File

@@ -52,12 +52,14 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.translator.protocol.bedrock.BedrockInventoryTransactionTranslator;
import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.mcprotocollib.protocol.data.ProtocolState;
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundClientTickEndPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundMoveVehiclePacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerAbilitiesPacket;
@@ -106,8 +108,10 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
attributesPacket.getAttributes().addAll(entity.getAttributes().values());
session.sendUpstreamPacket(attributesPacket);
} else {
sprintPacket = new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.START_SPRINTING);
session.setSprinting(true);
if (!session.isSprinting()) {
sprintPacket = new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.START_SPRINTING);
session.setSprinting(true);
}
}
}
}
@@ -209,6 +213,11 @@ public final class BedrockPlayerAuthInputTranslator extends PacketTranslator<Pla
BedrockMovePlayer.translate(session, packet);
// This is the best way send this since most modern anticheat will expect this to be in sync with the player movement packet.
if (session.isSpawned()) {
session.sendDownstreamGamePacket(ServerboundClientTickEndPacket.INSTANCE);
}
// Only set steering values when the vehicle is a boat and when the client is actually in it
if (entity.getVehicle() instanceof BoatEntity && inputData.contains(PlayerAuthInputData.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
boolean up = inputData.contains(PlayerAuthInputData.UP);