From 1b4d257e3571ef321e0778e33e6d98727012d0ac Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:09:31 -0500 Subject: [PATCH] Better onGround check Thanks to @AJ-Ferguson for the suggestion. Includes hacks to still maintain pre-1.21.30, but that will likely lose support very soon, anyway. --- .../level/physics/CollisionManager.java | 13 ++++++----- .../geysermc/geyser/network/GameProtocol.java | 4 ++++ .../player/input/BedrockMovePlayer.java | 22 ++++++++++++++----- 3 files changed, 27 insertions(+), 12 deletions(-) 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 c5f986499..69f88e2a1 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 @@ -154,10 +154,11 @@ public class CollisionManager { * the two versions. Will also send corrected movement packets back to Bedrock if they collide with pistons. * * @param bedrockPosition the current Bedrock position of the client + * @param onGround whether the Bedrock player is on the ground * @param teleported whether the Bedrock player has teleported to a new position. If true, movement correction is skipped. * @return the position to send to the Java server, or null to cancel sending the packet */ - public @Nullable CollisionResult adjustBedrockPosition(Vector3f bedrockPosition, boolean teleported) { + public @Nullable CollisionResult adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround, boolean teleported) { PistonCache pistonCache = session.getPistonCache(); // Bedrock clients tend to fall off of honey blocks, so we need to teleport them to the new position if (pistonCache.isPlayerAttachedToHoney()) { @@ -198,9 +199,9 @@ public class CollisionManager { position = playerBoundingBox.getBottomCenter(); - boolean onGround = (adjustedMovement.getY() != movement.getY() && movement.getY() < 0) || isOnGround(); + boolean newOnGround = adjustedMovement.getY() != movement.getY() && movement.getY() < 0 || onGround; // Send corrected position to Bedrock if they differ by too much to prevent de-syncs - if (movement.distanceSquared(adjustedMovement) > INCORRECT_MOVEMENT_THRESHOLD) { + if (onGround != newOnGround || movement.distanceSquared(adjustedMovement) > INCORRECT_MOVEMENT_THRESHOLD) { PlayerEntity playerEntity = session.getPlayerEntity(); // Client will dismount if on a vehicle if (playerEntity.getVehicle() == null && pistonCache.getPlayerMotion().equals(Vector3f.ZERO) && !pistonCache.isPlayerSlimeCollision()) { @@ -208,7 +209,7 @@ public class CollisionManager { } } - if (!onGround) { + if (!newOnGround) { // Trim the position to prevent rounding errors that make Java think we are clipping into a block position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ()); } @@ -415,8 +416,8 @@ public class CollisionManager { return BlockUtils.getCollision(blockId); } - private boolean isOnGround() { - // Someone smarter than me at collisions plz check this. + public boolean isOnGround() { + // Temporary until pre-1.21.30 support is dropped. Vector3d bottomCenter = playerBoundingBox.getBottomCenter(); Vector3i groundPos = Vector3i.from(bottomCenter.getX(), bottomCenter.getY() - 1, bottomCenter.getZ()); BlockCollision collision = BlockUtils.getCollisionAt(session, groundPos); diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index 873fa413a..463cc54fd 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -111,6 +111,10 @@ public final class GameProtocol { return session.getUpstream().getProtocolVersion() < Bedrock_v686.CODEC.getProtocolVersion(); } + public static boolean isPre1_21_30(GeyserSession session) { + return session.getUpstream().getProtocolVersion() < Bedrock_v729.CODEC.getProtocolVersion(); + } + public static boolean isPre1_21_40(GeyserSession session) { return session.getUpstream().getProtocolVersion() < Bedrock_v748.CODEC.getProtocolVersion(); } 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 6220b6623..6abb3899a 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 @@ -25,6 +25,7 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input; +import net.kyori.adventure.util.TriState; import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData; @@ -32,6 +33,7 @@ import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; 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.network.GameProtocol; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import org.geysermc.mcprotocollib.network.packet.Packet; @@ -86,6 +88,14 @@ final class BedrockMovePlayer { session.setLookBackScheduledFuture(null); } + TriState maybeOnGround; + if (GameProtocol.isPre1_21_30(session)) { + // VERTICAL_COLLISION input data does not exist. + maybeOnGround = TriState.NOT_SET; + } else { + // Client is telling us it wants to move down, but something is blocking it from doing so. + maybeOnGround = TriState.byBoolean(packet.getInputData().contains(PlayerAuthInputData.VERTICAL_COLLISION) && packet.getDelta().getY() < 0); + } // 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) boolean horizontalCollision = packet.getInputData().contains(PlayerAuthInputData.HORIZONTAL_COLLISION); @@ -94,7 +104,7 @@ 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 (!positionChanged && rotationChanged) { - ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket(entity.isOnGround(), horizontalCollision, yaw, pitch); + ServerboundMovePlayerRotPacket playerRotationPacket = new ServerboundMovePlayerRotPacket(maybeOnGround.toBooleanOrElse(entity.isOnGround()), horizontalCollision, yaw, pitch); entity.setYaw(yaw); entity.setPitch(pitch); @@ -103,10 +113,10 @@ final class BedrockMovePlayer { session.sendDownstreamGamePacket(playerRotationPacket); } else if (positionChanged) { if (isValidMove(session, entity.getPosition(), packet.getPosition())) { - CollisionResult result = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT)); + CollisionResult result = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), maybeOnGround.toBooleanOrElse(false), packet.getInputData().contains(PlayerAuthInputData.HANDLE_TELEPORT)); if (result != null) { // A null return value cancels the packet Vector3d position = result.correctedMovement(); - boolean onGround = result.onGround().toBooleanOrElse(entity.isOnGround()); + boolean onGround = maybeOnGround.toBooleanOrElseGet(() -> session.getCollisionManager().isOnGround()); boolean isBelowVoid = entity.isVoidPositionDesynched(); boolean teleportThroughVoidFloor, mustResyncPosition; @@ -155,7 +165,6 @@ final class BedrockMovePlayer { } entity.setPositionManual(packet.getPosition()); - entity.setOnGround(onGround); // Send final movement changes session.sendDownstreamGamePacket(movePacket); @@ -174,11 +183,12 @@ final class BedrockMovePlayer { session.getGeyser().getLogger().debug("Recalculating position..."); session.getCollisionManager().recalculatePosition(); } - } else if (horizontalCollision != session.getInputCache().lastHorizontalCollision()) { - session.sendDownstreamGamePacket(new ServerboundMovePlayerStatusOnlyPacket(entity.isOnGround(), horizontalCollision)); + } else if (horizontalCollision != session.getInputCache().lastHorizontalCollision() || maybeOnGround.toBooleanOrElse(entity.isOnGround()) != entity.isOnGround()) { + session.sendDownstreamGamePacket(new ServerboundMovePlayerStatusOnlyPacket(maybeOnGround.toBooleanOrElse(false), horizontalCollision)); } session.getInputCache().setLastHorizontalCollision(horizontalCollision); + entity.setOnGround(maybeOnGround.toBooleanOrElse(entity.isOnGround())); // Move parrots to match if applicable if (entity.getLeftParrot() != null) {