From 4074582059be6d5ee40124e0cb2ee8b35570336c Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sun, 20 Oct 2019 19:41:46 -0800 Subject: [PATCH] Close open inventory before opening a new one --- .../network/translators/TranslatorsInit.java | 12 +- .../DoubleChestInventoryTranslator.java | 157 ++++++++++++++++++ ...va => SingleChestInventoryTranslator.java} | 57 +------ .../java/window/JavaOpenWindowTranslator.java | 29 +++- .../connector/utils/InventoryUtils.java | 8 +- 5 files changed, 199 insertions(+), 64 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java rename connector/src/main/java/org/geysermc/connector/network/translators/inventory/{ChestInventoryTranslator.java => SingleChestInventoryTranslator.java} (66%) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java index 74c50dbf1..09741a23e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java @@ -162,12 +162,12 @@ public class TranslatorsInit { private static void registerInventoryTranslators() { inventoryTranslators.put(null, new PlayerInventoryTranslator()); //player inventory - inventoryTranslators.put(WindowType.GENERIC_9X1, new ChestInventoryTranslator(9)); - inventoryTranslators.put(WindowType.GENERIC_9X2, new ChestInventoryTranslator(18)); - inventoryTranslators.put(WindowType.GENERIC_9X3, new ChestInventoryTranslator(27)); - inventoryTranslators.put(WindowType.GENERIC_9X4, new ChestInventoryTranslator(36)); - inventoryTranslators.put(WindowType.GENERIC_9X5, new ChestInventoryTranslator(45)); - inventoryTranslators.put(WindowType.GENERIC_9X6, new ChestInventoryTranslator(54)); + inventoryTranslators.put(WindowType.GENERIC_9X1, new SingleChestInventoryTranslator(9)); + inventoryTranslators.put(WindowType.GENERIC_9X2, new SingleChestInventoryTranslator(18)); + inventoryTranslators.put(WindowType.GENERIC_9X3, new SingleChestInventoryTranslator(27)); + inventoryTranslators.put(WindowType.GENERIC_9X4, new DoubleChestInventoryTranslator(36)); + inventoryTranslators.put(WindowType.GENERIC_9X5, new DoubleChestInventoryTranslator(45)); + inventoryTranslators.put(WindowType.GENERIC_9X6, new DoubleChestInventoryTranslator(54)); inventoryTranslators.put(WindowType.GENERIC_3X3, new DispenserInventoryTranslator()); inventoryTranslators.put(WindowType.HOPPER, new HopperInventoryTranslator()); inventoryTranslators.put(WindowType.FURNACE, new FurnaceInventoryTranslator()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java new file mode 100644 index 000000000..027c59c69 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/DoubleChestInventoryTranslator.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2019 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.connector.network.translators.inventory; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.nukkitx.math.vector.Vector3i; +import com.nukkitx.nbt.tag.CompoundTag; +import com.nukkitx.protocol.bedrock.data.ContainerType; +import com.nukkitx.protocol.bedrock.data.InventoryAction; +import com.nukkitx.protocol.bedrock.data.ItemData; +import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; +import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import org.geysermc.connector.inventory.Inventory; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.TranslatorsInit; +import org.geysermc.connector.network.translators.block.BlockEntry; +import org.geysermc.connector.world.GlobalBlockPalette; + +public class DoubleChestInventoryTranslator extends InventoryTranslator { + public DoubleChestInventoryTranslator(int size) { + super(size); + } + + @Override + public void prepareInventory(GeyserSession session, Inventory inventory) { + Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP); + Vector3i pairPosition = position.add(Vector3i.UNIT_X); + UpdateBlockPacket blockPacket = new UpdateBlockPacket(); + blockPacket.setDataLayer(0); + blockPacket.setBlockPosition(position); + blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(54 << 4)); //chest + blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + session.getUpstream().sendPacket(blockPacket); + + CompoundTag tag = CompoundTag.EMPTY.toBuilder() + .stringTag("id", "Chest") + .intTag("x", position.getX()) + .intTag("y", position.getY()) + .intTag("z", position.getZ()) + .intTag("pairx", pairPosition.getX()) + .intTag("pairz", pairPosition.getZ()).buildRootTag(); + BlockEntityDataPacket dataPacket = new BlockEntityDataPacket(); + dataPacket.setData(tag); + dataPacket.setBlockPosition(position); + session.getUpstream().sendPacket(dataPacket); + + blockPacket = new UpdateBlockPacket(); + blockPacket.setDataLayer(0); + blockPacket.setBlockPosition(pairPosition); + blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(54 << 4)); + blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + session.getUpstream().sendPacket(blockPacket); + + tag = CompoundTag.EMPTY.toBuilder() + .stringTag("id", "Chest") + .intTag("x", pairPosition.getX()) + .intTag("y", pairPosition.getY()) + .intTag("z", pairPosition.getZ()) + .intTag("pairx", position.getX()) + .intTag("pairz", position.getZ()).buildRootTag(); + dataPacket = new BlockEntityDataPacket(); + dataPacket.setData(tag); + dataPacket.setBlockPosition(pairPosition); + session.getUpstream().sendPacket(dataPacket); + + inventory.setHolderPosition(position); + } + + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); + containerOpenPacket.setWindowId((byte) inventory.getId()); + containerOpenPacket.setType((byte) ContainerType.CONTAINER.id()); + containerOpenPacket.setBlockPosition(inventory.getHolderPosition()); + containerOpenPacket.setUniqueEntityId(inventory.getHolderId()); + session.getUpstream().sendPacket(containerOpenPacket); + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + Vector3i holderPos = inventory.getHolderPosition(); + Position pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ()); + BlockEntry realBlock = session.getChunkCache().getBlockAt(pos); + UpdateBlockPacket blockPacket = new UpdateBlockPacket(); + blockPacket.setDataLayer(0); + blockPacket.setBlockPosition(holderPos); + blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(realBlock.getBedrockId() << 4 | realBlock.getBedrockData())); + session.getUpstream().sendPacket(blockPacket); + + holderPos = holderPos.add(Vector3i.UNIT_X); + pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ()); + realBlock = session.getChunkCache().getBlockAt(pos); + blockPacket = new UpdateBlockPacket(); + blockPacket.setDataLayer(0); + blockPacket.setBlockPosition(holderPos); + blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(realBlock.getBedrockId() << 4 | realBlock.getBedrockData())); + session.getUpstream().sendPacket(blockPacket); + } + + @Override + public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + } + + @Override + public void updateInventory(GeyserSession session, Inventory inventory) { + //need to pad empty slots for 4x9, and 5x9 + final int paddedSize = 54; + ItemData[] bedrockItems = new ItemData[paddedSize]; + for (int i = 0; i < bedrockItems.length; i++) { + if (i <= this.size) { + bedrockItems[i] = TranslatorsInit.getItemTranslator().translateToBedrock(inventory.getItems()[i]); + } else { + bedrockItems[i] = ItemData.AIR; + } + } + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(inventory.getId()); + contentPacket.setContents(bedrockItems); + session.getUpstream().sendPacket(contentPacket); + + Inventory playerInventory = session.getInventory(); + for (int i = 0; i < 36; i++) { + playerInventory.getItems()[i + 9] = inventory.getItems()[i + this.size]; + } + TranslatorsInit.getInventoryTranslators().get(playerInventory.getWindowType()).updateInventory(session, playerInventory); + } + + @Override + public boolean isOutputSlot(InventoryAction action) { + return false; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java similarity index 66% rename from connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java index bd2aa5810..fd58e7c62 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/ChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/SingleChestInventoryTranslator.java @@ -39,8 +39,8 @@ import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.network.translators.block.BlockEntry; import org.geysermc.connector.world.GlobalBlockPalette; -public class ChestInventoryTranslator extends InventoryTranslator { - public ChestInventoryTranslator(int size) { +public class SingleChestInventoryTranslator extends InventoryTranslator { + public SingleChestInventoryTranslator(int size) { super(size); } @@ -54,39 +54,6 @@ public class ChestInventoryTranslator extends InventoryTranslator { blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(54 << 4)); //chest blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); session.getUpstream().sendPacket(blockPacket); - if (size > 27) { - Vector3i pairPosition = position.add(Vector3i.UNIT_X); - blockPacket = new UpdateBlockPacket(); - blockPacket.setDataLayer(0); - blockPacket.setBlockPosition(pairPosition); - blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(54 << 4)); - blockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); - session.getUpstream().sendPacket(blockPacket); - - CompoundTag tag = CompoundTag.EMPTY.toBuilder() - .stringTag("id", "Chest") - .intTag("x", position.getX()) - .intTag("y", position.getY()) - .intTag("z", position.getZ()) - .intTag("pairx", pairPosition.getX()) - .intTag("pairz", pairPosition.getZ()).buildRootTag(); - BlockEntityDataPacket dataPacket = new BlockEntityDataPacket(); - dataPacket.setData(tag); - dataPacket.setBlockPosition(position); - session.getUpstream().sendPacket(dataPacket); - - tag = CompoundTag.EMPTY.toBuilder() - .stringTag("id", "Chest") - .intTag("x", pairPosition.getX()) - .intTag("y", pairPosition.getY()) - .intTag("z", pairPosition.getZ()) - .intTag("pairx", position.getX()) - .intTag("pairz", position.getZ()).buildRootTag(); - dataPacket = new BlockEntityDataPacket(); - dataPacket.setData(tag); - dataPacket.setBlockPosition(pairPosition); - session.getUpstream().sendPacket(dataPacket); - } inventory.setHolderPosition(position); } @@ -110,17 +77,6 @@ public class ChestInventoryTranslator extends InventoryTranslator { blockPacket.setBlockPosition(holderPos); blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(realBlock.getBedrockId() << 4 | realBlock.getBedrockData())); session.getUpstream().sendPacket(blockPacket); - - if (this.size > 27) { - holderPos = holderPos.add(Vector3i.UNIT_X); - pos = new Position(holderPos.getX(), holderPos.getY(), holderPos.getZ()); - realBlock = session.getChunkCache().getBlockAt(pos); - blockPacket = new UpdateBlockPacket(); - blockPacket.setDataLayer(0); - blockPacket.setBlockPosition(holderPos); - blockPacket.setRuntimeId(GlobalBlockPalette.getOrCreateRuntimeId(realBlock.getBedrockId() << 4 | realBlock.getBedrockData())); - session.getUpstream().sendPacket(blockPacket); - } } @Override @@ -129,13 +85,8 @@ public class ChestInventoryTranslator extends InventoryTranslator { @Override public void updateInventory(GeyserSession session, Inventory inventory) { - //need to pad empty slots for 1x9, 2x9, 4x9, and 5x9 - int paddedSize; - if (this.size > 27) { - paddedSize = 54; - } else { - paddedSize = 27; - } + //need to pad empty slots for 1x9 and 2x9 + final int paddedSize = 27; ItemData[] bedrockItems = new ItemData[paddedSize]; for (int i = 0; i < bedrockItems.length; i++) { if (i <= this.size) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java index b5e0447c4..95aa618c0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java @@ -25,15 +25,42 @@ package org.geysermc.connector.network.translators.java.window; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket; +import org.geysermc.api.Geyser; +import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.TranslatorsInit; +import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.utils.InventoryUtils; +import java.util.concurrent.TimeUnit; + public class JavaOpenWindowTranslator extends PacketTranslator { @Override public void translate(ServerOpenWindowPacket packet, GeyserSession session) { - InventoryUtils.openInventory(session, packet); + InventoryTranslator newTranslator = TranslatorsInit.getInventoryTranslators().get(packet.getType()); + if (newTranslator == null) { + ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId()); + session.getDownstream().getSession().send(closeWindowPacket); + return; + } + Inventory openInventory = session.getInventoryCache().getOpenInventory(); + Inventory newInventory = new Inventory(packet.getWindowId(), packet.getType()); + newInventory.setItems(new ItemStack[newTranslator.size + 36]); + session.getInventoryCache().cacheInventory(newInventory); + if (openInventory != null) { + InventoryTranslator openTranslator = TranslatorsInit.getInventoryTranslators().get(openInventory.getWindowType()); + if (!openTranslator.getClass().equals(newTranslator.getClass())) { + InventoryUtils.closeInventory(session, openInventory.getId()); + Geyser.getGeneralThreadPool().schedule(() -> InventoryUtils.openInventory(session, newInventory), 350, TimeUnit.MILLISECONDS); + return; + } + } + + InventoryUtils.openInventory(session, newInventory); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java index b979aadb4..2663ab373 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -9,6 +9,7 @@ import org.geysermc.api.Geyser; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.TranslatorsInit; +import org.geysermc.connector.network.translators.inventory.DoubleChestInventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import java.util.Objects; @@ -16,21 +17,20 @@ import java.util.concurrent.TimeUnit; public class InventoryUtils { - public static void openInventory(GeyserSession session, ServerOpenWindowPacket packet) { - Inventory inventory = new Inventory(packet.getWindowId(), packet.getType()); + public static void openInventory(GeyserSession session, Inventory inventory) { InventoryTranslator translator = TranslatorsInit.getInventoryTranslators().get(inventory.getWindowType()); if (translator != null) { - session.getInventoryCache().cacheInventory(inventory); session.getInventoryCache().setOpenInventory(inventory); translator.prepareInventory(session, inventory); //TODO: find better way to handle double chest delay - if (inventory.getWindowType() == WindowType.GENERIC_9X4 || inventory.getWindowType() == WindowType.GENERIC_9X5 || inventory.getWindowType() == WindowType.GENERIC_9X6) { + if (translator instanceof DoubleChestInventoryTranslator) { Geyser.getGeneralThreadPool().schedule(() -> { translator.openInventory(session, inventory); translator.updateInventory(session, inventory); }, 200, TimeUnit.MILLISECONDS); } else { translator.openInventory(session, inventory); + translator.updateInventory(session, inventory); } } }