Fix bucket interactions on creative mode (#1369)

* Fix bucket interactions on creative mode

Bedrock uses the BLOCK_INTERACT enum of BedrockActionTranslator to truly indicate if a bucket should be used or not. In order to hook into this, we need to delay the bucket placing by about 5 milliseconds - this gives us time to cancel the interaction if needed.

Bucket sounds will now not play in this case as well.
This commit is contained in:
Camotoy 2020-10-12 20:02:41 -04:00 committed by GitHub
parent ffcff96bea
commit 96db37c14c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 10 deletions

View file

@ -84,6 +84,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.*; import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@Getter @Getter
@ -210,6 +211,13 @@ public class GeyserSession implements CommandSender {
@Setter @Setter
private long lastInteractionTime; private long lastInteractionTime;
/**
* Stores a future interaction to place a bucket. Will be cancelled if the client instead intended to
* interact with a block.
*/
@Setter
private ScheduledFuture<?> bucketScheduledFuture;
private boolean reducedDebugInfo = false; private boolean reducedDebugInfo = false;
@Setter @Setter

View file

@ -61,6 +61,8 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockUtils; import org.geysermc.connector.utils.BlockUtils;
import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.InventoryUtils;
import java.util.concurrent.TimeUnit;
@Translator(packet = InventoryTransactionPacket.class) @Translator(packet = InventoryTransactionPacket.class)
public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> { public class BedrockInventoryTransactionTranslator extends PacketTranslator<InventoryTransactionPacket> {
@ -120,18 +122,19 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
session.sendDownstreamPacket(itemPacket); session.sendDownstreamPacket(itemPacket);
} }
// Check actions, otherwise buckets may be activated when block inventories are accessed // Check actions, otherwise buckets may be activated when block inventories are accessed
// But don't check actions if the item damage is 0 else if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId()) {
else if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId() &&
(packet.getItemInHand().getDamage() == 0 || !packet.getActions().isEmpty())) {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
// Let the server decide if the bucket item should change, not the client, and revert the changes the client made // Let the server decide if the bucket item should change, not the client, and revert the changes the client made
InventorySlotPacket slotPacket = new InventorySlotPacket(); InventorySlotPacket slotPacket = new InventorySlotPacket();
slotPacket.setContainerId(ContainerId.INVENTORY); slotPacket.setContainerId(ContainerId.INVENTORY);
slotPacket.setSlot(packet.getHotbarSlot()); slotPacket.setSlot(packet.getHotbarSlot());
slotPacket.setItem(packet.getItemInHand()); slotPacket.setItem(packet.getItemInHand());
session.sendUpstreamPacket(slotPacket); session.sendUpstreamPacket(slotPacket);
// Delay the interaction in case the client doesn't intend to actually use the bucket
// See BedrockActionTranslator.java
session.setBucketScheduledFuture(session.getConnector().getGeneralThreadPool().schedule(() -> {
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
session.sendDownstreamPacket(itemPacket);
}, 5, TimeUnit.MILLISECONDS));
} }
if (packet.getActions().isEmpty()) { if (packet.getActions().isEmpty()) {
@ -167,10 +170,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
break; break;
} }
// Handled in ITEM_USE // Handled in ITEM_USE if the item is not milk
if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId() && if (packet.getItemInHand() != null && packet.getItemInHand().getId() == ItemRegistry.BUCKET.getBedrockId() &&
// Normal bucket, water bucket, lava bucket packet.getItemInHand().getDamage() != 1) {
(packet.getItemInHand().getDamage() == 0 || packet.getItemInHand().getDamage() == 8 || packet.getItemInHand().getDamage() == 10)) {
break; break;
} }

View file

@ -114,7 +114,12 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
session.sendDownstreamPacket(stopSleepingPacket); session.sendDownstreamPacket(stopSleepingPacket);
break; break;
case BLOCK_INTERACT: case BLOCK_INTERACT:
// Handled in BedrockInventoryTransactionTranslator // Client means to interact with a block; cancel bucket interaction, if any
if (session.getBucketScheduledFuture() != null) {
session.getBucketScheduledFuture().cancel(true);
session.setBucketScheduledFuture(null);
}
// Otherwise handled in BedrockInventoryTransactionTranslator
break; break;
case START_BREAK: case START_BREAK:
if (session.getConnector().getConfig().isCacheChunks()) { if (session.getConnector().getConfig().isCacheChunks()) {

View file

@ -38,6 +38,7 @@ public class BucketSoundInteractionHandler implements BlockSoundInteractionHandl
@Override @Override
public void handleInteraction(GeyserSession session, Vector3f position, String identifier) { public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {
if (session.getBucketScheduledFuture() == null) return; // No bucket was really interacted with
String handItemIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier(); String handItemIdentifier = ItemRegistry.getItem(session.getInventory().getItemInHand()).getJavaIdentifier();
LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket(); LevelSoundEventPacket soundEventPacket = new LevelSoundEventPacket();
soundEventPacket.setPosition(position); soundEventPacket.setPosition(position);
@ -68,5 +69,6 @@ public class BucketSoundInteractionHandler implements BlockSoundInteractionHandl
soundEventPacket.setSound(soundEvent); soundEventPacket.setSound(soundEvent);
session.sendUpstreamPacket(soundEventPacket); session.sendUpstreamPacket(soundEventPacket);
} }
session.setBucketScheduledFuture(null);
} }
} }