1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2026-01-06 15:41:50 +00:00

Fix virtual inventory opening

This commit is contained in:
onebeastchris
2025-03-26 15:47:58 +01:00
parent ae8062c5bc
commit b7c56037bb
7 changed files with 83 additions and 53 deletions

View File

@@ -168,19 +168,20 @@ public class BlockInventoryHolder extends InventoryHolder {
return;
}
// Bedrock broke inventory closing. I wish i was kidding.
// "type" is explicitly passed to keep track of which inventory types can be closed without
// ""workarounds"". yippie.
// Further, Lecterns cannot be closed with any of the two methods below.
// Lecterns are special, and cannot be closed with any of the two methods below.
if (container.isUsingRealBlock() && !(container instanceof LecternContainer)) {
// No need to reset a block since we didn't change any blocks
// But send a container close packet because we aren't destroying the original.
ContainerClosePacket packet = new ContainerClosePacket();
packet.setId((byte) inventory.getBedrockId());
packet.setServerInitiated(true);
packet.setType(type != null ? type : containerType);
session.sendUpstreamPacket(packet);
// Here comes the ugly part. This is a manual check to filter specific containers
// that won't close anymore with "just" a ContainerClosePacket.
if (type != null) {
// No need to reset a block since we didn't change any blocks
// But send a container close packet because we aren't destroying the original.
ContainerClosePacket packet = new ContainerClosePacket();
packet.setId((byte) inventory.getBedrockId());
packet.setServerInitiated(true);
packet.setType(type);
session.sendUpstreamPacket(packet);
return;
}

View File

@@ -675,6 +675,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Setter
private int stepTicks = 0;
/*
* Stores the number of attempts to open virtual inventories.
* Capped at 3, and isn't used in ideal circumstances.
* Used to resolve https://github.com/GeyserMC/Geyser/issues/5426
*/
@Setter
private int containerOpenAttempts;
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop tickEventLoop) {
this.geyser = geyser;

View File

@@ -102,12 +102,14 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(blockPacket);
NbtMap tag = BlockEntityTranslator.getConstantBedrockTag("Chest", position)
NbtMapBuilder tag = BlockEntityTranslator.getConstantBedrockTag("Chest", position)
.putInt("pairx", pairPosition.getX())
.putInt("pairz", pairPosition.getZ())
.putString("CustomName", inventory.getTitle()).build();
.putString("CustomName", inventory.getTitle())
.putBoolean("pairlead", false);
BlockEntityDataPacket dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setData(tag.build());
dataPacket.setBlockPosition(position);
session.sendUpstreamPacket(dataPacket);
@@ -125,14 +127,15 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
.putInt("z", pairPosition.getZ())
.putInt("pairx", position.getX())
.putInt("pairz", position.getZ())
.putString("CustomName", inventory.getTitle()).build();
.putString("CustomName", inventory.getTitle())
.putBoolean("pairlead", true);
dataPacket = new BlockEntityDataPacket();
dataPacket.setData(tag);
dataPacket.setData(tag.build());
dataPacket.setBlockPosition(pairPosition);
session.sendUpstreamPacket(dataPacket);
inventory.setHolderPosition(position);
return true;
}
@@ -160,31 +163,30 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
return;
}
if (container.isUsingRealBlock()) {
// No need to reset a block since we didn't change any blocks
// But send a container close packet because we aren't destroying the original.
ContainerClosePacket packet = new ContainerClosePacket();
packet.setId((byte) inventory.getBedrockId());
packet.setServerInitiated(true);
packet.setType(ContainerType.CONTAINER);
session.sendUpstreamPacket(packet);
return;
// No need to reset a block since we didn't change any blocks
// But send a container close packet because we aren't destroying the original.
ContainerClosePacket packet = new ContainerClosePacket();
packet.setId((byte) inventory.getBedrockId());
packet.setServerInitiated(true);
packet.setType(ContainerType.CONTAINER);
session.sendUpstreamPacket(packet);
if (!container.isUsingRealBlock()) {
Vector3i holderPos = inventory.getHolderPosition();
int realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
session.sendUpstreamPacket(blockPacket);
holderPos = holderPos.add(Vector3i.UNIT_X);
realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
session.sendUpstreamPacket(blockPacket);
}
Vector3i holderPos = inventory.getHolderPosition();
int realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
session.sendUpstreamPacket(blockPacket);
holderPos = holderPos.add(Vector3i.UNIT_X);
realBlock = session.getGeyser().getWorldManager().getBlockAt(session, holderPos);
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
session.sendUpstreamPacket(blockPacket);
}
}

View File

@@ -26,6 +26,8 @@
package org.geysermc.geyser.translator.protocol.bedrock;
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.Container;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.inventory.MerchantContainer;
import org.geysermc.geyser.session.GeyserSession;
@@ -45,12 +47,29 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
session.sendUpstreamPacket(packet);
session.setClosingInventory(false);
if (bedrockId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
// 1.16.200 - window ID is always -1 sent from Bedrock
bedrockId = (byte) session.getOpenInventory().getBedrockId();
// 1.21.70: Bedrock can reject opening inventories - in those cases it replies with -1
Inventory openInventory = session.getOpenInventory();
if (bedrockId == -1 && openInventory != null) {
// 1.16.200 - window ID is always -1 sent from Bedrock for merchant containers
bedrockId = (byte) openInventory.getBedrockId();
// If virtual inventories are opened too quickly, they can be occasionally rejected
if (openInventory instanceof Container container && !container.isUsingRealBlock()) {
if (session.getContainerOpenAttempts() < 3) {
session.setContainerOpenAttempts(session.getContainerOpenAttempts() + 1);
session.getInventoryTranslator().openInventory(session, session.getOpenInventory());
session.getInventoryTranslator().updateInventory(session, session.getOpenInventory());
session.getOpenInventory().setDisplayed(true);
return;
} else {
GeyserImpl.getInstance().getLogger().debug("Exceeded 3 attempts to open a virtual inventory!");
GeyserImpl.getInstance().getLogger().debug(packet + " " + session.getOpenInventory().getClass().getSimpleName());
}
}
}
Inventory openInventory = session.getOpenInventory();
session.setContainerOpenAttempts(0);
if (openInventory != null) {
if (bedrockId == openInventory.getBedrockId()) {
InventoryUtils.sendJavaContainerClose(session, openInventory);

View File

@@ -26,11 +26,11 @@
package org.geysermc.geyser.translator.protocol.java.inventory;
import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerClosePacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundContainerClosePacket;
@Translator(packet = ClientboundContainerClosePacket.class)
public class JavaContainerCloseTranslator extends PacketTranslator<ClientboundContainerClosePacket> {

View File

@@ -25,9 +25,6 @@
package org.geysermc.geyser.translator.protocol.java.inventory;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundOpenScreenPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
import net.kyori.adventure.text.Component;
import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.session.GeyserSession;
@@ -37,6 +34,9 @@ import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.inventory.ClientboundOpenScreenPacket;
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClosePacket;
@Translator(packet = ClientboundOpenScreenPacket.class)
public class JavaOpenScreenTranslator extends PacketTranslator<ClientboundOpenScreenPacket> {

View File

@@ -55,7 +55,6 @@ import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.recipe.display.slot.CompositeSlotDisplay;
@@ -100,7 +99,8 @@ public class InventoryUtils {
public static void displayInventory(GeyserSession session, Inventory inventory) {
InventoryTranslator translator = session.getInventoryTranslator();
if (translator.prepareInventory(session, inventory)) {
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
// 1.21.70 wants a delay on all virtual inventories: https://github.com/GeyserMC/Geyser/issues/5426
if (inventory instanceof Container container && !container.isUsingRealBlock()) {
session.scheduleInEventLoop(() -> {
Inventory openInv = session.getOpenInventory();
if (openInv != null && openInv.getJavaId() == inventory.getJavaId()) {
@@ -111,7 +111,7 @@ public class InventoryUtils {
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
displayInventory(session, openInv);
}
}, 200, TimeUnit.MILLISECONDS);
}, 300, TimeUnit.MILLISECONDS);
} else {
translator.openInventory(session, inventory);
translator.updateInventory(session, inventory);