From c7d47ab2e44d75172d2f8ad23fcc10abc2d2abc7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:31:55 -0500 Subject: [PATCH] Add bundle drop workaround --- .../geyser/session/GeyserSession.java | 9 +++++++ .../geyser/session/cache/BundleCache.java | 27 +++++++++++++++++++ ...BedrockInventoryTransactionTranslator.java | 8 +++--- 3 files changed, 40 insertions(+), 4 deletions(-) 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 cf48dd6cd9..b3a38f32f1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1355,6 +1355,8 @@ protected void tick() { } } + this.bundleCache.tick(); + if (spawned) { // 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 @@ -1473,6 +1475,13 @@ public void useItem(Hand hand) { hand, worldCache.nextPredictionSequence(), playerEntity.getYaw(), playerEntity.getPitch())); } + public void releaseItem() { + // 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); + } + /** * 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. diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java index a57c9bf1a1..f8f4dd9a5d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/BundleCache.java @@ -52,6 +52,8 @@ public final class BundleCache { private final GeyserSession session; private int nextBundleId; + private int releaseTick = -1; + public BundleCache(GeyserSession session) { this.session = session; } @@ -216,6 +218,31 @@ public void onInventoryClose(Inventory inventory) { } } + /* All utilities to track when a release item packet should be sent. + * As of 1.21.50, Bedrock seems to be picky and inspecific when sending its own release packet, + * but if Java does not receive a release packet, then it will continue to drop items out of a bundle. + * This workaround releases items on behalf of the client if it does not send a packet, while respecting + * if Bedrock sends its own. */ + + public void awaitRelease() { + if (session.getTagCache().is(ItemTag.BUNDLES, session.getPlayerInventory().getItemInHand())) { + releaseTick = session.getTicks() + 1; + } + } + + public void markRelease() { + releaseTick = -1; + } + + public void tick() { + if (this.releaseTick != -1) { + if (session.getTicks() >= this.releaseTick) { + session.releaseItem(); + markRelease(); + } + } + } + /** * Bidirectional; works for both Bedrock and Java. */ diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index 422c45b9bf..db1a050113 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -411,6 +411,8 @@ public void translate(GeyserSession session, InventoryTransactionPacket packet) session.useItem(Hand.MAIN_HAND); + session.getBundleCache().awaitRelease(); + List legacySlots = packet.getLegacySlots(); if (packet.getActions().size() == 1 && !legacySlots.isEmpty()) { InventoryActionData actionData = packet.getActions().get(0); @@ -439,10 +441,8 @@ public void translate(GeyserSession session, InventoryTransactionPacket packet) break; case ITEM_RELEASE: if (packet.getActionType() == 0) { - // Followed to the Minecraft Protocol specification outlined at wiki.vg - ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, Vector3i.ZERO, - Direction.DOWN, 0); - session.sendDownstreamGamePacket(releaseItemPacket); + session.releaseItem(); + session.getBundleCache().markRelease(); } break; case ITEM_USE_ON_ENTITY: