diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/AbstractArrowEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/AbstractArrowEntity.java index c916719d2c1..fe613cedbaa 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/AbstractArrowEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/AbstractArrowEntity.java @@ -31,7 +31,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -public class AbstractArrowEntity extends Entity { +public class AbstractArrowEntity extends ThrowableEntity { public AbstractArrowEntity(EntitySpawnContext context) { super(context); @@ -62,6 +62,16 @@ public void setPitch(float pitch) { public void setHeadYaw(float headYaw) { } + @Override + protected float getGravity() { + return getFlag(EntityFlag.HAS_GRAVITY) ? 0.05f : 0f; + } + + @Override + protected float getDrag() { + return isInWater() ? 0.6f : 0.99f; + } + @Override public void setMotion(Vector3f motion) { super.setMotion(motion); 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 b98b4162bee..091facd062b 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 @@ -413,8 +413,13 @@ public void setAbsorptionHearts(FloatEntityMetadata entityMetadata) { @Override public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) { + boolean serverUsingItem = (entityMetadata.getPrimitiveValue() & 0x01) == 0x01; super.setLivingEntityFlags(entityMetadata); + if (!serverUsingItem && session.shouldIgnoreServerUsingItemFalse()) { + setFlag(EntityFlag.USING_ITEM, true); + } + // Forcefully update flags since we're not tracking thing like using item properly. // For eg: when player start using item client-sided (and the USING_ITEM flag is false on geyser side) // If the server disagree with the player using item state, it will send a metadata set USING_ITEM flag to false 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 708d91cf3a4..5d70c19ae7a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -213,6 +213,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.ResolvableProfile; import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility; import org.geysermc.mcprotocollib.protocol.data.game.setting.ParticleStatus; @@ -257,6 +258,8 @@ @Getter public class GeyserSession implements GeyserConnection, GeyserCommandSource { + private static final long USING_ITEM_PREDICTION_GRACE_MILLIS = 250L; + private final GeyserImpl geyser; private final UpstreamSession upstream; private DownstreamSession downstream; @@ -624,6 +627,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { */ private int armAnimationTicks = -1; + /** + * Small grace window to ignore stale Java metadata that clears USING_ITEM too early. + */ + private long usingItemPredictionUntil; + /** * The tick in which the player last hit air. * Used to ensure we dont send two sing packets for one hit. @@ -1441,6 +1449,12 @@ public void useItem(Hand hand, boolean useTouchRotation) { return; } + if (shouldPredictUsingItem(hand)) { + playerEntity.setFlag(EntityFlag.USING_ITEM, true); + playerEntity.updateBedrockMetadata(); + beginUsingItemPrediction(); + } + 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(); @@ -1451,12 +1465,43 @@ public void useItem(Hand hand, boolean useTouchRotation) { } public void releaseItem() { + clearUsingItemPrediction(); // Followed to the Minecraft Protocol specification outlined at wiki.vg ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, Vector3i.ZERO, Direction.DOWN, 0); sendDownstreamGamePacket(releaseItemPacket); } + private boolean shouldPredictUsingItem(Hand hand) { + var itemInHand = playerInventoryHolder.inventory().getItemInHand(hand); + if (itemInHand.isEmpty()) { + return false; + } + + if (itemInHand.getComponent(DataComponentTypes.CONSUMABLE) != null) { + return true; + } + + return itemInHand.is(Items.BOW) + || itemInHand.is(Items.CROSSBOW) + || itemInHand.is(Items.TRIDENT) + || itemInHand.is(Items.SPYGLASS) + || itemInHand.is(Items.GOAT_HORN) + || itemInHand.is(Items.BRUSH); + } + + public void beginUsingItemPrediction() { + usingItemPredictionUntil = System.currentTimeMillis() + USING_ITEM_PREDICTION_GRACE_MILLIS; + } + + public void clearUsingItemPrediction() { + usingItemPredictionUntil = 0L; + } + + public boolean shouldIgnoreServerUsingItemFalse() { + return playerEntity.getFlag(EntityFlag.USING_ITEM) && System.currentTimeMillis() < usingItemPredictionUntil; + } + /** * Checks to see if a shield is in either hand to activate blocking. If so, it sets the Bedrock client to display * blocking and sends a packet to the Java server. @@ -1504,6 +1549,7 @@ public boolean isHandsBusy() { */ private boolean disableBlocking() { if (playerEntity.getFlag(EntityFlag.BLOCKING)) { + clearUsingItemPrediction(); ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, Vector3i.ZERO, Direction.DOWN, 0); sendDownstreamGamePacket(releaseItemPacket); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java index 617ddc7df08..ad1b858ba56 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/entity/JavaEntityEventTranslator.java @@ -126,6 +126,7 @@ public void translate(GeyserSession session, ClientboundEntityEventPacket packet break; case PLAYER_FINISH_USING_ITEM: if (entity instanceof SessionPlayerEntity) { + session.clearUsingItemPrediction(); entity.setFlag(EntityFlag.USING_ITEM, false); }