From 23a462d60c1d59486b2986fe7d69bfedea1956c6 Mon Sep 17 00:00:00 2001 From: Oryxel <172038521+Oryxel@users.noreply.github.com> Date: Sun, 13 Apr 2025 17:47:19 +0700 Subject: [PATCH] More accurate ground status translation (#5481) --- .../type/player/SessionPlayerEntity.java | 21 +++++++++++++++++++ .../session/cache/EntityEffectCache.java | 6 ++++++ .../player/input/BedrockMovePlayer.java | 18 ++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) 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 c7060d3e4..e0422036f 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 @@ -36,9 +36,11 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; +import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.level.BedrockDimension; +import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.DimensionUtils; @@ -88,6 +90,12 @@ public class SessionPlayerEntity extends PlayerEntity { private int lastAirSupply = getMaxAir(); + /** + * The client last tick end velocity, used for calculating player onGround. + */ + @Getter @Setter + private Vector3f lastTickEndVelocity = Vector3f.ZERO; + /** * Determines if our position is currently out-of-sync with the Java server * due to our workaround for the void floor @@ -407,4 +415,17 @@ public class SessionPlayerEntity extends PlayerEntity { movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR); session.sendUpstreamPacketImmediately(movePlayerPacket); } + + /** + * Used to calculate player jumping velocity for ground status calculation. + */ + public float getJumpVelocity() { + float velocity = 0.42F; + + if (session.getGeyser().getWorldManager().blockAt(session, this.getPosition().sub(0, EntityDefinitions.PLAYER.offset() + 0.1F, 0).toInt()).is(Blocks.HONEY_BLOCK)) { + velocity *= 0.6F; + } + + return velocity + 0.1F * session.getEffectCache().getJumpPower(); + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/EntityEffectCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/EntityEffectCache.java index a9679f6ef..359238482 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/EntityEffectCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/EntityEffectCache.java @@ -46,11 +46,16 @@ public class EntityEffectCache { @Getter private int miningFatigue; + /* Used to calculate jumping velocity */ + @Getter + private int jumpPower; + public void setEffect(Effect effect, int effectAmplifier) { switch (effect) { case CONDUIT_POWER -> conduitPower = effectAmplifier + 1; case HASTE -> haste = effectAmplifier + 1; case MINING_FATIGUE -> miningFatigue = effectAmplifier + 1; + case JUMP_BOOST -> jumpPower = effectAmplifier + 1; } entityEffects.add(effect); } @@ -60,6 +65,7 @@ public class EntityEffectCache { case CONDUIT_POWER -> conduitPower = 0; case HASTE -> haste = 0; case MINING_FATIGUE -> miningFatigue = 0; + case JUMP_BOOST -> jumpPower = 0; } entityEffects.remove(effect); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java index 23132a28b..0d8386bc1 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/input/BedrockMovePlayer.java @@ -33,6 +33,7 @@ import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.level.physics.CollisionResult; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.tags.BlockTag; import org.geysermc.geyser.text.ChatColor; import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket; @@ -86,15 +87,28 @@ final class BedrockMovePlayer { session.setLookBackScheduledFuture(null); } + // Simulate jumping since it happened this tick, not from the last tick end. + if (entity.isOnGround() && packet.getInputData().contains(PlayerAuthInputData.START_JUMPING)) { + entity.setLastTickEndVelocity(Vector3f.from(entity.getLastTickEndVelocity().getX(), Math.max(entity.getLastTickEndVelocity().getY(), entity.getJumpVelocity()), entity.getLastTickEndVelocity().getZ())); + } + + // Due to how ladder works on Bedrock, we won't get climbing velocity from tick end unless if you're colliding horizontally. So we account for it ourselves. + boolean onClimbableBlock = session.getTagCache().is(BlockTag.CLIMBABLE, session.getGeyser().getWorldManager().blockAt(session, entity.getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0).toInt()).block()); + if (onClimbableBlock && packet.getInputData().contains(PlayerAuthInputData.JUMPING)) { + entity.setLastTickEndVelocity(Vector3f.from(entity.getLastTickEndVelocity().getX(), 0.2F, entity.getLastTickEndVelocity().getZ())); + } + // Client is telling us it wants to move down, but something is blocking it from doing so. boolean isOnGround; if (hasVehicle) { // VERTICAL_COLLISION is not accurate while in a vehicle (as of 1.21.62) - isOnGround = Math.abs(packet.getDelta().getY()) < 0.1; + isOnGround = Math.abs(entity.getLastTickEndVelocity().getY()) < 0.1; } else { - isOnGround = packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && packet.getDelta().getY() < 0; + isOnGround = packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && entity.getLastTickEndVelocity().getY() < 0; } + entity.setLastTickEndVelocity(packet.getDelta()); + // This takes into account no movement sent from the client, but the player is trying to move anyway. // (Press into a wall in a corner - you're trying to move but nothing actually happens) // This isn't sent when a player is riding a vehicle (as of 1.21.62)